diff --git a/.classpath b/.classpath new file mode 100644 index 00000000..b0f1625e --- /dev/null +++ b/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 33cb509e..00000000 --- a/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.project -.classpath -.idea/ -*.iml -target/ -logs/ -log/ -test-output/ -hexid.txt - -.vscode/ \ No newline at end of file diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index eebc3c18..00000000 Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index e364bfe9..00000000 --- a/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,4 +0,0 @@ -wrapperVersion=3.3.4 -distributionType=script -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 04cfa2c1..00000000 --- a/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,6 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding//src/main/resources=UTF-8 -encoding//src/test/java=UTF-8 -encoding//src/test/resources=UTF-8 -encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.apt.core.prefs b/.settings/org.eclipse.jdt.apt.core.prefs deleted file mode 100644 index 8963047b..00000000 --- a/.settings/org.eclipse.jdt.apt.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.apt.aptEnabled=true -org.eclipse.jdt.apt.genSrcDir=target/generated-sources/annotations -org.eclipse.jdt.apt.genTestSrcDir=target/generated-test-sources/test-annotations diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 3a5c6da3..00000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,525 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.builder.annotationPath.allLocations=disabled -org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled -org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore -org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull -org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= -org.eclipse.jdt.core.compiler.annotation.notowning=org.eclipse.jdt.annotation.NotOwning -org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable -org.eclipse.jdt.core.compiler.annotation.nullable.secondary= -org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled -org.eclipse.jdt.core.compiler.annotation.owning=org.eclipse.jdt.annotation.Owning -org.eclipse.jdt.core.compiler.annotation.resourceanalysis=disabled -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=25 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=25 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.APILeak=warning -org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info -org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.autoboxing=ignore -org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning -org.eclipse.jdt.core.compiler.problem.deadCode=warning -org.eclipse.jdt.core.compiler.problem.deprecation=warning -org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled -org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled -org.eclipse.jdt.core.compiler.problem.discouragedReference=warning -org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore -org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning -org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore -org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled -org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore -org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning -org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled -org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning -org.eclipse.jdt.core.compiler.problem.incompatibleOwningContract=warning -org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore -org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore -org.eclipse.jdt.core.compiler.problem.insufficientResourceAnalysis=warning -org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore -org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning -org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore -org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning -org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled -org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled -org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning -org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore -org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning -org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning -org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore -org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning -org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning -org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error -org.eclipse.jdt.core.compiler.problem.nullReference=warning -org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error -org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning -org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning -org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore -org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning -org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning -org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore -org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore -org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning -org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning -org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning -org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning -org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore -org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore -org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning -org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled -org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning -org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled -org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info -org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled -org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning -org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning -org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning -org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled -org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning -org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning -org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore -org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning -org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning -org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled -org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info -org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning -org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore -org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled -org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedImport=warning -org.eclipse.jdt.core.compiler.problem.unusedLabel=warning -org.eclipse.jdt.core.compiler.problem.unusedLambdaParameter=warning -org.eclipse.jdt.core.compiler.problem.unusedLocal=warning -org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled -org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning -org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning -org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning -org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.processAnnotations=enabled -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=25 -org.eclipse.jdt.core.formatter.align_arrows_in_switch_on_columns=false -org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false -org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 -org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line=true -org.eclipse.jdt.core.formatter.align_type_members_on_columns=false -org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false -org.eclipse.jdt.core.formatter.align_with_spaces=false -org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant=49 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field=49 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=49 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method=49 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package=49 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=0 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type=49 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=0 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=48 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=0 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=0 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=0 -org.eclipse.jdt.core.formatter.alignment_for_assertion_message=0 -org.eclipse.jdt.core.formatter.alignment_for_assignment=4 -org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 -org.eclipse.jdt.core.formatter.alignment_for_compact_if=0 -org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 -org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=0 -org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 -org.eclipse.jdt.core.formatter.alignment_for_enum_constants=49 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=49 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow=0 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon=0 -org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 -org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 -org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 -org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 -org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 -org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_record_components=0 -org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 -org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=85 -org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=84 -org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 -org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 -org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=48 -org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow=0 -org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_type_annotations=0 -org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 -org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 -org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 -org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 -org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0 -org.eclipse.jdt.core.formatter.blank_lines_after_package=1 -org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1 -org.eclipse.jdt.core.formatter.blank_lines_before_field=0 -org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 -org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 -org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 -org.eclipse.jdt.core.formatter.blank_lines_before_method=1 -org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 -org.eclipse.jdt.core.formatter.blank_lines_before_package=0 -org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 -org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0 -org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 -org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_block_in_case_after_arrow=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_record_constructor=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_record_declaration=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line -org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false -org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false -org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true -org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=true -org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false -org.eclipse.jdt.core.formatter.comment.format_block_comments=true -org.eclipse.jdt.core.formatter.comment.format_header=false -org.eclipse.jdt.core.formatter.comment.format_html=true -org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true -org.eclipse.jdt.core.formatter.comment.format_line_comments=true -org.eclipse.jdt.core.formatter.comment.format_source_code=true -org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true -org.eclipse.jdt.core.formatter.comment.indent_root_tags=true -org.eclipse.jdt.core.formatter.comment.indent_tag_description=false -org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert -org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert -org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert -org.eclipse.jdt.core.formatter.comment.javadoc_do_not_separate_block_tags=false -org.eclipse.jdt.core.formatter.comment.line_length=300 -org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true -org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true -org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false -org.eclipse.jdt.core.formatter.compact_else_if=true -org.eclipse.jdt.core.formatter.continuation_indentation=1 -org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=1 -org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off -org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on -org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false -org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true -org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true -org.eclipse.jdt.core.formatter.indent_empty_lines=true -org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true -org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true -org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true -org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true -org.eclipse.jdt.core.formatter.indentation.size=4 -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_label=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=insert -org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert -org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert -org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert -org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert -org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert -org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert -org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert -org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert -org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert -org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert -org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert -org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert -org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert -org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert -org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert -org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.join_line_comments=false -org.eclipse.jdt.core.formatter.join_lines_in_comments=true -org.eclipse.jdt.core.formatter.join_wrapped_lines=false -org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false -org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=true -org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false -org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false -org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false -org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false -org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false -org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false -org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.lineSplit=320 -org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false -org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false -org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0 -org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0 -org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 -org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0 -org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0 -org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0 -org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 -org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines -org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false -org.eclipse.jdt.core.formatter.tabulation.char=tab -org.eclipse.jdt.core.formatter.tabulation.size=4 -org.eclipse.jdt.core.formatter.text_block_indentation=0 -org.eclipse.jdt.core.formatter.use_on_off_tags=true -org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false -org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true -org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator=true -org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false -org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true -org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true -org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true -org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true -org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true -org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true -org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true -org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true -org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator=false -org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true -org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs deleted file mode 100644 index 6a5a2714..00000000 --- a/.settings/org.eclipse.jdt.ui.prefs +++ /dev/null @@ -1,147 +0,0 @@ -cleanup.add_all=true -cleanup.add_default_serial_version_id=false -cleanup.add_generated_serial_version_id=true -cleanup.add_missing_annotations=true -cleanup.add_missing_deprecated_annotations=true -cleanup.add_missing_methods=true -cleanup.add_missing_nls_tags=false -cleanup.add_missing_override_annotations=true -cleanup.add_missing_override_annotations_interface_methods=true -cleanup.add_serial_version_id=true -cleanup.also_simplify_lambda=true -cleanup.always_use_blocks=true -cleanup.always_use_parentheses_in_expressions=true -cleanup.always_use_this_for_non_static_field_access=false -cleanup.always_use_this_for_non_static_method_access=false -cleanup.array_with_curly=false -cleanup.arrays_fill=false -cleanup.bitwise_conditional_expression=false -cleanup.boolean_literal=true -cleanup.boolean_value_rather_than_comparison=true -cleanup.break_loop=false -cleanup.collection_cloning=false -cleanup.comparing_on_criteria=false -cleanup.comparison_statement=true -cleanup.controlflow_merge=false -cleanup.convert_functional_interfaces=true -cleanup.convert_to_enhanced_for_loop=true -cleanup.convert_to_enhanced_for_loop_if_loop_var_used=true -cleanup.convert_to_switch_expressions=true -cleanup.correct_indentation=false -cleanup.do_while_rather_than_while=true -cleanup.double_negation=true -cleanup.else_if=false -cleanup.embedded_if=false -cleanup.evaluate_nullable=false -cleanup.extract_increment=false -cleanup.format_source_code=true -cleanup.format_source_code_changes_only=false -cleanup.hash=false -cleanup.if_condition=true -cleanup.insert_inferred_type_arguments=false -cleanup.instanceof=true -cleanup.instanceof_keyword=true -cleanup.invert_equals=false -cleanup.join=true -cleanup.lazy_logical_operator=true -cleanup.make_local_variable_final=false -cleanup.make_parameters_final=false -cleanup.make_private_fields_final=true -cleanup.make_type_abstract_if_missing_method=false -cleanup.make_variable_declarations_final=true -cleanup.map_cloning=false -cleanup.merge_conditional_blocks=false -cleanup.multi_catch=false -cleanup.never_use_blocks=false -cleanup.never_use_parentheses_in_expressions=false -cleanup.no_string_creation=true -cleanup.no_super=true -cleanup.number_suffix=false -cleanup.objects_equals=false -cleanup.one_if_rather_than_duplicate_blocks_that_fall_through=false -cleanup.operand_factorization=false -cleanup.organize_imports=true -cleanup.overridden_assignment=false -cleanup.overridden_assignment_move_decl=true -cleanup.plain_replacement=true -cleanup.precompile_regex=true -cleanup.primitive_comparison=true -cleanup.primitive_parsing=true -cleanup.primitive_rather_than_wrapper=true -cleanup.primitive_serialization=true -cleanup.pull_out_if_from_if_else=true -cleanup.pull_up_assignment=false -cleanup.push_down_negation=false -cleanup.qualify_static_field_accesses_with_declaring_class=false -cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true -cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true -cleanup.qualify_static_member_accesses_with_declaring_class=true -cleanup.qualify_static_method_accesses_with_declaring_class=false -cleanup.reduce_indentation=false -cleanup.redundant_comparator=true -cleanup.redundant_falling_through_block_end=false -cleanup.remove_private_constructors=true -cleanup.remove_redundant_modifiers=true -cleanup.remove_redundant_semicolons=true -cleanup.remove_redundant_type_arguments=true -cleanup.remove_trailing_whitespaces=false -cleanup.remove_trailing_whitespaces_all=true -cleanup.remove_trailing_whitespaces_ignore_empty=true -cleanup.remove_unnecessary_array_creation=false -cleanup.remove_unnecessary_casts=true -cleanup.remove_unnecessary_nls_tags=true -cleanup.remove_unused_imports=true -cleanup.remove_unused_local_variables=true -cleanup.remove_unused_method_parameters=false -cleanup.remove_unused_private_fields=true -cleanup.remove_unused_private_members=true -cleanup.remove_unused_private_methods=true -cleanup.remove_unused_private_types=true -cleanup.return_expression=false -cleanup.simplify_lambda_expression_and_method_ref=true -cleanup.single_used_field=false -cleanup.sort_members=false -cleanup.sort_members_all=false -cleanup.standard_comparison=false -cleanup.static_inner_class=true -cleanup.strictly_equal_or_different=false -cleanup.stringbuffer_to_stringbuilder=true -cleanup.stringbuilder=true -cleanup.stringbuilder_for_local_vars=true -cleanup.stringconcat_stringbuffer_stringbuilder=false -cleanup.stringconcat_to_textblock=false -cleanup.substring=true -cleanup.switch=false -cleanup.system_property=false -cleanup.system_property_boolean=false -cleanup.system_property_file_encoding=false -cleanup.system_property_file_separator=false -cleanup.system_property_line_separator=false -cleanup.system_property_path_separator=false -cleanup.ternary_operator=false -cleanup.try_with_resource=false -cleanup.unlooped_while=false -cleanup.unreachable_block=false -cleanup.use_anonymous_class_creation=false -cleanup.use_autoboxing=false -cleanup.use_blocks=true -cleanup.use_blocks_only_for_return_and_throw=false -cleanup.use_directly_map_method=true -cleanup.use_lambda=true -cleanup.use_parentheses_in_expressions=true -cleanup.use_string_is_blank=true -cleanup.use_this_for_non_static_field_access=true -cleanup.use_this_for_non_static_field_access_only_if_necessary=true -cleanup.use_this_for_non_static_method_access=true -cleanup.use_this_for_non_static_method_access_only_if_necessary=true -cleanup.use_unboxing=false -cleanup.use_var=false -cleanup.useless_continue=false -cleanup.useless_return=false -cleanup.valueof_rather_than_instantiation=true -cleanup_profile=_L2J Clean Up v0.5 -cleanup_settings_version=2 -eclipse.preferences.version=1 -formatter_profile=_L2J Formatter v0.9 -formatter_settings_version=23 -org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index 14b697b7..00000000 --- a/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/L2J_Server/.classpath b/L2J_Server/.classpath new file mode 100644 index 00000000..66fb4ee0 --- /dev/null +++ b/L2J_Server/.classpath @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/L2J_Server/.gitignore b/L2J_Server/.gitignore new file mode 100644 index 00000000..707caf28 --- /dev/null +++ b/L2J_Server/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/build/ diff --git a/L2J_Server/.project b/L2J_Server/.project new file mode 100644 index 00000000..e04bf58f --- /dev/null +++ b/L2J_Server/.project @@ -0,0 +1,19 @@ + + + L2J_Server + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.jdt.core.javanature + org.eclipse.jem.beaninfo.BeanInfoNature + + \ No newline at end of file diff --git a/L2J_Server/.settings/org.eclipse.core.resources.prefs b/L2J_Server/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..4824b802 --- /dev/null +++ b/L2J_Server/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/L2J_Server/.settings/org.eclipse.core.runtime.prefs b/L2J_Server/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 00000000..f8a67de1 --- /dev/null +++ b/L2J_Server/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +line.separator=\r\n diff --git a/L2J_Server/.settings/org.eclipse.jdt.core.prefs b/L2J_Server/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..03505355 --- /dev/null +++ b/L2J_Server/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,390 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.codeComplete.argumentPrefixes= +org.eclipse.jdt.core.codeComplete.argumentSuffixes= +org.eclipse.jdt.core.codeComplete.fieldPrefixes= +org.eclipse.jdt.core.codeComplete.fieldSuffixes= +org.eclipse.jdt.core.codeComplete.localPrefixes= +org.eclipse.jdt.core.codeComplete.localSuffixes= +org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=disabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=disabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=enabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=48 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=0 +org.eclipse.jdt.core.formatter.alignment_for_assignment=4 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=0 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=0 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=49 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=49 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=85 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=48 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=next_line +org.eclipse.jdt.core.formatter.brace_position_for_block=next_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=next_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=next_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=next_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=next_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=300 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=1 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=1 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=true +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=300 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/L2J_Server/.settings/org.eclipse.jdt.ui.prefs b/L2J_Server/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000..66adcee1 --- /dev/null +++ b/L2J_Server/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,125 @@ +cleanup.add_default_serial_version_id=false +cleanup.add_generated_serial_version_id=true +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=true +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=true +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=true +cleanup.always_use_this_for_non_static_field_access=false +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=true +cleanup.convert_to_enhanced_for_loop=true +cleanup.correct_indentation=false +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.make_local_variable_final=false +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=true +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=false +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_trailing_whitespaces=false +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=true +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=true +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=true +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=true +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=true +cleanup.use_this_for_non_static_method_access=true +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_L2J Clean Up v0.3 +cleanup_settings_version=2 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_L2J Formatter v0.4 +formatter_settings_version=12 +org.eclipse.jdt.ui.exception.name=e +org.eclipse.jdt.ui.gettersetter.use.is=true +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.keywordthis=false +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.overrideannotation=true +org.eclipse.jdt.ui.staticondemandthreshold=99 +org.eclipse.jdt.ui.text.custom_code_templates= +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=true +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=true +sp_cleanup.convert_to_enhanced_for_loop=true +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.make_local_variable_final=false +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=true +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=false +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=true +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_trailing_whitespaces=false +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=true +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=true +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/L2J_Server/.settings/org.eclipse.wst.sse.core.prefs b/L2J_Server/.settings/org.eclipse.wst.sse.core.prefs new file mode 100644 index 00000000..a640cfdb --- /dev/null +++ b/L2J_Server/.settings/org.eclipse.wst.sse.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +task-tags/enabled=true +task-tags/use-project-settings=true diff --git a/L2J_Server/.settings/org.eclipse.wst.validation.prefs b/L2J_Server/.settings/org.eclipse.wst.validation.prefs new file mode 100644 index 00000000..63a18689 --- /dev/null +++ b/L2J_Server/.settings/org.eclipse.wst.validation.prefs @@ -0,0 +1,12 @@ +DELEGATES_PREFERENCE=delegateValidatorList +USER_BUILD_PREFERENCE=enabledBuildValidatorList +USER_MANUAL_PREFERENCE=enabledManualValidatorList +USER_PREFERENCE=overrideGlobalPreferencestruedisableAllValidationfalseversion1.2.400.v201204260100 +eclipse.preferences.version=1 +override=true +suspend=false +vals/org.eclipse.wst.dtd.core.dtdDTDValidator/global=FF01 +vals/org.eclipse.wst.html.core.HTMLValidator/global=FF01 +vals/org.eclipse.wst.html.ui.HTMLValidator/global=TF01 +vals/org.eclipse.wst.xml.core.xml/groups=0107include05111contentType128org.eclipse.core.runtime.xmlT111contentType134org.eclipse.wst.xml.core.xmlsourceT111contentType134org.eclipse.wst.xml.core.xslsourceT111contentType134org.eclipse.jst.jsp.core.tldsourceT07fileext03xmlF0107exclude08113projectNature134org.eclipse.jst.j2ee.ejb.EJBNature113projectNature130org.eclipse.jst.j2ee.EARNature04file08.projectT0104file110.classpathT0104file110.settings/T0204file03binF0204file05buildF0204file09build.xmlF01 +vf.version=3 diff --git a/L2J_Server/build.xml b/L2J_Server/build.xml new file mode 100644 index 00000000..5938e70d --- /dev/null +++ b/L2J_Server/build.xml @@ -0,0 +1,135 @@ + + + + This script will build the L2J Server. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + + http://www.gnu.org/copyleft/gpl.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${l2j.changelog} + + + + version=${l2j.version}${line.separator}builddate=${build.tstamp} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/L2J_Server/dist/doc/L2J_Server_LICENSE.txt b/L2J_Server/dist/doc/L2J_Server_LICENSE.txt new file mode 100644 index 00000000..3d90694a --- /dev/null +++ b/L2J_Server/dist/doc/L2J_Server_LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/L2J_Server/dist/doc/L2J_Server_README.txt b/L2J_Server/dist/doc/L2J_Server_README.txt new file mode 100644 index 00000000..fd944620 --- /dev/null +++ b/L2J_Server/dist/doc/L2J_Server_README.txt @@ -0,0 +1,292 @@ +NOTE: This short guide is for a L2J Server. http://l2jserver.com + +If you received this file as a part of a packaged or bundled build: + +L2J only supports L2J software obtained directly from L2J sources. + +What this means is, if you obtained L2J from a source other than our SVN or nightly distribution, +you need to get support from where you got the files. +We are unable to provide any technical assistance for unsupported software packages. +The software may have viruses, cause security problems, +or even send your personal information to remote servers without your express consent. +We cannot recommend the use of any of these packages on a server. + +WARNING: L2J Server is not a plug and play type of server. +To setup the server and to run it successfully you need to do a lot of reading. +If reading and learning are not something you like to do, +Stop now. + +==================== +L2J Server +==================== +$Date: 2010/01/06 13:12:11 $ + +TOC: +I. OVERVIEW +II. LEGAL +III. REQUIREMENTS +IV. FIRST STARTUP +V. ADMIN/GM's GUIDE +VI. PLAYER's GUIDE +VII. UPDATING +VIII. TROUBLESHOOTING +IX. CONTACT +X. CONTRIBUTING +XI. BUG REPORTING +XII. CREDITS + + + +==================== +I. OVERVIEW +==================== + +L2J is an Alternative Lineage 2 Game Server written in pure Java for +best compatibility. L2J gives you the possibility to legally host a game +server for this popular Korean MMO created by NCSoft. It is still +unfinished and many features are missing, but L2J Dev team is working +hard on implementing them. L2J Server is distributed under the terms +of GNU/GPL in a hope that open source model is the best for +developing quality software giving everyone a possibility to +participate on development by submitting the code. + + +==================== +II. LEGAL +==================== + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +USA. + +Full GNU/GPL License is included in LICENSE.TXT file. + +Whereas L2J is distributed under the terms of GNU/GPL, we require you to: +a) Preserve login notice. This gives us, L2J Developers, appropriate +credit for our hard work done during our free time without any +revenues. + +b) Do not distribute any extended data files with the server files in +the same archive. NO world content should be incorporated in L2J +distribution. +Server and Datapack may not be not be bundled or packaged. + +==================== +III. REQUIREMENTS +==================== + +OS: +Any OS having Java JDK 1.6 installed and properly configured! + We recommend using SUN JDK available at java.sun.com + +Hardware: +Decent CPU & RAM + +Software: +Java JDK 1.6 +MySql +Datapack + +We recommend: +That you do not play and host the server from the same computer. +Have a good Internet connection - dial up just won't cut it. +Many low cost hosting options are available. + +==================== +IV. FIRST STARTUP +==================== + +Before you can start up the server it is extremely important to read +and get to know the wiki sites. +They contain all the info you need to setup and run a L2J Server. + +L2J Server wiki: Server Guides, How-to's, bugtracker: +https://www.l2jserver.com/trac/wiki + +L2J-Datapack wiki: Datapack Guides, How-to's, bugtracker: +http://www.l2jdp.com/trac/wiki + +This Server distribution does not contain any spawn/drop data or any world +content. (Datapack) + +L2J Server has also a possibility to change exp/sp/drop rates relative +to data in spawnlist/droplist files. To do that, just change the rates +you need in server.properties file found in config folder. + +You should also configure your IP address in server.properties. + +Server uses ports 2106 (LoginServer) and 7777 (GameServer) by +default. If your server runs behind NAT or firewall you will need to +open and/or forward these ports. + + +==================== +V. ADMIN/GM's GUIDE +==================== + +To make someone an admin you need to edit, in the l2jdb database, the desired character in the +characters table, while server is SHUT DOWN! Change the field, accesslevel from 0 to 100 or more. +You may start server after that and the person will have admin privileges. +Note that you must create the account and character before editing. + +Possible access levels: +-100 = banned +0 = normal account/character +51 or more = exempt from maximum connections limit (accounts table only) +100 or more = admin + +More info can be found in the community wiki. + +Admin commands implemented: +(Please see https://l2jserver.com/trac/wiki/GmCommands for more complete list.) +//admin = main GM interface +//gmchat = will send a message to all online GMs +//invul = makes your character untouchable + +==================== +VI. PLAYER's GUIDE +==================== + +CLIENT COMPATIBILITY +Our server is dependent on the official release of the North American lineage2 live client. +http://www.lineage2.com +All help and support will only be for the official client. +Any clients obtained elsewhere, must obtain support from where it was obtained. + +==================== +VI. UPDATING +==================== +It may arrive that you want to update your server to new version while +keeping old accounts. There are few steps you HAVE TO do in order to +keep the data accurate. + +- You should ALWAYS look at timelines before updating, sometimes a file + format may change, so you will need to edit data manually to fit with + new format. +- You should ALWAYS Backup all MySql data. +- You should ALWAYS Backup all Server and Datapack files. +- back up all .properties files (don't forget to check if new server use same + format for those files) +- download & unzip new server code to the Server directory +- download & unzip new datapack code to the Server directory +- edit and run update in the tools folder +- run newly installed server & enjoy ;) + + +==================== +VIII. TROUBLESHOOTING +==================== + +PROBLEM +- Client outputs bunch of messages about missing templates. +SOLUTION +- Check that the datapack has been properly installed. + +PROBLEM +- Message similar to "java is not recognized as internal command", +"java not found" or "unknown command: java" appears. +SOLUTION +- Install java, or, if java is already installed just add your java +binary directory to system PATH setting. If you don't know how to do +that, than DO NOT bother running your own server please. + +PROBLEM +- I can log in but ping is 9999s and I can't get past Server Select. +SOLUTION +- Set up your IP's properly, forward/open good ports if accessing from +outside. (or find server with admin that knows how to do it) + +PROBLEM +- Skills/quests/whatever don't work. +SOLUTION +- Patience brings it's fruits :p + +PROBLEM +- I found a bug. +SOLUTION +- Please refer to BUG REPORTING section of this read-me. + +Further help available at the wiki sites and the forums. + + +==================== +IX. CONTACT +==================== + +Website: http://www.l2jserver.com +Forum: http://www.l2jserver.com/forum +IRC: #l2j @ Freenode (irc.freenode.net) + +Please note that L2J Devs can't help players with connecting issues or +anything related to playing on private servers. If you can't connect, +you should contact your server GM's. We can solve only L2J server +~software~ related issues. We don't have any backdoors or anything +that would enable us GM accounts on every server using L2J, so there's +no point in coming to our channel if you need items/adena/whatever +ingame. + + +==================== +X. CONTRIBUTING +==================== + +Anyone who wants to contribute to the project is encouraged to do so. Java +programming skills are not always required as L2J needs much more than +java code. + +If you created any source code that may be helpful please use the User Contributions +section on our forums. If you contributed good stuff that will be +accepted, you might be invited to join L2J Dev Team. + +People willing to hang on chat and respond to user questions are also +ALWAYS welcome ;) + + +==================== +XI. BUG REPORTING +==================== + +Bugs can be reported on our wiki site. +http://l2jserver.com/trac/newticket +Basic rules for reporting are: + Please report only one bug/issue per ticket!! + You must include the revision (changeset) number when reporting a bug! + "The latest" does not mean anything when 5 more updates have been done since you set up the server. +If you are not sure if it should be reported here, make a post about it in the L2J forum. + +Players should ALWAYS consult bugs with their Admin/GM's and have them report it +on our wiki site. Some bugs may be caused by bad datapack, server +installation or modifications server owner has made. We can't help you +in that case. + +Please use the datapacks bugtracker for reporting datapack bugs. +Please do NOT report bugs related to unofficial add-ons to L2J. L2J +bugtracker is NOT a place to fix that. Contact the person who made +modification instead. + + +==================== +XII. CREDITS +==================== + +Dev team: +http://www.l2jserver.com/forum/memberlist.php?mode=leaders + +Have fun playing L2J ;) +Dev Team + + L2J Server, Copyright (C) 2006-2010 + L2J Server comes with ABSOLUTELY NO WARRANTY. + This is free software, and you are welcome to redistribute it + under certain conditions. \ No newline at end of file diff --git a/L2J_Server/dist/doc/bsh-engine_LICENSE.txt b/L2J_Server/dist/doc/bsh-engine_LICENSE.txt new file mode 100644 index 00000000..51202573 --- /dev/null +++ b/L2J_Server/dist/doc/bsh-engine_LICENSE.txt @@ -0,0 +1,32 @@ +Copyright (c) 2006, Sun Microsystems, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + - Neither the name of the Sun Microsystems, Inc. nor the names of + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/L2J_Server/dist/doc/c3p0_LICENSE.txt b/L2J_Server/dist/doc/c3p0_LICENSE.txt new file mode 100644 index 00000000..f5b77022 --- /dev/null +++ b/L2J_Server/dist/doc/c3p0_LICENSE.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! \ No newline at end of file diff --git a/L2J_Server/dist/doc/ecj_LICENSE.html b/L2J_Server/dist/doc/ecj_LICENSE.html new file mode 100644 index 00000000..e52bf6e2 --- /dev/null +++ b/L2J_Server/dist/doc/ecj_LICENSE.html @@ -0,0 +1,28 @@ + + + + +About + + +

About This Content

+ +

June 2, 2006

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise +indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is available +at http://www.eclipse.org/legal/epl-v10.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is +being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was +provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content +and such source code may be obtained at http://www.eclipse.org.

+ + + \ No newline at end of file diff --git a/L2J_Server/dist/doc/java-engine_LICENSE.txt b/L2J_Server/dist/doc/java-engine_LICENSE.txt new file mode 100644 index 00000000..51202573 --- /dev/null +++ b/L2J_Server/dist/doc/java-engine_LICENSE.txt @@ -0,0 +1,32 @@ +Copyright (c) 2006, Sun Microsystems, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + - Neither the name of the Sun Microsystems, Inc. nor the names of + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/L2J_Server/dist/doc/javolution_LICENSE.txt b/L2J_Server/dist/doc/javolution_LICENSE.txt new file mode 100644 index 00000000..f0868a39 --- /dev/null +++ b/L2J_Server/dist/doc/javolution_LICENSE.txt @@ -0,0 +1,27 @@ +/* + * Javolution - Java(tm) Solution for Real-Time and Embedded Systems + * Copyright (c) 2005-2009, Javolution (http://javolution.org/) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ \ No newline at end of file diff --git a/L2J_Server/dist/doc/jython-engine_LICENSE.txt b/L2J_Server/dist/doc/jython-engine_LICENSE.txt new file mode 100644 index 00000000..51202573 --- /dev/null +++ b/L2J_Server/dist/doc/jython-engine_LICENSE.txt @@ -0,0 +1,32 @@ +Copyright (c) 2006, Sun Microsystems, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + - Neither the name of the Sun Microsystems, Inc. nor the names of + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/L2J_Server/dist/doc/jython_LICENSE.txt b/L2J_Server/dist/doc/jython_LICENSE.txt new file mode 100644 index 00000000..35aa52d1 --- /dev/null +++ b/L2J_Server/dist/doc/jython_LICENSE.txt @@ -0,0 +1,191 @@ +==================================== +The Jython License +==================================== + + +A. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING JYTHON +============================================================================================================== + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +---------------------------------------------------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Jython") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Jython alone +or in any derivative version, provided, however, that PSF's License +Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2007 +Python Software Foundation; All Rights Reserved" are retained in +Jython alone or in any derivative version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Jython or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Jython. + +4. PSF is making Jython available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF JYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF JYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING JYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Jython, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + +Jython 2.0, 2.1 License +-------------------------------------------- + +Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Jython Developers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + - Neither the name of the Jython Developers nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + +JPython 1.1.x Software License. +______________________________________________________________________ + + 1. This LICENSE AGREEMENT is between the Corporation for National Research + Initiatives, having an office at 1895 Preston White Drive, Reston, VA + 20191 ("CNRI"), and the Individual or Organization ("Licensee") + accessing and using JPython version 1.1.x in source or binary form and + its associated documentation as provided herein ("Software"). + + 2. Subject to the terms and conditions of this License Agreement, CNRI + hereby grants Licensee a non-exclusive, non-transferable, royalty-free, + world-wide license to reproduce, analyze, test, perform and/or display + publicly, prepare derivative works, distribute, and otherwise use the + Software alone or in any derivative version, provided, however, that + CNRI's License Agreement and CNRI's notice of copyright, i.e., + "Copyright ©1996-1999 Corporation for National Research Initiatives; + All Rights Reserved" are both retained in the Software, alone or in any + derivative version prepared by Licensee. + + Alternatively, in lieu of CNRI's License Agreement, Licensee may + substitute the following text (omitting the quotes), provided, however, + that such text is displayed prominently in the Software alone or in any + derivative version prepared by Licensee: "JPython (Version 1.1.x) is + made available subject to the terms and conditions in CNRI's License + Agreement. This Agreement may be located on the Internet using the + following unique, persistent identifier (known as a handle): + 1895.22/1006. The License may also be obtained from a proxy server on + the Web using the following URL: http://hdl.handle.net/1895.22/1006." + + 3. In the event Licensee prepares a derivative work that is based on or + incorporates the Software or any part thereof, and wants to make the + derivative work available to the public as provided herein, then + Licensee hereby agrees to indicate in any such work, in a prominently + visible way, the nature of the modifications made to CNRI's Software. + + 4. Licensee may not use CNRI trademarks or trade name, including JPython + or CNRI, in a trademark sense to endorse or promote products or + services of Licensee, or any third party. Licensee may use the mark + JPython in connection with Licensee's derivative versions that are + based on or incorporate the Software, but only in the form + "JPython-based ___________________," or equivalent. + + 5. CNRI is making the Software available to Licensee on an "AS IS" basis. + CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY + OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY + REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY + PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE + ANY THIRD PARTY RIGHTS. + + 6. CNRI SHALL NOT BE LIABLE TO LICENSEE OR OTHER USERS OF THE SOFTWARE FOR + ANY INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. SOME STATES DO NOT + ALLOW THE LIMITATION OR EXCLUSION OF LIABILITY SO THE ABOVE DISCLAIMER + MAY NOT APPLY TO LICENSEE. + + 7. This License Agreement may be terminated by CNRI (i) immediately upon + written notice from CNRI of any material breach by the Licensee, if the + nature of the breach is such that it cannot be promptly remedied; or + (ii) sixty (60) days following notice from CNRI to Licensee of a + material remediable breach, if Licensee has not remedied such breach + within that sixty-day period. + + 8. This License Agreement shall be governed by and interpreted in all + respects by the law of the State of Virginia, excluding conflict of law + provisions. Nothing in this Agreement shall be deemed to create any + relationship of agency, partnership, or joint venture between CNRI and + Licensee. + + 9. By clicking on the "ACCEPT" button where indicated, or by installing, + copying or otherwise using the Software, Licensee agrees to be bound by + the terms and conditions of this License Agreement. + + [ACCEPT BUTTON] + +B. HISTORY OF THE SOFTWARE +======================================================= + +JPython was created in late 1997 by Jim Hugunin. Jim was also the +primary developer while he was at CNRI. In February 1999 Barry Warsaw +took over as primary developer and released JPython version 1.1. + +In October 2000 Barry helped move the software to SourceForge +where it was renamed to Jython. Jython 2.0 and 2.1 were developed +under the Jython specific license below. + +From the 2.2 release on, Jython contributors have signed +Python Software Foundation contributor agreements and releases are +covered under the Python Software Foundation license version 2. + +The standard library is covered by the Python Software Foundation +license as well. See the Lib/LICENSE file for details. + +The zxJDBC package was written by Brian Zimmer and originally licensed +under the GNU Public License. The package is now covered by the Jython +Software License. + +The command line interpreter is covered by the Apache Software +License. See the org/apache/LICENSE file for details. \ No newline at end of file diff --git a/L2J_Server/dist/doc/mysql-connector-java_LICENSE.txt b/L2J_Server/dist/doc/mysql-connector-java_LICENSE.txt new file mode 100644 index 00000000..621557bb --- /dev/null +++ b/L2J_Server/dist/doc/mysql-connector-java_LICENSE.txt @@ -0,0 +1,43 @@ +MySQL Connector/J @MYSQL_CJ_VERSION@ (formerly MM.MySQL) +Sun Microsystem's JDBC Driver for MySQL +Copyright 2003-2008 MySQL AB, 2008 Sun Microsystems + +CONTENTS + +* License +* Documentation Location + + +LICENSE + +MySQL Connector/J is licensed under the GPL or a commercial license +from Sun Microsystems. + +If you have licensed this product under the GPL, please see the COPYING +file for more information. + +There are special exceptions to the terms and conditions of the GPL +as it is applied to this software. View the full text of the +exception in file EXCEPTIONS-CONNECTOR-J in the directory of this +software distribution. + +If you have licensed this product under a commercial license from +MySQL AB, please see the file "LICENSE.mysql" that comes with this +distribution for the terms of the license. + +If you need non-GPL licenses for commercial distribution please contact +me or . + + +DOCUMENTATION LOCATION + +The documentation formerly contained in this file has moved into the +'doc' directory, where it is available in HTML, PDF and plaintext +forms. + +You may also find the latest copy of the documentation on the MySQL +website at http://dev.mysql.com/doc/refman/5.0/en/connector-j.html + +-- +This software is OSI Certified Open Source Software. +OSI Certified is a certification mark of the Open Source Initiative. \ No newline at end of file diff --git a/L2J_Server/dist/doc/trove_LICENSE.txt b/L2J_Server/dist/doc/trove_LICENSE.txt new file mode 100644 index 00000000..c6f39202 --- /dev/null +++ b/L2J_Server/dist/doc/trove_LICENSE.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/L2J_Server/dist/game/GameServer_loop.sh b/L2J_Server/dist/game/GameServer_loop.sh new file mode 100644 index 00000000..eecd45ae --- /dev/null +++ b/L2J_Server/dist/game/GameServer_loop.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# exit codes of GameServer: +# 0 normal shutdown +# 2 reboot attempt + +while :; do + [ -f log/java0.log.0 ] && mv log/java0.log.0 "log/`date +%Y-%m-%d_%H-%M-%S`_java.log" + [ -f log/stdout.log ] && mv log/stdout.log "log/`date +%Y-%m-%d_%H-%M-%S`_stdout.log" + java -Djava.util.logging.manager=com.l2jserver.util.L2LogManager -Xms1024m -Xmx1024m -cp ./../libs/*:l2jserver.jar com.l2jserver.gameserver.GameServer > log/stdout.log 2>&1 + [ $? -ne 2 ] && break +# /etc/init.d/mysql restart + sleep 10 +done diff --git a/L2J_Server/dist/game/config/Character.properties b/L2J_Server/dist/game/config/Character.properties new file mode 100644 index 00000000..92a208a2 --- /dev/null +++ b/L2J_Server/dist/game/config/Character.properties @@ -0,0 +1,707 @@ +# --------------------------------------------------------------------------- +# Alternative Character Settings +# --------------------------------------------------------------------------- +# This properties file is solely for the purpose of Character modifications that directly influence them. +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# AccessLevel +# --------------------------------------------------------------------------- +# This option will set the default AccessLevel for MasterAccess. +# Please be careful with this setting. Characters set to this AccessLevel will have the right to execute every AdminCommand in game. +# Default: 127 (Maximum value: 255) +MasterAccessLevel = 127 + +# Name color for those matching the above MasterAccess AccessLevel. +# Default: 00CCFF +MasterNameColor = 00CCFF + +# Title color for those matching the above MasterAccess AccessLevel. +# Default: 00CCFF +MasterTitleColor = 00CCFF + +# --------------------------------------------------------------------------- +# Statistics +# --------------------------------------------------------------------------- +# This option, if enabled, will force a character to de-level if the characters' experience is below their level after losing experience on death. If this is set to False, the character will not de-level even if their Experience is below their level after death. +# Default: True +Delevel = True + +# This option enable check for all player skills for skill level. +# If player level is lower than skill learn level - 9, skill level is decreased to next possible level. +# If there is no possible level, skill is removed from player. +DecreaseSkillOnDelevel = True + +# Weight limit multiplier. Example: Setting this to 5 will give players 5x the normal weight limit. +# Default: 1 +AltWeightLimit = 1 + +# Run speed modifier. Example: Setting this to 5 will give players +5 to their running speed. +# Default: 0 +RunSpeedBoost = 0 + +# Chance of receiving the Death Penalty debuff when killed by a mob. +# Default: 20 +DeathPenaltyChance = 20 + +# Percent of HP, MP, and CP which is restored on character revival. +# Use 0 to disable restore +# Default: 0, 65, 0 +RespawnRestoreCP = 0 +RespawnRestoreHP = 65 +RespawnRestoreMP = 0 + +# Percent of HP, MP, and CP regeneration for players. +# Example: Setting HP to 10 will cause player HP to regenerate 90% slower than normal. +# Default: 100, 100, 100 +HpRegenMultiplier = 100 +MpRegenMultiplier = 100 +CpRegenMultiplier = 100 + +# Decrease CP by 10 every normal hit a player do +# Default: False +AltGameTiredness = False + +# Allow player to learn transformations without quest. +# Default: False +AltTransformationWithoutQuest = False + +# --------------------------------------------------------------------------- +# Skills & Effects +# --------------------------------------------------------------------------- +# When this is enabled it will read the "SkillDurationList" option. +# This will basically overlook the "time = x" in the skill XMLs so that you do not need to modify the L2J Datapack XMLs to increase skill duration. +# Default: False +EnableModifySkillDuration = False +# Skill duration list +# Format: skillid,newtime;skillid2,newtime2... +# Example: +# This enable 1h(3600) duration for songs, the "\"indicates new line, +# and is only set for formating purposes. +# SkillDurationList = 264,3600;265,3600;266,3600;267,3600;268,3600;\ +# 269,3600;270,3600;304,3600;305,1200;306,3600;308,3600;349,3600;\ +# 363,3600;364,3600 +SkillDurationList = + +# When this is enabled it will read the "SkillReuseList" option. +EnableModifySkillReuse = False +# Format: skillid,newDelayTime;skillid,newDelayTime2 (See skillDuration for examples) +SkillReuseList = + +# If it's true all class skills will be delivered upon level up and login. +# Default: False +AutoLearnSkills = False + +# If it's true skills from forgotten scrolls will be delivered upon level up and login, require AutoLearnSkills. +# Default: False +AutoLearnForgottenScrollSkills = False + +# Default: False +AutoLootHerbs = False + +# Maximum number of buffs and songs/dances. +# Remember that Divine Inspiration will give players 4 additional buff slots on top of the number specified in "maxbuffamount". +# Default: 20, 12 +maxbuffamount = 20 +maxdanceamount = 12 + +# Allow players to cancel dances/songs via Alt+click on buff icon +# Default: False +DanceCancelBuff = False + +# This option enables/disables additional MP consume for dances and songs. +# Default: True +DanceConsumeAdditionalMP = True + +# This option allows a player to automatically learn Divine Inspiration. +# This is not included in AutoLearnSkills above. +# Default: False +AutoLearnDivineInspiration = False + +# This is to allow a character to be canceled during bow use, skill use, or both. +# Available Options: bow, cast, all +# Default: cast +AltGameCancelByHit = cast + +# If True, when effects of the same stack group are used, lesser effects will be canceled if stronger effects are used. Otherwise stacking of things like multiple levels of the same buff will be allowed. +# Default: True +CancelLesserEffect = True + +# This option, if enabled, will allow magic to fail, and if disabled magic damage will always succeed with a 100% chance. +# Default: True +MagicFailures = True + +# Protection from aggressive mobs after getting up from fake death. +# The value is specified in seconds. +# Default: 0 +PlayerFakeDeathUpProtection = 0 + +# This option is to enable or disable the storage of buffs/debuffs among other effects. +# Default: True +StoreSkillCooltime = True + +# This option is to enable or disable the storage of buffs/debuffs among other effects during +# a subclass change +# Deault: False +SubclassStoreSkillCooltime = False + +# These are alternative rules for shields. +# If True and they block: +# The damage is powerAtk-shieldDef, +# If False and they block: +# The damage is powerAtk / (shieldDef + powerDef) +# Default: False +AltShieldBlocks = False + +# This is the percentage for perfect shield block rate. +# DEFAULT NEEDS TO BE VERIFIED, MUST BE CHANGED HERE AND IN CONFIG.JAVA IF NOT CORRECT +# Default: 10 +AltPerfectShieldBlockRate = 10 + +# --------------------------------------------------------------------------- +# Class, Sub-class and skill learning options +# --------------------------------------------------------------------------- +# Allow use of Event Managers for changing occupation without any quests. +# Default: False +AllowClassMasters = False + +# ConfigClassMaster=1;[57(100000)];[];2;[57(1000000)];[];3;[57(10000000)],[5575(1000000)];[6622(1)] +# 1st occupation change for 100.000 Adena (item id 57) +# 2nd occupation change for 1.000.0000 Adena (item id 57) +# 3rd occupation change for 10.000.0000 Adena (item id 57) and 1.000.000 Ancient Adena (item id 5575) +# on 3rd occupation change player will be rewarded with 1 Book of Giants (item id 6622) +# ConfigClassMaster=1;[];[];2;[];[];3;[];[] +# 1st, 2nd, 3rd occupation change for free, without rewards. +# ConfigClassMaster=1;[];[];2;[];[] +# Allow only first and second change. +ConfigClassMaster = 1;[];[];2;[];[];3;[];[] + +# Class Masters will allow changing to any occupation on any level inside class tree +# For example, Dwarven Fighter will be able to advance to: +# Artisan, Scavenger, Warsmith, Bounty Hunter, Maestro, Fortune Seeker. +# But Warsmith will be able to change only to Maestro. +# Default = False +AllowEntireTree = False + +# When character reach levels 20,40,76 he will receive tutorial page +# with list of the all possible variants, and can select and immediately +# change to the new occupation, or decide to choose later (on next login). +# Can be used with or without classic Class Masters. +# Default = False +AlternateClassMaster = False + +# Require life crystal needed to learn clan skills. +# Default: True +LifeCrystalNeeded = True + +# Require book needed to enchant skills. +# Default: True +EnchantSkillSpBookNeeded = True + +# Require spell book needed to learn Divine Inspiration. +# Default: True +DivineInspirationSpBookNeeded = True + +# When enabled, the following will be true: +# All classes can learn all skills. +# Skills of another class costs x2 SP to learn. +# Skills of another race costs x2 SP to learn. +# Skills of fighters/mages costs x3 SP to learn. +# Default: False +AltGameSkillLearn = False + +# Allow player to sub-class without checking for unique quest items. +# Default: False +AltSubClassWithoutQuests = False + +# Allow player to add/change subclass at all village master +# Default: False +AltSubclassEverywhere = False + +# Fee to remove Transfer skills. +# Default: 10000000 +FeeDeleteTransferSkills = 10000000 + +# Fee to remove Sub-Class skills. +# Default: 10000000 +FeeDeleteSubClassSkills = 10000000 + +# --------------------------------------------------------------------------- +# Vitality configuration +# --------------------------------------------------------------------------- +# Enables vitality system +# Default: True +EnableVitality = True + +# Do you want players to recover their vitality when they reconnect? +# This is calculated with the time they've been offline +# Actual Time - Last Time Online / 1000 x rate recovery on reconnect +# Notes: +# Works only if EnableVitality = True +# Default: True +RecoverVitalityOnReconnect = True + +# Option to set a lower vitality at character creation. +# Vitality needs to be enabled, and startingpoints needs to be lower +# than max-vitality points. +# Default: 20000 +StartingVitalityPoints = 20000 + + +# --------------------------------------------------------------------------- +# Limits +# --------------------------------------------------------------------------- +# Maximum character running speed. +# Default: 250 +MaxRunSpeed = 250 + +# Maximum character Physical Critical Rate. (10 = 1%) +# Default: 500 +MaxPCritRate = 500 + +# Maximum character Magic Critical Rate. (10 = 1%) +# Default: 200 +MaxMCritRate = 200 + +# Maximum character Attack Speed. +# Default: 1500 +MaxPAtkSpeed = 1500 + +# Maximum character Cast Speed. +# Default: 1999 +MaxMAtkSpeed = 1999 + +# Maximum character Evasion. +# Default: 250 +MaxEvasion = 250 + +# Maximum number of allowed subclasses for every player. +# Default: 3 +MaxSubclass = 3 + +# Maximum subclass level. +# Default: 80 +MaxSubclassLevel = 80 + +# Maximum number of allowed slots for Private Stores Sell. +# Other means all the other races aside from Dwarf. +# Default: 4, 3 +MaxPvtStoreSellSlotsDwarf = 4 +MaxPvtStoreSellSlotsOther = 3 + +# Maximum number of allowed slots for Private Stores Buy. +# Other means all the other races aside from Dwarf. +# Default: 5, 4 +MaxPvtStoreBuySlotsDwarf = 5 +MaxPvtStoreBuySlotsOther = 4 + +# This will control the inventory space limit (NOT WEIGHT LIMIT). +# Default: 80, 100, 250 +MaximumSlotsForNoDwarf = 80 +MaximumSlotsForDwarf = 100 +MaximumSlotsForGMPlayer = 250 +MaximumSlotsForQuestItems = 100 + +# This will control a character's warehouse capacity. +# Notes: +# This must be LESS then 300 or the client will crash. +# Default: 120, 100, 200, 20 +MaximumWarehouseSlotsForDwarf = 120 +MaximumWarehouseSlotsForNoDwarf = 100 +MaximumWarehouseSlotsForClan = 200 + + +# --------------------------------------------------------------------------- +# Enchanting +# --------------------------------------------------------------------------- +# This controls the chance an item has to break if it is enchanted. +# This chance is in %, so if you set this to 100%, enchants will always succeed. +# DEFAULT NEEDS TO BE VERIFIED, MUST BE CHANGED HERE AND IN CONFIG.JAVA IF NOT CORRECT +# Default: 66,66,66 +EnchantChanceWeapon = 66 +EnchantChanceArmor = 66 +EnchantChanceJewelry = 66 +EnchantChanceElementStone = 50 +EnchantChanceElementCrystal = 30 +EnchantChanceElementJewel = 20 +EnchantChanceElementEnergy = 10 + +# DEFAULT NEEDS TO BE VERIFIED, MUST BE CHANGED HERE AND IN CONFIG.JAVA IF NOT CORRECT +# Default: 66,66,66 +BlessedEnchantChanceWeapon = 66 +BlessedEnchantChanceArmor = 66 +BlessedEnchantChanceJewelry = 66 + +# This is the enchant limit, if set to 0, there will be no limit. +# Example: If this is set to 10, the maximum enchant will be 10. +# Default: 0, 0, 0 +EnchantMaxWeapon = 0 +EnchantMaxArmor = 0 +EnchantMaxJewelry = 0 + +# This is the maximum safe enchant. +# Notes: +# If EnchantSafeMax is set to for 8 the item will be safely enchanted to 8 regardless of the max enchant value set above. +# EnchantSafeMaxFull is for full body armor (upper and lower). +# These values should always be greater than 0. +# Default: 3, 4 +EnchantSafeMax = 3 +EnchantSafeMaxFull = 4 + +# List of non-enchantable items. +# Currently apprentice, travelers weapons and Pailaka items +# Default: 7816,7817,7818,7819,7820,7821,7822,7823,7824,7825,7826,7827,7828,7829,7830,7831,13293,13294,13296 +EnchantBlackList = 7816,7817,7818,7819,7820,7821,7822,7823,7824,7825,7826,7827,7828,7829,7830,7831,13293,13294,13296 + +# --------------------------------------------------------------------------- +# Augmenting +# --------------------------------------------------------------------------- +# These control the chance to get a skill in the augmentation process. +# Default: 15, 30, 45, 60 +AugmentationNGSkillChance = 15 +AugmentationMidSkillChance = 30 +AugmentationHighSkillChance = 45 +AugmentationTopSkillChance = 60 +# Accessory augmentation skills currently disabled +AugmentationAccSkillChance = 0 + +# This controls the chance to get a base stat modifier in the augmentation process. +# Notes: +# This has no dependancy on the grade of Life Stone. +# Default: 1 +AugmentationBaseStatChance = 1 + +# These control the chance to get a glow effect in the augmentation process. +# Notes: +# No/Mid Grade Life Stone can not have glow effect if you do not get a skill or base stat modifier. +# Default: 0, 40, 70, 100 +AugmentationNGGlowChance = 0 +AugmentationMidGlowChance = 40 +AugmentationHighGlowChance = 70 +AugmentationTopGlowChance = 100 + +# List of non-augmentable items, currently contains only Grand Boss jewels +# Shadow, common, time-limited, hero, pvp, wear items are hardcoded, as well as all etcitems. +# Rods can't be augmented too. +# Default: 6656,6657,6658,6659,6660,6661,6662,8191,10170,10314,13740,13741,13742,13743,13744,13745,13746,13747,13748,14592,14593,14594,14595,14596,14597,14598,14599,14600,14664,14665,14666,14667,14668,14669,14670,14671,14672,14801,14802,14803,14804,14805,14806,14807,14808,14809,15282,15283,15284,15285,15286,15287,15288,15289,15290,15291,15292,15293,15294,15295,15296,15297,15298,15299,16025,16026 +AugmentationBlackList = 6656,6657,6658,6659,6660,6661,6662,8191,10170,10314,13740,13741,13742,13743,13744,13745,13746,13747,13748,14592,14593,14594,14595,14596,14597,14598,14599,14600,14664,14665,14666,14667,14668,14669,14670,14671,14672,14801,14802,14803,14804,14805,14806,14807,14808,14809,15282,15283,15284,15285,15286,15287,15288,15289,15290,15291,15292,15293,15294,15295,15296,15297,15298,15299,16025,16026 + +# --------------------------------------------------------------------------- +# Karma +# --------------------------------------------------------------------------- +# Karma player can be killed in Peace zone. +# Default: False +AltKarmaPlayerCanBeKilledInPeaceZone = False + +# Karma player can use GateKeeper. +# Default: False +AltKarmaPlayerCanUseGK = False + +# Karma player can use escape and recall skills. +# Default: True +AltKarmaPlayerCanTeleport = True + +# Karma player can shop. +# Default: True +AltKarmaPlayerCanShop = True + +# Karma player can trade. +# Default: True +AltKarmaPlayerCanTrade = True + +# Karma player can use warehouse. +# Default: True +AltKarmaPlayerCanUseWareHouse = True + + +# --------------------------------------------------------------------------- +# Fame +# --------------------------------------------------------------------------- +# The maximum number of Fame points a player can have +# Default: 50000 +MaxPersonalFamePoints = 50000 + +# How frequently the player gets Fame points while in a Fortress Siege zone +# Default: 300s (5 min) +FortressZoneFameTaskFrequency = 300 + +# How much Fame aquired while in a Fortress Siege Zone +# Default: 31 +FortressZoneFameAquirePoints = 31 + +# How frequently the player gets Fame points while in a Castle Siege zone +# Default: 300s (5 min) +CastleZoneFameTaskFrequency = 300 + +# How much Fame aquired while in a Castle Siege Zone +# Default: 125 +CastleZoneFameAquirePoints = 125 + +# Dead players can receive fame. +# Default: True +FameForDeadPlayers = True + +# --------------------------------------------------------------------------- +# Crafting +# --------------------------------------------------------------------------- +# Option to enable or disable crafting. +# Default: True +CraftingEnabled = True + +# Enable/Disable crafting of Masterwork items (If set to true, masterwork items can be created when crafting). +# Default: True +CraftMasterwork = True + +# Limits for recipes +# Default: 50, 50 +DwarfRecipeLimit = 50 +CommonRecipeLimit = 50 + +# Alternative crafting rules. If enabled, the following will be true: +# Crafting takes time. +# Players get EXP/SP for crafting. +# Default: False +AltGameCreation = False + +# Crafting Time multiplier. +# The higher the number, the more time the crafting process takes. XP/SP reward increases with time. +# Default: 1 +AltGameCreationSpeed = 1 + +# Additional crafting XP/SP rate multiplier. +# Default: 1, 1 +AltGameCreationXpRate = 1 +AltGameCreationSpRate = 1 + +# Additional XP/SP rate multiplier when creating rare item. +# Default: 2 +AltGameCreationRareXpSpRate = 2 + +# If set to False, blacksmiths don't take recipes from players inventory when crafting. +# Default: True +AltBlacksmithUseRecipes = True + +# Store/Restore Dwarven Manufacture list +# Keep manufacture shoplist after relog +# Default: False +StoreRecipeShopList = False + +# --------------------------------------------------------------------------- +# Clan +# --------------------------------------------------------------------------- +# Number of days you have to wait before joining another clan. +# Default: 1 +DaysBeforeJoinAClan = 1 + +# Number of days you have to wait before creating a new clan. +# Default: 10 +DaysBeforeCreateAClan = 10 + +# Number of days it takes to dissolve a clan. +# Default: 7 +DaysToPassToDissolveAClan = 7 + +# Number of days before joining a new alliance when clan voluntarily leave an alliance. +# DEFAULT NEEDS TO BE VERIFIED, MUST BE CHANGED HERE AND IN CONFIG.JAVA IF NOT CORRECT +# Default: 1 +DaysBeforeJoinAllyWhenLeaved = 1 + +# Number of days before joining a new alliance when clan was dismissed from an alliance. +# DEFAULT NEEDS TO BE VERIFIED, MUST BE CHANGED HERE AND IN CONFIG.JAVA IF NOT CORRECT +# Default: 1 +DaysBeforeJoinAllyWhenDismissed = 1 + +# Number of days before accepting a new clan for alliance when clan was dismissed from an alliance. +# DEFAULT NEEDS TO BE VERIFIED, MUST BE CHANGED HERE AND IN CONFIG.JAVA IF NOT CORRECT +# Default: 1 +DaysBeforeAcceptNewClanWhenDismissed = 1 + +# Number of days before creating a new alliance after dissolving an old alliance. +# DEFAULT NEEDS TO BE VERIFIED, MUST BE CHANGED HERE AND IN CONFIG.JAVA IF NOT CORRECT +# Default: 1 +DaysBeforeCreateNewAllyWhenDissolved = 1 + +# Maximum number of clans in alliance. +# Default: 3 +AltMaxNumOfClansInAlly = 3 + +# Allow clan members to withdraw from the clan warehouse. +# Default: False +AltMembersCanWithdrawFromClanWH = False + +# Remove castle circlets after a clan loses their castle or a player leaves a clan. +# Default: True +RemoveCastleCirclets = True + +# Number of members needed to request a clan war. +# Default: 15 +AltClanMembersForWar = 15 + + +# --------------------------------------------------------------------------- +# Party +# --------------------------------------------------------------------------- +# CONFUSING(nothing todo with party) -> When you made damage to a mob +# and are inside this range, you will be considered as player to reward. +# Checks for party range to mob to calculate rewards(exp, items). +# Default: 1600 +AltPartyRange = 1600 + +# 1. Used for adena distribution in party +# 2. Used to handle random and by turn party loot +# Default: 1400 +AltPartyRange2 = 1400 + + +# --------------------------------------------------------------------------- +# Random +# --------------------------------------------------------------------------- +# This is the amount of adena that a new character starts their character with. +# Default: 0 +StartingAdena = 0 + +# This is the starting level of the new character. +# Default: 1 +StartingLevel = 1 + +# This is the amount of SP that a new character starts their character with. +# Default: 0 +StartingSP = 0 + +# This option, when set to True, will enable automatically picking up items. +# If set False it will force the player to pickup dropped items from mobs. +# This excludes herbs mentioned above and items from Raid/GrandBosses with minions. +# Default: False +AutoLoot = False + +# This option, when set to True, will enable automatically picking up items from Raid/GrandBosses with minions. +# If set False it will force the player to pickup dropped items from bosses. +# This excludes herbs mentioned above and items from mobs. +# Default: False +AutoLootRaids = False + +# Delay for raid drop items loot privilege +# Require Command Channel , check next option +# Value is in seconds +# Default: 900 (15min) +RaidLootRightsInterval = 900 + +# Minimal size of Command Channel for apply raid loot privilege +# Default: 45 +RaidLootRightsCCSize = 45 + +# This is the time in seconds that it will take for the player command "/unstuck" to activate. +# Default: 300 +UnstuckInterval = 300 + +# Teleport Watchdog Timeout (seconds) +# Player forced to appear if remain in teleported state longer than timeout +# Does not set too low, recommended value 60s. +# This time is in seconds, leave it at 0 if you want this feature disabled. +# Default: 0 +TeleportWatchdogTimeout = 0 + +# After a player teleports, this is the time the player is protected. +# This time is in seconds, leave it at 0 if you want this feature disabled. +# Retail (Since GE): 600 (10 minutes) +# Default: 600 +PlayerSpawnProtection = 600 + +# Spawn protection should disappear with any action with the exception +# of the item usage from items in this list. +# Format: itemId,itemId,itemId,.... +# Default: 0 +PlayerSpawnProtectionAllowedItems = 0 + +# Teleport spawn protection time. It will protect the player in the +# teleport spawn for the given time. 0 to disable feature +PlayerTeleportProtection = 0 + +# If enabled, players respawn in town on different locations defined in zone.xml for given town. +# If disabled the first spawn location from zone.xml is used. +# Default: True +RandomRespawnInTownEnabled = True + +# This will allow a random offset from the base teleport location coordinates based on a maximum offset. +# Default: True +OffsetOnTeleportEnabled = True + +# Maximum offset for base teleport location when OffsetOnTeleportEnabled is enabled . +# Default: 50 +MaxOffsetOnTeleport = 50 + +# Restores the player to their previous instance (ie. an instanced area/dungeon) on EnterWorld. +# Default: False +RestorePlayerInstance = False + +# Set whether summon skills can be used to summon players inside an instance. +# When enabled individual instances can have summoning disabled in instance xml's. +# DEFAULT NEEDS TO BE VERIFIED, MUST BE CHANGED HERE AND IN CONFIG.JAVA IF NOT CORRECT +# Default: ? +AllowSummonToInstance = True + +# This option is to enable or disable the use of in game petitions. +# The MaxPetitionsPerPlayer is the amount of petitions a player can make. +# The MaximumPendingPetitions is the total amount of petitions in the server. +# Note: +# Logically, MaximumPendingPetitions must be higher then MaxPetitionsPerPlayer. +# Default: True, 5, 25 +PetitioningAllowed = True +MaxPetitionsPerPlayer = 5 +MaxPetitionsPending = 25 + +# Free teleporting around the world. +# Default: False +AltFreeTeleporting = False + +# Allow character deletion after days set below. To disallow character deletion, set this equal to 0. +# Default: 7 +DeleteCharAfterDays = 7 + +# Alternative Xp/Sp rewards, if not 0, then calculated as 2^((mob.level-player.level) / coef). Coef are the 2 numbers set below. +# A few examples for "AltGameExponentXp = 5." and "AltGameExponentSp = 3.": +# diff = 0 (player and mob has the same level), XP bonus rate = 1, SP bonus rate = 1 +# diff = 3 (mob is 3 levels above), XP bonus rate = 1.52, SP bonus rate = 2 +# diff = 5 (mob is 5 levels above), XP bonus rate = 2, SP bonus rate = 3.17 +# diff = -8 (mob is 8 levels below), XP bonus rate = 0.4, SP bonus rate = 0.16 +# Default: 0, 0 +AltGameExponentXp = 0 +AltGameExponentSp = 0 + + +# PARTY XP DISTRIBUTION +# With "auto method" member is cut from Exp/SP distribution when his share is lower than party bonus acquired for him (30% for 2 member party). +# In that case he will not receive any Exp/SP from party and is not counted for party bonus. +# If you don't want to have a cutoff point for party members' XP distribution, set the first option to "none". +# Available Options: auto, level, percentage, none +# Default: level +PartyXpCutoffMethod = level + +# This option takes effect when "percentage" method is chosen. Don't use high values for this! +# Default: 3.0 +PartyXpCutoffPercent = 3.0 + +# This option takes effect when "level" method is chosen. Don't use low values for this! +# Default: 20 +PartyXpCutoffLevel = 20 + +# Disable tutorial on new player enter into Game +# Please remember its sometimes important to novice players +# Default: False +DisableTutorial = False + +# Expertise penalty +# If disabled, player will not receive penalty for equip higher grade items +# Default: True +ExpertisePenalty = True + +# Player can in client define his own key mapping and for save it must be stored server side. +# Disabled until confirmed as working. +# Default: False +StoreCharUiSettings = False + +# Character name restriction +# Disallow characters to have a name which contains the words. +# Split them with ",". Example: announcements,announce... +ForbiddenNames = annou,ammou,amnou,anmou,anou,amou diff --git a/L2J_Server/dist/game/config/CommunityServer.properties b/L2J_Server/dist/game/config/CommunityServer.properties new file mode 100644 index 00000000..045f701c --- /dev/null +++ b/L2J_Server/dist/game/config/CommunityServer.properties @@ -0,0 +1,47 @@ +# This is the Community Board server configuration file. + +EnableCommunityBoard = False +CommunityServerHostname = localhost +CommunityServerPort = 9013 +CommunityServerSqlDpId = 200 +CommunityServerHexId = 123456 + +# NetCon configurations +# ================================================================ +# TCP Server start +# ================================================================ + +# ExternalHostAddress: Use * bind all available IPs +# Default: * +ExternalHostAddress = * +# ExternalPort: Use 0 bind all available Ports +# Default: 0 +ExternalPort = 0 +# ConnectionQueue: Maximum connections. +# Default: 50 +ConnectionQueue = 50 + +# Default: False +FloodProtectionEnabled = False +# Default: 15 +FastConnectionLimit = 15 +# Default: 350 +FastConnectionTime = 350 +# Default: 700 +NormalConnectionTime = 700 +# Default: 50 +MaxConnectionperIP = 50 +# Default: False +IPBannEnabled = False +# Default: +IPBannList = + +# ================================================================ +# TCP Client start +# ================================================================ +# SendBufferSize: DO NOT TOUCH! +# Default: 8192 +SendBufferSize = 8192 +# SendBufferSize: DO NOT TOUCH! +# Default: 8192 +ReceiveBufferSize = 8192 \ No newline at end of file diff --git a/L2J_Server/dist/game/config/Feature.properties b/L2J_Server/dist/game/config/Feature.properties new file mode 100644 index 00000000..0bb5619c --- /dev/null +++ b/L2J_Server/dist/game/config/Feature.properties @@ -0,0 +1,360 @@ +# --------------------------------------------------------------------------- +# Alternative Feature Settings +# --------------------------------------------------------------------------- +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# Castle +# --------------------------------------------------------------------------- +# Siege Time rules +# Which values can change Castle Lords (day,hour,minute) +# Default: hour +CLSetSiegeTimeList = hour + +# Which hours can change Castle Lords +# Default: 16,20 +SiegeHourList = 16,20 + +# Teleport Function price +# Price = 7 days +CastleTeleportFunctionFeeRatio = 604800000 +CastleTeleportFunctionFeeLvl1 = 7000 +CastleTeleportFunctionFeeLvl2 = 14000 + +# Support magic buff price +# Price = 1 day +CastleSupportFunctionFeeRatio = 86400000 +CastleSupportFeeLvl1 = 7000 +CastleSupportFeeLvl2 = 21000 +CastleSupportFeeLvl3 = 37000 +CastleSupportFeeLvl4 = 52000 + +# MP Regeneration price +# Price = 1 day +CastleMpRegenerationFunctionFeeRatio = 86400000 +CastleMpRegenerationFeeLvl1 = 2000 +CastleMpRegenerationFeeLvl2 = 6500 +CastleMpRegenerationFeeLvl3 = 13750 +CastleMpRegenerationFeeLvl4 = 20000 + +# Hp Regeneration price +# Price = 1 day +CastleHpRegenerationFunctionFeeRatio = 86400000 +CastleHpRegenerationFeeLvl1 = 1000 +CastleHpRegenerationFeeLvl2 = 1500 +CastleHpRegenerationFeeLvl3 = 2250 +CastleHpRegenerationFeeLvl4 = 3270 +CastleHpRegenerationFeeLvl5 = 5166 + +# Exp Regeneration price +# Price = 1 day +CastleExpRegenerationFunctionFeeRatio = 86400000 +CastleExpRegenerationFeeLvl1 = 9000 +CastleExpRegenerationFeeLvl2 = 15000 +CastleExpRegenerationFeeLvl3 = 21000 +CastleExpRegenerationFeeLvl4 = 30000 + + +# --------------------------------------------------------------------------- +# Clan Hall +# --------------------------------------------------------------------------- +# Teleport Function price +# Price = 7 days +ClanHallTeleportFunctionFeeRatio = 604800000 +ClanHallTeleportFunctionFeeLvl1 = 7000 +ClanHallTeleportFunctionFeeLvl2 = 14000 + +# Support magic buff price +# Price = 1 day +ClanHallSupportFunctionFeeRatio = 86400000 +ClanHallSupportFeeLvl1 = 2500 +ClanHallSupportFeeLvl2 = 5000 +ClanHallSupportFeeLvl3 = 7000 +ClanHallSupportFeeLvl4 = 11000 +ClanHallSupportFeeLvl5 = 21000 +ClanHallSupportFeeLvl6 = 36000 +ClanHallSupportFeeLvl7 = 37000 +ClanHallSupportFeeLvl8 = 364000 + +# MP Regeneration price +# Price = 1 day +ClanHallMpRegenerationFunctionFeeRatio = 86400000 +ClanHallMpRegenerationFeeLvl1 = 2000 +ClanHallMpRegenerationFeeLvl2 = 3750 +ClanHallMpRegenerationFeeLvl3 = 6500 +ClanHallMpRegenerationFeeLvl4 = 13750 +ClanHallMpRegenerationFeeLvl5 = 20000 + +# Hp Regeneration price +# Price = 1 day +ClanHallHpRegenerationFunctionFeeRatio = 86400000 +ClanHallHpRegenerationFeeLvl1 = 700 +ClanHallHpRegenerationFeeLvl2 = 800 +ClanHallHpRegenerationFeeLvl3 = 1000 +ClanHallHpRegenerationFeeLvl4 = 1166 +ClanHallHpRegenerationFeeLvl5 = 1500 +ClanHallHpRegenerationFeeLvl6 = 1750 +ClanHallHpRegenerationFeeLvl7 = 2000 +ClanHallHpRegenerationFeeLvl8 = 2250 +ClanHallHpRegenerationFeeLvl9 = 2500 +ClanHallHpRegenerationFeeLvl10 = 3250 +ClanHallHpRegenerationFeeLvl11 = 3750 +ClanHallHpRegenerationFeeLvl12 = 4250 +ClanHallHpRegenerationFeeLvl13 = 5166 + +# Exp Regeneration price +# Price = 1 day +ClanHallExpRegenerationFunctionFeeRatio = 86400000 +ClanHallExpRegenerationFeeLvl1 = 3000 +ClanHallExpRegenerationFeeLvl2 = 6000 +ClanHallExpRegenerationFeeLvl3 = 9000 +ClanHallExpRegenerationFeeLvl4 = 15000 +ClanHallExpRegenerationFeeLvl5 = 21000 +ClanHallExpRegenerationFeeLvl6 = 23330 +ClanHallExpRegenerationFeeLvl7 = 30000 + +# Creation item function +# Price = 1 day +ClanHallItemCreationFunctionFeeRatio = 86400000 +ClanHallItemCreationFunctionFeeLvl1 = 30000 +ClanHallItemCreationFunctionFeeLvl2 = 70000 +ClanHallItemCreationFunctionFeeLvl3 = 140000 + +# Curtains Decoration +# Price = 7 days +ClanHallCurtainFunctionFeeRatio = 604800000 +ClanHallCurtainFunctionFeeLvl1 = 2000 +ClanHallCurtainFunctionFeeLvl2 = 2500 + +# Fixtures Decoration +# Price = 3 days +ClanHallFrontPlatformFunctionFeeRatio = 259200000 +ClanHallFrontPlatformFunctionFeeLvl1 = 1300 +ClanHallFrontPlatformFunctionFeeLvl2 = 4000 + +# If true Clan Hall buff cost 0 mp. +# Default: False +AltClanHallMpBuffFree = False + +# --------------------------------------------------------------------------- +# Fortress +# --------------------------------------------------------------------------- +# Teleport Function price +# Price = 7 days +FortressTeleportFunctionFeeRatio = 604800000 +FortressTeleportFunctionFeeLvl1 = 1000 +FortressTeleportFunctionFeeLvl2 = 10000 + +# Support magic buff price +# Price = 1 day +FortressSupportFunctionFeeRatio = 86400000 +FortressSupportFeeLvl1 = 7000 +FortressSupportFeeLvl2 = 17000 + +# MP Regeneration price +# Price = 1 day +FortressMpRegenerationFunctionFeeRatio = 86400000 +FortressMpRegenerationFeeLvl1 = 6500 +FortressMpRegenerationFeeLvl2 = 9300 + +# Hp Regeneration price +# Price = 1 day +FortressHpRegenerationFunctionFeeRatio = 86400000 +FortressHpRegenerationFeeLvl1 = 2000 +FortressHpRegenerationFeeLvl2 = 3500 + +# Exp Regeneration price +# Price = 1 day +FortressExpRegenerationFunctionFeeRatio = 86400000 +FortressExpRegenerationFeeLvl1 = 9000 +FortressExpRegenerationFeeLvl2 = 10000 + +# This is the time frequently when Fort owner gets Blood Oath, supply level raised and Fort fee is payed +# Default 360 mins +FortressPeriodicUpdateFrequency = 360 + +# The number of Blood Oath which given to the Fort owner clan when Fort Updater runs +# Default: 1 +FortressBloodOathCount = 1 + +# The maximum Fort supply level +# Max lvl what you can define here is 21! +# Default: 6 +FortressMaxSupplyLevel = 6 + +# Fort fee which payed to the Castle +# Default: 25000 +FortressFeeForCastle = 25000 + +# The maximum time while a clan can own a fortress +# Deafault: 168 hours +FortressMaximumOwnTime = 168 + + +# --------------------------------------------------------------------------- +# Seven Signs +# --------------------------------------------------------------------------- +# Determines whether castle ownership is determined by clan or by alliance. +# Default is by alliance, as on official servers. +AltRequireClanCastle = False + +# Dawn: +# True - Players not owning castle need pay participation fee +# False - Anyone can join Dawn +AltCastleForDawn = True + +# Dusk: +# True - Players owning castle can not join Dusk side +# False - Anyone can join Dusk +AltCastleForDusk = True + +# Minimum Players for participate in SevenSigns Festival. +# Default: 5 +AltFestivalMinPlayer = 5 + +# Maximum seal stone contribution per player during festival. +# Default: 1000000 +AltMaxPlayerContrib = 1000000 + +# Festival Manager Start time (set in miliseconds; 1000 = 1 second). +# Default: 2 minutes +AltFestivalManagerStart = 120000 + +# Festival Length (set in miliseconds; 1000 = 1 second). +# Default: 18 minutes +AltFestivalLength = 1080000 + +# Festival Cycle Length. +# Default: 38 Minutes (20 minutes wait time + Festival Length) (set in miliseconds; 1000 = 1 second). +AltFestivalCycleLength = 2280000 + +# At what point the first festival spawn occurs. +# Default: 2 minutes (set in miliseconds; 1000 = 1 second). +AltFestivalFirstSpawn = 120000 + +# At what Point the first festival swarm occurs. +# Default: 5 minutes (set in miliseconds; 1000 = 1 second). +AltFestivalFirstSwarm = 300000 + +# At what Point the second festival spawn occurs. +# Default: 9 minutes (set in miliseconds; 1000 = 1 second). +AltFestivalSecondSpawn = 540000 + +# At what Point the second festival spawn occurs. +# Default: 12 minutes (set in miliseconds; 1000 = 1 second). +AltFestivalSecondSwarm = 720000 + +# At what point the chests spawn in. +# Default: 15 minutes (set in miliseconds; 1000 = 1 second). +AltFestivalChestSpawn = 900000 + +# This multipliers are used to change P.Def/M.Def of castle gates/walls while Seal of Strife is controlled by one of the sides. +# Default: 1.1,0.8,1.1,0.8 +AltDawnGatesPdefMult = 1.1 +AltDuskGatesPdefMult = 0.8 +AltDawnGatesMdefMult = 1.1 +AltDuskGatesMdefMult = 0.8 + +# If this = true only cabal period owners can use merchant and blacksmith of mammon. +# If false any player can use. +# Default: True +StrictSevenSigns = True + +# Save SevenSigns status only each 30 mins and after period change. +# Player info saved only during periodic data store (set by CharacterDataStoreInterval) and logout. +# If False then save info and status immediately after changes. +# Default: True +AltSevenSignsLazyUpdate = True + +# --------------------------------------------------------------------------- +# Clan Reputation Points +# --------------------------------------------------------------------------- +# Reputation score gained by taking Fortress. +TakeFortPoints = 200 + +# Reputation score gained by taking Castle. +TakeCastlePoints = 1500 + +# Reputation score gained by defended Castle. +CastleDefendedPoints = 750 + +# Reputation score gained per clan members of festival winning party. +FestivalOfDarknessWin = 200 + +# Reputation score gained for per hero clan members. +HeroPoints = 1000 + +# Minimum Reputation score gained after completing 2nd class transfer under Academy. +CompleteAcademyMinPoints = 190 + +# Maximum Reputation score gained after completing 2nd class transfer under Academy. +CompleteAcademyMaxPoints = 650 + +# Reputation score gained per killed ballista. +KillBallistaPoints = 30 + +# Reputation score gained for one Blood Alliance. +BloodAlliancePoints = 500 + +# Reputation score gained for 10 Blood Oaths. +BloodOathPoints = 200 + +# Reputation score gained for 100 Knight's Epaulettes. +KnightsEpaulettePoints = 20 + +# Reputation score gained per clan member listed as top raid killers. +1stRaidRankingPoints = 1250 +2ndRaidRankingPoints = 900 +3rdRaidRankingPoints = 700 +4thRaidRankingPoints = 600 +5thRaidRankingPoints = 450 +6thRaidRankingPoints = 350 +7thRaidRankingPoints = 300 +8thRaidRankingPoints = 200 +9thRaidRankingPoints = 150 +10thRaidRankingPoints = 100 +UpTo50thRaidRankingPoints = 25 +UpTo100thRaidRankingPoints = 12 + +# Reputation score gained/reduced per kill during a clan war or siege war. +ReputationScorePerKill = 1 + +# Reputation score reduced by loosing Fortress in battle. +LooseFortPoints = 0 + +# Reputation score reduced by loosing Castle in battle. +LooseCastlePoints = 3000 + +# Reputation score reduced by creating Royal Guard. +CreateRoyalGuardCost = 5000 + +# Reputation score reduced by creating Knight Unit. +CreateKnightUnitCost = 10000 + +# Reputation score reduced by reinforcing Knight Unit (if clan level is 9 or more). +ReinforceKnightUnitCost = 5000 + +# Reputation score reduced by increasing clan level. +ClanLevel6Cost = 10000 +ClanLevel7Cost = 20000 +ClanLevel8Cost = 40000 +ClanLevel9Cost = 40000 +ClanLevel10Cost = 40000 +ClanLevel11Cost = 75000 + +# Number of clan members needed to increase clan level. +ClanLevel6Requirement = 30 +ClanLevel7Requirement = 80 +ClanLevel8Requirement = 120 +ClanLevel9Requirement = 120 +ClanLevel10Requirement = 140 +ClanLevel11Requirement = 170 + +# --------------------------------------------------------------------------- +# Other +# --------------------------------------------------------------------------- + +# Allow riding wyvern during Castle/Fort Siege +# Default: True +AllowRideWyvernDuringSiege = True \ No newline at end of file diff --git a/L2J_Server/dist/game/config/General.properties b/L2J_Server/dist/game/config/General.properties new file mode 100644 index 00000000..b937f5fe --- /dev/null +++ b/L2J_Server/dist/game/config/General.properties @@ -0,0 +1,879 @@ +# --------------------------------------------------------------------------- +# General Server Settings +# --------------------------------------------------------------------------- +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# Administrator +# --------------------------------------------------------------------------- +# If this option is set to True every newly created character will have access level 127. This means that every character created will have Administrator Privileges. +# Default: False +EverybodyHasAdminRights = False + +# Show server/DP revision when players enter the game +# Default: True +DisplayServerRevision = True + +# Setting for serverList +# Displays [] in front of server name on character selection +# Default: False +ServerListBrackets = False + +# Displays server type next to the server name on character selection. +# Notes: +# Accepted Values: Normal, Relax, Test, NoLabel, Restricted, Event, Free +# Default: Normal +ServerListType = Normal + +# Displays server min age to the server name on character selection. +# Notes: +# Accepted values: 0, 15, 18 +# Default: 0 +ServerListAge = 0 + + +# If True, only accounts with GM access can enter the server. +# Default: False +ServerGMOnly = False + +# Enable GMs to have the glowing aura of a Hero character on login. +# Notes: +# GMs can do "///hero" on themselves and get this aura voluntarily. +# It's advised to keep this off due to graphic lag. +# Default: False +GMHeroAura = False + +# Auto set invulnerable status to a GM on login. +# Default: False +GMStartupInvulnerable = False + +# Auto set invisible status to a GM on login. +# Default: False +GMStartupInvisible = False + +# Auto block private messages to a GM on login. +# Default: False +GMStartupSilence = False + +# Auto list GMs in GM list (/gmlist) on login. +# Default: False +GMStartupAutoList = False + +# Auto set diet mode on to a GM on login (affects your weight penalty). +# Default: False +GMStartupDietMode = False + +# The way //admin panel looks can be "modern" or "classic" (default is modern while classic provides more compatibility). +# Default: modern +GMAdminMenuStyle = modern + +# Item restrictions apply to GMs aswell? (True = restricted usage) +# Default: True +GMItemRestriction = True + +# Skill restrictions apply to GMs aswell? (True = restricted usage) +# Default: True +GMSkillRestriction = True + +# Allow GMs to drop/trade non-tradeable and quest(drop only) items +# Default: False +GMTradeRestrictedItems = False + +# Allow GMs to restart/exit while is fighting stance +# Default: True +GMRestartFighting = True + +# Show the GM's name behind an announcement made by him +# example: "Announce: hi [HanWik]" +GMShowAnnouncerName = False + +# Give special skills for every GM +# 7041-7064 (Master's Blessing) +# Default: False +GMGiveSpecialSkills = False + +# --------------------------------------------------------------------------- +# Server Security +# --------------------------------------------------------------------------- +# Bypass exploit protection. To ENABLE protection set to True. +# Default: True +BypassValidation = True + +# Enforce gameguard for clients. Sends a gameguard query on character login. +# Default: False +GameGuardEnforce = False + +# Don't allow player to perform trade, talk with npc, or move until gameguard reply is received. +# Default: False +GameGuardProhibitAction = False + +#Logging settings. The following four settings, while enabled, will increase writing to your hard drive(s) considerably. Depending on the size of your server, the amount of players, and other factors, you may suffer a noticable performance hit. +# Default: False +LogChat = False + +# Default: False +LogItems = False + +# Log only Adena and equipable items if LogItems is enabled +LogItemsSmallLog = False + +# Default: False +LogItemEnchants = False + +# Default: False +LogSkillEnchants = False + +# Default: False +GMAudit = False + +# Default: False +LogGameDamage = False + +# If damage greater than threshold then log it +# Default: 5000 +LogGameDamageThreshold = 5000 + +# Check players for non-allowed skills +# Default: False +SkillCheckEnable = False + +# If true, remove invalid skills from player and database. +# Report only, if false. +# Default: False +SkillCheckRemove = False + +# Check also GM characters (only if SkillCheckEnable = True) +# Default: True +SkillCheckGM = True + + +# --------------------------------------------------------------------------- +# Thread Configuration +# --------------------------------------------------------------------------- +# Extreme caution should be here, set to defaults if you do not know what you are doing. +# These could possibly hurt your servers performance or improve it depending on your server's configuration, size, and other factors. +# Default: 10 +ThreadPoolSizeEffects = 10 + +# Default: 13 +ThreadPoolSizeGeneral = 13 + +# Default: 2 +UrgentPacketThreadCoreSize = 2 + +# Default: 4 +GeneralPacketThreadCoreSize = 4 + +# Default: 4 +GeneralThreadCoreSize = 4 + +# Default: 6 +AiMaxThread = 6 + +# Dead Lock Detector (a separate thread for detecting deadlocks). +# For improved crash logs and automatic restart in deadlock case if enabled. +# Check interval is in seconds. +# Default: False +DeadLockDetector = False + +# Default: 20 +DeadLockCheckInterval = 20 + +# Default: False +RestartOnDeadlock = False + + +# --------------------------------------------------------------------------- +# Client packet queue tuning +# --------------------------------------------------------------------------- +# Queue size, do not set it too low ! +# 0 - use value MaxReadPerPass + 2 (from mmo.properties) +# Default: 0 +ClientPacketQueueSize = 0 + +# Maximum number of packets in burst. +# Execution will be aborted and thread released if more packets executed in raw. +# 0 - use value MaxReadPerPass + 1 (from mmo.properties) +# Default: 0 +ClientPacketQueueMaxBurstSize = 0 + +# Maximum number of packets per second. +# Flood detector will be triggered if more packets received. +# After triggering all incoming packets will be dropped until flooding stopped. +# Default: 80 +ClientPacketQueueMaxPacketsPerSecond = 80 + +# Average number of packets per second calculated during this interval. +# Using larger value decrease number of false kicks, but slower reaction to flood. +# Avoid using too low or too high values, recommended between 3 and 10. +# Default: 5 +ClientPacketQueueMeasureInterval = 5 + +# Maximum average number of packets per second during measure interval. +# Flood detector will be triggered if more packets received. +# After triggering all incoming packets will be dropped until flooding stopped. +# Default: 40 +ClientPacketQueueMaxAveragePacketsPerSecond = 40 + +# Maximum number of flood triggers per minute. +# Client will be kicked if more floods detected. +# Default: 2 +ClientPacketQueueMaxFloodsPerMin = 2 + +# Maximum number of queue overflows per minute. +# After overflow all incoming packets from client are dropped until queue is flushed. +# Client will be kicked if more queue overflows detected. +# Default: 1 +ClientPacketQueueMaxOverflowsPerMin = 1 + +# Maximum number of buffer underflows per minute. +# Client will be kicked if more underflow exceptions detected. +# Default: 1 +ClientPacketQueueMaxUnderflowsPerMin = 1 + +# Maximum number of unknown packets per minute. +# Client will be kicked if more unknown packets received. +# Default: 5 +ClientPacketQueueMaxUnknownPerMin = 5 + + +# --------------------------------------------------------------------------- +# Optimization +# --------------------------------------------------------------------------- +# Items on ground management. +# Allow players to drop items on the ground. +# Default: True +AllowDiscardItem = True + +# Delete dropped reward items from world after a specified amount of seconds. Disabled = 0. +# Default: 600 +AutoDestroyDroppedItemAfter = 600 + +# Time in secs after which dropped herb will be auto-destroyed +# Default: 60 +AutoDestroyHerbTime = 60 + +# List of item id that will not be destroyed (separated by "," like 57,5575,6673). +# Notes: +# Make sure the lists do NOT CONTAIN trailing spaces or spaces between the numbers! +# Items on this list will be protected regardless of the following options. +# Default: 0 +ListOfProtectedItems = 0 + +# Cleans up the server database on startup. +# The bigger the database is, the longer it will take to clean up the database(the slower the server will start). +# Sometimes this ends up with 0 elements cleaned up, and a lot of wasted time on the server startup. +# If you want a faster server startup, set this to 'false', but its recommended to clean up the database from time to time. +# Default: True +DatabaseCleanUp = True + +# The time before a database connection closes (in miliseconds) +# If a query takes longer to execute than the time defined here, the server will throw "Unclosed Connection!" error. +# If you get often this error message, try increasing this. +# Default: 60000ms +ConnectionCloseTime = 60000 + +# This is the interval (in minutes), that the gameserver will update a players information such as location. +# The higher you set this number, there will be less character information saving so you will have less accessessing of the database and your hard drive(s). +# The lower you set this number, there will be more frequent character information saving so you will have more access to the database and your hard drive(s). +# A value of 0 disables periodic saving. +# Independent of this setting the character is always saved after leaving the world. +# Default: 15 +CharacterDataStoreInterval = 15 + +# This enables the server to only update items when saving the character. +# Enabling this greatly reduces DB usage and improves performance. +# WARNING: This option causes item loss during crashes. +# Default: False +LazyItemsUpdate = False + +# When enabled, this forces (even if using lazy item updates) the items owned by the character to be updated into DB when saving its character. +# Default: False +UpdateItemsOnCharStore = False + +# Also delete from world misc. items dropped by players (all except equip-able items). +# Notes: +# Works only if AutoDestroyDroppedItemAfter is greater than 0. +# Default: False +DestroyPlayerDroppedItem = False + +# Destroy dropped equipable items (armor, weapon, jewelry). +# Notes: +# Works only if DestroyPlayerDroppedItem = True +# Default: False +DestroyEquipableItem = False + +# Save dropped items into the database for restoring after restart. +# Default: False +SaveDroppedItem = False + +# Enable/Disable the emptying of the stored dropped items table after items are loaded into memory (safety setting). +# If the server crashed before saving items, on next start old items will be restored and players may already have picked up some of them so this will prevent duplicates. +# Default: False +EmptyDroppedItemTableAfterLoad = False + +# Time interval in minutes to save in DB items on ground. Disabled = 0. +# Notes: +# If SaveDroppedItemInterval is disabled, items will be saved into the database only at server shutdown. +# Default: 60 +SaveDroppedItemInterval = 60 + +# Delete all saved items from the database on next restart? +# Notes: +# Works only if SaveDroppedItem = False. +# Default: False +ClearDroppedItemTable = False + +# Delete invalid quest from players. +# Default: False +AutoDeleteInvalidQuestData = False + +# Setting False can improve server performance on high rate/population servers. +# Default: True +PreciseDropCalculation = True + +# Allow creating multiple non-stackable items at one time? +# Default: True +MultipleItemDrop = True + +# Forces full item inventory packet to be sent for any item change. +# Notes: +# This can increase network traffic +# Default: False +ForceInventoryUpdate = False + +# True = Load html's into cache only on first time html is requested. +# False = Load all html's into cache on server startup. +# Default: True +LazyCache = True + +# Cache all character names in to memory on server startup +# False - names are loaded from Db when they are requested +# True - decrease Db usage , increase memory consumption +# Default: True +CacheCharNames = True + +# Minimum and maximum variables in seconds for npc animation delay. +# You must keep MinNPCAnimation < = MaxNPCAnimation. +# Default: 10 +MinNPCAnimation = 10 + +# Default: 20 +MaxNPCAnimation = 20 + +# Default: 5 +MinMonsterAnimation = 5 + +# Default: 20 +MaxMonsterAnimation = 20 + +# Knownlist (the list of things a character sees) update method. Default is currently time based updating, which also makes it possible to use config options for guards to see moving monsters. Alternatively move based update can be used but guards cannot track mobs with that option but otherwise it should work well. +# Default: False +MoveBasedKnownlist = False + +# Interval (in miliseconds) in which the knownlist does full updates. +# For move based updates its used for intermediate updates. +# WARNING! +# Useful interval is between 300 - 2000. Too small value may kill your CPU, too high value may not update knownlists properly. The default value is chosen experimentally. +# Default: 1250 +KnownListUpdateInterval = 1250 + +# Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. +# Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. +# Turn off for a grid and neighbors occurs after the specified number of seconds have passed during which a grid has had no players in or in any of its neighbors. +# The always on option allows to ignore all this and let all grids be active at all times (not suggested). +# Default: False +GridsAlwaysOn = False + +# Default: 1 +GridNeighborTurnOnTime = 1 + +# Default: 90 +GridNeighborTurnOffTime = 90 + + +# --------------------------------------------------------------------------- +# World dimensions +# --------------------------------------------------------------------------- +# Min and max world regions (including) +# Default: X:11-26, Y:10-26 +WorldXMin = 11 +WorldXMax = 26 +WorldYMin = 10 +WorldYMax = 26 + +# --------------------------------------------------------------------------- +# Geodata +# --------------------------------------------------------------------------- +# GeoData options: +# 0 = GeoData and PathFinding OFF (default) +# 1 = GeoData is used to check Line Of Sight (LOS) targeting and +# L2Playable movement. You need to download files for data/geodata folder. +# Monsters can pass walls but not aggro (no line of sight) through them. +# 2 = Full GeoData enabled. Includes PathFinding (requires also /data/pathnode +# files if CellPathFinding not enabled) and all character moves go through +# geodata checks (if a mob passes a wall, pathfinding didn't find a route +# but we allow attack and returning home). +# Recommended server memory minimum 2 GB, rather 3 GB. +# Default: 0 +GeoData = 0 + +# Cell-level pathfinding, produces more accurate routes but is (maybe 10x) heavier to calculate. Recommended for small servers at least. +# If False, pathnode files are used. Uses a max number of nodes in calculation which can be adjusted in the algorithm if it needs to be faster. +# Default: False +CellPathFinding = False + +# Pathfinding array buffers configuration +PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 + +# Weight for nodes without obstacles far from walls +LowWeight = 0.5 + +# Weight for nodes near walls +MediumWeight = 2 + +# Weight for nodes with obstacles +HighWeight = 3 + +# Angle paths will be more "smart", but in cost of higher CPU utilization +AdvancedDiagonalStrategy = True + +# Weight for diagonal movement. Used only with AdvancedDiagonalStrategy = True +# Default: LowWeight * sqrt(2) +DiagonalWeight = 0.707 + +# Maximum number of LOS postfilter passes, 0 will disable postfilter. +# Default: 3 +MaxPostfilterPasses = 3 + +# Path debug function. +# Nodes known to pathfinder will be displayed as adena, constructed path as antidots. +# Number of the items show node cost * 10 +# Potions display path after first stage filter +# Red potions - actual waypoints. Green potions - nodes removed by LOS postfilter +# This function FOR DEBUG PURPOSES ONLY, never use it on the live server ! +DebugPath = False + +# True = Loads GeoData buffer's content into physical memory. +# False = Does not necessarily imply that the GeoData buffer's content is not resident in physical memory. +# Default: True +ForceGeodata = True + +# This setting controls Client <--> Server Player coordinates synchronization: +# -1 - Will synchronize only Z from Client --> Server. Default when no geodata. +# 1 - Synchronization Client --> Server only. Using this option (without geodata) makes it more difficult for players to bypass obstacles. +# 2 - Intended for geodata (at least with cell-level pathfinding, otherwise can you try -1). +# Server sends validation packet if client goes too far from server calculated coordinates. +# Default: -1 +CoordSynchronize = -1 + + +# --------------------------------------------------------------------------- +# Falling Damage +# --------------------------------------------------------------------------- +# Allow characters to receive damage from falling. +# CoordSynchronize = 2 is recommended. +# True - enabled. +# False - disabled. +# Auto - True if geodata enabled and False if disabled. +# Default: Auto +EnableFallingDamage = Auto + + +# --------------------------------------------------------------------------- +# Features +# --------------------------------------------------------------------------- +# Zone Setting Options: +# 0 = Peace All the Time +# 1 = PVP During Siege for siege participants +# 2 = PVP All the Time +# Default: 0 +ZoneTown = 0 + +# Global Chat. +# Available Options: ON, OFF, GM, GLOBAL +# Default: ON +GlobalChat = ON + +# Trade Chat. +# Available Options: ON, OFF, GM, GLOBAL +# Default: ON +TradeChat = ON + +# If you are experiencing problems with Warehouse transactions, feel free to disable them here. +# Default: True +AllowWarehouse = True + +# Enable Warehouse Cache. If warehouse is not used will server clear memory used by this warehouse. +# Default: False +WarehouseCache = False + +# How long warehouse should be stored in memory. +# Default: 15 +WarehouseCacheTime = 15 + +# Default: True +AllowRefund = True + +# Default: True +AllowMail = True + +# Default: True +AllowAttachments = True + +# If True player can try on weapon and armor in shop. +# Default: True +AllowWear = True + +# Default: 5 +WearDelay = 5 + +#Adena cost to try on an item. +# Default: 10 +WearPrice = 10 + + +# --------------------------------------------------------------------------- +# Misc Settings +# --------------------------------------------------------------------------- +# Default: True +AllowRace = True + +# Default: True +AllowWater = True + +# Enable pets for rent (wyvern & strider) from pet managers. +# Default: False +AllowRentPet = False + +# Default: True +AllowFishing = True + +# Default: True +AllowBoat = True + +# Boat broadcast radius. +# If players getting annoyed by boat shouts then radius can be decreased. +# Default: 20000 +BoatBroadcastRadius = 20000 + +# Default: True +AllowCursedWeapons = True + +# This controls whether or not NPCs (not mobs) will walk around from one point to another. +# Default: True +AllowNpcWalkers = True + +#Allow Pet manager's pets to walk around. +# Default: True +AllowPetWalkers = True + +# Show "data/html/servnews.htm" when a character enters world. +# Default: False +ShowServerNews = False + +# Type of the Community board: +# 0 = community is disabled +# 1 = old community +# 2 = full community (not fully implemented, experimental) +# Default: 1 +CommunityType = 1 + +# Default: False +BBSShowPlayerList = False + +# Default: _bbshome +BBSDefault = _bbshome + +# show level of character to others in Community Board +# Default: False +ShowLevelOnCommunityBoard = False + +# Default: True +ShowStatusOnCommunityBoard = True + +# Default: 50 +NamePageSizeOnCommunityBoard = 50 + +# Default: 5 +NamePerRowOnCommunityBoard = 5 + +# Enable chat filter +# Default = False +UseChatFilter = False + +# Replace filter words with following chars +ChatFilterChars = ^_^ + +# --------------------------------------------------------------------------- +# Manor +# --------------------------------------------------------------------------- +# Default: True +AllowManor = True + +# Manor refresh time in military hours. +# Default: 20 (8pm) +AltManorRefreshTime = 20 + +# Manor refresh time (minutes). +# Default: 00 (start of the hour) +AltManorRefreshMin = 00 + +# Manor period approve time in military hours. +# Default: 6 (6am) +AltManorApproveTime = 6 + +# Manor period approve time (minutes). +# Default: 00 (start of the hour) +AltManorApproveMin = 00 + +# Manor maintenance time. +# Default: 360000 (6 minutes) +AltManorMaintenancePeriod = 360000 + +# Manor Save Type. +# True = Save data into the database after every action +# Default: False +AltManorSaveAllActions = False + +# Manor Save Period (used only if AltManorSaveAllActions = False) Default very 2 hours +# Default: 2 +AltManorSavePeriodRate = 2 + + +# --------------------------------------------------------------------------- +# Lottery +# --------------------------------------------------------------------------- +# Default: True +AllowLottery = True + +# Initial Lottery prize. +# Default: 50000 +AltLotteryPrize = 50000 + +# Lottery Ticket Price +# Default: 2000 +AltLotteryTicketPrice = 2000 + +# What part of jackpot amount should receive characters who pick 5 wining numbers +# Default: 0.6 +AltLottery5NumberRate = 0.6 + +# What part of jackpot amount should receive characters who pick 4 wining numbers +# Default: 0.2 +AltLottery4NumberRate = 0.2 + +# What part of jackpot amount should receive characters who pick 3 wining numbers +# Default: 0.2 +AltLottery3NumberRate = 0.2 + +# How much adena receive characters who pick two or less of the winning number +# Default: 200 +AltLottery2and1NumberPrize = 200 + + +# --------------------------------------------------------------------------- +# Item Auction +# --------------------------------------------------------------------------- +# +AltItemAuctionEnabled = False + +# Number of days before auction cleared from database with all bids. +# Default: 14 +AltItemAuctionExpiredAfter = 14 + +# Auction extends to specified amount of seconds if one or more new bids added. +# By default auction extends only two times, by 5 and 3 minutes, this custom value used after it. +# Values higher than 60s is not recommended. +# Default: 0 +AltItemAuctionTimeExtendsOnBid = 0 + + +# --------------------------------------------------------------------------- +# Dimension Rift +# --------------------------------------------------------------------------- +# Minimal party size to enter rift. Min = 2, Max = 9. +# If while inside the rift, the party becomes smaller, all members will be teleported back. +# Default: 2 +RiftMinPartySize = 2 + +# Number of maximum jumps between rooms allowed, after this time party will be teleported back +# Default: 4 +MaxRiftJumps = 4 + +# Time in ms the party has to wait until the mobs spawn when entering a room. C4 retail: 10s +# Default: 10000 +RiftSpawnDelay = 10000 + +# Time between automatic jumps in seconds +# Default: 480 +AutoJumpsDelayMin = 480 + +# Default: 600 +AutoJumpsDelayMax = 600 + +# Time Multiplier for stay in the boss room +# Default: 1.5 +BossRoomTimeMultiply = 1.5 + +# Cost in dimension fragments to enter the rift, each party member must own this amount +# Default: 18 +RecruitCost = 18 + +# Default: 21 +SoldierCost = 21 + +# Default: 24 +OfficerCost = 24 + +# Default: 27 +CaptainCost = 27 + +# Default: 30 +CommanderCost = 30 + +# Default: 33 +HeroCost = 33 + + +# --------------------------------------------------------------------------- +# Four Sepulchers +# --------------------------------------------------------------------------- +# Default: 50 +TimeOfAttack = 50 + +# Default: 5 +TimeOfCoolDown = 5 + +# Default: 3 +TimeOfEntry = 3 + +# Default: 2 +TimeOfWarmUp = 2 + +# Default: 4 +NumberOfNecessaryPartyMembers = 4 + + +# --------------------------------------------------------------------------- +# Punishment +# --------------------------------------------------------------------------- +# Player punishment for illegal actions: +# 1 - broadcast warning to gms only +# 2 - kick player(default) +# 3 - kick & ban player +# 4 - jail player (define minutes of jail with param: 0 = infinite) +# Default: 2 +DefaultPunish = 2 + +# This setting typically specifies the duration of the above punishment. +# Default: 0 +DefaultPunishParam = 0 + +# Apply default punish if player buy items for zero adena. +# Default: True +OnlyGMItemsFree = True + +# Jail is a PvP zone. +# Default: False +JailIsPvp = False + +# Disable all chat in jail (except normal one) +# Default: True +JailDisableChat = True + +# Disable all transaction in jail +# Trade/Store/Drop +# Default: False +JailDisableTransaction = False + +# --------------------------------------------------------------------------- +# Database Custom Tables +# --------------------------------------------------------------------------- +# Default: False +CustomSpawnlistTable = False + +# Option to save GM spawn only in the custom table. +# Default: False +SaveGmSpawnOnCustom = False + +# Option to delete spawn in alternate table. +# Default: False +DeleteGmSpawnOnCustom = False + +# Default: False +CustomNpcTable = False + +# Default: False +CustomNpcSkillsTable = False + +# Default: False +CustomArmorSetsTable = False + +# Default: False +CustomTeleportTable = False + +# Default: False +CustomDroplistTable = False + +# Default: False +CustomMerchantTables = False + +# Default: False +CustomNpcBufferTables = False + +# --------------------------------------------------------------------------- +# Handy's Block Checker Event Settings +# --------------------------------------------------------------------------- +# Enable the event ? +# Default: True +EnableBlockCheckerEvent = True + +# Minimun number of members on each team before +# be able to start the event +# Min: 1 +# Max: 6 +# Retail: 2 +BlockCheckerMinTeamMembers = 2 + +# Fair play +# Players can choose what team to play. However, by +# enabling this property to true, the teams will be +# balanced in the teleport to the arena +HBCEFairPlay = True + +# --------------------------------------------------------------------------- +# Developer Settings +# --------------------------------------------------------------------------- +# Do not touch these if you do not know what you are doing. +# These settings are for debugging servers ONLY. They are not meant for LIVE servers. + +# Default: False +Debug = False + +# Packet handler debug output +# Default: False +PacketHandlerDebug = False + +# Default: False +Developer = False + +# Default: False +AcceptGeoeditorConn = False + +# If True the server will be a test server (listed by test server clients only). +# Default: False +TestServer = False + +# Don't load Handlers +# Default: False +AltDevNoHandlers = False + +# Don't load quests. +# Default: False +AltDevNoQuests = False + +# Don't load spawntable. +# Default: False +AltDevNoSpawns = False \ No newline at end of file diff --git a/L2J_Server/dist/game/config/GraciaSeeds.properties b/L2J_Server/dist/game/config/GraciaSeeds.properties new file mode 100644 index 00000000..20e7a695 --- /dev/null +++ b/L2J_Server/dist/game/config/GraciaSeeds.properties @@ -0,0 +1,17 @@ +# --------------------------------------------------------------------------- +# Gracia Seeds Settings +# --------------------------------------------------------------------------- +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# Seed of Destruction Settings +# --------------------------------------------------------------------------- + +# Count of Kills which needed for Stage 2 +# Default: 10 +TiatKillCountForNextState = 10 + +# Length of Stage 2 before the Defense state starts (in minutes). +# Default: 720min (12h) +Stage2Length = 720 diff --git a/L2J_Server/dist/game/config/Grandboss.properties b/L2J_Server/dist/game/config/Grandboss.properties new file mode 100644 index 00000000..f0d47728 --- /dev/null +++ b/L2J_Server/dist/game/config/Grandboss.properties @@ -0,0 +1,77 @@ +# --------------------------------------------------------------------------- +# Antharas +# --------------------------------------------------------------------------- +# Delay of appearance time of Antharas. Value is minute. Range 3-60 +AntharasWaitTime = 30 + +# Interval time of Antharas. Value is hour. Range 1-480 +IntervalOfAntharasSpawn = 264 + +# Random interval. Range 1-192 +RandomOfAntharasSpawn = 72 + +# --------------------------------------------------------------------------- +# Valakas +# --------------------------------------------------------------------------- +# Delay of appearance time of Valakas. Value is minute. Range 3-60 +ValakasWaitTime = 30 + +# Interval time of Valakas. Value is hour. Range 1-480 +IntervalOfValakasSpawn = 264 + +# Random interval. Range 1-192 +RandomOfValakasSpawn = 72 + +# --------------------------------------------------------------------------- +# Baium +# --------------------------------------------------------------------------- +# Interval time of Baium. Value is hour. Range 1-480 +IntervalOfBaiumSpawn = 168 + +# Random interval. Range 1-192 +RandomOfBaiumSpawn = 48 + +# --------------------------------------------------------------------------- +# Core +# --------------------------------------------------------------------------- +# Interval time of Core. Value is hour. Range 1-480 +IntervalOfCoreSpawn = 60 + +# Random interval. Range 1-192 +RandomOfCoreSpawn = 24 + +# --------------------------------------------------------------------------- +# Orfen +# --------------------------------------------------------------------------- +# Interval time of Orfen. Value is hour. Range 1-480 +IntervalOfOrfenSpawn = 48 + +# Random interval. Range 1-192 +RandomOfOrfenSpawn = 20 + +# --------------------------------------------------------------------------- +# Queen Ant +# --------------------------------------------------------------------------- +# Interval time of QueenAnt. Value is hour. Range 1-480 +IntervalOfQueenAntSpawn = 36 + +# Random interval. Range 1-192 +RandomOfQueenAntSpawn = 17 + +# --------------------------------------------------------------------------- +# Zaken +# --------------------------------------------------------------------------- +# Interval time of Zaken. Value is hour. Range 1-480 +IntervalOfZakenSpawn = 19 + +# Random interval. Range 1-192 +RandomOfZakenSpawn = 35 + +# --------------------------------------------------------------------------- +# Frintezza +# --------------------------------------------------------------------------- +# Interval time of Frintezza. Value is hour. Range 1-480 +IntervalOfFrintezzaSpawn = 48 + +# Random interval. Range 1-192 +RandomOfFrintezzaSpawn = 8 \ No newline at end of file diff --git a/L2J_Server/dist/game/config/NPC.properties b/L2J_Server/dist/game/config/NPC.properties new file mode 100644 index 00000000..385b059f --- /dev/null +++ b/L2J_Server/dist/game/config/NPC.properties @@ -0,0 +1,173 @@ +# --------------------------------------------------------------------------- +# NPC Settings +# --------------------------------------------------------------------------- +# This properties file is solely for the purpose of NPC modifications and settings that directly influence them. +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# General +# --------------------------------------------------------------------------- +# Global announcements will be made indicating Blacksmith/Merchant of Mammon +# Spawning points. +# Default: False +AnnounceMammonSpawn = False + +# True - Mobs can be aggressive while in peace zones. +# False - Mobs can NOT be aggressive while in peace zones. +# Default: True +AltMobAgroInPeaceZone = True + +# Defines whether NPCs are attackable by default +# Retail: True +AltAttackableNpcs = True + +# Allows non-GM players to view NPC stats via shift-click +# Default: False +AltGameViewNpc = False + +# Maximum distance mobs can randomly go from spawn point. +# DEFAULT NEEDS TO BE VERIFIED, MUST BE CHANGED HERE AND IN CONFIG.JAVA IF NOT CORRECT +# Default: 300 +MaxDriftRange = 300 + +# If True, the following deep blue mobs' drop penalties will be applied: +# - When player's level is 9 or greater than mob's level, drops got divided by 3. +# - After 9 levels of difference between player and mobs, drop chance is lowered by 9% each level that difference increases. (9lvls diff = -9%; 10lvls diff = -18%;) +# Default: True +UseDeepBlueDropRules = True + +# If True, the following deep blue raid bosses drop penalties will be applied: +# - When player's level is 9 or greater than raid's level, drops got divided by 3. +# - After 9 levels of difference between player and raid, drop chance is lowered by 9% each level that difference increases. (9lvls diff = -9%; 10lvls diff = -18%;) +# Default: True +UseDeepBlueDropRulesRaid = True + +# Default: False +ShowNpcLevel = False + +# Show clan, alliance crests for territory NPCs without quests +# Default: False +ShowCrestWithoutQuest = False + +# Do you want mobs to drop Vitality resplenishing herbs? +# Notes: +# Works only if EnableVitality = True +# Default: True +EnableVitalityHerbs = True + +# Custom random EnchantEffect +# All npcs with weapons get random weapon enchanted value +# Enchantment is only visual, range is 4-21 +# Default: False +EnableRandomEnchantEffect = False + +# The minimum NPC level for the Gracia Epilogue rule: +# "The amount of damage inflicted on monsters will be lower if your character is 2 or more levels below that of the level 78+ monster." +# Notes: +# If you want to disable this feature then set it 99 +# Default: 78 +MinNPCLevelForDmgPenalty = 78 + +# The penalty in percent for -2 till -5 level differences +# default: +# normal - 0.7, 0.6, 0.6, 0.55 +# critical - 0.75, 0.65, 0.6, 0.58 +# skill - 0.8, 0.7, 0.65, 0.62 +DmgPenaltyForLvLDifferences = 0.7, 0.6, 0.6, 0.55 +CritDmgPenaltyForLvLDifferences = 0.75, 0.65, 0.6, 0.58 +SkillDmgPenaltyForLvLDifferences = 0.8, 0.7, 0.65, 0.62 + +# The minimum NPC level for the Gracia Epilogue rule: +# "When a character's level is 3 or more levels lower than that of a monsters level the chance that the monster will be able to resist a magic spell will increase." +# Notes: +# If you want to disable this feature then set it 99 +# Default: 78 +MinNPCLevelForMagicPenalty = 78 + +# The penalty in percent for -3 till -6 level differences +# Default: unknown +SkillChancePenaltyForLvLDifferences = 2.5, 3.0, 3.25, 3.5 + + +# --------------------------------------------------------------------------- +# Guards +# --------------------------------------------------------------------------- +# True - Allows guards to attack aggressive mobs within range. +# Default: False +GuardAttackAggroMob = False + + +# --------------------------------------------------------------------------- +# Pets +# --------------------------------------------------------------------------- +# This option enables or disables the Wyvern manager located in every castle +# to train Wyverns and Striders from Hatchlings. +# Default: False +AllowWyvernUpgrader = False + +# Pets that can be rented. +# Example: 30827, 32471, 34486, 36547 +# Default: 30827 +ListPetRentNpc = 30827 + +# This will control the inventory space limit for pets (NOT WEIGHT LIMIT). +# Default: 12 +MaximumSlotsForPet = 12 + +# HP/MP Regen Multiplier for Pets +# Default: 100, 100 +PetHpRegenMultiplier = 100 +PetMpRegenMultiplier = 100 + +# --------------------------------------------------------------------------- +# Raid Bosses +# --------------------------------------------------------------------------- +# Percent of HP and MP regeneration for raid bosses. +# Example: Setting HP to 10 will cause raid boss HP to regenerate 90% slower than normal. +# Default: 100, 100 +RaidHpRegenMultiplier = 100 +RaidMpRegenMultiplier = 100 + +# Percent of physical and magical defense for raid bosses. +# Example: A setting of 10 will cause defense to be 90% lower than normal, +# while 110 will cause defense to be 10% higher than normal. +# Default: 100, 100 +RaidPDefenceMultiplier = 100 +RaidMDefenceMultiplier = 100 + +# Percent of physical and magical attack for raid bosses. +# Example: A setting of 10 will cause attack to be 90% lower than normal, +# while 110 will cause attack to be 10% higher than normal. +# Default: 100, 100 +RaidPAttackMultiplier = 100 +RaidMAttackMultiplier = 100 + +# Configure Minimum and Maximum time multiplier between raid boss re-spawn. +# By default 12Hours*1.0 for Minimum Time and 24Hours*1.0 for Maximum Time. +# Example: Setting RaidMaxRespawnMultiplier to 2 will make the time between +# re-spawn 24 hours to 48 hours. +# Default: 1.0, 1.0 +RaidMinRespawnMultiplier = 1.0 +RaidMaxRespawnMultiplier = 1.0 + +# Configure the interval at which raid boss minions will re-spawn. +# This time is in milliseconds, 1 minute is 60000 milliseconds. +# Default: 300000 +RaidMinionRespawnTime = 300000 + +# Disable Raid Curse if raid more than 8 levels lower. +# Caution: drop will be reduced or even absent if DeepBlue drop rules enabled. +# Default: False +DisableRaidCurse = False + +# Configure the interval at which raid bosses and minions wont reconsider their target +# This time is in seconds, 1 minute is 60 seconds. +# Default: 10,10,10 +RaidChaosTime = 10 +GrandChaosTime = 10 +MinionChaosTime = 10 + +# A comma separated list of all NPCs who have no dialog, i.e. double-clicking on them doesn't open a chat window +# Default: 18684,18685,18686,18687,18688,18689,18690,19691,18692,31557,31606,31671,31672,31673,31674,32026,32030,32031,32032,32306,32619,32620,32621 +NonTalkingNpcs = 18684,18685,18686,18687,18688,18689,18690,19691,18692,31557,31606,31671,31672,31673,31674,32026,32030,32031,32032,32306,32619,32620,32621 diff --git a/L2J_Server/dist/game/config/chatfilter.txt b/L2J_Server/dist/game/config/chatfilter.txt new file mode 100644 index 00000000..033a976b --- /dev/null +++ b/L2J_Server/dist/game/config/chatfilter.txt @@ -0,0 +1,11 @@ +# Example for and +# (not allow players to confuse others by writing from beginning of the line) +# \\r +# \\n +# +# These words will be replaced: +suck +gay +rape +fuck +dick \ No newline at end of file diff --git a/L2J_Server/dist/game/config/floodprotector.properties b/L2J_Server/dist/game/config/floodprotector.properties new file mode 100644 index 00000000..9e090bd7 --- /dev/null +++ b/L2J_Server/dist/game/config/floodprotector.properties @@ -0,0 +1,121 @@ +# --------------------------------------------------------------------------- +# Floodprotector Options +# --------------------------------------------------------------------------- +# The following settings can be applied to each feature: +# Interval - interval in gameserver ticks (1 tick = 100ms) in which only one request is allowed +# LogFlooding - whether flooding should be logged (only first ocurrance of flooding and total count of flood requests is logged) +# PunishmentLimit - if number of requests within single interval exceeds specified number then the specified punishment is applied (0 = disables punishment feature) +# PunishmentType - type of the punishment ('none', 'kick', 'ban', 'jail'), valid only if PunishmentLimit is greater than 0 +# PunishmentTime - for how many minutes should the player(jail)/account(ban) be punished (0 = forever), valid only for PunishmentType 'jail' or 'ban' + +# UseItem - item usage flooding +FloodProtectorUseItemInterval = 4 +FloodProtectorUseItemLogFlooding = False +FloodProtectorUseItemPunishmentLimit = 0 +FloodProtectorUseItemPunishmentType = none +FloodProtectorUseItemPunishmentTime = 0 + +# RollDice - rolling dice flooding +FloodProtectorRollDiceInterval = 42 +FloodProtectorRollDiceLogFlooding = False +FloodProtectorRollDicePunishmentLimit = 0 +FloodProtectorRollDicePunishmentType = none +FloodProtectorRollDicePunishmentTime = 0 + +# Firework - firework flooding +FloodProtectorFireworkInterval = 42 +FloodProtectorFireworkLogFlooding = False +FloodProtectorFireworkPunishmentLimit = 0 +FloodProtectorFireworkPunishmentType = none +FloodProtectorFireworkPunishmentTime = 0 + +# ItemPetSummon - item summonning and pet mounting flooding +FloodProtectorItemPetSummonInterval = 16 +FloodProtectorItemPetSummonLogFlooding = False +FloodProtectorItemPetSummonPunishmentLimit = 0 +FloodProtectorItemPetSummonPunishmentType = none +FloodProtectorItemPetSummonPunishmentTime = 0 + +# HeroVoice - hero voice flooding +FloodProtectorHeroVoiceInterval = 100 +FloodProtectorHeroVoiceLogFlooding = False +FloodProtectorHeroVoicePunishmentLimit = 0 +FloodProtectorHeroVoicePunishmentType = none +FloodProtectorHeroVoicePunishmentTime = 0 + +# GlobalChat - global chat flooding +FloodProtectorGlobalChatInterval = 5 +FloodProtectorGlobalChatLogFlooding = False +FloodProtectorGlobalChatPunishmentLimit = 0 +FloodProtectorGlobalChatPunishmentType = none +FloodProtectorGlobalChatPunishmentTime = 0 + +# Subclass - subclass flooding +FloodProtectorSubclassInterval = 20 +FloodProtectorSubclassLogFlooding = False +FloodProtectorSubclassPunishmentLimit = 0 +FloodProtectorSubclassPunishmentType = none +FloodProtectorSubclassPunishmentTime = 0 + +# DropItem - drop item flooding +FloodProtectorDropItemInterval = 10 +FloodProtectorDropItemLogFlooding = False +FloodProtectorDropItemPunishmentLimit = 0 +FloodProtectorDropItemPunishmentType = none +FloodProtectorDropItemPunishmentTime = 0 + +# ServerBypass - server bypass flooding +FloodProtectorServerBypassInterval = 5 +FloodProtectorServerBypassLogFlooding = False +FloodProtectorServerBypassPunishmentLimit = 0 +FloodProtectorServerBypassPunishmentType = none +FloodProtectorServerBypassPunishmentTime = 0 + +# ServerBypass - multisell list request flooding +FloodProtectorMultiSellInterval = 1 +FloodProtectorMultiSellLogFlooding = False +FloodProtectorMultiSellPunishmentLimit = 0 +FloodProtectorMultiSellPunishmentType = none +FloodProtectorMultiSellPunishmentTime = 0 + +# All kind of other transactions - to/from pet, private store, warehouse, destroy +FloodProtectorTransactionInterval = 10 +FloodProtectorTransactionLogFlooding = False +FloodProtectorTransactionPunishmentLimit = 0 +FloodProtectorTransactionPunishmentType = none +FloodProtectorTransactionPunishmentTime = 0 + +# Manufacture +FloodProtectorManufactureInterval = 3 +FloodProtectorManufactureLogFlooding = False +FloodProtectorManufacturePunishmentLimit = 0 +FloodProtectorManufacturePunishmentType = none +FloodProtectorManufacturePunishmentTime = 0 + +# Manor +FloodProtectorManorInterval = 30 +FloodProtectorManorLogFlooding = False +FloodProtectorManorPunishmentLimit = 0 +FloodProtectorManorPunishmentType = none +FloodProtectorManorPunishmentTime = 0 + +# SendMail - sending mail interval, 10s on retail +FloodProtectorSendMailInterval = 100 +FloodProtectorSendMailLogFlooding = False +FloodProtectorSendMailPunishmentLimit = 0 +FloodProtectorSendMailPunishmentType = none +FloodProtectorSendMailPunishmentTime = 0 + +# CharacterSelect - attempts to load character +FloodProtectorCharacterSelectInterval = 30 +FloodProtectorCharacterSelectLogFlooding = False +FloodProtectorCharacterSelectPunishmentLimit = 0 +FloodProtectorCharacterSelectPunishmentType = none +FloodProtectorCharacterSelectPunishmentTime = 0 + +# Item Auction - Request for refresh +FloodProtectorItemAuctionInterval = 9 +FloodProtectorItemAuctionLogFlooding = False +FloodProtectorItemAuctionPunishmentLimit = 0 +FloodProtectorItemAuctionPunishmentType = none +FloodProtectorItemAuctionPunishmentTime = 0 diff --git a/src/main/resources/config/fortsiege.properties b/L2J_Server/dist/game/config/fortsiege.properties similarity index 80% rename from src/main/resources/config/fortsiege.properties rename to L2J_Server/dist/game/config/fortsiege.properties index 8371ad2a..c2b9a0ac 100644 --- a/src/main/resources/config/fortsiege.properties +++ b/L2J_Server/dist/game/config/fortsiege.properties @@ -1,26 +1,22 @@ # --------------------------------------------------------------------------- # Fort Siege Settings # --------------------------------------------------------------------------- -# The defaults are set to be retail-like. -# If you modify any of these settings, your server will deviate from being retail-like. +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. # Warning: -# Please take extreme caution when changing anything. -# Also, please understand what you are changing before you do so on a live server. - +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. # --------------------------------------------------------------------------- # Standard Settings # --------------------------------------------------------------------------- - -# Length of siege before the count-down (in minutes). +# Length of siege before the count down (in minutes). SiegeLength = 60 -# This defines how long you need to wait until the suspicious merchant will spawn after the siege ends (in minutes). -# Keep in mind when merchant spawns, the fort can be immediately siege. +# This defines how long you need to wait until the suspicious merchant will spawn after siege ends (in minutes). +# Keep in mind when merchant spawns, the fort can be immediately sieged. # Default: 180 SuspiciousMerchantRespawnDelay = 180 # This defines how long you have to kill all commanders once you kill the first one (in minutes). -# After that time (if all commanders are not killed), all commanders and doors get respawned. +# After that time (if all commanders not killed) all commanders and doors get respawned. CountDownLength = 10 # Max number of flags per clan. @@ -32,16 +28,13 @@ SiegeClanMinLevel = 4 #Max number of clans that can register on each side. AttackerMaxClans = 500 -# This option, if enabled, will enable to register Fortress Siege to Castle owners just in territory. -# Default: True -JustToTerritory = True # --------------------------------------------------------------------------- # Fortress Commander Spawns # --------------------------------------------------------------------------- # Commander syntax: NameCommanderN = x,y,z,heading,npc_id # Name - fortress name -# N - number - 1: Archer Commander +# N - number - 1: Archer Comander # - 2: Guard Commander # - 3: Support Unit Commander # - 4: Main Machine - not supported yet, used General @@ -52,14 +45,14 @@ JustToTerritory = True # heading # npc_id - id of template -# Flag syntax: NameFlagN = x,y,z,flag_id +# Flag syntax: NameFlagN = x,y,z,flag_id # Name - fortress name # N - number # x,y,z - coords -# flag_id - id of a combat flag +# flag_id - id of combat flag # Shanty Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id ShantyCommander1 = -52435,155188,-1768,20000,35683 ShantyCommander2 = -52128,157752,-2024,29864,35677 ShantyCommander3 = -53944,155433,-2024,7304,35680 @@ -69,7 +62,7 @@ ShantyFlag2 = -53054,156605,-1896,9819 ShantyFlag3 = -53032,156689,-1896,9819 # Southern Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id SouthernCommander1 = -21328,218864,-2952,0,35719 SouthernCommander2 = -22992,218160,-3208,0,35713 SouthernCommander3 = -21520,221504,-3208,45328,35716 @@ -80,7 +73,7 @@ SouthernFlag2 = -22386,219798,-3079,9819 SouthernFlag3 = -22386,219679,-3079,9819 # Hive Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id HiveCommander1 = 15152,188128,-2640,0,35752 HiveCommander2 = 17984,187536,-2896,45056,35746 HiveCommander3 = 16016,189520,-2888,0,35749 @@ -90,7 +83,7 @@ HiveFlag2 = 16761,188306,-2770,9819 HiveFlag3 = 16847,188257,-2770,9819 # Valley Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id ValleyCommander1 = 124768,121856,-2296,0,35788 ValleyCommander2 = 124299,123614,-2552,49192,35782 ValleyCommander3 = 124768,124640,-2552,54480,35785 @@ -101,7 +94,7 @@ ValleyFlag2 = 126092,123650,-2429,9819 ValleyFlag3 = 126205,123648,-2429,9819 # Ivory Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id IvoryCommander1 = 72400,2896,-2760,0,35821 IvoryCommander2 = 73788,5479,-3016,55136,35815 IvoryCommander3 = 71264,4144,-3008,0,35818 @@ -111,7 +104,7 @@ IvoryFlag2 = 72660,4512,-2888,9819 IvoryFlag3 = 72759,4594,-2888,9819 # Narsell Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id NarsellCommander1 = 154704,53856,-2968,0,35852 NarsellCommander2 = 155576,56592,-3224,59224,35846 NarsellCommander3 = 153328,54848,-3216,5512,35849 @@ -121,7 +114,7 @@ NarsellFlag2 = 154650,55493,-3097,9819 NarsellFlag3 = 154715,55587,-3097,9819 # Bayou Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id BayouCommander1 = 188624,38240,-3128,0,35888 BayouCommander2 = 188160,39920,-3376,49284,35882 BayouCommander3 = 188626,41066,-3376,57140,35885 @@ -132,7 +125,7 @@ BayouFlag2 = 189931,40060,-3253,9819 BayouFlag3 = 190052,40062,-3253,9819 # White Sands Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id WhiteSandsCommander1 = 117216,205648,-3048,0,35921 WhiteSandsCommander2 = 118880,203568,-3304,5396,35915 WhiteSandsCommander3 = 118560,206560,-3304,48872,35918 @@ -142,7 +135,7 @@ WhiteSandsFlag2 = 118690,205062,-3176,9819 WhiteSandsFlag3 = 118742,204968,-3176,9819 # Borderland Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id BorderlandCommander1 = 159664,-72224,-2584,0,35957 BorderlandCommander2 = 157968,-71659,-2832,59020,35951 BorderlandCommander3 = 157312,-70640,-2832,0,35954 @@ -153,7 +146,7 @@ BorderlandFlag2 = 158883,-70145,-2708,9819 BorderlandFlag3 = 158946,-70045,-2708,9819 # Swamp Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id SwampCommander1 = 71264,-60512,-2504,0,35995 SwampCommander2 = 71248,-62352,-2752,12388,35989 SwampCommander3 = 68688,-59648,-2752,56012,35992 @@ -164,7 +157,7 @@ SwampFlag2 = 69979,-61144,-2632,9819 SwampFlag3 = 70069,-61182,-2629,9819 # Archaic Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id ArchaicCommander1 = 109856,-142640,-2672,0,36028 ArchaicCommander2 = 109600,-139735,-2928,62612,36022 ArchaicCommander3 = 108223,-142209,-2920,8524,36025 @@ -174,7 +167,7 @@ ArchaicFlag2 = 109184,-141129,-2801,9819 ArchaicFlag3 = 109214,-141016,-2801,9819 # Floran Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id FloranCommander1 = 6528,151872,-2608,0,36064 FloranCommander2 = 7006,148242,-2856,32768,36058 FloranCommander3 = 4384,150992,-2856,0,36061 @@ -185,7 +178,7 @@ FloranFlag2 = 5306,149743,-2732,9819 FloranFlag3 = 5299,149870,-2732,9819 # Cloud Mountain Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id CloudMountainCommander1 = -55248,90496,-2536,0,36102 CloudMountainCommander2 = -55791,91856,-2792,0,36096 CloudMountainCommander3 = -54168,92604,-2784,49196,36099 @@ -196,7 +189,7 @@ CloudMountainFlag2 = -53237,91537,-2664,9819 CloudMountainFlag3 = -53112,91537,-2664,9819 # Tanor Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id TanorCommander1 = 58480,139648,-1464,0,36135 TanorCommander2 = 61864,139257,-1728,46896,36129 TanorCommander3 = 59436,140834,-1720,47296,36132 @@ -206,7 +199,7 @@ TanorFlag2 = 60362,139742,-1597,9819 TanorFlag3 = 60467,139727,-1597,9819 # DragonSpine Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id DragonspineCommander1 = 13184,94928,-3144,0,36166 DragonspineCommander2 = 9472,94992,-3392,0,36160 DragonspineCommander3 = 12829,96214,-3392,49152,36163 @@ -216,7 +209,7 @@ DragonspineFlag2 = 11527,95301,-3264,9819 DragonspineFlag3 = 11623,95311,-3264,9819 # Antharas Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id AntharasCommander1 = 79440,88752,-2600,0,36202 AntharasCommander2 = 77262,91704,-2856,5112,36196 AntharasCommander3 = 80929,90510,-2856,40192,36199 @@ -227,7 +220,7 @@ AntharasFlag2 = 79528,91187,-2728,9819 AntharasFlag3 = 79580,91095,-2728,9819 # Western Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id WesternCommander1 = 113481,-16058,-712,0,36240 WesternCommander2 = 109872,-16624,-968,16384,36234 WesternCommander3 = 112601,-13933,-960,49152,36237 @@ -238,7 +231,7 @@ WesternFlag2 = 111380,-14820,-839,9819 WesternFlag3 = 111480,-14820,-839,9819 # Hunters Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id HuntersCommander1 = 123232,94400,-1856,0,36278 HuntersCommander2 = 122688,95760,-2112,0,36272 HuntersCommander3 = 124305,96528,-2104,49151,36275 @@ -249,7 +242,7 @@ HuntersFlag2 = 125255,95455,-1984,9819 HuntersFlag3 = 125355,95455,-1984,9819 # Aaru Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id AaruCommander1 = 74288,186912,-2296,0,36311 AaruCommander2 = 71392,184720,-2552,5528,36305 AaruCommander3 = 71542,186410,-2552,55088,36308 @@ -259,7 +252,7 @@ AaruFlag2 = 73923,186247,-2424,9819 AaruFlag3 = 72833,186178,-2424,9819 # Demon Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id DemonCommander1 = 100752,-53664,-360,0,36347 DemonCommander2 = 100688,-57440,-616,16384,36341 DemonCommander3 = 99484,-54027,-616,0,36344 @@ -269,7 +262,7 @@ DemonFlag2 = 100400,-55301,-488,9819 DemonFlag3 = 100400,-55201,-488,9819 # Monastic Fortress -# Commander syntax: NameCommanderN = x,y,z,heading,npc_id +# Commander syntax: NameCommanderN = x,y,z,heading,npc_id MonasticCommander1 = 73680,-95456,-1144,0,36385 MonasticCommander2 = 70189,-93935,-1400,61576,36379 MonasticCommander3 = 73831,-94119,-1400,45536,36382 diff --git a/L2J_Server/dist/game/config/idfactory.properties b/L2J_Server/dist/game/config/idfactory.properties new file mode 100644 index 00000000..40da98a2 --- /dev/null +++ b/L2J_Server/dist/game/config/idfactory.properties @@ -0,0 +1,26 @@ +# --------------------------------------------------------------------------- +# ID Factory Settings +# --------------------------------------------------------------------------- +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# Standard Settings +# --------------------------------------------------------------------------- +# Allows selection of new Classes for storage of World Objects. +# This may help servers with large amounts of players receiving error messages related to the L2ObjectHashMap and L2ObejctHashSet classes. +# +# By default it uses the new method, to use the old classes add the comment tag at the front of each of the following two lines. +L2Map = WorldObjectMap +L2Set = WorldObjectSet + +# Tell server which IDFactory Class to use: +# Compaction = Original method +# BitSet = One non compaction method +# Stack = Another non compaction method +# Default: BitSet +IDFactory = BitSet + +# Check for bad ids in the database on server boot up. +# Much faster load time without it, but may cause problems. +# Default: True +BadIdChecking = True \ No newline at end of file diff --git a/L2J_Server/dist/game/config/ipconfig.xml b/L2J_Server/dist/game/config/ipconfig.xml new file mode 100644 index 00000000..94e450ab --- /dev/null +++ b/L2J_Server/dist/game/config/ipconfig.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/config/ipconfig.xsd b/L2J_Server/dist/game/config/ipconfig.xsd similarity index 91% rename from src/main/resources/config/ipconfig.xsd rename to L2J_Server/dist/game/config/ipconfig.xsd index 62801e07..44dd05e6 100644 --- a/src/main/resources/config/ipconfig.xsd +++ b/L2J_Server/dist/game/config/ipconfig.xsd @@ -2,8 +2,8 @@ - - + + diff --git a/L2J_Server/dist/game/config/l2jmods.properties b/L2J_Server/dist/game/config/l2jmods.properties new file mode 100644 index 00000000..563834d6 --- /dev/null +++ b/L2J_Server/dist/game/config/l2jmods.properties @@ -0,0 +1,428 @@ +# --------------------------------------------------------------------------- +# L2JMODS - non-retail-like systems that have been integrated into the L2J project. +# Be warned that there may be no support for these mods beyond the original author's assistance. +# --------------------------------------------------------------------------- +# Champion mobs - Turn random mobs into Champions +# --------------------------------------------------------------------------- +# Enable/Disable Champion Mob System. +ChampionEnable = False + +# Force Champion mobs to be passive? +# To leave champion mobs to default/Agressive, set to False. +# To set all champion mobs to Passive, set True. +ChampionPassive = False + +# % chance for a mob to became champion (0 to disable). +ChampionFrequency = 5 + +# Title of all Champion Mobs. +ChampionTitle = Champion + +# Min and max levels allowed for a mob to be a Champion mob. +ChampionMinLevel = 20 +ChampionMaxLevel = 70 + +# Hp multiplier for Champion mobs. +ChampionHp = 8 + +# Hp Regen Multiplier for Champion mobs. +ChampionHpRegen = 1.0 + +# Standard rewards multiplier for Champion mobs. +ChampionRewards = 8 + +# Adena & Seal Stone rewards multiplier for Champion mobs. +ChampionAdenasRewards = 1.0 + +# P. Attack and M. Attack bonus for Champion mobs. +ChampionAtk = 1.0 + +# Physical/Magical Attack Speed bonus for Champion mobs. +ChampionSpdAtk = 1.0 + +# Specified reward item ID +ChampionRewardItemID = 6393 + +# The amount of the specified reward a player will receive if they are awarded the item. +ChampionRewardItemQty = 1 + +# % Chance to obtain a specified reward item from a HIGHER lvl Champion mob. +ChampionRewardLowerLvlItemChance = 0 + +# % Chance to obtain a specified reward item from a LOWER lvl Champion mob. +ChampionRewardHigherLvlItemChance = 0 + +# Do you want to enable the vitality calculation when killing champion mobs? +# Be aware that it can lead to huge unbalance on your server, your rate for that mob would +# then be "mobXP x serverRate x vitalityRate x championXpRate +# Notes: +# Works only if EnableVitality = True +# Default: False +ChampionEnableVitality = False + +# Enable spawning of the champions in instances +# Default = False +ChampionEnableInInstances = False + +# --------------------------------------------------------------------------- +# Wedding System (by evill33t) +# --------------------------------------------------------------------------- +# WARNING: this mod require custom NPC table support turned on ! +# CustomNpcTable = True in General.properties +# --------------------------------------------------------------------------- +# Wedding Manager ID: 50007 +# +# First part - "Engagement" +# 1) Target the player that you want to make a couple with. +# 2) Use the voice command ".engage nameofyourpartner" then press enter. +# 3) If the target player has you on listed as a friend (ie. you are in each other's friends list) a popup will appear with an engagement request along with a system message that you want to be engaged with him/her. +# 4) If the target player accepts the engagement invitation, you will be engaged. +# +# Second part - "Marriage" +# 1) Once two players are engaged, they can speak to Andromeda, the Wedding Priest in the Hot Springs Guild House (Goddard Area). +# (You may need Formal Wear and Adena to pay wedding fees!) +# 2) Each player needs to speak to the NPC and make the request to be married. +# 3) Once done, fireworks will display and the two players will be married. +# +# Afterwards you can use the voice command ".gotolove nameofyourpartner" to teleport to your partner if you're married (there may also be a fee which can be specified below) +# +# If you want to cancel your Engagement/Marriage, use the voice command ".divorce nameofyourpartner". +# +# If you're married you have to pay a specified % of your adena to your partner. +# +# If a player attempts to become engaged to another player while married they may suffer a penalty if it's enabled below. +# --------------------------------------------------------------------------- +# Enable/Disable Wedding System +AllowWedding = False + +# Amount of Adena required to get married +WeddingPrice = 250000000 + +# Enable/Disable punishing of players who attempt to be engaged to other players while married. +WeddingPunishInfidelity = True + +# Enable/Disable teleport function for married couples. +WeddingTeleport = True + +# Amount of Adena required to teleport to spouse. +WeddingTeleportPrice = 50000 + +# Time before character is teleported after using the skill. +WeddingTeleportDuration = 60 + +# Enable/Disable same sex marriages. +WeddingAllowSameSex = False + +# Require players to wear formal wear to be married? +WeddingFormalWear = True + +# Amount of Adena (%) a player must pay to the other to get divorced. +WeddingDivorceCosts = 20 + + +# --------------------------------------------------------------------------- +# Team vs. Team Event Engine (by FBIagent) +# --------------------------------------------------------------------------- +# WARNING: this mod require custom NPC table support turned on ! +# CustomNpcTable = True in General.properties +# --------------------------------------------------------------------------- +# Enable/Disable TvTEvent System +TvTEventEnabled = False + +# TvT in instance +TvTEventInInstance = False + +# Name of the instance file for TvT +TvTEventInstanceFile = coliseum.xml + +# Times TvT will occur (24h format). +TvTEventInterval = 9:00,15:00,21:00,3:00 + +# Registration timer from start of event (in minutes). +TvTEventParticipationTime = 60 + +# Event running time (in minutes). +TvTEventRunningTime = 20 + +# TvT Event NPC (create a custom npc of type L2TvTEventNpc). +TvTEventParticipationNpcId = 70010 + +# TvT Event Participation Fee (itemId, number). Fee is not returned. +# Example: 57,100000 +# Default = none +TvTEventParticipationFee = 0,0 + +# Location for TvTEvent NPC to spawn in form x,y,z[,heading] +TvTEventParticipationNpcCoordinates = 83425,148585,-3406 + +# Min/Max amount of players allowed in each team. +TvTEventMinPlayersInTeams = 1 +TvTEventMaxPlayersInTeams = 20 + +# Min/Max level of players that may join the event. +TvTEventMinPlayerLevel = 1 +TvTEventMaxPlayerLevel = 85 + +# Repsawn and exit delay timers (in seconds). +TvTEventRespawnTeleportDelay = 10 +TvTEventStartLeaveTeleportDelay = 10 + +# First Team - Name, Start/Death x,y,z location. +TvTEventTeam1Name = Team1 +TvTEventTeam1Coordinates = 148695,46725,-3414 + +# Second Team - Name, Start/Death x,y,z location. +TvTEventTeam2Name = Team2 +TvTEventTeam2Coordinates = 149999,46728,-3414 + +# Reward for winning team. +# Example: TvTEventReward = itemId,amount;itemId,amount;itemId,amount +TvTEventReward = 57,100000 + +# TvTEvent Rules +TvTEventTargetTeamMembersAllowed = True +TvTEventScrollsAllowed = False +TvTEventPotionsAllowed = False +TvTEventSummonByItemAllowed = False + +# Door ID's to open/close on start/end. +# Not supported in instance, use xml template for defining doors. +# Example: TvTDoorsToOpen = 1;2;3;4;5;6 +TvTDoorsToOpen = +TvTDoorsToClose = + +# Should both teams get reward if there's a tie? +TvTRewardTeamTie = False + +# Participant's effects handling on teleport/death. +# Effects lasting through death never removed. +# 0 - always remove all effects. +# 1 - remove all effects only during port to event (noblesse blessing can be used) +# 2 - never remove any effect +# Default: 0 +TvTEventEffectsRemoval = 0 + +# Fighter-class participants will be buffed with those buffs each respawn +# Format: skill1Id,skill1Level;skill2Id,skill2Level... +# Example: 1504,1;1501,1;1502,1;1499,1 +TvTEventFighterBuffs = + +# Mage-class participants will be buffed with those buffs each respawn +# Format: skill1Id,skill1Level;skill2Id,skill2Level... +# Example: 1504,1;1500,1;1501,1;1085,3 +TvTEventMageBuffs = + +# Maximum number of allowed participants per IP address (dualbox check) +# Default: 0 (no limits) +TvTEventMaxParticipantsPerIP = 0 + +# Voiced command (.tvt) working during TVT event to get information about event status +TvTAllowVoicedInfoCommand = false + + +# --------------------------------------------------------------------------- +# L2J Banking System +# --------------------------------------------------------------------------- +# Enable/Disable Banking System +BankingEnabled = False + +# Amount of Goldbars a player gets when they use the ".deposit" command. Also the same amount they will lose with ".withdraw". +BankingGoldbarCount = 1 + +# Amount of Adena a player gets when they use the ".withdraw" command. Also the same amount they will lose with ".deposit". +BankingAdenaCount = 500000000 + + +# --------------------------------------------------------------------------- +# Warehouse Sorting +# Shows Withdraw Window sorted by ItemType (Armor/Weapon/Spellbook....) +# --------------------------------------------------------------------------- +EnableWarehouseSortingClan = False +EnableWarehouseSortingPrivate = False + + +# --------------------------------------------------------------------------- +# Offline trade/craft +# --------------------------------------------------------------------------- +# Option to enable or disable offline trade feature. +# Enable -> true, Disable -> false +OfflineTradeEnable = False + +# Option to enable or disable offline craft feature. +# Enable -> true, Disable -> false +OfflineCraftEnable = False + +# If set to True, name color will be changed then entering offline mode +OfflineSetNameColor = False + +# Color of the name in offline mode (if OfflineSetNameColor = True) +OfflineNameColor = 808080 + +# Allow fame for characters in offline mode +# Enable -> true, Disable -> false +OfflineFame = True + +#Restore offline traders/crafters after restart/shutdown. Default: false. +RestoreOffliners = False + +#Do not restore offline characters, after OfflineMaxDays days spent from first restore. +#Require server restart to disconnect expired shops. +#0 = disabled (always restore). +#Default: 10 +OfflineMaxDays = 10 + +#Disconnect shop after finished selling, buying. +#Default: True +OfflineDisconnectFinished = True + +# --------------------------------------------------------------------------- +# Mana Drugs/Potions +# --------------------------------------------------------------------------- +# This option will enable core support for: +# Mana Drug (item ID 726), using skill ID 10000. +# Mana Potion (item ID 728), using skill ID 10001. +EnableManaPotionSupport = False + + +# --------------------------------------------------------------------------- +# Display Server Time +# --------------------------------------------------------------------------- +# This option will enable displaying of the local server time for /time command. +DisplayServerTime = False + + +# --------------------------------------------------------------------------- +# Welcome message +# --------------------------------------------------------------------------- +# Show screen welcome message on character login +# Default: False +ScreenWelcomeMessageEnable = False + +# Screen welcome message text to show on character login if enabled +# ('#' for a new line, but message can have max 2 lines) +ScreenWelcomeMessageText = Welcome to L2J server! + +# Show screen welcome message for x seconds when character log in to game if enabled +ScreenWelcomeMessageTime = 10 + + +# --------------------------------------------------------------------------- +# AntiFeed +# --------------------------------------------------------------------------- +# This option will enable antifeed for pvp/pk/clanrep points. +# Default: False +AntiFeedEnable = False + +# If set to True, kills from dualbox will not increase pvp/pk points +# and clan reputation will not be transferred. +# Default: True +AntiFeedDualbox = True + +# If set to True, server will count disconnected (unable to determine ip address) +# as dualbox. +# Default: True +AntiFeedDisconnectedAsDualbox = True + +# If character died faster than timeout - pvp/pk points for killer will not increase +# and clan reputation will not be transferred. +# Setting to 0 will disable this feature. +# Default: 120 seconds. +AntiFeedInterval = 120 + + +# --------------------------------------------------------------------------- +# Pvp/pk Announce +# --------------------------------------------------------------------------- +# Default: False +AnnouncePkPvP = False + +# Announce this as normal system message +# Default: True +AnnouncePkPvPNormalMessage = True + +# PK message template +# variables: $killer, $target +AnnouncePkMsg = $killer has slaughtered $target + +# Pvp message template +# variables: $killer, $target +AnnouncePvpMsg = $killer has defeated $target + + +# --------------------------------------------------------------------------- +# Chat Moderation +# --------------------------------------------------------------------------- +# This option will enable using of the voice commands .banchat and .unbanchat +# for players with corresponding access level (default: 7). +# Check access_levels.sql and admin_command_access_rights for details. +# Default: False +ChatAdmin = False + + +# --------------------------------------------------------------------------- +# Multilingual support +# --------------------------------------------------------------------------- +# Enable or disable multilingual support. +# Default: False +MultiLangEnable = False + +# List of allowed languages, semicolon separated. +# Default: en +MultiLangAllowed = en;ru + +# Default language, if not defined. +# Default: en +MultiLangDefault = en + +# Enable or disable voice command .lang for changing languages on the fly. +# Default: True +MultiLangVoiceCommand = True + +# Enable or disable multilingual SystemMessages support. +# Default: False +MultiLangSystemMessageEnable = False + +# List of allowed languages for SystemMessages, semicolon separated. +# Default: +MultiLangSystemMessageAllowed = + + +# --------------------------------------------------------------------------- +# Walker/Bot protection +# --------------------------------------------------------------------------- +# Basic protection against L2Walker. +# Default: False +L2WalkerProtection = False + + +# --------------------------------------------------------------------------- +# Debug enable/disable voice command +# --------------------------------------------------------------------------- +# This option will enable voice command .debug allowing players +# to turn on/off debugging on self only. +# (admin command //debug can enable debugging on any character) +# Use admin_command_access_rights table for defining access rights. +# Default: False +DebugVoiceCommand = False + + +# --------------------------------------------------------------------------- +# Dualbox Check +# --------------------------------------------------------------------------- +# Maximum number of players per IP address allowed to enter game. +# Default: 0 (unlimited) +DualboxCheckMaxPlayersPerIP = 0 + +# Maximum number of players per IP address allowed to participate in olympiad. +# Default: 0 (unlimited) +DualboxCheckMaxOlympiadParticipantsPerIP = 0 + +# Whitelist of the addresses for dualbox checks. +# Format: Address1,Number1;Address2,Number2... +# Network address can be number (127.0.0.1) or symbolic (localhost) formats. +# Additional connection number added to the global limits for this address. +# For example, if number of TvT event participants per IP address set to the 1 (no dualbox) +# and whitelist contains "l2jserver.com,2" then number of allowed participants from l2jserver.com +# will be 1+2=3. Use 0 or negative value for unlimited number of connections. +# Default: 127.0.0.1,0 (no limits from localhost) +DualboxCheckWhitelist = 127.0.0.1,0 diff --git a/L2J_Server/dist/game/config/olympiad.properties b/L2J_Server/dist/game/config/olympiad.properties new file mode 100644 index 00000000..048d3fd8 --- /dev/null +++ b/L2J_Server/dist/game/config/olympiad.properties @@ -0,0 +1,139 @@ +# --------------------------------------------------------------------------- +# Olympiad Settings +# --------------------------------------------------------------------------- +# The defaults are set to be retail-like. +# If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. +# Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# Olympiad Start Time in Military hours Default 6pm (18) +# Default: 18 +AltOlyStartTime = 18 + +# Olympiad Start Time for Min's, Default 00 so at the start of the hour. +# Default: 00 +AltOlyMin = 00 + +# Olympiad Competition Period, Default 6 hours. +# (If set different, should be increment by 10mins) +# Default: 21600000 +AltOlyCPeriod = 21600000 + +# Olympiad Battle Period, Default 6 minutes. +# Default: 360000 +AltOlyBattle = 360000 + +# Olympiad Weekly Period, Default 1 week +# Used for adding points to nobles +# Default: 604800000 +AltOlyWPeriod = 604800000 + +# Olympiad Validation Period, Default 24 Hours. +# Default: 86400000 +AltOlyVPeriod = 86400000 + +# Points for reaching Noblesse for the first time +# Default: 18 +AltOlyStartPoints = 18 + +# Points every week +# Default: 3 +AltOlyWeeklyPoints = 3 + +# Required number of participants for the class based games +# Default: 5 +AltOlyClassedParticipants = 5 + +# Required number of participants for the non-class based games +# Default: 9 +AltOlyNonClassedParticipants = 9 + +# Required number of participants for the 3x3 teams games +# Default: 9 +AltOlyTeamsParticipants = 9 + +# Number used for displaying amount of registered participants, messages "Fewer than ..." or "More than ...". +# 0 for displaying digits instead of text phrase (old style). +# Default: 100 +AltOlyRegistrationDisplayNumber = 100 + +# Reward for the class based games +# Format: itemId1,itemNum1;itemId2,itemNum2... +# Default: 13722,50 +AltOlyClassedReward = 13722,50 + +# Reward for the non-class based games +# Format: itemId1,itemNum1;itemId2,itemNum2... +# Default: 13722,40 +AltOlyNonClassedReward = 13722,40 + +# Reward for the 3x3 teams games +# Format: itemId1,itemNum1;itemId2,itemNum2... +# Default: 13722,85 +AltOlyTeamReward = 13722,85 + +# ItemId used for exchanging to the points. +# Default: 13722 +AltOlyCompRewItem = 13722 + +# The minimal matches you need to participate to receive point rewards +# Default: 9 +AltOlyMinMatchesForPoints = 9 + +# Rate to exchange points to reward item. +# Default: 1000 +AltOlyGPPerPoint = 1000 + +# Noblesse points awarded to Heros. +# Default: 180 +AltOlyHeroPoints = 180 + +# Noblesse points awarded to Rank 1 members. +# Default: 120 +AltOlyRank1Points = 120 + +# Noblesse points awarded to Rank 2 members. +# Default: 80 +AltOlyRank2Points = 80 + +# Noblesse points awarded to Rank 3 members. +# Default: 55 +AltOlyRank3Points = 55 + +# Noblesse points awarded to Rank 4 members. +# Default: 35 +AltOlyRank4Points = 35 + +# Noblesse points awarded to Rank 5 members. +# Default: 20 +AltOlyRank5Points = 20 + +# Maximum points that player can gain/lose on a match. +# Default: 10 +AltOlyMaxPoints = 10 + +# Hero tables show last month's winners or current status. +# Default: True +AltOlyShowMonthlyWinners = True + +# Olympiad Managers announce each start of fight. +# Default: True +AltOlyAnnounceGames = True + +# Restrict specified items in Olympiad. ItemID's need to be separated with a comma (ex. 1,200,350) +# Equipped items will be moved to inventory during port. +# Default: 6611,6612,6613,6614,6615,6616,6617,6618,6619,6620,6621,9388,9389,9390,17049,17050,17051,17052,17053,17054,17055,17056,17057,17058,17059,17060,17061,20759,20775,20776,20777,20778,14774 +AltOlyRestrictedItems = 6611,6612,6613,6614,6615,6616,6617,6618,6619,6620,6621,9388,9389,9390,17049,17050,17051,17052,17053,17054,17055,17056,17057,17058,17059,17060,17061,20759,20775,20776,20777,20778,14774 + +# Enchant limit for items during Olympiad battles. Disabled = -1. +# Default: -1 +AltOlyEnchantLimit = -1 + +# Log all Olympiad fights and outcome to olympiad.csv file. +# Default: False +AltOlyLogFights = False + +# Time to wait before teleported to arena. +# Default: 120 +AltOlyWaitTime = 120 \ No newline at end of file diff --git a/L2J_Server/dist/game/config/pvp.properties b/L2J_Server/dist/game/config/pvp.properties new file mode 100644 index 00000000..056fda80 --- /dev/null +++ b/L2J_Server/dist/game/config/pvp.properties @@ -0,0 +1,60 @@ +# --------------------------------------------------------------------------- +# PvP Related Settings +# --------------------------------------------------------------------------- +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# Karma Variables +# --------------------------------------------------------------------------- +# Karma gain/loss +# Default: 240 +MinKarma = 240 + +# Default: 10000 +MaxKarma = 10000 + +# The number to divide the xp recieved by to calculate karma lost on xp gain/loss. +# Default: 260 +XPDivider = 260 + +# The minimum karma lost if 0 karma is to be removed. +# Default: 0 +BaseKarmaLost = 0 + + +# --------------------------------------------------------------------------- +# PK'er Drop Settings +# --------------------------------------------------------------------------- +# Default: False +CanGMDropEquipment = False + +# Warning: Make sure the lists do NOT CONTAIN +# trailing spaces or spaces between the numbers! +# List of pet items we cannot drop. +# Default: 2375,3500,3501,3502,4422,4423,4424,4425,6648,6649,6650 +ListOfPetItems = 2375,3500,3501,3502,4422,4423,4424,4425,6648,6649,6650 + +# Lists of items which should NEVER be dropped (note, adena will +# never be dropped) whether on this list or not +# Default: 57,1147,425,1146,461,10,2368,7,6,2370,2369,6842,6611,6612,6613,6614,6615,6616,6617,6618,6619,6620,6621,7694,8181,5575,7694,9388,9389,9390 +ListOfNonDroppableItems = 57,1147,425,1146,461,10,2368,7,6,2370,2369,6842,6611,6612,6613,6614,6615,6616,6617,6618,6619,6620,6621,7694,8181,5575,7694,9388,9389,9390 + +# Default: 6 +MinimumPKRequiredToDrop = 6 + + +# --------------------------------------------------------------------------- +# Misc. +# --------------------------------------------------------------------------- +# Should we award a pvp point for killing a player with karma? +# Default: False +AwardPKKillPVPPoint = False + +# How much time one stays in PvP mode after hitting an innocent (in ms) +# Default: 120000 +PvPVsNormalTime = 120000 + +# Length one stays in PvP mode after hitting a purple player (in ms) +# Default: 60000 +PvPVsPvPTime = 60000 \ No newline at end of file diff --git a/L2J_Server/dist/game/config/rates.properties b/L2J_Server/dist/game/config/rates.properties new file mode 100644 index 00000000..0cf2e0ad --- /dev/null +++ b/L2J_Server/dist/game/config/rates.properties @@ -0,0 +1,166 @@ +# --------------------------------------------------------------------------- +# Rate Settings +# --------------------------------------------------------------------------- +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# Standard Settings (Retail value = 1) +# --------------------------------------------------------------------------- +# Experience multiplier +RateXp = 1 +# Skill points multiplier +RateSp = 1 +# Experience multiplier (Party) +RatePartyXp = 1 +# Skill points multiplier (Party) +RatePartySp = 1 +RateConsumableCost = 1 +RateDropItems = 1 +RateRaidDropItems = 1 +RateDropSpoil = 1 +RateDropManor = 1 +RateExtractFish = 1 +RateKarmaExpLost = 1 +RateSiegeGuardsPrice = 1 + +# Quest Multipliers +# Warning: Many quests need to be rewritten +# for this setting to work properly. + +# Quest item drop multiplier +RateQuestDrop = 1 + +# Exp/SP reward multipliers +RateQuestRewardXP = 1 +RateQuestRewardSP = 1 + +# Adena reward multiplier +RateQuestRewardAdena = 1 + +# Use additional item multipliers? +# Default: False +UseQuestRewardMultipliers = False + +# Default reward multiplier +# When UseRewardMultipliers=False - default multiplier is used for any reward +# When UseRewardMultipliers=True - default multiplier is used for all items not affected by additional multipliers +# Default: 1 +RateQuestReward = 1 + +# Additional quest-reward multipliers based on item type +RateQuestRewardPotion = 1 +RateQuestRewardScroll = 1 +RateQuestRewardRecipe = 1 +RateQuestRewardMaterial = 1 + +# List of items affected by custom drop rate by id, used now for Adena rate too. +# Usage: itemId1,dropChance1;itemId2,dropChance2;... +# Note: Make sure the lists do NOT CONTAIN trailing spaces or spaces between the numbers! +# Example for Raid boss 1x jewelry: 6656,1;6657,1;6658,1;6659,1;6660,1;6661,1;6662,1;8191,1;10170,1;10314,1; +# Default: 57,1 +RateDropItemsById = 57,1 + +# --------------------------------------------------------------------------- +# Vitality system rates. Works only if EnableVitality = True +# --------------------------------------------------------------------------- +# The following configures the xp multiplicator of each vitality level. Basically, you have +# 5 levels, the first one being 0. Official rates are: +# Level 1: 150% +# Level 2: 200% +# Level 3: 250% +# Level 4: 300% +# Take care setting these values according to your server rates, as the can lead to huge differences! +# Example with a server rate 15x and a level 4 vitality = 3. => final server rate = 45 (15x3)! +RateVitalityLevel1 = 1.5 +RateVitalityLevel2 = 2. +RateVitalityLevel3 = 2.5 +RateVitalityLevel4 = 3. + +# These options are to be used if you want to increase the vitality gain/lost for each mob you kills +# Default values are 1. +RateVitalityGain = 1. +RateVitalityLost = 1. + +# This defines how many times faster do the players regain their vitality when in peace zones +RateRecoveryPeaceZone = 1. + +# This defines how many times faster do the players regain their vitality when offline +# Note that you need to turn on "RecoverVitalityOnReconnect" to have this option effective +RateRecoveryOnReconnect = 4. + + +# --------------------------------------------------------------------------- +# Player Drops (values are set in PERCENTS) +# --------------------------------------------------------------------------- +PlayerDropLimit = 0 +# in % +PlayerRateDrop = 0 +# in % +PlayerRateDropItem = 0 +# in % +PlayerRateDropEquip = 0 +# in % +PlayerRateDropEquipWeapon = 0 + +# --------------------------------------------------------------------------- +# Player XP % lost on death. +# --------------------------------------------------------------------------- +# Must be defined an interval per each % following this: +# [limit1m,limit1M]-val1;[limit2m,limit2M]-val2;... +# For undefined intervals, default value is 1.0 . +# Example (Default settings): +# 0,39-7.0;40,75-4.0;76,76-2.5;77,77-2.0;78,78-1.5 +# This will set 7.0 from 0 to 39 (included), 4.00 from 40 to 75 (included), +# 2.5 to 76, 2.0 to 77 and 1.5 to 78 +# XP loss is affected by clan luck in core +PlayerXPPercentLost = 0,39-7.0;40,75-4.0;76,76-2.5;77,77-2.0;78,78-1.5 + +# Default: 10 +KarmaDropLimit = 10 + +# Default: 40 +KarmaRateDrop = 40 + +# Default: 50 +KarmaRateDropItem = 50 + +# Default: 40 +KarmaRateDropEquip = 40 + +# Default: 10 +KarmaRateDropEquipWeapon = 10 + + +# --------------------------------------------------------------------------- +# Pets (Default value = 1) +# --------------------------------------------------------------------------- +PetXpRate = 1 +PetFoodRate = 1 +SinEaterXpRate = 1 + + +# --------------------------------------------------------------------------- +# Herbs +# --------------------------------------------------------------------------- +# Herb of Power, Herb of Magic, Herb of Attack Speed, Herb of Casting Speed, Herb of Critical Attack, Herb of Speed +# Default: 1 +RateCommonHerbs = 1 + +# Herb of Life, Greater herb of Life, Superior Herb of Life +# Default: 1 +RateHpHerbs = 1 + +# Herb of Mana, Greater herb of Mana, Superior Herb of Mana +# Default: 1 +RateMpHerbs = 1 + +# Herb of Warrior, Herb of Mystic, Herb of Recovery +# Default: 1 +RateSpecialHerbs = 1 + +# Rate to configure the chance in percent vitality resplenishing herbs are dropping. +# Note you'll need to enable EnableVitalityHerbs +# Notes: +# Works only if EnableVitality = True +RateVitalityHerbs = 1 diff --git a/L2J_Server/dist/game/config/server.properties b/L2J_Server/dist/game/config/server.properties new file mode 100644 index 00000000..f849ffef --- /dev/null +++ b/L2J_Server/dist/game/config/server.properties @@ -0,0 +1,106 @@ +# --------------------------------------------------------------------------- +# Game Server Settings +# --------------------------------------------------------------------------- +# This is the server configuration file. Here you can set up the connection information for your server. +# This was written with the assumption that you are behind a router. +# Dumbed Down Definitions... +# LAN (LOCAL area network) - typically consists of computers connected to the same router as you. +# WAN (WIDE area network) - typically consists of computers OUTSIDE of your router (ie. the internet). +# x.x.x.x - Format of an IP address. Do not include the x'es into settings. Must be real numbers. +# --------------------------------------------------------------------------- +# Networking +# --------------------------------------------------------------------------- + +# Where's the Login server this gameserver should connect to +# WARNING: Please don't change default IPs here if you don't know what are you doing! +# WARNING: External/Internal IPs are now inside "ipconfig.xml" file. +# Default: 127.0.0.1 +LoginHost = 127.0.0.1 + +# TCP port the login server listen to for gameserver connection requests +# Default: 9014 +LoginPort = 9014 + +# Bind address for gameserver. You should not need to change it in most cases. +# WARNING: Please don't change default IPs here if you don't know what are you doing! +# WARNING: External/Internal IPs are now inside "ipconfig.xml" file. +# Default: * (0.0.0.0) +GameserverHostname = * + +# Default: 7777 +GameserverPort = 7777 + + +# --------------------------------------------------------------------------- +# Database +# --------------------------------------------------------------------------- +# Specify the appropriate driver and url for the database you're using. +# Examples: +# Driver = com.mysql.jdbc.Driver (default) +# Driver = org.hsqldb.jdbcDriver +# Driver = com.microsoft.sqlserver.jdbc.SQLServerDriver +Driver = com.mysql.jdbc.Driver +# Database URL +# URL = jdbc:mysql://localhost/l2jgs (default) +# URL = jdbc:hsqldb:hsql://localhost/l2jgs +# URL = jdbc:sqlserver://localhost/database = l2jgs/user = sa/password = +URL = jdbc:mysql://localhost/l2jgs +# Database user info (default is "root" but it's not recommended) +Login = root +# Database connection password +Password = + +# Default: 100 +MaximumDbConnections = 100 + +# Default: 0 +MaximumDbIdleTime = 0 + +# --------------------------------------------------------------------------- +# Misc Server Settings +# --------------------------------------------------------------------------- +# This is the server ID that the Game Server will request. +# Example: 1 = Bartz +# Default: 1 +RequestServerID = 1 + +# True = The Login Server will give an other ID to the server if the requested ID is already reserved. +# Default: True +AcceptAlternateID = True + +# Datapack root directory. +# Defaults to current directory from which the server is started. +DatapackRoot = . + +# Define how many players are allowed to play simultaneously on your server. +# Default: 100 +MaximumOnlineUsers = 100 + +# Numbers of protocol revisions that server allows to connect. +# Delimiter is ; +# WARNING: Changing the protocol revision may result in incompatible communication and many errors in game! +# Default: 216 +AllowedProtocolRevisions = 216 + + +# --------------------------------------------------------------------------- +# Misc Player Settings +# --------------------------------------------------------------------------- + +# Character name template. +# Examples: +# CnameTemplate = [A-Z][a-z]{3,3}[A-Za-z0-9]* +# The above setting will allow names with first capital letter, next three small letters, +# and any letter (case insensitive) or number, like OmfgWTF1 +# CnameTemplate = [A-Z][a-z]* +# The above setting will allow names only of letters with first one capital, like Omfgwtf +# Default .* (allows any symbol) +CnameTemplate = .* + +# This setting restricts names players can give to their pets. +# See CnameTemplate for details +PetNameTemplate = .* + +# Maximum number of characters per account. +# Default: 7 (client limit) +CharMaxNumber = 7 \ No newline at end of file diff --git a/L2J_Server/dist/game/config/siege.properties b/L2J_Server/dist/game/config/siege.properties new file mode 100644 index 00000000..9604c593 --- /dev/null +++ b/L2J_Server/dist/game/config/siege.properties @@ -0,0 +1,146 @@ +# --------------------------------------------------------------------------- +# Siege Settings +# --------------------------------------------------------------------------- +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# Standard Settings +# --------------------------------------------------------------------------- + +# Length of siege before the count down (in minutes). +# Default: 120 +SiegeLength = 120 + +# Maximum number of flags per clan. +# Default: 1 +MaxFlags = 1 + +# Minimum level to register. +# Default: 5 +SiegeClanMinLevel = 5 + +# Max number of clans that can register on each side. +# Default: 500 +AttackerMaxClans = 500 + +# Default: 500 +DefenderMaxClans = 500 + +# Respawn times (in miliseconds). +# Default: 0 +AttackerRespawn = 0 + +# Reward successful siege defense with blood alliance in clan warehouse +# Default: 0 +BloodAllianceReward = 0 + + +# --------------------------------------------------------------------------- +# Castle Control Tower Spawns +# --------------------------------------------------------------------------- +# Caste Artifacts and Control Towers spawns +# +# Control Towers syntax: NameControlTowerN=x,y,z,npc_id,hp +# Name - castle name +# N - number +# x,y,z - coords +# npc_id - id of template +# hp - maximum health +# +# Flame Towers syntax: NameFlameTowerN=x,y,z,npc_id,hp +# Name - castle name +# N - number +# x,y,z - coords +# npc_id - id of template +# hp - maximum health +# +# Artefacts syntax: NameArtefactN=x,y,z,heading,npc_id +# Name - castle name +# N - number +# x,y,z - coords +# heading +# npc_id - id of template + +# Gludio +GludioArtefact1=-18113,107972,-2480,16384,35063 +GludioFlameTower1=-18154,107591,-2560,13004,10000 +GludioFlameTower2=-19329,108154,-2384,13004,10000 +GludioControlTower1=-18325,112811,-2377,13002,10000 +GludioControlTower2=-18048,107098,-2378,13002,10000 +GludioControlTower3=-18113,108597,-2343,13002,10000 +GludioMaxMercenaries = 100 + +# Giran +GiranArtefact1=117950,145096,-2547,32768,35147 +GiranFlameTower1=118331,145055,-2627,13004,10000 +GiranFlameTower2=117768,143880,-2451,13004,10000 +GiranControlTower1=113115,144829,-2446,13002,10000 +GiranControlTower2=118828,145106,-2447,13002,10000 +GiranControlTower3=117329,145041,-2412,13002,10000 +GiranMaxMercenaries = 200 + +# Dion +DionArtefact1=22073,161778,-2674,49152,35105 +DionFlameTower1=22114,162159,-2754,13004,10000 +DionFlameTower2=23289,161596,-2578,13004,10000 +DionControlTower1=22285,156939,-2571,13002,10000 +DionControlTower2=22008,162652,-2572,13002,10000 +DionControlTower3=22073,161153,-2537,13002,10000 +DionMaxMercenaries = 150 + +# Oren +OrenArtefact1=8402,37191,-2274,32768,35189 +OrenFlameTower1=84407,37150,-2354,13004,10000 +OrenFlameTower2=83844,35975,-2178,13004,10000 +OrenControlTower1=79193,36977,-2167,13002,10000 +OrenControlTower2=84906,37254,-2168,13002,10000 +OrenControlTower3=83407,37189,-2133,13002,10000 +OrenMaxMercenaries = 300 + +# Aden +AdenArtefact1=147463,1529,-373,16384,35233 +AdenFlameTower1=149976,1583,-450,13004,10000 +AdenFlameTower2=144955,1603,-450,13004,10000 +AdenControlTower1=148774,2351,-389,13002,10000 +AdenControlTower2=147456,5724,158,13002,10000 +AdenControlTower3=146137,2352,-389,13002,10000 +AdenMaxMercenaries = 400 + +# Innadril +InnadrilArtefact1=116024,250557,-770,-16384,35279 +InnadrilFlameTower1=116065,250938,-850,13004,10000 +InnadrilFlameTower2=117240,250375,-674,13004,10000 +InnadrilControlTower1=116236,245718,-667,13002,10000 +InnadrilControlTower2=115959,251431,-667,13002,10000 +InnadrilControlTower3=116024,249932,-633,13002,10000 +InnadrilMaxMercenaries = 400 + +# Goddard +GoddardArtefact1=146601,-50441,-1505,32768,35322 +GoddardArtefact2=148353,-50457,-1505,0,35323 +GoddardFlameTower1=148144,-46992,-1609,13004,10000 +GoddardFlameTower2=146784,-46992,-1609,13004,10000 +GoddardControlTower1=144672,-48832,-1742,13002,10000 +GoddardControlTower2=150240,-48832,-1742,13002,10000 +GoddardControlTower3=147456,-49200,-1619,13002,10000 +GoddardMaxMercenaries = 400 + +# Rune +RuneArtefact1=9132,-49151,1095,0,35469 +RuneFlameTower1=12864,-47440,-1087,13004,10000 +RuneFlameTower2=12225,-50767,1248,13004,10000 +RuneControlTower1=16565,-50318,-641,13002,10000 +RuneControlTower2=16559,-48000,-641,13002,10000 +RuneControlTower3=10775,-48481,83,13002,10000 +RuneMaxMercenaries = 400 + +# Schuttgart +SchuttgartArtefact1=76674,-154521,228,32768,35515 +SchuttgartArtefact2=78440,-154521,228,0,35514 +SchuttgartFlameTower1=78233,-151037,120,13004,10000 +SchuttgartFlameTower2=76872,-151043,120,13004,10000 +SchuttgartControlTower1=80334,-152898,-8,13002,10000 +SchuttgartControlTower2=74775,-152928,-8,13002,10000 +SchuttgartControlTower3=77547,-153246,112,13002,10000 +SchuttgartMaxMercenaries = 400 diff --git a/L2J_Server/dist/game/config/telnet.properties b/L2J_Server/dist/game/config/telnet.properties new file mode 100644 index 00000000..cd638259 --- /dev/null +++ b/L2J_Server/dist/game/config/telnet.properties @@ -0,0 +1,27 @@ +# --------------------------------------------------------------------------- +# Telnet Settings +# --------------------------------------------------------------------------- +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# Allows text based monitoring and administration of L2J GS/LS +# by using a telnet client. Communication protocol is insecure +# and you should use SSL tunnels, VPN, etc. if you plan to connect +# over non-trusted channels. +# Default: False +EnableTelnet = False + +# This is the port L2J should listen to for incoming telnet +# requests. +# Default: 54321 +StatusPort = 54321 + +# If the following is not set, a random password is generated on server startup. +# Usage: StatusPW = somePass +StatusPW = + +# This list can contain IPs or Hosts of clients you wish to allow. Hostnames must be resolvable to an IP. +# Example: 0.0.0.0,host,0.0.0.1,host2,host3,host4,0.0.0.3 +# Default: 127.0.0.1,localhost,::1 +ListOfHosts = 127.0.0.1,localhost,::1 \ No newline at end of file diff --git a/src/main/resources/config/territorywar.properties b/L2J_Server/dist/game/config/territorywar.properties similarity index 77% rename from src/main/resources/config/territorywar.properties rename to L2J_Server/dist/game/config/territorywar.properties index 9abdf484..ad267193 100644 --- a/src/main/resources/config/territorywar.properties +++ b/L2J_Server/dist/game/config/territorywar.properties @@ -1,17 +1,14 @@ # --------------------------------------------------------------------------- # TerritoryWar Settings # --------------------------------------------------------------------------- -# The defaults are set to be retail-like. -# If you modify any of these settings, your server will deviate from being retail-like. +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. # Warning: -# Please take extreme caution when changing anything. -# Also, please understand what you are changing before you do so on a live server. - +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. # --------------------------------------------------------------------------- # Standard Settings # --------------------------------------------------------------------------- -# Length of siege before the count-down (in minutes). +# Length of siege before the count down (in minutes). # Default: 120 WarLength = 120 diff --git a/L2J_Server/dist/game/startGameServer.bat b/L2J_Server/dist/game/startGameServer.bat new file mode 100644 index 00000000..e29d1bc2 --- /dev/null +++ b/L2J_Server/dist/game/startGameServer.bat @@ -0,0 +1,29 @@ +@echo off +title Game Server Console +:start +echo Starting L2J Game Server. +echo. +REM ------------------------------------- +REM Default parameters for a basic server. +java -Djava.util.logging.manager=com.l2jserver.util.L2LogManager -Xms1024m -Xmx1024m -cp ./../libs/*;l2jserver.jar com.l2jserver.gameserver.GameServer +REM +REM If you have a big server and lots of memory, you could experiment for example with +REM java -server -Xmx1536m -Xms1024m -Xmn512m -XX:PermSize=256m -XX:SurvivorRatio=8 -Xnoclassgc -XX:+AggressiveOpts +REM ------------------------------------- +if ERRORLEVEL 2 goto restart +if ERRORLEVEL 1 goto error +goto end +:restart +echo. +echo Admin Restart ... +echo. +goto start +:error +echo. +echo Server terminated abnormaly +echo. +:end +echo. +echo server terminated +echo. +pause diff --git a/L2J_Server/dist/game/startGameServer.sh b/L2J_Server/dist/game/startGameServer.sh new file mode 100644 index 00000000..bff2be7f --- /dev/null +++ b/L2J_Server/dist/game/startGameServer.sh @@ -0,0 +1,3 @@ +#! /bin/sh + +./GameServer_loop.sh & \ No newline at end of file diff --git a/L2J_Server/dist/images/add.png b/L2J_Server/dist/images/add.png new file mode 100644 index 00000000..6332fefe Binary files /dev/null and b/L2J_Server/dist/images/add.png differ diff --git a/L2J_Server/dist/images/cross.png b/L2J_Server/dist/images/cross.png new file mode 100644 index 00000000..1514d51a Binary files /dev/null and b/L2J_Server/dist/images/cross.png differ diff --git a/L2J_Server/dist/images/disk.png b/L2J_Server/dist/images/disk.png new file mode 100644 index 00000000..99d532e8 Binary files /dev/null and b/L2J_Server/dist/images/disk.png differ diff --git a/L2J_Server/dist/images/help.png b/L2J_Server/dist/images/help.png new file mode 100644 index 00000000..5c870176 Binary files /dev/null and b/L2J_Server/dist/images/help.png differ diff --git a/L2J_Server/dist/images/l2j.png b/L2J_Server/dist/images/l2j.png new file mode 100644 index 00000000..28d25220 Binary files /dev/null and b/L2J_Server/dist/images/l2j.png differ diff --git a/L2J_Server/dist/images/l2jserverlogo.png b/L2J_Server/dist/images/l2jserverlogo.png new file mode 100644 index 00000000..8272dedc Binary files /dev/null and b/L2J_Server/dist/images/l2jserverlogo.png differ diff --git a/L2J_Server/dist/languages/configurator/Configurator.properties b/L2J_Server/dist/languages/configurator/Configurator.properties new file mode 100644 index 00000000..8c644065 --- /dev/null +++ b/L2J_Server/dist/languages/configurator/Configurator.properties @@ -0,0 +1,23 @@ +language = English +toolName = Server Configuration Tool + +success = Configuration saved successfully +error = Error +errorReading = Error reading +errorSaving = Error saving +reason = Reason: +yes = Yes +no = No + +fileMenu = File +exitItem = Exit +helpMenu = Help +aboutItem = About + +save = Save +cancel = Cancel + +credits = © 2008-2009 L2J Team. All rights reserved. +language = Language: English +icons = Icons by http://www.famfamfam.com +translation = Translation: L2J Team diff --git a/L2J_Server/dist/languages/gsregister/GSRegister.properties b/L2J_Server/dist/languages/gsregister/GSRegister.properties new file mode 100644 index 00000000..2c073030 --- /dev/null +++ b/L2J_Server/dist/languages/gsregister/GSRegister.properties @@ -0,0 +1,80 @@ +language = English +toolName = GS Registering Tool + +error = Error +reason = Reason: +yes = Yes +no = No + +fileMenu = File +exitItem = Exit +helpMenu = Help +aboutItem = About + +btnRemove = Remove +btnRemoveAll = Remove All +btnRegister = Register GameServer +confirmRemoveTitle = Confirm Removal +confirmRemoveText = Are you sure you want to remove GameServer %d - %s? +confirmRemoveAllText = Are you sure you want to remove ALL GameServers? +gsListRetrieveError = There was an error while attempting to retrieve the list of currently registered GameServers. + +gsName = GameServer Name +gsAction = Action + +registerGS = Register GameServer +serverName = Server name: +save = Save +cancel = Cancel +saveHexId = The hexid.txt file should be saved on GameServer's 'configs' folder to properly complete this process. +hexidDest = Choose hexid file destination folder... + +purpose = Allows to register/remove GameServers from LoginServer. +options = Options: +fallbackOpt = If during the register operation the specified GameServer ID is in use, an attempt with the first available ID will be made. +forceOpt = Forces GameServer register operation to overwrite a previous registration on the specified ID, if necessary. +cmdOpt = Forces this application to run in console mode, even if GUI is supported. +helpOpt = Shows this help message and exits. +languageOpt = Attempts to use the language for the specified locale, on failure reverts to auto-detection. +registerOpt1 = Registers a GameServer on ID and saves the hexid.txt file on . +registerOpt2 = You can provide a negative value for to register under the first available ID. +registerOpt3 = Nothing is done if is already in use, unless --force or --fallback is used. +unregisterOpt = Removes the GameServer specified by , use "all" to remove all currently registered GameServers. +wrongUnregisterArg = wrong argument for GameServer removal, specify a numeric ID or "all" without quotes to remove all. +noAction = No action to be taken. +checkingIdInUse = Checking if GameServer ID %d is in use... +removingGsId = Removing GameServer ID: %d +forcingRegistration = Forcing registration on ID %d, an existing registration on this ID is being overwritten. +fallingBack = Trying to register on the first available ID. +registrationOk = GameServer successfully registered on LoginServer with ID %d. +unregisterOk = GameServer ID: %d was successfully removed from LoginServer. +unregisterAllOk = All GameServers were successfully removed. +noFreeId = There was no free ID available to register. +sqlErrorRegister = An SQL error occurred while trying to register the GameServer. +ioErrorRegister = An error saving the hexid file occurred while trying to register the GameServer. +errorRegister = An error occurred while trying to register the GameServer. +errorUnregister = An error occurred while trying to remove the GameServer. +sqlErrorUnregister = An SQL error occurred while trying to remove the GameServer. +sqlErrorUnregisterAll = An SQL error occurred while trying to remove ALL GameServers. +noServerNames = No available names for GameServer, verify servername.xml file exists in the LoginServer folder. +noNameForId = No name for ID: %d +idIsNotFree = This ID is not available. +noServerForId = No GameServer is registered on ID: %d + + +cmdMenuRegister = Register GameServer +cmdMenuListNames = List GameServers Names and IDs +cmdMenuRemoveGS = Remove GameServer +cmdMenuRemoveAll = Remove ALL GameServers +cmdMenuExit = Exit +yourChoice = Choice: +invalidChoice = Invalid Choice: %s +gsInUse = In Use +gsFree = Free +enterDesiredId = Enter desired ID: + +credits = © 2008-2009 L2J Team. All rights reserved. +language = Language: English +icons = Icons by http://www.famfamfam.com +translation = Translation: L2J Team +bugReports = Bug Reports: \ No newline at end of file diff --git a/L2J_Server/dist/login/LoginServer_loop.sh b/L2J_Server/dist/login/LoginServer_loop.sh new file mode 100644 index 00000000..fda7b5e3 --- /dev/null +++ b/L2J_Server/dist/login/LoginServer_loop.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +err=1 +until [ $err == 0 ]; +do + [ -f log/java0.log.0 ] && mv log/java0.log.0 "log/`date +%Y-%m-%d_%H-%M-%S`_java.log" + [ -f log/stdout.log ] && mv log/stdout.log "log/`date +%Y-%m-%d_%H-%M-%S`_stdout.log" + nice -n -2 java -Xms128m -Xmx128m -cp ./../libs/*:l2jlogin.jar com.l2jserver.loginserver.L2LoginServer > log/stdout.log 2>&1 + err=$? +# /etc/init.d/mysql restart + sleep 10; +done diff --git a/L2J_Server/dist/login/RegisterGameServer.bat b/L2J_Server/dist/login/RegisterGameServer.bat new file mode 100644 index 00000000..21d8307d --- /dev/null +++ b/L2J_Server/dist/login/RegisterGameServer.bat @@ -0,0 +1,5 @@ +@echo off +color 17 +cls +java -Djava.util.logging.config.file=console.cfg -cp ./../libs/*;l2jlogin.jar com.l2jserver.gsregistering.BaseGameServerRegister -c +exit \ No newline at end of file diff --git a/L2J_Server/dist/login/RegisterGameServer.exe b/L2J_Server/dist/login/RegisterGameServer.exe new file mode 100644 index 00000000..171d5b97 Binary files /dev/null and b/L2J_Server/dist/login/RegisterGameServer.exe differ diff --git a/L2J_Server/dist/login/RegisterGameServer.sh b/L2J_Server/dist/login/RegisterGameServer.sh new file mode 100644 index 00000000..804dd429 --- /dev/null +++ b/L2J_Server/dist/login/RegisterGameServer.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -Djava.util.logging.config.file=console.cfg -cp ./../libs/*:l2jlogin.jar com.l2jserver.gsregistering.GameServerRegister -c \ No newline at end of file diff --git a/L2J_Server/dist/login/banned_ip.cfg b/L2J_Server/dist/login/banned_ip.cfg new file mode 100644 index 00000000..ee951522 --- /dev/null +++ b/L2J_Server/dist/login/banned_ip.cfg @@ -0,0 +1,7 @@ +# Subnets can be defined like 123.45.0.0 +# Comments after # are ignored +# Expiration timestamp can be added after IP for when to disable the ban +# Example: +# 192.168.0.0 1237618800000 # Bans all address starting with 192.168 and will expire on 21 Mar 2009 07:00:00 GMT +10.255.255.255 +10.254.254.254 diff --git a/L2J_Server/dist/login/config/loginserver.properties b/L2J_Server/dist/login/config/loginserver.properties new file mode 100644 index 00000000..ce73e21a --- /dev/null +++ b/L2J_Server/dist/login/config/loginserver.properties @@ -0,0 +1,110 @@ +# --------------------------------------------------------------------------- +# Login Server Settings +# --------------------------------------------------------------------------- +# This is the server configuration file. Here you can set up the connection information for your server. +# This was written with the assumption that you are behind a router. +# Dumbed Down Definitions... +# LAN (LOCAL area network) - typically consists of computers connected to the same router as you. +# WAN (WIDE area network) - typically consists of computers OUTSIDE of your router (ie. the internet). +# x.x.x.x - Format of an IP address. Do not include the x'es into settings. Must be real numbers. +# --------------------------------------------------------------------------- +# Networking +# --------------------------------------------------------------------------- + +# Bind ip of the LoginServer, use * to bind on all available IPs +# WARNING: Please don't change default IPs here if you don't know what are you doing! +# WARNING: External/Internal IPs are now inside "ipconfig.xml" file. +# Default: * (0.0.0.0) +LoginserverHostname = * + +# Default: 2106 +LoginserverPort = 2106 + +# The address on which login will listen for GameServers, use * to bind on all available IPs +# WARNING: Please don't change default IPs here if you don't know what are you doing! +# WARNING: External/Internal IPs are now inside "ipconfig.xml" file. +# Default: * (0.0.0.0) +LoginHostname = * + +# The port on which login will listen for GameServers +# Default: 9014 +LoginPort = 9014 + + +# --------------------------------------------------------------------------- +# Security +# --------------------------------------------------------------------------- +# Default: True +LogLoginController = True + +# How many times you can provide an invalid account/pass before the IP gets banned. +# Default: 10 +LoginTryBeforeBan = 10 + +# Time you won't be able to login back again after LoginTryBeforeBan tries to login. +# Default: 600 (10 minutes) +LoginBlockAfterBan = 600 + +# If set to True any GameServer can register on your login's free slots +# Default: False +AcceptNewGameServer = False + +# Flood Protection. All values are in MILISECONDS. +# Default: True +EnableFloodProtection = True + +# Default: 15 +FastConnectionLimit = 15 + +# Default: 700 +NormalConnectionTime = 700 + +# Default: 350 +FastConnectionTime = 350 + +# Default: 50 +MaxConnectionPerIP = 50 + + +# --------------------------------------------------------------------------- +# Database +# --------------------------------------------------------------------------- +# Specify the appropriate driver and url for the database you're using. +# Examples: +# Driver = com.mysql.jdbc.Driver (default) +# Driver = org.hsqldb.jdbcDriver +# Driver = com.microsoft.sqlserver.jdbc.SQLServerDriver +Driver = com.mysql.jdbc.Driver +# Database URL +# URL = jdbc:mysql://localhost/l2jls (default) +# URL = jdbc:hsqldb:hsql://localhost/l2jls +# URL = jdbc:sqlserver://localhost/database = l2jls/user = sa/password = +URL = jdbc:mysql://localhost/l2jls +# Database user info (default is "root" but it's not recommended) +Login = root +# Database connection password +Password = + +# Default: 10 +MaximumDbConnections = 10 + +# Default: 0 +MaximumDbIdleTime = 0 + + +# --------------------------------------------------------------------------- +# Misc. +# --------------------------------------------------------------------------- +# If False, the license (after the login) will not be shown. +# Default: True +ShowLicence = True + +# Default: True +AutoCreateAccounts = True + + +# --------------------------------------------------------------------------- +# Developer Settings +# --------------------------------------------------------------------------- +# Default: False +Debug = False \ No newline at end of file diff --git a/L2J_Server/dist/login/config/telnet.properties b/L2J_Server/dist/login/config/telnet.properties new file mode 100644 index 00000000..836ad5db --- /dev/null +++ b/L2J_Server/dist/login/config/telnet.properties @@ -0,0 +1,27 @@ +# --------------------------------------------------------------------------- +# Telnet Settings +# --------------------------------------------------------------------------- +# The defaults are set to be retail-like. If you modify any of these settings your server will deviate from being retail-like. +# Warning: +# Please take extreme caution when changing anything. Also please understand what you are changing before you do so on a live server. +# --------------------------------------------------------------------------- +# Allows text based monitoring and administration of L2J GS/LS +# by using a telnet client. Communication protocol is insecure +# and you should use SSL tunnels, VPN, etc. if you plan to connect +# over non-trusted channels. +# Default: False +EnableTelnet = False + +# This is the port L2J should listen to for incoming telnet +# requests. +# Default: 12345 +StatusPort = 12345 + +# If the following is not set, a random password is generated on server startup. +# Usage: StatusPW = somePass +StatusPW = + +# This list can contain IPs or Hosts of clients you wish to allow. Hostnames must be resolvable to an IP. +# Example: 0.0.0.0,host,0.0.0.1,host2,host3,host4,0.0.0.3 +# Default: 127.0.0.1,localhost,::1 +ListOfHosts = 127.0.0.1,localhost,::1 \ No newline at end of file diff --git a/L2J_Server/dist/login/startLoginServer.bat b/L2J_Server/dist/login/startLoginServer.bat new file mode 100644 index 00000000..2732a0c6 --- /dev/null +++ b/L2J_Server/dist/login/startLoginServer.bat @@ -0,0 +1,23 @@ +@echo off +title Login Server Console +:start +echo Starting L2J Login Server. +echo. +java -Xms128m -Xmx128m -cp ./../libs/*;l2jlogin.jar com.l2jserver.loginserver.L2LoginServer +if ERRORLEVEL 2 goto restart +if ERRORLEVEL 1 goto error +goto end +:restart +echo. +echo Admin Restart ... +echo. +goto start +:error +echo. +echo Server terminated abnormaly +echo. +:end +echo. +echo server terminated +echo. +pause diff --git a/L2J_Server/dist/login/startLoginServer.sh b/L2J_Server/dist/login/startLoginServer.sh new file mode 100644 index 00000000..7aa25ba3 --- /dev/null +++ b/L2J_Server/dist/login/startLoginServer.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./LoginServer_loop.sh & diff --git a/L2J_Server/dist/login/startSQLAccountManager.bat b/L2J_Server/dist/login/startSQLAccountManager.bat new file mode 100644 index 00000000..b518436a --- /dev/null +++ b/L2J_Server/dist/login/startSQLAccountManager.bat @@ -0,0 +1,23 @@ +@echo off +cls +title L2J - SQL Account Manager +color 17 +java -Djava.util.logging.config.file=console.cfg -cp ./../libs/*;l2jlogin.jar com.l2jserver.accountmanager.SQLAccountManager 2> NUL +if %errorlevel% == 0 ( +echo. +echo Execution succesful +echo. +) else ( +echo. +echo An error has ocurred while running the L2J Account Manager! +echo. +echo Possible reasons for this to happen: +echo. +echo - Missing .jar files or ../libs directory. +echo - MySQL server not running or incorrect MySQL settings: +echo check ./config/loginserver.properties +echo - Wrong data types or values out of range were provided: +echo specify correct values for each required field +echo. +) +pause diff --git a/L2J_Server/dist/login/startSQLAccountManager.sh b/L2J_Server/dist/login/startSQLAccountManager.sh new file mode 100644 index 00000000..332f34fa --- /dev/null +++ b/L2J_Server/dist/login/startSQLAccountManager.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -Djava.util.logging.config.file=console.cfg -cp ./../libs/*:l2jlogin.jar com.l2jserver.accountmanager.SQLAccountManager diff --git a/L2J_Server/java/com/l2jserver/Config.java b/L2J_Server/java/com/l2jserver/Config.java new file mode 100644 index 00000000..f66ed95b --- /dev/null +++ b/L2J_Server/java/com/l2jserver/Config.java @@ -0,0 +1,4390 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver; + +import gnu.trove.TIntArrayList; +import gnu.trove.TIntFloatHashMap; +import gnu.trove.TIntIntHashMap; +import gnu.trove.TIntObjectHashMap; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.InputStream; +import java.io.LineNumberReader; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import com.l2jserver.gameserver.util.FloodProtectorConfig; +import com.l2jserver.util.L2Properties; +import com.l2jserver.util.StringUtil; + +public final class Config +{ + protected static final Logger _log = Logger.getLogger(Config.class.getName()); + + // -------------------------------------------------- + // L2J Property File Definitions + // -------------------------------------------------- + public static final String CHARACTER_CONFIG_FILE = "./config/Character.properties"; + public static final String FEATURE_CONFIG_FILE = "./config/Feature.properties"; + public static final String FORTSIEGE_CONFIGURATION_FILE = "./config/fortsiege.properties"; + public static final String GENERAL_CONFIG_FILE = "./config/General.properties"; + public static final String HEXID_FILE = "./config/hexid.txt"; + public static final String ID_CONFIG_FILE = "./config/idfactory.properties"; + public static final String L2JMOD_CONFIG_FILE = "./config/l2jmods.properties"; + public static final String LOGIN_CONFIGURATION_FILE = "./config/loginserver.properties"; + public static final String NPC_CONFIG_FILE = "./config/NPC.properties"; + public static final String PVP_CONFIG_FILE = "./config/pvp.properties"; + public static final String RATES_CONFIG_FILE = "./config/rates.properties"; + public static final String CONFIGURATION_FILE = "./config/server.properties"; + public static final String IP_CONFIG_FILE = "./config/ipconfig.xml"; + public static final String SIEGE_CONFIGURATION_FILE = "./config/siege.properties"; + public static final String TW_CONFIGURATION_FILE = "./config/territorywar.properties"; + public static final String TELNET_FILE = "./config/telnet.properties"; + public static final String FLOOD_PROTECTOR_FILE = "./config/floodprotector.properties"; + public static final String MMO_CONFIG_FILE = "./config/mmo.properties"; + public static final String OLYMPIAD_CONFIG_FILE = "./config/olympiad.properties"; + public static final String COMMUNITY_CONFIGURATION_FILE = "./config/CommunityServer.properties"; + public static final String GRANDBOSS_CONFIG_FILE = "./config/Grandboss.properties"; + public static final String GRACIASEEDS_CONFIG_FILE = "./config/GraciaSeeds.properties"; + public static final String CHAT_FILTER_FILE = "./config/chatfilter.txt"; + + // -------------------------------------------------- + // L2J Variable Definitions + // -------------------------------------------------- + public static int MASTERACCESS_LEVEL; + public static int MASTERACCESS_NAME_COLOR; + public static int MASTERACCESS_TITLE_COLOR; + public static boolean ALT_GAME_DELEVEL; + public static boolean DECREASE_SKILL_LEVEL; + public static double ALT_WEIGHT_LIMIT; + public static int RUN_SPD_BOOST; + public static int DEATH_PENALTY_CHANCE; + public static double RESPAWN_RESTORE_CP; + public static double RESPAWN_RESTORE_HP; + public static double RESPAWN_RESTORE_MP; + public static boolean ALT_GAME_TIREDNESS; + public static boolean ENABLE_MODIFY_SKILL_DURATION; + public static TIntIntHashMap SKILL_DURATION_LIST; + public static boolean ENABLE_MODIFY_SKILL_REUSE; + public static TIntIntHashMap SKILL_REUSE_LIST; + public static boolean AUTO_LEARN_SKILLS; + public static boolean AUTO_LEARN_FS_SKILLS; + public static boolean AUTO_LOOT_HERBS; + public static byte BUFFS_MAX_AMOUNT; + public static byte DANCES_MAX_AMOUNT; + public static boolean DANCE_CANCEL_BUFF; + public static boolean DANCE_CONSUME_ADDITIONAL_MP; + public static boolean AUTO_LEARN_DIVINE_INSPIRATION; + public static boolean ALT_GAME_CANCEL_BOW; + public static boolean ALT_GAME_CANCEL_CAST; + public static boolean EFFECT_CANCELING; + public static boolean ALT_GAME_MAGICFAILURES; + public static int PLAYER_FAKEDEATH_UP_PROTECTION; + public static boolean STORE_SKILL_COOLTIME; + public static boolean SUBCLASS_STORE_SKILL_COOLTIME; + public static boolean ALT_GAME_SHIELD_BLOCKS; + public static int ALT_PERFECT_SHLD_BLOCK; + public static boolean ALLOW_CLASS_MASTERS; + public static ClassMasterSettings CLASS_MASTER_SETTINGS; + public static boolean ALLOW_ENTIRE_TREE; + public static boolean ALTERNATE_CLASS_MASTER; + public static boolean LIFE_CRYSTAL_NEEDED; + public static boolean ES_SP_BOOK_NEEDED; + public static boolean DIVINE_SP_BOOK_NEEDED; + public static boolean ALT_GAME_SKILL_LEARN; + public static boolean ALT_GAME_SUBCLASS_WITHOUT_QUESTS; + public static boolean ALT_GAME_SUBCLASS_EVERYWHERE; + public static boolean ALLOW_TRANSFORM_WITHOUT_QUEST; + public static int MAX_RUN_SPEED; + public static int MAX_PCRIT_RATE; + public static int MAX_MCRIT_RATE; + public static int FEE_DELETE_TRANSFER_SKILLS; + public static int FEE_DELETE_SUBCLASS_SKILLS; + public static int MAX_PATK_SPEED; + public static int MAX_MATK_SPEED; + public static int MAX_EVASION; + public static byte MAX_SUBCLASS; + public static byte MAX_SUBCLASS_LEVEL; + public static int MAX_PVTSTORESELL_SLOTS_DWARF; + public static int MAX_PVTSTORESELL_SLOTS_OTHER; + public static int MAX_PVTSTOREBUY_SLOTS_DWARF; + public static int MAX_PVTSTOREBUY_SLOTS_OTHER; + public static int INVENTORY_MAXIMUM_NO_DWARF; + public static int INVENTORY_MAXIMUM_DWARF; + public static int INVENTORY_MAXIMUM_GM; + public static int INVENTORY_MAXIMUM_QUEST_ITEMS; + public static int WAREHOUSE_SLOTS_DWARF; + public static int WAREHOUSE_SLOTS_NO_DWARF; + public static int WAREHOUSE_SLOTS_CLAN; + public static boolean ALT_GAME_KARMA_PLAYER_CAN_BE_KILLED_IN_PEACEZONE; + public static boolean ALT_GAME_KARMA_PLAYER_CAN_SHOP; + public static boolean ALT_GAME_KARMA_PLAYER_CAN_TELEPORT; + public static boolean ALT_GAME_KARMA_PLAYER_CAN_USE_GK; + public static boolean ALT_GAME_KARMA_PLAYER_CAN_TRADE; + public static boolean ALT_GAME_KARMA_PLAYER_CAN_USE_WAREHOUSE; + public static int MAX_PERSONAL_FAME_POINTS; + public static int FORTRESS_ZONE_FAME_TASK_FREQUENCY; + public static int FORTRESS_ZONE_FAME_AQUIRE_POINTS; + public static int CASTLE_ZONE_FAME_TASK_FREQUENCY; + public static int CASTLE_ZONE_FAME_AQUIRE_POINTS; + public static boolean FAME_FOR_DEAD_PLAYERS; + public static boolean IS_CRAFTING_ENABLED; + public static boolean CRAFT_MASTERWORK; + public static int DWARF_RECIPE_LIMIT; + public static int COMMON_RECIPE_LIMIT; + public static boolean ALT_GAME_CREATION; + public static double ALT_GAME_CREATION_SPEED; + public static double ALT_GAME_CREATION_XP_RATE; + public static double ALT_GAME_CREATION_RARE_XPSP_RATE; + public static double ALT_GAME_CREATION_SP_RATE; + public static boolean ALT_BLACKSMITH_USE_RECIPES; + public static int ALT_CLAN_JOIN_DAYS; + public static int ALT_CLAN_CREATE_DAYS; + public static int ALT_CLAN_DISSOLVE_DAYS; + public static int ALT_ALLY_JOIN_DAYS_WHEN_LEAVED; + public static int ALT_ALLY_JOIN_DAYS_WHEN_DISMISSED; + public static int ALT_ACCEPT_CLAN_DAYS_WHEN_DISMISSED; + public static int ALT_CREATE_ALLY_DAYS_WHEN_DISSOLVED; + public static int ALT_MAX_NUM_OF_CLANS_IN_ALLY; + public static int ALT_CLAN_MEMBERS_FOR_WAR; + public static boolean ALT_MEMBERS_CAN_WITHDRAW_FROM_CLANWH; + public static boolean REMOVE_CASTLE_CIRCLETS; + public static int ALT_PARTY_RANGE; + public static int ALT_PARTY_RANGE2; + public static long STARTING_ADENA; + public static byte STARTING_LEVEL; + public static int STARTING_SP; + public static boolean AUTO_LOOT; + public static boolean AUTO_LOOT_RAIDS; + public static int LOOT_RAIDS_PRIVILEGE_INTERVAL; + public static int LOOT_RAIDS_PRIVILEGE_CC_SIZE; + public static int UNSTUCK_INTERVAL; + public static int TELEPORT_WATCHDOG_TIMEOUT; + public static int PLAYER_SPAWN_PROTECTION; + public static ArrayList SPAWN_PROTECTION_ALLOWED_ITEMS; + public static int PLAYER_TELEPORT_PROTECTION; + public static boolean RANDOM_RESPAWN_IN_TOWN_ENABLED; + public static boolean OFFSET_ON_TELEPORT_ENABLED; + public static int MAX_OFFSET_ON_TELEPORT; + public static boolean RESTORE_PLAYER_INSTANCE; + public static boolean ALLOW_SUMMON_TO_INSTANCE; + public static boolean PETITIONING_ALLOWED; + public static int MAX_PETITIONS_PER_PLAYER; + public static int MAX_PETITIONS_PENDING; + public static boolean ALT_GAME_FREE_TELEPORT; + public static int DELETE_DAYS; + public static float ALT_GAME_EXPONENT_XP; + public static float ALT_GAME_EXPONENT_SP; + public static String PARTY_XP_CUTOFF_METHOD; + public static double PARTY_XP_CUTOFF_PERCENT; + public static int PARTY_XP_CUTOFF_LEVEL; + public static boolean DISABLE_TUTORIAL; + public static boolean EXPERTISE_PENALTY; + public static boolean STORE_RECIPE_SHOPLIST; + public static boolean STORE_UI_SETTINGS; + public static String[] FORBIDDEN_NAMES; + + // -------------------------------------------------- + // ClanHall Settings + // -------------------------------------------------- + public static long CH_TELE_FEE_RATIO; + public static int CH_TELE1_FEE; + public static int CH_TELE2_FEE; + public static long CH_ITEM_FEE_RATIO; + public static int CH_ITEM1_FEE; + public static int CH_ITEM2_FEE; + public static int CH_ITEM3_FEE; + public static long CH_MPREG_FEE_RATIO; + public static int CH_MPREG1_FEE; + public static int CH_MPREG2_FEE; + public static int CH_MPREG3_FEE; + public static int CH_MPREG4_FEE; + public static int CH_MPREG5_FEE; + public static long CH_HPREG_FEE_RATIO; + public static int CH_HPREG1_FEE; + public static int CH_HPREG2_FEE; + public static int CH_HPREG3_FEE; + public static int CH_HPREG4_FEE; + public static int CH_HPREG5_FEE; + public static int CH_HPREG6_FEE; + public static int CH_HPREG7_FEE; + public static int CH_HPREG8_FEE; + public static int CH_HPREG9_FEE; + public static int CH_HPREG10_FEE; + public static int CH_HPREG11_FEE; + public static int CH_HPREG12_FEE; + public static int CH_HPREG13_FEE; + public static long CH_EXPREG_FEE_RATIO; + public static int CH_EXPREG1_FEE; + public static int CH_EXPREG2_FEE; + public static int CH_EXPREG3_FEE; + public static int CH_EXPREG4_FEE; + public static int CH_EXPREG5_FEE; + public static int CH_EXPREG6_FEE; + public static int CH_EXPREG7_FEE; + public static long CH_SUPPORT_FEE_RATIO; + public static int CH_SUPPORT1_FEE; + public static int CH_SUPPORT2_FEE; + public static int CH_SUPPORT3_FEE; + public static int CH_SUPPORT4_FEE; + public static int CH_SUPPORT5_FEE; + public static int CH_SUPPORT6_FEE; + public static int CH_SUPPORT7_FEE; + public static int CH_SUPPORT8_FEE; + public static long CH_CURTAIN_FEE_RATIO; + public static int CH_CURTAIN1_FEE; + public static int CH_CURTAIN2_FEE; + public static long CH_FRONT_FEE_RATIO; + public static int CH_FRONT1_FEE; + public static int CH_FRONT2_FEE; + public static boolean CH_BUFF_FREE; + + // -------------------------------------------------- + // Castle Settings + // -------------------------------------------------- + public static long CS_TELE_FEE_RATIO; + public static int CS_TELE1_FEE; + public static int CS_TELE2_FEE; + public static long CS_MPREG_FEE_RATIO; + public static int CS_MPREG1_FEE; + public static int CS_MPREG2_FEE; + public static int CS_MPREG3_FEE; + public static int CS_MPREG4_FEE; + public static long CS_HPREG_FEE_RATIO; + public static int CS_HPREG1_FEE; + public static int CS_HPREG2_FEE; + public static int CS_HPREG3_FEE; + public static int CS_HPREG4_FEE; + public static int CS_HPREG5_FEE; + public static long CS_EXPREG_FEE_RATIO; + public static int CS_EXPREG1_FEE; + public static int CS_EXPREG2_FEE; + public static int CS_EXPREG3_FEE; + public static int CS_EXPREG4_FEE; + public static long CS_SUPPORT_FEE_RATIO; + public static int CS_SUPPORT1_FEE; + public static int CS_SUPPORT2_FEE; + public static int CS_SUPPORT3_FEE; + public static int CS_SUPPORT4_FEE; + public static List CL_SET_SIEGE_TIME_LIST; + public static List SIEGE_HOUR_LIST_MORNING; + public static List SIEGE_HOUR_LIST_AFTERNOON; + + // -------------------------------------------------- + // Fortress Settings + // -------------------------------------------------- + public static long FS_TELE_FEE_RATIO; + public static int FS_TELE1_FEE; + public static int FS_TELE2_FEE; + public static long FS_MPREG_FEE_RATIO; + public static int FS_MPREG1_FEE; + public static int FS_MPREG2_FEE; + public static long FS_HPREG_FEE_RATIO; + public static int FS_HPREG1_FEE; + public static int FS_HPREG2_FEE; + public static long FS_EXPREG_FEE_RATIO; + public static int FS_EXPREG1_FEE; + public static int FS_EXPREG2_FEE; + public static long FS_SUPPORT_FEE_RATIO; + public static int FS_SUPPORT1_FEE; + public static int FS_SUPPORT2_FEE; + public static int FS_BLOOD_OATH_COUNT; + public static int FS_UPDATE_FRQ; + public static int FS_MAX_SUPPLY_LEVEL; + public static int FS_FEE_FOR_CASTLE; + public static int FS_MAX_OWN_TIME; + + // -------------------------------------------------- + // Feature Settings + // -------------------------------------------------- + public static int TAKE_FORT_POINTS; + public static int LOOSE_FORT_POINTS; + public static int TAKE_CASTLE_POINTS; + public static int LOOSE_CASTLE_POINTS; + public static int CASTLE_DEFENDED_POINTS; + public static int FESTIVAL_WIN_POINTS; + public static int HERO_POINTS; + public static int ROYAL_GUARD_COST; + public static int KNIGHT_UNIT_COST; + public static int KNIGHT_REINFORCE_COST; + public static int BALLISTA_POINTS; + public static int BLOODALLIANCE_POINTS; + public static int BLOODOATH_POINTS; + public static int KNIGHTSEPAULETTE_POINTS; + public static int REPUTATION_SCORE_PER_KILL; + public static int JOIN_ACADEMY_MIN_REP_SCORE; + public static int JOIN_ACADEMY_MAX_REP_SCORE; + public static int RAID_RANKING_1ST; + public static int RAID_RANKING_2ND; + public static int RAID_RANKING_3RD; + public static int RAID_RANKING_4TH; + public static int RAID_RANKING_5TH; + public static int RAID_RANKING_6TH; + public static int RAID_RANKING_7TH; + public static int RAID_RANKING_8TH; + public static int RAID_RANKING_9TH; + public static int RAID_RANKING_10TH; + public static int RAID_RANKING_UP_TO_50TH; + public static int RAID_RANKING_UP_TO_100TH; + public static int CLAN_LEVEL_6_COST; + public static int CLAN_LEVEL_7_COST; + public static int CLAN_LEVEL_8_COST; + public static int CLAN_LEVEL_9_COST; + public static int CLAN_LEVEL_10_COST; + public static int CLAN_LEVEL_11_COST; + public static int CLAN_LEVEL_6_REQUIREMENT; + public static int CLAN_LEVEL_7_REQUIREMENT; + public static int CLAN_LEVEL_8_REQUIREMENT; + public static int CLAN_LEVEL_9_REQUIREMENT; + public static int CLAN_LEVEL_10_REQUIREMENT; + public static int CLAN_LEVEL_11_REQUIREMENT; + public static boolean ALLOW_WYVERN_DURING_SIEGE; + + // -------------------------------------------------- + // General Settings + // -------------------------------------------------- + public static boolean EVERYBODY_HAS_ADMIN_RIGHTS; + public static boolean DISPLAY_SERVER_VERSION; + public static boolean SERVER_LIST_BRACKET; + public static int SERVER_LIST_TYPE; + public static int SERVER_LIST_AGE; + public static boolean SERVER_GMONLY; + public static boolean GM_HERO_AURA; + public static boolean GM_STARTUP_INVULNERABLE; + public static boolean GM_STARTUP_INVISIBLE; + public static boolean GM_STARTUP_SILENCE; + public static boolean GM_STARTUP_AUTO_LIST; + public static boolean GM_STARTUP_DIET_MODE; + public static String GM_ADMIN_MENU_STYLE; + public static boolean GM_ITEM_RESTRICTION; + public static boolean GM_SKILL_RESTRICTION; + public static boolean GM_TRADE_RESTRICTED_ITEMS; + public static boolean GM_RESTART_FIGHTING; + public static boolean GM_ANNOUNCER_NAME; + public static boolean GM_GIVE_SPECIAL_SKILLS; + public static boolean BYPASS_VALIDATION; + public static boolean GAMEGUARD_ENFORCE; + public static boolean GAMEGUARD_PROHIBITACTION; + public static boolean LOG_CHAT; + public static boolean LOG_ITEMS; + public static boolean LOG_ITEMS_SMALL_LOG; + public static boolean LOG_ITEM_ENCHANTS; + public static boolean LOG_SKILL_ENCHANTS; + public static boolean GMAUDIT; + public static boolean LOG_GAME_DAMAGE; + public static int LOG_GAME_DAMAGE_THRESHOLD; + public static boolean SKILL_CHECK_ENABLE; + public static boolean SKILL_CHECK_REMOVE; + public static boolean SKILL_CHECK_GM; + public static boolean DEBUG; + public static boolean PACKET_HANDLER_DEBUG; + public static boolean DEVELOPER; + public static boolean ACCEPT_GEOEDITOR_CONN; + public static boolean TEST_SERVER; + public static boolean ALT_DEV_NO_HANDLERS; + public static boolean ALT_DEV_NO_QUESTS; + public static boolean ALT_DEV_NO_SPAWNS; + public static int THREAD_P_EFFECTS; + public static int THREAD_P_GENERAL; + public static int GENERAL_PACKET_THREAD_CORE_SIZE; + public static int IO_PACKET_THREAD_CORE_SIZE; + public static int GENERAL_THREAD_CORE_SIZE; + public static int AI_MAX_THREAD; + public static int CLIENT_PACKET_QUEUE_SIZE; + public static int CLIENT_PACKET_QUEUE_MAX_BURST_SIZE; + public static int CLIENT_PACKET_QUEUE_MAX_PACKETS_PER_SECOND; + public static int CLIENT_PACKET_QUEUE_MEASURE_INTERVAL; + public static int CLIENT_PACKET_QUEUE_MAX_AVERAGE_PACKETS_PER_SECOND; + public static int CLIENT_PACKET_QUEUE_MAX_FLOODS_PER_MIN; + public static int CLIENT_PACKET_QUEUE_MAX_OVERFLOWS_PER_MIN; + public static int CLIENT_PACKET_QUEUE_MAX_UNDERFLOWS_PER_MIN; + public static int CLIENT_PACKET_QUEUE_MAX_UNKNOWN_PER_MIN; + public static boolean DEADLOCK_DETECTOR; + public static int DEADLOCK_CHECK_INTERVAL; + public static boolean RESTART_ON_DEADLOCK; + public static boolean ALLOW_DISCARDITEM; + public static int AUTODESTROY_ITEM_AFTER; + public static int HERB_AUTO_DESTROY_TIME; + public static TIntArrayList LIST_PROTECTED_ITEMS; + public static boolean DATABASE_CLEAN_UP; + public static long CONNECTION_CLOSE_TIME; + public static int CHAR_STORE_INTERVAL; + public static boolean LAZY_ITEMS_UPDATE; + public static boolean UPDATE_ITEMS_ON_CHAR_STORE; + public static boolean DESTROY_DROPPED_PLAYER_ITEM; + public static boolean DESTROY_EQUIPABLE_PLAYER_ITEM; + public static boolean SAVE_DROPPED_ITEM; + public static boolean EMPTY_DROPPED_ITEM_TABLE_AFTER_LOAD; + public static int SAVE_DROPPED_ITEM_INTERVAL; + public static boolean CLEAR_DROPPED_ITEM_TABLE; + public static boolean AUTODELETE_INVALID_QUEST_DATA; + public static boolean PRECISE_DROP_CALCULATION; + public static boolean MULTIPLE_ITEM_DROP; + public static boolean FORCE_INVENTORY_UPDATE; + public static boolean LAZY_CACHE; + public static boolean CACHE_CHAR_NAMES; + public static int MIN_NPC_ANIMATION; + public static int MAX_NPC_ANIMATION; + public static int MIN_MONSTER_ANIMATION; + public static int MAX_MONSTER_ANIMATION; + public static int COORD_SYNCHRONIZE; + public static boolean ENABLE_FALLING_DAMAGE; + public static boolean GRIDS_ALWAYS_ON; + public static int GRID_NEIGHBOR_TURNON_TIME; + public static int GRID_NEIGHBOR_TURNOFF_TIME; + public static int WORLD_X_MIN; + public static int WORLD_X_MAX; + public static int WORLD_Y_MIN; + public static int WORLD_Y_MAX; + public static int GEODATA; + public static boolean GEODATA_CELLFINDING; + public static String PATHFIND_BUFFERS; + public static float LOW_WEIGHT; + public static float MEDIUM_WEIGHT; + public static float HIGH_WEIGHT; + public static boolean ADVANCED_DIAGONAL_STRATEGY; + public static float DIAGONAL_WEIGHT; + public static int MAX_POSTFILTER_PASSES; + public static boolean DEBUG_PATH; + public static boolean FORCE_GEODATA; + public static boolean MOVE_BASED_KNOWNLIST; + public static long KNOWNLIST_UPDATE_INTERVAL; + public static int ZONE_TOWN; + public static String DEFAULT_GLOBAL_CHAT; + public static String DEFAULT_TRADE_CHAT; + public static boolean ALLOW_WAREHOUSE; + public static boolean WAREHOUSE_CACHE; + public static int WAREHOUSE_CACHE_TIME; + public static boolean ALLOW_REFUND; + public static boolean ALLOW_MAIL; + public static boolean ALLOW_ATTACHMENTS; + public static boolean ALLOW_WEAR; + public static int WEAR_DELAY; + public static int WEAR_PRICE; + public static boolean ALLOW_LOTTERY; + public static boolean ALLOW_RACE; + public static boolean ALLOW_WATER; + public static boolean ALLOW_RENTPET; + public static boolean ALLOWFISHING; + public static boolean ALLOW_BOAT; + public static int BOAT_BROADCAST_RADIUS; + public static boolean ALLOW_CURSED_WEAPONS; + public static boolean ALLOW_MANOR; + public static boolean ALLOW_NPC_WALKERS; + public static boolean ALLOW_PET_WALKERS; + public static boolean SERVER_NEWS; + public static int COMMUNITY_TYPE; + public static boolean BBS_SHOW_PLAYERLIST; + public static String BBS_DEFAULT; + public static boolean SHOW_LEVEL_COMMUNITYBOARD; + public static boolean SHOW_STATUS_COMMUNITYBOARD; + public static int NAME_PAGE_SIZE_COMMUNITYBOARD; + public static int NAME_PER_ROW_COMMUNITYBOARD; + public static boolean USE_SAY_FILTER; + public static String CHAT_FILTER_CHARS; + public static int ALT_OLY_START_TIME; + public static int ALT_OLY_MIN; + public static long ALT_OLY_CPERIOD; + public static long ALT_OLY_BATTLE; + public static long ALT_OLY_WPERIOD; + public static long ALT_OLY_VPERIOD; + public static int ALT_OLY_START_POINTS; + public static int ALT_OLY_WEEKLY_POINTS; + public static int ALT_OLY_CLASSED; + public static int ALT_OLY_NONCLASSED; + public static int ALT_OLY_TEAMS; + public static int ALT_OLY_REG_DISPLAY; + public static int[][] ALT_OLY_CLASSED_REWARD; + public static int[][] ALT_OLY_NONCLASSED_REWARD; + public static int[][] ALT_OLY_TEAM_REWARD; + public static int ALT_OLY_COMP_RITEM; + public static int ALT_OLY_MIN_MATCHES; + public static int ALT_OLY_GP_PER_POINT; + public static int ALT_OLY_HERO_POINTS; + public static int ALT_OLY_RANK1_POINTS; + public static int ALT_OLY_RANK2_POINTS; + public static int ALT_OLY_RANK3_POINTS; + public static int ALT_OLY_RANK4_POINTS; + public static int ALT_OLY_RANK5_POINTS; + public static int ALT_OLY_MAX_POINTS; + public static boolean ALT_OLY_LOG_FIGHTS; + public static boolean ALT_OLY_SHOW_MONTHLY_WINNERS; + public static boolean ALT_OLY_ANNOUNCE_GAMES; + public static TIntArrayList LIST_OLY_RESTRICTED_ITEMS; + public static int ALT_OLY_ENCHANT_LIMIT; + public static int ALT_OLY_WAIT_TIME; + public static int ALT_MANOR_REFRESH_TIME; + public static int ALT_MANOR_REFRESH_MIN; + public static int ALT_MANOR_APPROVE_TIME; + public static int ALT_MANOR_APPROVE_MIN; + public static int ALT_MANOR_MAINTENANCE_PERIOD; + public static boolean ALT_MANOR_SAVE_ALL_ACTIONS; + public static int ALT_MANOR_SAVE_PERIOD_RATE; + public static long ALT_LOTTERY_PRIZE; + public static long ALT_LOTTERY_TICKET_PRICE; + public static float ALT_LOTTERY_5_NUMBER_RATE; + public static float ALT_LOTTERY_4_NUMBER_RATE; + public static float ALT_LOTTERY_3_NUMBER_RATE; + public static long ALT_LOTTERY_2_AND_1_NUMBER_PRIZE; + public static boolean ALT_ITEM_AUCTION_ENABLED; + public static int ALT_ITEM_AUCTION_EXPIRED_AFTER; + public static long ALT_ITEM_AUCTION_TIME_EXTENDS_ON_BID; + public static int FS_TIME_ATTACK; + public static int FS_TIME_COOLDOWN; + public static int FS_TIME_ENTRY; + public static int FS_TIME_WARMUP; + public static int FS_PARTY_MEMBER_COUNT; + public static int RIFT_MIN_PARTY_SIZE; + public static int RIFT_SPAWN_DELAY; + public static int RIFT_MAX_JUMPS; + public static int RIFT_AUTO_JUMPS_TIME_MIN; + public static int RIFT_AUTO_JUMPS_TIME_MAX; + public static float RIFT_BOSS_ROOM_TIME_MUTIPLY; + public static int RIFT_ENTER_COST_RECRUIT; + public static int RIFT_ENTER_COST_SOLDIER; + public static int RIFT_ENTER_COST_OFFICER; + public static int RIFT_ENTER_COST_CAPTAIN; + public static int RIFT_ENTER_COST_COMMANDER; + public static int RIFT_ENTER_COST_HERO; + public static int DEFAULT_PUNISH; + public static int DEFAULT_PUNISH_PARAM; + public static boolean ONLY_GM_ITEMS_FREE; + public static boolean JAIL_IS_PVP; + public static boolean JAIL_DISABLE_CHAT; + public static boolean JAIL_DISABLE_TRANSACTION; + public static boolean CUSTOM_SPAWNLIST_TABLE; + public static boolean SAVE_GMSPAWN_ON_CUSTOM; + public static boolean CUSTOM_NPC_TABLE; + public static boolean CUSTOM_NPC_SKILLS_TABLE; + public static boolean CUSTOM_ARMORSETS_TABLE; + public static boolean CUSTOM_TELEPORT_TABLE; + public static boolean CUSTOM_DROPLIST_TABLE; + public static boolean CUSTOM_MERCHANT_TABLES; + public static boolean CUSTOM_NPCBUFFER_TABLES; + public static boolean ENABLE_BLOCK_CHECKER_EVENT; + public static int MIN_BLOCK_CHECKER_TEAM_MEMBERS; + public static boolean HBCE_FAIR_PLAY; + + // -------------------------------------------------- + // FloodProtector Settings + // -------------------------------------------------- + public static FloodProtectorConfig FLOOD_PROTECTOR_USE_ITEM; + public static FloodProtectorConfig FLOOD_PROTECTOR_ROLL_DICE; + public static FloodProtectorConfig FLOOD_PROTECTOR_FIREWORK; + public static FloodProtectorConfig FLOOD_PROTECTOR_ITEM_PET_SUMMON; + public static FloodProtectorConfig FLOOD_PROTECTOR_HERO_VOICE; + public static FloodProtectorConfig FLOOD_PROTECTOR_GLOBAL_CHAT; + public static FloodProtectorConfig FLOOD_PROTECTOR_SUBCLASS; + public static FloodProtectorConfig FLOOD_PROTECTOR_DROP_ITEM; + public static FloodProtectorConfig FLOOD_PROTECTOR_SERVER_BYPASS; + public static FloodProtectorConfig FLOOD_PROTECTOR_MULTISELL; + public static FloodProtectorConfig FLOOD_PROTECTOR_TRANSACTION; + public static FloodProtectorConfig FLOOD_PROTECTOR_MANUFACTURE; + public static FloodProtectorConfig FLOOD_PROTECTOR_MANOR; + public static FloodProtectorConfig FLOOD_PROTECTOR_SENDMAIL; + public static FloodProtectorConfig FLOOD_PROTECTOR_CHARACTER_SELECT; + public static FloodProtectorConfig FLOOD_PROTECTOR_ITEM_AUCTION; + + // -------------------------------------------------- + // L2JMods Settings + // -------------------------------------------------- + public static boolean L2JMOD_CHAMPION_ENABLE; + public static boolean L2JMOD_CHAMPION_PASSIVE; + public static int L2JMOD_CHAMPION_FREQUENCY; + public static String L2JMOD_CHAMP_TITLE; + public static int L2JMOD_CHAMP_MIN_LVL; + public static int L2JMOD_CHAMP_MAX_LVL; + public static int L2JMOD_CHAMPION_HP; + public static int L2JMOD_CHAMPION_REWARDS; + public static float L2JMOD_CHAMPION_ADENAS_REWARDS; + public static float L2JMOD_CHAMPION_HP_REGEN; + public static float L2JMOD_CHAMPION_ATK; + public static float L2JMOD_CHAMPION_SPD_ATK; + public static int L2JMOD_CHAMPION_REWARD_LOWER_LVL_ITEM_CHANCE; + public static int L2JMOD_CHAMPION_REWARD_HIGHER_LVL_ITEM_CHANCE; + public static int L2JMOD_CHAMPION_REWARD_ID; + public static int L2JMOD_CHAMPION_REWARD_QTY; + public static boolean L2JMOD_CHAMPION_ENABLE_VITALITY; + public static boolean L2JMOD_CHAMPION_ENABLE_IN_INSTANCES; + public static boolean TVT_EVENT_ENABLED; + public static boolean TVT_EVENT_IN_INSTANCE; + public static String TVT_EVENT_INSTANCE_FILE; + public static String[] TVT_EVENT_INTERVAL; + public static int TVT_EVENT_PARTICIPATION_TIME; + public static int TVT_EVENT_RUNNING_TIME; + public static int TVT_EVENT_PARTICIPATION_NPC_ID; + public static int[] TVT_EVENT_PARTICIPATION_NPC_COORDINATES = new int[4]; + public static int[] TVT_EVENT_PARTICIPATION_FEE = new int[2]; + public static int TVT_EVENT_MIN_PLAYERS_IN_TEAMS; + public static int TVT_EVENT_MAX_PLAYERS_IN_TEAMS; + public static int TVT_EVENT_RESPAWN_TELEPORT_DELAY; + public static int TVT_EVENT_START_LEAVE_TELEPORT_DELAY; + public static String TVT_EVENT_TEAM_1_NAME; + public static int[] TVT_EVENT_TEAM_1_COORDINATES = new int[3]; + public static String TVT_EVENT_TEAM_2_NAME; + public static int[] TVT_EVENT_TEAM_2_COORDINATES = new int[3]; + public static List TVT_EVENT_REWARDS; + public static boolean TVT_EVENT_TARGET_TEAM_MEMBERS_ALLOWED; + public static boolean TVT_EVENT_SCROLL_ALLOWED; + public static boolean TVT_EVENT_POTIONS_ALLOWED; + public static boolean TVT_EVENT_SUMMON_BY_ITEM_ALLOWED; + public static List TVT_DOORS_IDS_TO_OPEN; + public static List TVT_DOORS_IDS_TO_CLOSE; + public static boolean TVT_REWARD_TEAM_TIE; + public static byte TVT_EVENT_MIN_LVL; + public static byte TVT_EVENT_MAX_LVL; + public static int TVT_EVENT_EFFECTS_REMOVAL; + public static TIntIntHashMap TVT_EVENT_FIGHTER_BUFFS; + public static TIntIntHashMap TVT_EVENT_MAGE_BUFFS; + public static int TVT_EVENT_MAX_PARTICIPANTS_PER_IP; + public static boolean TVT_ALLOW_VOICED_COMMAND; + public static boolean L2JMOD_ALLOW_WEDDING; + public static int L2JMOD_WEDDING_PRICE; + public static boolean L2JMOD_WEDDING_PUNISH_INFIDELITY; + public static boolean L2JMOD_WEDDING_TELEPORT; + public static int L2JMOD_WEDDING_TELEPORT_PRICE; + public static int L2JMOD_WEDDING_TELEPORT_DURATION; + public static boolean L2JMOD_WEDDING_SAMESEX; + public static boolean L2JMOD_WEDDING_FORMALWEAR; + public static int L2JMOD_WEDDING_DIVORCE_COSTS; + public static boolean BANKING_SYSTEM_ENABLED; + public static int BANKING_SYSTEM_GOLDBARS; + public static int BANKING_SYSTEM_ADENA; + public static boolean L2JMOD_ENABLE_WAREHOUSESORTING_CLAN; + public static boolean L2JMOD_ENABLE_WAREHOUSESORTING_PRIVATE; + public static boolean OFFLINE_TRADE_ENABLE; + public static boolean OFFLINE_CRAFT_ENABLE; + public static boolean RESTORE_OFFLINERS; + public static int OFFLINE_MAX_DAYS; + public static boolean OFFLINE_DISCONNECT_FINISHED; + public static boolean OFFLINE_SET_NAME_COLOR; + public static int OFFLINE_NAME_COLOR; + public static boolean OFFLINE_FAME; + public static boolean L2JMOD_ENABLE_MANA_POTIONS_SUPPORT; + public static boolean L2JMOD_DISPLAY_SERVER_TIME; + public static boolean WELCOME_MESSAGE_ENABLED; + public static String WELCOME_MESSAGE_TEXT; + public static int WELCOME_MESSAGE_TIME; + public static boolean L2JMOD_ANTIFEED_ENABLE; + public static boolean L2JMOD_ANTIFEED_DUALBOX; + public static boolean L2JMOD_ANTIFEED_DISCONNECTED_AS_DUALBOX; + public static int L2JMOD_ANTIFEED_INTERVAL; + public static boolean ANNOUNCE_PK_PVP; + public static boolean ANNOUNCE_PK_PVP_NORMAL_MESSAGE; + public static String ANNOUNCE_PK_MSG; + public static String ANNOUNCE_PVP_MSG; + public static boolean L2JMOD_CHAT_ADMIN; + public static boolean L2JMOD_MULTILANG_ENABLE; + public static List L2JMOD_MULTILANG_ALLOWED = new ArrayList<>(); + public static String L2JMOD_MULTILANG_DEFAULT; + public static boolean L2JMOD_MULTILANG_VOICED_ALLOW; + public static boolean L2JMOD_MULTILANG_SM_ENABLE; + public static List L2JMOD_MULTILANG_SM_ALLOWED = new ArrayList<>(); + public static boolean L2WALKER_PROTECTION; + public static boolean L2JMOD_DEBUG_VOICE_COMMAND; + public static int L2JMOD_DUALBOX_CHECK_MAX_PLAYERS_PER_IP; + public static int L2JMOD_DUALBOX_CHECK_MAX_OLYMPIAD_PARTICIPANTS_PER_IP; + public static TIntIntHashMap L2JMOD_DUALBOX_CHECK_WHITELIST; + + // -------------------------------------------------- + // NPC Settings + // -------------------------------------------------- + public static boolean ANNOUNCE_MAMMON_SPAWN; + public static boolean ALT_MOB_AGRO_IN_PEACEZONE; + public static boolean ALT_ATTACKABLE_NPCS; + public static boolean ALT_GAME_VIEWNPC; + public static int MAX_DRIFT_RANGE; + public static boolean DEEPBLUE_DROP_RULES; + public static boolean DEEPBLUE_DROP_RULES_RAID; + public static boolean SHOW_NPC_LVL; + public static boolean SHOW_CREST_WITHOUT_QUEST; + public static boolean ENABLE_RANDOM_ENCHANT_EFFECT; + public static int MIN_NPC_LVL_DMG_PENALTY; + public static TIntFloatHashMap NPC_DMG_PENALTY; + public static TIntFloatHashMap NPC_CRIT_DMG_PENALTY; + public static TIntFloatHashMap NPC_SKILL_DMG_PENALTY; + public static int MIN_NPC_LVL_MAGIC_PENALTY; + public static TIntFloatHashMap NPC_SKILL_CHANCE_PENALTY; + public static boolean GUARD_ATTACK_AGGRO_MOB; + public static boolean ALLOW_WYVERN_UPGRADER; + public static TIntArrayList LIST_PET_RENT_NPC; + public static double RAID_HP_REGEN_MULTIPLIER; + public static double RAID_MP_REGEN_MULTIPLIER; + public static double RAID_PDEFENCE_MULTIPLIER; + public static double RAID_MDEFENCE_MULTIPLIER; + public static double RAID_PATTACK_MULTIPLIER; + public static double RAID_MATTACK_MULTIPLIER; + public static double RAID_MINION_RESPAWN_TIMER; + public static float RAID_MIN_RESPAWN_MULTIPLIER; + public static float RAID_MAX_RESPAWN_MULTIPLIER; + public static boolean RAID_DISABLE_CURSE; + public static int RAID_CHAOS_TIME; + public static int GRAND_CHAOS_TIME; + public static int MINION_CHAOS_TIME; + public static int INVENTORY_MAXIMUM_PET; + public static double PET_HP_REGEN_MULTIPLIER; + public static double PET_MP_REGEN_MULTIPLIER; + public static TIntArrayList NON_TALKING_NPCS; + + // -------------------------------------------------- + // PvP Settings + // -------------------------------------------------- + public static int KARMA_MIN_KARMA; + public static int KARMA_MAX_KARMA; + public static int KARMA_XP_DIVIDER; + public static int KARMA_LOST_BASE; + public static boolean KARMA_DROP_GM; + public static boolean KARMA_AWARD_PK_KILL; + public static int KARMA_PK_LIMIT; + public static String KARMA_NONDROPPABLE_PET_ITEMS; + public static String KARMA_NONDROPPABLE_ITEMS; + public static int[] KARMA_LIST_NONDROPPABLE_PET_ITEMS; + public static int[] KARMA_LIST_NONDROPPABLE_ITEMS; + + // -------------------------------------------------- + // Rate Settings + // -------------------------------------------------- + public static float RATE_XP; + public static float RATE_SP; + public static float RATE_PARTY_XP; + public static float RATE_PARTY_SP; + public static float RATE_CONSUMABLE_COST; + public static float RATE_EXTR_FISH; + public static float RATE_DROP_ITEMS; + public static float RATE_DROP_ITEMS_BY_RAID; + public static float RATE_DROP_SPOIL; + public static int RATE_DROP_MANOR; + public static float RATE_QUEST_DROP; + public static float RATE_QUEST_REWARD; + public static float RATE_QUEST_REWARD_XP; + public static float RATE_QUEST_REWARD_SP; + public static float RATE_QUEST_REWARD_ADENA; + public static boolean RATE_QUEST_REWARD_USE_MULTIPLIERS; + public static float RATE_QUEST_REWARD_POTION; + public static float RATE_QUEST_REWARD_SCROLL; + public static float RATE_QUEST_REWARD_RECIPE; + public static float RATE_QUEST_REWARD_MATERIAL; + public static TIntFloatHashMap RATE_DROP_ITEMS_ID; + public static float RATE_KARMA_EXP_LOST; + public static float RATE_SIEGE_GUARDS_PRICE; + public static float RATE_DROP_COMMON_HERBS; + public static float RATE_DROP_HP_HERBS; + public static float RATE_DROP_MP_HERBS; + public static float RATE_DROP_SPECIAL_HERBS; + public static int PLAYER_DROP_LIMIT; + public static int PLAYER_RATE_DROP; + public static int PLAYER_RATE_DROP_ITEM; + public static int PLAYER_RATE_DROP_EQUIP; + public static int PLAYER_RATE_DROP_EQUIP_WEAPON; + public static float PET_XP_RATE; + public static int PET_FOOD_RATE; + public static float SINEATER_XP_RATE; + public static int KARMA_DROP_LIMIT; + public static int KARMA_RATE_DROP; + public static int KARMA_RATE_DROP_ITEM; + public static int KARMA_RATE_DROP_EQUIP; + public static int KARMA_RATE_DROP_EQUIP_WEAPON; + public static double[] PLAYER_XP_PERCENT_LOST; + + // -------------------------------------------------- + // Seven Signs Settings + // -------------------------------------------------- + public static boolean ALT_GAME_CASTLE_DAWN; + public static boolean ALT_GAME_CASTLE_DUSK; + public static boolean ALT_GAME_REQUIRE_CLAN_CASTLE; + public static int ALT_FESTIVAL_MIN_PLAYER; + public static int ALT_MAXIMUM_PLAYER_CONTRIB; + public static long ALT_FESTIVAL_MANAGER_START; + public static long ALT_FESTIVAL_LENGTH; + public static long ALT_FESTIVAL_CYCLE_LENGTH; + public static long ALT_FESTIVAL_FIRST_SPAWN; + public static long ALT_FESTIVAL_FIRST_SWARM; + public static long ALT_FESTIVAL_SECOND_SPAWN; + public static long ALT_FESTIVAL_SECOND_SWARM; + public static long ALT_FESTIVAL_CHEST_SPAWN; + public static double ALT_SIEGE_DAWN_GATES_PDEF_MULT; + public static double ALT_SIEGE_DUSK_GATES_PDEF_MULT; + public static double ALT_SIEGE_DAWN_GATES_MDEF_MULT; + public static double ALT_SIEGE_DUSK_GATES_MDEF_MULT; + public static boolean ALT_STRICT_SEVENSIGNS; + public static boolean ALT_SEVENSIGNS_LAZY_UPDATE; + + // -------------------------------------------------- + // Server Settings + // -------------------------------------------------- + public static int PORT_GAME; + public static int PORT_LOGIN; + public static String LOGIN_BIND_ADDRESS; + public static int LOGIN_TRY_BEFORE_BAN; + public static int LOGIN_BLOCK_AFTER_BAN; + public static String GAMESERVER_HOSTNAME; + public static String DATABASE_DRIVER; + public static String DATABASE_URL; + public static String DATABASE_LOGIN; + public static String DATABASE_PASSWORD; + public static int DATABASE_MAX_CONNECTIONS; + public static int DATABASE_MAX_IDLE_TIME; + public static int MAXIMUM_ONLINE_USERS; + public static String CNAME_TEMPLATE; + public static String PET_NAME_TEMPLATE; + public static int MAX_CHARACTERS_NUMBER_PER_ACCOUNT; + public static File DATAPACK_ROOT; + public static boolean ACCEPT_ALTERNATE_ID; + public static int REQUEST_ID; + public static boolean RESERVE_HOST_ON_LOGIN = false; + public static TIntArrayList PROTOCOL_LIST; + public static boolean LOG_LOGIN_CONTROLLER; + + // -------------------------------------------------- + // CommunityServer Settings + // -------------------------------------------------- + public static boolean ENABLE_COMMUNITY_BOARD; + public static String COMMUNITY_SERVER_ADDRESS; + public static int COMMUNITY_SERVER_PORT; + public static byte[] COMMUNITY_SERVER_HEX_ID; + public static int COMMUNITY_SERVER_SQL_DP_ID; + + // -------------------------------------------------- + // MMO Settings + // -------------------------------------------------- + public static int MMO_SELECTOR_SLEEP_TIME; + public static int MMO_MAX_SEND_PER_PASS; + public static int MMO_MAX_READ_PER_PASS; + public static int MMO_HELPER_BUFFER_COUNT; + + // -------------------------------------------------- + // Vitality Settings + // -------------------------------------------------- + public static boolean ENABLE_VITALITY; + public static boolean RECOVER_VITALITY_ON_RECONNECT; + public static boolean ENABLE_DROP_VITALITY_HERBS; + public static float RATE_VITALITY_LEVEL_1; + public static float RATE_VITALITY_LEVEL_2; + public static float RATE_VITALITY_LEVEL_3; + public static float RATE_VITALITY_LEVEL_4; + public static float RATE_DROP_VITALITY_HERBS; + public static float RATE_RECOVERY_VITALITY_PEACE_ZONE; + public static float RATE_VITALITY_LOST; + public static float RATE_VITALITY_GAIN; + public static float RATE_RECOVERY_ON_RECONNECT; + public static int STARTING_VITALITY_POINTS; + + // -------------------------------------------------- + // No classification assigned to the following yet + // -------------------------------------------------- + public static int MAX_ITEM_IN_PACKET; + public static boolean CHECK_KNOWN; + public static int GAME_SERVER_LOGIN_PORT; + public static String GAME_SERVER_LOGIN_HOST; + public static String[] GAME_SERVER_SUBNETS; + public static String[] GAME_SERVER_HOSTS; + public static int NEW_NODE_ID; + public static int SELECTED_NODE_ID; + public static int LINKED_NODE_ID; + public static String NEW_NODE_TYPE; + public static int PVP_NORMAL_TIME; + public static int PVP_PVP_TIME; + + public static enum IdFactoryType + { + Compaction, + BitSet, + Stack + } + + public static IdFactoryType IDFACTORY_TYPE; + public static boolean BAD_ID_CHECKING; + + public static enum ObjectMapType + { + L2ObjectHashMap, + WorldObjectMap + } + + public static enum ObjectSetType + { + L2ObjectHashSet, + WorldObjectSet + } + + public static ObjectMapType MAP_TYPE; + public static ObjectSetType SET_TYPE; + public static int ENCHANT_CHANCE_WEAPON; + public static int ENCHANT_CHANCE_ARMOR; + public static int ENCHANT_CHANCE_JEWELRY; + public static int ENCHANT_CHANCE_ELEMENT_STONE; + public static int ENCHANT_CHANCE_ELEMENT_CRYSTAL; + public static int ENCHANT_CHANCE_ELEMENT_JEWEL; + public static int ENCHANT_CHANCE_ELEMENT_ENERGY; + public static int BLESSED_ENCHANT_CHANCE_WEAPON; + public static int BLESSED_ENCHANT_CHANCE_ARMOR; + public static int BLESSED_ENCHANT_CHANCE_JEWELRY; + public static int ENCHANT_MAX_WEAPON; + public static int ENCHANT_MAX_ARMOR; + public static int ENCHANT_MAX_JEWELRY; + public static int ENCHANT_SAFE_MAX; + public static int ENCHANT_SAFE_MAX_FULL; + public static int[] ENCHANT_BLACKLIST; + public static int AUGMENTATION_NG_SKILL_CHANCE; + public static int AUGMENTATION_NG_GLOW_CHANCE; + public static int AUGMENTATION_MID_SKILL_CHANCE; + public static int AUGMENTATION_MID_GLOW_CHANCE; + public static int AUGMENTATION_HIGH_SKILL_CHANCE; + public static int AUGMENTATION_HIGH_GLOW_CHANCE; + public static int AUGMENTATION_TOP_SKILL_CHANCE; + public static int AUGMENTATION_TOP_GLOW_CHANCE; + public static int AUGMENTATION_BASESTAT_CHANCE; + public static int AUGMENTATION_ACC_SKILL_CHANCE; + public static int[] AUGMENTATION_BLACKLIST; + public static double HP_REGEN_MULTIPLIER; + public static double MP_REGEN_MULTIPLIER; + public static double CP_REGEN_MULTIPLIER; + public static boolean IS_TELNET_ENABLED; + public static boolean SHOW_LICENCE; + public static boolean ACCEPT_NEW_GAMESERVER; + public static int SERVER_ID; + public static byte[] HEX_ID; + public static boolean AUTO_CREATE_ACCOUNTS; + public static boolean FLOOD_PROTECTION; + public static int FAST_CONNECTION_LIMIT; + public static int NORMAL_CONNECTION_TIME; + public static int FAST_CONNECTION_TIME; + public static int MAX_CONNECTION_PER_IP; + + // GrandBoss Settings + + public static int Antharas_Wait_Time; + public static int Valakas_Wait_Time; + public static int Interval_Of_Antharas_Spawn; + public static int Random_Of_Antharas_Spawn; + public static int Interval_Of_Valakas_Spawn; + public static int Random_Of_Valakas_Spawn; + public static int Interval_Of_Baium_Spawn; + public static int Random_Of_Baium_Spawn; + public static int Interval_Of_Core_Spawn; + public static int Random_Of_Core_Spawn; + public static int Interval_Of_Orfen_Spawn; + public static int Random_Of_Orfen_Spawn; + public static int Interval_Of_QueenAnt_Spawn; + public static int Random_Of_QueenAnt_Spawn; + public static int Interval_Of_Zaken_Spawn; + public static int Random_Of_Zaken_Spawn; + public static int Interval_Of_Frintezza_Spawn; + public static int Random_Of_Frintezza_Spawn; + + // Gracia Seeds Settings + + public static int SOD_TIAT_KILL_COUNT; + public static long SOD_STAGE_2_LENGTH; + + // chatfilter + public static ArrayList FILTER_LIST; + + /** + * This class initializes all global variables for configuration.
+ * If the key doesn't appear in properties file, a default value is set by this class. + * @see CONFIGURATION_FILE (properties file) for configuring your server. + */ + public static void load() + { + if (Server.serverMode == Server.MODE_GAMESERVER) + { + FLOOD_PROTECTOR_USE_ITEM = new FloodProtectorConfig("UseItemFloodProtector"); + FLOOD_PROTECTOR_ROLL_DICE = new FloodProtectorConfig("RollDiceFloodProtector"); + FLOOD_PROTECTOR_FIREWORK = new FloodProtectorConfig("FireworkFloodProtector"); + FLOOD_PROTECTOR_ITEM_PET_SUMMON = new FloodProtectorConfig("ItemPetSummonFloodProtector"); + FLOOD_PROTECTOR_HERO_VOICE = new FloodProtectorConfig("HeroVoiceFloodProtector"); + FLOOD_PROTECTOR_GLOBAL_CHAT = new FloodProtectorConfig("GlobalChatFloodProtector"); + FLOOD_PROTECTOR_SUBCLASS = new FloodProtectorConfig("SubclassFloodProtector"); + FLOOD_PROTECTOR_DROP_ITEM = new FloodProtectorConfig("DropItemFloodProtector"); + FLOOD_PROTECTOR_SERVER_BYPASS = new FloodProtectorConfig("ServerBypassFloodProtector"); + FLOOD_PROTECTOR_MULTISELL = new FloodProtectorConfig("MultiSellFloodProtector"); + FLOOD_PROTECTOR_TRANSACTION = new FloodProtectorConfig("TransactionFloodProtector"); + FLOOD_PROTECTOR_MANUFACTURE = new FloodProtectorConfig("ManufactureFloodProtector"); + FLOOD_PROTECTOR_MANOR = new FloodProtectorConfig("ManorFloodProtector"); + FLOOD_PROTECTOR_SENDMAIL = new FloodProtectorConfig("SendMailFloodProtector"); + FLOOD_PROTECTOR_CHARACTER_SELECT = new FloodProtectorConfig("CharacterSelectFloodProtector"); + FLOOD_PROTECTOR_ITEM_AUCTION = new FloodProtectorConfig("ItemAuctionFloodProtector"); + + _log.info("Loading GameServer Configuration Files..."); + InputStream is = null; + try + { + try + { + L2Properties serverSettings = new L2Properties(); + is = new FileInputStream(new File(CONFIGURATION_FILE)); + serverSettings.load(is); + + GAMESERVER_HOSTNAME = serverSettings.getProperty("GameserverHostname"); + PORT_GAME = Integer.parseInt(serverSettings.getProperty("GameserverPort", "7777")); + + GAME_SERVER_LOGIN_PORT = Integer.parseInt(serverSettings.getProperty("LoginPort", "9014")); + GAME_SERVER_LOGIN_HOST = serverSettings.getProperty("LoginHost", "127.0.0.1"); + + REQUEST_ID = Integer.parseInt(serverSettings.getProperty("RequestServerID", "0")); + ACCEPT_ALTERNATE_ID = Boolean.parseBoolean(serverSettings.getProperty("AcceptAlternateID", "True")); + + DATABASE_DRIVER = serverSettings.getProperty("Driver", "com.mysql.jdbc.Driver"); + DATABASE_URL = serverSettings.getProperty("URL", "jdbc:mysql://localhost/l2jgs"); + DATABASE_LOGIN = serverSettings.getProperty("Login", "root"); + DATABASE_PASSWORD = serverSettings.getProperty("Password", ""); + DATABASE_MAX_CONNECTIONS = Integer.parseInt(serverSettings.getProperty("MaximumDbConnections", "10")); + DATABASE_MAX_IDLE_TIME = Integer.parseInt(serverSettings.getProperty("MaximumDbIdleTime", "0")); + + DATAPACK_ROOT = new File(serverSettings.getProperty("DatapackRoot", ".")).getCanonicalFile(); + + CNAME_TEMPLATE = serverSettings.getProperty("CnameTemplate", ".*"); + PET_NAME_TEMPLATE = serverSettings.getProperty("PetNameTemplate", ".*"); + + MAX_CHARACTERS_NUMBER_PER_ACCOUNT = Integer.parseInt(serverSettings.getProperty("CharMaxNumber", "0")); + MAXIMUM_ONLINE_USERS = Integer.parseInt(serverSettings.getProperty("MaximumOnlineUsers", "100")); + + String[] protocols = serverSettings.getProperty("AllowedProtocolRevisions", "146;152").split(";"); + PROTOCOL_LIST = new TIntArrayList(protocols.length); + for (String protocol : protocols) + { + try + { + PROTOCOL_LIST.add(Integer.parseInt(protocol.trim())); + } + catch (NumberFormatException e) + { + _log.info("Wrong config protocol version: " + protocol + ". Skipped."); + } + } + + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + CONFIGURATION_FILE + " File."); + } + + File file = new File(IP_CONFIG_FILE); + Document doc = null; + ArrayList subnets = new ArrayList<>(5); + ArrayList hosts = new ArrayList<>(5); + try + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setValidating(false); + factory.setIgnoringComments(true); + doc = factory.newDocumentBuilder().parse(file); + + for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling()) + { + NamedNodeMap attrs; + Node att; + + if ("gameserver".equalsIgnoreCase(n.getNodeName())) + { + for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("define".equalsIgnoreCase(d.getNodeName())) + { + attrs = d.getAttributes(); + + att = attrs.getNamedItem("subnet"); + if (att == null) + { + continue; + } + + subnets.add(att.getNodeValue()); + + att = attrs.getNamedItem("address"); + if (att == null) + { + continue; + } + + hosts.add(att.getNodeValue()); + + if (hosts.size() != subnets.size()) + { + throw new Error("Failed to Load " + IP_CONFIG_FILE + " File - subnets does not match server addresses."); + } + } + } + + attrs = n.getAttributes(); + + att = attrs.getNamedItem("address"); + if (att == null) + { + throw new Error("Failed to Load " + IP_CONFIG_FILE + " File - default server address is missing."); + } + + subnets.add("0.0.0.0/0"); + hosts.add(att.getNodeValue()); + } + } + GAME_SERVER_SUBNETS = subnets.toArray(new String[subnets.size()]); + GAME_SERVER_HOSTS = hosts.toArray(new String[hosts.size()]); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + IP_CONFIG_FILE + " File."); + } + + // Load Community Properties file (if exists) + try + { + L2Properties communityServerSettings = new L2Properties(); + is = new FileInputStream(new File(COMMUNITY_CONFIGURATION_FILE)); + communityServerSettings.load(is); + ENABLE_COMMUNITY_BOARD = Boolean.parseBoolean(communityServerSettings.getProperty("EnableCommunityBoard", "False")); + COMMUNITY_SERVER_ADDRESS = communityServerSettings.getProperty("CommunityServerHostname", "localhost"); + COMMUNITY_SERVER_PORT = Integer.parseInt(communityServerSettings.getProperty("CommunityServerPort", "9013")); + COMMUNITY_SERVER_HEX_ID = new BigInteger(communityServerSettings.getProperty("CommunityServerHexId"), 16).toByteArray(); + COMMUNITY_SERVER_SQL_DP_ID = Integer.parseInt(communityServerSettings.getProperty("CommunityServerSqlDpId", "200")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + COMMUNITY_CONFIGURATION_FILE + " File."); + } + + // Load Feature L2Properties file (if exists) + try + { + L2Properties Feature = new L2Properties(); + is = new FileInputStream(new File(FEATURE_CONFIG_FILE)); + Feature.load(is); + + CH_TELE_FEE_RATIO = Long.parseLong(Feature.getProperty("ClanHallTeleportFunctionFeeRatio", "604800000")); + CH_TELE1_FEE = Integer.parseInt(Feature.getProperty("ClanHallTeleportFunctionFeeLvl1", "7000")); + CH_TELE2_FEE = Integer.parseInt(Feature.getProperty("ClanHallTeleportFunctionFeeLvl2", "14000")); + CH_SUPPORT_FEE_RATIO = Long.parseLong(Feature.getProperty("ClanHallSupportFunctionFeeRatio", "86400000")); + CH_SUPPORT1_FEE = Integer.parseInt(Feature.getProperty("ClanHallSupportFeeLvl1", "2500")); + CH_SUPPORT2_FEE = Integer.parseInt(Feature.getProperty("ClanHallSupportFeeLvl2", "5000")); + CH_SUPPORT3_FEE = Integer.parseInt(Feature.getProperty("ClanHallSupportFeeLvl3", "7000")); + CH_SUPPORT4_FEE = Integer.parseInt(Feature.getProperty("ClanHallSupportFeeLvl4", "11000")); + CH_SUPPORT5_FEE = Integer.parseInt(Feature.getProperty("ClanHallSupportFeeLvl5", "21000")); + CH_SUPPORT6_FEE = Integer.parseInt(Feature.getProperty("ClanHallSupportFeeLvl6", "36000")); + CH_SUPPORT7_FEE = Integer.parseInt(Feature.getProperty("ClanHallSupportFeeLvl7", "37000")); + CH_SUPPORT8_FEE = Integer.parseInt(Feature.getProperty("ClanHallSupportFeeLvl8", "52000")); + CH_MPREG_FEE_RATIO = Long.parseLong(Feature.getProperty("ClanHallMpRegenerationFunctionFeeRatio", "86400000")); + CH_MPREG1_FEE = Integer.parseInt(Feature.getProperty("ClanHallMpRegenerationFeeLvl1", "2000")); + CH_MPREG2_FEE = Integer.parseInt(Feature.getProperty("ClanHallMpRegenerationFeeLvl2", "3750")); + CH_MPREG3_FEE = Integer.parseInt(Feature.getProperty("ClanHallMpRegenerationFeeLvl3", "6500")); + CH_MPREG4_FEE = Integer.parseInt(Feature.getProperty("ClanHallMpRegenerationFeeLvl4", "13750")); + CH_MPREG5_FEE = Integer.parseInt(Feature.getProperty("ClanHallMpRegenerationFeeLvl5", "20000")); + CH_HPREG_FEE_RATIO = Long.parseLong(Feature.getProperty("ClanHallHpRegenerationFunctionFeeRatio", "86400000")); + CH_HPREG1_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl1", "700")); + CH_HPREG2_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl2", "800")); + CH_HPREG3_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl3", "1000")); + CH_HPREG4_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl4", "1166")); + CH_HPREG5_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl5", "1500")); + CH_HPREG6_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl6", "1750")); + CH_HPREG7_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl7", "2000")); + CH_HPREG8_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl8", "2250")); + CH_HPREG9_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl9", "2500")); + CH_HPREG10_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl10", "3250")); + CH_HPREG11_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl11", "3270")); + CH_HPREG12_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl12", "4250")); + CH_HPREG13_FEE = Integer.parseInt(Feature.getProperty("ClanHallHpRegenerationFeeLvl13", "5166")); + CH_EXPREG_FEE_RATIO = Long.parseLong(Feature.getProperty("ClanHallExpRegenerationFunctionFeeRatio", "86400000")); + CH_EXPREG1_FEE = Integer.parseInt(Feature.getProperty("ClanHallExpRegenerationFeeLvl1", "3000")); + CH_EXPREG2_FEE = Integer.parseInt(Feature.getProperty("ClanHallExpRegenerationFeeLvl2", "6000")); + CH_EXPREG3_FEE = Integer.parseInt(Feature.getProperty("ClanHallExpRegenerationFeeLvl3", "9000")); + CH_EXPREG4_FEE = Integer.parseInt(Feature.getProperty("ClanHallExpRegenerationFeeLvl4", "15000")); + CH_EXPREG5_FEE = Integer.parseInt(Feature.getProperty("ClanHallExpRegenerationFeeLvl5", "21000")); + CH_EXPREG6_FEE = Integer.parseInt(Feature.getProperty("ClanHallExpRegenerationFeeLvl6", "23330")); + CH_EXPREG7_FEE = Integer.parseInt(Feature.getProperty("ClanHallExpRegenerationFeeLvl7", "30000")); + CH_ITEM_FEE_RATIO = Long.parseLong(Feature.getProperty("ClanHallItemCreationFunctionFeeRatio", "86400000")); + CH_ITEM1_FEE = Integer.parseInt(Feature.getProperty("ClanHallItemCreationFunctionFeeLvl1", "30000")); + CH_ITEM2_FEE = Integer.parseInt(Feature.getProperty("ClanHallItemCreationFunctionFeeLvl2", "70000")); + CH_ITEM3_FEE = Integer.parseInt(Feature.getProperty("ClanHallItemCreationFunctionFeeLvl3", "140000")); + CH_CURTAIN_FEE_RATIO = Long.parseLong(Feature.getProperty("ClanHallCurtainFunctionFeeRatio", "604800000")); + CH_CURTAIN1_FEE = Integer.parseInt(Feature.getProperty("ClanHallCurtainFunctionFeeLvl1", "2000")); + CH_CURTAIN2_FEE = Integer.parseInt(Feature.getProperty("ClanHallCurtainFunctionFeeLvl2", "2500")); + CH_FRONT_FEE_RATIO = Long.parseLong(Feature.getProperty("ClanHallFrontPlatformFunctionFeeRatio", "259200000")); + CH_FRONT1_FEE = Integer.parseInt(Feature.getProperty("ClanHallFrontPlatformFunctionFeeLvl1", "1300")); + CH_FRONT2_FEE = Integer.parseInt(Feature.getProperty("ClanHallFrontPlatformFunctionFeeLvl2", "4000")); + CH_BUFF_FREE = Boolean.parseBoolean(Feature.getProperty("AltClanHallMpBuffFree", "False")); + + CL_SET_SIEGE_TIME_LIST = new ArrayList<>(); + SIEGE_HOUR_LIST_MORNING = new ArrayList<>(); + SIEGE_HOUR_LIST_AFTERNOON = new ArrayList<>(); + String[] sstl = Feature.getProperty("CLSetSiegeTimeList", "").split(","); + if (sstl.length != 0) + { + boolean isHour = false; + for (String st : sstl) + { + if (st.equalsIgnoreCase("day") || st.equalsIgnoreCase("hour") || st.equalsIgnoreCase("minute")) + { + if (st.equalsIgnoreCase("hour")) + { + isHour = true; + } + CL_SET_SIEGE_TIME_LIST.add(st.toLowerCase()); + } + else + { + _log.warning(StringUtil.concat("[CLSetSiegeTimeList]: invalid config property -> CLSetSiegeTimeList \"", st, "\"")); + } + } + if (isHour) + { + String[] shl = Feature.getProperty("SiegeHourList", "").split(","); + for (String st : shl) + { + if (!st.equalsIgnoreCase("")) + { + int val = Integer.parseInt(st); + if ((val > 23) || (val < 0)) + { + _log.warning(StringUtil.concat("[SiegeHourList]: invalid config property -> SiegeHourList \"", st, "\"")); + } + else if (val < 12) + { + SIEGE_HOUR_LIST_MORNING.add(val); + } + else + { + val -= 12; + SIEGE_HOUR_LIST_AFTERNOON.add(val); + } + } + } + if (Config.SIEGE_HOUR_LIST_AFTERNOON.isEmpty() && Config.SIEGE_HOUR_LIST_AFTERNOON.isEmpty()) + { + _log.warning("[SiegeHourList]: invalid config property -> SiegeHourList is empty"); + CL_SET_SIEGE_TIME_LIST.remove("hour"); + } + } + } + CS_TELE_FEE_RATIO = Long.parseLong(Feature.getProperty("CastleTeleportFunctionFeeRatio", "604800000")); + CS_TELE1_FEE = Integer.parseInt(Feature.getProperty("CastleTeleportFunctionFeeLvl1", "7000")); + CS_TELE2_FEE = Integer.parseInt(Feature.getProperty("CastleTeleportFunctionFeeLvl2", "14000")); + CS_SUPPORT_FEE_RATIO = Long.parseLong(Feature.getProperty("CastleSupportFunctionFeeRatio", "86400000")); + CS_SUPPORT1_FEE = Integer.parseInt(Feature.getProperty("CastleSupportFeeLvl1", "7000")); + CS_SUPPORT2_FEE = Integer.parseInt(Feature.getProperty("CastleSupportFeeLvl2", "21000")); + CS_SUPPORT3_FEE = Integer.parseInt(Feature.getProperty("CastleSupportFeeLvl3", "37000")); + CS_SUPPORT4_FEE = Integer.parseInt(Feature.getProperty("CastleSupportFeeLvl4", "52000")); + CS_MPREG_FEE_RATIO = Long.parseLong(Feature.getProperty("CastleMpRegenerationFunctionFeeRatio", "86400000")); + CS_MPREG1_FEE = Integer.parseInt(Feature.getProperty("CastleMpRegenerationFeeLvl1", "2000")); + CS_MPREG2_FEE = Integer.parseInt(Feature.getProperty("CastleMpRegenerationFeeLvl2", "6500")); + CS_MPREG3_FEE = Integer.parseInt(Feature.getProperty("CastleMpRegenerationFeeLvl3", "13750")); + CS_MPREG4_FEE = Integer.parseInt(Feature.getProperty("CastleMpRegenerationFeeLvl4", "20000")); + CS_HPREG_FEE_RATIO = Long.parseLong(Feature.getProperty("CastleHpRegenerationFunctionFeeRatio", "86400000")); + CS_HPREG1_FEE = Integer.parseInt(Feature.getProperty("CastleHpRegenerationFeeLvl1", "1000")); + CS_HPREG2_FEE = Integer.parseInt(Feature.getProperty("CastleHpRegenerationFeeLvl2", "1500")); + CS_HPREG3_FEE = Integer.parseInt(Feature.getProperty("CastleHpRegenerationFeeLvl3", "2250")); + CS_HPREG4_FEE = Integer.parseInt(Feature.getProperty("CastleHpRegenerationFeeLvl4", "3270")); + CS_HPREG5_FEE = Integer.parseInt(Feature.getProperty("CastleHpRegenerationFeeLvl5", "5166")); + CS_EXPREG_FEE_RATIO = Long.parseLong(Feature.getProperty("CastleExpRegenerationFunctionFeeRatio", "86400000")); + CS_EXPREG1_FEE = Integer.parseInt(Feature.getProperty("CastleExpRegenerationFeeLvl1", "9000")); + CS_EXPREG2_FEE = Integer.parseInt(Feature.getProperty("CastleExpRegenerationFeeLvl2", "15000")); + CS_EXPREG3_FEE = Integer.parseInt(Feature.getProperty("CastleExpRegenerationFeeLvl3", "21000")); + CS_EXPREG4_FEE = Integer.parseInt(Feature.getProperty("CastleExpRegenerationFeeLvl4", "30000")); + + FS_TELE_FEE_RATIO = Long.parseLong(Feature.getProperty("FortressTeleportFunctionFeeRatio", "604800000")); + FS_TELE1_FEE = Integer.parseInt(Feature.getProperty("FortressTeleportFunctionFeeLvl1", "1000")); + FS_TELE2_FEE = Integer.parseInt(Feature.getProperty("FortressTeleportFunctionFeeLvl2", "10000")); + FS_SUPPORT_FEE_RATIO = Long.parseLong(Feature.getProperty("FortressSupportFunctionFeeRatio", "86400000")); + FS_SUPPORT1_FEE = Integer.parseInt(Feature.getProperty("FortressSupportFeeLvl1", "7000")); + FS_SUPPORT2_FEE = Integer.parseInt(Feature.getProperty("FortressSupportFeeLvl2", "17000")); + FS_MPREG_FEE_RATIO = Long.parseLong(Feature.getProperty("FortressMpRegenerationFunctionFeeRatio", "86400000")); + FS_MPREG1_FEE = Integer.parseInt(Feature.getProperty("FortressMpRegenerationFeeLvl1", "6500")); + FS_MPREG2_FEE = Integer.parseInt(Feature.getProperty("FortressMpRegenerationFeeLvl2", "9300")); + FS_HPREG_FEE_RATIO = Long.parseLong(Feature.getProperty("FortressHpRegenerationFunctionFeeRatio", "86400000")); + FS_HPREG1_FEE = Integer.parseInt(Feature.getProperty("FortressHpRegenerationFeeLvl1", "2000")); + FS_HPREG2_FEE = Integer.parseInt(Feature.getProperty("FortressHpRegenerationFeeLvl2", "3500")); + FS_EXPREG_FEE_RATIO = Long.parseLong(Feature.getProperty("FortressExpRegenerationFunctionFeeRatio", "86400000")); + FS_EXPREG1_FEE = Integer.parseInt(Feature.getProperty("FortressExpRegenerationFeeLvl1", "9000")); + FS_EXPREG2_FEE = Integer.parseInt(Feature.getProperty("FortressExpRegenerationFeeLvl2", "10000")); + FS_UPDATE_FRQ = Integer.parseInt(Feature.getProperty("FortressPeriodicUpdateFrequency", "360")); + FS_BLOOD_OATH_COUNT = Integer.parseInt(Feature.getProperty("FortressBloodOathCount", "1")); + FS_MAX_SUPPLY_LEVEL = Integer.parseInt(Feature.getProperty("FortressMaxSupplyLevel", "6")); + FS_FEE_FOR_CASTLE = Integer.parseInt(Feature.getProperty("FortressFeeForCastle", "25000")); + FS_MAX_OWN_TIME = Integer.parseInt(Feature.getProperty("FortressMaximumOwnTime", "168")); + + ALT_GAME_CASTLE_DAWN = Boolean.parseBoolean(Feature.getProperty("AltCastleForDawn", "True")); + ALT_GAME_CASTLE_DUSK = Boolean.parseBoolean(Feature.getProperty("AltCastleForDusk", "True")); + ALT_GAME_REQUIRE_CLAN_CASTLE = Boolean.parseBoolean(Feature.getProperty("AltRequireClanCastle", "False")); + ALT_FESTIVAL_MIN_PLAYER = Integer.parseInt(Feature.getProperty("AltFestivalMinPlayer", "5")); + ALT_MAXIMUM_PLAYER_CONTRIB = Integer.parseInt(Feature.getProperty("AltMaxPlayerContrib", "1000000")); + ALT_FESTIVAL_MANAGER_START = Long.parseLong(Feature.getProperty("AltFestivalManagerStart", "120000")); + ALT_FESTIVAL_LENGTH = Long.parseLong(Feature.getProperty("AltFestivalLength", "1080000")); + ALT_FESTIVAL_CYCLE_LENGTH = Long.parseLong(Feature.getProperty("AltFestivalCycleLength", "2280000")); + ALT_FESTIVAL_FIRST_SPAWN = Long.parseLong(Feature.getProperty("AltFestivalFirstSpawn", "120000")); + ALT_FESTIVAL_FIRST_SWARM = Long.parseLong(Feature.getProperty("AltFestivalFirstSwarm", "300000")); + ALT_FESTIVAL_SECOND_SPAWN = Long.parseLong(Feature.getProperty("AltFestivalSecondSpawn", "540000")); + ALT_FESTIVAL_SECOND_SWARM = Long.parseLong(Feature.getProperty("AltFestivalSecondSwarm", "720000")); + ALT_FESTIVAL_CHEST_SPAWN = Long.parseLong(Feature.getProperty("AltFestivalChestSpawn", "900000")); + ALT_SIEGE_DAWN_GATES_PDEF_MULT = Double.parseDouble(Feature.getProperty("AltDawnGatesPdefMult", "1.1")); + ALT_SIEGE_DUSK_GATES_PDEF_MULT = Double.parseDouble(Feature.getProperty("AltDuskGatesPdefMult", "0.8")); + ALT_SIEGE_DAWN_GATES_MDEF_MULT = Double.parseDouble(Feature.getProperty("AltDawnGatesMdefMult", "1.1")); + ALT_SIEGE_DUSK_GATES_MDEF_MULT = Double.parseDouble(Feature.getProperty("AltDuskGatesMdefMult", "0.8")); + ALT_STRICT_SEVENSIGNS = Boolean.parseBoolean(Feature.getProperty("StrictSevenSigns", "True")); + ALT_SEVENSIGNS_LAZY_UPDATE = Boolean.parseBoolean(Feature.getProperty("AltSevenSignsLazyUpdate", "True")); + + TAKE_FORT_POINTS = Integer.parseInt(Feature.getProperty("TakeFortPoints", "200")); + LOOSE_FORT_POINTS = Integer.parseInt(Feature.getProperty("LooseFortPoints", "0")); + TAKE_CASTLE_POINTS = Integer.parseInt(Feature.getProperty("TakeCastlePoints", "1500")); + LOOSE_CASTLE_POINTS = Integer.parseInt(Feature.getProperty("LooseCastlePoints", "3000")); + CASTLE_DEFENDED_POINTS = Integer.parseInt(Feature.getProperty("CastleDefendedPoints", "750")); + FESTIVAL_WIN_POINTS = Integer.parseInt(Feature.getProperty("FestivalOfDarknessWin", "200")); + HERO_POINTS = Integer.parseInt(Feature.getProperty("HeroPoints", "1000")); + ROYAL_GUARD_COST = Integer.parseInt(Feature.getProperty("CreateRoyalGuardCost", "5000")); + KNIGHT_UNIT_COST = Integer.parseInt(Feature.getProperty("CreateKnightUnitCost", "10000")); + KNIGHT_REINFORCE_COST = Integer.parseInt(Feature.getProperty("ReinforceKnightUnitCost", "5000")); + BALLISTA_POINTS = Integer.parseInt(Feature.getProperty("KillBallistaPoints", "30")); + BLOODALLIANCE_POINTS = Integer.parseInt(Feature.getProperty("BloodAlliancePoints", "500")); + BLOODOATH_POINTS = Integer.parseInt(Feature.getProperty("BloodOathPoints", "200")); + KNIGHTSEPAULETTE_POINTS = Integer.parseInt(Feature.getProperty("KnightsEpaulettePoints", "20")); + REPUTATION_SCORE_PER_KILL = Integer.parseInt(Feature.getProperty("ReputationScorePerKill", "1")); + JOIN_ACADEMY_MIN_REP_SCORE = Integer.parseInt(Feature.getProperty("CompleteAcademyMinPoints", "190")); + JOIN_ACADEMY_MAX_REP_SCORE = Integer.parseInt(Feature.getProperty("CompleteAcademyMaxPoints", "650")); + RAID_RANKING_1ST = Integer.parseInt(Feature.getProperty("1stRaidRankingPoints", "1250")); + RAID_RANKING_2ND = Integer.parseInt(Feature.getProperty("2ndRaidRankingPoints", "900")); + RAID_RANKING_3RD = Integer.parseInt(Feature.getProperty("3rdRaidRankingPoints", "700")); + RAID_RANKING_4TH = Integer.parseInt(Feature.getProperty("4thRaidRankingPoints", "600")); + RAID_RANKING_5TH = Integer.parseInt(Feature.getProperty("5thRaidRankingPoints", "450")); + RAID_RANKING_6TH = Integer.parseInt(Feature.getProperty("6thRaidRankingPoints", "350")); + RAID_RANKING_7TH = Integer.parseInt(Feature.getProperty("7thRaidRankingPoints", "300")); + RAID_RANKING_8TH = Integer.parseInt(Feature.getProperty("8thRaidRankingPoints", "200")); + RAID_RANKING_9TH = Integer.parseInt(Feature.getProperty("9thRaidRankingPoints", "150")); + RAID_RANKING_10TH = Integer.parseInt(Feature.getProperty("10thRaidRankingPoints", "100")); + RAID_RANKING_UP_TO_50TH = Integer.parseInt(Feature.getProperty("UpTo50thRaidRankingPoints", "25")); + RAID_RANKING_UP_TO_100TH = Integer.parseInt(Feature.getProperty("UpTo100thRaidRankingPoints", "12")); + CLAN_LEVEL_6_COST = Integer.parseInt(Feature.getProperty("ClanLevel6Cost", "10000")); + CLAN_LEVEL_7_COST = Integer.parseInt(Feature.getProperty("ClanLevel7Cost", "20000")); + CLAN_LEVEL_8_COST = Integer.parseInt(Feature.getProperty("ClanLevel8Cost", "40000")); + CLAN_LEVEL_9_COST = Integer.parseInt(Feature.getProperty("ClanLevel9Cost", "40000")); + CLAN_LEVEL_10_COST = Integer.parseInt(Feature.getProperty("ClanLevel10Cost", "40000")); + CLAN_LEVEL_11_COST = Integer.parseInt(Feature.getProperty("ClanLevel11Cost", "75000")); + CLAN_LEVEL_6_REQUIREMENT = Integer.parseInt(Feature.getProperty("ClanLevel6Requirement", "30")); + CLAN_LEVEL_7_REQUIREMENT = Integer.parseInt(Feature.getProperty("ClanLevel7Requirement", "80")); + CLAN_LEVEL_8_REQUIREMENT = Integer.parseInt(Feature.getProperty("ClanLevel8Requirement", "120")); + CLAN_LEVEL_9_REQUIREMENT = Integer.parseInt(Feature.getProperty("ClanLevel9Requirement", "120")); + CLAN_LEVEL_10_REQUIREMENT = Integer.parseInt(Feature.getProperty("ClanLevel10Requirement", "140")); + CLAN_LEVEL_11_REQUIREMENT = Integer.parseInt(Feature.getProperty("ClanLevel11Requirement", "170")); + ALLOW_WYVERN_DURING_SIEGE = Boolean.parseBoolean(Feature.getProperty("AllowRideWyvernDuringSiege", "True")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + FEATURE_CONFIG_FILE + " File."); + } + + // Load Character L2Properties file (if exists) + try + { + L2Properties Character = new L2Properties(); + is = new FileInputStream(new File(CHARACTER_CONFIG_FILE)); + Character.load(is); + + MASTERACCESS_LEVEL = Integer.parseInt(Character.getProperty("MasterAccessLevel", "127")); + MASTERACCESS_NAME_COLOR = Integer.decode(StringUtil.concat("0x", Character.getProperty("MasterNameColor", "00FF00"))); + MASTERACCESS_TITLE_COLOR = Integer.decode(StringUtil.concat("0x", Character.getProperty("MasterTitleColor", "00FF00"))); + ALT_GAME_DELEVEL = Boolean.parseBoolean(Character.getProperty("Delevel", "true")); + DECREASE_SKILL_LEVEL = Boolean.parseBoolean(Character.getProperty("DecreaseSkillOnDelevel", "true")); + ALT_WEIGHT_LIMIT = Double.parseDouble(Character.getProperty("AltWeightLimit", "1")); + RUN_SPD_BOOST = Integer.parseInt(Character.getProperty("RunSpeedBoost", "0")); + DEATH_PENALTY_CHANCE = Integer.parseInt(Character.getProperty("DeathPenaltyChance", "20")); + RESPAWN_RESTORE_CP = Double.parseDouble(Character.getProperty("RespawnRestoreCP", "0")) / 100; + RESPAWN_RESTORE_HP = Double.parseDouble(Character.getProperty("RespawnRestoreHP", "65")) / 100; + RESPAWN_RESTORE_MP = Double.parseDouble(Character.getProperty("RespawnRestoreMP", "0")) / 100; + HP_REGEN_MULTIPLIER = Double.parseDouble(Character.getProperty("HpRegenMultiplier", "100")) / 100; + MP_REGEN_MULTIPLIER = Double.parseDouble(Character.getProperty("MpRegenMultiplier", "100")) / 100; + CP_REGEN_MULTIPLIER = Double.parseDouble(Character.getProperty("CpRegenMultiplier", "100")) / 100; + ALT_GAME_TIREDNESS = Boolean.parseBoolean(Character.getProperty("AltGameTiredness", "false")); + ENABLE_MODIFY_SKILL_DURATION = Boolean.parseBoolean(Character.getProperty("EnableModifySkillDuration", "false")); + + // Create Map only if enabled + if (ENABLE_MODIFY_SKILL_DURATION) + { + String[] propertySplit = Character.getProperty("SkillDurationList", "").split(";"); + SKILL_DURATION_LIST = new TIntIntHashMap(propertySplit.length); + for (String skill : propertySplit) + { + String[] skillSplit = skill.split(","); + if (skillSplit.length != 2) + { + _log.warning(StringUtil.concat("[SkillDurationList]: invalid config property -> SkillDurationList \"", skill, "\"")); + } + else + { + try + { + SKILL_DURATION_LIST.put(Integer.parseInt(skillSplit[0]), Integer.parseInt(skillSplit[1])); + } + catch (NumberFormatException nfe) + { + if (!skill.isEmpty()) + { + _log.warning(StringUtil.concat("[SkillDurationList]: invalid config property -> SkillList \"", skillSplit[0], "\"", skillSplit[1])); + } + } + } + } + } + ENABLE_MODIFY_SKILL_REUSE = Boolean.parseBoolean(Character.getProperty("EnableModifySkillReuse", "false")); + // Create Map only if enabled + if (ENABLE_MODIFY_SKILL_REUSE) + { + String[] propertySplit = Character.getProperty("SkillReuseList", "").split(";"); + SKILL_REUSE_LIST = new TIntIntHashMap(propertySplit.length); + for (String skill : propertySplit) + { + String[] skillSplit = skill.split(","); + if (skillSplit.length != 2) + { + _log.warning(StringUtil.concat("[SkillReuseList]: invalid config property -> SkillReuseList \"", skill, "\"")); + } + else + { + try + { + SKILL_REUSE_LIST.put(Integer.valueOf(skillSplit[0]), Integer.valueOf(skillSplit[1])); + } + catch (NumberFormatException nfe) + { + if (!skill.isEmpty()) + { + _log.warning(StringUtil.concat("[SkillReuseList]: invalid config property -> SkillList \"", skillSplit[0], "\"", skillSplit[1])); + } + } + } + } + } + + AUTO_LEARN_SKILLS = Boolean.parseBoolean(Character.getProperty("AutoLearnSkills", "False")); + AUTO_LEARN_FS_SKILLS = Boolean.parseBoolean(Character.getProperty("AutoLearnForgottenScrollSkills", "False")); + AUTO_LOOT_HERBS = Boolean.parseBoolean(Character.getProperty("AutoLootHerbs", "false")); + BUFFS_MAX_AMOUNT = Byte.parseByte(Character.getProperty("maxbuffamount", "20")); + DANCES_MAX_AMOUNT = Byte.parseByte(Character.getProperty("maxdanceamount", "12")); + DANCE_CANCEL_BUFF = Boolean.parseBoolean(Character.getProperty("DanceCancelBuff", "false")); + DANCE_CONSUME_ADDITIONAL_MP = Boolean.parseBoolean(Character.getProperty("DanceConsumeAdditionalMP", "true")); + AUTO_LEARN_DIVINE_INSPIRATION = Boolean.parseBoolean(Character.getProperty("AutoLearnDivineInspiration", "false")); + ALT_GAME_CANCEL_BOW = Character.getProperty("AltGameCancelByHit", "Cast").equalsIgnoreCase("bow") || Character.getProperty("AltGameCancelByHit", "Cast").equalsIgnoreCase("all"); + ALT_GAME_CANCEL_CAST = Character.getProperty("AltGameCancelByHit", "Cast").equalsIgnoreCase("cast") || Character.getProperty("AltGameCancelByHit", "Cast").equalsIgnoreCase("all"); + EFFECT_CANCELING = Boolean.parseBoolean(Character.getProperty("CancelLesserEffect", "True")); + ALT_GAME_MAGICFAILURES = Boolean.parseBoolean(Character.getProperty("MagicFailures", "true")); + PLAYER_FAKEDEATH_UP_PROTECTION = Integer.parseInt(Character.getProperty("PlayerFakeDeathUpProtection", "0")); + STORE_SKILL_COOLTIME = Boolean.parseBoolean(Character.getProperty("StoreSkillCooltime", "true")); + SUBCLASS_STORE_SKILL_COOLTIME = Boolean.parseBoolean(Character.getProperty("SubclassStoreSkillCooltime", "false")); + ALT_GAME_SHIELD_BLOCKS = Boolean.parseBoolean(Character.getProperty("AltShieldBlocks", "false")); + ALT_PERFECT_SHLD_BLOCK = Integer.parseInt(Character.getProperty("AltPerfectShieldBlockRate", "10")); + ALLOW_CLASS_MASTERS = Boolean.parseBoolean(Character.getProperty("AllowClassMasters", "False")); + ALLOW_ENTIRE_TREE = Boolean.parseBoolean(Character.getProperty("AllowEntireTree", "False")); + ALTERNATE_CLASS_MASTER = Boolean.parseBoolean(Character.getProperty("AlternateClassMaster", "False")); + if (ALLOW_CLASS_MASTERS || ALTERNATE_CLASS_MASTER) + { + CLASS_MASTER_SETTINGS = new ClassMasterSettings(Character.getProperty("ConfigClassMaster")); + } + LIFE_CRYSTAL_NEEDED = Boolean.parseBoolean(Character.getProperty("LifeCrystalNeeded", "true")); + ES_SP_BOOK_NEEDED = Boolean.parseBoolean(Character.getProperty("EnchantSkillSpBookNeeded", "true")); + DIVINE_SP_BOOK_NEEDED = Boolean.parseBoolean(Character.getProperty("DivineInspirationSpBookNeeded", "true")); + ALT_GAME_SKILL_LEARN = Boolean.parseBoolean(Character.getProperty("AltGameSkillLearn", "false")); + ALT_GAME_SUBCLASS_WITHOUT_QUESTS = Boolean.parseBoolean(Character.getProperty("AltSubClassWithoutQuests", "False")); + ALT_GAME_SUBCLASS_EVERYWHERE = Boolean.parseBoolean(Character.getProperty("AltSubclassEverywhere", "False")); + ENABLE_VITALITY = Boolean.parseBoolean(Character.getProperty("EnableVitality", "True")); + RECOVER_VITALITY_ON_RECONNECT = Boolean.parseBoolean(Character.getProperty("RecoverVitalityOnReconnect", "True")); + ALLOW_TRANSFORM_WITHOUT_QUEST = Boolean.parseBoolean(Character.getProperty("AltTransformationWithoutQuest", "False")); + STARTING_VITALITY_POINTS = Integer.parseInt(Character.getProperty("StartingVitalityPoints", "20000")); + MAX_RUN_SPEED = Integer.parseInt(Character.getProperty("MaxRunSpeed", "250")); + MAX_PCRIT_RATE = Integer.parseInt(Character.getProperty("MaxPCritRate", "500")); + MAX_MCRIT_RATE = Integer.parseInt(Character.getProperty("MaxMCritRate", "200")); + MAX_PATK_SPEED = Integer.parseInt(Character.getProperty("MaxPAtkSpeed", "1500")); + MAX_MATK_SPEED = Integer.parseInt(Character.getProperty("MaxMAtkSpeed", "1999")); + MAX_EVASION = Integer.parseInt(Character.getProperty("MaxEvasion", "250")); + MAX_SUBCLASS = Byte.parseByte(Character.getProperty("MaxSubclass", "3")); + MAX_SUBCLASS_LEVEL = Byte.parseByte(Character.getProperty("MaxSubclassLevel", "80")); + MAX_PVTSTORESELL_SLOTS_DWARF = Integer.parseInt(Character.getProperty("MaxPvtStoreSellSlotsDwarf", "4")); + MAX_PVTSTORESELL_SLOTS_OTHER = Integer.parseInt(Character.getProperty("MaxPvtStoreSellSlotsOther", "3")); + MAX_PVTSTOREBUY_SLOTS_DWARF = Integer.parseInt(Character.getProperty("MaxPvtStoreBuySlotsDwarf", "5")); + MAX_PVTSTOREBUY_SLOTS_OTHER = Integer.parseInt(Character.getProperty("MaxPvtStoreBuySlotsOther", "4")); + INVENTORY_MAXIMUM_NO_DWARF = Integer.parseInt(Character.getProperty("MaximumSlotsForNoDwarf", "80")); + INVENTORY_MAXIMUM_DWARF = Integer.parseInt(Character.getProperty("MaximumSlotsForDwarf", "100")); + INVENTORY_MAXIMUM_GM = Integer.parseInt(Character.getProperty("MaximumSlotsForGMPlayer", "250")); + INVENTORY_MAXIMUM_QUEST_ITEMS = Integer.parseInt(Character.getProperty("MaximumSlotsForQuestItems", "100")); + MAX_ITEM_IN_PACKET = Math.max(INVENTORY_MAXIMUM_NO_DWARF, Math.max(INVENTORY_MAXIMUM_DWARF, INVENTORY_MAXIMUM_GM)); + WAREHOUSE_SLOTS_DWARF = Integer.parseInt(Character.getProperty("MaximumWarehouseSlotsForDwarf", "120")); + WAREHOUSE_SLOTS_NO_DWARF = Integer.parseInt(Character.getProperty("MaximumWarehouseSlotsForNoDwarf", "100")); + WAREHOUSE_SLOTS_CLAN = Integer.parseInt(Character.getProperty("MaximumWarehouseSlotsForClan", "150")); + ENCHANT_CHANCE_WEAPON = Integer.parseInt(Character.getProperty("EnchantChanceWeapon", "66")); + ENCHANT_CHANCE_ARMOR = Integer.parseInt(Character.getProperty("EnchantChanceArmor", "66")); + ENCHANT_CHANCE_JEWELRY = Integer.parseInt(Character.getProperty("EnchantChanceJewelry", "66")); + ENCHANT_CHANCE_ELEMENT_STONE = Integer.parseInt(Character.getProperty("EnchantChanceElementStone", "50")); + ENCHANT_CHANCE_ELEMENT_CRYSTAL = Integer.parseInt(Character.getProperty("EnchantChanceElementCrystal", "30")); + ENCHANT_CHANCE_ELEMENT_JEWEL = Integer.parseInt(Character.getProperty("EnchantChanceElementJewel", "20")); + ENCHANT_CHANCE_ELEMENT_ENERGY = Integer.parseInt(Character.getProperty("EnchantChanceElementEnergy", "10")); + BLESSED_ENCHANT_CHANCE_WEAPON = Integer.parseInt(Character.getProperty("BlessedEnchantChanceWeapon", "66")); + BLESSED_ENCHANT_CHANCE_ARMOR = Integer.parseInt(Character.getProperty("BlessedEnchantChanceArmor", "66")); + BLESSED_ENCHANT_CHANCE_JEWELRY = Integer.parseInt(Character.getProperty("BlessedEnchantChanceJewelry", "66")); + ENCHANT_MAX_WEAPON = Integer.parseInt(Character.getProperty("EnchantMaxWeapon", "0")); + ENCHANT_MAX_ARMOR = Integer.parseInt(Character.getProperty("EnchantMaxArmor", "0")); + ENCHANT_MAX_JEWELRY = Integer.parseInt(Character.getProperty("EnchantMaxJewelry", "0")); + ENCHANT_SAFE_MAX = Integer.parseInt(Character.getProperty("EnchantSafeMax", "3")); + ENCHANT_SAFE_MAX_FULL = Integer.parseInt(Character.getProperty("EnchantSafeMaxFull", "4")); + String[] notenchantable = Character.getProperty("EnchantBlackList", "7816,7817,7818,7819,7820,7821,7822,7823,7824,7825,7826,7827,7828,7829,7830,7831,13293,13294,13296").split(","); + ENCHANT_BLACKLIST = new int[notenchantable.length]; + for (int i = 0; i < notenchantable.length; i++) + { + ENCHANT_BLACKLIST[i] = Integer.parseInt(notenchantable[i]); + } + Arrays.sort(ENCHANT_BLACKLIST); + + AUGMENTATION_NG_SKILL_CHANCE = Integer.parseInt(Character.getProperty("AugmentationNGSkillChance", "15")); + AUGMENTATION_NG_GLOW_CHANCE = Integer.parseInt(Character.getProperty("AugmentationNGGlowChance", "0")); + AUGMENTATION_MID_SKILL_CHANCE = Integer.parseInt(Character.getProperty("AugmentationMidSkillChance", "30")); + AUGMENTATION_MID_GLOW_CHANCE = Integer.parseInt(Character.getProperty("AugmentationMidGlowChance", "40")); + AUGMENTATION_HIGH_SKILL_CHANCE = Integer.parseInt(Character.getProperty("AugmentationHighSkillChance", "45")); + AUGMENTATION_HIGH_GLOW_CHANCE = Integer.parseInt(Character.getProperty("AugmentationHighGlowChance", "70")); + AUGMENTATION_TOP_SKILL_CHANCE = Integer.parseInt(Character.getProperty("AugmentationTopSkillChance", "60")); + AUGMENTATION_TOP_GLOW_CHANCE = Integer.parseInt(Character.getProperty("AugmentationTopGlowChance", "100")); + AUGMENTATION_BASESTAT_CHANCE = Integer.parseInt(Character.getProperty("AugmentationBaseStatChance", "1")); + AUGMENTATION_ACC_SKILL_CHANCE = Integer.parseInt(Character.getProperty("AugmentationAccSkillChance", "0")); + + FEE_DELETE_TRANSFER_SKILLS = Integer.parseInt(Character.getProperty("FeeDeleteTransferSkills", "10000000")); + FEE_DELETE_SUBCLASS_SKILLS = Integer.parseInt(Character.getProperty("FeeDeleteSubClassSkills", "10000000")); + String[] array = Character.getProperty("AugmentationBlackList", "6656,6657,6658,6659,6660,6661,6662,8191,10170,10314,13740,13741,13742,13743,13744,13745,13746,13747,13748,14592,14593,14594,14595,14596,14597,14598,14599,14600,14664,14665,14666,14667,14668,14669,14670,14671,14672,14801,14802,14803,14804,14805,14806,14807,14808,14809,15282,15283,15284,15285,15286,15287,15288,15289,15290,15291,15292,15293,15294,15295,15296,15297,15298,15299,16025,16026").split(","); + AUGMENTATION_BLACKLIST = new int[array.length]; + + for (int i = 0; i < array.length; i++) + { + AUGMENTATION_BLACKLIST[i] = Integer.parseInt(array[i]); + } + + Arrays.sort(AUGMENTATION_BLACKLIST); + + ALT_GAME_KARMA_PLAYER_CAN_BE_KILLED_IN_PEACEZONE = Boolean.parseBoolean(Character.getProperty("AltKarmaPlayerCanBeKilledInPeaceZone", "false")); + ALT_GAME_KARMA_PLAYER_CAN_SHOP = Boolean.parseBoolean(Character.getProperty("AltKarmaPlayerCanShop", "true")); + ALT_GAME_KARMA_PLAYER_CAN_TELEPORT = Boolean.parseBoolean(Character.getProperty("AltKarmaPlayerCanTeleport", "true")); + ALT_GAME_KARMA_PLAYER_CAN_USE_GK = Boolean.parseBoolean(Character.getProperty("AltKarmaPlayerCanUseGK", "false")); + ALT_GAME_KARMA_PLAYER_CAN_TRADE = Boolean.parseBoolean(Character.getProperty("AltKarmaPlayerCanTrade", "true")); + ALT_GAME_KARMA_PLAYER_CAN_USE_WAREHOUSE = Boolean.parseBoolean(Character.getProperty("AltKarmaPlayerCanUseWareHouse", "true")); + MAX_PERSONAL_FAME_POINTS = Integer.parseInt(Character.getProperty("MaxPersonalFamePoints", "65535")); + FORTRESS_ZONE_FAME_TASK_FREQUENCY = Integer.parseInt(Character.getProperty("FortressZoneFameTaskFrequency", "300")); + FORTRESS_ZONE_FAME_AQUIRE_POINTS = Integer.parseInt(Character.getProperty("FortressZoneFameAquirePoints", "31")); + CASTLE_ZONE_FAME_TASK_FREQUENCY = Integer.parseInt(Character.getProperty("CastleZoneFameTaskFrequency", "300")); + CASTLE_ZONE_FAME_AQUIRE_POINTS = Integer.parseInt(Character.getProperty("CastleZoneFameAquirePoints", "125")); + FAME_FOR_DEAD_PLAYERS = Boolean.parseBoolean(Character.getProperty("FameForDeadPlayers", "true")); + IS_CRAFTING_ENABLED = Boolean.parseBoolean(Character.getProperty("CraftingEnabled", "true")); + CRAFT_MASTERWORK = Boolean.parseBoolean(Character.getProperty("CraftMasterwork", "True")); + DWARF_RECIPE_LIMIT = Integer.parseInt(Character.getProperty("DwarfRecipeLimit", "50")); + COMMON_RECIPE_LIMIT = Integer.parseInt(Character.getProperty("CommonRecipeLimit", "50")); + ALT_GAME_CREATION = Boolean.parseBoolean(Character.getProperty("AltGameCreation", "false")); + ALT_GAME_CREATION_SPEED = Double.parseDouble(Character.getProperty("AltGameCreationSpeed", "1")); + ALT_GAME_CREATION_XP_RATE = Double.parseDouble(Character.getProperty("AltGameCreationXpRate", "1")); + ALT_GAME_CREATION_SP_RATE = Double.parseDouble(Character.getProperty("AltGameCreationSpRate", "1")); + ALT_GAME_CREATION_RARE_XPSP_RATE = Double.parseDouble(Character.getProperty("AltGameCreationRareXpSpRate", "2")); + ALT_BLACKSMITH_USE_RECIPES = Boolean.parseBoolean(Character.getProperty("AltBlacksmithUseRecipes", "true")); + ALT_CLAN_JOIN_DAYS = Integer.parseInt(Character.getProperty("DaysBeforeJoinAClan", "1")); + ALT_CLAN_CREATE_DAYS = Integer.parseInt(Character.getProperty("DaysBeforeCreateAClan", "10")); + ALT_CLAN_DISSOLVE_DAYS = Integer.parseInt(Character.getProperty("DaysToPassToDissolveAClan", "7")); + ALT_ALLY_JOIN_DAYS_WHEN_LEAVED = Integer.parseInt(Character.getProperty("DaysBeforeJoinAllyWhenLeaved", "1")); + ALT_ALLY_JOIN_DAYS_WHEN_DISMISSED = Integer.parseInt(Character.getProperty("DaysBeforeJoinAllyWhenDismissed", "1")); + ALT_ACCEPT_CLAN_DAYS_WHEN_DISMISSED = Integer.parseInt(Character.getProperty("DaysBeforeAcceptNewClanWhenDismissed", "1")); + ALT_CREATE_ALLY_DAYS_WHEN_DISSOLVED = Integer.parseInt(Character.getProperty("DaysBeforeCreateNewAllyWhenDissolved", "1")); + ALT_MAX_NUM_OF_CLANS_IN_ALLY = Integer.parseInt(Character.getProperty("AltMaxNumOfClansInAlly", "3")); + ALT_CLAN_MEMBERS_FOR_WAR = Integer.parseInt(Character.getProperty("AltClanMembersForWar", "15")); + ALT_MEMBERS_CAN_WITHDRAW_FROM_CLANWH = Boolean.parseBoolean(Character.getProperty("AltMembersCanWithdrawFromClanWH", "false")); + REMOVE_CASTLE_CIRCLETS = Boolean.parseBoolean(Character.getProperty("RemoveCastleCirclets", "true")); + ALT_PARTY_RANGE = Integer.parseInt(Character.getProperty("AltPartyRange", "1600")); + ALT_PARTY_RANGE2 = Integer.parseInt(Character.getProperty("AltPartyRange2", "1400")); + STARTING_ADENA = Long.parseLong(Character.getProperty("StartingAdena", "0")); + STARTING_LEVEL = Byte.parseByte(Character.getProperty("StartingLevel", "1")); + STARTING_SP = Integer.parseInt(Character.getProperty("StartingSP", "0")); + AUTO_LOOT = Boolean.parseBoolean(Character.getProperty("AutoLoot", "false")); + AUTO_LOOT_RAIDS = Boolean.parseBoolean(Character.getProperty("AutoLootRaids", "false")); + LOOT_RAIDS_PRIVILEGE_INTERVAL = Integer.parseInt(Character.getProperty("RaidLootRightsInterval", "900")) * 1000; + LOOT_RAIDS_PRIVILEGE_CC_SIZE = Integer.parseInt(Character.getProperty("RaidLootRightsCCSize", "45")); + UNSTUCK_INTERVAL = Integer.parseInt(Character.getProperty("UnstuckInterval", "300")); + TELEPORT_WATCHDOG_TIMEOUT = Integer.parseInt(Character.getProperty("TeleportWatchdogTimeout", "0")); + PLAYER_SPAWN_PROTECTION = Integer.parseInt(Character.getProperty("PlayerSpawnProtection", "0")); + String[] items = Character.getProperty("PlayerSpawnProtectionAllowedItems", "0").split(","); + SPAWN_PROTECTION_ALLOWED_ITEMS = new ArrayList<>(items.length); + for (String item : items) + { + Integer itm = 0; + try + { + itm = Integer.parseInt(item); + } + catch (NumberFormatException nfe) + { + _log.warning("Player Spawn Protection: Wrong ItemId passed: " + item); + _log.warning(nfe.getMessage()); + } + if (itm != 0) + { + SPAWN_PROTECTION_ALLOWED_ITEMS.add(itm); + } + } + SPAWN_PROTECTION_ALLOWED_ITEMS.trimToSize(); + PLAYER_TELEPORT_PROTECTION = Integer.parseInt(Character.getProperty("PlayerTeleportProtection", "0")); + RANDOM_RESPAWN_IN_TOWN_ENABLED = Boolean.parseBoolean(Character.getProperty("RandomRespawnInTownEnabled", "True")); + OFFSET_ON_TELEPORT_ENABLED = Boolean.parseBoolean(Character.getProperty("OffsetOnTeleportEnabled", "True")); + MAX_OFFSET_ON_TELEPORT = Integer.parseInt(Character.getProperty("MaxOffsetOnTeleport", "50")); + RESTORE_PLAYER_INSTANCE = Boolean.parseBoolean(Character.getProperty("RestorePlayerInstance", "False")); + ALLOW_SUMMON_TO_INSTANCE = Boolean.parseBoolean(Character.getProperty("AllowSummonToInstance", "True")); + PETITIONING_ALLOWED = Boolean.parseBoolean(Character.getProperty("PetitioningAllowed", "True")); + MAX_PETITIONS_PER_PLAYER = Integer.parseInt(Character.getProperty("MaxPetitionsPerPlayer", "5")); + MAX_PETITIONS_PENDING = Integer.parseInt(Character.getProperty("MaxPetitionsPending", "25")); + ALT_GAME_FREE_TELEPORT = Boolean.parseBoolean(Character.getProperty("AltFreeTeleporting", "False")); + DELETE_DAYS = Integer.parseInt(Character.getProperty("DeleteCharAfterDays", "7")); + ALT_GAME_EXPONENT_XP = Float.parseFloat(Character.getProperty("AltGameExponentXp", "0.")); + ALT_GAME_EXPONENT_SP = Float.parseFloat(Character.getProperty("AltGameExponentSp", "0.")); + PARTY_XP_CUTOFF_METHOD = Character.getProperty("PartyXpCutoffMethod", "level"); + PARTY_XP_CUTOFF_PERCENT = Double.parseDouble(Character.getProperty("PartyXpCutoffPercent", "3.")); + PARTY_XP_CUTOFF_LEVEL = Integer.parseInt(Character.getProperty("PartyXpCutoffLevel", "20")); + DISABLE_TUTORIAL = Boolean.parseBoolean(Character.getProperty("DisableTutorial", "False")); + EXPERTISE_PENALTY = Boolean.parseBoolean(Character.getProperty("ExpertisePenalty", "True")); + STORE_RECIPE_SHOPLIST = Boolean.parseBoolean(Character.getProperty("StoreRecipeShopList", "False")); + STORE_UI_SETTINGS = Boolean.parseBoolean(Character.getProperty("StoreCharUiSettings", "False")); + FORBIDDEN_NAMES = Character.getProperty("ForbiddenNames", "").split(","); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + CHARACTER_CONFIG_FILE + " file."); + } + + // Load Telnet L2Properties file (if exists) + try + { + L2Properties telnetSettings = new L2Properties(); + is = new FileInputStream(new File(TELNET_FILE)); + telnetSettings.load(is); + + IS_TELNET_ENABLED = Boolean.parseBoolean(telnetSettings.getProperty("EnableTelnet", "false")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + TELNET_FILE + " File."); + } + + // MMO + try + { + // _log.info("Loading " + MMO_CONFIG_FILE.replaceAll("./config/", "")); + L2Properties mmoSettings = new L2Properties(); + is = new FileInputStream(new File(MMO_CONFIG_FILE)); + mmoSettings.load(is); + MMO_SELECTOR_SLEEP_TIME = Integer.parseInt(mmoSettings.getProperty("SleepTime", "20")); + MMO_MAX_SEND_PER_PASS = Integer.parseInt(mmoSettings.getProperty("MaxSendPerPass", "12")); + MMO_MAX_READ_PER_PASS = Integer.parseInt(mmoSettings.getProperty("MaxReadPerPass", "12")); + MMO_HELPER_BUFFER_COUNT = Integer.parseInt(mmoSettings.getProperty("HelperBufferCount", "20")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + MMO_CONFIG_FILE + " File."); + } + + // Load IdFactory L2Properties file (if exists) + try + { + L2Properties idSettings = new L2Properties(); + is = new FileInputStream(new File(ID_CONFIG_FILE)); + idSettings.load(is); + + MAP_TYPE = ObjectMapType.valueOf(idSettings.getProperty("L2Map", "WorldObjectMap")); + SET_TYPE = ObjectSetType.valueOf(idSettings.getProperty("L2Set", "WorldObjectSet")); + IDFACTORY_TYPE = IdFactoryType.valueOf(idSettings.getProperty("IDFactory", "Compaction")); + BAD_ID_CHECKING = Boolean.parseBoolean(idSettings.getProperty("BadIdChecking", "True")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + ID_CONFIG_FILE + " file."); + } + + // Load General L2Properties file (if exists) + try + { + L2Properties General = new L2Properties(); + is = new FileInputStream(new File(GENERAL_CONFIG_FILE)); + General.load(is); + + EVERYBODY_HAS_ADMIN_RIGHTS = Boolean.parseBoolean(General.getProperty("EverybodyHasAdminRights", "false")); + DISPLAY_SERVER_VERSION = Boolean.parseBoolean(General.getProperty("DisplayServerRevision", "True")); + SERVER_LIST_BRACKET = Boolean.parseBoolean(General.getProperty("ServerListBrackets", "false")); + SERVER_LIST_TYPE = getServerTypeId(General.getProperty("ServerListType", "Normal").split(",")); + SERVER_LIST_AGE = Integer.parseInt(General.getProperty("ServerListAge", "0")); + SERVER_GMONLY = Boolean.parseBoolean(General.getProperty("ServerGMOnly", "false")); + GM_HERO_AURA = Boolean.parseBoolean(General.getProperty("GMHeroAura", "False")); + GM_STARTUP_INVULNERABLE = Boolean.parseBoolean(General.getProperty("GMStartupInvulnerable", "False")); + GM_STARTUP_INVISIBLE = Boolean.parseBoolean(General.getProperty("GMStartupInvisible", "False")); + GM_STARTUP_SILENCE = Boolean.parseBoolean(General.getProperty("GMStartupSilence", "False")); + GM_STARTUP_AUTO_LIST = Boolean.parseBoolean(General.getProperty("GMStartupAutoList", "False")); + GM_STARTUP_DIET_MODE = Boolean.parseBoolean(General.getProperty("GMStartupDietMode", "False")); + GM_ADMIN_MENU_STYLE = General.getProperty("GMAdminMenuStyle", "modern"); + GM_ITEM_RESTRICTION = Boolean.parseBoolean(General.getProperty("GMItemRestriction", "True")); + GM_SKILL_RESTRICTION = Boolean.parseBoolean(General.getProperty("GMSkillRestriction", "True")); + GM_TRADE_RESTRICTED_ITEMS = Boolean.parseBoolean(General.getProperty("GMTradeRestrictedItems", "False")); + GM_RESTART_FIGHTING = Boolean.parseBoolean(General.getProperty("GMRestartFighting", "True")); + GM_ANNOUNCER_NAME = Boolean.parseBoolean(General.getProperty("GMShowAnnouncerName", "False")); + GM_GIVE_SPECIAL_SKILLS = Boolean.parseBoolean(General.getProperty("GMGiveSpecialSkills", "False")); + BYPASS_VALIDATION = Boolean.parseBoolean(General.getProperty("BypassValidation", "True")); + GAMEGUARD_ENFORCE = Boolean.parseBoolean(General.getProperty("GameGuardEnforce", "False")); + GAMEGUARD_PROHIBITACTION = Boolean.parseBoolean(General.getProperty("GameGuardProhibitAction", "False")); + LOG_CHAT = Boolean.parseBoolean(General.getProperty("LogChat", "false")); + LOG_ITEMS = Boolean.parseBoolean(General.getProperty("LogItems", "false")); + LOG_ITEMS_SMALL_LOG = Boolean.parseBoolean(General.getProperty("LogItemsSmallLog", "false")); + LOG_ITEM_ENCHANTS = Boolean.parseBoolean(General.getProperty("LogItemEnchants", "false")); + LOG_SKILL_ENCHANTS = Boolean.parseBoolean(General.getProperty("LogSkillEnchants", "false")); + GMAUDIT = Boolean.parseBoolean(General.getProperty("GMAudit", "False")); + LOG_GAME_DAMAGE = Boolean.parseBoolean(General.getProperty("LogGameDamage", "False")); + LOG_GAME_DAMAGE_THRESHOLD = Integer.parseInt(General.getProperty("LogGameDamageThreshold", "5000")); + SKILL_CHECK_ENABLE = Boolean.parseBoolean(General.getProperty("SkillCheckEnable", "False")); + SKILL_CHECK_REMOVE = Boolean.parseBoolean(General.getProperty("SkillCheckRemove", "False")); + SKILL_CHECK_GM = Boolean.parseBoolean(General.getProperty("SkillCheckGM", "True")); + DEBUG = Boolean.parseBoolean(General.getProperty("Debug", "false")); + PACKET_HANDLER_DEBUG = Boolean.parseBoolean(General.getProperty("PacketHandlerDebug", "false")); + DEVELOPER = Boolean.parseBoolean(General.getProperty("Developer", "false")); + ACCEPT_GEOEDITOR_CONN = Boolean.parseBoolean(General.getProperty("AcceptGeoeditorConn", "false")); + TEST_SERVER = Boolean.parseBoolean(General.getProperty("TestServer", "false")); + ALT_DEV_NO_HANDLERS = Boolean.parseBoolean(General.getProperty("AltDevNoHandlers", "False")); + ALT_DEV_NO_QUESTS = Boolean.parseBoolean(General.getProperty("AltDevNoQuests", "False")); + ALT_DEV_NO_SPAWNS = Boolean.parseBoolean(General.getProperty("AltDevNoSpawns", "False")); + THREAD_P_EFFECTS = Integer.parseInt(General.getProperty("ThreadPoolSizeEffects", "10")); + THREAD_P_GENERAL = Integer.parseInt(General.getProperty("ThreadPoolSizeGeneral", "13")); + IO_PACKET_THREAD_CORE_SIZE = Integer.parseInt(General.getProperty("UrgentPacketThreadCoreSize", "2")); + GENERAL_PACKET_THREAD_CORE_SIZE = Integer.parseInt(General.getProperty("GeneralPacketThreadCoreSize", "4")); + GENERAL_THREAD_CORE_SIZE = Integer.parseInt(General.getProperty("GeneralThreadCoreSize", "4")); + AI_MAX_THREAD = Integer.parseInt(General.getProperty("AiMaxThread", "6")); + CLIENT_PACKET_QUEUE_SIZE = Integer.parseInt(General.getProperty("ClientPacketQueueSize", "0")); + if (CLIENT_PACKET_QUEUE_SIZE == 0) + { + CLIENT_PACKET_QUEUE_SIZE = MMO_MAX_READ_PER_PASS + 2; + } + CLIENT_PACKET_QUEUE_MAX_BURST_SIZE = Integer.parseInt(General.getProperty("ClientPacketQueueMaxBurstSize", "0")); + if (CLIENT_PACKET_QUEUE_MAX_BURST_SIZE == 0) + { + CLIENT_PACKET_QUEUE_MAX_BURST_SIZE = MMO_MAX_READ_PER_PASS + 1; + } + CLIENT_PACKET_QUEUE_MAX_PACKETS_PER_SECOND = Integer.parseInt(General.getProperty("ClientPacketQueueMaxPacketsPerSecond", "80")); + CLIENT_PACKET_QUEUE_MEASURE_INTERVAL = Integer.parseInt(General.getProperty("ClientPacketQueueMeasureInterval", "5")); + CLIENT_PACKET_QUEUE_MAX_AVERAGE_PACKETS_PER_SECOND = Integer.parseInt(General.getProperty("ClientPacketQueueMaxAveragePacketsPerSecond", "40")); + CLIENT_PACKET_QUEUE_MAX_FLOODS_PER_MIN = Integer.parseInt(General.getProperty("ClientPacketQueueMaxFloodsPerMin", "2")); + CLIENT_PACKET_QUEUE_MAX_OVERFLOWS_PER_MIN = Integer.parseInt(General.getProperty("ClientPacketQueueMaxOverflowsPerMin", "1")); + CLIENT_PACKET_QUEUE_MAX_UNDERFLOWS_PER_MIN = Integer.parseInt(General.getProperty("ClientPacketQueueMaxUnderflowsPerMin", "1")); + CLIENT_PACKET_QUEUE_MAX_UNKNOWN_PER_MIN = Integer.parseInt(General.getProperty("ClientPacketQueueMaxUnknownPerMin", "5")); + DEADLOCK_DETECTOR = Boolean.parseBoolean(General.getProperty("DeadLockDetector", "False")); + DEADLOCK_CHECK_INTERVAL = Integer.parseInt(General.getProperty("DeadLockCheckInterval", "20")); + RESTART_ON_DEADLOCK = Boolean.parseBoolean(General.getProperty("RestartOnDeadlock", "False")); + ALLOW_DISCARDITEM = Boolean.parseBoolean(General.getProperty("AllowDiscardItem", "True")); + AUTODESTROY_ITEM_AFTER = Integer.parseInt(General.getProperty("AutoDestroyDroppedItemAfter", "600")); + HERB_AUTO_DESTROY_TIME = Integer.parseInt(General.getProperty("AutoDestroyHerbTime", "60")) * 1000; + String[] split = General.getProperty("ListOfProtectedItems", "0").split(","); + LIST_PROTECTED_ITEMS = new TIntArrayList(split.length); + for (String id : split) + { + LIST_PROTECTED_ITEMS.add(Integer.parseInt(id)); + } + DATABASE_CLEAN_UP = Boolean.parseBoolean(General.getProperty("DatabaseCleanUp", "true")); + CONNECTION_CLOSE_TIME = Long.parseLong(General.getProperty("ConnectionCloseTime", "60000")); + CHAR_STORE_INTERVAL = Integer.parseInt(General.getProperty("CharacterDataStoreInterval", "15")); + LAZY_ITEMS_UPDATE = Boolean.parseBoolean(General.getProperty("LazyItemsUpdate", "false")); + UPDATE_ITEMS_ON_CHAR_STORE = Boolean.parseBoolean(General.getProperty("UpdateItemsOnCharStore", "false")); + DESTROY_DROPPED_PLAYER_ITEM = Boolean.parseBoolean(General.getProperty("DestroyPlayerDroppedItem", "false")); + DESTROY_EQUIPABLE_PLAYER_ITEM = Boolean.parseBoolean(General.getProperty("DestroyEquipableItem", "false")); + SAVE_DROPPED_ITEM = Boolean.parseBoolean(General.getProperty("SaveDroppedItem", "false")); + EMPTY_DROPPED_ITEM_TABLE_AFTER_LOAD = Boolean.parseBoolean(General.getProperty("EmptyDroppedItemTableAfterLoad", "false")); + SAVE_DROPPED_ITEM_INTERVAL = Integer.parseInt(General.getProperty("SaveDroppedItemInterval", "60")) * 60000; + CLEAR_DROPPED_ITEM_TABLE = Boolean.parseBoolean(General.getProperty("ClearDroppedItemTable", "false")); + AUTODELETE_INVALID_QUEST_DATA = Boolean.parseBoolean(General.getProperty("AutoDeleteInvalidQuestData", "False")); + PRECISE_DROP_CALCULATION = Boolean.parseBoolean(General.getProperty("PreciseDropCalculation", "True")); + MULTIPLE_ITEM_DROP = Boolean.parseBoolean(General.getProperty("MultipleItemDrop", "True")); + FORCE_INVENTORY_UPDATE = Boolean.parseBoolean(General.getProperty("ForceInventoryUpdate", "False")); + LAZY_CACHE = Boolean.parseBoolean(General.getProperty("LazyCache", "True")); + CACHE_CHAR_NAMES = Boolean.parseBoolean(General.getProperty("CacheCharNames", "True")); + MIN_NPC_ANIMATION = Integer.parseInt(General.getProperty("MinNPCAnimation", "10")); + MAX_NPC_ANIMATION = Integer.parseInt(General.getProperty("MaxNPCAnimation", "20")); + MIN_MONSTER_ANIMATION = Integer.parseInt(General.getProperty("MinMonsterAnimation", "5")); + MAX_MONSTER_ANIMATION = Integer.parseInt(General.getProperty("MaxMonsterAnimation", "20")); + MOVE_BASED_KNOWNLIST = Boolean.parseBoolean(General.getProperty("MoveBasedKnownlist", "False")); + KNOWNLIST_UPDATE_INTERVAL = Long.parseLong(General.getProperty("KnownListUpdateInterval", "1250")); + GRIDS_ALWAYS_ON = Boolean.parseBoolean(General.getProperty("GridsAlwaysOn", "False")); + GRID_NEIGHBOR_TURNON_TIME = Integer.parseInt(General.getProperty("GridNeighborTurnOnTime", "1")); + GRID_NEIGHBOR_TURNOFF_TIME = Integer.parseInt(General.getProperty("GridNeighborTurnOffTime", "90")); + WORLD_X_MIN = Integer.parseInt(General.getProperty("WorldXMin", "10")); + WORLD_X_MAX = Integer.parseInt(General.getProperty("WorldXMax", "26")); + WORLD_Y_MIN = Integer.parseInt(General.getProperty("WorldYMin", "10")); + WORLD_Y_MAX = Integer.parseInt(General.getProperty("WorldYMax", "26")); + GEODATA = Integer.parseInt(General.getProperty("GeoData", "0")); + GEODATA_CELLFINDING = Boolean.parseBoolean(General.getProperty("CellPathFinding", "False")); + PATHFIND_BUFFERS = General.getProperty("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); + LOW_WEIGHT = Float.parseFloat(General.getProperty("LowWeight", "0.5")); + MEDIUM_WEIGHT = Float.parseFloat(General.getProperty("MediumWeight", "2")); + HIGH_WEIGHT = Float.parseFloat(General.getProperty("HighWeight", "3")); + ADVANCED_DIAGONAL_STRATEGY = Boolean.parseBoolean(General.getProperty("AdvancedDiagonalStrategy", "True")); + DIAGONAL_WEIGHT = Float.parseFloat(General.getProperty("DiagonalWeight", "0.707")); + MAX_POSTFILTER_PASSES = Integer.parseInt(General.getProperty("MaxPostfilterPasses", "3")); + DEBUG_PATH = Boolean.parseBoolean(General.getProperty("DebugPath", "False")); + FORCE_GEODATA = Boolean.parseBoolean(General.getProperty("ForceGeodata", "True")); + COORD_SYNCHRONIZE = Integer.parseInt(General.getProperty("CoordSynchronize", "-1")); + + String str = General.getProperty("EnableFallingDamage", "auto"); + ENABLE_FALLING_DAMAGE = "auto".equalsIgnoreCase(str) ? GEODATA > 0 : Boolean.parseBoolean(str); + + ZONE_TOWN = Integer.parseInt(General.getProperty("ZoneTown", "0")); + DEFAULT_GLOBAL_CHAT = General.getProperty("GlobalChat", "ON"); + DEFAULT_TRADE_CHAT = General.getProperty("TradeChat", "ON"); + ALLOW_WAREHOUSE = Boolean.parseBoolean(General.getProperty("AllowWarehouse", "True")); + WAREHOUSE_CACHE = Boolean.parseBoolean(General.getProperty("WarehouseCache", "False")); + WAREHOUSE_CACHE_TIME = Integer.parseInt(General.getProperty("WarehouseCacheTime", "15")); + ALLOW_REFUND = Boolean.parseBoolean(General.getProperty("AllowRefund", "True")); + ALLOW_MAIL = Boolean.parseBoolean(General.getProperty("AllowMail", "True")); + ALLOW_ATTACHMENTS = Boolean.parseBoolean(General.getProperty("AllowAttachments", "True")); + ALLOW_WEAR = Boolean.parseBoolean(General.getProperty("AllowWear", "True")); + WEAR_DELAY = Integer.parseInt(General.getProperty("WearDelay", "5")); + WEAR_PRICE = Integer.parseInt(General.getProperty("WearPrice", "10")); + ALLOW_LOTTERY = Boolean.parseBoolean(General.getProperty("AllowLottery", "True")); + ALLOW_RACE = Boolean.parseBoolean(General.getProperty("AllowRace", "True")); + ALLOW_WATER = Boolean.parseBoolean(General.getProperty("AllowWater", "True")); + ALLOW_RENTPET = Boolean.parseBoolean(General.getProperty("AllowRentPet", "False")); + ALLOWFISHING = Boolean.parseBoolean(General.getProperty("AllowFishing", "True")); + ALLOW_MANOR = Boolean.parseBoolean(General.getProperty("AllowManor", "True")); + ALLOW_BOAT = Boolean.parseBoolean(General.getProperty("AllowBoat", "True")); + BOAT_BROADCAST_RADIUS = Integer.parseInt(General.getProperty("BoatBroadcastRadius", "20000")); + ALLOW_CURSED_WEAPONS = Boolean.parseBoolean(General.getProperty("AllowCursedWeapons", "True")); + ALLOW_NPC_WALKERS = Boolean.parseBoolean(General.getProperty("AllowNpcWalkers", "true")); + ALLOW_PET_WALKERS = Boolean.parseBoolean(General.getProperty("AllowPetWalkers", "True")); + SERVER_NEWS = Boolean.parseBoolean(General.getProperty("ShowServerNews", "False")); + COMMUNITY_TYPE = Integer.parseInt(General.getProperty("CommunityType", "1")); + BBS_SHOW_PLAYERLIST = Boolean.parseBoolean(General.getProperty("BBSShowPlayerList", "false")); + BBS_DEFAULT = General.getProperty("BBSDefault", "_bbshome"); + SHOW_LEVEL_COMMUNITYBOARD = Boolean.parseBoolean(General.getProperty("ShowLevelOnCommunityBoard", "False")); + SHOW_STATUS_COMMUNITYBOARD = Boolean.parseBoolean(General.getProperty("ShowStatusOnCommunityBoard", "True")); + NAME_PAGE_SIZE_COMMUNITYBOARD = Integer.parseInt(General.getProperty("NamePageSizeOnCommunityBoard", "50")); + NAME_PER_ROW_COMMUNITYBOARD = Integer.parseInt(General.getProperty("NamePerRowOnCommunityBoard", "5")); + USE_SAY_FILTER = Boolean.parseBoolean(General.getProperty("UseChatFilter", "false")); + CHAT_FILTER_CHARS = General.getProperty("ChatFilterChars", "^_^"); + ALT_MANOR_REFRESH_TIME = Integer.parseInt(General.getProperty("AltManorRefreshTime", "20")); + ALT_MANOR_REFRESH_MIN = Integer.parseInt(General.getProperty("AltManorRefreshMin", "00")); + ALT_MANOR_APPROVE_TIME = Integer.parseInt(General.getProperty("AltManorApproveTime", "6")); + ALT_MANOR_APPROVE_MIN = Integer.parseInt(General.getProperty("AltManorApproveMin", "00")); + ALT_MANOR_MAINTENANCE_PERIOD = Integer.parseInt(General.getProperty("AltManorMaintenancePeriod", "360000")); + ALT_MANOR_SAVE_ALL_ACTIONS = Boolean.parseBoolean(General.getProperty("AltManorSaveAllActions", "false")); + ALT_MANOR_SAVE_PERIOD_RATE = Integer.parseInt(General.getProperty("AltManorSavePeriodRate", "2")); + ALT_LOTTERY_PRIZE = Long.parseLong(General.getProperty("AltLotteryPrize", "50000")); + ALT_LOTTERY_TICKET_PRICE = Long.parseLong(General.getProperty("AltLotteryTicketPrice", "2000")); + ALT_LOTTERY_5_NUMBER_RATE = Float.parseFloat(General.getProperty("AltLottery5NumberRate", "0.6")); + ALT_LOTTERY_4_NUMBER_RATE = Float.parseFloat(General.getProperty("AltLottery4NumberRate", "0.2")); + ALT_LOTTERY_3_NUMBER_RATE = Float.parseFloat(General.getProperty("AltLottery3NumberRate", "0.2")); + ALT_LOTTERY_2_AND_1_NUMBER_PRIZE = Long.parseLong(General.getProperty("AltLottery2and1NumberPrize", "200")); + ALT_ITEM_AUCTION_ENABLED = Boolean.valueOf(General.getProperty("AltItemAuctionEnabled", "False")); + ALT_ITEM_AUCTION_EXPIRED_AFTER = Integer.valueOf(General.getProperty("AltItemAuctionExpiredAfter", "14")); + ALT_ITEM_AUCTION_TIME_EXTENDS_ON_BID = 1000 * (long) Integer.valueOf(General.getProperty("AltItemAuctionTimeExtendsOnBid", "0")); + FS_TIME_ATTACK = Integer.parseInt(General.getProperty("TimeOfAttack", "50")); + FS_TIME_COOLDOWN = Integer.parseInt(General.getProperty("TimeOfCoolDown", "5")); + FS_TIME_ENTRY = Integer.parseInt(General.getProperty("TimeOfEntry", "3")); + FS_TIME_WARMUP = Integer.parseInt(General.getProperty("TimeOfWarmUp", "2")); + FS_PARTY_MEMBER_COUNT = Integer.parseInt(General.getProperty("NumberOfNecessaryPartyMembers", "4")); + if (FS_TIME_ATTACK <= 0) + { + FS_TIME_ATTACK = 50; + } + if (FS_TIME_COOLDOWN <= 0) + { + FS_TIME_COOLDOWN = 5; + } + if (FS_TIME_ENTRY <= 0) + { + FS_TIME_ENTRY = 3; + } + if (FS_TIME_ENTRY <= 0) + { + FS_TIME_ENTRY = 3; + } + if (FS_TIME_ENTRY <= 0) + { + FS_TIME_ENTRY = 3; + } + RIFT_MIN_PARTY_SIZE = Integer.parseInt(General.getProperty("RiftMinPartySize", "5")); + RIFT_MAX_JUMPS = Integer.parseInt(General.getProperty("MaxRiftJumps", "4")); + RIFT_SPAWN_DELAY = Integer.parseInt(General.getProperty("RiftSpawnDelay", "10000")); + RIFT_AUTO_JUMPS_TIME_MIN = Integer.parseInt(General.getProperty("AutoJumpsDelayMin", "480")); + RIFT_AUTO_JUMPS_TIME_MAX = Integer.parseInt(General.getProperty("AutoJumpsDelayMax", "600")); + RIFT_BOSS_ROOM_TIME_MUTIPLY = Float.parseFloat(General.getProperty("BossRoomTimeMultiply", "1.5")); + RIFT_ENTER_COST_RECRUIT = Integer.parseInt(General.getProperty("RecruitCost", "18")); + RIFT_ENTER_COST_SOLDIER = Integer.parseInt(General.getProperty("SoldierCost", "21")); + RIFT_ENTER_COST_OFFICER = Integer.parseInt(General.getProperty("OfficerCost", "24")); + RIFT_ENTER_COST_CAPTAIN = Integer.parseInt(General.getProperty("CaptainCost", "27")); + RIFT_ENTER_COST_COMMANDER = Integer.parseInt(General.getProperty("CommanderCost", "30")); + RIFT_ENTER_COST_HERO = Integer.parseInt(General.getProperty("HeroCost", "33")); + DEFAULT_PUNISH = Integer.parseInt(General.getProperty("DefaultPunish", "2")); + DEFAULT_PUNISH_PARAM = Integer.parseInt(General.getProperty("DefaultPunishParam", "0")); + ONLY_GM_ITEMS_FREE = Boolean.parseBoolean(General.getProperty("OnlyGMItemsFree", "True")); + JAIL_IS_PVP = Boolean.parseBoolean(General.getProperty("JailIsPvp", "False")); + JAIL_DISABLE_CHAT = Boolean.parseBoolean(General.getProperty("JailDisableChat", "True")); + JAIL_DISABLE_TRANSACTION = Boolean.parseBoolean(General.getProperty("JailDisableTransaction", "False")); + CUSTOM_SPAWNLIST_TABLE = Boolean.valueOf(General.getProperty("CustomSpawnlistTable", "false")); + SAVE_GMSPAWN_ON_CUSTOM = Boolean.valueOf(General.getProperty("SaveGmSpawnOnCustom", "false")); + CUSTOM_NPC_TABLE = Boolean.valueOf(General.getProperty("CustomNpcTable", "false")); + CUSTOM_NPC_SKILLS_TABLE = Boolean.valueOf(General.getProperty("CustomNpcSkillsTable", "false")); + CUSTOM_ARMORSETS_TABLE = Boolean.valueOf(General.getProperty("CustomArmorSetsTable", "false")); + CUSTOM_TELEPORT_TABLE = Boolean.valueOf(General.getProperty("CustomTeleportTable", "false")); + CUSTOM_DROPLIST_TABLE = Boolean.valueOf(General.getProperty("CustomDroplistTable", "false")); + CUSTOM_MERCHANT_TABLES = Boolean.valueOf(General.getProperty("CustomMerchantTables", "false")); + CUSTOM_NPCBUFFER_TABLES = Boolean.valueOf(General.getProperty("CustomNpcBufferTables", "false")); + ENABLE_BLOCK_CHECKER_EVENT = Boolean.valueOf(General.getProperty("EnableBlockCheckerEvent", "false")); + MIN_BLOCK_CHECKER_TEAM_MEMBERS = Integer.valueOf(General.getProperty("BlockCheckerMinTeamMembers", "2")); + if (MIN_BLOCK_CHECKER_TEAM_MEMBERS < 1) + { + MIN_BLOCK_CHECKER_TEAM_MEMBERS = 1; + } + else if (MIN_BLOCK_CHECKER_TEAM_MEMBERS > 6) + { + MIN_BLOCK_CHECKER_TEAM_MEMBERS = 6; + } + HBCE_FAIR_PLAY = Boolean.parseBoolean(General.getProperty("HBCEFairPlay", "false")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + GENERAL_CONFIG_FILE + " File."); + } + + // Load FloodProtector L2Properties file + try + { + L2Properties security = new L2Properties(); + is = new FileInputStream(new File(FLOOD_PROTECTOR_FILE)); + security.load(is); + + loadFloodProtectorConfigs(security); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + FLOOD_PROTECTOR_FILE); + } + + // Load NPC L2Properties file (if exists) + try + { + L2Properties NPC = new L2Properties(); + is = new FileInputStream(new File(NPC_CONFIG_FILE)); + NPC.load(is); + + ANNOUNCE_MAMMON_SPAWN = Boolean.parseBoolean(NPC.getProperty("AnnounceMammonSpawn", "False")); + ALT_MOB_AGRO_IN_PEACEZONE = Boolean.parseBoolean(NPC.getProperty("AltMobAgroInPeaceZone", "True")); + ALT_ATTACKABLE_NPCS = Boolean.parseBoolean(NPC.getProperty("AltAttackableNpcs", "True")); + ALT_GAME_VIEWNPC = Boolean.parseBoolean(NPC.getProperty("AltGameViewNpc", "False")); + MAX_DRIFT_RANGE = Integer.parseInt(NPC.getProperty("MaxDriftRange", "300")); + DEEPBLUE_DROP_RULES = Boolean.parseBoolean(NPC.getProperty("UseDeepBlueDropRules", "True")); + DEEPBLUE_DROP_RULES_RAID = Boolean.parseBoolean(NPC.getProperty("UseDeepBlueDropRulesRaid", "True")); + SHOW_NPC_LVL = Boolean.parseBoolean(NPC.getProperty("ShowNpcLevel", "False")); + SHOW_CREST_WITHOUT_QUEST = Boolean.parseBoolean(NPC.getProperty("ShowCrestWithoutQuest", "False")); + ENABLE_RANDOM_ENCHANT_EFFECT = Boolean.parseBoolean(NPC.getProperty("EnableRandomEnchantEffect", "False")); + MIN_NPC_LVL_DMG_PENALTY = Integer.parseInt(NPC.getProperty("MinNPCLevelForDmgPenalty", "78")); + NPC_DMG_PENALTY = parseConfigLine(NPC.getProperty("DmgPenaltyForLvLDifferences", "0.7, 0.6, 0.6, 0.55")); + NPC_CRIT_DMG_PENALTY = parseConfigLine(NPC.getProperty("CritDmgPenaltyForLvLDifferences", "0.75, 0.65, 0.6, 0.58")); + NPC_SKILL_DMG_PENALTY = parseConfigLine(NPC.getProperty("SkillDmgPenaltyForLvLDifferences", "0.8, 0.7, 0.65, 0.62")); + MIN_NPC_LVL_MAGIC_PENALTY = Integer.parseInt(NPC.getProperty("MinNPCLevelForMagicPenalty", "78")); + NPC_SKILL_CHANCE_PENALTY = parseConfigLine(NPC.getProperty("SkillChancePenaltyForLvLDifferences", "2.5, 3.0, 3.25, 3.5")); + ENABLE_DROP_VITALITY_HERBS = Boolean.parseBoolean(NPC.getProperty("EnableVitalityHerbs", "True")); + GUARD_ATTACK_AGGRO_MOB = Boolean.parseBoolean(NPC.getProperty("GuardAttackAggroMob", "False")); + ALLOW_WYVERN_UPGRADER = Boolean.parseBoolean(NPC.getProperty("AllowWyvernUpgrader", "False")); + String[] split = NPC.getProperty("ListPetRentNpc", "30827").split(","); + LIST_PET_RENT_NPC = new TIntArrayList(split.length); + for (String id : split) + { + LIST_PET_RENT_NPC.add(Integer.parseInt(id)); + } + RAID_HP_REGEN_MULTIPLIER = Double.parseDouble(NPC.getProperty("RaidHpRegenMultiplier", "100")) / 100; + RAID_MP_REGEN_MULTIPLIER = Double.parseDouble(NPC.getProperty("RaidMpRegenMultiplier", "100")) / 100; + RAID_PDEFENCE_MULTIPLIER = Double.parseDouble(NPC.getProperty("RaidPDefenceMultiplier", "100")) / 100; + RAID_MDEFENCE_MULTIPLIER = Double.parseDouble(NPC.getProperty("RaidMDefenceMultiplier", "100")) / 100; + RAID_PATTACK_MULTIPLIER = Double.parseDouble(NPC.getProperty("RaidPAttackMultiplier", "100")) / 100; + RAID_MATTACK_MULTIPLIER = Double.parseDouble(NPC.getProperty("RaidMAttackMultiplier", "100")) / 100; + RAID_MIN_RESPAWN_MULTIPLIER = Float.parseFloat(NPC.getProperty("RaidMinRespawnMultiplier", "1.0")); + RAID_MAX_RESPAWN_MULTIPLIER = Float.parseFloat(NPC.getProperty("RaidMaxRespawnMultiplier", "1.0")); + RAID_MINION_RESPAWN_TIMER = Integer.parseInt(NPC.getProperty("RaidMinionRespawnTime", "300000")); + RAID_DISABLE_CURSE = Boolean.parseBoolean(NPC.getProperty("DisableRaidCurse", "False")); + RAID_CHAOS_TIME = Integer.parseInt(NPC.getProperty("RaidChaosTime", "10")); + GRAND_CHAOS_TIME = Integer.parseInt(NPC.getProperty("GrandChaosTime", "10")); + MINION_CHAOS_TIME = Integer.parseInt(NPC.getProperty("MinionChaosTime", "10")); + INVENTORY_MAXIMUM_PET = Integer.parseInt(NPC.getProperty("MaximumSlotsForPet", "12")); + PET_HP_REGEN_MULTIPLIER = Double.parseDouble(NPC.getProperty("PetHpRegenMultiplier", "100")) / 100; + PET_MP_REGEN_MULTIPLIER = Double.parseDouble(NPC.getProperty("PetMpRegenMultiplier", "100")) / 100; + split = NPC.getProperty("NonTalkingNpcs", "18684,18685,18686,18687,18688,18689,18690,19691,18692,31557,31606,31671,31672,31673,31674,32026,32030,32031,32032,32306,32619,32620,32621").split(","); + NON_TALKING_NPCS = new TIntArrayList(split.length); + for (String npcId : split) + { + try + { + NON_TALKING_NPCS.add(Integer.parseInt(npcId)); + } + catch (NumberFormatException nfe) + { + if (!npcId.isEmpty()) + { + _log.warning("Could not parse " + npcId + " id for NonTalkingNpcs. Please check that all values are digits and coma separated."); + } + } + } + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + NPC_CONFIG_FILE + " File."); + } + + // Load Rates L2Properties file (if exists) + try + { + L2Properties ratesSettings = new L2Properties(); + is = new FileInputStream(new File(RATES_CONFIG_FILE)); + ratesSettings.load(is); + + RATE_XP = Float.parseFloat(ratesSettings.getProperty("RateXp", "1.")); + RATE_SP = Float.parseFloat(ratesSettings.getProperty("RateSp", "1.")); + RATE_PARTY_XP = Float.parseFloat(ratesSettings.getProperty("RatePartyXp", "1.")); + RATE_PARTY_SP = Float.parseFloat(ratesSettings.getProperty("RatePartySp", "1.")); + RATE_CONSUMABLE_COST = Float.parseFloat(ratesSettings.getProperty("RateConsumableCost", "1.")); + RATE_EXTR_FISH = Float.parseFloat(ratesSettings.getProperty("RateExtractFish", "1.")); + RATE_DROP_ITEMS = Float.parseFloat(ratesSettings.getProperty("RateDropItems", "1.")); + RATE_DROP_ITEMS_BY_RAID = Float.parseFloat(ratesSettings.getProperty("RateRaidDropItems", "1.")); + RATE_DROP_SPOIL = Float.parseFloat(ratesSettings.getProperty("RateDropSpoil", "1.")); + RATE_DROP_MANOR = Integer.parseInt(ratesSettings.getProperty("RateDropManor", "1")); + RATE_QUEST_DROP = Float.parseFloat(ratesSettings.getProperty("RateQuestDrop", "1.")); + RATE_QUEST_REWARD = Float.parseFloat(ratesSettings.getProperty("RateQuestReward", "1.")); + RATE_QUEST_REWARD_XP = Float.parseFloat(ratesSettings.getProperty("RateQuestRewardXP", "1.")); + RATE_QUEST_REWARD_SP = Float.parseFloat(ratesSettings.getProperty("RateQuestRewardSP", "1.")); + RATE_QUEST_REWARD_ADENA = Float.parseFloat(ratesSettings.getProperty("RateQuestRewardAdena", "1.")); + RATE_QUEST_REWARD_USE_MULTIPLIERS = Boolean.parseBoolean(ratesSettings.getProperty("UseQuestRewardMultipliers", "False")); + RATE_QUEST_REWARD_POTION = Float.parseFloat(ratesSettings.getProperty("RateQuestRewardPotion", "1.")); + RATE_QUEST_REWARD_SCROLL = Float.parseFloat(ratesSettings.getProperty("RateQuestRewardScroll", "1.")); + RATE_QUEST_REWARD_RECIPE = Float.parseFloat(ratesSettings.getProperty("RateQuestRewardRecipe", "1.")); + RATE_QUEST_REWARD_MATERIAL = Float.parseFloat(ratesSettings.getProperty("RateQuestRewardMaterial", "1.")); + + RATE_VITALITY_LEVEL_1 = Float.parseFloat(ratesSettings.getProperty("RateVitalityLevel1", "1.5")); + RATE_VITALITY_LEVEL_2 = Float.parseFloat(ratesSettings.getProperty("RateVitalityLevel2", "2.")); + RATE_VITALITY_LEVEL_3 = Float.parseFloat(ratesSettings.getProperty("RateVitalityLevel3", "2.5")); + RATE_VITALITY_LEVEL_4 = Float.parseFloat(ratesSettings.getProperty("RateVitalityLevel4", "3.")); + RATE_RECOVERY_VITALITY_PEACE_ZONE = Float.parseFloat(ratesSettings.getProperty("RateRecoveryPeaceZone", "1.")); + RATE_VITALITY_LOST = Float.parseFloat(ratesSettings.getProperty("RateVitalityLost", "1.")); + RATE_VITALITY_GAIN = Float.parseFloat(ratesSettings.getProperty("RateVitalityGain", "1.")); + RATE_RECOVERY_ON_RECONNECT = Float.parseFloat(ratesSettings.getProperty("RateRecoveryOnReconnect", "4.")); + RATE_KARMA_EXP_LOST = Float.parseFloat(ratesSettings.getProperty("RateKarmaExpLost", "1.")); + RATE_SIEGE_GUARDS_PRICE = Float.parseFloat(ratesSettings.getProperty("RateSiegeGuardsPrice", "1.")); + RATE_DROP_COMMON_HERBS = Float.parseFloat(ratesSettings.getProperty("RateCommonHerbs", "1.")); + RATE_DROP_HP_HERBS = Float.parseFloat(ratesSettings.getProperty("RateHpHerbs", "1.")); + RATE_DROP_MP_HERBS = Float.parseFloat(ratesSettings.getProperty("RateMpHerbs", "1.")); + RATE_DROP_SPECIAL_HERBS = Float.parseFloat(ratesSettings.getProperty("RateSpecialHerbs", "1.")); + RATE_DROP_VITALITY_HERBS = Float.parseFloat(ratesSettings.getProperty("RateVitalityHerbs", "1.")); + PLAYER_DROP_LIMIT = Integer.parseInt(ratesSettings.getProperty("PlayerDropLimit", "3")); + PLAYER_RATE_DROP = Integer.parseInt(ratesSettings.getProperty("PlayerRateDrop", "5")); + PLAYER_RATE_DROP_ITEM = Integer.parseInt(ratesSettings.getProperty("PlayerRateDropItem", "70")); + PLAYER_RATE_DROP_EQUIP = Integer.parseInt(ratesSettings.getProperty("PlayerRateDropEquip", "25")); + PLAYER_RATE_DROP_EQUIP_WEAPON = Integer.parseInt(ratesSettings.getProperty("PlayerRateDropEquipWeapon", "5")); + PET_XP_RATE = Float.parseFloat(ratesSettings.getProperty("PetXpRate", "1.")); + PET_FOOD_RATE = Integer.parseInt(ratesSettings.getProperty("PetFoodRate", "1")); + SINEATER_XP_RATE = Float.parseFloat(ratesSettings.getProperty("SinEaterXpRate", "1.")); + KARMA_DROP_LIMIT = Integer.parseInt(ratesSettings.getProperty("KarmaDropLimit", "10")); + KARMA_RATE_DROP = Integer.parseInt(ratesSettings.getProperty("KarmaRateDrop", "70")); + KARMA_RATE_DROP_ITEM = Integer.parseInt(ratesSettings.getProperty("KarmaRateDropItem", "50")); + KARMA_RATE_DROP_EQUIP = Integer.parseInt(ratesSettings.getProperty("KarmaRateDropEquip", "40")); + KARMA_RATE_DROP_EQUIP_WEAPON = Integer.parseInt(ratesSettings.getProperty("KarmaRateDropEquipWeapon", "10")); + + // Initializing table + PLAYER_XP_PERCENT_LOST = new double[Byte.MAX_VALUE + 1]; + + // Default value + for (int i = 0; i <= Byte.MAX_VALUE; i++) + { + PLAYER_XP_PERCENT_LOST[i] = 1.; + } + + // Now loading into table parsed values + try + { + String[] values = ratesSettings.getProperty("PlayerXPPercentLost", "0,39-7.0;40,75-4.0;76,76-2.5;77,77-2.0;78,78-1.5").split(";"); + + for (String s : values) + { + int min; + int max; + double val; + + String[] vals = s.split("-"); + String[] mM = vals[0].split(","); + + min = Integer.parseInt(mM[0]); + max = Integer.parseInt(mM[1]); + val = Double.parseDouble(vals[1]); + + for (int i = min; i <= max; i++) + { + PLAYER_XP_PERCENT_LOST[i] = val; + } + } + } + catch (Exception e) + { + _log.warning("Error while loading Player XP percent lost"); + e.printStackTrace(); + } + + String[] propertySplit = ratesSettings.getProperty("RateDropItemsById", "").split(";"); + RATE_DROP_ITEMS_ID = new TIntFloatHashMap(propertySplit.length); + if (!propertySplit[0].isEmpty()) + { + for (String item : propertySplit) + { + String[] itemSplit = item.split(","); + if (itemSplit.length != 2) + { + _log.warning(StringUtil.concat("Config.load(): invalid config property -> RateDropItemsById \"", item, "\"")); + } + else + { + try + { + RATE_DROP_ITEMS_ID.put(Integer.parseInt(itemSplit[0]), Float.parseFloat(itemSplit[1])); + } + catch (NumberFormatException nfe) + { + if (!item.isEmpty()) + { + _log.warning(StringUtil.concat("Config.load(): invalid config property -> RateDropItemsById \"", item, "\"")); + } + } + } + } + } + if (RATE_DROP_ITEMS_ID.get(57) == 0f) + { + RATE_DROP_ITEMS_ID.put(57, RATE_DROP_ITEMS); // for Adena rate if not defined + } + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + RATES_CONFIG_FILE + " File."); + } + + // Load L2JMod L2Properties file (if exists) + try + { + L2Properties L2JModSettings = new L2Properties(); + is = new FileInputStream(new File(L2JMOD_CONFIG_FILE)); + L2JModSettings.load(is); + + L2JMOD_CHAMPION_ENABLE = Boolean.parseBoolean(L2JModSettings.getProperty("ChampionEnable", "false")); + L2JMOD_CHAMPION_PASSIVE = Boolean.parseBoolean(L2JModSettings.getProperty("ChampionPassive", "false")); + L2JMOD_CHAMPION_FREQUENCY = Integer.parseInt(L2JModSettings.getProperty("ChampionFrequency", "0")); + L2JMOD_CHAMP_TITLE = L2JModSettings.getProperty("ChampionTitle", "Champion"); + L2JMOD_CHAMP_MIN_LVL = Integer.parseInt(L2JModSettings.getProperty("ChampionMinLevel", "20")); + L2JMOD_CHAMP_MAX_LVL = Integer.parseInt(L2JModSettings.getProperty("ChampionMaxLevel", "60")); + L2JMOD_CHAMPION_HP = Integer.parseInt(L2JModSettings.getProperty("ChampionHp", "7")); + L2JMOD_CHAMPION_HP_REGEN = Float.parseFloat(L2JModSettings.getProperty("ChampionHpRegen", "1.")); + L2JMOD_CHAMPION_REWARDS = Integer.parseInt(L2JModSettings.getProperty("ChampionRewards", "8")); + L2JMOD_CHAMPION_ADENAS_REWARDS = Float.parseFloat(L2JModSettings.getProperty("ChampionAdenasRewards", "1")); + L2JMOD_CHAMPION_ATK = Float.parseFloat(L2JModSettings.getProperty("ChampionAtk", "1.")); + L2JMOD_CHAMPION_SPD_ATK = Float.parseFloat(L2JModSettings.getProperty("ChampionSpdAtk", "1.")); + L2JMOD_CHAMPION_REWARD_LOWER_LVL_ITEM_CHANCE = Integer.parseInt(L2JModSettings.getProperty("ChampionRewardLowerLvlItemChance", "0")); + L2JMOD_CHAMPION_REWARD_HIGHER_LVL_ITEM_CHANCE = Integer.parseInt(L2JModSettings.getProperty("ChampionRewardHigherLvlItemChance", "0")); + L2JMOD_CHAMPION_REWARD_ID = Integer.parseInt(L2JModSettings.getProperty("ChampionRewardItemID", "6393")); + L2JMOD_CHAMPION_REWARD_QTY = Integer.parseInt(L2JModSettings.getProperty("ChampionRewardItemQty", "1")); + L2JMOD_CHAMPION_ENABLE_VITALITY = Boolean.parseBoolean(L2JModSettings.getProperty("ChampionEnableVitality", "False")); + L2JMOD_CHAMPION_ENABLE_IN_INSTANCES = Boolean.parseBoolean(L2JModSettings.getProperty("ChampionEnableInInstances", "False")); + + TVT_EVENT_ENABLED = Boolean.parseBoolean(L2JModSettings.getProperty("TvTEventEnabled", "false")); + TVT_EVENT_IN_INSTANCE = Boolean.parseBoolean(L2JModSettings.getProperty("TvTEventInInstance", "false")); + TVT_EVENT_INSTANCE_FILE = L2JModSettings.getProperty("TvTEventInstanceFile", "coliseum.xml"); + TVT_EVENT_INTERVAL = L2JModSettings.getProperty("TvTEventInterval", "20:00").split(","); + TVT_EVENT_PARTICIPATION_TIME = Integer.parseInt(L2JModSettings.getProperty("TvTEventParticipationTime", "3600")); + TVT_EVENT_RUNNING_TIME = Integer.parseInt(L2JModSettings.getProperty("TvTEventRunningTime", "1800")); + TVT_EVENT_PARTICIPATION_NPC_ID = Integer.parseInt(L2JModSettings.getProperty("TvTEventParticipationNpcId", "0")); + + L2JMOD_ALLOW_WEDDING = Boolean.parseBoolean(L2JModSettings.getProperty("AllowWedding", "False")); + L2JMOD_WEDDING_PRICE = Integer.parseInt(L2JModSettings.getProperty("WeddingPrice", "250000000")); + L2JMOD_WEDDING_PUNISH_INFIDELITY = Boolean.parseBoolean(L2JModSettings.getProperty("WeddingPunishInfidelity", "True")); + L2JMOD_WEDDING_TELEPORT = Boolean.parseBoolean(L2JModSettings.getProperty("WeddingTeleport", "True")); + L2JMOD_WEDDING_TELEPORT_PRICE = Integer.parseInt(L2JModSettings.getProperty("WeddingTeleportPrice", "50000")); + L2JMOD_WEDDING_TELEPORT_DURATION = Integer.parseInt(L2JModSettings.getProperty("WeddingTeleportDuration", "60")); + L2JMOD_WEDDING_SAMESEX = Boolean.parseBoolean(L2JModSettings.getProperty("WeddingAllowSameSex", "False")); + L2JMOD_WEDDING_FORMALWEAR = Boolean.parseBoolean(L2JModSettings.getProperty("WeddingFormalWear", "True")); + L2JMOD_WEDDING_DIVORCE_COSTS = Integer.parseInt(L2JModSettings.getProperty("WeddingDivorceCosts", "20")); + + L2JMOD_ENABLE_WAREHOUSESORTING_CLAN = Boolean.valueOf(L2JModSettings.getProperty("EnableWarehouseSortingClan", "False")); + L2JMOD_ENABLE_WAREHOUSESORTING_PRIVATE = Boolean.valueOf(L2JModSettings.getProperty("EnableWarehouseSortingPrivate", "False")); + + if (TVT_EVENT_PARTICIPATION_NPC_ID == 0) + { + TVT_EVENT_ENABLED = false; + _log.warning("TvTEventEngine[Config.load()]: invalid config property -> TvTEventParticipationNpcId"); + } + else + { + String[] propertySplit = L2JModSettings.getProperty("TvTEventParticipationNpcCoordinates", "0,0,0").split(","); + if (propertySplit.length < 3) + { + TVT_EVENT_ENABLED = false; + _log.warning("TvTEventEngine[Config.load()]: invalid config property -> TvTEventParticipationNpcCoordinates"); + } + else + { + TVT_EVENT_REWARDS = new ArrayList<>(); + TVT_DOORS_IDS_TO_OPEN = new ArrayList<>(); + TVT_DOORS_IDS_TO_CLOSE = new ArrayList<>(); + TVT_EVENT_PARTICIPATION_NPC_COORDINATES = new int[4]; + TVT_EVENT_TEAM_1_COORDINATES = new int[3]; + TVT_EVENT_TEAM_2_COORDINATES = new int[3]; + TVT_EVENT_PARTICIPATION_NPC_COORDINATES[0] = Integer.parseInt(propertySplit[0]); + TVT_EVENT_PARTICIPATION_NPC_COORDINATES[1] = Integer.parseInt(propertySplit[1]); + TVT_EVENT_PARTICIPATION_NPC_COORDINATES[2] = Integer.parseInt(propertySplit[2]); + if (propertySplit.length == 4) + { + TVT_EVENT_PARTICIPATION_NPC_COORDINATES[3] = Integer.parseInt(propertySplit[3]); + } + TVT_EVENT_MIN_PLAYERS_IN_TEAMS = Integer.parseInt(L2JModSettings.getProperty("TvTEventMinPlayersInTeams", "1")); + TVT_EVENT_MAX_PLAYERS_IN_TEAMS = Integer.parseInt(L2JModSettings.getProperty("TvTEventMaxPlayersInTeams", "20")); + TVT_EVENT_MIN_LVL = (byte) Integer.parseInt(L2JModSettings.getProperty("TvTEventMinPlayerLevel", "1")); + TVT_EVENT_MAX_LVL = (byte) Integer.parseInt(L2JModSettings.getProperty("TvTEventMaxPlayerLevel", "80")); + TVT_EVENT_RESPAWN_TELEPORT_DELAY = Integer.parseInt(L2JModSettings.getProperty("TvTEventRespawnTeleportDelay", "20")); + TVT_EVENT_START_LEAVE_TELEPORT_DELAY = Integer.parseInt(L2JModSettings.getProperty("TvTEventStartLeaveTeleportDelay", "20")); + TVT_EVENT_EFFECTS_REMOVAL = Integer.parseInt(L2JModSettings.getProperty("TvTEventEffectsRemoval", "0")); + TVT_EVENT_MAX_PARTICIPANTS_PER_IP = Integer.parseInt(L2JModSettings.getProperty("TvTEventMaxParticipantsPerIP", "0")); + TVT_ALLOW_VOICED_COMMAND = Boolean.parseBoolean(L2JModSettings.getProperty("TvTAllowVoicedInfoCommand", "false")); + TVT_EVENT_TEAM_1_NAME = L2JModSettings.getProperty("TvTEventTeam1Name", "Team1"); + propertySplit = L2JModSettings.getProperty("TvTEventTeam1Coordinates", "0,0,0").split(","); + if (propertySplit.length < 3) + { + TVT_EVENT_ENABLED = false; + _log.warning("TvTEventEngine[Config.load()]: invalid config property -> TvTEventTeam1Coordinates"); + } + else + { + TVT_EVENT_TEAM_1_COORDINATES[0] = Integer.parseInt(propertySplit[0]); + TVT_EVENT_TEAM_1_COORDINATES[1] = Integer.parseInt(propertySplit[1]); + TVT_EVENT_TEAM_1_COORDINATES[2] = Integer.parseInt(propertySplit[2]); + TVT_EVENT_TEAM_2_NAME = L2JModSettings.getProperty("TvTEventTeam2Name", "Team2"); + propertySplit = L2JModSettings.getProperty("TvTEventTeam2Coordinates", "0,0,0").split(","); + if (propertySplit.length < 3) + { + TVT_EVENT_ENABLED = false; + _log.warning("TvTEventEngine[Config.load()]: invalid config property -> TvTEventTeam2Coordinates"); + } + else + { + TVT_EVENT_TEAM_2_COORDINATES[0] = Integer.parseInt(propertySplit[0]); + TVT_EVENT_TEAM_2_COORDINATES[1] = Integer.parseInt(propertySplit[1]); + TVT_EVENT_TEAM_2_COORDINATES[2] = Integer.parseInt(propertySplit[2]); + propertySplit = L2JModSettings.getProperty("TvTEventParticipationFee", "0,0").split(","); + try + { + TVT_EVENT_PARTICIPATION_FEE[0] = Integer.parseInt(propertySplit[0]); + TVT_EVENT_PARTICIPATION_FEE[1] = Integer.parseInt(propertySplit[1]); + } + catch (NumberFormatException nfe) + { + if (propertySplit.length > 0) + { + _log.warning("TvTEventEngine[Config.load()]: invalid config property -> TvTEventParticipationFee"); + } + } + propertySplit = L2JModSettings.getProperty("TvTEventReward", "57,100000").split(";"); + for (String reward : propertySplit) + { + String[] rewardSplit = reward.split(","); + if (rewardSplit.length != 2) + { + _log.warning(StringUtil.concat("TvTEventEngine[Config.load()]: invalid config property -> TvTEventReward \"", reward, "\"")); + } + else + { + try + { + TVT_EVENT_REWARDS.add(new int[] + { + Integer.parseInt(rewardSplit[0]), + Integer.parseInt(rewardSplit[1]) + }); + } + catch (NumberFormatException nfe) + { + if (!reward.isEmpty()) + { + _log.warning(StringUtil.concat("TvTEventEngine[Config.load()]: invalid config property -> TvTEventReward \"", reward, "\"")); + } + } + } + } + + TVT_EVENT_TARGET_TEAM_MEMBERS_ALLOWED = Boolean.parseBoolean(L2JModSettings.getProperty("TvTEventTargetTeamMembersAllowed", "true")); + TVT_EVENT_SCROLL_ALLOWED = Boolean.parseBoolean(L2JModSettings.getProperty("TvTEventScrollsAllowed", "false")); + TVT_EVENT_POTIONS_ALLOWED = Boolean.parseBoolean(L2JModSettings.getProperty("TvTEventPotionsAllowed", "false")); + TVT_EVENT_SUMMON_BY_ITEM_ALLOWED = Boolean.parseBoolean(L2JModSettings.getProperty("TvTEventSummonByItemAllowed", "false")); + TVT_REWARD_TEAM_TIE = Boolean.parseBoolean(L2JModSettings.getProperty("TvTRewardTeamTie", "false")); + propertySplit = L2JModSettings.getProperty("TvTDoorsToOpen", "").split(";"); + for (String door : propertySplit) + { + try + { + TVT_DOORS_IDS_TO_OPEN.add(Integer.parseInt(door)); + } + catch (NumberFormatException nfe) + { + if (!door.isEmpty()) + { + _log.warning(StringUtil.concat("TvTEventEngine[Config.load()]: invalid config property -> TvTDoorsToOpen \"", door, "\"")); + } + } + } + + propertySplit = L2JModSettings.getProperty("TvTDoorsToClose", "").split(";"); + for (String door : propertySplit) + { + try + { + TVT_DOORS_IDS_TO_CLOSE.add(Integer.parseInt(door)); + } + catch (NumberFormatException nfe) + { + if (!door.isEmpty()) + { + _log.warning(StringUtil.concat("TvTEventEngine[Config.load()]: invalid config property -> TvTDoorsToClose \"", door, "\"")); + } + } + } + + propertySplit = L2JModSettings.getProperty("TvTEventFighterBuffs", "").split(";"); + if (!propertySplit[0].isEmpty()) + { + TVT_EVENT_FIGHTER_BUFFS = new TIntIntHashMap(propertySplit.length); + for (String skill : propertySplit) + { + String[] skillSplit = skill.split(","); + if (skillSplit.length != 2) + { + _log.warning(StringUtil.concat("TvTEventEngine[Config.load()]: invalid config property -> TvTEventFighterBuffs \"", skill, "\"")); + } + else + { + try + { + TVT_EVENT_FIGHTER_BUFFS.put(Integer.parseInt(skillSplit[0]), Integer.parseInt(skillSplit[1])); + } + catch (NumberFormatException nfe) + { + if (!skill.isEmpty()) + { + _log.warning(StringUtil.concat("TvTEventEngine[Config.load()]: invalid config property -> TvTEventFighterBuffs \"", skill, "\"")); + } + } + } + } + } + + propertySplit = L2JModSettings.getProperty("TvTEventMageBuffs", "").split(";"); + if (!propertySplit[0].isEmpty()) + { + TVT_EVENT_MAGE_BUFFS = new TIntIntHashMap(propertySplit.length); + for (String skill : propertySplit) + { + String[] skillSplit = skill.split(","); + if (skillSplit.length != 2) + { + _log.warning(StringUtil.concat("TvTEventEngine[Config.load()]: invalid config property -> TvTEventMageBuffs \"", skill, "\"")); + } + else + { + try + { + TVT_EVENT_MAGE_BUFFS.put(Integer.parseInt(skillSplit[0]), Integer.parseInt(skillSplit[1])); + } + catch (NumberFormatException nfe) + { + if (!skill.isEmpty()) + { + _log.warning(StringUtil.concat("TvTEventEngine[Config.load()]: invalid config property -> TvTEventMageBuffs \"", skill, "\"")); + } + } + } + } + } + } + } + } + } + + BANKING_SYSTEM_ENABLED = Boolean.parseBoolean(L2JModSettings.getProperty("BankingEnabled", "false")); + BANKING_SYSTEM_GOLDBARS = Integer.parseInt(L2JModSettings.getProperty("BankingGoldbarCount", "1")); + BANKING_SYSTEM_ADENA = Integer.parseInt(L2JModSettings.getProperty("BankingAdenaCount", "500000000")); + + OFFLINE_TRADE_ENABLE = Boolean.parseBoolean(L2JModSettings.getProperty("OfflineTradeEnable", "false")); + OFFLINE_CRAFT_ENABLE = Boolean.parseBoolean(L2JModSettings.getProperty("OfflineCraftEnable", "false")); + OFFLINE_SET_NAME_COLOR = Boolean.parseBoolean(L2JModSettings.getProperty("OfflineSetNameColor", "false")); + OFFLINE_NAME_COLOR = Integer.decode("0x" + L2JModSettings.getProperty("OfflineNameColor", "808080")); + OFFLINE_FAME = Boolean.parseBoolean(L2JModSettings.getProperty("OfflineFame", "true")); + RESTORE_OFFLINERS = Boolean.parseBoolean(L2JModSettings.getProperty("RestoreOffliners", "false")); + OFFLINE_MAX_DAYS = Integer.parseInt(L2JModSettings.getProperty("OfflineMaxDays", "10")); + OFFLINE_DISCONNECT_FINISHED = Boolean.parseBoolean(L2JModSettings.getProperty("OfflineDisconnectFinished", "true")); + + L2JMOD_ENABLE_MANA_POTIONS_SUPPORT = Boolean.parseBoolean(L2JModSettings.getProperty("EnableManaPotionSupport", "false")); + + L2JMOD_DISPLAY_SERVER_TIME = Boolean.parseBoolean(L2JModSettings.getProperty("DisplayServerTime", "false")); + + WELCOME_MESSAGE_ENABLED = Boolean.parseBoolean(L2JModSettings.getProperty("ScreenWelcomeMessageEnable", "false")); + WELCOME_MESSAGE_TEXT = L2JModSettings.getProperty("ScreenWelcomeMessageText", "Welcome to L2J server!"); + WELCOME_MESSAGE_TIME = Integer.parseInt(L2JModSettings.getProperty("ScreenWelcomeMessageTime", "10")) * 1000; + + L2JMOD_ANTIFEED_ENABLE = Boolean.parseBoolean(L2JModSettings.getProperty("AntiFeedEnable", "false")); + L2JMOD_ANTIFEED_DUALBOX = Boolean.parseBoolean(L2JModSettings.getProperty("AntiFeedDualbox", "true")); + L2JMOD_ANTIFEED_DISCONNECTED_AS_DUALBOX = Boolean.parseBoolean(L2JModSettings.getProperty("AntiFeedDisconnectedAsDualbox", "true")); + L2JMOD_ANTIFEED_INTERVAL = 1000 * Integer.parseInt(L2JModSettings.getProperty("AntiFeedInterval", "120")); + ANNOUNCE_PK_PVP = Boolean.parseBoolean(L2JModSettings.getProperty("AnnouncePkPvP", "False")); + ANNOUNCE_PK_PVP_NORMAL_MESSAGE = Boolean.parseBoolean(L2JModSettings.getProperty("AnnouncePkPvPNormalMessage", "True")); + ANNOUNCE_PK_MSG = L2JModSettings.getProperty("AnnouncePkMsg", "$killer has slaughtered $target"); + ANNOUNCE_PVP_MSG = L2JModSettings.getProperty("AnnouncePvpMsg", "$killer has defeated $target"); + + L2JMOD_CHAT_ADMIN = Boolean.parseBoolean(L2JModSettings.getProperty("ChatAdmin", "false")); + + L2JMOD_MULTILANG_ENABLE = Boolean.parseBoolean(L2JModSettings.getProperty("MultiLangEnable", "false")); + String[] allowed = L2JModSettings.getProperty("MultiLangAllowed", "en").split(";"); + L2JMOD_MULTILANG_ALLOWED = new ArrayList<>(allowed.length); + for (String lang : allowed) + { + L2JMOD_MULTILANG_ALLOWED.add(lang); + } + L2JMOD_MULTILANG_DEFAULT = L2JModSettings.getProperty("MultiLangDefault", "en"); + if (!L2JMOD_MULTILANG_ALLOWED.contains(L2JMOD_MULTILANG_DEFAULT)) + { + _log.warning("MultiLang[Config.load()]: default language: " + L2JMOD_MULTILANG_DEFAULT + " is not in allowed list !"); + } + L2JMOD_MULTILANG_VOICED_ALLOW = Boolean.parseBoolean(L2JModSettings.getProperty("MultiLangVoiceCommand", "True")); + L2JMOD_MULTILANG_SM_ENABLE = Boolean.parseBoolean(L2JModSettings.getProperty("MultiLangSystemMessageEnable", "false")); + allowed = L2JModSettings.getProperty("MultiLangSystemMessageAllowed", "").split(";"); + L2JMOD_MULTILANG_SM_ALLOWED = new ArrayList<>(allowed.length); + for (String lang : allowed) + { + if (!lang.isEmpty()) + { + L2JMOD_MULTILANG_SM_ALLOWED.add(lang); + } + } + + L2WALKER_PROTECTION = Boolean.parseBoolean(L2JModSettings.getProperty("L2WalkerProtection", "False")); + L2JMOD_DEBUG_VOICE_COMMAND = Boolean.parseBoolean(L2JModSettings.getProperty("DebugVoiceCommand", "False")); + + L2JMOD_DUALBOX_CHECK_MAX_PLAYERS_PER_IP = Integer.parseInt(L2JModSettings.getProperty("DualboxCheckMaxPlayersPerIP", "0")); + L2JMOD_DUALBOX_CHECK_MAX_OLYMPIAD_PARTICIPANTS_PER_IP = Integer.parseInt(L2JModSettings.getProperty("DualboxCheckMaxOlympiadParticipantsPerIP", "0")); + String[] propertySplit = L2JModSettings.getProperty("DualboxCheckWhitelist", "127.0.0.1,0").split(";"); + L2JMOD_DUALBOX_CHECK_WHITELIST = new TIntIntHashMap(propertySplit.length); + for (String entry : propertySplit) + { + String[] entrySplit = entry.split(","); + if (entrySplit.length != 2) + { + _log.warning(StringUtil.concat("DualboxCheck[Config.load()]: invalid config property -> DualboxCheckWhitelist \"", entry, "\"")); + } + else + { + try + { + int num = Integer.parseInt(entrySplit[1]); + num = num == 0 ? -1 : num; + L2JMOD_DUALBOX_CHECK_WHITELIST.put(InetAddress.getByName(entrySplit[0]).hashCode(), num); + } + catch (UnknownHostException e) + { + _log.warning(StringUtil.concat("DualboxCheck[Config.load()]: invalid address -> DualboxCheckWhitelist \"", entrySplit[0], "\"")); + } + catch (NumberFormatException e) + { + _log.warning(StringUtil.concat("DualboxCheck[Config.load()]: invalid number -> DualboxCheckWhitelist \"", entrySplit[1], "\"")); + } + } + } + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + L2JMOD_CONFIG_FILE + " File."); + } + + // Load PvP L2Properties file (if exists) + try + { + L2Properties pvpSettings = new L2Properties(); + is = new FileInputStream(new File(PVP_CONFIG_FILE)); + pvpSettings.load(is); + + KARMA_MIN_KARMA = Integer.parseInt(pvpSettings.getProperty("MinKarma", "240")); + KARMA_MAX_KARMA = Integer.parseInt(pvpSettings.getProperty("MaxKarma", "10000")); + KARMA_XP_DIVIDER = Integer.parseInt(pvpSettings.getProperty("XPDivider", "260")); + KARMA_LOST_BASE = Integer.parseInt(pvpSettings.getProperty("BaseKarmaLost", "0")); + KARMA_DROP_GM = Boolean.parseBoolean(pvpSettings.getProperty("CanGMDropEquipment", "false")); + KARMA_AWARD_PK_KILL = Boolean.parseBoolean(pvpSettings.getProperty("AwardPKKillPVPPoint", "true")); + KARMA_PK_LIMIT = Integer.parseInt(pvpSettings.getProperty("MinimumPKRequiredToDrop", "5")); + KARMA_NONDROPPABLE_PET_ITEMS = pvpSettings.getProperty("ListOfPetItems", "2375,3500,3501,3502,4422,4423,4424,4425,6648,6649,6650,9882"); + KARMA_NONDROPPABLE_ITEMS = pvpSettings.getProperty("ListOfNonDroppableItems", "57,1147,425,1146,461,10,2368,7,6,2370,2369,6842,6611,6612,6613,6614,6615,6616,6617,6618,6619,6620,6621,7694,8181,5575,7694,9388,9389,9390"); + + String[] array = KARMA_NONDROPPABLE_PET_ITEMS.split(","); + KARMA_LIST_NONDROPPABLE_PET_ITEMS = new int[array.length]; + + for (int i = 0; i < array.length; i++) + { + KARMA_LIST_NONDROPPABLE_PET_ITEMS[i] = Integer.parseInt(array[i]); + } + + array = KARMA_NONDROPPABLE_ITEMS.split(","); + KARMA_LIST_NONDROPPABLE_ITEMS = new int[array.length]; + + for (int i = 0; i < array.length; i++) + { + KARMA_LIST_NONDROPPABLE_ITEMS[i] = Integer.parseInt(array[i]); + } + + // sorting so binarySearch can be used later + Arrays.sort(KARMA_LIST_NONDROPPABLE_PET_ITEMS); + Arrays.sort(KARMA_LIST_NONDROPPABLE_ITEMS); + + PVP_NORMAL_TIME = Integer.parseInt(pvpSettings.getProperty("PvPVsNormalTime", "120000")); + PVP_PVP_TIME = Integer.parseInt(pvpSettings.getProperty("PvPVsPvPTime", "60000")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + PVP_CONFIG_FILE + " File."); + } + // Load Olympiad L2Properties file (if exists) + try + { + L2Properties olympiad = new L2Properties(); + is = new FileInputStream(new File(OLYMPIAD_CONFIG_FILE)); + olympiad.load(is); + + ALT_OLY_START_TIME = Integer.parseInt(olympiad.getProperty("AltOlyStartTime", "18")); + ALT_OLY_MIN = Integer.parseInt(olympiad.getProperty("AltOlyMin", "00")); + ALT_OLY_CPERIOD = Long.parseLong(olympiad.getProperty("AltOlyCPeriod", "21600000")); + ALT_OLY_BATTLE = Long.parseLong(olympiad.getProperty("AltOlyBattle", "360000")); + ALT_OLY_WPERIOD = Long.parseLong(olympiad.getProperty("AltOlyWPeriod", "604800000")); + ALT_OLY_VPERIOD = Long.parseLong(olympiad.getProperty("AltOlyVPeriod", "86400000")); + ALT_OLY_START_POINTS = Integer.parseInt(olympiad.getProperty("AltOlyStartPoints", "18")); + ALT_OLY_WEEKLY_POINTS = Integer.parseInt(olympiad.getProperty("AltOlyWeeklyPoints", "3")); + ALT_OLY_CLASSED = Integer.parseInt(olympiad.getProperty("AltOlyClassedParticipants", "5")); + ALT_OLY_NONCLASSED = Integer.parseInt(olympiad.getProperty("AltOlyNonClassedParticipants", "9")); + ALT_OLY_TEAMS = Integer.parseInt(olympiad.getProperty("AltOlyTeamsParticipants", "9")); + ALT_OLY_REG_DISPLAY = Integer.parseInt(olympiad.getProperty("AltOlyRegistrationDisplayNumber", "100")); + ALT_OLY_CLASSED_REWARD = parseItemsList(olympiad.getProperty("AltOlyClassedReward", "13722,50")); + ALT_OLY_NONCLASSED_REWARD = parseItemsList(olympiad.getProperty("AltOlyNonClassedReward", "13722,40")); + ALT_OLY_TEAM_REWARD = parseItemsList(olympiad.getProperty("AltOlyTeamReward", "13722,85")); + ALT_OLY_COMP_RITEM = Integer.parseInt(olympiad.getProperty("AltOlyCompRewItem", "13722")); + ALT_OLY_MIN_MATCHES = Integer.parseInt(olympiad.getProperty("AltOlyMinMatchesForPoints", "9")); + ALT_OLY_GP_PER_POINT = Integer.parseInt(olympiad.getProperty("AltOlyGPPerPoint", "1000")); + ALT_OLY_HERO_POINTS = Integer.parseInt(olympiad.getProperty("AltOlyHeroPoints", "180")); + ALT_OLY_RANK1_POINTS = Integer.parseInt(olympiad.getProperty("AltOlyRank1Points", "120")); + ALT_OLY_RANK2_POINTS = Integer.parseInt(olympiad.getProperty("AltOlyRank2Points", "80")); + ALT_OLY_RANK3_POINTS = Integer.parseInt(olympiad.getProperty("AltOlyRank3Points", "55")); + ALT_OLY_RANK4_POINTS = Integer.parseInt(olympiad.getProperty("AltOlyRank4Points", "35")); + ALT_OLY_RANK5_POINTS = Integer.parseInt(olympiad.getProperty("AltOlyRank5Points", "20")); + ALT_OLY_MAX_POINTS = Integer.parseInt(olympiad.getProperty("AltOlyMaxPoints", "10")); + ALT_OLY_LOG_FIGHTS = Boolean.parseBoolean(olympiad.getProperty("AltOlyLogFights", "false")); + ALT_OLY_SHOW_MONTHLY_WINNERS = Boolean.parseBoolean(olympiad.getProperty("AltOlyShowMonthlyWinners", "true")); + ALT_OLY_ANNOUNCE_GAMES = Boolean.parseBoolean(olympiad.getProperty("AltOlyAnnounceGames", "true")); + String[] split = olympiad.getProperty("AltOlyRestrictedItems", "6611,6612,6613,6614,6615,6616,6617,6618,6619,6620,6621,9388,9389,9390,17049,17050,17051,17052,17053,17054,17055,17056,17057,17058,17059,17060,17061,20759,20775,20776,20777,20778,14774").split(","); + LIST_OLY_RESTRICTED_ITEMS = new TIntArrayList(split.length); + for (String id : split) + { + LIST_OLY_RESTRICTED_ITEMS.add(Integer.parseInt(id)); + } + ALT_OLY_ENCHANT_LIMIT = Integer.parseInt(olympiad.getProperty("AltOlyEnchantLimit", "-1")); + ALT_OLY_WAIT_TIME = Integer.parseInt(olympiad.getProperty("AltOlyWaitTime", "120")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + OLYMPIAD_CONFIG_FILE + " File."); + } + try + { + L2Properties Settings = new L2Properties(); + is = new FileInputStream(HEXID_FILE); + Settings.load(is); + SERVER_ID = Integer.parseInt(Settings.getProperty("ServerID")); + HEX_ID = new BigInteger(Settings.getProperty("HexID"), 16).toByteArray(); + } + catch (Exception e) + { + _log.warning("Could not load HexID file (" + HEXID_FILE + "). Hopefully login will give us one."); + } + + // Grandboss + try + { + L2Properties grandbossSettings = new L2Properties(); + is = new FileInputStream(new File(GRANDBOSS_CONFIG_FILE)); + grandbossSettings.load(is); + + Antharas_Wait_Time = Integer.parseInt(grandbossSettings.getProperty("AntharasWaitTime", "30")); + if ((Antharas_Wait_Time < 3) || (Antharas_Wait_Time > 60)) + { + Antharas_Wait_Time = 30; + } + Antharas_Wait_Time = Antharas_Wait_Time * 60000; + + Valakas_Wait_Time = Integer.parseInt(grandbossSettings.getProperty("ValakasWaitTime", "30")); + if ((Valakas_Wait_Time < 3) || (Valakas_Wait_Time > 60)) + { + Valakas_Wait_Time = 30; + } + Valakas_Wait_Time = Valakas_Wait_Time * 60000; + + Interval_Of_Antharas_Spawn = Integer.parseInt(grandbossSettings.getProperty("IntervalOfAntharasSpawn", "264")); + if ((Interval_Of_Antharas_Spawn < 1) || (Interval_Of_Antharas_Spawn > 480)) + { + Interval_Of_Antharas_Spawn = 192; + } + Interval_Of_Antharas_Spawn = Interval_Of_Antharas_Spawn * 3600000; + + Random_Of_Antharas_Spawn = Integer.parseInt(grandbossSettings.getProperty("RandomOfAntharasSpawn", "72")); + if ((Random_Of_Antharas_Spawn < 1) || (Random_Of_Antharas_Spawn > 192)) + { + Random_Of_Antharas_Spawn = 145; + } + Random_Of_Antharas_Spawn = Random_Of_Antharas_Spawn * 3600000; + + Interval_Of_Valakas_Spawn = Integer.parseInt(grandbossSettings.getProperty("IntervalOfValakasSpawn", "264")); + if ((Interval_Of_Valakas_Spawn < 1) || (Interval_Of_Valakas_Spawn > 480)) + { + Interval_Of_Valakas_Spawn = 192; + } + Interval_Of_Valakas_Spawn = Interval_Of_Valakas_Spawn * 3600000; + + Random_Of_Valakas_Spawn = Integer.parseInt(grandbossSettings.getProperty("RandomOfValakasSpawn", "72")); + if ((Random_Of_Valakas_Spawn < 1) || (Random_Of_Valakas_Spawn > 192)) + { + Random_Of_Valakas_Spawn = 145; + } + Random_Of_Valakas_Spawn = Random_Of_Valakas_Spawn * 3600000; + + Interval_Of_Baium_Spawn = Integer.parseInt(grandbossSettings.getProperty("IntervalOfBaiumSpawn", "168")); + if ((Interval_Of_Baium_Spawn < 1) || (Interval_Of_Baium_Spawn > 480)) + { + Interval_Of_Baium_Spawn = 121; + } + Interval_Of_Baium_Spawn = Interval_Of_Baium_Spawn * 3600000; + + Random_Of_Baium_Spawn = Integer.parseInt(grandbossSettings.getProperty("RandomOfBaiumSpawn", "48")); + if ((Random_Of_Baium_Spawn < 1) || (Random_Of_Baium_Spawn > 192)) + { + Random_Of_Baium_Spawn = 8; + } + Random_Of_Baium_Spawn = Random_Of_Baium_Spawn * 3600000; + + Interval_Of_Core_Spawn = Integer.parseInt(grandbossSettings.getProperty("IntervalOfCoreSpawn", "60")); + if ((Interval_Of_Core_Spawn < 1) || (Interval_Of_Core_Spawn > 480)) + { + Interval_Of_Core_Spawn = 27; + } + Interval_Of_Core_Spawn = Interval_Of_Core_Spawn * 3600000; + + Random_Of_Core_Spawn = Integer.parseInt(grandbossSettings.getProperty("RandomOfCoreSpawn", "24")); + if ((Random_Of_Core_Spawn < 1) || (Random_Of_Core_Spawn > 192)) + { + Random_Of_Core_Spawn = 47; + } + Random_Of_Core_Spawn = Random_Of_Core_Spawn * 3600000; + + Interval_Of_Orfen_Spawn = Integer.parseInt(grandbossSettings.getProperty("IntervalOfOrfenSpawn", "48")); + if ((Interval_Of_Orfen_Spawn < 1) || (Interval_Of_Orfen_Spawn > 480)) + { + Interval_Of_Orfen_Spawn = 28; + } + Interval_Of_Orfen_Spawn = Interval_Of_Orfen_Spawn * 3600000; + + Random_Of_Orfen_Spawn = Integer.parseInt(grandbossSettings.getProperty("RandomOfOrfenSpawn", "20")); + if ((Random_Of_Orfen_Spawn < 1) || (Random_Of_Orfen_Spawn > 192)) + { + Random_Of_Orfen_Spawn = 41; + } + Random_Of_Orfen_Spawn = Random_Of_Orfen_Spawn * 3600000; + + Interval_Of_QueenAnt_Spawn = Integer.parseInt(grandbossSettings.getProperty("IntervalOfQueenAntSpawn", "36")); + if ((Interval_Of_QueenAnt_Spawn < 1) || (Interval_Of_QueenAnt_Spawn > 480)) + { + Interval_Of_QueenAnt_Spawn = 19; + } + Interval_Of_QueenAnt_Spawn = Interval_Of_QueenAnt_Spawn * 3600000; + + Random_Of_QueenAnt_Spawn = Integer.parseInt(grandbossSettings.getProperty("RandomOfQueenAntSpawn", "17")); + if ((Random_Of_QueenAnt_Spawn < 1) || (Random_Of_QueenAnt_Spawn > 192)) + { + Random_Of_QueenAnt_Spawn = 35; + } + Random_Of_QueenAnt_Spawn = Random_Of_QueenAnt_Spawn * 3600000; + + Interval_Of_Zaken_Spawn = Integer.parseInt(grandbossSettings.getProperty("IntervalOfZakenSpawn", "19")); + if ((Interval_Of_Zaken_Spawn < 1) || (Interval_Of_Zaken_Spawn > 480)) + { + Interval_Of_Zaken_Spawn = 19; + } + Interval_Of_Zaken_Spawn = Interval_Of_Zaken_Spawn * 3600000; + + Random_Of_Zaken_Spawn = Integer.parseInt(grandbossSettings.getProperty("RandomOfZakenSpawn", "35")); + if ((Random_Of_Zaken_Spawn < 1) || (Random_Of_Zaken_Spawn > 192)) + { + Random_Of_Zaken_Spawn = 35; + } + Random_Of_Zaken_Spawn = Random_Of_Zaken_Spawn * 3600000; + + Interval_Of_Frintezza_Spawn = Integer.parseInt(grandbossSettings.getProperty("IntervalOfFrintezzaSpawn", "48")); + if ((Interval_Of_Frintezza_Spawn < 1) || (Interval_Of_Frintezza_Spawn > 480)) + { + Interval_Of_Frintezza_Spawn = 121; + } + Interval_Of_Frintezza_Spawn = Interval_Of_Frintezza_Spawn * 3600000; + + Random_Of_Frintezza_Spawn = Integer.parseInt(grandbossSettings.getProperty("RandomOfFrintezzaSpawn", "8")); + if ((Random_Of_Frintezza_Spawn < 1) || (Random_Of_Frintezza_Spawn > 192)) + { + Random_Of_Frintezza_Spawn = 8; + } + Random_Of_Frintezza_Spawn = Random_Of_Frintezza_Spawn * 3600000; + + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + GRANDBOSS_CONFIG_FILE + " File."); + } + + // Gracia Seeds + try + { + L2Properties graciaseedsSettings = new L2Properties(); + is = new FileInputStream(new File(GRACIASEEDS_CONFIG_FILE)); + graciaseedsSettings.load(is); + + // Seed of Destruction + SOD_TIAT_KILL_COUNT = Integer.parseInt(graciaseedsSettings.getProperty("TiatKillCountForNextState", "10")); + SOD_STAGE_2_LENGTH = Long.parseLong(graciaseedsSettings.getProperty("Stage2Length", "720")) * 60000; + + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + GRACIASEEDS_CONFIG_FILE + " File."); + } + + try (LineNumberReader lnr = new LineNumberReader(new BufferedReader(new FileReader(new File(CHAT_FILTER_FILE))))) + { + FILTER_LIST = new ArrayList<>(); + String line = null; + while ((line = lnr.readLine()) != null) + { + if (line.trim().isEmpty() || line.startsWith("#")) + { + continue; + } + + FILTER_LIST.add(line.trim()); + } + _log.info("Loaded " + FILTER_LIST.size() + " Filter Words."); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + CHAT_FILTER_FILE + " File."); + } + } + finally + { + try + { + is.close(); + } + catch (Exception e) + { + } + } + } + else if (Server.serverMode == Server.MODE_LOGINSERVER) + { + _log.info("loading login config"); + InputStream is = null; + try + { + try + { + L2Properties serverSettings = new L2Properties(); + is = new FileInputStream(new File(LOGIN_CONFIGURATION_FILE)); + serverSettings.load(is); + + GAME_SERVER_LOGIN_HOST = serverSettings.getProperty("LoginHostname", "*"); + GAME_SERVER_LOGIN_PORT = Integer.parseInt(serverSettings.getProperty("LoginPort", "9013")); + + LOGIN_BIND_ADDRESS = serverSettings.getProperty("LoginserverHostname", "*"); + PORT_LOGIN = Integer.parseInt(serverSettings.getProperty("LoginserverPort", "2106")); + + DEBUG = Boolean.parseBoolean(serverSettings.getProperty("Debug", "false")); + + ACCEPT_NEW_GAMESERVER = Boolean.parseBoolean(serverSettings.getProperty("AcceptNewGameServer", "True")); + + LOGIN_TRY_BEFORE_BAN = Integer.parseInt(serverSettings.getProperty("LoginTryBeforeBan", "10")); + LOGIN_BLOCK_AFTER_BAN = Integer.parseInt(serverSettings.getProperty("LoginBlockAfterBan", "600")); + + LOG_LOGIN_CONTROLLER = Boolean.parseBoolean(serverSettings.getProperty("LogLoginController", "true")); + + DATABASE_DRIVER = serverSettings.getProperty("Driver", "com.mysql.jdbc.Driver"); + DATABASE_URL = serverSettings.getProperty("URL", "jdbc:mysql://localhost/l2jls"); + DATABASE_LOGIN = serverSettings.getProperty("Login", "root"); + DATABASE_PASSWORD = serverSettings.getProperty("Password", ""); + DATABASE_MAX_CONNECTIONS = Integer.parseInt(serverSettings.getProperty("MaximumDbConnections", "10")); + DATABASE_MAX_IDLE_TIME = Integer.parseInt(serverSettings.getProperty("MaximumDbIdleTime", "0")); + + SHOW_LICENCE = Boolean.parseBoolean(serverSettings.getProperty("ShowLicence", "true")); + + AUTO_CREATE_ACCOUNTS = Boolean.parseBoolean(serverSettings.getProperty("AutoCreateAccounts", "True")); + + FLOOD_PROTECTION = Boolean.parseBoolean(serverSettings.getProperty("EnableFloodProtection", "True")); + FAST_CONNECTION_LIMIT = Integer.parseInt(serverSettings.getProperty("FastConnectionLimit", "15")); + NORMAL_CONNECTION_TIME = Integer.parseInt(serverSettings.getProperty("NormalConnectionTime", "700")); + FAST_CONNECTION_TIME = Integer.parseInt(serverSettings.getProperty("FastConnectionTime", "350")); + MAX_CONNECTION_PER_IP = Integer.parseInt(serverSettings.getProperty("MaxConnectionPerIP", "50")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + LOGIN_CONFIGURATION_FILE + " File."); + } + // MMO + try + { + _log.info("Loading " + MMO_CONFIG_FILE.replaceAll("./config/", "")); + L2Properties mmoSettings = new L2Properties(); + is = new FileInputStream(new File(MMO_CONFIG_FILE)); + mmoSettings.load(is); + MMO_SELECTOR_SLEEP_TIME = Integer.parseInt(mmoSettings.getProperty("SleepTime", "20")); + MMO_MAX_SEND_PER_PASS = Integer.parseInt(mmoSettings.getProperty("MaxSendPerPass", "12")); + MMO_MAX_READ_PER_PASS = Integer.parseInt(mmoSettings.getProperty("MaxReadPerPass", "12")); + MMO_HELPER_BUFFER_COUNT = Integer.parseInt(mmoSettings.getProperty("HelperBufferCount", "20")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + MMO_CONFIG_FILE + " File."); + } + + // Load Telnet L2Properties file (if exists) + try + { + L2Properties telnetSettings = new L2Properties(); + is = new FileInputStream(new File(TELNET_FILE)); + telnetSettings.load(is); + + IS_TELNET_ENABLED = Boolean.parseBoolean(telnetSettings.getProperty("EnableTelnet", "false")); + } + catch (Exception e) + { + e.printStackTrace(); + throw new Error("Failed to Load " + TELNET_FILE + " File."); + } + } + finally + { + try + { + is.close(); + } + catch (Exception e) + { + } + } + } + else + { + _log.severe("Could not Load Config: server mode was not set"); + } + } + + /** + * Set a new value to a game parameter from the admin console. + * @param pName (String) : name of the parameter to change + * @param pValue (String) : new value of the parameter + * @return boolean : true if modification has been made + * @link useAdminCommand + */ + public static boolean setParameterValue(String pName, String pValue) + { + // rates.properties + if (pName.equalsIgnoreCase("RateXp")) + { + RATE_XP = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateSp")) + { + RATE_SP = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RatePartyXp")) + { + RATE_PARTY_XP = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RatePartySp")) + { + RATE_PARTY_SP = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateConsumableCost")) + { + RATE_CONSUMABLE_COST = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateExtractFish")) + { + RATE_EXTR_FISH = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateDropItems")) + { + RATE_DROP_ITEMS = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateDropAdena")) + { + RATE_DROP_ITEMS_ID.put(57, Float.parseFloat(pValue)); + } + else if (pName.equalsIgnoreCase("RateRaidDropItems")) + { + RATE_DROP_ITEMS_BY_RAID = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateDropSpoil")) + { + RATE_DROP_SPOIL = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateDropManor")) + { + RATE_DROP_MANOR = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("RateQuestDrop")) + { + RATE_QUEST_DROP = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateQuestReward")) + { + RATE_QUEST_REWARD = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateQuestRewardXP")) + { + RATE_QUEST_REWARD_XP = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateQuestRewardSP")) + { + RATE_QUEST_REWARD_SP = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateQuestRewardAdena")) + { + RATE_QUEST_REWARD_ADENA = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("UseQuestRewardMultipliers")) + { + RATE_QUEST_REWARD_USE_MULTIPLIERS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("RateQuestRewardPotion")) + { + RATE_QUEST_REWARD_POTION = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateQuestRewardScroll")) + { + RATE_QUEST_REWARD_SCROLL = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateQuestRewardRecipe")) + { + RATE_QUEST_REWARD_RECIPE = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateQuestRewardMaterial")) + { + RATE_QUEST_REWARD_MATERIAL = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateVitalityLevel1")) + { + RATE_VITALITY_LEVEL_1 = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateVitalityLevel2")) + { + RATE_VITALITY_LEVEL_2 = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateVitalityLevel3")) + { + RATE_VITALITY_LEVEL_3 = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateVitalityLevel4")) + { + RATE_VITALITY_LEVEL_4 = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateRecoveryPeaceZone")) + { + RATE_RECOVERY_VITALITY_PEACE_ZONE = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateVitalityLost")) + { + RATE_VITALITY_LOST = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateVitalityGain")) + { + RATE_VITALITY_GAIN = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateRecoveryOnReconnect")) + { + RATE_RECOVERY_ON_RECONNECT = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateKarmaExpLost")) + { + RATE_KARMA_EXP_LOST = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateSiegeGuardsPrice")) + { + RATE_SIEGE_GUARDS_PRICE = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateCommonHerbs")) + { + RATE_DROP_COMMON_HERBS = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateHpHerbs")) + { + RATE_DROP_HP_HERBS = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateMpHerbs")) + { + RATE_DROP_MP_HERBS = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateSpecialHerbs")) + { + RATE_DROP_SPECIAL_HERBS = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("RateVitalityHerbs")) + { + RATE_DROP_VITALITY_HERBS = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("PlayerDropLimit")) + { + PLAYER_DROP_LIMIT = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("PlayerRateDrop")) + { + PLAYER_RATE_DROP = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("PlayerRateDropItem")) + { + PLAYER_RATE_DROP_ITEM = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("PlayerRateDropEquip")) + { + PLAYER_RATE_DROP_EQUIP = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("PlayerRateDropEquipWeapon")) + { + PLAYER_RATE_DROP_EQUIP_WEAPON = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("PetXpRate")) + { + PET_XP_RATE = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("PetFoodRate")) + { + PET_FOOD_RATE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("SinEaterXpRate")) + { + SINEATER_XP_RATE = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("KarmaDropLimit")) + { + KARMA_DROP_LIMIT = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("KarmaRateDrop")) + { + KARMA_RATE_DROP = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("KarmaRateDropItem")) + { + KARMA_RATE_DROP_ITEM = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("KarmaRateDropEquip")) + { + KARMA_RATE_DROP_EQUIP = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("KarmaRateDropEquipWeapon")) + { + KARMA_RATE_DROP_EQUIP_WEAPON = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AutoDestroyDroppedItemAfter")) + { + AUTODESTROY_ITEM_AFTER = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("DestroyPlayerDroppedItem")) + { + DESTROY_DROPPED_PLAYER_ITEM = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("DestroyEquipableItem")) + { + DESTROY_EQUIPABLE_PLAYER_ITEM = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("SaveDroppedItem")) + { + SAVE_DROPPED_ITEM = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("EmptyDroppedItemTableAfterLoad")) + { + EMPTY_DROPPED_ITEM_TABLE_AFTER_LOAD = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("SaveDroppedItemInterval")) + { + SAVE_DROPPED_ITEM_INTERVAL = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ClearDroppedItemTable")) + { + CLEAR_DROPPED_ITEM_TABLE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("PreciseDropCalculation")) + { + PRECISE_DROP_CALCULATION = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("MultipleItemDrop")) + { + MULTIPLE_ITEM_DROP = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("LowWeight")) + { + LOW_WEIGHT = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("MediumWeight")) + { + MEDIUM_WEIGHT = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("HighWeight")) + { + HIGH_WEIGHT = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("AdvancedDiagonalStrategy")) + { + ADVANCED_DIAGONAL_STRATEGY = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("DiagonalWeight")) + { + DIAGONAL_WEIGHT = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("MaxPostfilterPasses")) + { + MAX_POSTFILTER_PASSES = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("CoordSynchronize")) + { + COORD_SYNCHRONIZE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("DeleteCharAfterDays")) + { + DELETE_DAYS = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ClientPacketQueueSize")) + { + CLIENT_PACKET_QUEUE_SIZE = Integer.parseInt(pValue); + if (CLIENT_PACKET_QUEUE_SIZE == 0) + { + CLIENT_PACKET_QUEUE_SIZE = MMO_MAX_READ_PER_PASS + 1; + } + } + else if (pName.equalsIgnoreCase("ClientPacketQueueMaxBurstSize")) + { + CLIENT_PACKET_QUEUE_MAX_BURST_SIZE = Integer.parseInt(pValue); + if (CLIENT_PACKET_QUEUE_MAX_BURST_SIZE == 0) + { + CLIENT_PACKET_QUEUE_MAX_BURST_SIZE = MMO_MAX_READ_PER_PASS; + } + } + else if (pName.equalsIgnoreCase("ClientPacketQueueMaxPacketsPerSecond")) + { + CLIENT_PACKET_QUEUE_MAX_PACKETS_PER_SECOND = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ClientPacketQueueMeasureInterval")) + { + CLIENT_PACKET_QUEUE_MEASURE_INTERVAL = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ClientPacketQueueMaxAveragePacketsPerSecond")) + { + CLIENT_PACKET_QUEUE_MAX_AVERAGE_PACKETS_PER_SECOND = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ClientPacketQueueMaxFloodsPerMin")) + { + CLIENT_PACKET_QUEUE_MAX_FLOODS_PER_MIN = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ClientPacketQueueMaxOverflowsPerMin")) + { + CLIENT_PACKET_QUEUE_MAX_OVERFLOWS_PER_MIN = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ClientPacketQueueMaxUnderflowsPerMin")) + { + CLIENT_PACKET_QUEUE_MAX_UNDERFLOWS_PER_MIN = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ClientPacketQueueMaxUnknownPerMin")) + { + CLIENT_PACKET_QUEUE_MAX_UNKNOWN_PER_MIN = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AllowDiscardItem")) + { + ALLOW_DISCARDITEM = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AllowRefund")) + { + ALLOW_REFUND = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AllowWarehouse")) + { + ALLOW_WAREHOUSE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AllowWear")) + { + ALLOW_WEAR = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("WearDelay")) + { + WEAR_DELAY = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("WearPrice")) + { + WEAR_PRICE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AllowWater")) + { + ALLOW_WATER = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AllowRentPet")) + { + ALLOW_RENTPET = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("BoatBroadcastRadius")) + { + BOAT_BROADCAST_RADIUS = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AllowCursedWeapons")) + { + ALLOW_CURSED_WEAPONS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AllowManor")) + { + ALLOW_MANOR = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AllowNpcWalkers")) + { + ALLOW_NPC_WALKERS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AllowPetWalkers")) + { + ALLOW_PET_WALKERS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("BypassValidation")) + { + BYPASS_VALIDATION = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("CommunityType")) + { + COMMUNITY_TYPE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("BBSShowPlayerList")) + { + BBS_SHOW_PLAYERLIST = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("BBSDefault")) + { + BBS_DEFAULT = pValue; + } + else if (pName.equalsIgnoreCase("ShowLevelOnCommunityBoard")) + { + SHOW_LEVEL_COMMUNITYBOARD = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("ShowStatusOnCommunityBoard")) + { + SHOW_STATUS_COMMUNITYBOARD = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("NamePageSizeOnCommunityBoard")) + { + NAME_PAGE_SIZE_COMMUNITYBOARD = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("NamePerRowOnCommunityBoard")) + { + NAME_PER_ROW_COMMUNITYBOARD = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ShowServerNews")) + { + SERVER_NEWS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("ShowNpcLevel")) + { + SHOW_NPC_LVL = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("ShowCrestWithoutQuest")) + { + SHOW_CREST_WITHOUT_QUEST = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("ForceInventoryUpdate")) + { + FORCE_INVENTORY_UPDATE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AutoDeleteInvalidQuestData")) + { + AUTODELETE_INVALID_QUEST_DATA = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("MaximumOnlineUsers")) + { + MAXIMUM_ONLINE_USERS = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ZoneTown")) + { + ZONE_TOWN = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("CheckKnownList")) + { + CHECK_KNOWN = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("MaxDriftRange")) + { + MAX_DRIFT_RANGE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("UseDeepBlueDropRules")) + { + DEEPBLUE_DROP_RULES = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("UseDeepBlueDropRulesRaid")) + { + DEEPBLUE_DROP_RULES_RAID = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("GuardAttackAggroMob")) + { + GUARD_ATTACK_AGGRO_MOB = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("CancelLesserEffect")) + { + EFFECT_CANCELING = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("MaximumSlotsForNoDwarf")) + { + INVENTORY_MAXIMUM_NO_DWARF = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MaximumSlotsForDwarf")) + { + INVENTORY_MAXIMUM_DWARF = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MaximumSlotsForGMPlayer")) + { + INVENTORY_MAXIMUM_GM = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MaximumSlotsForQuestItems")) + { + INVENTORY_MAXIMUM_QUEST_ITEMS = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MaximumWarehouseSlotsForNoDwarf")) + { + WAREHOUSE_SLOTS_NO_DWARF = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MaximumWarehouseSlotsForDwarf")) + { + WAREHOUSE_SLOTS_DWARF = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MaximumWarehouseSlotsForClan")) + { + WAREHOUSE_SLOTS_CLAN = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantChanceWeapon")) + { + ENCHANT_CHANCE_WEAPON = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantChanceArmor")) + { + ENCHANT_CHANCE_ARMOR = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantChanceJewelry")) + { + ENCHANT_CHANCE_JEWELRY = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantChanceElementStone")) + { + ENCHANT_CHANCE_ELEMENT_STONE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantChanceElementCrystal")) + { + ENCHANT_CHANCE_ELEMENT_CRYSTAL = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantChanceElementJewel")) + { + ENCHANT_CHANCE_ELEMENT_JEWEL = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantChanceElementEnergy")) + { + ENCHANT_CHANCE_ELEMENT_ENERGY = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantMaxWeapon")) + { + ENCHANT_MAX_WEAPON = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantMaxArmor")) + { + ENCHANT_MAX_ARMOR = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantMaxJewelry")) + { + ENCHANT_MAX_JEWELRY = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantSafeMax")) + { + ENCHANT_SAFE_MAX = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnchantSafeMaxFull")) + { + ENCHANT_SAFE_MAX_FULL = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AugmentationNGSkillChance")) + { + AUGMENTATION_NG_SKILL_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AugmentationNGGlowChance")) + { + AUGMENTATION_NG_GLOW_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AugmentationMidSkillChance")) + { + AUGMENTATION_MID_SKILL_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AugmentationMidGlowChance")) + { + AUGMENTATION_MID_GLOW_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AugmentationHighSkillChance")) + { + AUGMENTATION_HIGH_SKILL_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AugmentationHighGlowChance")) + { + AUGMENTATION_HIGH_GLOW_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AugmentationTopSkillChance")) + { + AUGMENTATION_TOP_SKILL_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AugmentationTopGlowChance")) + { + AUGMENTATION_TOP_GLOW_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AugmentationBaseStatChance")) + { + AUGMENTATION_BASESTAT_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("HpRegenMultiplier")) + { + HP_REGEN_MULTIPLIER = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("MpRegenMultiplier")) + { + MP_REGEN_MULTIPLIER = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("CpRegenMultiplier")) + { + CP_REGEN_MULTIPLIER = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("RaidHpRegenMultiplier")) + { + RAID_HP_REGEN_MULTIPLIER = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("RaidMpRegenMultiplier")) + { + RAID_MP_REGEN_MULTIPLIER = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("RaidPDefenceMultiplier")) + { + RAID_PDEFENCE_MULTIPLIER = Double.parseDouble(pValue) / 100; + } + else if (pName.equalsIgnoreCase("RaidMDefenceMultiplier")) + { + RAID_MDEFENCE_MULTIPLIER = Double.parseDouble(pValue) / 100; + } + else if (pName.equalsIgnoreCase("RaidPAttackMultiplier")) + { + RAID_PATTACK_MULTIPLIER = Double.parseDouble(pValue) / 100; + } + else if (pName.equalsIgnoreCase("RaidMAttackMultiplier")) + { + RAID_MATTACK_MULTIPLIER = Double.parseDouble(pValue) / 100; + } + else if (pName.equalsIgnoreCase("RaidMinionRespawnTime")) + { + RAID_MINION_RESPAWN_TIMER = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("RaidChaosTime")) + { + RAID_CHAOS_TIME = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("GrandChaosTime")) + { + GRAND_CHAOS_TIME = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MinionChaosTime")) + { + MINION_CHAOS_TIME = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("StartingAdena")) + { + STARTING_ADENA = Long.parseLong(pValue); + } + else if (pName.equalsIgnoreCase("StartingLevel")) + { + STARTING_LEVEL = Byte.parseByte(pValue); + } + else if (pName.equalsIgnoreCase("StartingSP")) + { + STARTING_SP = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("UnstuckInterval")) + { + UNSTUCK_INTERVAL = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("TeleportWatchdogTimeout")) + { + TELEPORT_WATCHDOG_TIMEOUT = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("PlayerSpawnProtection")) + { + PLAYER_SPAWN_PROTECTION = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("PlayerFakeDeathUpProtection")) + { + PLAYER_FAKEDEATH_UP_PROTECTION = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("RestorePlayerInstance")) + { + RESTORE_PLAYER_INSTANCE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AllowSummonToInstance")) + { + ALLOW_SUMMON_TO_INSTANCE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("PartyXpCutoffMethod")) + { + PARTY_XP_CUTOFF_METHOD = pValue; + } + else if (pName.equalsIgnoreCase("PartyXpCutoffPercent")) + { + PARTY_XP_CUTOFF_PERCENT = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("PartyXpCutoffLevel")) + { + PARTY_XP_CUTOFF_LEVEL = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("RespawnRestoreCP")) + { + RESPAWN_RESTORE_CP = Double.parseDouble(pValue) / 100; + } + else if (pName.equalsIgnoreCase("RespawnRestoreHP")) + { + RESPAWN_RESTORE_HP = Double.parseDouble(pValue) / 100; + } + else if (pName.equalsIgnoreCase("RespawnRestoreMP")) + { + RESPAWN_RESTORE_MP = Double.parseDouble(pValue) / 100; + } + else if (pName.equalsIgnoreCase("MaxPvtStoreSellSlotsDwarf")) + { + MAX_PVTSTORESELL_SLOTS_DWARF = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MaxPvtStoreSellSlotsOther")) + { + MAX_PVTSTORESELL_SLOTS_OTHER = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MaxPvtStoreBuySlotsDwarf")) + { + MAX_PVTSTOREBUY_SLOTS_DWARF = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MaxPvtStoreBuySlotsOther")) + { + MAX_PVTSTOREBUY_SLOTS_OTHER = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("StoreSkillCooltime")) + { + STORE_SKILL_COOLTIME = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("SubclassStoreSkillCooltime")) + { + SUBCLASS_STORE_SKILL_COOLTIME = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AnnounceMammonSpawn")) + { + ANNOUNCE_MAMMON_SPAWN = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltGameTiredness")) + { + ALT_GAME_TIREDNESS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("EnableFallingDamage")) + { + ENABLE_FALLING_DAMAGE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltGameCreation")) + { + ALT_GAME_CREATION = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltGameCreationSpeed")) + { + ALT_GAME_CREATION_SPEED = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("AltGameCreationXpRate")) + { + ALT_GAME_CREATION_XP_RATE = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("AltGameCreationRareXpSpRate")) + { + ALT_GAME_CREATION_RARE_XPSP_RATE = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("AltGameCreationSpRate")) + { + ALT_GAME_CREATION_SP_RATE = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("AltWeightLimit")) + { + ALT_WEIGHT_LIMIT = Double.parseDouble(pValue); + } + else if (pName.equalsIgnoreCase("AltBlacksmithUseRecipes")) + { + ALT_BLACKSMITH_USE_RECIPES = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltGameSkillLearn")) + { + ALT_GAME_SKILL_LEARN = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("RemoveCastleCirclets")) + { + REMOVE_CASTLE_CIRCLETS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("ReputationScorePerKill")) + { + REPUTATION_SCORE_PER_KILL = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AltGameCancelByHit")) + { + ALT_GAME_CANCEL_BOW = pValue.equalsIgnoreCase("bow") || pValue.equalsIgnoreCase("all"); + ALT_GAME_CANCEL_CAST = pValue.equalsIgnoreCase("cast") || pValue.equalsIgnoreCase("all"); + } + + else if (pName.equalsIgnoreCase("AltShieldBlocks")) + { + ALT_GAME_SHIELD_BLOCKS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltPerfectShieldBlockRate")) + { + ALT_PERFECT_SHLD_BLOCK = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("Delevel")) + { + ALT_GAME_DELEVEL = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("MagicFailures")) + { + ALT_GAME_MAGICFAILURES = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltMobAgroInPeaceZone")) + { + ALT_MOB_AGRO_IN_PEACEZONE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltGameExponentXp")) + { + ALT_GAME_EXPONENT_XP = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("AltGameExponentSp")) + { + ALT_GAME_EXPONENT_SP = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("AllowClassMasters")) + { + ALLOW_CLASS_MASTERS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AllowEntireTree")) + { + ALLOW_ENTIRE_TREE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AlternateClassMaster")) + { + ALTERNATE_CLASS_MASTER = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltPartyRange")) + { + ALT_PARTY_RANGE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AltPartyRange2")) + { + ALT_PARTY_RANGE2 = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("CraftingEnabled")) + { + IS_CRAFTING_ENABLED = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("CraftMasterwork")) + { + CRAFT_MASTERWORK = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("LifeCrystalNeeded")) + { + LIFE_CRYSTAL_NEEDED = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AutoLoot")) + { + AUTO_LOOT = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AutoLootRaids")) + { + AUTO_LOOT_RAIDS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AutoLootHerbs")) + { + AUTO_LOOT_HERBS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltKarmaPlayerCanBeKilledInPeaceZone")) + { + ALT_GAME_KARMA_PLAYER_CAN_BE_KILLED_IN_PEACEZONE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltKarmaPlayerCanShop")) + { + ALT_GAME_KARMA_PLAYER_CAN_SHOP = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltKarmaPlayerCanUseGK")) + { + ALT_GAME_KARMA_PLAYER_CAN_USE_GK = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltKarmaPlayerCanTeleport")) + { + ALT_GAME_KARMA_PLAYER_CAN_TELEPORT = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltKarmaPlayerCanTrade")) + { + ALT_GAME_KARMA_PLAYER_CAN_TRADE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltKarmaPlayerCanUseWareHouse")) + { + ALT_GAME_KARMA_PLAYER_CAN_USE_WAREHOUSE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("MaxPersonalFamePoints")) + { + MAX_PERSONAL_FAME_POINTS = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("FortressZoneFameTaskFrequency")) + { + FORTRESS_ZONE_FAME_TASK_FREQUENCY = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("FortressZoneFameAquirePoints")) + { + FORTRESS_ZONE_FAME_AQUIRE_POINTS = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("CastleZoneFameTaskFrequency")) + { + CASTLE_ZONE_FAME_TASK_FREQUENCY = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("CastleZoneFameAquirePoints")) + { + CASTLE_ZONE_FAME_AQUIRE_POINTS = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("AltCastleForDawn")) + { + ALT_GAME_CASTLE_DAWN = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltCastleForDusk")) + { + ALT_GAME_CASTLE_DUSK = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltRequireClanCastle")) + { + ALT_GAME_REQUIRE_CLAN_CASTLE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltFreeTeleporting")) + { + ALT_GAME_FREE_TELEPORT = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltSubClassWithoutQuests")) + { + ALT_GAME_SUBCLASS_WITHOUT_QUESTS = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltSubclassEverywhere")) + { + ALT_GAME_SUBCLASS_EVERYWHERE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AltMembersCanWithdrawFromClanWH")) + { + ALT_MEMBERS_CAN_WITHDRAW_FROM_CLANWH = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("DwarfRecipeLimit")) + { + DWARF_RECIPE_LIMIT = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("CommonRecipeLimit")) + { + COMMON_RECIPE_LIMIT = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ChampionEnable")) + { + L2JMOD_CHAMPION_ENABLE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("ChampionFrequency")) + { + L2JMOD_CHAMPION_FREQUENCY = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ChampionMinLevel")) + { + L2JMOD_CHAMP_MIN_LVL = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ChampionMaxLevel")) + { + L2JMOD_CHAMP_MAX_LVL = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ChampionHp")) + { + L2JMOD_CHAMPION_HP = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ChampionHpRegen")) + { + L2JMOD_CHAMPION_HP_REGEN = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("ChampionRewards")) + { + L2JMOD_CHAMPION_REWARDS = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ChampionAdenasRewards")) + { + L2JMOD_CHAMPION_ADENAS_REWARDS = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("ChampionAtk")) + { + L2JMOD_CHAMPION_ATK = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("ChampionSpdAtk")) + { + L2JMOD_CHAMPION_SPD_ATK = Float.parseFloat(pValue); + } + else if (pName.equalsIgnoreCase("ChampionRewardLowerLvlItemChance")) + { + L2JMOD_CHAMPION_REWARD_LOWER_LVL_ITEM_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ChampionRewardHigherLvlItemChance")) + { + L2JMOD_CHAMPION_REWARD_HIGHER_LVL_ITEM_CHANCE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ChampionRewardItemID")) + { + L2JMOD_CHAMPION_REWARD_ID = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ChampionRewardItemQty")) + { + L2JMOD_CHAMPION_REWARD_QTY = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("ChampionEnableInInstances")) + { + L2JMOD_CHAMPION_ENABLE_IN_INSTANCES = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AllowWedding")) + { + L2JMOD_ALLOW_WEDDING = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("WeddingPrice")) + { + L2JMOD_WEDDING_PRICE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("WeddingPunishInfidelity")) + { + L2JMOD_WEDDING_PUNISH_INFIDELITY = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("WeddingTeleport")) + { + L2JMOD_WEDDING_TELEPORT = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("WeddingTeleportPrice")) + { + L2JMOD_WEDDING_TELEPORT_PRICE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("WeddingTeleportDuration")) + { + L2JMOD_WEDDING_TELEPORT_DURATION = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("WeddingAllowSameSex")) + { + L2JMOD_WEDDING_SAMESEX = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("WeddingFormalWear")) + { + L2JMOD_WEDDING_FORMALWEAR = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("WeddingDivorceCosts")) + { + L2JMOD_WEDDING_DIVORCE_COSTS = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("TvTEventEnabled")) + { + TVT_EVENT_ENABLED = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("TvTEventInterval")) + { + TVT_EVENT_INTERVAL = pValue.split(","); + } + else if (pName.equalsIgnoreCase("TvTEventParticipationTime")) + { + TVT_EVENT_PARTICIPATION_TIME = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("TvTEventRunningTime")) + { + TVT_EVENT_RUNNING_TIME = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("TvTEventParticipationNpcId")) + { + TVT_EVENT_PARTICIPATION_NPC_ID = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("EnableWarehouseSortingClan")) + { + L2JMOD_ENABLE_WAREHOUSESORTING_CLAN = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("EnableWarehouseSortingPrivate")) + { + L2JMOD_ENABLE_WAREHOUSESORTING_PRIVATE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("EnableManaPotionSupport")) + { + L2JMOD_ENABLE_MANA_POTIONS_SUPPORT = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("DisplayServerTime")) + { + L2JMOD_DISPLAY_SERVER_TIME = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AntiFeedEnable")) + { + L2JMOD_ANTIFEED_ENABLE = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AntiFeedDualbox")) + { + L2JMOD_ANTIFEED_DUALBOX = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AntiFeedDisconnectedAsDualbox")) + { + L2JMOD_ANTIFEED_DISCONNECTED_AS_DUALBOX = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AntiFeedInterval")) + { + L2JMOD_ANTIFEED_INTERVAL = 1000 * Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MinKarma")) + { + KARMA_MIN_KARMA = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("MaxKarma")) + { + KARMA_MAX_KARMA = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("XPDivider")) + { + KARMA_XP_DIVIDER = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("BaseKarmaLost")) + { + KARMA_LOST_BASE = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("CanGMDropEquipment")) + { + KARMA_DROP_GM = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("AwardPKKillPVPPoint")) + { + KARMA_AWARD_PK_KILL = Boolean.parseBoolean(pValue); + } + else if (pName.equalsIgnoreCase("MinimumPKRequiredToDrop")) + { + KARMA_PK_LIMIT = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("PvPVsNormalTime")) + { + PVP_NORMAL_TIME = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("PvPVsPvPTime")) + { + PVP_PVP_TIME = Integer.parseInt(pValue); + } + else if (pName.equalsIgnoreCase("GlobalChat")) + { + DEFAULT_GLOBAL_CHAT = pValue; + } + else if (pName.equalsIgnoreCase("TradeChat")) + { + DEFAULT_TRADE_CHAT = pValue; + } + else if (pName.equalsIgnoreCase("GMAdminMenuStyle")) + { + GM_ADMIN_MENU_STYLE = pValue; + } + else + { + try + { + // TODO: stupid GB configs... + if (!pName.startsWith("Interval_") && !pName.startsWith("Random_")) + { + pName = pName.toUpperCase(); + } + Field clazField = Config.class.getField(pName); + int modifiers = clazField.getModifiers(); + // just in case :) + if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) + { + throw new SecurityException("Cannot modify non public or non static config!"); + } + + if (clazField.getType() == int.class) + { + clazField.setInt(clazField, Integer.parseInt(pValue)); + } + else if (clazField.getType() == short.class) + { + clazField.setShort(clazField, Short.parseShort(pValue)); + } + else if (clazField.getType() == byte.class) + { + clazField.setByte(clazField, Byte.parseByte(pValue)); + } + else if (clazField.getType() == long.class) + { + clazField.setLong(clazField, Long.parseLong(pValue)); + } + else if (clazField.getType() == float.class) + { + clazField.setFloat(clazField, Float.parseFloat(pValue)); + } + else if (clazField.getType() == double.class) + { + clazField.setDouble(clazField, Double.parseDouble(pValue)); + } + else if (clazField.getType() == boolean.class) + { + clazField.setBoolean(clazField, Boolean.parseBoolean(pValue)); + } + else if (clazField.getType() == String.class) + { + clazField.set(clazField, pValue); + } + else + { + return false; + } + } + catch (NoSuchFieldException e) + { + return false; + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + return false; + } + } + return true; + } + + private Config() + { + } + + /** + * Save hexadecimal ID of the server in the L2Properties file. + * @param string (String) : hexadecimal ID of the server to store + * @see HEXID_FILE + * @see saveHexid(String string, String fileName) + * @link LoginServerThread + */ + public static void saveHexid(int serverId, String string) + { + Config.saveHexid(serverId, string, HEXID_FILE); + } + + /** + * Save hexadecimal ID of the server in the L2Properties file. + * @param hexId (String) : hexadecimal ID of the server to store + * @param fileName (String) : name of the L2Properties file + */ + public static void saveHexid(int serverId, String hexId, String fileName) + { + try + { + L2Properties hexSetting = new L2Properties(); + File file = new File(fileName); + // Create a new empty file only if it doesn't exist + file.createNewFile(); + OutputStream out = new FileOutputStream(file); + hexSetting.setProperty("ServerID", String.valueOf(serverId)); + hexSetting.setProperty("HexID", hexId); + hexSetting.store(out, "the hexID to auth into login"); + out.close(); + } + catch (Exception e) + { + _log.warning(StringUtil.concat("Failed to save hex id to ", fileName, " File.")); + e.printStackTrace(); + } + } + + /** + * Loads flood protector configurations. + */ + private static void loadFloodProtectorConfigs(final L2Properties properties) + { + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_USE_ITEM, "UseItem", "4"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_ROLL_DICE, "RollDice", "42"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_FIREWORK, "Firework", "42"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_ITEM_PET_SUMMON, "ItemPetSummon", "16"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_HERO_VOICE, "HeroVoice", "100"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_GLOBAL_CHAT, "GlobalChat", "5"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_SUBCLASS, "Subclass", "20"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_DROP_ITEM, "DropItem", "10"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_SERVER_BYPASS, "ServerBypass", "5"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_MULTISELL, "MultiSell", "1"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_TRANSACTION, "Transaction", "10"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_MANUFACTURE, "Manufacture", "3"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_MANOR, "Manor", "30"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_SENDMAIL, "SendMail", "100"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_CHARACTER_SELECT, "CharacterSelect", "30"); + loadFloodProtectorConfig(properties, FLOOD_PROTECTOR_ITEM_AUCTION, "ItemAuction", "9"); + } + + /** + * Loads single flood protector configuration. + * @param properties L2Properties file reader + * @param config flood protector configuration instance + * @param configString flood protector configuration string that determines for which flood protector configuration should be read + * @param defaultInterval default flood protector interval + */ + private static void loadFloodProtectorConfig(final L2Properties properties, final FloodProtectorConfig config, final String configString, final String defaultInterval) + { + config.FLOOD_PROTECTION_INTERVAL = Integer.parseInt(properties.getProperty(StringUtil.concat("FloodProtector", configString, "Interval"), defaultInterval)); + config.LOG_FLOODING = Boolean.parseBoolean(properties.getProperty(StringUtil.concat("FloodProtector", configString, "LogFlooding"), "False")); + config.PUNISHMENT_LIMIT = Integer.parseInt(properties.getProperty(StringUtil.concat("FloodProtector", configString, "PunishmentLimit"), "0")); + config.PUNISHMENT_TYPE = properties.getProperty(StringUtil.concat("FloodProtector", configString, "PunishmentType"), "none"); + config.PUNISHMENT_TIME = Integer.parseInt(properties.getProperty(StringUtil.concat("FloodProtector", configString, "PunishmentTime"), "0")); + } + + public static int getServerTypeId(String[] serverTypes) + { + int tType = 0; + for (String cType : serverTypes) + { + cType = cType.trim(); + if (cType.equalsIgnoreCase("Normal")) + { + tType |= 0x01; + } + else if (cType.equalsIgnoreCase("Relax")) + { + tType |= 0x02; + } + else if (cType.equalsIgnoreCase("Test")) + { + tType |= 0x04; + } + else if (cType.equalsIgnoreCase("NoLabel")) + { + tType |= 0x08; + } + else if (cType.equalsIgnoreCase("Restricted")) + { + tType |= 0x10; + } + else if (cType.equalsIgnoreCase("Event")) + { + tType |= 0x20; + } + else if (cType.equalsIgnoreCase("Free")) + { + tType |= 0x40; + } + } + return tType; + } + + public static class ClassMasterSettings + { + private final TIntObjectHashMap _claimItems; + private final TIntObjectHashMap _rewardItems; + private final TIntObjectHashMap _allowedClassChange; + + public ClassMasterSettings(String _configLine) + { + _claimItems = new TIntObjectHashMap<>(3); + _rewardItems = new TIntObjectHashMap<>(3); + _allowedClassChange = new TIntObjectHashMap<>(3); + if (_configLine != null) + { + parseConfigLine(_configLine.trim()); + } + } + + private void parseConfigLine(String _configLine) + { + StringTokenizer st = new StringTokenizer(_configLine, ";"); + + while (st.hasMoreTokens()) + { + // get allowed class change + int job = Integer.parseInt(st.nextToken()); + + _allowedClassChange.put(job, true); + + TIntIntHashMap _items = new TIntIntHashMap(); + // parse items needed for class change + if (st.hasMoreTokens()) + { + StringTokenizer st2 = new StringTokenizer(st.nextToken(), "[],"); + + while (st2.hasMoreTokens()) + { + StringTokenizer st3 = new StringTokenizer(st2.nextToken(), "()"); + int _itemId = Integer.parseInt(st3.nextToken()); + int _quantity = Integer.parseInt(st3.nextToken()); + _items.put(_itemId, _quantity); + } + } + + _claimItems.put(job, _items); + + _items = new TIntIntHashMap(); + // parse gifts after class change + if (st.hasMoreTokens()) + { + StringTokenizer st2 = new StringTokenizer(st.nextToken(), "[],"); + + while (st2.hasMoreTokens()) + { + StringTokenizer st3 = new StringTokenizer(st2.nextToken(), "()"); + int _itemId = Integer.parseInt(st3.nextToken()); + int _quantity = Integer.parseInt(st3.nextToken()); + _items.put(_itemId, _quantity); + } + } + + _rewardItems.put(job, _items); + } + } + + public boolean isAllowed(int job) + { + if (_allowedClassChange == null) + { + return false; + } + if (_allowedClassChange.containsKey(job)) + { + return _allowedClassChange.get(job); + } + + return false; + } + + public TIntIntHashMap getRewardItems(int job) + { + if (_rewardItems.containsKey(job)) + { + return _rewardItems.get(job); + } + + return null; + } + + public TIntIntHashMap getRequireItems(int job) + { + if (_claimItems.containsKey(job)) + { + return _claimItems.get(job); + } + + return null; + } + } + + private static TIntFloatHashMap parseConfigLine(String line) + { + String[] propertySplit = line.split(","); + TIntFloatHashMap ret = new TIntFloatHashMap(propertySplit.length); + int i = 1; + for (String value : propertySplit) + { + ret.put(i++, Float.parseFloat(value)); + } + return ret; + } + + /** + * itemId1,itemNumber1;itemId2,itemNumber2... to the int[n][2] = [itemId1][itemNumber1],[itemId2][itemNumber2]... + */ + private static int[][] parseItemsList(String line) + { + final String[] propertySplit = line.split(";"); + if (propertySplit.length == 0) + { + return null; + } + + int i = 0; + String[] valueSplit; + final int[][] result = new int[propertySplit.length][]; + for (String value : propertySplit) + { + valueSplit = value.split(","); + if (valueSplit.length != 2) + { + _log.warning(StringUtil.concat("parseItemsList[Config.load()]: invalid entry -> \"", valueSplit[0], "\", should be itemId,itemNumber")); + return null; + } + + result[i] = new int[2]; + try + { + result[i][0] = Integer.parseInt(valueSplit[0]); + } + catch (NumberFormatException e) + { + _log.warning(StringUtil.concat("parseItemsList[Config.load()]: invalid itemId -> \"", valueSplit[0], "\"")); + return null; + } + try + { + result[i][1] = Integer.parseInt(valueSplit[1]); + } + catch (NumberFormatException e) + { + _log.warning(StringUtil.concat("parseItemsList[Config.load()]: invalid item number -> \"", valueSplit[1], "\"")); + return null; + } + i++; + } + return result; + } +} diff --git a/L2J_Server/java/com/l2jserver/L2DatabaseFactory.java b/L2J_Server/java/com/l2jserver/L2DatabaseFactory.java new file mode 100644 index 00000000..88741a26 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/L2DatabaseFactory.java @@ -0,0 +1,336 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.mchange.v2.c3p0.ComboPooledDataSource; + +public class L2DatabaseFactory +{ + static Logger _log = Logger.getLogger(L2DatabaseFactory.class.getName()); + + public static enum ProviderType + { + MySql, + MsSql + } + + // ========================================================= + // Data Field + private static L2DatabaseFactory _instance; + private static ScheduledExecutorService _executor; + private ProviderType _providerType; + private ComboPooledDataSource _source; + + // ========================================================= + // Constructor + public L2DatabaseFactory() throws SQLException + { + try + { + if (Config.DATABASE_MAX_CONNECTIONS < 2) + { + Config.DATABASE_MAX_CONNECTIONS = 2; + _log.warning("A minimum of " + Config.DATABASE_MAX_CONNECTIONS + " db connections are required."); + } + + _source = new ComboPooledDataSource(); + _source.setAutoCommitOnClose(true); + + _source.setInitialPoolSize(10); + _source.setMinPoolSize(10); + _source.setMaxPoolSize(Math.max(10, Config.DATABASE_MAX_CONNECTIONS)); + + _source.setAcquireRetryAttempts(0); // try to obtain connections indefinitely (0 = never quit) + _source.setAcquireRetryDelay(500); // 500 milliseconds wait before try to acquire connection again + _source.setCheckoutTimeout(0); // 0 = wait indefinitely for new connection + // if pool is exhausted + _source.setAcquireIncrement(5); // if pool is exhausted, get 5 more connections at a time + // cause there is a "long" delay on acquire connection + // so taking more than one connection at once will make connection pooling + // more effective. + + // this "connection_test_table" is automatically created if not already there + _source.setAutomaticTestTable("connection_test_table"); + _source.setTestConnectionOnCheckin(false); + + // testing OnCheckin used with IdleConnectionTestPeriod is faster than testing on checkout + + _source.setIdleConnectionTestPeriod(3600); // test idle connection every 60 sec + _source.setMaxIdleTime(Config.DATABASE_MAX_IDLE_TIME); // 0 = idle connections never expire + // *THANKS* to connection testing configured above + // but I prefer to disconnect all connections not used + // for more than 1 hour + + // enables statement caching, there is a "semi-bug" in c3p0 0.9.0 but in 0.9.0.2 and later it's fixed + _source.setMaxStatementsPerConnection(100); + + _source.setBreakAfterAcquireFailure(false); // never fail if any way possible + // setting this to true will make + // c3p0 "crash" and refuse to work + // till restart thus making acquire + // errors "FATAL" ... we don't want that + // it should be possible to recover + _source.setDriverClass(Config.DATABASE_DRIVER); + _source.setJdbcUrl(Config.DATABASE_URL); + _source.setUser(Config.DATABASE_LOGIN); + _source.setPassword(Config.DATABASE_PASSWORD); + + /* Test the connection */ + _source.getConnection().close(); + + if (Config.DEBUG) + { + _log.fine("Database Connection Working"); + } + + if (Config.DATABASE_DRIVER.toLowerCase().contains("microsoft")) + { + _providerType = ProviderType.MsSql; + } + else + { + _providerType = ProviderType.MySql; + } + } + catch (SQLException x) + { + if (Config.DEBUG) + { + _log.fine("Database Connection FAILED"); + } + // re-throw the exception + throw x; + } + catch (Exception e) + { + if (Config.DEBUG) + { + _log.fine("Database Connection FAILED"); + } + throw new SQLException("Could not init DB connection:" + e.getMessage()); + } + } + + // ========================================================= + // Method - Public + public final String prepQuerySelect(String[] fields, String tableName, String whereClause, boolean returnOnlyTopRecord) + { + String msSqlTop1 = ""; + String mySqlTop1 = ""; + if (returnOnlyTopRecord) + { + if (getProviderType() == ProviderType.MsSql) + { + msSqlTop1 = " Top 1 "; + } + if (getProviderType() == ProviderType.MySql) + { + mySqlTop1 = " Limit 1 "; + } + } + String query = "SELECT " + msSqlTop1 + safetyString(fields) + " FROM " + tableName + " WHERE " + whereClause + mySqlTop1; + return query; + } + + public void shutdown() + { + try + { + _source.close(); + } + catch (Exception e) + { + _log.log(Level.INFO, "", e); + } + try + { + _source = null; + } + catch (Exception e) + { + _log.log(Level.INFO, "", e); + } + } + + public final String safetyString(String... whatToCheck) + { + // NOTE: Use brace as a safty precaution just incase name is a reserved word + final char braceLeft; + final char braceRight; + + if (getProviderType() == ProviderType.MsSql) + { + braceLeft = '['; + braceRight = ']'; + } + else + { + braceLeft = '`'; + braceRight = '`'; + } + + int length = 0; + + for (String word : whatToCheck) + { + length += word.length() + 4; + } + + final StringBuilder sbResult = new StringBuilder(length); + + for (String word : whatToCheck) + { + if (sbResult.length() > 0) + { + sbResult.append(", "); + } + + sbResult.append(braceLeft); + sbResult.append(word); + sbResult.append(braceRight); + } + + return sbResult.toString(); + } + + // ========================================================= + // Property - Public + public static L2DatabaseFactory getInstance() throws SQLException + { + synchronized (L2DatabaseFactory.class) + { + if (_instance == null) + { + _instance = new L2DatabaseFactory(); + } + } + return _instance; + } + + public Connection getConnection() // throws SQLException + { + Connection con = null; + + while (con == null) + { + try + { + con = _source.getConnection(); + if (Server.serverMode == Server.MODE_GAMESERVER) + { + ThreadPoolManager.getInstance().scheduleGeneral(new ConnectionCloser(con, new RuntimeException()), Config.CONNECTION_CLOSE_TIME); + } + else + { + getExecutor().schedule(new ConnectionCloser(con, new RuntimeException()), 60, TimeUnit.SECONDS); + } + } + catch (SQLException e) + { + _log.log(Level.WARNING, "L2DatabaseFactory: getConnection() failed, trying again " + e.getMessage(), e); + } + } + return con; + } + + private static class ConnectionCloser implements Runnable + { + private final Connection c; + private final RuntimeException exp; + + public ConnectionCloser(Connection con, RuntimeException e) + { + c = con; + exp = e; + } + + /* + * (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() + { + try + { + if (!c.isClosed()) + { + _log.log(Level.WARNING, "Unclosed connection! Trace: " + exp.getStackTrace()[1], exp); + } + } + catch (SQLException e) + { + _log.log(Level.WARNING, "", e); + } + + } + } + + public static void close(Connection con) + { + if (con == null) + { + return; + } + + try + { + con.close(); + } + catch (SQLException e) + { + _log.log(Level.WARNING, "Failed to close database connection!", e); + } + } + + private static ScheduledExecutorService getExecutor() + { + if (_executor == null) + { + synchronized (L2DatabaseFactory.class) + { + if (_executor == null) + { + _executor = Executors.newSingleThreadScheduledExecutor(); + } + } + } + return _executor; + } + + public int getBusyConnectionCount() throws SQLException + { + return _source.getNumBusyConnectionsDefaultUser(); + } + + public int getIdleConnectionCount() throws SQLException + { + return _source.getNumIdleConnectionsDefaultUser(); + } + + public final ProviderType getProviderType() + { + return _providerType; + } +} diff --git a/L2J_Server/java/com/l2jserver/Server.java b/L2J_Server/java/com/l2jserver/Server.java new file mode 100644 index 00000000..1b3721b1 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/Server.java @@ -0,0 +1,28 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver; + +/** + * This class used to be the starter class, since LS/GS split, it only retains server mode + */ +public class Server +{ + // constants for the server mode + public static final int MODE_NONE = 0; + public static final int MODE_GAMESERVER = 1; + public static final int MODE_LOGINSERVER = 2; + + public static int serverMode = MODE_NONE; +} diff --git a/L2J_Server/java/com/l2jserver/accountmanager/SQLAccountManager.java b/L2J_Server/java/com/l2jserver/accountmanager/SQLAccountManager.java new file mode 100644 index 00000000..e87e0d92 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/accountmanager/SQLAccountManager.java @@ -0,0 +1,475 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.accountmanager; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Base64; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.Server; + +/** + * This class SQL Account Manager + * @author netimperia + * @version $Revision: 2.3.2.1.2.3 $ $Date: 2005/08/08 22:47:12 $ + */ +public class SQLAccountManager +{ + private static String _uname = ""; + private static String _pass = ""; + private static String _level = ""; + private static String _mode = ""; + + public static void main(String[] args) throws SQLException, IOException, NoSuchAlgorithmException + { + Server.serverMode = Server.MODE_LOGINSERVER; + Config.load(); + while (true) + { + System.out.println("Please choose an option:"); + System.out.println(""); + System.out.println("1 - Create new account or update existing one (change pass and access level)."); + System.out.println("2 - Change access level."); + System.out.println("3 - Delete existing account."); + System.out.println("4 - List accounts & access levels."); + System.out.println("5 - Exit."); + LineNumberReader _in = new LineNumberReader(new InputStreamReader(System.in)); + while (!(_mode.equals("1") || _mode.equals("2") || _mode.equals("3") || _mode.equals("4") || _mode.equals("5"))) + { + System.out.print("Your choice: "); + _mode = _in.readLine(); + } + + if (_mode.equals("1") || _mode.equals("2") || _mode.equals("3")) + { + if (_mode.equals("1") || _mode.equals("2")) + { + while (_uname.trim().length() == 0) + { + System.out.print("Username: "); + _uname = _in.readLine().toLowerCase(); + } + } + else if (_mode.equals("3")) + { + while (_uname.trim().length() == 0) + { + System.out.print("Account name: "); + _uname = _in.readLine().toLowerCase(); + } + } + if (_mode.equals("1")) + { + while (_pass.trim().length() == 0) + { + System.out.print("Password: "); + _pass = _in.readLine(); + } + } + if (_mode.equals("1") || _mode.equals("2")) + { + while (_level.trim().length() == 0) + { + System.out.print("Access level: "); + _level = _in.readLine(); + } + } + } + + if (_mode.equals("1")) + { + // Add or Update + addOrUpdateAccount(_uname.trim(), _pass.trim(), _level.trim()); + } + else if (_mode.equals("2")) + { + // Change Level + changeAccountLevel(_uname.trim(), _level.trim()); + } + else if (_mode.equals("3")) + { + // Delete + System.out.print("Do you really want to delete this account ? Y/N : "); + String yesno = _in.readLine(); + if (yesno.equalsIgnoreCase("Y")) + { + deleteAccount(_uname.trim()); + } + else + { + System.out.println("Deletion cancelled"); + } + } + else if (_mode.equals("4")) + { + // List + _mode = ""; + System.out.println(""); + System.out.println("Please choose a listing mode:"); + System.out.println(""); + System.out.println("1 - Banned accounts only (accessLevel < 0)"); + System.out.println("2 - GM/privileged accounts (accessLevel > 0)"); + System.out.println("3 - Regular accounts only (accessLevel = 0)"); + System.out.println("4 - List all"); + while (!(_mode.equals("1") || _mode.equals("2") || _mode.equals("3") || _mode.equals("4"))) + { + System.out.print("Your choice: "); + _mode = _in.readLine(); + } + System.out.println(""); + printAccInfo(_mode); + } + else if (_mode.equals("5")) + { + System.exit(0); + } + + _uname = ""; + _pass = ""; + _level = ""; + _mode = ""; + System.out.println(); + } + } + + private static void printAccInfo(String m) throws SQLException + { + int count = 0; + Connection con = null; + con = L2DatabaseFactory.getInstance().getConnection(); + String q = "SELECT login, accessLevel FROM accounts "; + if (m.equals("1")) + { + q = q.concat("WHERE accessLevel < 0"); + } + else if (m.equals("2")) + { + q = q.concat("WHERE accessLevel > 0"); + } + else if (m.equals("3")) + { + q = q.concat("WHERE accessLevel = 0"); + } + q = q.concat(" ORDER BY login ASC"); + + PreparedStatement statement = con.prepareStatement(q); + ResultSet rset = statement.executeQuery(); + while (rset.next()) + { + System.out.println(rset.getString("login") + " -> " + rset.getInt("accessLevel")); + count++; + } + rset.close(); + statement.close(); + L2DatabaseFactory.close(con); + System.out.println("Displayed accounts: " + count + "."); + } + + private static void addOrUpdateAccount(String account, String password, String level) throws IOException, SQLException, NoSuchAlgorithmException + { + // Encode Password + MessageDigest md = MessageDigest.getInstance("SHA"); + byte[] newpass; + newpass = password.getBytes("UTF-8"); + newpass = md.digest(newpass); + + // Add to Base + Connection con = null; + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("REPLACE accounts (login, password, accessLevel) VALUES (?,?,?)"); + statement.setString(1, account); + statement.setString(2, Base64.getEncoder().encodeToString(newpass)); + statement.setString(3, level); + statement.executeUpdate(); + statement.close(); + L2DatabaseFactory.close(con); + } + + private static void changeAccountLevel(String account, String level) throws SQLException + { + Connection con = null; + con = L2DatabaseFactory.getInstance().getConnection(); + + // Check Account Exist + PreparedStatement statement = con.prepareStatement("SELECT COUNT(*) FROM accounts WHERE login=?;"); + statement.setString(1, account); + ResultSet rset = statement.executeQuery(); + if (!rset.next()) + { + System.out.println("False"); + } + else if (rset.getInt(1) > 0) + { + // Exist + // Update + statement = con.prepareStatement("UPDATE accounts SET accessLevel=? WHERE login=?;"); + statement.setEscapeProcessing(true); + statement.setString(1, level); + statement.setString(2, account); + statement.executeUpdate(); + + System.out.println("Account " + account + " has been updated."); + } + else + { + // Not Exist + System.out.println("Account " + account + " does not exist."); + } + rset.close(); + statement.close(); + L2DatabaseFactory.close(con); + } + + private static void deleteAccount(String account) throws SQLException + { + Connection con = null; + con = L2DatabaseFactory.getInstance().getConnection(); + + // Check Account Exist + PreparedStatement statement = con.prepareStatement("SELECT COUNT(*) FROM accounts WHERE login=?;"); + statement.setString(1, account); + ResultSet rset = statement.executeQuery(); + if (!rset.next()) + { + System.out.println("False"); + rset.close(); + } + else if (rset.getInt(1) > 0) + { + rset.close(); + // Account exist + // Get Accounts ID + ResultSet rcln; + statement = con.prepareStatement("SELECT charId, char_name, clanid FROM characters WHERE account_name=?;"); + statement.setEscapeProcessing(true); + statement.setString(1, account); + rset = statement.executeQuery(); + + FastList objIds = new FastList<>(); + FastList charNames = new FastList<>(); + FastList clanIds = new FastList<>(); + + while (rset.next()) + { + objIds.add(rset.getString("charId")); + charNames.add(rset.getString("char_name")); + clanIds.add(rset.getString("clanid")); + } + rset.close(); + + for (int index = 0; index < objIds.size(); index++) + { + System.out.println("Deleting character " + charNames.get(index) + "."); + + // Check If clan leader Remove Clan and remove all from it + statement.close(); + statement = con.prepareStatement("SELECT COUNT(*) FROM clan_data WHERE leader_id=?;"); + statement.setString(1, clanIds.get(index)); + rcln = statement.executeQuery(); + rcln.next(); + if (rcln.getInt(1) > 0) + { + rcln.close(); + // Clan Leader + + // Get Clan Name + statement.close(); + statement = con.prepareStatement("SELECT clan_name FROM clan_data WHERE leader_id=?;"); + statement.setString(1, clanIds.get(index)); + rcln = statement.executeQuery(); + rcln.next(); + + String clanName = rcln.getString("clan_name"); + + System.out.println("Deleting clan " + clanName + "."); + + // Delete Clan Wars + statement.close(); + statement = con.prepareStatement("DELETE FROM clan_wars WHERE clan1=? OR clan2=?;"); + statement.setEscapeProcessing(true); + statement.setString(1, clanName); + statement.setString(2, clanName); + statement.executeUpdate(); + + rcln.close(); + + // Remove All From clan + statement.close(); + statement = con.prepareStatement("UPDATE characters SET clanid=0 WHERE clanid=?;"); + statement.setString(1, clanIds.get(index)); + statement.executeUpdate(); + // Free Clan Halls + statement.close(); + statement = con.prepareStatement("UPDATE clanhall SET ownerId=0, paidUntil=0, paid=0 WHERE ownerId=?;"); + statement.setString(1, clanIds.get(index)); + statement.executeUpdate(); + // Delete Clan + statement.close(); + statement = con.prepareStatement("DELETE FROM clan_data WHERE clan_id=?;"); + statement.setString(1, clanIds.get(index)); + statement.executeUpdate(); + // Clan privileges + statement.close(); + statement = con.prepareStatement("DELETE FROM clan_privs WHERE clan_id=?;"); + statement.setString(1, clanIds.get(index)); + statement.executeUpdate(); + // Clan subpledges + statement.close(); + statement = con.prepareStatement("DELETE FROM clan_subpledges WHERE clan_id=?;"); + statement.setString(1, clanIds.get(index)); + statement.executeUpdate(); + // Clan skills + statement.close(); + statement = con.prepareStatement("DELETE FROM clan_skills WHERE clan_id=?;"); + statement.setString(1, clanIds.get(index)); + statement.executeUpdate(); + + } + else + { + rcln.close(); + } + + // skills + statement.close(); + statement = con.prepareStatement("DELETE FROM character_skills WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // skills save + statement.close(); + statement = con.prepareStatement("DELETE FROM character_skills_save WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // subclasses + statement.close(); + statement = con.prepareStatement("DELETE FROM character_subclasses WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // shortcuts + statement.close(); + statement = con.prepareStatement("DELETE FROM character_shortcuts WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // items + statement.close(); + statement = con.prepareStatement("DELETE FROM items WHERE owner_id=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // recipebook + statement.close(); + statement = con.prepareStatement("DELETE FROM character_recipebook WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // quests + statement.close(); + statement = con.prepareStatement("DELETE FROM character_quests WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // macroses + statement.close(); + statement = con.prepareStatement("DELETE FROM character_macroses WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // friends + statement.close(); + statement = con.prepareStatement("DELETE FROM character_friends WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // merchant_lease + statement.close(); + statement = con.prepareStatement("DELETE FROM merchant_lease WHERE player_id=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // boxaccess + statement.close(); + statement = con.prepareStatement("DELETE FROM boxaccess WHERE charname=?;"); + statement.setString(1, charNames.get(index)); + statement.executeUpdate(); + + // hennas + statement.close(); + statement = con.prepareStatement("DELETE FROM character_hennas WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // recommends + statement.close(); + statement = con.prepareStatement("DELETE FROM character_reco_bonus WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // ui categories + statement.close(); + statement = con.prepareStatement("DELETE FROM character_ui_categories WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // ui keys + statement.close(); + statement = con.prepareStatement("DELETE FROM character_ui_keys WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // characters + statement.close(); + statement = con.prepareStatement("DELETE FROM characters WHERE charId=?;"); + statement.setString(1, objIds.get(index)); + statement.executeUpdate(); + + // TODO: delete pets, olympiad/noble/hero stuff + } + + // Delete Account + statement.close(); + statement = con.prepareStatement("DELETE FROM accounts WHERE login=?;"); + statement.setEscapeProcessing(true); + statement.setString(1, account); + statement.executeUpdate(); + + System.out.println("Account " + account + " has been deleted."); + } + else + { + // Not Exist + System.out.println("Account " + account + " does not exist."); + } + + // Close Connection + statement.close(); + L2DatabaseFactory.close(con); + } + +} diff --git a/L2J_Server/java/com/l2jserver/configurator/ConfigUserInterface.java b/L2J_Server/java/com/l2jserver/configurator/ConfigUserInterface.java new file mode 100644 index 00000000..d8add5e8 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/configurator/ConfigUserInterface.java @@ -0,0 +1,771 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.configurator; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; +import java.util.Locale; +import java.util.NoSuchElementException; +import java.util.ResourceBundle; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTabbedPane; +import javax.swing.JTextArea; +import javax.swing.JToolBar; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.ToolTipManager; +import javax.swing.UIManager; + +import javolution.util.FastList; + +import com.l2jserver.configurator.ConfigUserInterface.ConfigFile.ConfigComment; +import com.l2jserver.configurator.ConfigUserInterface.ConfigFile.ConfigProperty; +import com.l2jserver.i18n.LanguageControl; +import com.l2jserver.images.ImagesTable; + +/** + * @author KenM + */ +public class ConfigUserInterface extends JFrame implements ActionListener +{ + + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = 1L; + + private final JTabbedPane _tabPane = new JTabbedPane(); + + private List _configs = new FastList<>(); + + private ResourceBundle _bundle; + + /** + * @param args + */ + public static void main(String[] args) + { + try + { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch (Exception e) + { + // couldn't care less + } + + final ResourceBundle bundle = ResourceBundle.getBundle("configurator.Configurator", Locale.getDefault(), LanguageControl.INSTANCE); + + SwingUtilities.invokeLater(() -> + { + ConfigUserInterface cui = new ConfigUserInterface(bundle); + cui.setVisible(true); + }); + } + + public ConfigUserInterface(ResourceBundle bundle) + { + setBundle(bundle); + setTitle(bundle.getString("toolName")); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setSize(750, 500); + setLayout(new GridBagLayout()); + + setDefaultLookAndFeelDecorated(true); + setIconImage(ImagesTable.getImage("l2j.png").getImage()); + + GridBagConstraints cons = new GridBagConstraints(); + cons.fill = GridBagConstraints.HORIZONTAL; + cons.gridx = 0; + cons.gridy = 0; + cons.weighty = 0; + cons.weightx = 1; + + JMenuBar menubar = new JMenuBar(); + + JMenu fileMenu = new JMenu(bundle.getString("fileMenu")); + JMenu helpMenu = new JMenu(bundle.getString("helpMenu")); + + JMenuItem exitItem = new JMenuItem(bundle.getString("exitItem")); + exitItem.setActionCommand("exit"); + exitItem.addActionListener(this); + fileMenu.add(exitItem); + + JMenuItem aboutItem = new JMenuItem(bundle.getString("aboutItem")); + aboutItem.setActionCommand("about"); + aboutItem.addActionListener(this); + helpMenu.add(aboutItem); + + menubar.add(fileMenu); + menubar.add(helpMenu); + + setJMenuBar(menubar); + + JToolBar toolBar = new JToolBar(); + toolBar.setFloatable(false); + toolBar.setRollover(true); + toolBar.add(createToolButton("disk.png", bundle.getString("save"), "save")); + this.add(toolBar, cons); + + cons.gridy++; + cons.fill = GridBagConstraints.BOTH; + cons.weighty = 1; + loadConfigs(); + buildInterface(); + this.add(_tabPane, cons); + } + + private JButton createToolButton(String image, String text, String action) + { + JButton button = new JButton(text, ImagesTable.getImage(image)); + button.setActionCommand(action); + button.addActionListener(this); + return button; + } + + private void buildInterface() + { + ToolTipManager.sharedInstance().setDismissDelay(Integer.MAX_VALUE); + ToolTipManager.sharedInstance().setInitialDelay(0); + ToolTipManager.sharedInstance().setReshowDelay(0); + + GridBagConstraints cons = new GridBagConstraints(); + cons.fill = GridBagConstraints.NONE; + cons.anchor = GridBagConstraints.FIRST_LINE_START; + cons.insets = new Insets(2, 2, 2, 2); + for (ConfigFile cf : getConfigs()) + { + JPanel panel = new JPanel() + { + /** + * + */ + private static final long serialVersionUID = -6580868086294343672L; + + @Override + public void scrollRectToVisible(Rectangle r) + { + } + }; + panel.setLayout(new GridBagLayout()); + + cons.gridy = 0; + cons.weighty = 0; + for (ConfigComment cc : cf.getConfigProperties()) + { + if (!(cc instanceof ConfigProperty)) + { + continue; + } + + ConfigProperty cp = (ConfigProperty) cc; + cons.gridx = 0; + + JLabel keyLabel = new JLabel(cp.getDisplayName() + ':', ImagesTable.getImage("help.png"), SwingConstants.LEFT); + String comments = "" + cp.getName() + ":
" + cp.getComments(); + comments = comments.replace("\r\n", "
"); + comments = "" + comments + ""; + keyLabel.setToolTipText(comments); + cons.weightx = 0; + panel.add(keyLabel, cons); + cons.gridx++; + + JComponent valueComponent = cp.getValueComponent(); + valueComponent.setToolTipText(comments); + cons.weightx = 1; + panel.add(valueComponent, cons); + cons.gridx++; + cons.gridy++; + } + cons.gridy++; + cons.weighty = 1; + panel.add(new JLabel(), cons); // filler + _tabPane.addTab(cf.getName(), new JScrollPane(panel)); + } + } + + /** + * + */ + private void loadConfigs() + { + File configsDir = new File("config"); + for (File file : configsDir.listFiles()) + { + if (file.getName().endsWith(".properties") && file.isFile() && file.canWrite()) + { + try + { + parsePropertiesFile(file); + } + catch (IOException e) + { + JOptionPane.showMessageDialog(ConfigUserInterface.this, getBundle().getString("errorReading") + file.getName(), getBundle().getString("error"), JOptionPane.ERROR_MESSAGE); + System.exit(3); + // e.printStackTrace(); + } + } + } + } + + /** + * @param file + * @throws IOException + */ + private void parsePropertiesFile(File file) throws IOException + { + LineNumberReader lnr = new LineNumberReader(new InputStreamReader(new FileInputStream(file))); + + String line; + StringBuilder commentBuffer = new StringBuilder(); + ConfigFile cf = new ConfigFile(file); + while ((line = lnr.readLine()) != null) + { + line = line.trim(); + + if (line.startsWith("#")) + { + if (commentBuffer.length() > 0) + { + commentBuffer.append("\r\n"); + } + commentBuffer.append(line.substring(1)); + } + else if (line.length() == 0) + { + // blank line, reset comments + if (commentBuffer.length() > 0) + { + cf.addConfigComment(commentBuffer.toString()); + } + commentBuffer.setLength(0); + } + else if (line.indexOf('=') >= 0) + { + String[] kv = line.split("="); + String key = kv[0].trim(); + String value = ""; + if (kv.length > 1) + { + value = kv[1].trim(); + } + + if (line.indexOf('\\') >= 0) + { + while (((line = lnr.readLine()) != null) && (line.indexOf('\\') >= 0)) + { + value += "\r\n" + line; + } + value += "\r\n" + line; + } + + String comments = commentBuffer.toString(); + commentBuffer.setLength(0); // reset + + cf.addConfigProperty(key, parseValue(value), comments); + } + } + getConfigs().add(cf); + lnr.close(); + } + + /** + * @param value + */ + private Object parseValue(String value) + { + if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("true")) + { + return Boolean.parseBoolean(value); + } + + /* + * try { double parseDouble = Double.parseDouble(value); return parseDouble; } catch (NumberFormatException e) { // not a double, ignore } + */ + + // localhost -> 127.0.0.1 + if (value.equals("localhost")) + { + value = "127.0.0.1"; + } + + String[] parts = value.split("\\."); + if (parts.length == 4) + { + boolean ok = true; + for (int i = 0; (i < 4) && ok; i++) + { + try + { + int parseInt = Integer.parseInt(parts[i]); + if ((parseInt < 0) || (parseInt > 255)) + { + ok = false; + } + } + catch (NumberFormatException e) + { + ok = false; + } + } + + if (ok) + { + try + { + InetAddress address = InetAddress.getByName(value); + return address; + } + catch (UnknownHostException e) + { + // ignore + } + } + } + + return value; + } + + static class ConfigFile + { + private final File _file; + private String _name; + private final List _configs = new FastList<>(); + + public ConfigFile(File file) + { + _file = file; + int lastIndex = file.getName().lastIndexOf('.'); + setName(file.getName().substring(0, lastIndex)); + } + + public void addConfigProperty(String name, Object value, ValueType type, String comments) + { + _configs.add(new ConfigProperty(name, value, type, comments)); + } + + public void addConfigComment(String comment) + { + _configs.add(new ConfigComment(comment)); + } + + public void addConfigProperty(String name, Object value, String comments) + { + this.addConfigProperty(name, value, ValueType.firstTypeMatch(value), comments); + } + + public List getConfigProperties() + { + return _configs; + } + + /** + * @param name The name to set. + */ + public void setName(String name) + { + _name = name; + } + + /** + * @return Returns the name. + */ + public String getName() + { + return _name; + } + + public void save() throws IOException + { + BufferedWriter bufWriter = null; + try + { + bufWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(_file))); + for (ConfigComment cc : _configs) + { + cc.save(bufWriter); + } + } + finally + { + if (bufWriter != null) + { + bufWriter.close(); + } + } + } + + class ConfigComment + { + + private String _comments; + + /** + * @param comments + */ + public ConfigComment(String comments) + { + _comments = comments; + } + + /** + * @return Returns the comments. + */ + public String getComments() + { + return _comments; + } + + /** + * @param comments The comments to set. + */ + public void setComments(String comments) + { + _comments = comments; + } + + public void save(Writer writer) throws IOException + { + StringBuilder sb = new StringBuilder(); + sb.append('#'); + sb.append(getComments().replace("\r\n", "\r\n#")); + sb.append("\r\n\r\n"); + writer.write(sb.toString()); + } + } + + class ConfigProperty extends ConfigComment + { + private String _propname; + private Object _value; + private ValueType _type; + private JComponent _component; + + /** + * @param name + * @param value + * @param type + * @param comments + */ + public ConfigProperty(String name, Object value, ValueType type, String comments) + { + super(comments); + if (!type.getType().isAssignableFrom(value.getClass())) + { + throw new IllegalArgumentException("Value Instance Type doesn't match the type argument."); + } + _propname = name; + _type = type; + _value = value; + } + + /** + * @return Returns the name. + */ + public String getName() + { + return _propname; + } + + /** + * @return Returns the name. + */ + public String getDisplayName() + { + return unCamelize(_propname); + } + + /** + * @param name The name to set. + */ + public void setName(String name) + { + _propname = name; + } + + /** + * @return Returns the value. + */ + public Object getValue() + { + return _value; + } + + /** + * @param value The value to set. + */ + public void setValue(String value) + { + _value = value; + } + + /** + * @return Returns the type. + */ + public ValueType getType() + { + return _type; + } + + /** + * @param type The type to set. + */ + public void setType(ValueType type) + { + _type = type; + } + + public JComponent getValueComponent() + { + if (_component == null) + { + _component = createValueComponent(); + } + return _component; + } + + public JComponent createValueComponent() + { + switch (getType()) + { + case BOOLEAN: + boolean bool = (Boolean) getValue(); + JCheckBox checkBox = new JCheckBox(); + checkBox.setSelected(bool); + return checkBox; + case IPv4: + return new JIPTextField((Inet4Address) getValue()); + case DOUBLE: + case INTEGER: + case STRING: + default: + String val = getValue().toString(); + JTextArea textArea = new JTextArea(val); + textArea.setFont(UIManager.getFont("TextField.font")); + int rows = 1; + for (int i = 0; i < val.length(); i++) + { + if (val.charAt(i) == '\\') + { + rows++; + } + } + textArea.setRows(rows); + textArea.setColumns(Math.max(val.length() / rows, 20)); + return textArea; + } + } + + @Override + public void save(Writer writer) throws IOException + { + String value; + if (getValueComponent() instanceof JCheckBox) + { + value = Boolean.toString(((JCheckBox) getValueComponent()).isSelected()); + value = value.substring(0, 1).toUpperCase() + value.substring(1); + } + else if (getValueComponent() instanceof JIPTextField) + { + value = ((JIPTextField) getValueComponent()).getText(); + } + else if (getValueComponent() instanceof JTextArea) + { + value = ((JTextArea) getValueComponent()).getText(); + } + else + { + throw new IllegalStateException("Unhandled component value"); + } + + StringBuilder sb = new StringBuilder(); + sb.append('#'); + sb.append(getComments().replace("\r\n", "\r\n#")); + sb.append("\r\n"); + sb.append(getName()); + sb.append(" = "); + sb.append(value); + sb.append("\r\n"); + sb.append("\r\n"); + writer.write(sb.toString()); + + } + } + } + + public static enum ValueType + { + BOOLEAN(Boolean.class), + DOUBLE(Double.class), + INTEGER(Integer.class), + IPv4(Inet4Address.class), + STRING(String.class); + + private final Class _type; + + private ValueType(Class type) + { + _type = type; + } + + /** + * @return Returns the type. + */ + public Class getType() + { + return _type; + } + + public static ValueType firstTypeMatch(Object value) + { + for (ValueType vt : ValueType.values()) + { + if (vt.getType() == value.getClass()) + { + return vt; + } + } + throw new NoSuchElementException("No match for: " + value.getClass().getName()); + } + } + + /** + * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) + { + String cmd = e.getActionCommand(); + + StringBuilder errors = new StringBuilder(); + + if (cmd.equals("save")) + { + for (ConfigFile cf : ConfigUserInterface.this.getConfigs()) + { + try + { + cf.save(); + } + catch (Exception e1) + { + e1.printStackTrace(); + errors.append(getBundle().getString("errorSaving") + cf.getName() + ".properties. " + getBundle().getString("reason") + e1.getLocalizedMessage() + "\r\n"); + } + } + if (errors.length() == 0) + { + JOptionPane.showMessageDialog(ConfigUserInterface.this, getBundle().getString("success"), "OK", JOptionPane.INFORMATION_MESSAGE); + } + else + { + JOptionPane.showMessageDialog(ConfigUserInterface.this, errors, getBundle().getString("error"), JOptionPane.ERROR_MESSAGE); + System.exit(2); + } + } + else if (cmd.equals("exit")) + { + System.exit(0); + } + else if (cmd.equals("about")) + { + JOptionPane.showMessageDialog(ConfigUserInterface.this, getBundle().getString("credits") + "\nhttp://www.l2jserver.com\n\n" + getBundle().getString("icons") + "\n\n" + getBundle().getString("language") + '\n' + getBundle().getString("translation"), getBundle().getString("aboutItem"), JOptionPane.INFORMATION_MESSAGE, ImagesTable.getImage("l2jserverlogo.png")); + } + } + + /** + * @param configs The configuration to set. + */ + public void setConfigs(List configs) + { + _configs = configs; + } + + /** + * @return Returns the configuration. + */ + public List getConfigs() + { + return _configs; + } + + /** + * @return Returns the configuration setting name in a human readable form. + */ + + public static String unCamelize(final String keyName) + { + Pattern p = Pattern.compile("\\p{Lu}"); + Matcher m = p.matcher(keyName); + StringBuffer sb = new StringBuffer(); + int last = 0; + while (m.find()) + { + if (m.start() != (last + 1)) + { + m.appendReplacement(sb, " " + m.group()); + } + last = m.start(); + } + m.appendTail(sb); + return sb.toString().trim(); + } + + /** + * @param bundle The bundle to set. + */ + public void setBundle(ResourceBundle bundle) + { + _bundle = bundle; + } + + /** + * @return Returns the bundle. + */ + public ResourceBundle getBundle() + { + return _bundle; + } +} diff --git a/L2J_Server/java/com/l2jserver/configurator/JIPTextField.java b/L2J_Server/java/com/l2jserver/configurator/JIPTextField.java new file mode 100644 index 00000000..182bcd74 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/configurator/JIPTextField.java @@ -0,0 +1,323 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.configurator; + +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Toolkit; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.PlainDocument; + +/** + * @author KenM + */ +public class JIPTextField extends JPanel implements FocusListener +{ + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = 1L; + private JTextField[] _textFields; + private List _focusListeners; + + public JIPTextField(String textIp) + { + super.addFocusListener(this); + + initIPTextField(textIp); + + for (JTextField _textField : _textFields) + { + _textField.addFocusListener(this); + } + } + + public JIPTextField() + { + this("..."); + } + + /** + * @param value + */ + public JIPTextField(Inet4Address value) + { + this(value.getHostAddress()); + } + + private void initIPTextField(String textIp) + { + final ActionListener nextfocusaction = evt -> ((Component) evt.getSource()).transferFocus(); + + setLayout(new GridBagLayout()); + _textFields = new JTextField[4]; + + GridBagConstraints cons = new GridBagConstraints(); + cons.anchor = GridBagConstraints.PAGE_START; + cons.fill = GridBagConstraints.HORIZONTAL; + cons.insets = new Insets(1, 1, 1, 1); + cons.gridx = 0; + cons.gridy = 0; + + MaxLengthDocument previous = null; + String[] parts = textIp.split("\\."); + for (int i = 0; i < 4; i++) + { + String str = parts[i]; + if (i > 0) + { + JLabel dot = new JLabel("."); + cons.weightx = 0; + add(dot, cons); + cons.gridx++; + } + MaxLengthDocument maxDoc = new MaxLengthDocument(3); + _textFields[i] = new JTextField(maxDoc, str, 3); + if (previous != null) + { + previous.setNext(_textFields[i]); + } + previous = maxDoc; + // ic.weightx = 1; + add(_textFields[i], cons); + _textFields[i].addActionListener(nextfocusaction); + cons.gridx++; + } + } + + @Override + public void addFocusListener(FocusListener fl) + { + if (_focusListeners == null) + { + _focusListeners = new LinkedList<>(); + } + + if ((fl != null) && !_focusListeners.contains(fl)) + { + _focusListeners.add(fl); + } + } + + @Override + public void removeFocusListener(FocusListener fl) + { + if (_focusListeners != null) + { + _focusListeners.remove(fl); + } + } + + public String getText() + { + String str = ""; + for (int i = 0; i < 4; i++) + { + if (_textFields[i].getText().length() == 0) + { + str += "0"; + } + else + { + str += _textFields[i].getText(); + } + if (i < 3) + { + str += "."; + } + } + return str; + } + + public void setText(String str) + { + try + { + // make sure string is not null; throw a NullPointerException otherwise + str.length(); + + InetAddress ip = InetAddress.getByName(str); + byte b[] = ip.getAddress(); + for (int i = 0; i < 4; i++) + { + // byte always have a sign in Java, IP addresses aren't + if (b[i] >= 0) + { + _textFields[i].setText(Byte.toString(b[i])); + } + else + { + _textFields[i].setText(Integer.toString(b[i] + 256)); + } + } + return; + } + catch (UnknownHostException ex) + { + } + catch (NullPointerException npe) + { + } + for (int i = 0; i < 4; i++) + { + _textFields[i].setText(""); + } + } + + @Override + public void setEnabled(boolean enabled) + { + for (JTextField _textField : _textFields) + { + if (_textField != null) + { + _textField.setEnabled(enabled); + } + } + } + + public boolean isEmpty() + { + for (int i = 0; i < 4; i++) + { + if (_textFields[i].getText().length() != 0) + { + return false; + } + } + return true; + } + + public boolean isCorrect() + { + for (int i = 0; i < 4; i++) + { + if (_textFields[i].getText().length() == 0) + { + return false; + } + } + return true; + } + + @Override + public void focusGained(FocusEvent event) + { + if (_focusListeners != null) + { + for (FocusListener fl : _focusListeners) + { + fl.focusGained(event); + } + } + } + + @Override + public void focusLost(FocusEvent event) + { + if (isCorrect() || isEmpty()) + { + if (_focusListeners != null) + { + for (FocusListener fl : _focusListeners) + { + fl.focusLost(event); + } + } + } + } + + public class MaxLengthDocument extends PlainDocument + { + + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = 1L; + + private final int _max; + private JTextField _next; + + public MaxLengthDocument(int maxLength) + { + this(maxLength, null); + } + + public MaxLengthDocument(int maxLength, JTextField next) + { + _max = maxLength; + setNext(next); + } + + @Override + public void insertString(int offset, String str, AttributeSet a) throws BadLocationException + { + if ((getLength() + str.length()) > _max) + { + if (getNext() != null) + { + if (getNext().getText().length() > 0) + { + getNext().select(0, getNext().getText().length()); + } + else + { + getNext().getDocument().insertString(0, str, a); + } + getNext().requestFocusInWindow(); + } + else + { + Toolkit.getDefaultToolkit().beep(); + } + } + else + { + super.insertString(offset, str, a); + } + } + + /** + * @param next The next to set. + */ + public void setNext(JTextField next) + { + _next = next; + } + + /** + * @return Returns the next. + */ + public JTextField getNext() + { + return _next; + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/Announcements.java b/L2J_Server/java/com/l2jserver/gameserver/Announcements.java new file mode 100644 index 00000000..dc035da4 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/Announcements.java @@ -0,0 +1,237 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.Date; +import java.util.List; +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.clientpackets.Say2; +import com.l2jserver.gameserver.network.serverpackets.CreatureSay; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.script.DateRange; +import com.l2jserver.gameserver.util.Broadcast; +import com.l2jserver.util.StringUtil; + +/** + * This class ... + * @version $Revision: 1.5.2.1.2.7 $ $Date: 2005/03/29 23:15:14 $ + */ +public class Announcements +{ + private static Logger _log = Logger.getLogger(Announcements.class.getName()); + + private final List _announcements = new FastList<>(); + private final List> _eventAnnouncements = new FastList<>(); + + private Announcements() + { + loadAnnouncements(); + } + + public static Announcements getInstance() + { + return SingletonHolder._instance; + } + + public void loadAnnouncements() + { + _announcements.clear(); + File file = new File(Config.DATAPACK_ROOT, "data/announcements.txt"); + if (file.exists()) + { + readFromDisk(file); + } + else + { + _log.warning("data/announcements.txt doesn't exist"); + } + } + + public void showAnnouncements(L2PcInstance activeChar) + { + for (int i = 0; i < _announcements.size(); i++) + { + CreatureSay cs = new CreatureSay(0, Say2.ANNOUNCEMENT, activeChar.getName(), _announcements.get(i)); + activeChar.sendPacket(cs); + } + + for (int i = 0; i < _eventAnnouncements.size(); i++) + { + List entry = _eventAnnouncements.get(i); + + DateRange validDateRange = (DateRange) entry.get(0); + String[] msg = (String[]) entry.get(1); + Date currentDate = new Date(); + + if (!validDateRange.isValid() || validDateRange.isWithinRange(currentDate)) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1); + for (String element : msg) + { + sm.addString(element); + } + activeChar.sendPacket(sm); + } + + } + } + + public void addEventAnnouncement(DateRange validDateRange, String[] msg) + { + List entry = new FastList<>(); + entry.add(validDateRange); + entry.add(msg); + _eventAnnouncements.add(entry); + } + + public void listAnnouncements(L2PcInstance activeChar) + { + String content = HtmCache.getInstance().getHtmForce(activeChar.getHtmlPrefix(), "data/html/admin/announce.htm"); + NpcHtmlMessage adminReply = new NpcHtmlMessage(5); + adminReply.setHtml(content); + final StringBuilder replyMSG = StringUtil.startAppend(500, "
"); + for (int i = 0; i < _announcements.size(); i++) + { + StringUtil.append(replyMSG, "
", _announcements.get(i), "" + "
"); + } + adminReply.replace("%announces%", replyMSG.toString()); + activeChar.sendPacket(adminReply); + } + + public void addAnnouncement(String text) + { + _announcements.add(text); + saveToDisk(); + } + + public void delAnnouncement(int line) + { + _announcements.remove(line); + saveToDisk(); + } + + private void readFromDisk(File file) + { + try (LineNumberReader lnr = new LineNumberReader(new FileReader(file));) + { + int i = 0; + String line = null; + + while ((line = lnr.readLine()) != null) + { + StringTokenizer st = new StringTokenizer(line, "\n\r"); + if (st.hasMoreTokens()) + { + String announcement = st.nextToken(); + _announcements.add(announcement); + + i++; + } + } + + if (Config.DEBUG) + { + _log.info("Announcements: Loaded " + i + " Announcements."); + } + } + catch (IOException e1) + { + _log.log(Level.SEVERE, "Error reading announcements: ", e1); + } + } + + private void saveToDisk() + { + File file = new File("data/announcements.txt"); + FileWriter save = null; + + try + { + save = new FileWriter(file); + for (int i = 0; i < _announcements.size(); i++) + { + save.write(_announcements.get(i)); + save.write("\r\n"); + } + } + catch (IOException e) + { + _log.log(Level.SEVERE, "Saving to the announcements file has failed: ", e); + } + finally + { + try + { + save.close(); + } + catch (Exception e) + { + } + } + } + + public void announceToAll(String text) + { + Broadcast.announceToOnlinePlayers(text); + } + + public void announceToAll(SystemMessage sm) + { + Broadcast.toAllOnlinePlayers(sm); + } + + public void announceToInstance(SystemMessage sm, int instanceId) + { + Broadcast.toPlayersInInstance(sm, instanceId); + } + + // Method for handling announcements from admin + public void handleAnnounce(String command, int lengthToTrim) + { + try + { + // Announce string to everyone on server + String text = command.substring(lengthToTrim); + SingletonHolder._instance.announceToAll(text); + } + + // No body cares! + catch (StringIndexOutOfBoundsException e) + { + // empty message.. ignore + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final Announcements _instance = new Announcements(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/CastleUpdater.java b/L2J_Server/java/com/l2jserver/gameserver/CastleUpdater.java new file mode 100644 index 00000000..2d7dd653 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/CastleUpdater.java @@ -0,0 +1,72 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.entity.Castle; +import com.l2jserver.gameserver.model.itemcontainer.ItemContainer; + +/** + * Thorgrim - 2005 Class managing periodical events with castle + */ +public class CastleUpdater implements Runnable +{ + protected static final Logger _log = Logger.getLogger(CastleUpdater.class.getName()); + private final L2Clan _clan; + private int _runCount = 0; + + public CastleUpdater(L2Clan clan, int runCount) + { + _clan = clan; + _runCount = runCount; + } + + @Override + public void run() + { + try + { + // Move current castle treasury to clan warehouse every 2 hour + ItemContainer warehouse = _clan.getWarehouse(); + if ((warehouse != null) && (_clan.getHasCastle() > 0)) + { + Castle castle = CastleManager.getInstance().getCastleById(_clan.getHasCastle()); + if (!Config.ALT_MANOR_SAVE_ALL_ACTIONS) + { + if ((_runCount % Config.ALT_MANOR_SAVE_PERIOD_RATE) == 0) + { + castle.saveSeedData(); + castle.saveCropData(); + if (Config.DEBUG) + { + _log.info("Manor System: all data for " + castle.getName() + " saved"); + } + } + } + CastleUpdater cu = new CastleUpdater(_clan, ++_runCount); + ThreadPoolManager.getInstance().scheduleGeneral(cu, 3600000); + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/FortUpdater.java b/L2J_Server/java/com/l2jserver/gameserver/FortUpdater.java new file mode 100644 index 00000000..748911c5 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/FortUpdater.java @@ -0,0 +1,104 @@ +/* This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +package com.l2jserver.gameserver; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.entity.Fort; + +/** + * Class managing periodical events with castle + * @author Vice - 2008 + */ +public class FortUpdater implements Runnable +{ + protected static Logger _log = Logger.getLogger(FortUpdater.class.getName()); + private final L2Clan _clan; + private final Fort _fort; + private int _runCount; + private final UpdaterType _updaterType; + + public enum UpdaterType + { + MAX_OWN_TIME, // gives fort back to NPC clan + PERIODIC_UPDATE // raise blood oath/supply level + } + + public FortUpdater(Fort fort, L2Clan clan, int runCount, UpdaterType ut) + { + _fort = fort; + _clan = clan; + _runCount = runCount; + _updaterType = ut; + } + + @Override + public void run() + { + try + { + switch (_updaterType) + { + case PERIODIC_UPDATE: + _runCount++; + if ((_fort.getOwnerClan() == null) || (_fort.getOwnerClan() != _clan)) + { + return; + } + + _fort.setBloodOathReward(_fort.getBloodOathReward() + Config.FS_BLOOD_OATH_COUNT); + if (_fort.getFortState() == 2) + { + if (_clan.getWarehouse().getAdena() >= Config.FS_FEE_FOR_CASTLE) + { + _clan.getWarehouse().destroyItemByItemId("FS_fee_for_Castle", 57, Config.FS_FEE_FOR_CASTLE, null, null); + CastleManager.getInstance().getCastleById(_fort.getCastleId()).addToTreasuryNoTax(Config.FS_FEE_FOR_CASTLE); + _fort.raiseSupplyLvL(); + } + else + { + _fort.setFortState(1, 0); + } + } + _fort.saveFortVariables(); + break; + case MAX_OWN_TIME: + if ((_fort.getOwnerClan() == null) || (_fort.getOwnerClan() != _clan)) + { + return; + } + if (_fort.getOwnedTime() > (Config.FS_MAX_OWN_TIME * 3600)) + { + _fort.removeOwner(true); + _fort.setFortState(0, 0); + } + break; + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + } + } + + public int getRunCount() + { + return _runCount; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/GameServer.java b/L2J_Server/java/com/l2jserver/gameserver/GameServer.java new file mode 100644 index 00000000..20c85923 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/GameServer.java @@ -0,0 +1,514 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +package com.l2jserver.gameserver; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Calendar; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +import org.mmocore.network.SelectorConfig; +import org.mmocore.network.SelectorThread; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.Server; +import com.l2jserver.gameserver.cache.CrestCache; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.datatables.AccessLevels; +import com.l2jserver.gameserver.datatables.AdminCommandAccessRights; +import com.l2jserver.gameserver.datatables.ArmorSetsTable; +import com.l2jserver.gameserver.datatables.AugmentationData; +import com.l2jserver.gameserver.datatables.CharNameTable; +import com.l2jserver.gameserver.datatables.CharTemplateTable; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.datatables.DoorTable; +import com.l2jserver.gameserver.datatables.EnchantGroupsTable; +import com.l2jserver.gameserver.datatables.EnchantHPBonusData; +import com.l2jserver.gameserver.datatables.EventDroplist; +import com.l2jserver.gameserver.datatables.FishTable; +import com.l2jserver.gameserver.datatables.GMSkillTable; +import com.l2jserver.gameserver.datatables.HelperBuffTable; +import com.l2jserver.gameserver.datatables.HennaTable; +import com.l2jserver.gameserver.datatables.HennaTreeTable; +import com.l2jserver.gameserver.datatables.HerbDropTable; +import com.l2jserver.gameserver.datatables.HeroSkillTable; +import com.l2jserver.gameserver.datatables.ItemTable; +import com.l2jserver.gameserver.datatables.LevelUpData; +import com.l2jserver.gameserver.datatables.MapRegionTable; +import com.l2jserver.gameserver.datatables.MerchantPriceConfigTable; +import com.l2jserver.gameserver.datatables.MultiSell; +import com.l2jserver.gameserver.datatables.NobleSkillTable; +import com.l2jserver.gameserver.datatables.NpcBufferTable; +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.datatables.NpcWalkerRoutesTable; +import com.l2jserver.gameserver.datatables.OfflineTradersTable; +import com.l2jserver.gameserver.datatables.PetDataTable; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.datatables.SpawnTable; +import com.l2jserver.gameserver.datatables.StaticObjects; +import com.l2jserver.gameserver.datatables.SummonItemsData; +import com.l2jserver.gameserver.datatables.SummonSkillsTable; +import com.l2jserver.gameserver.datatables.TeleportLocationTable; +import com.l2jserver.gameserver.datatables.UITable; +import com.l2jserver.gameserver.geoeditorcon.GeoEditorListener; +import com.l2jserver.gameserver.handler.AdminCommandHandler; +import com.l2jserver.gameserver.handler.ChatHandler; +import com.l2jserver.gameserver.handler.ItemHandler; +import com.l2jserver.gameserver.handler.SkillHandler; +import com.l2jserver.gameserver.handler.UserCommandHandler; +import com.l2jserver.gameserver.handler.VoicedCommandHandler; +import com.l2jserver.gameserver.idfactory.IdFactory; +import com.l2jserver.gameserver.instancemanager.AirShipManager; +import com.l2jserver.gameserver.instancemanager.AntiFeedManager; +import com.l2jserver.gameserver.instancemanager.AuctionManager; +import com.l2jserver.gameserver.instancemanager.BoatManager; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.instancemanager.CastleManorManager; +import com.l2jserver.gameserver.instancemanager.ClanHallManager; +import com.l2jserver.gameserver.instancemanager.CoupleManager; +import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager; +import com.l2jserver.gameserver.instancemanager.DayNightSpawnManager; +import com.l2jserver.gameserver.instancemanager.DimensionalRiftManager; +import com.l2jserver.gameserver.instancemanager.FortManager; +import com.l2jserver.gameserver.instancemanager.FortSiegeManager; +import com.l2jserver.gameserver.instancemanager.FourSepulchersManager; +import com.l2jserver.gameserver.instancemanager.GlobalVariablesManager; +import com.l2jserver.gameserver.instancemanager.GraciaSeedsManager; +import com.l2jserver.gameserver.instancemanager.GrandBossManager; +import com.l2jserver.gameserver.instancemanager.HellboundManager; +import com.l2jserver.gameserver.instancemanager.InstanceManager; +import com.l2jserver.gameserver.instancemanager.ItemAuctionManager; +import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager; +import com.l2jserver.gameserver.instancemanager.MailManager; +import com.l2jserver.gameserver.instancemanager.MercTicketManager; +import com.l2jserver.gameserver.instancemanager.PetitionManager; +import com.l2jserver.gameserver.instancemanager.QuestManager; +import com.l2jserver.gameserver.instancemanager.RaidBossPointsManager; +import com.l2jserver.gameserver.instancemanager.RaidBossSpawnManager; +import com.l2jserver.gameserver.instancemanager.SiegeManager; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; +import com.l2jserver.gameserver.instancemanager.TransformationManager; +import com.l2jserver.gameserver.instancemanager.ZoneManager; +import com.l2jserver.gameserver.model.AutoChatHandler; +import com.l2jserver.gameserver.model.AutoSpawnHandler; +import com.l2jserver.gameserver.model.L2Manor; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.PartyMatchRoomList; +import com.l2jserver.gameserver.model.PartyMatchWaitingList; +import com.l2jserver.gameserver.model.entity.Hero; +import com.l2jserver.gameserver.model.entity.TvTManager; +import com.l2jserver.gameserver.model.olympiad.Olympiad; +import com.l2jserver.gameserver.network.L2GameClient; +import com.l2jserver.gameserver.network.L2GamePacketHandler; +import com.l2jserver.gameserver.network.communityserver.CommunityServerThread; +import com.l2jserver.gameserver.pathfinding.PathFinding; +import com.l2jserver.gameserver.script.faenor.FaenorScriptEngine; +import com.l2jserver.gameserver.scripting.CompiledScriptCache; +import com.l2jserver.gameserver.scripting.L2ScriptEngineManager; +import com.l2jserver.gameserver.taskmanager.AutoAnnounceTaskManager; +import com.l2jserver.gameserver.taskmanager.KnownListUpdateTaskManager; +import com.l2jserver.gameserver.taskmanager.TaskManager; +import com.l2jserver.status.Status; +import com.l2jserver.util.DeadLockDetector; +import com.l2jserver.util.IPv4Filter; + +/** + * This class ... + * @version $Revision: 1.29.2.15.2.19 $ $Date: 2005/04/05 19:41:23 $ + */ +public class GameServer +{ + private static final Logger _log = Logger.getLogger(GameServer.class.getName()); + + private final SelectorThread _selectorThread; + private final L2GamePacketHandler _gamePacketHandler; + private final DeadLockDetector _deadDetectThread; + private final IdFactory _idFactory; + public static GameServer gameServer; + private final LoginServerThread _loginThread; + private static Status _statusServer; + public static final Calendar dateTimeServerStarted = Calendar.getInstance(); + + public long getUsedMemoryMB() + { + return (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1048576; // ; + } + + public SelectorThread getSelectorThread() + { + return _selectorThread; + } + + public L2GamePacketHandler getL2GamePacketHandler() + { + return _gamePacketHandler; + } + + public DeadLockDetector getDeadLockDetectorThread() + { + return _deadDetectThread; + } + + public GameServer() throws Exception + { + long serverLoadStart = System.currentTimeMillis(); + + gameServer = this; + _log.finest("Used memory:" + getUsedMemoryMB() + "MB"); + + _idFactory = IdFactory.getInstance(); + + if (!_idFactory.isInitialized()) + { + _log.severe("Could not read object IDs from DB. Please Check Your Data."); + throw new Exception("Could not initialize the ID factory"); + } + + ThreadPoolManager.getInstance(); + + new File(Config.DATAPACK_ROOT, "data/crests").mkdirs(); + new File("log/game").mkdirs(); + + // load script engines + printSection("Engines"); + L2ScriptEngineManager.getInstance(); + + printSection("World"); + // start game time control early + GameTimeController.getInstance(); + InstanceManager.getInstance(); + L2World.getInstance(); + MapRegionTable.getInstance(); + Announcements.getInstance(); + GlobalVariablesManager.getInstance(); + + printSection("Skills"); + EnchantGroupsTable.getInstance(); + SkillTable.getInstance(); + SkillTreesData.getInstance(); + NobleSkillTable.getInstance(); + GMSkillTable.getInstance(); + HeroSkillTable.getInstance(); + SummonSkillsTable.getInstance(); + + printSection("Items"); + ItemTable.getInstance(); + SummonItemsData.getInstance(); + EnchantHPBonusData.getInstance(); + MerchantPriceConfigTable.getInstance().loadInstances(); + TradeController.getInstance(); + MultiSell.getInstance(); + RecipeController.getInstance(); + ArmorSetsTable.getInstance(); + FishTable.getInstance(); + + printSection("Characters"); + CharTemplateTable.getInstance(); + CharNameTable.getInstance(); + LevelUpData.getInstance(); + AccessLevels.getInstance(); + AdminCommandAccessRights.getInstance(); + GmListTable.getInstance(); + RaidBossPointsManager.getInstance(); + PetDataTable.getInstance(); + + printSection("Clans"); + ClanTable.getInstance(); + ClanHallManager.getInstance(); + AuctionManager.getInstance(); + + printSection("Geodata"); + GeoData.getInstance(); + if (Config.GEODATA == 2) + { + PathFinding.getInstance(); + } + + printSection("NPCs"); + HerbDropTable.getInstance(); + NpcTable.getInstance(); + NpcWalkerRoutesTable.getInstance(); + ZoneManager.getInstance(); + DoorTable.getInstance(); + StaticObjects.getInstance(); + ItemAuctionManager.getInstance(); + CastleManager.getInstance().loadInstances(); + FortManager.getInstance().loadInstances(); + NpcBufferTable.getInstance(); + SpawnTable.getInstance(); + RaidBossSpawnManager.getInstance(); + DayNightSpawnManager.getInstance().trim().notifyChangeMode(); + GrandBossManager.getInstance().initZones(); + FourSepulchersManager.getInstance().init(); + DimensionalRiftManager.getInstance(); + EventDroplist.getInstance(); + + printSection("Siege"); + SiegeManager.getInstance().getSieges(); + FortSiegeManager.getInstance(); + TerritoryWarManager.getInstance(); + CastleManorManager.getInstance(); + MercTicketManager.getInstance(); + L2Manor.getInstance(); + + printSection("Olympiad"); + Olympiad.getInstance(); + Hero.getInstance(); + + // Call to load caches + printSection("Cache"); + HtmCache.getInstance(); + CrestCache.getInstance(); + TeleportLocationTable.getInstance(); + UITable.getInstance(); + PartyMatchWaitingList.getInstance(); + PartyMatchRoomList.getInstance(); + PetitionManager.getInstance(); + HennaTable.getInstance(); + HennaTreeTable.getInstance(); + HelperBuffTable.getInstance(); + AugmentationData.getInstance(); + CursedWeaponsManager.getInstance(); + + printSection("Scripts"); + QuestManager.getInstance(); + TransformationManager.getInstance(); + BoatManager.getInstance(); + AirShipManager.getInstance(); + GraciaSeedsManager.getInstance(); + + try + { + _log.info("Loading Server Scripts"); + File scripts = new File(Config.DATAPACK_ROOT + "/data/scripts.cfg"); + if (!Config.ALT_DEV_NO_HANDLERS || !Config.ALT_DEV_NO_QUESTS) + { + L2ScriptEngineManager.getInstance().executeScriptList(scripts); + } + } + catch (IOException ioe) + { + _log.severe("Failed loading scripts.cfg, no script going to be loaded"); + } + try + { + CompiledScriptCache compiledScriptCache = L2ScriptEngineManager.getInstance().getCompiledScriptCache(); + if (compiledScriptCache == null) + { + _log.info("Compiled Scripts Cache is disabled."); + } + else + { + compiledScriptCache.purge(); + + if (compiledScriptCache.isModified()) + { + compiledScriptCache.save(); + _log.info("Compiled Scripts Cache was saved."); + } + else + { + _log.info("Compiled Scripts Cache is up-to-date."); + } + } + + } + catch (IOException e) + { + _log.log(Level.SEVERE, "Failed to store Compiled Scripts Cache.", e); + } + QuestManager.getInstance().report(); + TransformationManager.getInstance().report(); + + if (Config.SAVE_DROPPED_ITEM) + { + ItemsOnGroundManager.getInstance(); + } + + if ((Config.AUTODESTROY_ITEM_AFTER > 0) || (Config.HERB_AUTO_DESTROY_TIME > 0)) + { + ItemsAutoDestroy.getInstance(); + } + + MonsterRace.getInstance(); + + SevenSigns.getInstance().spawnSevenSignsNPC(); + SevenSignsFestival.getInstance(); + AutoSpawnHandler.getInstance(); + AutoChatHandler.getInstance(); + + FaenorScriptEngine.getInstance(); + // Init of a cursed weapon manager + + _log.info("AutoChatHandler: Loaded " + AutoChatHandler.getInstance().size() + " handlers in total."); + _log.info("AutoSpawnHandler: Loaded " + AutoSpawnHandler.getInstance().size() + " handlers in total."); + + AdminCommandHandler.getInstance(); + ChatHandler.getInstance(); + ItemHandler.getInstance(); + SkillHandler.getInstance(); + UserCommandHandler.getInstance(); + VoicedCommandHandler.getInstance(); + + if (Config.L2JMOD_ALLOW_WEDDING) + { + CoupleManager.getInstance(); + } + + TaskManager.getInstance(); + + AntiFeedManager.getInstance().registerEvent(AntiFeedManager.GAME_ID); + MerchantPriceConfigTable.getInstance().updateReferences(); + CastleManager.getInstance().activateInstances(); + FortManager.getInstance().activateInstances(); + HellboundManager.getInstance(); + + if (Config.ALLOW_MAIL) + { + MailManager.getInstance(); + } + + if (Config.ACCEPT_GEOEDITOR_CONN) + { + GeoEditorListener.getInstance(); + } + + Runtime.getRuntime().addShutdownHook(Shutdown.getInstance()); + + _log.info("IdFactory: Free ObjectID's remaining: " + IdFactory.getInstance().size()); + + TvTManager.getInstance(); + KnownListUpdateTaskManager.getInstance(); + + if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS) + { + OfflineTradersTable.restoreOfflineTraders(); + } + + if (Config.DEADLOCK_DETECTOR) + { + _deadDetectThread = new DeadLockDetector(); + _deadDetectThread.setDaemon(true); + _deadDetectThread.start(); + } + else + { + _deadDetectThread = null; + } + System.gc(); + // maxMemory is the upper limit the jvm can use, totalMemory the size of + // the current allocation pool, freeMemory the unused memory in the + // allocation pool + long freeMem = ((Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory()) + Runtime.getRuntime().freeMemory()) / 1048576; + long totalMem = Runtime.getRuntime().maxMemory() / 1048576; + _log.info("GameServer Started, free memory " + freeMem + " Mb of " + totalMem + " Mb"); + + _loginThread = LoginServerThread.getInstance(); + _loginThread.start(); + + CommunityServerThread.initialize(); + + final SelectorConfig sc = new SelectorConfig(); + sc.MAX_READ_PER_PASS = Config.MMO_MAX_READ_PER_PASS; + sc.MAX_SEND_PER_PASS = Config.MMO_MAX_SEND_PER_PASS; + sc.SLEEP_TIME = Config.MMO_SELECTOR_SLEEP_TIME; + sc.HELPER_BUFFER_COUNT = Config.MMO_HELPER_BUFFER_COUNT; + + _gamePacketHandler = new L2GamePacketHandler(); + _selectorThread = new SelectorThread<>(sc, _gamePacketHandler, _gamePacketHandler, _gamePacketHandler, new IPv4Filter()); + + InetAddress bindAddress = null; + if (!Config.GAMESERVER_HOSTNAME.equals("*")) + { + try + { + bindAddress = InetAddress.getByName(Config.GAMESERVER_HOSTNAME); + } + catch (UnknownHostException e1) + { + _log.log(Level.SEVERE, "WARNING: The GameServer bind address is invalid, using all avaliable IPs. Reason: " + e1.getMessage(), e1); + } + } + + try + { + _selectorThread.openServerSocket(bindAddress, Config.PORT_GAME); + } + catch (IOException e) + { + _log.log(Level.SEVERE, "FATAL: Failed to open server socket. Reason: " + e.getMessage(), e); + System.exit(1); + } + _selectorThread.start(); + _log.info("Maximum Numbers of Connected Players: " + Config.MAXIMUM_ONLINE_USERS); + long serverLoadEnd = System.currentTimeMillis(); + _log.info("Server Loaded in " + ((serverLoadEnd - serverLoadStart) / 1000) + " seconds"); + + AutoAnnounceTaskManager.getInstance(); + } + + public static void main(String[] args) throws Exception + { + Server.serverMode = Server.MODE_GAMESERVER; + // Local Constants + final String LOG_FOLDER = "log"; // Name of folder for log file + final String LOG_NAME = "./log.cfg"; // Name of log file + + /*** Main ***/ + // Create log folder + File logFolder = new File(Config.DATAPACK_ROOT, LOG_FOLDER); + logFolder.mkdir(); + + // Create input stream for log file -- or store file data into memory + InputStream is = new FileInputStream(new File(LOG_NAME)); + LogManager.getLogManager().readConfiguration(is); + is.close(); + + // Initialize config + Config.load(); + printSection("Database"); + L2DatabaseFactory.getInstance(); + gameServer = new GameServer(); + + if (Config.IS_TELNET_ENABLED) + { + _statusServer = new Status(Server.serverMode); + _statusServer.start(); + } + else + { + _log.info("Telnet server is currently disabled."); + } + } + + public static void printSection(String s) + { + s = "=[ " + s + " ]"; + while (s.length() < 78) + { + s = "-" + s; + } + _log.info(s); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/GameTimeController.java b/L2J_Server/java/com/l2jserver/gameserver/GameTimeController.java new file mode 100644 index 00000000..86d04073 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/GameTimeController.java @@ -0,0 +1,259 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.util.Iterator; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ai.CtrlEvent; +import com.l2jserver.gameserver.instancemanager.DayNightSpawnManager; +import com.l2jserver.gameserver.model.actor.L2Character; + +/** + * Removed TimerThread watcher [DrHouse] + * @version $Date: 2010/02/02 22:43:00 $ + */ +public class GameTimeController +{ + protected static final Logger _log = Logger.getLogger(GameTimeController.class.getName()); + + public static final int TICKS_PER_SECOND = 10; // not able to change this without checking through code + public static final int MILLIS_IN_TICK = 1000 / TICKS_PER_SECOND; + + protected static int _gameTicks; + protected static long _gameStartTime; + protected static boolean _isNight = false; + protected static boolean _interruptRequest = false; + + private static final FastMap _movingObjects = new FastMap().shared(); + + protected static TimerThread _timer; + + /** + * one ingame day is 240 real minutes + */ + public static GameTimeController getInstance() + { + return SingletonHolder._instance; + } + + private GameTimeController() + { + _gameStartTime = System.currentTimeMillis() - 3600000; // offset so that the server starts a day begin + _gameTicks = 3600000 / MILLIS_IN_TICK; // offset so that the server starts a day begin + + _timer = new TimerThread(); + _timer.start(); + + ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new BroadcastSunState(), 0, 600000); + + } + + public boolean isNowNight() + { + return _isNight; + } + + public int getGameTime() + { + return (_gameTicks / (TICKS_PER_SECOND * 10)); + } + + public static int getGameTicks() + { + return _gameTicks; + } + + /** + * Add a L2Character to movingObjects of GameTimeController.
+ *
+ * Concept :
+ *
+ * All L2Character in movement are identified in movingObjects of GameTimeController.
+ *
+ * @param cha The L2Character to add to movingObjects of GameTimeController + */ + public void registerMovingObject(L2Character cha) + { + if (cha == null) + { + return; + } + + _movingObjects.putIfAbsent(cha.getObjectId(), cha); + } + + /** + * Move all L2Characters contained in movingObjects of GameTimeController.
+ *
+ * Concept :
+ *
+ * All L2Character in movement are identified in movingObjects of GameTimeController.
+ *
+ * Actions :
+ *
+ *
  • Update the position of each L2Character
  • If movement is finished, the L2Character is removed from movingObjects
  • Create a task to update the _knownObject and _knowPlayers of each L2Character that finished its movement and of their already known L2Object then notify AI with + * EVT_ARRIVED

  • + *
    + */ + protected void moveObjects() + { + // Go throw the table containing L2Character in movement + Iterator> it = _movingObjects.entrySet().iterator(); + while (it.hasNext()) + { + // If movement is finished, the L2Character is removed from + // movingObjects and added to the ArrayList ended + L2Character ch = it.next().getValue(); + if (ch.updatePosition(_gameTicks)) + { + it.remove(); + ThreadPoolManager.getInstance().executeTask(new MovingObjectArrived(ch)); + } + } + } + + public void stopTimer() + { + _interruptRequest = true; + _timer.interrupt(); + } + + class TimerThread extends Thread + { + public TimerThread() + { + super("GameTimeController"); + setDaemon(true); + setPriority(MAX_PRIORITY); + } + + @Override + public void run() + { + int oldTicks; + long runtime; + int sleepTime; + + for (;;) + { + try + { + oldTicks = _gameTicks; // save old ticks value to avoid moving objects 2x in same tick + runtime = System.currentTimeMillis() - _gameStartTime; // from server boot to now + + _gameTicks = (int) (runtime / MILLIS_IN_TICK); // new ticks value (ticks now) + + if (oldTicks != _gameTicks) + { + moveObjects(); // Runs possibly too often + } + + runtime = (System.currentTimeMillis() - _gameStartTime) - runtime; + + // calculate sleep time... time needed to next tick minus time it takes to call moveObjects() + sleepTime = (1 + MILLIS_IN_TICK) - (((int) runtime) % MILLIS_IN_TICK); + + // _log.finest("TICK: "+_gameTicks); + + if (sleepTime > 0) + { + Thread.sleep(sleepTime); + } + } + catch (InterruptedException ie) + { + if (_interruptRequest) + { + return; + } + + _log.log(Level.WARNING, "", ie); + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + } + } + } + } + + /** + * Update the _knownObject and _knowPlayers of each L2Character that finished its movement and of their already known L2Object then notify AI with EVT_ARRIVED.
    + *
    + */ + private static class MovingObjectArrived implements Runnable + { + private final L2Character _ended; + + MovingObjectArrived(L2Character ended) + { + _ended = ended; + } + + @Override + public void run() + { + try + { + if (_ended.hasAI()) // AI could be just disabled due to region turn off + { + if (Config.MOVE_BASED_KNOWNLIST) + { + _ended.getKnownList().findObjects(); + } + _ended.getAI().notifyEvent(CtrlEvent.EVT_ARRIVED); + } + } + catch (NullPointerException e) + { + _log.log(Level.WARNING, "", e); + } + } + } + + /** + * @param rise + */ + class BroadcastSunState implements Runnable + { + int h; + boolean tempIsNight; + + @Override + public void run() + { + h = ((getGameTime() + 29) / 60) % 24; // Time in hour (+ 29 is to round 60) + tempIsNight = (h < 6); + + if (tempIsNight != _isNight) + { // If diff day/night state + _isNight = tempIsNight; // Set current day/night varible to value of temp varible + DayNightSpawnManager.getInstance().notifyChangeMode(); + } + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final GameTimeController _instance = new GameTimeController(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/GeoData.java b/L2J_Server/java/com/l2jserver/gameserver/GeoData.java new file mode 100644 index 00000000..6588b7db --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/GeoData.java @@ -0,0 +1,196 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.util.logging.Logger; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Spawn; +import com.l2jserver.gameserver.model.Location; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.util.Point3D; + +/** + * @author -Nemesiss- + */ +public class GeoData +{ + private static Logger _log = Logger.getLogger(GeoData.class.getName()); + + protected GeoData() + { + } + + private GeoData(final boolean disabled) + { + if (disabled) + { + _log.info("Geodata Engine: Disabled."); + } + } + + public static GeoData getInstance() + { + return SingletonHolder._instance; + } + + // Public Methods + /** + * @param x + * @param y + * @return Geo Block Type + */ + public short getType(int x, int y) + { + return 0; + } + + /** + * @param x + * @param y + * @param z + * @return Nearles Z + */ + public short getHeight(int x, int y, int z) + { + return (short) z; + } + + /** + * @param x + * @param y + * @param zmin + * @param zmax + * @param spawnid + * @return + */ + public short getSpawnHeight(int x, int y, int zmin, int zmax, L2Spawn spawn) + { + return (short) zmin; + } + + /** + * @param x + * @param y + * @return + */ + public String geoPosition(int x, int y) + { + return ""; + } + + /** + * @param cha + * @param target + * @return True if cha can see target (LOS) + */ + public boolean canSeeTarget(L2Object cha, L2Object target) + { + // If geo is off do simple check :] + // Don't allow casting on players on different dungeon lvls etc + return (Math.abs(target.getZ() - cha.getZ()) < 1000); + } + + public boolean canSeeTarget(L2Object cha, Point3D worldPosition) + { + // If geo is off do simple check :] + // Don't allow casting on players on different dungeon lvls etc + return Math.abs(worldPosition.getZ() - cha.getZ()) < 1000; + } + + public boolean canSeeTarget(int x, int y, int z, int tx, int ty, int tz) + { + // If geo is off do simple check :] + // Don't allow casting on players on different dungeon lvls etc + return (Math.abs(z - tz) < 1000); + } + + /** + * @param cha + * @param target + * @return True if cha can see target (LOS) and send usful info to PC + */ + public boolean canSeeTargetDebug(L2PcInstance gm, L2Object target) + { + return true; + } + + /** + * @param x + * @param y + * @param z + * @return Geo NSWE (0-15) + */ + public short getNSWE(int x, int y, int z) + { + return 15; + } + + public short getHeightAndNSWE(int x, int y, int z) + { + return (short) ((z << 1) | 15); + } + + /** + * @param x + * @param y + * @param z + * @param tx + * @param ty + * @param tz + * @param instanceId + * @return Last Location (x,y,z) where player can walk - just before wall + */ + public Location moveCheck(int x, int y, int z, int tx, int ty, int tz, int instanceId) + { + return new Location(tx, ty, tz); + } + + public boolean canMoveFromToTarget(int x, int y, int z, int tx, int ty, int tz, int instanceId) + { + return true; + } + + /** + * @param gm + * @param comment + */ + public void addGeoDataBug(L2PcInstance gm, String comment) + { + // Do Nothing + } + + public static void unloadGeodata(byte rx, byte ry) + { + + } + + public static boolean loadGeodataFile(byte rx, byte ry) + { + return false; + } + + public boolean hasGeo(int x, int y) + { + return false; + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final GeoData _instance = Config.GEODATA > 0 ? GeoEngine.getInstance() : new GeoData(true); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/GeoEngine.java b/L2J_Server/java/com/l2jserver/gameserver/GeoEngine.java new file mode 100644 index 00000000..8ff73252 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/GeoEngine.java @@ -0,0 +1,1658 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import gnu.trove.TShortObjectHashMap; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.LineNumberReader; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.datatables.DoorTable; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Spawn; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.Location; +import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.util.Point3D; + +/** + * @author -Nemesiss- + */ +public class GeoEngine extends GeoData +{ + private static Logger _log = Logger.getLogger(GeoData.class.getName()); + private static final byte EAST = 1; + private static final byte WEST = 2; + private static final byte SOUTH = 4; + private static final byte NORTH = 8; + private static final byte NSWE_ALL = 15; + private static TShortObjectHashMap _geodata = new TShortObjectHashMap<>(); + private static TShortObjectHashMap _geodataIndex = new TShortObjectHashMap<>(); + private static BufferedOutputStream _geoBugsOut; + + public static GeoEngine getInstance() + { + return SingletonHolder._instance; + } + + private GeoEngine() + { + nInitGeodata(); + } + + @Override + public short getType(int x, int y) + { + return nGetType((x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4); + } + + @Override + public short getHeight(int x, int y, int z) + { + return nGetHeight((x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4, z); + } + + @Override + public short getSpawnHeight(int x, int y, int zmin, int zmax, L2Spawn spawn) + { + return nGetSpawnHeight((x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4, zmin, zmax, spawn); + } + + @Override + public String geoPosition(int x, int y) + { + int gx = (x - L2World.MAP_MIN_X) >> 4; + int gy = (y - L2World.MAP_MIN_Y) >> 4; + return "bx: " + getBlock(gx) + " by: " + getBlock(gy) + " cx: " + getCell(gx) + " cy: " + getCell(gy) + " region offset: " + getRegionOffset(gx, gy); + } + + @Override + public boolean canSeeTarget(L2Object cha, Point3D target) + { + if (DoorTable.getInstance().checkIfDoorsBetween(cha.getX(), cha.getY(), cha.getZ(), target.getX(), target.getY(), target.getZ(), cha.getInstanceId())) + { + return false; + } + if (cha.getZ() >= target.getZ()) + { + return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), target.getX(), target.getY(), target.getZ()); + } + return canSeeTarget(target.getX(), target.getY(), target.getZ(), cha.getX(), cha.getY(), cha.getZ()); + } + + @Override + public boolean canSeeTarget(L2Object cha, L2Object target) + { + if ((cha == null) || (target == null)) + { + return false; + } + // To be able to see over fences and give the player the viewpoint + // game client has, all coordinates are lifted 45 from ground. + // Because of layer selection in LOS algorithm (it selects -45 there + // and some layers can be very close...) do not change this without + // changing the LOS code. + // Basically the +45 is character height. Raid bosses are naturally higher, + // dwarves shorter, but this should work relatively well. + // If this is going to be improved, use e.g. + // ((L2Character)cha).getTemplate().collisionHeight + int z = cha.getZ() + 45; + if (cha instanceof L2DefenderInstance) + { + z += 30; // well they don't move closer to balcony fence at the moment :( + } + int z2 = target.getZ() + 45; + if (!(target instanceof L2DoorInstance) && DoorTable.getInstance().checkIfDoorsBetween(cha.getX(), cha.getY(), z, target.getX(), target.getY(), z2, cha.getInstanceId())) + { + return false; + } + if (target instanceof L2DoorInstance) + { + return true; // door coordinates are hinge coords.. + } + if (target instanceof L2DefenderInstance) + { + z2 += 30; // well they don't move closer to balcony fence at the moment :( + } + if (cha.getZ() >= target.getZ()) + { + return canSeeTarget(cha.getX(), cha.getY(), z, target.getX(), target.getY(), z2); + } + return canSeeTarget(target.getX(), target.getY(), z2, cha.getX(), cha.getY(), z); + } + + @Override + public boolean canSeeTargetDebug(L2PcInstance gm, L2Object target) + { + // comments: see above + int z = gm.getZ() + 45; + int z2 = target.getZ() + 45; + if (target instanceof L2DoorInstance) + { + gm.sendMessage("door always true"); + return true; // door coordinates are hinge coords.. + } + + if (gm.getZ() >= target.getZ()) + { + return canSeeDebug(gm, (gm.getX() - L2World.MAP_MIN_X) >> 4, (gm.getY() - L2World.MAP_MIN_Y) >> 4, z, (target.getX() - L2World.MAP_MIN_X) >> 4, (target.getY() - L2World.MAP_MIN_Y) >> 4, z2); + } + return canSeeDebug(gm, (target.getX() - L2World.MAP_MIN_X) >> 4, (target.getY() - L2World.MAP_MIN_Y) >> 4, z2, (gm.getX() - L2World.MAP_MIN_X) >> 4, (gm.getY() - L2World.MAP_MIN_Y) >> 4, z); + } + + @Override + public short getNSWE(int x, int y, int z) + { + return nGetNSWE((x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4, z); + } + + @Override + public boolean canMoveFromToTarget(int x, int y, int z, int tx, int ty, int tz, int instanceId) + { + Location destiny = moveCheck(x, y, z, tx, ty, tz, instanceId); + return ((destiny.getX() == tx) && (destiny.getY() == ty) && (destiny.getZ() == tz)); + } + + @Override + public Location moveCheck(int x, int y, int z, int tx, int ty, int tz, int instanceId) + { + Location startpoint = new Location(x, y, z); + if (DoorTable.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId)) + { + return startpoint; + } + + Location destiny = new Location(tx, ty, tz); + return moveCheck(startpoint, destiny, (x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4, z, (tx - L2World.MAP_MIN_X) >> 4, (ty - L2World.MAP_MIN_Y) >> 4, tz); + } + + /** + * @see com.l2jserver.gameserver.GeoData#addGeoDataBug(com.l2jserver.gameserver.model.actor.instance.L2PcInstance, java.lang.String) + */ + @Override + public void addGeoDataBug(L2PcInstance gm, String comment) + { + int gx = (gm.getX() - L2World.MAP_MIN_X) >> 4; + int gy = (gm.getY() - L2World.MAP_MIN_Y) >> 4; + int bx = getBlock(gx); + int by = getBlock(gy); + int cx = getCell(gx); + int cy = getCell(gy); + int rx = (gx >> 11) + Config.WORLD_X_MIN; + int ry = (gy >> 11) + Config.WORLD_X_MAX; + String out = rx + ";" + ry + ";" + bx + ";" + by + ";" + cx + ";" + cy + ";" + gm.getZ() + ";" + comment + "\n"; + try + { + _geoBugsOut.write(out.getBytes()); + _geoBugsOut.flush(); + gm.sendMessage("GeoData bug saved!"); + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + gm.sendMessage("GeoData bug save Failed!"); + } + } + + @Override + public boolean canSeeTarget(int x, int y, int z, int tx, int ty, int tz) + { + return canSee((x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4, z, (tx - L2World.MAP_MIN_X) >> 4, (ty - L2World.MAP_MIN_Y) >> 4, tz); + } + + @Override + public boolean hasGeo(int x, int y) + { + int gx = (x - L2World.MAP_MIN_X) >> 4; + int gy = (y - L2World.MAP_MIN_Y) >> 4; + short region = getRegionOffset(gx, gy); + if (_geodata.contains(region)) + { + return true; + } + return false; + } + + private static boolean canSee(int x, int y, double z, int tx, int ty, int tz) + { + int dx = (tx - x); + int dy = (ty - y); + final double dz = (tz - z); + final int distance2 = (dx * dx) + (dy * dy); + + if (distance2 > 90000) // (300*300) 300*16 = 4800 in world coord + { + // Avoid too long check + return false; + } + // very short checks: 9 => 144 world distance + // this ensures NLOS function has enough points to calculate, + // it might not work when distance is small and path vertical + else if (distance2 < 82) + { + // 150 should be too deep/high. + if ((dz * dz) > 22500) + { + short region = getRegionOffset(x, y); + // geodata is loaded for region and mobs should have correct Z coordinate... + // so there would likely be a floor in between the two + if (_geodata.contains(region)) + { + return false; + } + } + return true; + } + + // Increment in Z coordinate when moving along X or Y axis + // and not straight to the target. This is done because + // calculation moves either in X or Y direction. + final int inc_x = sign(dx); + final int inc_y = sign(dy); + dx = Math.abs(dx); + dy = Math.abs(dy); + final double inc_z_directionx = (dz * dx) / (distance2); + final double inc_z_directiony = (dz * dy) / (distance2); + + // next_* are used in NLOS check from x,y + int next_x = x; + int next_y = y; + + // creates path to the target + // calculation stops when next_* == target + if (dx >= dy)// dy/dx <= 1 + { + int delta_A = 2 * dy; + int d = delta_A - dx; + int delta_B = delta_A - (2 * dx); + + for (int i = 0; i < dx; i++) + { + x = next_x; + y = next_y; + if (d > 0) + { + d += delta_B; + next_x += inc_x; + z += inc_z_directionx; + if (!nLOS(x, y, (int) z, inc_x, 0, inc_z_directionx, tz, false)) + { + return false; + } + next_y += inc_y; + z += inc_z_directiony; + // _log.warning("1: next_x:"+next_x+" next_y"+next_y); + if (!nLOS(next_x, y, (int) z, 0, inc_y, inc_z_directiony, tz, false)) + { + return false; + } + } + else + { + d += delta_A; + next_x += inc_x; + // _log.warning("2: next_x:"+next_x+" next_y"+next_y); + z += inc_z_directionx; + if (!nLOS(x, y, (int) z, inc_x, 0, inc_z_directionx, tz, false)) + { + return false; + } + } + } + } + else + { + int delta_A = 2 * dx; + int d = delta_A - dy; + int delta_B = delta_A - (2 * dy); + for (int i = 0; i < dy; i++) + { + x = next_x; + y = next_y; + if (d > 0) + { + d += delta_B; + next_y += inc_y; + z += inc_z_directiony; + if (!nLOS(x, y, (int) z, 0, inc_y, inc_z_directiony, tz, false)) + { + return false; + } + next_x += inc_x; + z += inc_z_directionx; + // _log.warning("3: next_x:"+next_x+" next_y"+next_y); + if (!nLOS(x, next_y, (int) z, inc_x, 0, inc_z_directionx, tz, false)) + { + return false; + } + } + else + { + d += delta_A; + next_y += inc_y; + // _log.warning("4: next_x:"+next_x+" next_y"+next_y); + z += inc_z_directiony; + if (!nLOS(x, y, (int) z, 0, inc_y, inc_z_directiony, tz, false)) + { + return false; + } + } + } + } + return true; + } + + /* + * Debug function for checking if there's a line of sight between two coordinates. Creates points for line of sight check (x,y,z towards target) and in each point, layer and movement checks are made with NLOS function. Coordinates here are geodata x,y but z coordinate is world coordinate + */ + private static boolean canSeeDebug(L2PcInstance gm, int x, int y, double z, int tx, int ty, int tz) + { + int dx = (tx - x); + int dy = (ty - y); + final double dz = (tz - z); + final int distance2 = (dx * dx) + (dy * dy); + + if (distance2 > 90000) // (300*300) 300*16 = 4800 in world coord + { + // Avoid too long check + gm.sendMessage("dist > 300"); + return false; + } + // very short checks: 9 => 144 world distance + // this ensures NLOS function has enough points to calculate, + // it might not work when distance is small and path vertical + else if (distance2 < 82) + { + // 150 should be too deep/high. + if ((dz * dz) > 22500) + { + short region = getRegionOffset(x, y); + // geodata is loaded for region and mobs should have correct Z coordinate... + // so there would likely be a floor in between the two + if (_geodata.get(region) != null) + { + return false; + } + } + return true; + } + + // Increment in Z coordinate when moving along X or Y axis + // and not straight to the target. This is done because + // calculation moves either in X or Y direction. + final int inc_x = sign(dx); + final int inc_y = sign(dy); + dx = Math.abs(dx); + dy = Math.abs(dy); + final double inc_z_directionx = (dz * dx) / (distance2); + final double inc_z_directiony = (dz * dy) / (distance2); + + gm.sendMessage("Los: from X: " + x + "Y: " + y + "--->> X: " + tx + " Y: " + ty); + + // next_* are used in NLOS check from x,y + int next_x = x; + int next_y = y; + + // creates path to the target + // calculation stops when next_* == target + if (dx >= dy)// dy/dx <= 1 + { + int delta_A = 2 * dy; + int d = delta_A - dx; + int delta_B = delta_A - (2 * dx); + + for (int i = 0; i < dx; i++) + { + x = next_x; + y = next_y; + if (d > 0) + { + d += delta_B; + next_x += inc_x; + z += inc_z_directionx; + if (!nLOS(x, y, (int) z, inc_x, 0, inc_z_directionx, tz, true)) + { + return false; + } + next_y += inc_y; + z += inc_z_directiony; + // _log.warning("1: next_x:"+next_x+" next_y"+next_y); + if (!nLOS(next_x, y, (int) z, 0, inc_y, inc_z_directiony, tz, true)) + { + return false; + } + } + else + { + d += delta_A; + next_x += inc_x; + // _log.warning("2: next_x:"+next_x+" next_y"+next_y); + z += inc_z_directionx; + if (!nLOS(x, y, (int) z, inc_x, 0, inc_z_directionx, tz, true)) + { + return false; + } + } + } + } + else + { + int delta_A = 2 * dx; + int d = delta_A - dy; + int delta_B = delta_A - (2 * dy); + for (int i = 0; i < dy; i++) + { + x = next_x; + y = next_y; + if (d > 0) + { + d += delta_B; + next_y += inc_y; + z += inc_z_directiony; + if (!nLOS(x, y, (int) z, 0, inc_y, inc_z_directiony, tz, true)) + { + return false; + } + next_x += inc_x; + z += inc_z_directionx; + // _log.warning("3: next_x:"+next_x+" next_y"+next_y); + if (!nLOS(x, next_y, (int) z, inc_x, 0, inc_z_directionx, tz, true)) + { + return false; + } + } + else + { + d += delta_A; + next_y += inc_y; + // _log.warning("4: next_x:"+next_x+" next_y"+next_y); + z += inc_z_directiony; + if (!nLOS(x, y, (int) z, 0, inc_y, inc_z_directiony, tz, true)) + { + return false; + } + } + } + } + return true; + } + + /* + * MoveCheck + */ + private static Location moveCheck(Location startpoint, Location destiny, int x, int y, double z, int tx, int ty, int tz) + { + int dx = (tx - x); + int dy = (ty - y); + final int distance2 = (dx * dx) + (dy * dy); + + if (distance2 == 0) + { + return destiny; + } + if (distance2 > 36100) // 190*190*16 = 3040 world coord + { + // Avoid too long check + // Currently we calculate a middle point + // for wyvern users and otherwise for comfort + double divider = Math.sqrt((double) 30000 / distance2); + tx = x + (int) (divider * dx); + ty = y + (int) (divider * dy); + int dz = (tz - startpoint.getZ()); + tz = startpoint.getZ() + (int) (divider * dz); + dx = (tx - x); + dy = (ty - y); + // return startpoint; + } + + // Increment in Z coordinate when moving along X or Y axis + // and not straight to the target. This is done because + // calculation moves either in X or Y direction. + final int inc_x = sign(dx); + final int inc_y = sign(dy); + dx = Math.abs(dx); + dy = Math.abs(dy); + + // gm.sendMessage("MoveCheck: from X: "+x+ "Y: "+y+ "--->> X: "+tx+" Y: "+ty); + + // next_* are used in NcanMoveNext check from x,y + int next_x = x; + int next_y = y; + double tempz = z; + + // creates path to the target, using only x or y direction + // calculation stops when next_* == target + if (dx >= dy)// dy/dx <= 1 + { + int delta_A = 2 * dy; + int d = delta_A - dx; + int delta_B = delta_A - (2 * dx); + + for (int i = 0; i < dx; i++) + { + x = next_x; + y = next_y; + if (d > 0) + { + d += delta_B; + next_x += inc_x; + tempz = nCanMoveNext(x, y, (int) z, next_x, next_y, tz); + if (tempz == Double.MIN_VALUE) + { + return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z); + } + z = tempz; + next_y += inc_y; + // _log.warning("2: next_x:"+next_x+" next_y"+next_y); + tempz = nCanMoveNext(next_x, y, (int) z, next_x, next_y, tz); + if (tempz == Double.MIN_VALUE) + { + return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z); + } + z = tempz; + } + else + { + d += delta_A; + next_x += inc_x; + // _log.warning("3: next_x:"+next_x+" next_y"+next_y); + tempz = nCanMoveNext(x, y, (int) z, next_x, next_y, tz); + if (tempz == Double.MIN_VALUE) + { + return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z); + } + z = tempz; + } + } + } + else + { + int delta_A = 2 * dx; + int d = delta_A - dy; + int delta_B = delta_A - (2 * dy); + for (int i = 0; i < dy; i++) + { + x = next_x; + y = next_y; + if (d > 0) + { + d += delta_B; + next_y += inc_y; + tempz = nCanMoveNext(x, y, (int) z, next_x, next_y, tz); + if (tempz == Double.MIN_VALUE) + { + return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z); + } + z = tempz; + next_x += inc_x; + // _log.warning("5: next_x:"+next_x+" next_y"+next_y); + tempz = nCanMoveNext(x, next_y, (int) z, next_x, next_y, tz); + if (tempz == Double.MIN_VALUE) + { + return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z); + } + z = tempz; + } + else + { + d += delta_A; + next_y += inc_y; + // _log.warning("6: next_x:"+next_x+" next_y"+next_y); + tempz = nCanMoveNext(x, y, (int) z, next_x, next_y, tz); + if (tempz == Double.MIN_VALUE) + { + return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z); + } + z = tempz; + } + } + } + if (z == startpoint.getZ()) + { + return destiny; + } + return new Location(destiny.getX(), destiny.getY(), (int) z); + } + + private static byte sign(int x) + { + if (x >= 0) + { + return +1; + } + return -1; + } + + // GeoEngine + private static void nInitGeodata() + { + LineNumberReader lnr = null; + try + { + _log.info("Geo Engine: - Loading Geodata..."); + File Data = new File("./data/geodata/geo_index.txt"); + if (!Data.exists()) + { + return; + } + + lnr = new LineNumberReader(new BufferedReader(new FileReader(Data))); + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + throw new Error("Failed to Load geo_index File."); + } + String line; + try + { + while ((line = lnr.readLine()) != null) + { + if (line.trim().length() == 0) + { + continue; + } + StringTokenizer st = new StringTokenizer(line, "_"); + byte rx = Byte.parseByte(st.nextToken()); + byte ry = Byte.parseByte(st.nextToken()); + loadGeodataFile(rx, ry); + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + throw new Error("Failed to Read geo_index File."); + } + finally + { + try + { + lnr.close(); + } + catch (Exception e) + { + } + } + try + { + File geo_bugs = new File("./data/geodata/geo_bugs.txt"); + + _geoBugsOut = new BufferedOutputStream(new FileOutputStream(geo_bugs, true)); + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + throw new Error("Failed to Load geo_bugs.txt File."); + } + } + + public static void unloadGeodata(byte rx, byte ry) + { + short regionoffset = (short) ((rx << 5) + ry); + _geodataIndex.remove(regionoffset); + _geodata.remove(regionoffset); + } + + public static boolean loadGeodataFile(byte rx, byte ry) + { + if ((rx < Config.WORLD_X_MIN) || (rx > Config.WORLD_X_MAX) || (ry < Config.WORLD_Y_MIN) || (ry > Config.WORLD_Y_MAX)) + { + _log.warning("Failed to Load GeoFile: invalid region " + rx + "," + ry + "\n"); + return false; + } + String fname = "./data/geodata/" + rx + "_" + ry + ".l2j"; + short regionoffset = (short) ((rx << 5) + ry); + _log.info("Geo Engine: - Loading: " + fname + " -> region offset: " + regionoffset + "X: " + rx + " Y: " + ry); + File Geo = new File(fname); + int size, index = 0, block = 0, flor = 0; + + try (RandomAccessFile raf = new RandomAccessFile(Geo, "r")) + { + FileChannel roChannel = raf.getChannel(); + size = (int) roChannel.size(); + MappedByteBuffer geo; + if (Config.FORCE_GEODATA) + { + // it is not guarantee, because the underlying operating system may have paged out some of the buffer's data + geo = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size).load(); + } + else + { + geo = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size); + } + geo.order(ByteOrder.LITTLE_ENDIAN); + + if (size > 196608) + { + // Indexing geo files, so we will know where each block starts + IntBuffer indexs = IntBuffer.allocate(65536); + while (block < 65536) + { + byte type = geo.get(index); + indexs.put(block, index); + block++; + index++; + if (type == 0) + { + index += 2; // 1x short + } + else if (type == 1) + { + index += 128; // 64 x short + } + else + { + int b; + for (b = 0; b < 64; b++) + { + byte layers = geo.get(index); + index += (layers << 1) + 1; + if (layers > flor) + { + flor = layers; + } + } + } + } + _geodataIndex.put(regionoffset, indexs); + } + _geodata.put(regionoffset, geo); + + _log.info("Geo Engine: - Max Layers: " + flor + " Size: " + size + " Loaded: " + index); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Failed to Load GeoFile at block: " + block, e); + return false; + } + return true; + } + + // Geodata Methods + /** + * @param x + * @param y + * @return Region Offset + */ + private static short getRegionOffset(int x, int y) + { + int rx = x >> 11; // =/(256 * 8) + int ry = y >> 11; + return (short) (((rx + Config.WORLD_X_MIN) << 5) + (ry + Config.WORLD_Y_MIN)); + } + + /** + * @param pos + * @return Block Index: 0-255 + */ + private static int getBlock(int geo_pos) + { + return (geo_pos >> 3) % 256; + } + + /** + * @param pos + * @return Cell Index: 0-7 + */ + private static int getCell(int geo_pos) + { + return geo_pos % 8; + } + + // Geodata Functions + + /** + * @param x + * @param y + * @return Type of geo_block: 0-2 + */ + private static short nGetType(int x, int y) + { + short region = getRegionOffset(x, y); + int blockX = getBlock(x); + int blockY = getBlock(y); + int index = 0; + final IntBuffer idx = _geodataIndex.get(region); + // Geodata without index - it is just empty so index can be calculated on the fly + if (idx == null) + { + index = ((blockX << 8) + blockY) * 3; + // Get Index for current block of current geodata region + } + else + { + index = idx.get((blockX << 8) + blockY); + } + // Buffer that Contains current Region GeoData + ByteBuffer geo = _geodata.get(region); + if (geo == null) + { + if (Config.DEBUG) + { + _log.warning("Geo Region - Region Offset: " + region + " dosnt exist!!"); + } + return 0; + } + return geo.get(index); + } + + /** + * @param x + * @param y + * @param z + * @return Nearest Z + */ + private static short nGetHeight(int geox, int geoy, int z) + { + short region = getRegionOffset(geox, geoy); + int blockX = getBlock(geox); + int blockY = getBlock(geoy); + int cellX, cellY, index; + final IntBuffer idx = _geodataIndex.get(region); + // Geodata without index - it is just empty so index can be calculated on the fly + if (idx == null) + { + index = ((blockX << 8) + blockY) * 3; + // Get Index for current block of current region geodata + } + else + { + index = idx.get(((blockX << 8)) + (blockY)); + } + // Buffer that Contains current Region GeoData + ByteBuffer geo = _geodata.get(region); + if (geo == null) + { + if (Config.DEBUG) + { + _log.warning("Geo Region - Region Offset: " + region + " dosnt exist!!"); + } + return (short) z; + } + // Read current block type: 0-flat,1-complex,2-multilevel + byte type = geo.get(index); + index++; + if (type == 0) + { + return geo.getShort(index); + } + else if (type == 1)// complex + { + cellX = getCell(geox); + cellY = getCell(geoy); + index += ((cellX << 3) + cellY) << 1; + short height = geo.getShort(index); + height = (short) (height & 0x0fff0); + height = (short) (height >> 1); // height / 2 + return height; + } + else + // multilevel + { + cellX = getCell(geox); + cellY = getCell(geoy); + int offset = (cellX << 3) + cellY; + while (offset > 0) + { + byte lc = geo.get(index); + index += (lc << 1) + 1; + offset--; + } + byte layers = geo.get(index); + index++; + short height = -1; + if ((layers <= 0) || (layers > 125)) + { + _log.warning("Broken geofile (case1), region: " + region + " - invalid layer count: " + layers + " at: " + geox + " " + geoy); + return (short) z; + } + short temph = Short.MIN_VALUE; + while (layers > 0) + { + height = geo.getShort(index); + height = (short) (height & 0x0fff0); + height = (short) (height >> 1); // height / 2 + if (((z - temph) * (z - temph)) > ((z - height) * (z - height))) + { + temph = height; + } + layers--; + index += 2; + } + return temph; + } + } + + /** + * @param x + * @param y + * @param z + * @return One layer higher Z than parameter Z + */ + private static short nGetUpperHeight(int geox, int geoy, int z) + { + short region = getRegionOffset(geox, geoy); + int blockX = getBlock(geox); + int blockY = getBlock(geoy); + int cellX, cellY, index; + // Geodata without index - it is just empty so index can be calculated on the fly + final IntBuffer idx = _geodataIndex.get(region); + if (idx == null) + { + index = ((blockX << 8) + blockY) * 3; + // Get Index for current block of current region geodata + } + else + { + index = idx.get(((blockX << 8)) + (blockY)); + } + // Buffer that Contains current Region GeoData + ByteBuffer geo = _geodata.get(region); + if (geo == null) + { + if (Config.DEBUG) + { + _log.warning("Geo Region - Region Offset: " + region + " dosnt exist!!"); + } + return (short) z; + } + // Read current block type: 0-flat,1-complex,2-multilevel + byte type = geo.get(index); + index++; + if (type == 0) + { + return geo.getShort(index); + } + else if (type == 1)// complex + { + cellX = getCell(geox); + cellY = getCell(geoy); + index += ((cellX << 3) + cellY) << 1; + short height = geo.getShort(index); + height = (short) (height & 0x0fff0); + height = (short) (height >> 1); // height / 2 + return height; + } + else + // multilevel + { + cellX = getCell(geox); + cellY = getCell(geoy); + int offset = (cellX << 3) + cellY; + while (offset > 0) + { + byte lc = geo.get(index); + index += (lc << 1) + 1; + offset--; + } + byte layers = geo.get(index); + index++; + short height = -1; + if ((layers <= 0) || (layers > 125)) + { + _log.warning("Broken geofile (case1), region: " + region + " - invalid layer count: " + layers + " at: " + geox + " " + geoy); + return (short) z; + } + short temph = Short.MAX_VALUE; + while (layers > 0) // from higher to lower + { + height = geo.getShort(index); + height = (short) (height & 0x0fff0); + height = (short) (height >> 1); // height / 2 + if (height < z) + { + return temph; + } + temph = height; + layers--; + index += 2; + } + return temph; + } + } + + /** + * @param x + * @param y + * @param zmin + * @param zmax + * @return Z betwen zmin and zmax + */ + private static short nGetSpawnHeight(int geox, int geoy, int zmin, int zmax, L2Spawn spawn) + { + short region = getRegionOffset(geox, geoy); + int blockX = getBlock(geox); + int blockY = getBlock(geoy); + int cellX, cellY, index; + short temph = Short.MIN_VALUE; + final IntBuffer idx = _geodataIndex.get(region); + // Geodata without index - it is just empty so index can be calculated on the fly + if (idx == null) + { + index = ((blockX << 8) + blockY) * 3; + // Get Index for current block of current region geodata + } + else + { + index = idx.get(((blockX << 8)) + (blockY)); + } + // Buffer that Contains current Region GeoData + ByteBuffer geo = _geodata.get(region); + if (geo == null) + { + if (Config.DEBUG) + { + _log.warning("Geo Region - Region Offset: " + region + " dosnt exist!!"); + } + return (short) zmin; + } + // Read current block type: 0-flat,1-complex,2-multilevel + byte type = geo.get(index); + index++; + if (type == 0) + { + temph = geo.getShort(index); + } + else if (type == 1)// complex + { + cellX = getCell(geox); + cellY = getCell(geoy); + index += ((cellX << 3) + cellY) << 1; + short height = geo.getShort(index); + height = (short) (height & 0x0fff0); + height = (short) (height >> 1); // height / 2 + temph = height; + } + else + // multilevel + { + cellX = getCell(geox); + cellY = getCell(geoy); + short height; + int offset = (cellX << 3) + cellY; + while (offset > 0) + { + byte lc = geo.get(index); + index += (lc << 1) + 1; + offset--; + } + // Read current block type: 0-flat,1-complex,2-multilevel + byte layers = geo.get(index); + index++; + if ((layers <= 0) || (layers > 125)) + { + _log.warning("Broken geofile (case2), region: " + region + " - invalid layer count: " + layers + " at: " + geox + " " + geoy); + return (short) zmin; + } + while (layers > 0) + { + height = geo.getShort(index); + height = (short) (height & 0x0fff0); + height = (short) (height >> 1); // height / 2 + if (((zmin - temph) * (zmin - temph)) > ((zmin - height) * (zmin - height))) + { + temph = height; + } + layers--; + index += 2; + } + if ((temph > (zmax + 200)) || (temph < (zmin - 200))) + { + if (Config.DEBUG) + { + _log.warning("SpawnHeight Error - Couldnt find correct layer to spawn NPC - GeoData or Spawnlist Bug!: zmin: " + zmin + " zmax: " + zmax + " value: " + temph + " Spawn: " + spawn + " at: " + geox + " : " + geoy); + } + return (short) zmin; + } + } + if ((temph > (zmax + 1000)) || (temph < (zmin - 1000))) + { + if (Config.DEBUG) + { + _log.warning("SpawnHeight Error - Spawnlist z value is wrong or GeoData error: zmin: " + zmin + " zmax: " + zmax + " value: " + temph + " Spawn: " + spawn + " at: " + geox + " : " + geoy); + } + return (short) zmin; + } + return temph; + } + + /** + * @param x + * @param y + * @param z + * @param tx + * @param ty + * @param tz + * @return True if char can move to (tx,ty,tz) + */ + private static double nCanMoveNext(int x, int y, int z, int tx, int ty, int tz) + { + short region = getRegionOffset(x, y); + int blockX = getBlock(x); + int blockY = getBlock(y); + int cellX, cellY; + short NSWE = 0; + + int index = 0; + final IntBuffer idx = _geodataIndex.get(region); + // Geodata without index - it is just empty so index can be calculated on the fly + if (idx == null) + { + index = ((blockX << 8) + blockY) * 3; + // Get Index for current block of current region geodata + } + else + { + index = idx.get(((blockX << 8)) + (blockY)); + } + // Buffer that Contains current Region GeoData + ByteBuffer geo = _geodata.get(region); + if (geo == null) + { + if (Config.DEBUG) + { + _log.warning("Geo Region - Region Offset: " + region + " dosnt exist!!"); + } + return z; + } + // Read current block type: 0-flat,1-complex,2-multilevel + byte type = geo.get(index); + index++; + if (type == 0) + { + return geo.getShort(index); + } + else if (type == 1) // complex + { + cellX = getCell(x); + cellY = getCell(y); + index += ((cellX << 3) + cellY) << 1; + short height = geo.getShort(index); + NSWE = (short) (height & 0x0F); + height = (short) (height & 0x0fff0); + height = (short) (height >> 1); // height / 2 + if (checkNSWE(NSWE, x, y, tx, ty)) + { + return height; + } + return Double.MIN_VALUE; + } + else + // multilevel, type == 2 + { + cellX = getCell(x); + cellY = getCell(y); + int offset = (cellX << 3) + cellY; + while (offset > 0) // iterates (too many times?) to get to layer count + { + byte lc = geo.get(index); + index += (lc << 1) + 1; + offset--; + } + byte layers = geo.get(index); + // _log.warning("layers"+layers); + index++; + short height = -1; + if ((layers <= 0) || (layers > 125)) + { + _log.warning("Broken geofile (case3), region: " + region + " - invalid layer count: " + layers + " at: " + x + " " + y); + return z; + } + short tempz = Short.MIN_VALUE; + while (layers > 0) + { + height = geo.getShort(index); + height = (short) (height & 0x0fff0); + height = (short) (height >> 1); // height / 2 + + // searches the closest layer to current z coordinate + if (((z - tempz) * (z - tempz)) > ((z - height) * (z - height))) + { + // layercurr = layers; + tempz = height; + NSWE = geo.getShort(index); + NSWE = (short) (NSWE & 0x0F); + } + layers--; + index += 2; + } + if (checkNSWE(NSWE, x, y, tx, ty)) + { + return tempz; + } + return Double.MIN_VALUE; + } + } + + /** + * @param x + * @param y + * @param z + * @param inc_x + * @param inc_y + * @param tz + * @return True if Char can see target + */ + private static boolean nLOS(int x, int y, int z, int inc_x, int inc_y, double inc_z, int tz, boolean debug) + { + short region = getRegionOffset(x, y); + int blockX = getBlock(x); + int blockY = getBlock(y); + int cellX, cellY; + short NSWE = 0; + + int index; + final IntBuffer idx = _geodataIndex.get(region); + // Geodata without index - it is just empty so index can be calculated on the fly + if (idx == null) + { + index = ((blockX << 8) + blockY) * 3; + // Get Index for current block of current region geodata + } + else + { + index = idx.get(((blockX << 8)) + (blockY)); + } + // Buffer that Contains current Region GeoData + ByteBuffer geo = _geodata.get(region); + if (geo == null) + { + if (Config.DEBUG) + { + _log.warning("Geo Region - Region Offset: " + region + " dosnt exist!!"); + } + return true; + } + // Read current block type: 0-flat,1-complex,2-multilevel + byte type = geo.get(index); + index++; + if (type == 0) // flat, movement and sight always possible + { + short height = geo.getShort(index); + if (debug) + { + _log.warning("flatheight:" + height); + } + if (z > height) + { + return (z + inc_z) > height; + } + return (z + inc_z) < height; + } + else if (type == 1) // complex + { + cellX = getCell(x); + cellY = getCell(y); + index += ((cellX << 3) + cellY) << 1; + short height = geo.getShort(index); + NSWE = (short) (height & 0x0F); + height = (short) (height & 0x0fff0); + height = (short) (height >> 1); // height / 2 + if (!checkNSWE(NSWE, x, y, x + inc_x, y + inc_y)) + { + if (debug) + { + _log.warning("height:" + height + " z" + z); + } + if (z < nGetUpperHeight(x + inc_x, y + inc_y, height)) + { + return false; // an obstacle high enough + } + return true; + } + return true; + } + else + // multilevel, type == 2 + { + cellX = getCell(x); + cellY = getCell(y); + + int offset = (cellX << 3) + cellY; + while (offset > 0) // iterates (too many times?) to get to layer count + { + byte lc = geo.get(index); + index += (lc << 1) + 1; + offset--; + } + byte layers = geo.get(index); + + index++; + short tempZ = -1; + if ((layers <= 0) || (layers > 125)) + { + _log.warning("Broken geofile (case4), region: " + region + " - invalid layer count: " + layers + " at: " + x + " " + y); + return false; + } + short upperHeight = Short.MAX_VALUE; // big positive value + short lowerHeight = Short.MIN_VALUE; // big negative value + byte temp_layers = layers; + boolean highestlayer = true; + while (temp_layers > 0) // from higher to lower + { + // reads tempZ for current layer, result in world z coordinate + tempZ = geo.getShort(index); + tempZ = (short) (tempZ & 0x0fff0); + tempZ = (short) (tempZ >> 1); // tempZ / 2 + + if (z > tempZ) + { + lowerHeight = tempZ; + NSWE = geo.getShort(index); + NSWE = (short) (NSWE & 0x0F); + break; + } + highestlayer = false; + upperHeight = tempZ; + + temp_layers--; + index += 2; + } + if (debug) + { + _log.warning("z:" + z + " x: " + cellX + " y:" + cellY + " la " + layers + " lo:" + lowerHeight + " up:" + upperHeight); + } + // Check if LOS goes under a layer/floor + // clearly under layer but not too much under + // lowerheight here only for geodata bug checking, layers very close? maybe could be removed + if (((z - upperHeight) < -10) && ((z - upperHeight) > (inc_z - 20)) && ((z - lowerHeight) > 40)) + { + if (debug) + { + _log.warning("false, incz" + inc_z); + } + return false; + } + + // or there's a fence/wall ahead when we're not on highest layer + if (!highestlayer) + { + // a probable wall, there's movement block and layers above you + if (!checkNSWE(NSWE, x, y, x + inc_x, y + inc_y)) // cannot move + { + if (debug) + { + _log.warning("block and next in x" + inc_x + " y" + inc_y + " is:" + nGetUpperHeight(x + inc_x, y + inc_y, lowerHeight)); + } + // check one inc_x inc_y further, for the height there + if (z < nGetUpperHeight(x + inc_x, y + inc_y, lowerHeight)) + { + return false; // a wall + } + return true; // we see over it, e.g. a fence + } + return true; + } + if (!checkNSWE(NSWE, x, y, x + inc_x, y + inc_y)) + { + // check one inc_x inc_y further, for the height there + if (z < nGetUpperHeight(x + inc_x, y + inc_y, lowerHeight)) + { + return false; // we hit an obstacle high enough + } + return true; + } + return true; + } + } + + /** + * @param x + * @param y + * @param z + * @return NSWE: 0-15 + */ + private static short nGetNSWE(int x, int y, int z) + { + short region = getRegionOffset(x, y); + int blockX = getBlock(x); + int blockY = getBlock(y); + int cellX, cellY; + short NSWE = 0; + + int index = 0; + final IntBuffer idx = _geodataIndex.get(region); + // Geodata without index - it is just empty so index can be calculated on the fly + if (idx == null) + { + index = ((blockX << 8) + blockY) * 3; + // Get Index for current block of current region geodata + } + else + { + index = idx.get(((blockX << 8)) + (blockY)); + } + // Buffer that Contains current Region GeoData + ByteBuffer geo = _geodata.get(region); + if (geo == null) + { + if (Config.DEBUG) + { + _log.warning("Geo Region - Region Offset: " + region + " dosnt exist!!"); + } + return 15; + } + // Read current block type: 0-flat,1-complex,2-multilevel + byte type = geo.get(index); + index++; + if (type == 0) + { + return 15; + } + else if (type == 1)// complex + { + cellX = getCell(x); + cellY = getCell(y); + index += ((cellX << 3) + cellY) << 1; + short height = geo.getShort(index); + NSWE = (short) (height & 0x0F); + } + else + // multilevel + { + cellX = getCell(x); + cellY = getCell(y); + int offset = (cellX << 3) + cellY; + while (offset > 0) + { + byte lc = geo.get(index); + index += (lc << 1) + 1; + offset--; + } + byte layers = geo.get(index); + index++; + short height = -1; + if ((layers <= 0) || (layers > 125)) + { + _log.warning("Broken geofile (case5), region: " + region + " - invalid layer count: " + layers + " at: " + x + " " + y); + return 15; + } + short tempz = Short.MIN_VALUE; + while (layers > 0) + { + height = geo.getShort(index); + height = (short) (height & 0x0fff0); + height = (short) (height >> 1); // height / 2 + + if (((z - tempz) * (z - tempz)) > ((z - height) * (z - height))) + { + tempz = height; + NSWE = geo.get(index); + NSWE = (short) (NSWE & 0x0F); + } + layers--; + index += 2; + } + } + return NSWE; + } + + /** + * @param x + * @param y + * @param z + * @return array [0] - height, [1] - NSWE + */ + @Override + public short getHeightAndNSWE(int x, int y, int z) + { + short region = getRegionOffset(x, y); + int blockX = getBlock(x); + int blockY = getBlock(y); + int cellX, cellY; + + int index = 0; + final IntBuffer idx = _geodataIndex.get(region); + // Geodata without index - it is just empty so index can be calculated on the fly + if (idx == null) + { + index = ((blockX << 8) + blockY) * 3; + // Get Index for current block of current region geodata + } + else + { + index = idx.get(((blockX << 8)) + (blockY)); + } + // Buffer that Contains current Region GeoData + ByteBuffer geo = _geodata.get(region); + if (geo == null) + { + if (Config.DEBUG) + { + _log.warning("Geo Region - Region Offset: " + region + " dosnt exist!!"); + } + return (short) ((z << 1) | NSWE_ALL); + } + // Read current block type: 0-flat,1-complex,2-multilevel + byte type = geo.get(index); + index++; + if (type == 0) + { + return (short) ((geo.getShort(index) << 1) | NSWE_ALL); + } + else if (type == 1)// complex + { + cellX = getCell(x); + cellY = getCell(y); + index += ((cellX << 3) + cellY) << 1; + return geo.getShort(index); + } + else + // multilevel + { + cellX = getCell(x); + cellY = getCell(y); + int offset = (cellX << 3) + cellY; + while (offset > 0) + { + byte lc = geo.get(index); + index += (lc << 1) + 1; + offset--; + } + byte layers = geo.get(index); + index++; + short height = -1; + if ((layers <= 0) || (layers > 125)) + { + _log.warning("Broken geofile (case1), region: " + region + " - invalid layer count: " + layers + " at: " + x + " " + y); + return (short) ((z << 1) | NSWE_ALL); + } + short temph = Short.MIN_VALUE; + short result = 0; + while (layers > 0) + { + short block = geo.getShort(index); + height = (short) (block & 0x0fff0); + height = (short) (height >> 1); // height / 2 + if (((z - temph) * (z - temph)) > ((z - height) * (z - height))) + { + temph = height; + result = block; + } + layers--; + index += 2; + } + return result; + } + } + + /** + * @param NSWE + * @param x + * @param y + * @param tx + * @param ty + * @return True if NSWE dont block given direction + */ + private static boolean checkNSWE(short NSWE, int x, int y, int tx, int ty) + { + // Check NSWE + if (NSWE == 15) + { + return true; + } + if (tx > x)// E + { + if ((NSWE & EAST) == 0) + { + return false; + } + } + else if (tx < x)// W + { + if ((NSWE & WEST) == 0) + { + return false; + } + } + if (ty > y)// S + { + if ((NSWE & SOUTH) == 0) + { + return false; + } + } + else if (ty < y)// N + { + if ((NSWE & NORTH) == 0) + { + return false; + } + } + return true; + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final GeoEngine _instance = new GeoEngine(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/GmListTable.java b/L2J_Server/java/com/l2jserver/gameserver/GmListTable.java new file mode 100644 index 00000000..8f95067a --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/GmListTable.java @@ -0,0 +1,184 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; + +/** + * This class stores references to all online game masters. (access level > 100) + * @version $Revision: 1.2.2.1.2.7 $ $Date: 2005/04/05 19:41:24 $ + */ +public class GmListTable +{ + private static Logger _log = Logger.getLogger(GmListTable.class.getName()); + + /** Set(L2PcInstance>) containing all the GM in game */ + private final FastMap _gmList; + + public static GmListTable getInstance() + { + return SingletonHolder._instance; + } + + public FastList getAllGms(boolean includeHidden) + { + FastList tmpGmList = new FastList<>(); + + for (FastMap.Entry n = _gmList.head(), end = _gmList.tail(); (n = n.getNext()) != end;) + { + if (includeHidden || !n.getValue()) + { + tmpGmList.add(n.getKey()); + } + } + + return tmpGmList; + } + + public FastList getAllGmNames(boolean includeHidden) + { + FastList tmpGmList = new FastList<>(); + + for (FastMap.Entry n = _gmList.head(), end = _gmList.tail(); (n = n.getNext()) != end;) + { + if (!n.getValue()) + { + tmpGmList.add(n.getKey().getName()); + } + else if (includeHidden) + { + tmpGmList.add(n.getKey().getName() + " (invis)"); + } + } + + return tmpGmList; + } + + private GmListTable() + { + _gmList = new FastMap().shared(); + } + + /** + * Add a L2PcInstance player to the Set _gmList + */ + public void addGm(L2PcInstance player, boolean hidden) + { + if (Config.DEBUG) + { + _log.fine("added gm: " + player.getName()); + } + _gmList.put(player, hidden); + } + + public void deleteGm(L2PcInstance player) + { + if (Config.DEBUG) + { + _log.fine("deleted gm: " + player.getName()); + } + + _gmList.remove(player); + } + + /** + * GM will be displayed on clients gmlist + * @param player + */ + public void showGm(L2PcInstance player) + { + FastMap.Entry gm = _gmList.getEntry(player); + if (gm != null) + { + gm.setValue(false); + } + } + + /** + * GM will no longer be displayed on clients gmlist + * @param player + */ + public void hideGm(L2PcInstance player) + { + FastMap.Entry gm = _gmList.getEntry(player); + if (gm != null) + { + gm.setValue(true); + } + } + + public boolean isGmOnline(boolean includeHidden) + { + for (FastMap.Entry n = _gmList.head(), end = _gmList.tail(); (n = n.getNext()) != end;) + { + if (includeHidden || !n.getValue()) + { + return true; + } + } + + return false; + } + + public void sendListToPlayer(L2PcInstance player) + { + if (isGmOnline(player.isGM())) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.GM_LIST)); + + for (String name : getAllGmNames(player.isGM())) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.GM_C1); + sm.addString(name); + player.sendPacket(sm); + } + } + else + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_GM_PROVIDING_SERVICE_NOW)); + } + } + + public static void broadcastToGMs(L2GameServerPacket packet) + { + for (L2PcInstance gm : getInstance().getAllGms(true)) + { + gm.sendPacket(packet); + } + } + + public static void broadcastMessageToGMs(String message) + { + for (L2PcInstance gm : getInstance().getAllGms(true)) + { + gm.sendMessage(message); + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final GmListTable _instance = new GmListTable(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/InstanceListManager.java b/L2J_Server/java/com/l2jserver/gameserver/InstanceListManager.java new file mode 100644 index 00000000..3f083327 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/InstanceListManager.java @@ -0,0 +1,24 @@ +package com.l2jserver.gameserver; + +/** + * Interface for managers of list of instances. + * @author fordfrog + */ +public interface InstanceListManager +{ + + /** + * Loads instances with their data from persistant format. This method has no side effect as calling methods of another instance manager. + */ + void loadInstances(); + + /** + * For each loaded instance, updates references to related instances. + */ + void updateReferences(); + + /** + * Activates instances so their setup is performed. + */ + void activateInstances(); +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/Item.java b/L2J_Server/java/com/l2jserver/gameserver/Item.java new file mode 100644 index 00000000..69aac812 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/Item.java @@ -0,0 +1,44 @@ +/* + * $Header: Item.java, 2/08/2005 00:49:12 luisantonioa Exp $ + * + * $Author: luisantonioa $ $Date: 2/08/2005 00:49:12 $ $Revision: 1 $ $Log: + * Item.java,v $ Revision 1 2/08/2005 00:49:12 luisantonioa Added copyright + * notice + * + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import com.l2jserver.gameserver.templates.StatsSet; +import com.l2jserver.gameserver.templates.item.L2Item; + +/** + * This class ... + * @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $ + */ +public class Item +{ + public int id; + + public String type; + + public String name; + + public StatsSet set; + + public int currentLevel; + + public L2Item item; +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ItemsAutoDestroy.java b/L2J_Server/java/com/l2jserver/gameserver/ItemsAutoDestroy.java new file mode 100644 index 00000000..c914cf73 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ItemsAutoDestroy.java @@ -0,0 +1,124 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.util.List; +import java.util.logging.Logger; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.templates.item.L2EtcItemType; + +public class ItemsAutoDestroy +{ + protected static final Logger _log = Logger.getLogger(ItemsAutoDestroy.class.getName()); + protected List _items = null; + protected static long _sleep; + + private ItemsAutoDestroy() + { + _log.info("Initializing ItemsAutoDestroy."); + _items = new FastList<>(); + _sleep = Config.AUTODESTROY_ITEM_AFTER * 1000; + if (_sleep == 0) + { + _sleep = 3600000; + } + ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new CheckItemsForDestroy(), 5000, 5000); + } + + public static ItemsAutoDestroy getInstance() + { + return SingletonHolder._instance; + } + + public synchronized void addItem(L2ItemInstance item) + { + item.setDropTime(System.currentTimeMillis()); + _items.add(item); + } + + public synchronized void removeItems() + { + if (Config.DEBUG) + { + _log.info("[ItemsAutoDestroy] : " + _items.size() + " items to check."); + } + + if (_items.isEmpty()) + { + return; + } + + long curtime = System.currentTimeMillis(); + for (L2ItemInstance item : _items) + { + if ((item == null) || (item.getDropTime() == 0) || (item.getLocation() != L2ItemInstance.ItemLocation.VOID)) + { + _items.remove(item); + } + else + { + if (item.getItemType() == L2EtcItemType.HERB) + { + if ((curtime - item.getDropTime()) > Config.HERB_AUTO_DESTROY_TIME) + { + L2World.getInstance().removeVisibleObject(item, item.getWorldRegion()); + L2World.getInstance().removeObject(item); + _items.remove(item); + if (Config.SAVE_DROPPED_ITEM) + { + ItemsOnGroundManager.getInstance().removeObject(item); + } + } + } + else if ((curtime - item.getDropTime()) > _sleep) + { + L2World.getInstance().removeVisibleObject(item, item.getWorldRegion()); + L2World.getInstance().removeObject(item); + _items.remove(item); + if (Config.SAVE_DROPPED_ITEM) + { + ItemsOnGroundManager.getInstance().removeObject(item); + } + } + } + } + + if (Config.DEBUG) + { + _log.info("[ItemsAutoDestroy] : " + _items.size() + " items remaining."); + } + } + + protected class CheckItemsForDestroy extends Thread + { + @Override + public void run() + { + removeItems(); + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final ItemsAutoDestroy _instance = new ItemsAutoDestroy(); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/LoginServerThread.java b/L2J_Server/java/com/l2jserver/gameserver/LoginServerThread.java new file mode 100644 index 00000000..9e72d1f5 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/LoginServerThread.java @@ -0,0 +1,702 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.RSAKeyGenParameterSpec; +import java.security.spec.RSAPublicKeySpec; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.L2GameClient; +import com.l2jserver.gameserver.network.L2GameClient.GameClientState; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.gameserverpackets.AuthRequest; +import com.l2jserver.gameserver.network.gameserverpackets.BlowFishKey; +import com.l2jserver.gameserver.network.gameserverpackets.ChangeAccessLevel; +import com.l2jserver.gameserver.network.gameserverpackets.PlayerAuthRequest; +import com.l2jserver.gameserver.network.gameserverpackets.PlayerInGame; +import com.l2jserver.gameserver.network.gameserverpackets.PlayerLogout; +import com.l2jserver.gameserver.network.gameserverpackets.PlayerTracert; +import com.l2jserver.gameserver.network.gameserverpackets.ServerStatus; +import com.l2jserver.gameserver.network.loginserverpackets.AuthResponse; +import com.l2jserver.gameserver.network.loginserverpackets.InitLS; +import com.l2jserver.gameserver.network.loginserverpackets.KickPlayer; +import com.l2jserver.gameserver.network.loginserverpackets.LoginServerFail; +import com.l2jserver.gameserver.network.loginserverpackets.PlayerAuthResponse; +import com.l2jserver.gameserver.network.serverpackets.CharSelectionInfo; +import com.l2jserver.gameserver.network.serverpackets.LoginFail; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.util.Util; +import com.l2jserver.util.crypt.NewCrypt; +import com.l2jserver.util.network.BaseSendablePacket; + +public class LoginServerThread extends Thread +{ + protected static final Logger _log = Logger.getLogger(LoginServerThread.class.getName()); + protected static final Logger _logAccounting = Logger.getLogger("accounting"); + + /** {@see com.l2jserver.loginserver.LoginServer#PROTOCOL_REV } */ + private static final int REVISION = 0x0104; + private RSAPublicKey _publicKey; + private final String _hostname; + private final int _port; + private final int _gamePort; + private Socket _loginSocket; + private InputStream _in; + private OutputStream _out; + + /** + * The BlowFish engine used to encrypt packets
    + * It is first initialized with a unified key:
    + * "_;v.]05-31!|+-%xT!^[$\00"
    + *
    + * and then after handshake, with a new key sent by
    + * loginserver during the handshake. This new key is stored
    + * in {@link #_blowfishKey} + */ + private NewCrypt _blowfish; + private byte[] _blowfishKey; + private byte[] _hexID; + private final boolean _acceptAlternate; + private int _requestID; + private int _serverID; + private final boolean _reserveHost; + private int _maxPlayer; + private final List _waitingClients; + private final Map _accountsInGameServer; + private int _status; + private String _serverName; + private final String[] _subnets; + private final String[] _hosts; + + private LoginServerThread() + { + super("LoginServerThread"); + _port = Config.GAME_SERVER_LOGIN_PORT; + _gamePort = Config.PORT_GAME; + _hostname = Config.GAME_SERVER_LOGIN_HOST; + _hexID = Config.HEX_ID; + if (_hexID == null) + { + _requestID = Config.REQUEST_ID; + _hexID = Util.generateHex(16); + } + else + { + _requestID = Config.SERVER_ID; + } + _acceptAlternate = Config.ACCEPT_ALTERNATE_ID; + _reserveHost = Config.RESERVE_HOST_ON_LOGIN; + _subnets = Config.GAME_SERVER_SUBNETS; + _hosts = Config.GAME_SERVER_HOSTS; + _waitingClients = new FastList<>(); + _accountsInGameServer = new FastMap().shared(); + _maxPlayer = Config.MAXIMUM_ONLINE_USERS; + } + + public static LoginServerThread getInstance() + { + return SingletonHolder._instance; + } + + @Override + public void run() + { + while (!isInterrupted()) + { + int lengthHi = 0; + int lengthLo = 0; + int length = 0; + boolean checksumOk = false; + try + { + // Connection + _log.info("Connecting to login on " + _hostname + ":" + _port); + _loginSocket = new Socket(_hostname, _port); + _in = _loginSocket.getInputStream(); + _out = new BufferedOutputStream(_loginSocket.getOutputStream()); + + // init Blowfish + _blowfishKey = Util.generateHex(40); + _blowfish = new NewCrypt("_;v.]05-31!|+-%xT!^[$\00"); + while (!isInterrupted()) + { + lengthLo = _in.read(); + lengthHi = _in.read(); + length = (lengthHi * 256) + lengthLo; + + if (lengthHi < 0) + { + _log.finer("LoginServerThread: Login terminated the connection."); + break; + } + + byte[] incoming = new byte[length - 2]; + + int receivedBytes = 0; + int newBytes = 0; + int left = length - 2; + while ((newBytes != -1) && (receivedBytes < (length - 2))) + { + newBytes = _in.read(incoming, receivedBytes, left); + receivedBytes = receivedBytes + newBytes; + left -= newBytes; + } + + if (receivedBytes != (length - 2)) + { + _log.warning("Incomplete Packet is sent to the server, closing connection.(LS)"); + break; + } + + // decrypt if we have a key + byte[] decrypt = _blowfish.decrypt(incoming); + checksumOk = NewCrypt.verifyChecksum(decrypt); + + if (!checksumOk) + { + _log.warning("Incorrect packet checksum, ignoring packet (LS)"); + break; + } + + if (Config.DEBUG) + { + _log.warning("[C]\n" + Util.printData(decrypt)); + } + + int packetType = decrypt[0] & 0xff; + switch (packetType) + { + case 0x00: + InitLS init = new InitLS(decrypt); + if (Config.DEBUG) + { + _log.info("Init received"); + } + if (init.getRevision() != REVISION) + { + // TODO: revision mismatch + _log.warning("/!\\ Revision mismatch between LS and GS /!\\"); + break; + } + try + { + KeyFactory kfac = KeyFactory.getInstance("RSA"); + BigInteger modulus = new BigInteger(init.getRSAKey()); + RSAPublicKeySpec kspec1 = new RSAPublicKeySpec(modulus, RSAKeyGenParameterSpec.F4); + _publicKey = (RSAPublicKey) kfac.generatePublic(kspec1); + if (Config.DEBUG) + { + _log.info("RSA key set up"); + } + } + + catch (GeneralSecurityException e) + { + _log.warning("Troubles while init the public key send by login"); + break; + } + // send the blowfish key through the rsa encryption + BlowFishKey bfk = new BlowFishKey(_blowfishKey, _publicKey); + sendPacket(bfk); + if (Config.DEBUG) + { + _log.info("Sent new blowfish key"); + } + // now, only accept paket with the new encryption + _blowfish = new NewCrypt(_blowfishKey); + if (Config.DEBUG) + { + _log.info("Changed blowfish key"); + } + AuthRequest ar = new AuthRequest(_requestID, _acceptAlternate, _hexID, _gamePort, _reserveHost, _maxPlayer, _subnets, _hosts); + sendPacket(ar); + if (Config.DEBUG) + { + _log.info("Sent AuthRequest to login"); + } + break; + case 0x01: + LoginServerFail lsf = new LoginServerFail(decrypt); + _log.info("Damn! Registeration Failed: " + lsf.getReasonString()); + // login will close the connection here + break; + case 0x02: + AuthResponse aresp = new AuthResponse(decrypt); + _serverID = aresp.getServerId(); + _serverName = aresp.getServerName(); + Config.saveHexid(_serverID, hexToString(_hexID)); + _log.info("Registered on login as Server " + _serverID + " : " + _serverName); + ServerStatus st = new ServerStatus(); + if (Config.SERVER_LIST_BRACKET) + { + st.addAttribute(ServerStatus.SERVER_LIST_SQUARE_BRACKET, ServerStatus.ON); + } + else + { + st.addAttribute(ServerStatus.SERVER_LIST_SQUARE_BRACKET, ServerStatus.OFF); + } + st.addAttribute(ServerStatus.SERVER_TYPE, Config.SERVER_LIST_TYPE); + if (Config.SERVER_GMONLY) + { + st.addAttribute(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GM_ONLY); + } + else + { + st.addAttribute(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_AUTO); + } + if (Config.SERVER_LIST_AGE == 15) + { + st.addAttribute(ServerStatus.SERVER_AGE, ServerStatus.SERVER_AGE_15); + } + else if (Config.SERVER_LIST_AGE == 18) + { + st.addAttribute(ServerStatus.SERVER_AGE, ServerStatus.SERVER_AGE_18); + } + else + { + st.addAttribute(ServerStatus.SERVER_AGE, ServerStatus.SERVER_AGE_ALL); + } + sendPacket(st); + if (L2World.getInstance().getAllPlayersCount() > 0) + { + FastList playerList = new FastList<>(); + Collection pls = L2World.getInstance().getAllPlayers().values(); + // synchronized (L2World.getInstance().getAllPlayers()) + { + for (L2PcInstance player : pls) + { + playerList.add(player.getAccountName()); + } + } + PlayerInGame pig = new PlayerInGame(playerList); + sendPacket(pig); + } + break; + case 0x03: + PlayerAuthResponse par = new PlayerAuthResponse(decrypt); + String account = par.getAccount(); + WaitingClient wcToRemove = null; + synchronized (_waitingClients) + { + for (WaitingClient wc : _waitingClients) + { + if (wc.account.equals(account)) + { + wcToRemove = wc; + } + } + } + if (wcToRemove != null) + { + if (par.isAuthed()) + { + if (Config.DEBUG) + { + _log.info("Login accepted player " + wcToRemove.account + " waited(" + (GameTimeController.getGameTicks() - wcToRemove.timestamp) + "ms)"); + } + PlayerInGame pig = new PlayerInGame(par.getAccount()); + sendPacket(pig); + wcToRemove.gameClient.setState(GameClientState.AUTHED); + wcToRemove.gameClient.setSessionId(wcToRemove.session); + CharSelectionInfo cl = new CharSelectionInfo(wcToRemove.account, wcToRemove.gameClient.getSessionId().playOkID1); + wcToRemove.gameClient.getConnection().sendPacket(cl); + wcToRemove.gameClient.setCharSelection(cl.getCharInfo()); + } + else + { + _log.warning("Session key is not correct. Closing connection for account " + wcToRemove.account + "."); + // wcToRemove.gameClient.getConnection().sendPacket(new LoginFail(LoginFail.SYSTEM_ERROR_LOGIN_LATER)); + wcToRemove.gameClient.close(new LoginFail(LoginFail.SYSTEM_ERROR_LOGIN_LATER)); + _accountsInGameServer.remove(wcToRemove.account); + } + _waitingClients.remove(wcToRemove); + } + break; + case 0x04: + KickPlayer kp = new KickPlayer(decrypt); + doKickPlayer(kp.getAccount()); + break; + } + } + } + catch (UnknownHostException e) + { + if (Config.DEBUG) + { + _log.log(Level.WARNING, "", e); + } + } + catch (IOException e) + { + _log.log(Level.WARNING, "Disconnected from Login, Trying to reconnect: " + e.getMessage(), e); + } + finally + { + try + { + _loginSocket.close(); + if (isInterrupted()) + { + return; + } + } + catch (Exception e) + { + } + } + + try + { + Thread.sleep(5000); // 5 seconds tempo. + } + catch (InterruptedException e) + { + return; // never swallow an interrupt! + } + } + } + + public void addWaitingClientAndSendRequest(String acc, L2GameClient client, SessionKey key) + { + if (Config.DEBUG) + { + _log.info(String.valueOf(key)); + } + WaitingClient wc = new WaitingClient(acc, client, key); + synchronized (_waitingClients) + { + _waitingClients.add(wc); + } + PlayerAuthRequest par = new PlayerAuthRequest(acc, key); + try + { + sendPacket(par); + } + catch (IOException e) + { + _log.warning("Error while sending player auth request"); + if (Config.DEBUG) + { + _log.log(Level.WARNING, "", e); + } + } + } + + public void removeWaitingClient(L2GameClient client) + { + WaitingClient toRemove = null; + synchronized (_waitingClients) + { + for (WaitingClient c : _waitingClients) + { + if (c.gameClient == client) + { + toRemove = c; + } + } + if (toRemove != null) + { + _waitingClients.remove(toRemove); + } + } + } + + public void sendLogout(String account) + { + if (account == null) + { + return; + } + + PlayerLogout pl = new PlayerLogout(account); + try + { + sendPacket(pl); + } + catch (IOException e) + { + _log.warning("Error while sending logout packet to login"); + if (Config.DEBUG) + { + _log.log(Level.WARNING, "", e); + } + } + finally + { + _accountsInGameServer.remove(account); + } + } + + public void addGameServerLogin(String account, L2GameClient client) + { + _accountsInGameServer.put(account, client); + } + + public void sendAccessLevel(String account, int level) + { + ChangeAccessLevel cal = new ChangeAccessLevel(account, level); + try + { + sendPacket(cal); + } + catch (IOException e) + { + if (Config.DEBUG) + { + _log.log(Level.WARNING, "", e); + } + } + } + + public void sendClientTracert(String account, String[] adress) + { + PlayerTracert ptc = new PlayerTracert(account, adress[0], adress[1], adress[2], adress[3], adress[4]); + try + { + sendPacket(ptc); + } + catch (IOException e) + { + if (Config.DEBUG) + { + _log.log(Level.WARNING, "", e); + } + } + } + + private String hexToString(byte[] hex) + { + return new BigInteger(hex).toString(16); + } + + public void doKickPlayer(String account) + { + L2GameClient client = _accountsInGameServer.get(account); + if (client != null) + { + LogRecord record = new LogRecord(Level.WARNING, "Kicked by login"); + record.setParameters(new Object[] + { + client + }); + _logAccounting.log(record); + client.setAditionalClosePacket(SystemMessage.getSystemMessage(SystemMessageId.ANOTHER_LOGIN_WITH_ACCOUNT)); + client.closeNow(); + } + } + + /** + * @param sl + * @throws IOException + */ + private void sendPacket(BaseSendablePacket sl) throws IOException + { + byte[] data = sl.getContent(); + NewCrypt.appendChecksum(data); + if (Config.DEBUG) + { + _log.finest("[S]\n" + Util.printData(data)); + } + data = _blowfish.crypt(data); + + int len = data.length + 2; + synchronized (_out) // avoids tow threads writing in the mean time + { + _out.write(len & 0xff); + _out.write((len >> 8) & 0xff); + _out.write(data); + _out.flush(); + } + } + + /** + * @param maxPlayer The maxPlayer to set. + */ + public void setMaxPlayer(int maxPlayer) + { + sendServerStatus(ServerStatus.MAX_PLAYERS, maxPlayer); + _maxPlayer = maxPlayer; + } + + /** + * @return Returns the maxPlayer. + */ + public int getMaxPlayer() + { + return _maxPlayer; + } + + /** + * @param server_gm_only + */ + public void sendServerStatus(int id, int value) + { + ServerStatus ss = new ServerStatus(); + ss.addAttribute(id, value); + try + { + sendPacket(ss); + } + catch (IOException e) + { + if (Config.DEBUG) + { + _log.log(Level.WARNING, "", e); + } + } + } + + /** + * Send Server Type Config to LS + */ + public void sendServerType() + { + ServerStatus ss = new ServerStatus(); + ss.addAttribute(ServerStatus.SERVER_TYPE, Config.SERVER_LIST_TYPE); + try + { + sendPacket(ss); + } + catch (IOException e) + { + if (Config.DEBUG) + { + _log.log(Level.WARNING, "", e); + } + } + } + + /** + * @return + */ + public String getStatusString() + { + return ServerStatus.STATUS_STRING[_status]; + } + + /** + * @return + */ + public boolean isBracketShown() + { + return Config.SERVER_LIST_BRACKET; + } + + /** + * @return Returns the serverName. + */ + public String getServerName() + { + return _serverName; + } + + public void setServerStatus(int status) + { + switch (status) + { + case ServerStatus.STATUS_AUTO: + sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_AUTO); + _status = status; + break; + case ServerStatus.STATUS_DOWN: + sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_DOWN); + _status = status; + break; + case ServerStatus.STATUS_FULL: + sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_FULL); + _status = status; + break; + case ServerStatus.STATUS_GM_ONLY: + sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GM_ONLY); + _status = status; + break; + case ServerStatus.STATUS_GOOD: + sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_GOOD); + _status = status; + break; + case ServerStatus.STATUS_NORMAL: + sendServerStatus(ServerStatus.SERVER_LIST_STATUS, ServerStatus.STATUS_NORMAL); + _status = status; + break; + default: + throw new IllegalArgumentException("Status does not exists:" + status); + } + } + + public static class SessionKey + { + public int playOkID1; + public int playOkID2; + public int loginOkID1; + public int loginOkID2; + + public SessionKey(int loginOK1, int loginOK2, int playOK1, int playOK2) + { + playOkID1 = playOK1; + playOkID2 = playOK2; + loginOkID1 = loginOK1; + loginOkID2 = loginOK2; + } + + @Override + public String toString() + { + return "PlayOk: " + playOkID1 + " " + playOkID2 + " LoginOk:" + loginOkID1 + " " + loginOkID2; + } + } + + private static class WaitingClient + { + public int timestamp; + public String account; + public L2GameClient gameClient; + public SessionKey session; + + public WaitingClient(String acc, L2GameClient client, SessionKey key) + { + account = acc; + timestamp = GameTimeController.getGameTicks(); + gameClient = client; + session = key; + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final LoginServerThread _instance = new LoginServerThread(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/MonsterRace.java b/L2J_Server/java/com/l2jserver/gameserver/MonsterRace.java new file mode 100644 index 00000000..48475dde --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/MonsterRace.java @@ -0,0 +1,152 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.lang.reflect.Constructor; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.idfactory.IdFactory; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.Rnd; + +public class MonsterRace +{ + protected static final Logger _log = Logger.getLogger(MonsterRace.class.getName()); + + private final L2Npc[] _monsters; + private Constructor _constructor; + private int[][] _speeds; + private final int[] _first, _second; + + private MonsterRace() + { + _monsters = new L2Npc[8]; + _speeds = new int[8][20]; + _first = new int[2]; + _second = new int[2]; + } + + public static MonsterRace getInstance() + { + return SingletonHolder._instance; + } + + public void newRace() + { + int random = 0; + + for (int i = 0; i < 8; i++) + { + int id = 31003; + random = Rnd.get(24); + while (true) + { + for (int j = i - 1; j >= 0; j--) + { + if (_monsters[j].getTemplate().npcId == (id + random)) + { + random = Rnd.get(24); + continue; + } + } + break; + } + try + { + L2NpcTemplate template = NpcTable.getInstance().getTemplate(id + random); + _constructor = Class.forName("com.l2jserver.gameserver.model.actor.instance." + template.type + "Instance").getConstructors()[0]; + int objectId = IdFactory.getInstance().getNextId(); + _monsters[i] = (L2Npc) _constructor.newInstance(objectId, template); + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + } + // _log.info("Monster "+i+" is id: "+(id+random)); + } + newSpeeds(); + } + + public void newSpeeds() + { + _speeds = new int[8][20]; + int total = 0; + _first[1] = 0; + _second[1] = 0; + for (int i = 0; i < 8; i++) + { + total = 0; + for (int j = 0; j < 20; j++) + { + if (j == 19) + { + _speeds[i][j] = 100; + } + else + { + _speeds[i][j] = Rnd.get(60) + 65; + } + total += _speeds[i][j]; + } + if (total >= _first[1]) + { + _second[0] = _first[0]; + _second[1] = _first[1]; + _first[0] = 8 - i; + _first[1] = total; + } + else if (total >= _second[1]) + { + _second[0] = 8 - i; + _second[1] = total; + } + } + } + + /** + * @return Returns the monsters. + */ + public L2Npc[] getMonsters() + { + return _monsters; + } + + /** + * @return Returns the speeds. + */ + public int[][] getSpeeds() + { + return _speeds; + } + + public int getFirstPlace() + { + return _first[0]; + } + + public int getSecondPlace() + { + return _second[0]; + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final MonsterRace _instance = new MonsterRace(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/RecipeController.java b/L2J_Server/java/com/l2jserver/gameserver/RecipeController.java new file mode 100644 index 00000000..6076fbfc --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/RecipeController.java @@ -0,0 +1,1027 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.datatables.ItemTable; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2ManufactureItem; +import com.l2jserver.gameserver.model.L2RecipeInstance; +import com.l2jserver.gameserver.model.L2RecipeList; +import com.l2jserver.gameserver.model.L2RecipeStatInstance; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.TempItem; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.itemcontainer.Inventory; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.ItemList; +import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse; +import com.l2jserver.gameserver.network.serverpackets.RecipeBookItemList; +import com.l2jserver.gameserver.network.serverpackets.RecipeItemMakeInfo; +import com.l2jserver.gameserver.network.serverpackets.RecipeShopItemInfo; +import com.l2jserver.gameserver.network.serverpackets.SetupGauge; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.skills.Stats; +import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager; +import com.l2jserver.gameserver.templates.StatsSet; +import com.l2jserver.gameserver.templates.item.L2Item; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.Rnd; + +public class RecipeController +{ + protected static final Logger _log = Logger.getLogger(RecipeController.class.getName()); + + private final Map _lists; + protected static final Map _activeMakers = new FastMap<>(); + private static final String RECIPES_FILE = "recipes.xml"; + + public static RecipeController getInstance() + { + return SingletonHolder._instance; + } + + private RecipeController() + { + _lists = new FastMap<>(); + + try + { + loadFromXML(); + _log.info("RecipeController: Loaded " + _lists.size() + " recipes."); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Failed loading recipe list", e); + } + } + + public int getRecipesCount() + { + return _lists.size(); + } + + public L2RecipeList getRecipeList(int listId) + { + return _lists.get(listId); + } + + public L2RecipeList getRecipeByItemId(int itemId) + { + for (L2RecipeList find : _lists.values()) + { + if (find.getRecipeId() == itemId) + { + return find; + } + } + return null; + } + + public int[] getAllItemIds() + { + int[] idList = new int[_lists.size()]; + int i = 0; + for (L2RecipeList rec : _lists.values()) + { + idList[i++] = rec.getRecipeId(); + } + return idList; + } + + public synchronized void requestBookOpen(L2PcInstance player, boolean isDwarvenCraft) + { + RecipeItemMaker maker = null; + if (Config.ALT_GAME_CREATION) + { + maker = _activeMakers.get(player.getObjectId()); + } + + if (maker == null) + { + RecipeBookItemList response = new RecipeBookItemList(isDwarvenCraft, player.getMaxMp()); + response.addRecipes(isDwarvenCraft ? player.getDwarvenRecipeBook() : player.getCommonRecipeBook()); + player.sendPacket(response); + return; + } + + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANT_ALTER_RECIPEBOOK_WHILE_CRAFTING)); + } + + public synchronized void requestMakeItemAbort(L2PcInstance player) + { + _activeMakers.remove(player.getObjectId()); // TODO: anything else here? + } + + public synchronized void requestManufactureItem(L2PcInstance manufacturer, int recipeListId, L2PcInstance player) + { + L2RecipeList recipeList = getValidRecipeList(player, recipeListId); + + if (recipeList == null) + { + return; + } + + List dwarfRecipes = Arrays.asList(manufacturer.getDwarvenRecipeBook()); + List commonRecipes = Arrays.asList(manufacturer.getCommonRecipeBook()); + + if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList)) + { + Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH); + return; + } + + RecipeItemMaker maker; + + if (Config.ALT_GAME_CREATION && ((maker = _activeMakers.get(manufacturer.getObjectId())) != null)) // check if busy + { + player.sendMessage("Manufacturer is busy, please try later."); + return; + } + + maker = new RecipeItemMaker(manufacturer, recipeList, player); + if (maker._isValid) + { + if (Config.ALT_GAME_CREATION) + { + _activeMakers.put(manufacturer.getObjectId(), maker); + ThreadPoolManager.getInstance().scheduleGeneral(maker, 100); + } + else + { + maker.run(); + } + } + } + + public synchronized void requestMakeItem(L2PcInstance player, int recipeListId) + { + if (AttackStanceTaskManager.getInstance().getAttackStanceTask(player) || player.isInDuel()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANT_OPERATE_PRIVATE_STORE_DURING_COMBAT)); + return; + } + + L2RecipeList recipeList = getValidRecipeList(player, recipeListId); + + if (recipeList == null) + { + return; + } + + List dwarfRecipes = Arrays.asList(player.getDwarvenRecipeBook()); + List commonRecipes = Arrays.asList(player.getCommonRecipeBook()); + + if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList)) + { + Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH); + return; + } + + RecipeItemMaker maker; + + // check if already busy (possible in alt mode only) + if (Config.ALT_GAME_CREATION && ((maker = _activeMakers.get(player.getObjectId())) != null)) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1); + sm.addItemName(recipeList.getItemId()); + sm.addString("You are busy creating"); + player.sendPacket(sm); + return; + } + + maker = new RecipeItemMaker(player, recipeList, player); + if (maker._isValid) + { + if (Config.ALT_GAME_CREATION) + { + _activeMakers.put(player.getObjectId(), maker); + ThreadPoolManager.getInstance().scheduleGeneral(maker, 100); + } + else + { + maker.run(); + } + } + } + + private void loadFromXML() throws SAXException, IOException, ParserConfigurationException + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setValidating(false); + factory.setIgnoringComments(true); + File file = new File(Config.DATAPACK_ROOT + "/data/" + RECIPES_FILE); + if (file.exists()) + { + Document doc = factory.newDocumentBuilder().parse(file); + List recipePartList = new FastList<>(); + List recipeStatUseList = new FastList<>(); + List recipeAltStatChangeList = new FastList<>(); + + for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling()) + { + if ("list".equalsIgnoreCase(n.getNodeName())) + { + recipesFile: for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("item".equalsIgnoreCase(d.getNodeName())) + { + recipePartList.clear(); + recipeStatUseList.clear(); + recipeAltStatChangeList.clear(); + NamedNodeMap attrs = d.getAttributes(); + Node att; + int id = -1; + boolean haveRare = false; + StatsSet set = new StatsSet(); + + att = attrs.getNamedItem("id"); + if (att == null) + { + _log.severe("Missing id for recipe item, skipping"); + continue; + } + id = Integer.parseInt(att.getNodeValue()); + set.set("id", id); + + att = attrs.getNamedItem("recipeId"); + if (att == null) + { + _log.severe("Missing recipeId for recipe item id: " + id + ", skipping"); + continue; + } + set.set("recipeId", Integer.parseInt(att.getNodeValue())); + + att = attrs.getNamedItem("name"); + if (att == null) + { + _log.severe("Missing name for recipe item id: " + id + ", skipping"); + continue; + } + set.set("recipeName", att.getNodeValue()); + + att = attrs.getNamedItem("craftLevel"); + if (att == null) + { + _log.severe("Missing level for recipe item id: " + id + ", skipping"); + continue; + } + set.set("craftLevel", Integer.parseInt(att.getNodeValue())); + + att = attrs.getNamedItem("type"); + if (att == null) + { + _log.severe("Missing type for recipe item id: " + id + ", skipping"); + continue; + } + set.set("isDwarvenRecipe", att.getNodeValue().equalsIgnoreCase("dwarven")); + + att = attrs.getNamedItem("successRate"); + if (att == null) + { + _log.severe("Missing successRate for recipe item id: " + id + ", skipping"); + continue; + } + set.set("successRate", Integer.parseInt(att.getNodeValue())); + + for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling()) + { + if ("statUse".equalsIgnoreCase(c.getNodeName())) + { + String statName = c.getAttributes().getNamedItem("name").getNodeValue(); + int value = Integer.parseInt(c.getAttributes().getNamedItem("value").getNodeValue()); + try + { + recipeStatUseList.add(new L2RecipeStatInstance(statName, value)); + } + catch (Exception e) + { + _log.severe("Error in StatUse parameter for recipe item id: " + id + ", skipping"); + continue recipesFile; + } + } + else if ("altStatChange".equalsIgnoreCase(c.getNodeName())) + { + String statName = c.getAttributes().getNamedItem("name").getNodeValue(); + int value = Integer.parseInt(c.getAttributes().getNamedItem("value").getNodeValue()); + try + { + recipeAltStatChangeList.add(new L2RecipeStatInstance(statName, value)); + } + catch (Exception e) + { + _log.severe("Error in AltStatChange parameter for recipe item id: " + id + ", skipping"); + continue recipesFile; + } + } + else if ("ingredient".equalsIgnoreCase(c.getNodeName())) + { + int ingId = Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue()); + int ingCount = Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue()); + recipePartList.add(new L2RecipeInstance(ingId, ingCount)); + } + else if ("production".equalsIgnoreCase(c.getNodeName())) + { + set.set("itemId", Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue())); + set.set("count", Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue())); + } + else if ("productionRare".equalsIgnoreCase(c.getNodeName())) + { + set.set("rareItemId", Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue())); + set.set("rareCount", Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue())); + set.set("rarity", Integer.parseInt(c.getAttributes().getNamedItem("rarity").getNodeValue())); + haveRare = true; + } + } + + L2RecipeList recipeList = new L2RecipeList(set, haveRare); + for (L2RecipeInstance recipePart : recipePartList) + { + recipeList.addRecipe(recipePart); + } + for (L2RecipeStatInstance recipeStatUse : recipeStatUseList) + { + recipeList.addStatUse(recipeStatUse); + } + for (L2RecipeStatInstance recipeAltStatChange : recipeAltStatChangeList) + { + recipeList.addAltStatChange(recipeAltStatChange); + } + + _lists.put(id, recipeList); + } + } + } + } + } + else + { + _log.severe("Recipes file (" + file.getAbsolutePath() + ") doesnt exists."); + } + } + + private static class RecipeItemMaker implements Runnable + { + protected boolean _isValid; + protected List _items = null; + protected final L2RecipeList _recipeList; + protected final L2PcInstance _player; // "crafter" + protected final L2PcInstance _target; // "customer" + protected final L2Skill _skill; + protected final int _skillId; + protected final int _skillLevel; + protected int _creationPasses = 1; + protected int _itemGrab; + protected int _exp = -1; + protected int _sp = -1; + protected long _price; + protected int _totalItems; + protected int _delay; + + public RecipeItemMaker(L2PcInstance pPlayer, L2RecipeList pRecipeList, L2PcInstance pTarget) + { + _player = pPlayer; + _target = pTarget; + _recipeList = pRecipeList; + + _isValid = false; + _skillId = _recipeList.isDwarvenRecipe() ? L2Skill.SKILL_CREATE_DWARVEN : L2Skill.SKILL_CREATE_COMMON; + _skillLevel = _player.getSkillLevel(_skillId); + _skill = _player.getKnownSkill(_skillId); + + _player.isInCraftMode(true); + + if (_player.isAlikeDead()) + { + _player.sendPacket(ActionFailed.STATIC_PACKET); + abort(); + return; + } + + if (_target.isAlikeDead()) + { + _target.sendPacket(ActionFailed.STATIC_PACKET); + abort(); + return; + } + + if (_target.isProcessingTransaction()) + { + _target.sendPacket(ActionFailed.STATIC_PACKET); + abort(); + return; + } + + if (_player.isProcessingTransaction()) + { + _player.sendPacket(ActionFailed.STATIC_PACKET); + abort(); + return; + } + + // validate recipe list + if (_recipeList.getRecipes().length == 0) + { + _player.sendPacket(ActionFailed.STATIC_PACKET); + abort(); + return; + } + + // validate skill level + if (_recipeList.getLevel() > _skillLevel) + { + _player.sendPacket(ActionFailed.STATIC_PACKET); + abort(); + return; + } + + // check that customer can afford to pay for creation services + if (_player != _target) + { + for (L2ManufactureItem temp : _player.getCreateList().getList()) + { + if (temp.getRecipeId() == _recipeList.getId()) // find recipe for item we want manufactured + { + _price = temp.getCost(); + if (_target.getAdena() < _price) // check price + { + _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA)); + abort(); + return; + } + break; + } + } + } + + // make temporary items + if ((_items = listItems(false)) == null) + { + abort(); + return; + } + + for (TempItem i : _items) + { + _totalItems += i.getQuantity(); + } + + // initial statUse checks + if (!calculateStatUse(false, false)) + { + abort(); + return; + } + + // initial AltStatChange checks + if (Config.ALT_GAME_CREATION) + { + calculateAltStatChange(); + } + + updateMakeInfo(true); + updateCurMp(); + updateCurLoad(); + + _player.isInCraftMode(false); + _isValid = true; + } + + @Override + public void run() + { + if (!Config.IS_CRAFTING_ENABLED) + { + _target.sendMessage("Item creation is currently disabled."); + abort(); + return; + } + + if ((_player == null) || (_target == null)) + { + _log.warning("player or target == null (disconnected?), aborting" + _target + _player); + abort(); + return; + } + + if (!_player.isOnline() || !_target.isOnline()) + { + _log.warning("player or target is not online, aborting " + _target + _player); + abort(); + return; + } + + if (Config.ALT_GAME_CREATION && (_activeMakers.get(_player.getObjectId()) == null)) + { + if (_target != _player) + { + _target.sendMessage("Manufacture aborted"); + _player.sendMessage("Manufacture aborted"); + } + else + { + _player.sendMessage("Item creation aborted"); + } + + abort(); + return; + } + + if (Config.ALT_GAME_CREATION && !_items.isEmpty()) + { + + if (!calculateStatUse(true, true)) + { + return; // check stat use + } + updateCurMp(); // update craft window mp bar + + grabSomeItems(); // grab (equip) some more items with a nice msg to player + + // if still not empty, schedule another pass + if (!_items.isEmpty()) + { + // divided by RATE_CONSUMABLES_COST to remove craft time increase on higher consumables rates + _delay = (int) ((Config.ALT_GAME_CREATION_SPEED * _player.getMReuseRate(_skill) * GameTimeController.TICKS_PER_SECOND) / Config.RATE_CONSUMABLE_COST) * GameTimeController.MILLIS_IN_TICK; + + // FIXME: please fix this packet to show crafting animation (somebody) + MagicSkillUse msk = new MagicSkillUse(_player, _skillId, _skillLevel, _delay, 0); + _player.broadcastPacket(msk); + + _player.sendPacket(new SetupGauge(0, _delay)); + ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay); + } + else + { + // for alt mode, sleep delay msec before finishing + _player.sendPacket(new SetupGauge(0, _delay)); + + try + { + Thread.sleep(_delay); + } + catch (InterruptedException e) + { + } + finally + { + finishCrafting(); + } + } + } // for old craft mode just finish + else + { + finishCrafting(); + } + } + + private void finishCrafting() + { + if (!Config.ALT_GAME_CREATION) + { + calculateStatUse(false, true); + } + + // first take adena for manufacture + if ((_target != _player) && (_price > 0)) // customer must pay for services + { + // attempt to pay for item + L2ItemInstance adenatransfer = _target.transferItem("PayManufacture", _target.getInventory().getAdenaInstance().getObjectId(), _price, _player.getInventory(), _player); + + if (adenatransfer == null) + { + _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA)); + abort(); + return; + } + } + + if ((_items = listItems(true)) == null) // this line actually takes materials from inventory + { // handle possible cheaters here + // (they click craft then try to get rid of items in order to get free craft) + } + else if (Rnd.get(100) < _recipeList.getSuccessRate()) + { + rewardPlayer(); // and immediately puts created item in its place + updateMakeInfo(true); + } + else + { + if (_target != _player) + { + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.CREATION_OF_S2_FOR_C1_AT_S3_ADENA_FAILED); + msg.addString(_target.getName()); + msg.addItemName(_recipeList.getItemId()); + msg.addItemNumber(_price); + _player.sendPacket(msg); + + msg = SystemMessage.getSystemMessage(SystemMessageId.C1_FAILED_TO_CREATE_S2_FOR_S3_ADENA); + msg.addString(_player.getName()); + msg.addItemName(_recipeList.getItemId()); + msg.addItemNumber(_price); + _target.sendPacket(msg); + } + else + { + _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MIXING_FAILED)); + } + updateMakeInfo(false); + } + // update load and mana bar of craft window + updateCurMp(); + updateCurLoad(); + _activeMakers.remove(_player.getObjectId()); + _player.isInCraftMode(false); + _target.sendPacket(new ItemList(_target, false)); + } + + private void updateMakeInfo(boolean success) + { + if (_target == _player) + { + _target.sendPacket(new RecipeItemMakeInfo(_recipeList.getId(), _target, success)); + } + else + { + _target.sendPacket(new RecipeShopItemInfo(_player, _recipeList.getId())); + } + } + + private void updateCurLoad() + { + StatusUpdate su = new StatusUpdate(_target); + su.addAttribute(StatusUpdate.CUR_LOAD, _target.getCurrentLoad()); + _target.sendPacket(su); + } + + private void updateCurMp() + { + StatusUpdate su = new StatusUpdate(_target); + su.addAttribute(StatusUpdate.CUR_MP, (int) _target.getCurrentMp()); + _target.sendPacket(su); + } + + private void grabSomeItems() + { + int grabItems = _itemGrab; + while ((grabItems > 0) && !_items.isEmpty()) + { + TempItem item = _items.get(0); + + int count = item.getQuantity(); + if (count >= grabItems) + { + count = grabItems; + } + + item.setQuantity(item.getQuantity() - count); + if (item.getQuantity() <= 0) + { + _items.remove(0); + } + else + { + _items.set(0, item); + } + + grabItems -= count; + + if (_target == _player) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_S2_EQUIPPED); // you equipped ... + sm.addItemNumber(count); + sm.addItemName(item.getItemId()); + _player.sendPacket(sm); + } + else + { + _target.sendMessage("Manufacturer " + _player.getName() + " used " + count + " " + item.getItemName()); + } + } + } + + // AltStatChange parameters make their effect here + private void calculateAltStatChange() + { + _itemGrab = _skillLevel; + + for (L2RecipeStatInstance altStatChange : _recipeList.getAltStatChange()) + { + if (altStatChange.getType() == L2RecipeStatInstance.StatType.XP) + { + _exp = altStatChange.getValue(); + } + else if (altStatChange.getType() == L2RecipeStatInstance.StatType.SP) + { + _sp = altStatChange.getValue(); + } + else if (altStatChange.getType() == L2RecipeStatInstance.StatType.GIM) + { + _itemGrab *= altStatChange.getValue(); + } + } + // determine number of creation passes needed + _creationPasses = (_totalItems / _itemGrab) + ((_totalItems % _itemGrab) != 0 ? 1 : 0); + if (_creationPasses < 1) + { + _creationPasses = 1; + } + } + + // StatUse + private boolean calculateStatUse(boolean isWait, boolean isReduce) + { + boolean ret = true; + for (L2RecipeStatInstance statUse : _recipeList.getStatUse()) + { + double modifiedValue = statUse.getValue() / _creationPasses; + if (statUse.getType() == L2RecipeStatInstance.StatType.HP) + { + // we do not want to kill the player, so its CurrentHP must be greater than the reduce value + if (_player.getCurrentHp() <= modifiedValue) + { + // rest (wait for HP) + if (Config.ALT_GAME_CREATION && isWait) + { + _player.sendPacket(new SetupGauge(0, _delay)); + ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay); + } + else + // no rest - report no hp + { + _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_HP)); + abort(); + } + ret = false; + } + else if (isReduce) + { + _player.reduceCurrentHp(modifiedValue, _player, _skill); + } + } + else if (statUse.getType() == L2RecipeStatInstance.StatType.MP) + { + if (_player.getCurrentMp() < modifiedValue) + { + // rest (wait for MP) + if (Config.ALT_GAME_CREATION && isWait) + { + _player.sendPacket(new SetupGauge(0, _delay)); + ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay); + } + else + // no rest - report no mana + { + _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_MP)); + abort(); + } + ret = false; + } + else if (isReduce) + { + _player.reduceCurrentMp(modifiedValue); + } + } + else + { + // there is an unknown StatUse value + _target.sendMessage("Recipe error!!!, please tell this to your GM."); + ret = false; + abort(); + } + } + return ret; + } + + private List listItems(boolean remove) + { + L2RecipeInstance[] recipes = _recipeList.getRecipes(); + Inventory inv = _target.getInventory(); + List materials = new FastList<>(); + SystemMessage sm; + + for (L2RecipeInstance recipe : recipes) + { + int quantity = _recipeList.isConsumable() ? (int) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST) : recipe.getQuantity(); + + if (quantity > 0) + { + L2ItemInstance item = inv.getItemByItemId(recipe.getItemId()); + long itemQuantityAmount = item == null ? 0 : item.getCount(); + + // check materials + if (itemQuantityAmount < quantity) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.MISSING_S2_S1_TO_CREATE); + sm.addItemName(recipe.getItemId()); + sm.addItemNumber(quantity - itemQuantityAmount); + _target.sendPacket(sm); + + abort(); + return null; + } + + // make new temporary object, just for counting purposes + + TempItem temp = new TempItem(item, quantity); + materials.add(temp); + } + } + + if (remove) + { + for (TempItem tmp : materials) + { + inv.destroyItemByItemId("Manufacture", tmp.getItemId(), tmp.getQuantity(), _target, _player); + + if (tmp.getQuantity() > 1) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + sm.addItemName(tmp.getItemId()); + sm.addItemNumber(tmp.getQuantity()); + _target.sendPacket(sm); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); + sm.addItemName(tmp.getItemId()); + _target.sendPacket(sm); + } + } + } + return materials; + } + + private void abort() + { + updateMakeInfo(false); + _player.isInCraftMode(false); + _activeMakers.remove(_player.getObjectId()); + } + + private void rewardPlayer() + { + int rareProdId = _recipeList.getRareItemId(); + int itemId = _recipeList.getItemId(); + int itemCount = _recipeList.getCount(); + L2Item template = ItemTable.getInstance().getTemplate(itemId); + + // check that the current recipe has a rare production or not + if ((rareProdId != -1) && ((rareProdId == itemId) || Config.CRAFT_MASTERWORK)) + { + if (Rnd.get(100) < _recipeList.getRarity()) + { + itemId = rareProdId; + itemCount = _recipeList.getRareCount(); + } + } + + _target.getInventory().addItem("Manufacture", itemId, itemCount, _target, _player); + + // inform customer of earned item + SystemMessage sm = null; + if (_target != _player) + { + // inform manufacturer of earned profit + if (itemCount == 1) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S2_CREATED_FOR_C1_FOR_S3_ADENA); + sm.addString(_target.getName()); + sm.addItemName(itemId); + sm.addItemNumber(_price); + _player.sendPacket(sm); + + sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_FOR_S3_ADENA); + sm.addString(_player.getName()); + sm.addItemName(itemId); + sm.addItemNumber(_price); + _target.sendPacket(sm); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S3_S_CREATED_FOR_C1_FOR_S4_ADENA); + sm.addString(_target.getName()); + sm.addNumber(itemCount); + sm.addItemName(itemId); + sm.addItemNumber(_price); + _player.sendPacket(sm); + + sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_S3_S_FOR_S4_ADENA); + sm.addString(_player.getName()); + sm.addNumber(itemCount); + sm.addItemName(itemId); + sm.addItemNumber(_price); + _target.sendPacket(sm); + } + } + + if (itemCount > 1) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S); + sm.addItemName(itemId); + sm.addItemNumber(itemCount); + _target.sendPacket(sm); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1); + sm.addItemName(itemId); + _target.sendPacket(sm); + } + + if (Config.ALT_GAME_CREATION) + { + int recipeLevel = _recipeList.getLevel(); + if (_exp < 0) + { + _exp = template.getReferencePrice() * itemCount; + _exp /= recipeLevel; + } + if (_sp < 0) + { + _sp = _exp / 10; + } + if (itemId == rareProdId) + { + _exp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE; + _sp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE; + } + + if (_exp < 0) + { + _exp = 0; + } + if (_sp < 0) + { + _sp = 0; + } + + for (int i = _skillLevel; i > recipeLevel; i--) + { + _exp /= 4; + _sp /= 4; + } + + // Added multiplication of Creation speed with XP/SP gain + // slower crafting -> more XP, faster crafting -> less XP + // you can use ALT_GAME_CREATION_XP_RATE/SP to + // modify XP/SP gained (default = 1) + + _player.addExpAndSp((int) _player.calcStat(Stats.EXPSP_RATE, _exp * Config.ALT_GAME_CREATION_XP_RATE * Config.ALT_GAME_CREATION_SPEED, null, null), (int) _player.calcStat(Stats.EXPSP_RATE, _sp * Config.ALT_GAME_CREATION_SP_RATE * Config.ALT_GAME_CREATION_SPEED, null, null)); + } + updateMakeInfo(true); // success + } + } + + private L2RecipeList getValidRecipeList(L2PcInstance player, int id) + { + L2RecipeList recipeList = getRecipeList(id); + + if ((recipeList == null) || (recipeList.getRecipes().length == 0)) + { + player.sendMessage("No recipe for: " + id); + player.isInCraftMode(false); + return null; + } + return recipeList; + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final RecipeController _instance = new RecipeController(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/SevenSigns.java b/L2J_Server/java/com/l2jserver/gameserver/SevenSigns.java new file mode 100644 index 00000000..8d744d4d --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/SevenSigns.java @@ -0,0 +1,1795 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Calendar; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.datatables.MapRegionTable; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.instancemanager.QuestManager; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; +import com.l2jserver.gameserver.model.AutoChatHandler; +import com.l2jserver.gameserver.model.AutoSpawnHandler; +import com.l2jserver.gameserver.model.AutoSpawnHandler.AutoSpawnInstance; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.entity.Castle; +import com.l2jserver.gameserver.model.quest.Quest; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.SSQInfo; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.StatsSet; +import com.l2jserver.gameserver.util.Broadcast; + +/** + * Seven Signs Engine + * @author Tempy + */ +public class SevenSigns +{ + protected static final Logger _log = Logger.getLogger(SevenSigns.class.getName()); + + // Basic Seven Signs Constants \\ + public static final String SEVEN_SIGNS_DATA_FILE = "config/signs.properties"; + public static final String SEVEN_SIGNS_HTML_PATH = "data/html/seven_signs/"; + + public static final int CABAL_NULL = 0; + public static final int CABAL_DUSK = 1; + public static final int CABAL_DAWN = 2; + + public static final int SEAL_NULL = 0; + public static final int SEAL_AVARICE = 1; + public static final int SEAL_GNOSIS = 2; + public static final int SEAL_STRIFE = 3; + + public static final int PERIOD_COMP_RECRUITING = 0; + public static final int PERIOD_COMPETITION = 1; + public static final int PERIOD_COMP_RESULTS = 2; + public static final int PERIOD_SEAL_VALIDATION = 3; + + public static final int PERIOD_START_HOUR = 18; + public static final int PERIOD_START_MINS = 00; + public static final int PERIOD_START_DAY = Calendar.MONDAY; + + // The quest event and seal validation periods last for approximately one week + // with a 15 minutes "interval" period sandwiched between them. + public static final int PERIOD_MINOR_LENGTH = 900000; + public static final int PERIOD_MAJOR_LENGTH = 604800000 - PERIOD_MINOR_LENGTH; + + public static final int ANCIENT_ADENA_ID = 5575; + public static final int RECORD_SEVEN_SIGNS_ID = 5707; + public static final int CERTIFICATE_OF_APPROVAL_ID = 6388; + public static final int RECORD_SEVEN_SIGNS_COST = 500; + public static final int ADENA_JOIN_DAWN_COST = 50000; + + // NPC Related Constants \\ + public static final int ORATOR_NPC_ID = 31094; + public static final int PREACHER_NPC_ID = 31093; + public static final int MAMMON_MERCHANT_ID = 31113; + public static final int MAMMON_BLACKSMITH_ID = 31126; + public static final int MAMMON_MARKETEER_ID = 31092; + public static final int LILITH_NPC_ID = 25283; + public static final int ANAKIM_NPC_ID = 25286; + public static final int CREST_OF_DAWN_ID = 31170; + public static final int CREST_OF_DUSK_ID = 31171; + // Seal Stone Related Constants \\ + public static final int SEAL_STONE_BLUE_ID = 6360; + public static final int SEAL_STONE_GREEN_ID = 6361; + public static final int SEAL_STONE_RED_ID = 6362; + + public static final int SEAL_STONE_BLUE_VALUE = 3; + public static final int SEAL_STONE_GREEN_VALUE = 5; + public static final int SEAL_STONE_RED_VALUE = 10; + + public static final int BLUE_CONTRIB_POINTS = 3; + public static final int GREEN_CONTRIB_POINTS = 5; + public static final int RED_CONTRIB_POINTS = 10; + + private final Calendar _nextPeriodChange = Calendar.getInstance(); + + protected int _activePeriod; + protected int _currentCycle; + protected double _dawnStoneScore; + protected double _duskStoneScore; + protected int _dawnFestivalScore; + protected int _duskFestivalScore; + protected int _compWinner; + protected int _previousWinner; + protected Calendar _lastSave = Calendar.getInstance(); + + private final Map _signsPlayerData; + + private final Map _signsSealOwners; + private final Map _signsDuskSealTotals; + private final Map _signsDawnSealTotals; + + private static AutoSpawnInstance _merchantSpawn; + private static AutoSpawnInstance _blacksmithSpawn; + private static AutoSpawnInstance _lilithSpawn; + private static AutoSpawnInstance _anakimSpawn; + private static Map _crestofdawnspawns; + private static Map _crestofduskspawns; + private static Map _oratorSpawns; + private static Map _preacherSpawns; + private static Map _marketeerSpawns; + + private static final String LOAD_DATA = "SELECT charId, cabal, seal, red_stones, green_stones, blue_stones, " + "ancient_adena_amount, contribution_score FROM seven_signs"; + + private static final String LOAD_STATUS = "SELECT * FROM seven_signs_status WHERE id=0"; + + private static final String INSERT_PLAYER = "INSERT INTO seven_signs (charId, cabal, seal) VALUES (?,?,?)"; + + private static final String UPDATE_PLAYER = "UPDATE seven_signs SET cabal=?, seal=?, red_stones=?, green_stones=?, blue_stones=?, " + "ancient_adena_amount=?, contribution_score=? WHERE charId=?"; + + private static final String UPDATE_STATUS = "UPDATE seven_signs_status SET current_cycle=?, active_period=?, previous_winner=?, " + "dawn_stone_score=?, dawn_festival_score=?, dusk_stone_score=?, dusk_festival_score=?, " + "avarice_owner=?, gnosis_owner=?, strife_owner=?, avarice_dawn_score=?, gnosis_dawn_score=?, " + "strife_dawn_score=?, avarice_dusk_score=?, gnosis_dusk_score=?, strife_dusk_score=?, " + "festival_cycle=?, accumulated_bonus0=?, accumulated_bonus1=?, accumulated_bonus2=?," + "accumulated_bonus3=?, accumulated_bonus4=?, date=? WHERE id=0"; + + private SevenSigns() + { + _signsPlayerData = new FastMap<>(); + _signsSealOwners = new FastMap<>(); + _signsDuskSealTotals = new FastMap<>(); + _signsDawnSealTotals = new FastMap<>(); + + try + { + restoreSevenSignsData(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "SevenSigns: Failed to load configuration: " + e.getMessage(), e); + } + + _log.info("SevenSigns: Currently in the " + getCurrentPeriodName() + " period!"); + initializeSeals(); + + if (isSealValidationPeriod()) + { + if (getCabalHighestScore() == CABAL_NULL) + { + _log.info("SevenSigns: The competition ended with a tie last week."); + } + else + { + _log.info("SevenSigns: The " + getCabalName(getCabalHighestScore()) + " were victorious last week."); + } + } + else if (getCabalHighestScore() == CABAL_NULL) + { + _log.info("SevenSigns: The competition, if the current trend continues, will end in a tie this week."); + } + else + { + _log.info("SevenSigns: The " + getCabalName(getCabalHighestScore()) + " are in the lead this week."); + } + + long milliToChange = 0; + if (isNextPeriodChangeInPast()) + { + _log.info("SevenSigns: Next period change was in the past (server was offline), changing periods now!"); + } + else + { + setCalendarForNextPeriodChange(); + milliToChange = getMilliToPeriodChange(); + } + + // Schedule a time for the next period change. + SevenSignsPeriodChange sspc = new SevenSignsPeriodChange(); + ThreadPoolManager.getInstance().scheduleGeneral(sspc, milliToChange); + + // Thanks to http://rainbow.arch.scriptmania.com/scripts/timezone_countdown.html for help with this. + double numSecs = (milliToChange / 1000) % 60; + double countDown = ((milliToChange / 1000) - numSecs) / 60; + int numMins = (int) Math.floor(countDown % 60); + countDown = (countDown - numMins) / 60; + int numHours = (int) Math.floor(countDown % 24); + int numDays = (int) Math.floor((countDown - numHours) / 24); + + _log.info("SevenSigns: Next period begins in " + numDays + " days, " + numHours + " hours and " + numMins + " mins."); + + } + + private boolean isNextPeriodChangeInPast() + { + Calendar lastPeriodChange = Calendar.getInstance(); + switch (getCurrentPeriod()) + { + case PERIOD_SEAL_VALIDATION: + case PERIOD_COMPETITION: + lastPeriodChange.set(Calendar.DAY_OF_WEEK, PERIOD_START_DAY); + lastPeriodChange.set(Calendar.HOUR_OF_DAY, PERIOD_START_HOUR); + lastPeriodChange.set(Calendar.MINUTE, PERIOD_START_MINS); + lastPeriodChange.set(Calendar.SECOND, 0); + // if we hit next week, just turn back 1 week + if (Calendar.getInstance().before(lastPeriodChange)) + { + lastPeriodChange.add(Calendar.HOUR, -24 * 7); + } + break; + case PERIOD_COMP_RECRUITING: + case PERIOD_COMP_RESULTS: + // because of the short duration of this period, just check it from last save + lastPeriodChange.setTimeInMillis(_lastSave.getTimeInMillis() + PERIOD_MINOR_LENGTH); + break; + } + + // because of previous "date" column usage, check only if it already contains usable data for us + if ((_lastSave.getTimeInMillis() > 7) && _lastSave.before(lastPeriodChange)) + { + return true; + } + return false; + } + + /** + * Registers all random spawns and auto-chats for Seven Signs NPCs, along with spawns for the Preachers of Doom and Orators of Revelations at the beginning of the Seal Validation period. + */ + public void spawnSevenSignsNPC() + { + _merchantSpawn = AutoSpawnHandler.getInstance().getAutoSpawnInstance(MAMMON_MERCHANT_ID, false); + _blacksmithSpawn = AutoSpawnHandler.getInstance().getAutoSpawnInstance(MAMMON_BLACKSMITH_ID, false); + _marketeerSpawns = AutoSpawnHandler.getInstance().getAutoSpawnInstances(MAMMON_MARKETEER_ID); + _lilithSpawn = AutoSpawnHandler.getInstance().getAutoSpawnInstance(LILITH_NPC_ID, false); + _anakimSpawn = AutoSpawnHandler.getInstance().getAutoSpawnInstance(ANAKIM_NPC_ID, false); + _crestofdawnspawns = AutoSpawnHandler.getInstance().getAutoSpawnInstances(CREST_OF_DAWN_ID); + _crestofduskspawns = AutoSpawnHandler.getInstance().getAutoSpawnInstances(CREST_OF_DUSK_ID); + _oratorSpawns = AutoSpawnHandler.getInstance().getAutoSpawnInstances(ORATOR_NPC_ID); + _preacherSpawns = AutoSpawnHandler.getInstance().getAutoSpawnInstances(PREACHER_NPC_ID); + + if (isSealValidationPeriod() || isCompResultsPeriod()) + { + for (AutoSpawnInstance spawnInst : _marketeerSpawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, true); + } + + if ((getSealOwner(SEAL_GNOSIS) == getCabalHighestScore()) && (getSealOwner(SEAL_GNOSIS) != CABAL_NULL)) + { + if (!Config.ANNOUNCE_MAMMON_SPAWN) + { + _blacksmithSpawn.setBroadcast(false); + } + + if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(_blacksmithSpawn.getObjectId(), true).isSpawnActive()) + { + AutoSpawnHandler.getInstance().setSpawnActive(_blacksmithSpawn, true); + } + + for (AutoSpawnInstance spawnInst : _oratorSpawns.values()) + { + if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(spawnInst.getObjectId(), true).isSpawnActive()) + { + AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, true); + } + } + + for (AutoSpawnInstance spawnInst : _preacherSpawns.values()) + { + if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(spawnInst.getObjectId(), true).isSpawnActive()) + { + AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, true); + } + } + + if (!AutoChatHandler.getInstance().getAutoChatInstance(PREACHER_NPC_ID, false).isActive() && !AutoChatHandler.getInstance().getAutoChatInstance(ORATOR_NPC_ID, false).isActive()) + { + AutoChatHandler.getInstance().setAutoChatActive(true); + } + } + else + { + AutoSpawnHandler.getInstance().setSpawnActive(_blacksmithSpawn, false); + + for (AutoSpawnInstance spawnInst : _oratorSpawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, false); + } + + for (AutoSpawnInstance spawnInst : _preacherSpawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, false); + } + + AutoChatHandler.getInstance().setAutoChatActive(false); + } + + if ((getSealOwner(SEAL_AVARICE) == getCabalHighestScore()) && (getSealOwner(SEAL_AVARICE) != CABAL_NULL)) + { + if (!Config.ANNOUNCE_MAMMON_SPAWN) + { + _merchantSpawn.setBroadcast(false); + } + + if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(_merchantSpawn.getObjectId(), true).isSpawnActive()) + { + AutoSpawnHandler.getInstance().setSpawnActive(_merchantSpawn, true); + } + + switch (getCabalHighestScore()) + { + case CABAL_DAWN: + if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(_lilithSpawn.getObjectId(), true).isSpawnActive()) + { + AutoSpawnHandler.getInstance().setSpawnActive(_lilithSpawn, true); + } + + AutoSpawnHandler.getInstance().setSpawnActive(_anakimSpawn, false); + + for (AutoSpawnInstance dawnCrest : _crestofdawnspawns.values()) + { + if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(dawnCrest.getObjectId(), true).isSpawnActive()) + { + AutoSpawnHandler.getInstance().setSpawnActive(dawnCrest, true); + } + } + + for (AutoSpawnInstance duskCrest : _crestofduskspawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(duskCrest, false); + } + break; + + case CABAL_DUSK: + if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(_anakimSpawn.getObjectId(), true).isSpawnActive()) + { + AutoSpawnHandler.getInstance().setSpawnActive(_anakimSpawn, true); + } + + AutoSpawnHandler.getInstance().setSpawnActive(_lilithSpawn, false); + + for (AutoSpawnInstance duskCrest : _crestofduskspawns.values()) + { + if (!AutoSpawnHandler.getInstance().getAutoSpawnInstance(duskCrest.getObjectId(), true).isSpawnActive()) + { + AutoSpawnHandler.getInstance().setSpawnActive(duskCrest, true); + } + } + + for (AutoSpawnInstance dawnCrest : _crestofdawnspawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(dawnCrest, false); + } + break; + } + } + else + { + AutoSpawnHandler.getInstance().setSpawnActive(_merchantSpawn, false); + AutoSpawnHandler.getInstance().setSpawnActive(_lilithSpawn, false); + AutoSpawnHandler.getInstance().setSpawnActive(_anakimSpawn, false); + for (AutoSpawnInstance dawnCrest : _crestofdawnspawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(dawnCrest, false); + } + for (AutoSpawnInstance duskCrest : _crestofduskspawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(duskCrest, false); + } + } + } + else + { + AutoSpawnHandler.getInstance().setSpawnActive(_merchantSpawn, false); + AutoSpawnHandler.getInstance().setSpawnActive(_blacksmithSpawn, false); + AutoSpawnHandler.getInstance().setSpawnActive(_lilithSpawn, false); + AutoSpawnHandler.getInstance().setSpawnActive(_anakimSpawn, false); + for (AutoSpawnInstance dawnCrest : _crestofdawnspawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(dawnCrest, false); + } + for (AutoSpawnInstance duskCrest : _crestofduskspawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(duskCrest, false); + } + for (AutoSpawnInstance spawnInst : _oratorSpawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, false); + } + + for (AutoSpawnInstance spawnInst : _preacherSpawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, false); + } + + for (AutoSpawnInstance spawnInst : _marketeerSpawns.values()) + { + AutoSpawnHandler.getInstance().setSpawnActive(spawnInst, false); + } + + AutoChatHandler.getInstance().setAutoChatActive(false); + } + } + + public static SevenSigns getInstance() + { + return SingletonHolder._instance; + } + + public static long calcContributionScore(long blueCount, long greenCount, long redCount) + { + long contrib = blueCount * BLUE_CONTRIB_POINTS; + contrib += greenCount * GREEN_CONTRIB_POINTS; + contrib += redCount * RED_CONTRIB_POINTS; + + return contrib; + } + + public static long calcAncientAdenaReward(long blueCount, long greenCount, long redCount) + { + long reward = blueCount * SEAL_STONE_BLUE_VALUE; + reward += greenCount * SEAL_STONE_GREEN_VALUE; + reward += redCount * SEAL_STONE_RED_VALUE; + + return reward; + } + + public static final String getCabalShortName(int cabal) + { + switch (cabal) + { + case CABAL_DAWN: + return "dawn"; + case CABAL_DUSK: + return "dusk"; + } + + return "No Cabal"; + } + + public static final String getCabalName(int cabal) + { + switch (cabal) + { + case CABAL_DAWN: + return "Lords of Dawn"; + case CABAL_DUSK: + return "Revolutionaries of Dusk"; + } + + return "No Cabal"; + } + + public static final String getSealName(int seal, boolean shortName) + { + String sealName = (!shortName) ? "Seal of " : ""; + + switch (seal) + { + case SEAL_AVARICE: + sealName += "Avarice"; + break; + case SEAL_GNOSIS: + sealName += "Gnosis"; + break; + case SEAL_STRIFE: + sealName += "Strife"; + break; + } + + return sealName; + } + + public final int getCurrentCycle() + { + return _currentCycle; + } + + public final int getCurrentPeriod() + { + return _activePeriod; + } + + private final int getDaysToPeriodChange() + { + int numDays = _nextPeriodChange.get(Calendar.DAY_OF_WEEK) - PERIOD_START_DAY; + + if (numDays < 0) + { + return 0 - numDays; + } + + return 7 - numDays; + } + + public final long getMilliToPeriodChange() + { + long currTimeMillis = System.currentTimeMillis(); + long changeTimeMillis = _nextPeriodChange.getTimeInMillis(); + + return (changeTimeMillis - currTimeMillis); + } + + protected void setCalendarForNextPeriodChange() + { + // Calculate the number of days until the next period + // A period starts at 18:00 pm (local time), like on official servers. + switch (getCurrentPeriod()) + { + case PERIOD_SEAL_VALIDATION: + case PERIOD_COMPETITION: + int daysToChange = getDaysToPeriodChange(); + + if (daysToChange == 7) + { + if (_nextPeriodChange.get(Calendar.HOUR_OF_DAY) < PERIOD_START_HOUR) + { + daysToChange = 0; + } + else if ((_nextPeriodChange.get(Calendar.HOUR_OF_DAY) == PERIOD_START_HOUR) && (_nextPeriodChange.get(Calendar.MINUTE) < PERIOD_START_MINS)) + { + daysToChange = 0; + } + } + + // Otherwise... + if (daysToChange > 0) + { + _nextPeriodChange.add(Calendar.DATE, daysToChange); + } + + _nextPeriodChange.set(Calendar.HOUR_OF_DAY, PERIOD_START_HOUR); + _nextPeriodChange.set(Calendar.MINUTE, PERIOD_START_MINS); + break; + case PERIOD_COMP_RECRUITING: + case PERIOD_COMP_RESULTS: + _nextPeriodChange.add(Calendar.MILLISECOND, PERIOD_MINOR_LENGTH); + break; + } + _log.info("SevenSigns: Next period change set to " + _nextPeriodChange.getTime()); + } + + public final String getCurrentPeriodName() + { + String periodName = null; + + switch (_activePeriod) + { + case PERIOD_COMP_RECRUITING: + periodName = "Quest Event Initialization"; + break; + case PERIOD_COMPETITION: + periodName = "Competition (Quest Event)"; + break; + case PERIOD_COMP_RESULTS: + periodName = "Quest Event Results"; + break; + case PERIOD_SEAL_VALIDATION: + periodName = "Seal Validation"; + break; + } + + return periodName; + } + + public final boolean isSealValidationPeriod() + { + return (_activePeriod == PERIOD_SEAL_VALIDATION); + } + + public final boolean isCompResultsPeriod() + { + return (_activePeriod == PERIOD_COMP_RESULTS); + } + + /** + * returns true if the given date is in Seal Validation or in Quest Event Results period + * @param date + */ + public boolean isDateInSealValidPeriod(Calendar date) + { + long nextPeriodChange = getMilliToPeriodChange(); + long nextQuestStart = 0; + long nextValidStart = 0; + long tillDate = date.getTimeInMillis() - Calendar.getInstance().getTimeInMillis(); + while (((2 * PERIOD_MAJOR_LENGTH) + (2 * PERIOD_MINOR_LENGTH)) < tillDate) + { + tillDate -= ((2 * PERIOD_MAJOR_LENGTH) + (2 * PERIOD_MINOR_LENGTH)); + } + while (tillDate < 0) + { + tillDate += ((2 * PERIOD_MAJOR_LENGTH) + (2 * PERIOD_MINOR_LENGTH)); + } + + switch (getCurrentPeriod()) + { + case PERIOD_COMP_RECRUITING: + nextValidStart = nextPeriodChange + PERIOD_MAJOR_LENGTH; + nextQuestStart = nextValidStart + PERIOD_MAJOR_LENGTH + PERIOD_MINOR_LENGTH; + break; + case PERIOD_COMPETITION: + nextValidStart = nextPeriodChange; + nextQuestStart = nextPeriodChange + PERIOD_MAJOR_LENGTH + PERIOD_MINOR_LENGTH; + break; + case PERIOD_COMP_RESULTS: + nextQuestStart = nextPeriodChange + PERIOD_MAJOR_LENGTH; + nextValidStart = nextQuestStart + PERIOD_MAJOR_LENGTH + PERIOD_MINOR_LENGTH; + break; + case PERIOD_SEAL_VALIDATION: + nextQuestStart = nextPeriodChange; + nextValidStart = nextPeriodChange + PERIOD_MAJOR_LENGTH + PERIOD_MINOR_LENGTH; + break; + } + + if (((nextQuestStart < tillDate) && (tillDate < nextValidStart)) || ((nextValidStart < nextQuestStart) && ((tillDate < nextValidStart) || (nextQuestStart < tillDate)))) + { + return false; + } + return true; + } + + public final int getCurrentScore(int cabal) + { + double totalStoneScore = _dawnStoneScore + _duskStoneScore; + + switch (cabal) + { + case CABAL_NULL: + return 0; + case CABAL_DAWN: + return Math.round((float) (_dawnStoneScore / ((float) totalStoneScore == 0 ? 1 : totalStoneScore)) * 500) + _dawnFestivalScore; + case CABAL_DUSK: + return Math.round((float) (_duskStoneScore / ((float) totalStoneScore == 0 ? 1 : totalStoneScore)) * 500) + _duskFestivalScore; + } + + return 0; + } + + public final double getCurrentStoneScore(int cabal) + { + switch (cabal) + { + case CABAL_NULL: + return 0; + case CABAL_DAWN: + return _dawnStoneScore; + case CABAL_DUSK: + return _duskStoneScore; + } + + return 0; + } + + public final int getCurrentFestivalScore(int cabal) + { + switch (cabal) + { + case CABAL_NULL: + return 0; + case CABAL_DAWN: + return _dawnFestivalScore; + case CABAL_DUSK: + return _duskFestivalScore; + } + + return 0; + } + + public final int getCabalHighestScore() + { + if (getCurrentScore(CABAL_DUSK) == getCurrentScore(CABAL_DAWN)) + { + return CABAL_NULL; + } + else if (getCurrentScore(CABAL_DUSK) > getCurrentScore(CABAL_DAWN)) + { + return CABAL_DUSK; + } + else + { + return CABAL_DAWN; + } + } + + public final int getSealOwner(int seal) + { + return _signsSealOwners.get(seal); + } + + public final int getSealProportion(int seal, int cabal) + { + if (cabal == CABAL_NULL) + { + return 0; + } + else if (cabal == CABAL_DUSK) + { + return _signsDuskSealTotals.get(seal); + } + else + { + return _signsDawnSealTotals.get(seal); + } + } + + public final int getTotalMembers(int cabal) + { + int cabalMembers = 0; + String cabalName = getCabalShortName(cabal); + + for (StatsSet sevenDat : _signsPlayerData.values()) + { + if (sevenDat.getString("cabal").equals(cabalName)) + { + cabalMembers++; + } + } + + return cabalMembers; + } + + public int getPlayerStoneContrib(int objectId) + { + final StatsSet currPlayer = _signsPlayerData.get(objectId); + if (currPlayer == null) + { + return 0; + } + + int stoneCount = 0; + stoneCount += currPlayer.getInteger("red_stones"); + stoneCount += currPlayer.getInteger("green_stones"); + stoneCount += currPlayer.getInteger("blue_stones"); + + return stoneCount; + } + + public int getPlayerContribScore(int objectId) + { + final StatsSet currPlayer = _signsPlayerData.get(objectId); + if (currPlayer == null) + { + return 0; + } + + return currPlayer.getInteger("contribution_score"); + } + + public int getPlayerAdenaCollect(int objectId) + { + final StatsSet currPlayer = _signsPlayerData.get(objectId); + if (currPlayer == null) + { + return 0; + } + + return currPlayer.getInteger("ancient_adena_amount"); + } + + public int getPlayerSeal(int objectId) + { + final StatsSet currPlayer = _signsPlayerData.get(objectId); + if (currPlayer == null) + { + return SEAL_NULL; + } + + return currPlayer.getInteger("seal"); + } + + public int getPlayerCabal(int objectId) + { + final StatsSet currPlayer = _signsPlayerData.get(objectId); + if (currPlayer == null) + { + return CABAL_NULL; + } + + String playerCabal = currPlayer.getString("cabal"); + if (playerCabal.equalsIgnoreCase("dawn")) + { + return CABAL_DAWN; + } + else if (playerCabal.equalsIgnoreCase("dusk")) + { + return CABAL_DUSK; + } + else + { + return CABAL_NULL; + } + } + + /** + * Restores all Seven Signs data and settings, usually called at server startup. + * @throws Exception + */ + protected void restoreSevenSignsData() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(LOAD_DATA); + ResultSet rset = statement.executeQuery(); + + StatsSet sevenDat = null; + int charObjId; + + while (rset.next()) + { + charObjId = rset.getInt("charId"); + + sevenDat = new StatsSet(); + sevenDat.set("charId", charObjId); + sevenDat.set("cabal", rset.getString("cabal")); + sevenDat.set("seal", rset.getInt("seal")); + sevenDat.set("red_stones", rset.getInt("red_stones")); + sevenDat.set("green_stones", rset.getInt("green_stones")); + sevenDat.set("blue_stones", rset.getInt("blue_stones")); + sevenDat.set("ancient_adena_amount", rset.getDouble("ancient_adena_amount")); + sevenDat.set("contribution_score", rset.getDouble("contribution_score")); + + if (Config.DEBUG) + { + _log.info("SevenSigns: Loaded data from DB for char ID " + charObjId + " (" + sevenDat.getString("cabal") + ")"); + } + + _signsPlayerData.put(charObjId, sevenDat); + } + + rset.close(); + statement.close(); + + statement = con.prepareStatement(LOAD_STATUS); + rset = statement.executeQuery(); + + while (rset.next()) + { + _currentCycle = rset.getInt("current_cycle"); + _activePeriod = rset.getInt("active_period"); + _previousWinner = rset.getInt("previous_winner"); + + _dawnStoneScore = rset.getDouble("dawn_stone_score"); + _dawnFestivalScore = rset.getInt("dawn_festival_score"); + _duskStoneScore = rset.getDouble("dusk_stone_score"); + _duskFestivalScore = rset.getInt("dusk_festival_score"); + + _signsSealOwners.put(SEAL_AVARICE, rset.getInt("avarice_owner")); + _signsSealOwners.put(SEAL_GNOSIS, rset.getInt("gnosis_owner")); + _signsSealOwners.put(SEAL_STRIFE, rset.getInt("strife_owner")); + + _signsDawnSealTotals.put(SEAL_AVARICE, rset.getInt("avarice_dawn_score")); + _signsDawnSealTotals.put(SEAL_GNOSIS, rset.getInt("gnosis_dawn_score")); + _signsDawnSealTotals.put(SEAL_STRIFE, rset.getInt("strife_dawn_score")); + _signsDuskSealTotals.put(SEAL_AVARICE, rset.getInt("avarice_dusk_score")); + _signsDuskSealTotals.put(SEAL_GNOSIS, rset.getInt("gnosis_dusk_score")); + _signsDuskSealTotals.put(SEAL_STRIFE, rset.getInt("strife_dusk_score")); + + _lastSave.setTimeInMillis(rset.getLong("date")); + } + rset.close(); + statement.close(); + } + catch (SQLException e) + { + _log.log(Level.SEVERE, "SevenSigns: Unable to load Seven Signs data from database: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + + // Festival data is loaded now after the Seven Signs engine data. + } + + /** + * Saves all Seven Signs player data. Should be called on period change and shutdown only.
    + * @param player + * @param updateSettings + * @throws Exception + */ + public void saveSevenSignsData() + { + if (Config.DEBUG) + { + _log.info("SevenSigns: Saving data to disk."); + } + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(UPDATE_PLAYER); + + for (StatsSet sevenDat : _signsPlayerData.values()) + { + statement.setString(1, sevenDat.getString("cabal")); + statement.setInt(2, sevenDat.getInteger("seal")); + statement.setInt(3, sevenDat.getInteger("red_stones")); + statement.setInt(4, sevenDat.getInteger("green_stones")); + statement.setInt(5, sevenDat.getInteger("blue_stones")); + statement.setDouble(6, sevenDat.getDouble("ancient_adena_amount")); + statement.setDouble(7, sevenDat.getDouble("contribution_score")); + statement.setInt(8, sevenDat.getInteger("charId")); + statement.execute(); + statement.clearParameters(); + if (Config.DEBUG) + { + _log.info("SevenSigns: Updated data in database for char ID " + sevenDat.getInteger("charId") + " (" + sevenDat.getString("cabal") + ")"); + } + } + statement.close(); + } + catch (SQLException e) + { + _log.log(Level.SEVERE, "SevenSigns: Unable to save data to database: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public final void saveSevenSignsData(int objectId) + { + StatsSet sevenDat = _signsPlayerData.get(objectId); + if (sevenDat == null) + { + return; + } + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + PreparedStatement statement = con.prepareStatement(UPDATE_PLAYER); + statement.setString(1, sevenDat.getString("cabal")); + statement.setInt(2, sevenDat.getInteger("seal")); + statement.setInt(3, sevenDat.getInteger("red_stones")); + statement.setInt(4, sevenDat.getInteger("green_stones")); + statement.setInt(5, sevenDat.getInteger("blue_stones")); + statement.setDouble(6, sevenDat.getDouble("ancient_adena_amount")); + statement.setDouble(7, sevenDat.getDouble("contribution_score")); + statement.setInt(8, sevenDat.getInteger("charId")); + statement.execute(); + statement.close(); + } + catch (SQLException e) + { + _log.log(Level.SEVERE, "SevenSigns: Unable to save data to database: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public final void saveSevenSignsStatus() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + PreparedStatement statement = con.prepareStatement(UPDATE_STATUS); + statement.setInt(1, _currentCycle); + statement.setInt(2, _activePeriod); + statement.setInt(3, _previousWinner); + statement.setDouble(4, _dawnStoneScore); + statement.setInt(5, _dawnFestivalScore); + statement.setDouble(6, _duskStoneScore); + statement.setInt(7, _duskFestivalScore); + statement.setInt(8, _signsSealOwners.get(SEAL_AVARICE)); + statement.setInt(9, _signsSealOwners.get(SEAL_GNOSIS)); + statement.setInt(10, _signsSealOwners.get(SEAL_STRIFE)); + statement.setInt(11, _signsDawnSealTotals.get(SEAL_AVARICE)); + statement.setInt(12, _signsDawnSealTotals.get(SEAL_GNOSIS)); + statement.setInt(13, _signsDawnSealTotals.get(SEAL_STRIFE)); + statement.setInt(14, _signsDuskSealTotals.get(SEAL_AVARICE)); + statement.setInt(15, _signsDuskSealTotals.get(SEAL_GNOSIS)); + statement.setInt(16, _signsDuskSealTotals.get(SEAL_STRIFE)); + statement.setInt(17, SevenSignsFestival.getInstance().getCurrentFestivalCycle()); + + for (int i = 0; i < SevenSignsFestival.FESTIVAL_COUNT; i++) + { + statement.setInt(18 + i, SevenSignsFestival.getInstance().getAccumulatedBonus(i)); + } + + _lastSave = Calendar.getInstance(); + statement.setLong(18 + SevenSignsFestival.FESTIVAL_COUNT, _lastSave.getTimeInMillis()); + statement.execute(); + statement.close(); + if (Config.DEBUG) + { + _log.info("SevenSigns: Updated data in database."); + } + } + catch (SQLException e) + { + _log.log(Level.SEVERE, "SevenSigns: Unable to save data to database: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Used to reset the cabal details of all players, and update the database.
    + * Primarily used when beginning a new cycle, and should otherwise never be called. + */ + protected void resetPlayerData() + { + if (Config.DEBUG) + { + _log.info("SevenSigns: Resetting player data for new event period."); + } + + int charObjId; + + // Reset each player's contribution data as well as seal and cabal. + for (StatsSet sevenDat : _signsPlayerData.values()) + { + charObjId = sevenDat.getInteger("charId"); + + // Reset the player's cabal and seal information + sevenDat.set("cabal", ""); + sevenDat.set("seal", SEAL_NULL); + sevenDat.set("contribution_score", 0); + + _signsPlayerData.put(charObjId, sevenDat); + } + } + + /** + * Used to specify cabal-related details for the specified player. This method checks to see if the player has registered before and will update the database if necessary.
    + * Returns the cabal ID the player has joined. + * @param player + * @param chosenCabal + * @param chosenSeal + * @return int cabal + */ + public int setPlayerInfo(int objectId, int chosenCabal, int chosenSeal) + { + StatsSet currPlayerData = _signsPlayerData.get(objectId); + + if (currPlayerData != null) + { + // If the seal validation period has passed, + // cabal information was removed and so "re-register" player + currPlayerData.set("cabal", getCabalShortName(chosenCabal)); + currPlayerData.set("seal", chosenSeal); + + _signsPlayerData.put(objectId, currPlayerData); + } + else + { + currPlayerData = new StatsSet(); + currPlayerData.set("charId", objectId); + currPlayerData.set("cabal", getCabalShortName(chosenCabal)); + currPlayerData.set("seal", chosenSeal); + currPlayerData.set("red_stones", 0); + currPlayerData.set("green_stones", 0); + currPlayerData.set("blue_stones", 0); + currPlayerData.set("ancient_adena_amount", 0); + currPlayerData.set("contribution_score", 0); + + _signsPlayerData.put(objectId, currPlayerData); + + // Update data in database, as we have a new player signing up. + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(INSERT_PLAYER); + statement.setInt(1, objectId); + statement.setString(2, getCabalShortName(chosenCabal)); + statement.setInt(3, chosenSeal); + statement.execute(); + statement.close(); + + if (Config.DEBUG) + { + _log.info("SevenSigns: Inserted data in DB for char ID " + currPlayerData.getInteger("charId") + " (" + currPlayerData.getString("cabal") + ")"); + } + } + catch (SQLException e) + { + _log.log(Level.SEVERE, "SevenSigns: Failed to save data: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + // Increasing Seal total score for the player chosen Seal. + if ("dawn".equals(currPlayerData.getString("cabal"))) + { + _signsDawnSealTotals.put(chosenSeal, _signsDawnSealTotals.get(chosenSeal) + 1); + } + else + { + _signsDuskSealTotals.put(chosenSeal, _signsDuskSealTotals.get(chosenSeal) + 1); + } + + if (!Config.ALT_SEVENSIGNS_LAZY_UPDATE) + { + saveSevenSignsStatus(); + } + + return chosenCabal; + } + + /** + * Returns the amount of ancient adena the specified player can claim, if any.
    + * If removeReward = True, all the ancient adena owed to them is removed, then DB is updated. + * @param player + * @param removeReward + * @return int rewardAmount + */ + public int getAncientAdenaReward(int objectId, boolean removeReward) + { + StatsSet currPlayer = _signsPlayerData.get(objectId); + int rewardAmount = currPlayer.getInteger("ancient_adena_amount"); + + currPlayer.set("red_stones", 0); + currPlayer.set("green_stones", 0); + currPlayer.set("blue_stones", 0); + currPlayer.set("ancient_adena_amount", 0); + + if (removeReward) + { + _signsPlayerData.put(objectId, currPlayer); + if (!Config.ALT_SEVENSIGNS_LAZY_UPDATE) + { + saveSevenSignsData(objectId); + saveSevenSignsStatus(); + } + } + + return rewardAmount; + } + + /** + * Used to add the specified player's seal stone contribution points to the current total for their cabal. Returns the point score the contribution was worth. Each stone count must be broken down and specified by the stone's color. + * @param player + * @param blueCount + * @param greenCount + * @param redCount + * @return int contribScore + */ + public long addPlayerStoneContrib(int objectId, long blueCount, long greenCount, long redCount) + { + StatsSet currPlayer = _signsPlayerData.get(objectId); + + long contribScore = calcContributionScore(blueCount, greenCount, redCount); + long totalAncientAdena = currPlayer.getLong("ancient_adena_amount") + calcAncientAdenaReward(blueCount, greenCount, redCount); + long totalContribScore = currPlayer.getLong("contribution_score") + contribScore; + + if (totalContribScore > Config.ALT_MAXIMUM_PLAYER_CONTRIB) + { + return -1; + } + + currPlayer.set("red_stones", currPlayer.getInteger("red_stones") + redCount); + currPlayer.set("green_stones", currPlayer.getInteger("green_stones") + greenCount); + currPlayer.set("blue_stones", currPlayer.getInteger("blue_stones") + blueCount); + currPlayer.set("ancient_adena_amount", totalAncientAdena); + currPlayer.set("contribution_score", totalContribScore); + _signsPlayerData.put(objectId, currPlayer); + + switch (getPlayerCabal(objectId)) + { + case CABAL_DAWN: + _dawnStoneScore += contribScore; + break; + case CABAL_DUSK: + _duskStoneScore += contribScore; + break; + } + + if (!Config.ALT_SEVENSIGNS_LAZY_UPDATE) + { + saveSevenSignsData(objectId); + saveSevenSignsStatus(); + } + + return contribScore; + } + + /** + * Adds the specified number of festival points to the specified cabal. Remember, the same number of points are deducted from the rival cabal to maintain proportionality. + * @param cabal + * @param amount + */ + public void addFestivalScore(int cabal, int amount) + { + if (cabal == CABAL_DUSK) + { + _duskFestivalScore += amount; + + // To prevent negative scores! + if (_dawnFestivalScore >= amount) + { + _dawnFestivalScore -= amount; + } + } + else + { + _dawnFestivalScore += amount; + + if (_duskFestivalScore >= amount) + { + _duskFestivalScore -= amount; + } + } + } + + /** + * Send info on the current Seven Signs period to the specified player. + * @param player + */ + public void sendCurrentPeriodMsg(L2PcInstance player) + { + SystemMessage sm = null; + + switch (getCurrentPeriod()) + { + case PERIOD_COMP_RECRUITING: + sm = SystemMessage.getSystemMessage(SystemMessageId.PREPARATIONS_PERIOD_BEGUN); + break; + case PERIOD_COMPETITION: + sm = SystemMessage.getSystemMessage(SystemMessageId.COMPETITION_PERIOD_BEGUN); + break; + case PERIOD_COMP_RESULTS: + sm = SystemMessage.getSystemMessage(SystemMessageId.RESULTS_PERIOD_BEGUN); + break; + case PERIOD_SEAL_VALIDATION: + sm = SystemMessage.getSystemMessage(SystemMessageId.VALIDATION_PERIOD_BEGUN); + break; + } + + player.sendPacket(sm); + } + + /** + * Sends the built-in system message specified by sysMsgId to all online players. + * @param sysMsgId + */ + public void sendMessageToAll(SystemMessageId sysMsgId) + { + SystemMessage sm = SystemMessage.getSystemMessage(sysMsgId); + Broadcast.toAllOnlinePlayers(sm); + } + + /** + * Used to initialize the seals for each cabal. (Used at startup or at beginning of a new cycle). This method should be called after resetSeals() and calcNewSealOwners() on a new cycle. + */ + protected void initializeSeals() + { + for (Integer currSeal : _signsSealOwners.keySet()) + { + int sealOwner = _signsSealOwners.get(currSeal); + + if (sealOwner != CABAL_NULL) + { + if (isSealValidationPeriod()) + { + _log.info("SevenSigns: The " + getCabalName(sealOwner) + " have won the " + getSealName(currSeal, false) + "."); + } + else + { + _log.info("SevenSigns: The " + getSealName(currSeal, false) + " is currently owned by " + getCabalName(sealOwner) + "."); + } + } + else + { + _log.info("SevenSigns: The " + getSealName(currSeal, false) + " remains unclaimed."); + } + } + } + + /** + * Only really used at the beginning of a new cycle, this method resets all seal-related data. + */ + protected void resetSeals() + { + _signsDawnSealTotals.put(SEAL_AVARICE, 0); + _signsDawnSealTotals.put(SEAL_GNOSIS, 0); + _signsDawnSealTotals.put(SEAL_STRIFE, 0); + _signsDuskSealTotals.put(SEAL_AVARICE, 0); + _signsDuskSealTotals.put(SEAL_GNOSIS, 0); + _signsDuskSealTotals.put(SEAL_STRIFE, 0); + } + + /** + * Calculates the ownership of the three Seals of the Seven Signs, based on various criterion.
    + *
    + * Should only ever called at the beginning of a new cycle. + */ + protected void calcNewSealOwners() + { + if (Config.DEBUG) + { + _log.info("SevenSigns: (Avarice) Dawn = " + _signsDawnSealTotals.get(SEAL_AVARICE) + ", Dusk = " + _signsDuskSealTotals.get(SEAL_AVARICE)); + _log.info("SevenSigns: (Gnosis) Dawn = " + _signsDawnSealTotals.get(SEAL_GNOSIS) + ", Dusk = " + _signsDuskSealTotals.get(SEAL_GNOSIS)); + _log.info("SevenSigns: (Strife) Dawn = " + _signsDawnSealTotals.get(SEAL_STRIFE) + ", Dusk = " + _signsDuskSealTotals.get(SEAL_STRIFE)); + } + + for (Integer currSeal : _signsDawnSealTotals.keySet()) + { + int prevSealOwner = _signsSealOwners.get(currSeal); + int newSealOwner = CABAL_NULL; + int dawnProportion = getSealProportion(currSeal, CABAL_DAWN); + int totalDawnMembers = getTotalMembers(CABAL_DAWN) == 0 ? 1 : getTotalMembers(CABAL_DAWN); + int dawnPercent = Math.round(((float) dawnProportion / (float) totalDawnMembers) * 100); + int duskProportion = getSealProportion(currSeal, CABAL_DUSK); + int totalDuskMembers = getTotalMembers(CABAL_DUSK) == 0 ? 1 : getTotalMembers(CABAL_DUSK); + int duskPercent = Math.round(((float) duskProportion / (float) totalDuskMembers) * 100); + + /* + * - If a Seal was already closed or owned by the opponent and the new winner wants to assume ownership of the Seal, 35% or more of the members of the Cabal must have chosen the Seal. If they chose less than 35%, they cannot own the Seal. - If the Seal was owned by the winner in the + * previous Seven Signs, they can retain that seal if 10% or more members have chosen it. If they want to possess a new Seal, at least 35% of the members of the Cabal must have chosen the new Seal. + */ + switch (prevSealOwner) + { + case CABAL_NULL: + switch (getCabalHighestScore()) + { + case CABAL_NULL: + newSealOwner = CABAL_NULL; + break; + case CABAL_DAWN: + if (dawnPercent >= 35) + { + newSealOwner = CABAL_DAWN; + } + else + { + newSealOwner = CABAL_NULL; + } + break; + case CABAL_DUSK: + if (duskPercent >= 35) + { + newSealOwner = CABAL_DUSK; + } + else + { + newSealOwner = CABAL_NULL; + } + break; + } + break; + case CABAL_DAWN: + switch (getCabalHighestScore()) + { + case CABAL_NULL: + if (dawnPercent >= 10) + { + newSealOwner = CABAL_DAWN; + } + else + { + newSealOwner = CABAL_NULL; + } + break; + case CABAL_DAWN: + if (dawnPercent >= 10) + { + newSealOwner = CABAL_DAWN; + } + else + { + newSealOwner = CABAL_NULL; + } + break; + case CABAL_DUSK: + if (duskPercent >= 35) + { + newSealOwner = CABAL_DUSK; + } + else if (dawnPercent >= 10) + { + newSealOwner = CABAL_DAWN; + } + else + { + newSealOwner = CABAL_NULL; + } + break; + } + break; + case CABAL_DUSK: + switch (getCabalHighestScore()) + { + case CABAL_NULL: + if (duskPercent >= 10) + { + newSealOwner = CABAL_DUSK; + } + else + { + newSealOwner = CABAL_NULL; + } + break; + case CABAL_DAWN: + if (dawnPercent >= 35) + { + newSealOwner = CABAL_DAWN; + } + else if (duskPercent >= 10) + { + newSealOwner = CABAL_DUSK; + } + else + { + newSealOwner = CABAL_NULL; + } + break; + case CABAL_DUSK: + if (duskPercent >= 10) + { + newSealOwner = CABAL_DUSK; + } + else + { + newSealOwner = CABAL_NULL; + } + break; + } + break; + } + + _signsSealOwners.put(currSeal, newSealOwner); + + // Alert all online players to new seal status. + switch (currSeal) + { + case SEAL_AVARICE: + if (newSealOwner == CABAL_DAWN) + { + sendMessageToAll(SystemMessageId.DAWN_OBTAINED_AVARICE); + } + else if (newSealOwner == CABAL_DUSK) + { + sendMessageToAll(SystemMessageId.DUSK_OBTAINED_AVARICE); + } + break; + case SEAL_GNOSIS: + if (newSealOwner == CABAL_DAWN) + { + sendMessageToAll(SystemMessageId.DAWN_OBTAINED_GNOSIS); + } + else if (newSealOwner == CABAL_DUSK) + { + sendMessageToAll(SystemMessageId.DUSK_OBTAINED_GNOSIS); + } + break; + case SEAL_STRIFE: + if (newSealOwner == CABAL_DAWN) + { + sendMessageToAll(SystemMessageId.DAWN_OBTAINED_STRIFE); + } + else if (newSealOwner == CABAL_DUSK) + { + sendMessageToAll(SystemMessageId.DUSK_OBTAINED_STRIFE); + } + + CastleManager.getInstance().validateTaxes(newSealOwner); + break; + } + } + } + + /** + * This method is called to remove all players from catacombs and necropolises, who belong to the losing cabal.
    + *
    + * Should only ever called at the beginning of Seal Validation. + */ + protected void teleLosingCabalFromDungeons(String compWinner) + { + Collection pls = L2World.getInstance().getAllPlayers().values(); + // synchronized (L2World.getInstance().getAllPlayers()) + { + StatsSet currPlayer; + for (L2PcInstance onlinePlayer : pls) + { + if (onlinePlayer == null) + { + continue; + } + + currPlayer = _signsPlayerData.get(onlinePlayer.getObjectId()); + + if (isSealValidationPeriod() || isCompResultsPeriod()) + { + if (!onlinePlayer.isGM() && onlinePlayer.isIn7sDungeon() && ((currPlayer == null) || !currPlayer.getString("cabal").equals(compWinner))) + { + onlinePlayer.teleToLocation(MapRegionTable.TeleportWhereType.Town); + onlinePlayer.setIsIn7sDungeon(false); + onlinePlayer.sendMessage("You have been teleported to the nearest town due to the beginning of the Seal Validation period."); + } + } + else + { + if (!onlinePlayer.isGM() && onlinePlayer.isIn7sDungeon() && ((currPlayer == null) || !currPlayer.getString("cabal").isEmpty())) + { + onlinePlayer.teleToLocation(MapRegionTable.TeleportWhereType.Town); + onlinePlayer.setIsIn7sDungeon(false); + onlinePlayer.sendMessage("You have been teleported to the nearest town because you have not signed for any cabal."); + } + } + } + } + } + + /** + * The primary controller of period change of the Seven Signs system. This runs all related tasks depending on the period that is about to begin. + * @author Tempy + */ + protected class SevenSignsPeriodChange implements Runnable + { + @Override + public void run() + { + /* + * Remember the period check here refers to the period just ENDED! + */ + final int periodEnded = getCurrentPeriod(); + _activePeriod++; + + switch (periodEnded) + { + case PERIOD_COMP_RECRUITING: // Initialization + + // Start the Festival of Darkness cycle. + SevenSignsFestival.getInstance().startFestivalManager(); + + // Send message that Competition has begun. + sendMessageToAll(SystemMessageId.QUEST_EVENT_PERIOD_BEGUN); + break; + case PERIOD_COMPETITION: // Results Calculation + + // Send message that Competition has ended. + sendMessageToAll(SystemMessageId.QUEST_EVENT_PERIOD_ENDED); + + int compWinner = getCabalHighestScore(); + + // Schedule a stop of the festival engine and reward highest ranking members from cycle + SevenSignsFestival.getInstance().getFestivalManagerSchedule().cancel(false); + SevenSignsFestival.getInstance().rewardHighestRanked(); + + calcNewSealOwners(); + + switch (compWinner) + { + case CABAL_DAWN: + sendMessageToAll(SystemMessageId.DAWN_WON); + break; + case CABAL_DUSK: + sendMessageToAll(SystemMessageId.DUSK_WON); + break; + } + + _previousWinner = compWinner; + break; + case PERIOD_COMP_RESULTS: // Seal Validation + + // Perform initial Seal Validation set up. + initializeSeals(); + // Buff/Debuff members of the event when Seal of Strife captured. + giveCPMult(getSealOwner(SEAL_STRIFE)); + // Send message that Seal Validation has begun. + sendMessageToAll(SystemMessageId.SEAL_VALIDATION_PERIOD_BEGUN); + + // Change next Territory War date + Quest twQuest = QuestManager.getInstance().getQuest(TerritoryWarManager.qn); + if (twQuest != null) + { + twQuest.startQuestTimer("setNextTWDate", 30000, null, null); + } + + _log.info("SevenSigns: The " + getCabalName(_previousWinner) + " have won the competition with " + getCurrentScore(_previousWinner) + " points!"); + break; + case PERIOD_SEAL_VALIDATION: // Reset for New Cycle + + // Ensure a cycle restart when this period ends. + _activePeriod = PERIOD_COMP_RECRUITING; + + // Send message that Seal Validation has ended. + sendMessageToAll(SystemMessageId.SEAL_VALIDATION_PERIOD_ENDED); + // Clear Seal of Strife influence. + removeCPMult(); + // Reset all data + resetPlayerData(); + resetSeals(); + + _currentCycle++; + // Reset all Festival-related data and remove any unused blood offerings. + // NOTE: A full update of Festival data in the database is also performed. + SevenSignsFestival.getInstance().resetFestivalData(false); + + _dawnStoneScore = 0; + _duskStoneScore = 0; + + _dawnFestivalScore = 0; + _duskFestivalScore = 0; + break; + } + + // Make sure all Seven Signs data is saved for future use. + saveSevenSignsData(); + saveSevenSignsStatus(); + + teleLosingCabalFromDungeons(getCabalShortName(getCabalHighestScore())); + + SSQInfo ss = new SSQInfo(); + + Broadcast.toAllOnlinePlayers(ss); + spawnSevenSignsNPC(); + + _log.info("SevenSigns: The " + getCurrentPeriodName() + " period has begun!"); + + setCalendarForNextPeriodChange(); + + // make sure that all the scheduled siege dates are in the Seal Validation period + List castles = CastleManager.getInstance().getCastles(); + for (Castle castle : castles) + { + castle.getSiege().correctSiegeDateTime(); + } + + SevenSignsPeriodChange sspc = new SevenSignsPeriodChange(); + ThreadPoolManager.getInstance().scheduleGeneral(sspc, getMilliToPeriodChange()); + } + } + + public boolean checkIsDawnPostingTicket(int itemId) + { + // TODO I think it should be some kind of a list in the datapack for compare; + if ((itemId > 6114) && (itemId < 6175)) + { + return true; + } + if ((itemId > 6801) && (itemId < 6812)) + { + return true; + } + if ((itemId > 7997) && (itemId < 8008)) + { + return true; + } + if ((itemId > 7940) && (itemId < 7951)) + { + return true; + } + if ((itemId > 6294) && (itemId < 6307)) + { + return true; + } + if ((itemId > 6831) && (itemId < 6834)) + { + return true; + } + if ((itemId > 8027) && (itemId < 8030)) + { + return true; + } + if ((itemId > 7970) && (itemId < 7973)) + { + return true; + } + return false; + } + + public boolean checkIsRookiePostingTicket(int itemId) + { + // TODO I think it should be some kind of a list in the datapack for compare; + if ((itemId > 6174) && (itemId < 6295)) + { + return true; + } + if ((itemId > 6811) && (itemId < 6832)) + { + return true; + } + if ((itemId > 7950) && (itemId < 7971)) + { + return true; + } + if ((itemId > 8007) && (itemId < 8028)) + { + return true; + } + return false; + } + + public void giveCPMult(int StrifeOwner) + { + int cabal; + // Gives "Victor of War" passive skill to all online characters with Cabal, which controls Seal of Strife + for (L2PcInstance character : L2World.getInstance().getAllPlayers().values()) + { + if (character == null) + { + continue; + } + + cabal = getPlayerCabal(character.getObjectId()); + if (cabal != SevenSigns.CABAL_NULL) + { + if (cabal == StrifeOwner) + { + character.addSkill(SkillTable.FrequentSkill.THE_VICTOR_OF_WAR.getSkill()); + } + else + { + // Gives "The Vanquished of War" passive skill to all online characters with Cabal, which does not control Seal of Strife + character.addSkill(SkillTable.FrequentSkill.THE_VANQUISHED_OF_WAR.getSkill()); + } + } + } + } + + public void removeCPMult() + { + for (L2PcInstance character : L2World.getInstance().getAllPlayers().values()) + { + if (character == null) + { + continue; + } + + // Remove SevenSigns' buffs/debuffs. + character.removeSkill(SkillTable.FrequentSkill.THE_VICTOR_OF_WAR.getSkill()); + character.removeSkill(SkillTable.FrequentSkill.THE_VANQUISHED_OF_WAR.getSkill()); + } + } + + public boolean checkSummonConditions(L2PcInstance activeChar) + { + if (activeChar == null) + { + return true; + } + // Golems cannot be summoned by Dusk when the Seal of Strife is controlled by the Dawn + if (isSealValidationPeriod()) + { + if (getSealOwner(SEAL_STRIFE) == CABAL_DAWN) + { + if (getPlayerCabal(activeChar.getObjectId()) == CABAL_DUSK) + { + activeChar.sendMessage("You cannot summon Siege Golem or Cannon while Seal of Strife posessed by Lords of Dawn."); + return true; + } + } + } + + return false; + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final SevenSigns _instance = new SevenSigns(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/SevenSignsFestival.java b/L2J_Server/java/com/l2jserver/gameserver/SevenSignsFestival.java new file mode 100644 index 00000000..0dfd8b8a --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/SevenSignsFestival.java @@ -0,0 +1,4961 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledFuture; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.datatables.CharNameTable; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.datatables.MapRegionTable; +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.datatables.SpawnTable; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Party; +import com.l2jserver.gameserver.model.L2Spawn; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.SpawnListener; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.instance.L2FestivalMonsterInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.base.Experience; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.clientpackets.Say2; +import com.l2jserver.gameserver.network.serverpackets.CreatureSay; +import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.StatsSet; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.Rnd; + +/** + * Seven Signs Festival of Darkness Engine TODO: - Archer mobs should target healer characters over other party members. - ADDED 29 Sep: Players that leave a party during the Seven Signs Festival will now take damage and cannot be healed. + * @author Tempy + */ +public class SevenSignsFestival implements SpawnListener +{ + protected static final Logger _log = Logger.getLogger(SevenSignsFestival.class.getName()); + + private static final String GET_CLAN_NAME = "SELECT clan_name FROM clan_data WHERE clan_id = (SELECT clanid FROM characters WHERE char_name = ?)"; + + /** + * These length settings are important! :) All times are relative to the ELAPSED time (in ms) since a festival begins. Festival manager start is the time after the server starts to begin the first festival cycle. The cycle length should ideally be at least 2x longer than the festival length. + * This allows ample time for players to sign-up to participate in the festival. The intermission is the time between the festival participants being moved to the "arenas" and the spawning of the first set of mobs. The monster swarm time is the time before the monsters swarm to the center of the + * arena, after they are spawned. The chest spawn time is for when the bonus festival chests spawn, usually towards the end of the festival. + */ + public static final long FESTIVAL_SIGNUP_TIME = Config.ALT_FESTIVAL_CYCLE_LENGTH - Config.ALT_FESTIVAL_LENGTH - 60000; + + // Key Constants \\ + private static final int FESTIVAL_MAX_OFFSET_X = 230; + private static final int FESTIVAL_MAX_OFFSET_Y = 230; + private static final int FESTIVAL_DEFAULT_RESPAWN = 60; // Specify in seconds! + + public static final int FESTIVAL_COUNT = 5; // do not change without correcting db and SevenSigns itself ! + public static final int FESTIVAL_LEVEL_MAX_31 = 0; + public static final int FESTIVAL_LEVEL_MAX_42 = 1; + public static final int FESTIVAL_LEVEL_MAX_53 = 2; + public static final int FESTIVAL_LEVEL_MAX_64 = 3; + public static final int FESTIVAL_LEVEL_MAX_NONE = 4; + public static final int[] FESTIVAL_LEVEL_SCORES = + { + 60, + 70, + 100, + 120, + 150 + }; // 500 maximum possible score + + public static final int FESTIVAL_OFFERING_ID = 5901; + public static final int FESTIVAL_OFFERING_VALUE = 5; + + // ////////////////////// \\\\\\\\\\\\\\\\\\\\\\\\\\ + /* + * The following contains all the necessary spawn data for: - Player Start Locations - Witches - Monsters - Chests All data is given by: X, Y, Z (coords), Heading, NPC ID (if necessary) This may be moved externally in time, but the data should not change. + */ + public static final int[][] FESTIVAL_DAWN_PLAYER_SPAWNS = + { + { + -79187, + 113186, + -4895, + 0 + }, // 31 and below + { + -75918, + 110137, + -4895, + 0 + }, // 42 and below + { + -73835, + 111969, + -4895, + 0 + }, // 53 and below + { + -76170, + 113804, + -4895, + 0 + }, // 64 and below + { + -78927, + 109528, + -4895, + 0 + } + // No level limit + }; + + public static final int[][] FESTIVAL_DUSK_PLAYER_SPAWNS = + { + { + -77200, + 88966, + -5151, + 0 + }, // 31 and below + { + -76941, + 85307, + -5151, + 0 + }, // 42 and below + { + -74855, + 87135, + -5151, + 0 + }, // 53 and below + { + -80208, + 88222, + -5151, + 0 + }, // 64 and below + { + -79954, + 84697, + -5151, + 0 + } + // No level limit + }; + + protected static final int[][] FESTIVAL_DAWN_WITCH_SPAWNS = + { + { + -79183, + 113052, + -4891, + 0, + 31132 + }, // 31 and below + { + -75916, + 110270, + -4891, + 0, + 31133 + }, // 42 and below + { + -73979, + 111970, + -4891, + 0, + 31134 + }, // 53 and below + { + -76174, + 113663, + -4891, + 0, + 31135 + }, // 64 and below + { + -78930, + 109664, + -4891, + 0, + 31136 + } + // No level limit + }; + + protected static final int[][] FESTIVAL_DUSK_WITCH_SPAWNS = + { + { + -77199, + 88830, + -5147, + 0, + 31142 + }, // 31 and below + { + -76942, + 85438, + -5147, + 0, + 31143 + }, // 42 and below + { + -74990, + 87135, + -5147, + 0, + 31144 + }, // 53 and below + { + -80207, + 88222, + -5147, + 0, + 31145 + }, // 64 and below + { + -79952, + 84833, + -5147, + 0, + 31146 + } + // No level limit + }; + + protected static final int[][][] FESTIVAL_DAWN_PRIMARY_SPAWNS = + { + { + /* Level 31 and Below - Offering of the Branded */ + { + -78537, + 113839, + -4895, + -1, + 18009 + }, + { + -78466, + 113852, + -4895, + -1, + 18010 + }, + { + -78509, + 113899, + -4895, + -1, + 18010 + }, + + { + -78481, + 112557, + -4895, + -1, + 18009 + }, + { + -78559, + 112504, + -4895, + -1, + 18010 + }, + { + -78489, + 112494, + -4895, + -1, + 18010 + }, + + { + -79803, + 112543, + -4895, + -1, + 18012 + }, + { + -79854, + 112492, + -4895, + -1, + 18013 + }, + { + -79886, + 112557, + -4895, + -1, + 18014 + }, + + { + -79821, + 113811, + -4895, + -1, + 18015 + }, + { + -79857, + 113896, + -4895, + -1, + 18017 + }, + { + -79878, + 113816, + -4895, + -1, + 18018 + }, + + // Archers and Marksmen \\ + { + -79190, + 113660, + -4895, + -1, + 18011 + }, + { + -78710, + 113188, + -4895, + -1, + 18011 + }, + { + -79190, + 112730, + -4895, + -1, + 18016 + }, + { + -79656, + 113188, + -4895, + -1, + 18016 + } + }, + { + /* Level 42 and Below - Apostate Offering */ + { + -76558, + 110784, + -4895, + -1, + 18019 + }, + { + -76607, + 110815, + -4895, + -1, + 18020 + }, // South West + { + -76559, + 110820, + -4895, + -1, + 18020 + }, + + { + -75277, + 110792, + -4895, + -1, + 18019 + }, + { + -75225, + 110801, + -4895, + -1, + 18020 + }, // South East + { + -75262, + 110832, + -4895, + -1, + 18020 + }, + + { + -75249, + 109441, + -4895, + -1, + 18022 + }, + { + -75278, + 109495, + -4895, + -1, + 18023 + }, // North East + { + -75223, + 109489, + -4895, + -1, + 18024 + }, + + { + -76556, + 109490, + -4895, + -1, + 18025 + }, + { + -76607, + 109469, + -4895, + -1, + 18027 + }, // North West + { + -76561, + 109450, + -4895, + -1, + 18028 + }, + // Archers and Marksmen \\ + { + -76399, + 110144, + -4895, + -1, + 18021 + }, + { + -75912, + 110606, + -4895, + -1, + 18021 + }, + { + -75444, + 110144, + -4895, + -1, + 18026 + }, + { + -75930, + 109665, + -4895, + -1, + 18026 + } + }, + { + /* Level 53 and Below - Witch's Offering */ + { + -73184, + 111319, + -4895, + -1, + 18029 + }, + { + -73135, + 111294, + -4895, + -1, + 18030 + }, // South West + { + -73185, + 111281, + -4895, + -1, + 18030 + }, + + { + -74477, + 111321, + -4895, + -1, + 18029 + }, + { + -74523, + 111293, + -4895, + -1, + 18030 + }, // South East + { + -74481, + 111280, + -4895, + -1, + 18030 + }, + + { + -74489, + 112604, + -4895, + -1, + 18032 + }, + { + -74491, + 112660, + -4895, + -1, + 18033 + }, // North East + { + -74527, + 112629, + -4895, + -1, + 18034 + }, + + { + -73197, + 112621, + -4895, + -1, + 18035 + }, + { + -73142, + 112631, + -4895, + -1, + 18037 + }, // North West + { + -73182, + 112656, + -4895, + -1, + 18038 + }, + + // Archers and Marksmen \\ + { + -73834, + 112430, + -4895, + -1, + 18031 + }, + { + -74299, + 111959, + -4895, + -1, + 18031 + }, + { + -73841, + 111491, + -4895, + -1, + 18036 + }, + { + -73363, + 111959, + -4895, + -1, + 18036 + } + }, + { + /* Level 64 and Below - Dark Omen Offering */ + { + -75543, + 114461, + -4895, + -1, + 18039 + }, + { + -75514, + 114493, + -4895, + -1, + 18040 + }, // South West + { + -75488, + 114456, + -4895, + -1, + 18040 + }, + + { + -75521, + 113158, + -4895, + -1, + 18039 + }, + { + -75504, + 113110, + -4895, + -1, + 18040 + }, // South East + { + -75489, + 113142, + -4895, + -1, + 18040 + }, + + { + -76809, + 113143, + -4895, + -1, + 18042 + }, + { + -76860, + 113138, + -4895, + -1, + 18043 + }, // North East + { + -76831, + 113112, + -4895, + -1, + 18044 + }, + + { + -76831, + 114441, + -4895, + -1, + 18045 + }, + { + -76840, + 114490, + -4895, + -1, + 18047 + }, // North West + { + -76864, + 114455, + -4895, + -1, + 18048 + }, + + // Archers and Marksmen \\ + { + -75703, + 113797, + -4895, + -1, + 18041 + }, + { + -76180, + 114263, + -4895, + -1, + 18041 + }, + { + -76639, + 113797, + -4895, + -1, + 18046 + }, + { + -76180, + 113337, + -4895, + -1, + 18046 + } + }, + { + /* No Level Limit - Offering of Forbidden Path */ + { + -79576, + 108881, + -4895, + -1, + 18049 + }, + { + -79592, + 108835, + -4895, + -1, + 18050 + }, // South West + { + -79614, + 108871, + -4895, + -1, + 18050 + }, + + { + -79586, + 110171, + -4895, + -1, + 18049 + }, + { + -79589, + 110216, + -4895, + -1, + 18050 + }, // South East + { + -79620, + 110177, + -4895, + -1, + 18050 + }, + + { + -78825, + 110182, + -4895, + -1, + 18052 + }, + { + -78238, + 110182, + -4895, + -1, + 18053 + }, // North East + { + -78266, + 110218, + -4895, + -1, + 18054 + }, + + { + -78275, + 108883, + -4895, + -1, + 18055 + }, + { + -78267, + 108839, + -4895, + -1, + 18057 + }, // North West + { + -78241, + 108871, + -4895, + -1, + 18058 + }, + + // Archers and Marksmen \\ + { + -79394, + 109538, + -4895, + -1, + 18051 + }, + { + -78929, + 109992, + -4895, + -1, + 18051 + }, + { + -78454, + 109538, + -4895, + -1, + 18056 + }, + { + -78929, + 109053, + -4895, + -1, + 18056 + } + } + }; + + protected static final int[][][] FESTIVAL_DUSK_PRIMARY_SPAWNS = + { + { + /* Level 31 and Below - Offering of the Branded */ + { + -76542, + 89653, + -5151, + -1, + 18009 + }, + { + -76509, + 89637, + -5151, + -1, + 18010 + }, + { + -76548, + 89614, + -5151, + -1, + 18010 + }, + + { + -76539, + 88326, + -5151, + -1, + 18009 + }, + { + -76512, + 88289, + -5151, + -1, + 18010 + }, + { + -76546, + 88287, + -5151, + -1, + 18010 + }, + + { + -77879, + 88308, + -5151, + -1, + 18012 + }, + { + -77886, + 88310, + -5151, + -1, + 18013 + }, + { + -77879, + 88278, + -5151, + -1, + 18014 + }, + + { + -77857, + 89605, + -5151, + -1, + 18015 + }, + { + -77858, + 89658, + -5151, + -1, + 18017 + }, + { + -77891, + 89633, + -5151, + -1, + 18018 + }, + + // Archers and Marksmen \\ + { + -76728, + 88962, + -5151, + -1, + 18011 + }, + { + -77194, + 88494, + -5151, + -1, + 18011 + }, + { + -77660, + 88896, + -5151, + -1, + 18016 + }, + { + -77195, + 89438, + -5151, + -1, + 18016 + } + }, + { + /* Level 42 and Below - Apostate's Offering */ + { + -77585, + 84650, + -5151, + -1, + 18019 + }, + { + -77628, + 84643, + -5151, + -1, + 18020 + }, + { + -77607, + 84613, + -5151, + -1, + 18020 + }, + + { + -76603, + 85946, + -5151, + -1, + 18019 + }, + { + -77606, + 85994, + -5151, + -1, + 18020 + }, + { + -77638, + 85959, + -5151, + -1, + 18020 + }, + + { + -76301, + 85960, + -5151, + -1, + 18022 + }, + { + -76257, + 85972, + -5151, + -1, + 18023 + }, + { + -76286, + 85992, + -5151, + -1, + 18024 + }, + + { + -76281, + 84667, + -5151, + -1, + 18025 + }, + { + -76291, + 84611, + -5151, + -1, + 18027 + }, + { + -76257, + 84616, + -5151, + -1, + 18028 + }, + + // Archers and Marksmen \\ + { + -77419, + 85307, + -5151, + -1, + 18021 + }, + { + -76952, + 85768, + -5151, + -1, + 18021 + }, + { + -76477, + 85312, + -5151, + -1, + 18026 + }, + { + -76942, + 84832, + -5151, + -1, + 18026 + } + }, + { + /* Level 53 and Below - Witch's Offering */ + { + -74211, + 86494, + -5151, + -1, + 18029 + }, + { + -74200, + 86449, + -5151, + -1, + 18030 + }, + { + -74167, + 86464, + -5151, + -1, + 18030 + }, + + { + -75495, + 86482, + -5151, + -1, + 18029 + }, + { + -75540, + 86473, + -5151, + -1, + 18030 + }, + { + -75509, + 86445, + -5151, + -1, + 18030 + }, + + { + -75509, + 87775, + -5151, + -1, + 18032 + }, + { + -75518, + 87826, + -5151, + -1, + 18033 + }, + { + -75542, + 87780, + -5151, + -1, + 18034 + }, + + { + -74214, + 87789, + -5151, + -1, + 18035 + }, + { + -74169, + 87801, + -5151, + -1, + 18037 + }, + { + -74198, + 87827, + -5151, + -1, + 18038 + }, + + // Archers and Marksmen \\ + { + -75324, + 87135, + -5151, + -1, + 18031 + }, + { + -74852, + 87606, + -5151, + -1, + 18031 + }, + { + -74388, + 87146, + -5151, + -1, + 18036 + }, + { + -74856, + 86663, + -5151, + -1, + 18036 + } + }, + { + /* Level 64 and Below - Dark Omen Offering */ + { + -79560, + 89007, + -5151, + -1, + 18039 + }, + { + -79521, + 89016, + -5151, + -1, + 18040 + }, + { + -79544, + 89047, + -5151, + -1, + 18040 + }, + + { + -79552, + 87717, + -5151, + -1, + 18039 + }, + { + -79552, + 87673, + -5151, + -1, + 18040 + }, + { + -79510, + 87702, + -5151, + -1, + 18040 + }, + + { + -80866, + 87719, + -5151, + -1, + 18042 + }, + { + -80897, + 87689, + -5151, + -1, + 18043 + }, + { + -80850, + 87685, + -5151, + -1, + 18044 + }, + + { + -80848, + 89013, + -5151, + -1, + 18045 + }, + { + -80887, + 89051, + -5151, + -1, + 18047 + }, + { + -80891, + 89004, + -5151, + -1, + 18048 + }, + + // Archers and Marksmen \\ + { + -80205, + 87895, + -5151, + -1, + 18041 + }, + { + -80674, + 88350, + -5151, + -1, + 18041 + }, + { + -80209, + 88833, + -5151, + -1, + 18046 + }, + { + -79743, + 88364, + -5151, + -1, + 18046 + } + }, + { + /* No Level Limit - Offering of Forbidden Path */ + { + -80624, + 84060, + -5151, + -1, + 18049 + }, + { + -80621, + 84007, + -5151, + -1, + 18050 + }, + { + -80590, + 84039, + -5151, + -1, + 18050 + }, + + { + -80605, + 85349, + -5151, + -1, + 18049 + }, + { + -80639, + 85363, + -5151, + -1, + 18050 + }, + { + -80611, + 85385, + -5151, + -1, + 18050 + }, + + { + -79311, + 85353, + -5151, + -1, + 18052 + }, + { + -79277, + 85384, + -5151, + -1, + 18053 + }, + { + -79273, + 85539, + -5151, + -1, + 18054 + }, + + { + -79297, + 84054, + -5151, + -1, + 18055 + }, + { + -79285, + 84006, + -5151, + -1, + 18057 + }, + { + -79260, + 84040, + -5151, + -1, + 18058 + }, + + // Archers and Marksmen \\ + { + -79945, + 85171, + -5151, + -1, + 18051 + }, + { + -79489, + 84707, + -5151, + -1, + 18051 + }, + { + -79952, + 84222, + -5151, + -1, + 18056 + }, + { + -80423, + 84703, + -5151, + -1, + 18056 + } + } + }; + + protected static final int[][][] FESTIVAL_DAWN_SECONDARY_SPAWNS = + { + { + /* 31 and Below */ + { + -78757, + 112834, + -4895, + -1, + 18016 + }, + { + -78581, + 112834, + -4895, + -1, + 18016 + }, + { + -78822, + 112526, + -4895, + -1, + 18011 + }, + + { + -78822, + 113702, + -4895, + -1, + 18011 + }, + { + -78822, + 113874, + -4895, + -1, + 18011 + }, + { + -79524, + 113546, + -4895, + -1, + 18011 + }, + + { + -79693, + 113546, + -4895, + -1, + 18011 + }, + { + -79858, + 113546, + -4895, + -1, + 18011 + }, + { + -79545, + 112757, + -4895, + -1, + 18016 + }, + { + -79545, + 112586, + -4895, + -1, + 18016 + }, + }, + { + /* 42 and Below */ + { + -75565, + 110580, + -4895, + -1, + 18026 + }, + { + -75565, + 110740, + -4895, + -1, + 18026 + }, + { + -75577, + 109776, + -4895, + -1, + 18021 + }, + + { + -75413, + 109776, + -4895, + -1, + 18021 + }, + { + -75237, + 109776, + -4895, + -1, + 18021 + }, + { + -76274, + 109468, + -4895, + -1, + 18021 + }, + + { + -76274, + 109635, + -4895, + -1, + 18021 + }, + { + -76274, + 109795, + -4895, + -1, + 18021 + }, + { + -76351, + 110500, + -4895, + -1, + 18056 + }, + { + -76528, + 110500, + -4895, + -1, + 18056 + }, + }, + { + /* 53 and Below */ + { + -74191, + 111527, + -4895, + -1, + 18036 + }, + { + -74191, + 111362, + -4895, + -1, + 18036 + }, + { + -73495, + 111611, + -4895, + -1, + 18031 + }, + + { + -73327, + 111611, + -4895, + -1, + 18031 + }, + { + -73154, + 111611, + -4895, + -1, + 18031 + }, + { + -73473, + 112301, + -4895, + -1, + 18031 + }, + + { + -73473, + 112475, + -4895, + -1, + 18031 + }, + { + -73473, + 112649, + -4895, + -1, + 18031 + }, + { + -74270, + 112326, + -4895, + -1, + 18036 + }, + { + -74443, + 112326, + -4895, + -1, + 18036 + }, + }, + { + /* 64 and Below */ + { + -75738, + 113439, + -4895, + -1, + 18046 + }, + { + -75571, + 113439, + -4895, + -1, + 18046 + }, + { + -75824, + 114141, + -4895, + -1, + 18041 + }, + + { + -75824, + 114309, + -4895, + -1, + 18041 + }, + { + -75824, + 114477, + -4895, + -1, + 18041 + }, + { + -76513, + 114158, + -4895, + -1, + 18041 + }, + + { + -76683, + 114158, + -4895, + -1, + 18041 + }, + { + -76857, + 114158, + -4895, + -1, + 18041 + }, + { + -76535, + 113357, + -4895, + -1, + 18056 + }, + { + -76535, + 113190, + -4895, + -1, + 18056 + }, + }, + { + /* No Level Limit */ + { + -79350, + 109894, + -4895, + -1, + 18056 + }, + { + -79534, + 109894, + -4895, + -1, + 18056 + }, + { + -79285, + 109187, + -4895, + -1, + 18051 + }, + + { + -79285, + 109019, + -4895, + -1, + 18051 + }, + { + -79285, + 108860, + -4895, + -1, + 18051 + }, + { + -78587, + 109172, + -4895, + -1, + 18051 + }, + + { + -78415, + 109172, + -4895, + -1, + 18051 + }, + { + -78249, + 109172, + -4895, + -1, + 18051 + }, + { + -78575, + 109961, + -4895, + -1, + 18056 + }, + { + -78575, + 110130, + -4895, + -1, + 18056 + }, + } + }; + + protected static final int[][][] FESTIVAL_DUSK_SECONDARY_SPAWNS = + { + { + /* 31 and Below */ + { + -76844, + 89304, + -5151, + -1, + 18011 + }, + { + -76844, + 89479, + -5151, + -1, + 18011 + }, + { + -76844, + 89649, + -5151, + -1, + 18011 + }, + + { + -77544, + 89326, + -5151, + -1, + 18011 + }, + { + -77716, + 89326, + -5151, + -1, + 18011 + }, + { + -77881, + 89326, + -5151, + -1, + 18011 + }, + + { + -77561, + 88530, + -5151, + -1, + 18016 + }, + { + -77561, + 88364, + -5151, + -1, + 18016 + }, + { + -76762, + 88615, + -5151, + -1, + 18016 + }, + { + -76594, + 88615, + -5151, + -1, + 18016 + }, + }, + { + /* 42 and Below */ + { + -77307, + 84969, + -5151, + -1, + 18021 + }, + { + -77307, + 84795, + -5151, + -1, + 18021 + }, + { + -77307, + 84623, + -5151, + -1, + 18021 + }, + + { + -76614, + 84944, + -5151, + -1, + 18021 + }, + { + -76433, + 84944, + -5151, + -1, + 18021 + }, + { + -76251, + 84944, + -5151, + -1, + 18021 + }, + + { + -76594, + 85745, + -5151, + -1, + 18026 + }, + { + -76594, + 85910, + -5151, + -1, + 18026 + }, + { + -77384, + 85660, + -5151, + -1, + 18026 + }, + { + -77555, + 85660, + -5151, + -1, + 18026 + }, + }, + { + /* 53 and Below */ + { + -74517, + 86782, + -5151, + -1, + 18031 + }, + { + -74344, + 86782, + -5151, + -1, + 18031 + }, + { + -74185, + 86782, + -5151, + -1, + 18031 + }, + + { + -74496, + 87464, + -5151, + -1, + 18031 + }, + { + -74496, + 87636, + -5151, + -1, + 18031 + }, + { + -74496, + 87815, + -5151, + -1, + 18031 + }, + + { + -75298, + 87497, + -5151, + -1, + 18036 + }, + { + -75460, + 87497, + -5151, + -1, + 18036 + }, + { + -75219, + 86712, + -5151, + -1, + 18036 + }, + { + -75219, + 86531, + -5151, + -1, + 18036 + }, + }, + { + /* 64 and Below */ + { + -79851, + 88703, + -5151, + -1, + 18041 + }, + { + -79851, + 88868, + -5151, + -1, + 18041 + }, + { + -79851, + 89040, + -5151, + -1, + 18041 + }, + + { + -80548, + 88722, + -5151, + -1, + 18041 + }, + { + -80711, + 88722, + -5151, + -1, + 18041 + }, + { + -80883, + 88722, + -5151, + -1, + 18041 + }, + + { + -80565, + 87916, + -5151, + -1, + 18046 + }, + { + -80565, + 87752, + -5151, + -1, + 18046 + }, + { + -79779, + 87996, + -5151, + -1, + 18046 + }, + { + -79613, + 87996, + -5151, + -1, + 18046 + }, + }, + { + /* No Level Limit */ + { + -79271, + 84330, + -5151, + -1, + 18051 + }, + { + -79448, + 84330, + -5151, + -1, + 18051 + }, + { + -79601, + 84330, + -5151, + -1, + 18051 + }, + + { + -80311, + 84367, + -5151, + -1, + 18051 + }, + { + -80311, + 84196, + -5151, + -1, + 18051 + }, + { + -80311, + 84015, + -5151, + -1, + 18051 + }, + + { + -80556, + 85049, + -5151, + -1, + 18056 + }, + { + -80384, + 85049, + -5151, + -1, + 18056 + }, + { + -79598, + 85127, + -5151, + -1, + 18056 + }, + { + -79598, + 85303, + -5151, + -1, + 18056 + }, + } + }; + + protected static final int[][][] FESTIVAL_DAWN_CHEST_SPAWNS = + { + { + /* Level 31 and Below */ + { + -78999, + 112957, + -4927, + -1, + 18109 + }, + { + -79153, + 112873, + -4927, + -1, + 18109 + }, + { + -79256, + 112873, + -4927, + -1, + 18109 + }, + + { + -79368, + 112957, + -4927, + -1, + 18109 + }, + { + -79481, + 113124, + -4927, + -1, + 18109 + }, + { + -79481, + 113275, + -4927, + -1, + 18109 + }, + + { + -79364, + 113398, + -4927, + -1, + 18109 + }, + { + -79213, + 113500, + -4927, + -1, + 18109 + }, + { + -79099, + 113500, + -4927, + -1, + 18109 + }, + + { + -78960, + 113398, + -4927, + -1, + 18109 + }, + { + -78882, + 113235, + -4927, + -1, + 18109 + }, + { + -78882, + 113099, + -4927, + -1, + 18109 + }, + }, + { + /* Level 42 and Below */ + { + -76119, + 110383, + -4927, + -1, + 18110 + }, + { + -75980, + 110442, + -4927, + -1, + 18110 + }, + { + -75848, + 110442, + -4927, + -1, + 18110 + }, + + { + -75720, + 110383, + -4927, + -1, + 18110 + }, + { + -75625, + 110195, + -4927, + -1, + 18110 + }, + { + -75625, + 110063, + -4927, + -1, + 18110 + }, + + { + -75722, + 109908, + -4927, + -1, + 18110 + }, + { + -75863, + 109832, + -4927, + -1, + 18110 + }, + { + -75989, + 109832, + -4927, + -1, + 18110 + }, + + { + -76130, + 109908, + -4927, + -1, + 18110 + }, + { + -76230, + 110079, + -4927, + -1, + 18110 + }, + { + -76230, + 110215, + -4927, + -1, + 18110 + }, + }, + { + /* Level 53 and Below */ + { + -74055, + 111781, + -4927, + -1, + 18111 + }, + { + -74144, + 111938, + -4927, + -1, + 18111 + }, + { + -74144, + 112075, + -4927, + -1, + 18111 + }, + + { + -74055, + 112173, + -4927, + -1, + 18111 + }, + { + -73885, + 112289, + -4927, + -1, + 18111 + }, + { + -73756, + 112289, + -4927, + -1, + 18111 + }, + + { + -73574, + 112141, + -4927, + -1, + 18111 + }, + { + -73511, + 112040, + -4927, + -1, + 18111 + }, + { + -73511, + 111912, + -4927, + -1, + 18111 + }, + + { + -73574, + 111772, + -4927, + -1, + 18111 + }, + { + -73767, + 111669, + -4927, + -1, + 18111 + }, + { + -73899, + 111669, + -4927, + -1, + 18111 + }, + }, + { + /* Level 64 and Below */ + { + -76008, + 113566, + -4927, + -1, + 18112 + }, + { + -76159, + 113485, + -4927, + -1, + 18112 + }, + { + -76267, + 113485, + -4927, + -1, + 18112 + }, + + { + -76386, + 113566, + -4927, + -1, + 18112 + }, + { + -76482, + 113748, + -4927, + -1, + 18112 + }, + { + -76482, + 113885, + -4927, + -1, + 18112 + }, + + { + -76371, + 114029, + -4927, + -1, + 18112 + }, + { + -76220, + 114118, + -4927, + -1, + 18112 + }, + { + -76092, + 114118, + -4927, + -1, + 18112 + }, + + { + -75975, + 114029, + -4927, + -1, + 18112 + }, + { + -75861, + 113851, + -4927, + -1, + 18112 + }, + { + -75861, + 113713, + -4927, + -1, + 18112 + }, + }, + { + /* No Level Limit */ + { + -79100, + 109782, + -4927, + -1, + 18113 + }, + { + -78962, + 109853, + -4927, + -1, + 18113 + }, + { + -78851, + 109853, + -4927, + -1, + 18113 + }, + + { + -78721, + 109782, + -4927, + -1, + 18113 + }, + { + -78615, + 109596, + -4927, + -1, + 18113 + }, + { + -78615, + 109453, + -4927, + -1, + 18113 + }, + + { + -78746, + 109300, + -4927, + -1, + 18113 + }, + { + -78881, + 109203, + -4927, + -1, + 18113 + }, + { + -79027, + 109203, + -4927, + -1, + 18113 + }, + + { + -79159, + 109300, + -4927, + -1, + 18113 + }, + { + -79240, + 109480, + -4927, + -1, + 18113 + }, + { + -79240, + 109615, + -4927, + -1, + 18113 + }, + } + }; + + protected static final int[][][] FESTIVAL_DUSK_CHEST_SPAWNS = + { + { + /* Level 31 and Below */ + { + -77016, + 88726, + -5183, + -1, + 18114 + }, + { + -77136, + 88646, + -5183, + -1, + 18114 + }, + { + -77247, + 88646, + -5183, + -1, + 18114 + }, + + { + -77380, + 88726, + -5183, + -1, + 18114 + }, + { + -77512, + 88883, + -5183, + -1, + 18114 + }, + { + -77512, + 89053, + -5183, + -1, + 18114 + }, + + { + -77378, + 89287, + -5183, + -1, + 18114 + }, + { + -77254, + 89238, + -5183, + -1, + 18114 + }, + { + -77095, + 89238, + -5183, + -1, + 18114 + }, + + { + -76996, + 89287, + -5183, + -1, + 18114 + }, + { + -76901, + 89025, + -5183, + -1, + 18114 + }, + { + -76901, + 88891, + -5183, + -1, + 18114 + }, + }, + { + /* Level 42 and Below */ + { + -77128, + 85553, + -5183, + -1, + 18115 + }, + { + -77036, + 85594, + -5183, + -1, + 18115 + }, + { + -76919, + 85594, + -5183, + -1, + 18115 + }, + + { + -76755, + 85553, + -5183, + -1, + 18115 + }, + { + -76635, + 85392, + -5183, + -1, + 18115 + }, + { + -76635, + 85216, + -5183, + -1, + 18115 + }, + + { + -76761, + 85025, + -5183, + -1, + 18115 + }, + { + -76908, + 85004, + -5183, + -1, + 18115 + }, + { + -77041, + 85004, + -5183, + -1, + 18115 + }, + + { + -77138, + 85025, + -5183, + -1, + 18115 + }, + { + -77268, + 85219, + -5183, + -1, + 18115 + }, + { + -77268, + 85410, + -5183, + -1, + 18115 + }, + }, + { + /* Level 53 and Below */ + { + -75150, + 87303, + -5183, + -1, + 18116 + }, + { + -75150, + 87175, + -5183, + -1, + 18116 + }, + { + -75150, + 87175, + -5183, + -1, + 18116 + }, + + { + -75150, + 87303, + -5183, + -1, + 18116 + }, + { + -74943, + 87433, + -5183, + -1, + 18116 + }, + { + -74767, + 87433, + -5183, + -1, + 18116 + }, + + { + -74556, + 87306, + -5183, + -1, + 18116 + }, + { + -74556, + 87184, + -5183, + -1, + 18116 + }, + { + -74556, + 87184, + -5183, + -1, + 18116 + }, + + { + -74556, + 87306, + -5183, + -1, + 18116 + }, + { + -74757, + 86830, + -5183, + -1, + 18116 + }, + { + -74927, + 86830, + -5183, + -1, + 18116 + }, + }, + { + /* Level 64 and Below */ + { + -80010, + 88128, + -5183, + -1, + 18117 + }, + { + -80113, + 88066, + -5183, + -1, + 18117 + }, + { + -80220, + 88066, + -5183, + -1, + 18117 + }, + + { + -80359, + 88128, + -5183, + -1, + 18117 + }, + { + -80467, + 88267, + -5183, + -1, + 18117 + }, + { + -80467, + 88436, + -5183, + -1, + 18117 + }, + + { + -80381, + 88639, + -5183, + -1, + 18117 + }, + { + -80278, + 88577, + -5183, + -1, + 18117 + }, + { + -80142, + 88577, + -5183, + -1, + 18117 + }, + + { + -80028, + 88639, + -5183, + -1, + 18117 + }, + { + -79915, + 88466, + -5183, + -1, + 18117 + }, + { + -79915, + 88322, + -5183, + -1, + 18117 + }, + }, + { + /* No Level Limit */ + { + -80153, + 84947, + -5183, + -1, + 18118 + }, + { + -80003, + 84962, + -5183, + -1, + 18118 + }, + { + -79848, + 84962, + -5183, + -1, + 18118 + }, + + { + -79742, + 84947, + -5183, + -1, + 18118 + }, + { + -79668, + 84772, + -5183, + -1, + 18118 + }, + { + -79668, + 84619, + -5183, + -1, + 18118 + }, + + { + -79772, + 84471, + -5183, + -1, + 18118 + }, + { + -79888, + 84414, + -5183, + -1, + 18118 + }, + { + -80023, + 84414, + -5183, + -1, + 18118 + }, + + { + -80166, + 84471, + -5183, + -1, + 18118 + }, + { + -80253, + 84600, + -5183, + -1, + 18118 + }, + { + -80253, + 84780, + -5183, + -1, + 18118 + }, + } + }; + + // ////////////////////// \\\\\\\\\\\\\\\\\\\\\\\\\\ + + protected FestivalManager _managerInstance; + protected ScheduledFuture _managerScheduledTask; + + protected int _signsCycle = SevenSigns.getInstance().getCurrentCycle(); + protected int _festivalCycle; + protected long _nextFestivalCycleStart; + protected long _nextFestivalStart; + protected boolean _festivalInitialized; + protected boolean _festivalInProgress; + protected List _accumulatedBonuses; // The total bonus available (in Ancient Adena) + + boolean _noPartyRegister; + private L2Npc _dawnChatGuide; + private L2Npc _duskChatGuide; + + protected Map> _dawnFestivalParticipants; + protected Map> _duskFestivalParticipants; + + protected Map> _dawnPreviousParticipants; + protected Map> _duskPreviousParticipants; + + private Map _dawnFestivalScores; + private Map _duskFestivalScores; + + /** + * _festivalData is essentially an instance of the seven_signs_festival table and should be treated as such. Data is initially accessed by the related Seven Signs cycle, with _signsCycle representing data for the current round of Festivals. The actual table data is stored as a series of StatsSet + * constructs. These are accessed by the use of an offset based on the number of festivals, thus: offset = FESTIVAL_COUNT + festivalId (Data for Dawn is always accessed by offset > FESTIVAL_COUNT) + */ + private Map> _festivalData; + + private SevenSignsFestival() + { + _accumulatedBonuses = new FastList<>(); + + _dawnFestivalParticipants = new FastMap<>(); + _dawnPreviousParticipants = new FastMap<>(); + _dawnFestivalScores = new FastMap<>(); + + _duskFestivalParticipants = new FastMap<>(); + _duskPreviousParticipants = new FastMap<>(); + _duskFestivalScores = new FastMap<>(); + + _festivalData = new FastMap<>(); + + restoreFestivalData(); + + if (SevenSigns.getInstance().isSealValidationPeriod()) + { + _log.info("SevenSignsFestival: Initialization bypassed due to Seal Validation in effect."); + return; + } + + L2Spawn.addSpawnListener(this); + startFestivalManager(); + } + + public static SevenSignsFestival getInstance() + { + return SingletonHolder._instance; + } + + /** + * Returns the associated name (level range) to a given festival ID. + * @param int festivalID + * @return String festivalName + */ + public static final String getFestivalName(int festivalID) + { + String festivalName; + + switch (festivalID) + { + case FESTIVAL_LEVEL_MAX_31: + festivalName = "Level 31 or lower"; + break; + case FESTIVAL_LEVEL_MAX_42: + festivalName = "Level 42 or lower"; + break; + case FESTIVAL_LEVEL_MAX_53: + festivalName = "Level 53 or lower"; + break; + case FESTIVAL_LEVEL_MAX_64: + festivalName = "Level 64 or lower"; + break; + default: + festivalName = "No Level Limit"; + break; + } + + return festivalName; + } + + /** + * Returns the maximum allowed player level for the given festival type. + * @param festivalId + * @return int maxLevel + */ + public static final int getMaxLevelForFestival(int festivalId) + { + int maxLevel = (Experience.MAX_LEVEL - 1); + + switch (festivalId) + { + case SevenSignsFestival.FESTIVAL_LEVEL_MAX_31: + maxLevel = 31; + break; + case SevenSignsFestival.FESTIVAL_LEVEL_MAX_42: + maxLevel = 42; + break; + case SevenSignsFestival.FESTIVAL_LEVEL_MAX_53: + maxLevel = 53; + break; + case SevenSignsFestival.FESTIVAL_LEVEL_MAX_64: + maxLevel = 64; + break; + } + + return maxLevel; + } + + /** + * Returns true if the monster ID given is of an archer/marksman type. + * @param npcId + * @return boolean isArcher + */ + protected static final boolean isFestivalArcher(int npcId) + { + if ((npcId < 18009) || (npcId > 18108)) + { + return false; + } + + int identifier = npcId % 10; + return ((identifier == 4) || (identifier == 9)); + } + + /** + * Returns true if the monster ID given is a festival chest. + * @param npcId + * @return boolean isChest + */ + protected static final boolean isFestivalChest(int npcId) + { + return ((npcId < 18109) || (npcId > 18118)); + } + + /** + * Primarily used to terminate the Festival Manager, when the Seven Signs period changes. + * @return ScheduledFuture festManagerScheduler + */ + protected final ScheduledFuture getFestivalManagerSchedule() + { + if (_managerScheduledTask == null) + { + startFestivalManager(); + } + + return _managerScheduledTask; + } + + /** + * Used to start the Festival Manager, if the current period is not Seal Validation. + */ + protected void startFestivalManager() + { + // Start the Festival Manager for the first time after the server has started + // at the specified time, then invoke it automatically after every cycle. + FestivalManager fm = new FestivalManager(); + setNextFestivalStart(Config.ALT_FESTIVAL_MANAGER_START + FESTIVAL_SIGNUP_TIME); + _managerScheduledTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(fm, Config.ALT_FESTIVAL_MANAGER_START, Config.ALT_FESTIVAL_CYCLE_LENGTH); + + _log.info("SevenSignsFestival: The first Festival of Darkness cycle begins in " + (Config.ALT_FESTIVAL_MANAGER_START / 60000) + " minute(s)."); + } + + /** + * Restores saved festival data, basic settings from the properties file and past high score data from the database. + * @throws Exception + */ + protected void restoreFestivalData() + { + Connection con = null; + PreparedStatement statement = null; + ResultSet rset = null; + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Restoring festival data. Current SS Cycle: " + _signsCycle); + } + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("SELECT festivalId, cabal, cycle, date, score, members " + "FROM seven_signs_festival"); + rset = statement.executeQuery(); + + while (rset.next()) + { + int festivalCycle = rset.getInt("cycle"); + int festivalId = rset.getInt("festivalId"); + String cabal = rset.getString("cabal"); + + StatsSet festivalDat = new StatsSet(); + festivalDat.set("festivalId", festivalId); + festivalDat.set("cabal", cabal); + festivalDat.set("cycle", festivalCycle); + festivalDat.set("date", rset.getString("date")); + festivalDat.set("score", rset.getInt("score")); + festivalDat.set("members", rset.getString("members")); + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Loaded data from DB for (Cycle = " + festivalCycle + ", Oracle = " + cabal + ", Festival = " + getFestivalName(festivalId)); + } + + if (cabal.equals("dawn")) + { + festivalId += FESTIVAL_COUNT; + } + + Map tempData = _festivalData.get(festivalCycle); + + if (tempData == null) + { + tempData = new FastMap<>(); + } + + tempData.put(festivalId, festivalDat); + _festivalData.put(festivalCycle, tempData); + } + + rset.close(); + statement.close(); + + String query = "SELECT festival_cycle, "; + + for (int i = 0; i < (FESTIVAL_COUNT - 1); i++) + { + query += "accumulated_bonus" + String.valueOf(i) + ", "; + } + query += "accumulated_bonus" + String.valueOf(FESTIVAL_COUNT - 1) + " "; + query += "FROM seven_signs_status WHERE id=0"; + + statement = con.prepareStatement(query); + rset = statement.executeQuery(); + + while (rset.next()) + { + _festivalCycle = rset.getInt("festival_cycle"); + + for (int i = 0; i < FESTIVAL_COUNT; i++) + { + _accumulatedBonuses.add(i, rset.getInt("accumulated_bonus" + String.valueOf(i))); + } + } + + rset.close(); + statement.close(); + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Loaded data from database."); + } + } + catch (SQLException e) + { + _log.log(Level.SEVERE, "SevenSignsFestival: Failed to load configuration: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Stores current festival data, basic settings to the properties file and past high score data to the database. If updateSettings = true, then all Seven Signs data is updated in the database. + * @param updateSettings + * @throws Exception + */ + public void saveFestivalData(boolean updateSettings) + { + Connection con = null; + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Saving festival data to disk."); + } + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statementUpdate = con.prepareStatement("UPDATE seven_signs_festival SET date=?, score=?, members=? WHERE cycle=? AND cabal=? AND festivalId=?"); + PreparedStatement statementInsert = con.prepareStatement("INSERT INTO seven_signs_festival (festivalId, cabal, cycle, date, score, members) VALUES (?,?,?,?,?,?)"); + for (Map currCycleData : _festivalData.values()) + { + for (StatsSet festivalDat : currCycleData.values()) + { + int festivalCycle = festivalDat.getInteger("cycle"); + int festivalId = festivalDat.getInteger("festivalId"); + String cabal = festivalDat.getString("cabal"); + + // Try to update an existing record. + statementUpdate.setLong(1, Long.valueOf(festivalDat.getString("date"))); + statementUpdate.setInt(2, festivalDat.getInteger("score")); + statementUpdate.setString(3, festivalDat.getString("members")); + statementUpdate.setInt(4, festivalCycle); + statementUpdate.setString(5, cabal); + statementUpdate.setInt(6, festivalId); + + // If there was no record to update, assume it doesn't exist and add a new one, + // otherwise continue with the next record to store. + if (statementUpdate.executeUpdate() > 0) + { + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Updated data in DB (Cycle = " + festivalCycle + ", Cabal = " + cabal + ", FestID = " + festivalId + ")"); + } + continue; + } + + statementInsert.setInt(1, festivalId); + statementInsert.setString(2, cabal); + statementInsert.setInt(3, festivalCycle); + statementInsert.setLong(4, Long.valueOf(festivalDat.getString("date"))); + statementInsert.setInt(5, festivalDat.getInteger("score")); + statementInsert.setString(6, festivalDat.getString("members")); + statementInsert.execute(); + statementInsert.clearParameters(); + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Inserted data in DB (Cycle = " + festivalCycle + ", Cabal = " + cabal + ", FestID = " + festivalId + ")"); + } + } + } + statementUpdate.close(); + statementInsert.close(); + + // Updates Seven Signs DB data also, so call only if really necessary. + if (updateSettings) + { + SevenSigns.getInstance().saveSevenSignsStatus(); + } + } + catch (SQLException e) + { + _log.log(Level.SEVERE, "SevenSignsFestival: Failed to save configuration: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * If a clan member is a member of the highest-ranked party in the Festival of Darkness, 100 points are added per member + */ + protected void rewardHighestRanked() + { + String[] partyMembers; + StatsSet overallData = getOverallHighestScoreData(FESTIVAL_LEVEL_MAX_31); + if (overallData != null) + { + partyMembers = overallData.getString("members").split(","); + for (String partyMemberName : partyMembers) + { + addReputationPointsForPartyMemberClan(partyMemberName); + } + } + + overallData = getOverallHighestScoreData(FESTIVAL_LEVEL_MAX_42); + if (overallData != null) + { + partyMembers = overallData.getString("members").split(","); + for (String partyMemberName : partyMembers) + { + addReputationPointsForPartyMemberClan(partyMemberName); + } + } + + overallData = getOverallHighestScoreData(FESTIVAL_LEVEL_MAX_53); + if (overallData != null) + { + partyMembers = overallData.getString("members").split(","); + for (String partyMemberName : partyMembers) + { + addReputationPointsForPartyMemberClan(partyMemberName); + } + } + + overallData = getOverallHighestScoreData(FESTIVAL_LEVEL_MAX_64); + if (overallData != null) + { + partyMembers = overallData.getString("members").split(","); + for (String partyMemberName : partyMembers) + { + addReputationPointsForPartyMemberClan(partyMemberName); + } + } + + overallData = getOverallHighestScoreData(FESTIVAL_LEVEL_MAX_NONE); + if (overallData != null) + { + partyMembers = overallData.getString("members").split(","); + for (String partyMemberName : partyMembers) + { + addReputationPointsForPartyMemberClan(partyMemberName); + } + } + } + + private void addReputationPointsForPartyMemberClan(String partyMemberName) + { + L2PcInstance player = L2World.getInstance().getPlayer(partyMemberName); + if (player != null) + { + if (player.getClan() != null) + { + player.getClan().addReputationScore(Config.FESTIVAL_WIN_POINTS, true); + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.CLAN_MEMBER_C1_WAS_IN_HIGHEST_RANKED_PARTY_IN_FESTIVAL_OF_DARKNESS_AND_GAINED_S2_REPUTATION); + sm.addString(partyMemberName); + sm.addNumber(Config.FESTIVAL_WIN_POINTS); + player.getClan().broadcastToOnlineMembers(sm); + } + } + else + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(GET_CLAN_NAME); + statement.setString(1, partyMemberName); + ResultSet rset = statement.executeQuery(); + if (rset.next()) + { + String clanName = rset.getString("clan_name"); + if (clanName != null) + { + L2Clan clan = ClanTable.getInstance().getClanByName(clanName); + if (clan != null) + { + clan.addReputationScore(Config.FESTIVAL_WIN_POINTS, true); + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.CLAN_MEMBER_C1_WAS_IN_HIGHEST_RANKED_PARTY_IN_FESTIVAL_OF_DARKNESS_AND_GAINED_S2_REPUTATION); + sm.addString(partyMemberName); + sm.addNumber(Config.FESTIVAL_WIN_POINTS); + clan.broadcastToOnlineMembers(sm); + } + } + } + + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not get clan name of " + partyMemberName + ": " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + } + + /** + * Used to reset all festival data at the beginning of a new quest event period. + */ + protected void resetFestivalData(boolean updateSettings) + { + _festivalCycle = 0; + _signsCycle = SevenSigns.getInstance().getCurrentCycle(); + + // Set all accumulated bonuses back to 0. + for (int i = 0; i < FESTIVAL_COUNT; i++) + { + _accumulatedBonuses.set(i, 0); + } + + _dawnFestivalParticipants.clear(); + _dawnPreviousParticipants.clear(); + _dawnFestivalScores.clear(); + + _duskFestivalParticipants.clear(); + _duskPreviousParticipants.clear(); + _duskFestivalScores.clear(); + + // Set up a new data set for the current cycle of festivals + Map newData = new FastMap<>(); + + for (int i = 0; i < (FESTIVAL_COUNT * 2); i++) + { + int festivalId = i; + + if (i >= FESTIVAL_COUNT) + { + festivalId -= FESTIVAL_COUNT; + } + + // Create a new StatsSet with "default" data for Dusk + StatsSet tempStats = new StatsSet(); + tempStats.set("festivalId", festivalId); + tempStats.set("cycle", _signsCycle); + tempStats.set("date", "0"); + tempStats.set("score", 0); + tempStats.set("members", ""); + + if (i >= FESTIVAL_COUNT) + { + tempStats.set("cabal", SevenSigns.getCabalShortName(SevenSigns.CABAL_DAWN)); + } + else + { + tempStats.set("cabal", SevenSigns.getCabalShortName(SevenSigns.CABAL_DUSK)); + } + + newData.put(i, tempStats); + } + + // Add the newly created cycle data to the existing festival data, and + // subsequently save it to the database. + _festivalData.put(_signsCycle, newData); + + saveFestivalData(updateSettings); + + // Remove any unused blood offerings from online players. + Collection pls = L2World.getInstance().getAllPlayers().values(); + // synchronized (L2World.getInstance().getAllPlayers()) + { + for (L2PcInstance onlinePlayer : pls) + { + try + { + L2ItemInstance bloodOfferings = onlinePlayer.getInventory().getItemByItemId(FESTIVAL_OFFERING_ID); + + if (bloodOfferings != null) + { + onlinePlayer.destroyItem("SevenSigns", bloodOfferings, null, false); + } + } + catch (NullPointerException e) + { + } + } + } + + _log.info("SevenSignsFestival: Reinitialized engine for next competition period."); + } + + public final int getCurrentFestivalCycle() + { + return _festivalCycle; + } + + public final boolean isFestivalInitialized() + { + return _festivalInitialized; + } + + public final boolean isFestivalInProgress() + { + return _festivalInProgress; + } + + public void setNextCycleStart() + { + _nextFestivalCycleStart = System.currentTimeMillis() + Config.ALT_FESTIVAL_CYCLE_LENGTH; + } + + public void setNextFestivalStart(long milliFromNow) + { + _nextFestivalStart = System.currentTimeMillis() + milliFromNow; + } + + public final int getMinsToNextCycle() + { + if (SevenSigns.getInstance().isSealValidationPeriod()) + { + return -1; + } + + return Math.round((_nextFestivalCycleStart - System.currentTimeMillis()) / 60000); + } + + public final int getMinsToNextFestival() + { + if (SevenSigns.getInstance().isSealValidationPeriod()) + { + return -1; + } + + return Math.round((_nextFestivalStart - System.currentTimeMillis()) / 60000) + 1; + } + + public final String getTimeToNextFestivalStr() + { + if (SevenSigns.getInstance().isSealValidationPeriod()) + { + return "This is the Seal Validation period. Festivals will resume next week."; + } + + return "The next festival will begin in " + getMinsToNextFestival() + " minute(s)."; + } + + /** + * Returns the current festival ID and oracle ID that the specified player is in, but will return the default of {-1, -1} if the player is not found as a participant. + * @param player + * @return int[] playerFestivalInfo + */ + public final int[] getFestivalForPlayer(L2PcInstance player) + { + int[] playerFestivalInfo = + { + -1, + -1 + }; + int festivalId = 0; + + while (festivalId < FESTIVAL_COUNT) + { + List participants = _dawnFestivalParticipants.get(festivalId); + + // If there are no participants in this festival, move on to the next. + if ((participants != null) && participants.contains(player.getObjectId())) + { + playerFestivalInfo[0] = SevenSigns.CABAL_DAWN; + playerFestivalInfo[1] = festivalId; + + return playerFestivalInfo; + } + + participants = _duskFestivalParticipants.get(++festivalId); + + if ((participants != null) && participants.contains(player.getObjectId())) + { + playerFestivalInfo[0] = SevenSigns.CABAL_DUSK; + playerFestivalInfo[1] = festivalId; + + return playerFestivalInfo; + } + + festivalId++; + } + + // Return default data if the player is not found as a participant. + return playerFestivalInfo; + } + + public final boolean isParticipant(L2PcInstance player) + { + if (SevenSigns.getInstance().isSealValidationPeriod()) + { + return false; + } + + if (_managerInstance == null) + { + return false; + } + + for (List participants : _dawnFestivalParticipants.values()) + { + if ((participants != null) && participants.contains(player.getObjectId())) + { + return true; + } + } + + for (List participants : _duskFestivalParticipants.values()) + { + if ((participants != null) && participants.contains(player.getObjectId())) + { + return true; + } + } + + return false; + } + + public final List getParticipants(int oracle, int festivalId) + { + if (oracle == SevenSigns.CABAL_DAWN) + { + return _dawnFestivalParticipants.get(festivalId); + } + + return _duskFestivalParticipants.get(festivalId); + } + + public final List getPreviousParticipants(int oracle, int festivalId) + { + if (oracle == SevenSigns.CABAL_DAWN) + { + return _dawnPreviousParticipants.get(festivalId); + } + + return _duskPreviousParticipants.get(festivalId); + } + + public void setParticipants(int oracle, int festivalId, L2Party festivalParty) + { + List participants = null; + + if (festivalParty != null) + { + participants = new ArrayList<>(festivalParty.getMemberCount()); + for (L2PcInstance player : festivalParty.getPartyMembers()) + { + if (player == null) + { + continue; + } + participants.add(player.getObjectId()); + } + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: " + festivalParty.getPartyMembers().toString() + " have signed up to the " + SevenSigns.getCabalShortName(oracle) + " " + getFestivalName(festivalId) + " festival."); + } + } + + if (oracle == SevenSigns.CABAL_DAWN) + { + _dawnFestivalParticipants.put(festivalId, participants); + } + else + { + _duskFestivalParticipants.put(festivalId, participants); + } + } + + public void updateParticipants(L2PcInstance player, L2Party festivalParty) + { + if (!isParticipant(player)) + { + return; + } + + final int[] playerFestInfo = getFestivalForPlayer(player); + final int oracle = playerFestInfo[0]; + final int festivalId = playerFestInfo[1]; + + if (festivalId > -1) + { + if (_festivalInitialized) + { + L2DarknessFestival festivalInst = _managerInstance.getFestivalInstance(oracle, festivalId); + + if (festivalParty == null) + { + for (int partyMemberObjId : getParticipants(oracle, festivalId)) + { + L2PcInstance partyMember = L2World.getInstance().getPlayer(partyMemberObjId); + if (partyMember == null) + { + continue; + } + + festivalInst.relocatePlayer(partyMember, true); + } + } + else + { + festivalInst.relocatePlayer(player, true); + } + } + + setParticipants(oracle, festivalId, festivalParty); + + // Check on disconnect if min player in party + if ((festivalParty != null) && (festivalParty.getMemberCount() < Config.ALT_FESTIVAL_MIN_PLAYER)) + { + updateParticipants(player, null); // under minimum count + festivalParty.removePartyMember(player); + } + } + } + + public final long getFinalScore(int oracle, int festivalId) + { + if (oracle == SevenSigns.CABAL_DAWN) + { + return _dawnFestivalScores.get(festivalId); + } + + return _duskFestivalScores.get(festivalId); + } + + public final int getHighestScore(int oracle, int festivalId) + { + return getHighestScoreData(oracle, festivalId).getInteger("score"); + } + + /** + * Returns a stats set containing the highest score this cycle for the the specified cabal and associated festival ID. + * @param oracle + * @param festivalId + * @return StatsSet festivalDat + */ + public final StatsSet getHighestScoreData(int oracle, int festivalId) + { + int offsetId = festivalId; + + if (oracle == SevenSigns.CABAL_DAWN) + { + offsetId += 5; + } + + // Attempt to retrieve existing score data (if found), otherwise create a + // new blank data set and display a console warning. + StatsSet currData = null; + + try + { + currData = _festivalData.get(_signsCycle).get(offsetId); + } + catch (Exception e) + { + currData = new StatsSet(); + currData.set("score", 0); + currData.set("members", ""); + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Data missing for " + SevenSigns.getCabalName(oracle) + ", FestivalID = " + festivalId + " (Current Cycle " + _signsCycle + ")"); + } + } + + return currData; + } + + /** + * Returns a stats set containing the highest ever recorded score data for the specified festival. + * @param festivalId + * @return StatsSet result + */ + public final StatsSet getOverallHighestScoreData(int festivalId) + { + StatsSet result = null; + int highestScore = 0; + + for (Map currCycleData : _festivalData.values()) + { + for (StatsSet currFestData : currCycleData.values()) + { + int currFestID = currFestData.getInteger("festivalId"); + int festivalScore = currFestData.getInteger("score"); + + if (currFestID != festivalId) + { + continue; + } + + if (festivalScore > highestScore) + { + highestScore = festivalScore; + result = currFestData; + } + } + } + + return result; + } + + /** + * Set the final score details for the last participants of the specified festival data. Returns true if the score is higher than that previously recorded this cycle. + * @param player + * @param oracle + * @param festivalId + * @param offeringScore + * @return boolean isHighestScore + */ + public boolean setFinalScore(L2PcInstance player, int oracle, int festivalId, long offeringScore) + { + List partyMembers; + + int currDawnHighScore = getHighestScore(SevenSigns.CABAL_DAWN, festivalId); + int currDuskHighScore = getHighestScore(SevenSigns.CABAL_DUSK, festivalId); + + int thisCabalHighScore = 0; + int otherCabalHighScore = 0; + + if (oracle == SevenSigns.CABAL_DAWN) + { + thisCabalHighScore = currDawnHighScore; + otherCabalHighScore = currDuskHighScore; + + _dawnFestivalScores.put(festivalId, offeringScore); + } + else + { + thisCabalHighScore = currDuskHighScore; + otherCabalHighScore = currDawnHighScore; + + _duskFestivalScores.put(festivalId, offeringScore); + } + + StatsSet currFestData = getHighestScoreData(oracle, festivalId); + + // Check if this is the highest score for this level range so far for the player's cabal. + if (offeringScore > thisCabalHighScore) + { + // If the current score is greater than that for the other cabal, + // then they already have the points from this festival. + if (thisCabalHighScore < otherCabalHighScore) + { + return false; + } + + List prevParticipants = getPreviousParticipants(oracle, festivalId); + partyMembers = new ArrayList<>(prevParticipants.size()); + + // Record a string list of the party members involved. + for (Integer partyMember : prevParticipants) + { + partyMembers.add(CharNameTable.getInstance().getNameById(partyMember)); + } + + // Update the highest scores and party list. + currFestData.set("date", String.valueOf(System.currentTimeMillis())); + currFestData.set("score", offeringScore); + currFestData.set("members", Util.implodeString(partyMembers, ",")); + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: " + player.getName() + "'s party has the highest score (" + offeringScore + ") so far for " + SevenSigns.getCabalName(oracle) + " in " + getFestivalName(festivalId)); + } + + // Only add the score to the cabal's overall if it's higher than the other cabal's score. + if (offeringScore > otherCabalHighScore) + { + int contribPoints = FESTIVAL_LEVEL_SCORES[festivalId]; + + // Give this cabal the festival points, while deducting them from the other. + SevenSigns.getInstance().addFestivalScore(oracle, contribPoints); + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: This is the highest score overall so far for the " + getFestivalName(festivalId) + " festival!"); + } + } + + saveFestivalData(true); + + return true; + } + + return false; + } + + public final int getAccumulatedBonus(int festivalId) + { + return _accumulatedBonuses.get(festivalId); + } + + public final int getTotalAccumulatedBonus() + { + int totalAccumBonus = 0; + + for (int accumBonus : _accumulatedBonuses) + { + totalAccumBonus += accumBonus; + } + + return totalAccumBonus; + } + + public void addAccumulatedBonus(int festivalId, int stoneType, int stoneAmount) + { + int eachStoneBonus = 0; + + switch (stoneType) + { + case SevenSigns.SEAL_STONE_BLUE_ID: + eachStoneBonus = SevenSigns.SEAL_STONE_BLUE_VALUE; + break; + case SevenSigns.SEAL_STONE_GREEN_ID: + eachStoneBonus = SevenSigns.SEAL_STONE_GREEN_VALUE; + break; + case SevenSigns.SEAL_STONE_RED_ID: + eachStoneBonus = SevenSigns.SEAL_STONE_RED_VALUE; + break; + } + + int newTotalBonus = _accumulatedBonuses.get(festivalId) + (stoneAmount * eachStoneBonus); + _accumulatedBonuses.set(festivalId, newTotalBonus); + } + + /** + * Calculate and return the proportion of the accumulated bonus for the festival where the player was in the winning party, if the winning party's cabal won the event. The accumulated bonus is then updated, with the player's share deducted. + * @param player + * @return playerBonus (the share of the bonus for the party) + */ + public final int distribAccumulatedBonus(L2PcInstance player) + { + int playerBonus = 0; + String playerName = player.getName(); + int playerCabal = SevenSigns.getInstance().getPlayerCabal(player.getObjectId()); + + if (playerCabal != SevenSigns.getInstance().getCabalHighestScore()) + { + return 0; + } + + if (_festivalData.get(_signsCycle) != null) + { + for (StatsSet festivalData : _festivalData.get(_signsCycle).values()) + { + if (festivalData.getString("members").indexOf(playerName) > -1) + { + int festivalId = festivalData.getInteger("festivalId"); + int numPartyMembers = festivalData.getString("members").split(",").length; + int totalAccumBonus = _accumulatedBonuses.get(festivalId); + + playerBonus = totalAccumBonus / numPartyMembers; + _accumulatedBonuses.set(festivalId, totalAccumBonus - playerBonus); + break; + } + } + } + + return playerBonus; + } + + /** + * Used to send a "shout" message to all players currently present in an Oracle. Primarily used for Festival Guide and Witch related speech. + * @param senderName + * @param message + */ + public void sendMessageToAll(String senderName, String message) + { + if ((_dawnChatGuide == null) || (_duskChatGuide == null)) + { + return; + } + + CreatureSay cs = new CreatureSay(_dawnChatGuide.getObjectId(), Say2.SHOUT, senderName, message); + _dawnChatGuide.broadcastPacket(cs); + + cs = new CreatureSay(_duskChatGuide.getObjectId(), Say2.SHOUT, senderName, message); + _duskChatGuide.broadcastPacket(cs); + } + + /** + * Basically a wrapper-call to signal to increase the challenge of the specified festival. + * @param oracle + * @param festivalId + * @return boolean isChalIncreased + */ + public final boolean increaseChallenge(int oracle, int festivalId) + { + L2DarknessFestival festivalInst = _managerInstance.getFestivalInstance(oracle, festivalId); + + return festivalInst.increaseChallenge(); + } + + /** + * Used with the SpawnListener, to update the required "chat guide" instances, for use with announcements in the oracles. + * @param npc + */ + @Override + public void npcSpawned(L2Npc npc) + { + if (npc == null) + { + return; + } + + int npcId = npc.getNpcId(); + + // If the spawned NPC ID matches the ones we need, assign their instances. + if (npcId == 31127) + { + if (Config.DEBUG) + { + _log.warning("SevenSignsFestival: Instance found for NPC ID 31127 (" + npc.getObjectId() + ")."); + } + + _dawnChatGuide = npc; + } + + if (npcId == 31137) + { + if (Config.DEBUG) + { + _log.warning("SevenSignsFestival: Instance found for NPC ID 31137 (" + npc.getObjectId() + ")."); + } + + _duskChatGuide = npc; + } + } + + /** + * The FestivalManager class is the main runner of all the festivals. It is used for easier integration and management of all running festivals. + * @author Tempy + */ + private class FestivalManager implements Runnable + { + protected Map _festivalInstances; + + public FestivalManager() + { + _festivalInstances = new FastMap<>(); + _managerInstance = this; + + // Increment the cycle counter. + _festivalCycle++; + + // Set the next start timers. + setNextCycleStart(); + setNextFestivalStart(Config.ALT_FESTIVAL_CYCLE_LENGTH - FESTIVAL_SIGNUP_TIME); + } + + @Override + public synchronized void run() + { + try + { + // The manager shouldn't be running if Seal Validation is in effect. + if (SevenSigns.getInstance().isSealValidationPeriod()) + { + return; + } + + // If the next period is due to start before the end of this + // festival cycle, then don't run it. + if (SevenSigns.getInstance().getMilliToPeriodChange() < Config.ALT_FESTIVAL_CYCLE_LENGTH) + { + return; + } + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Festival manager initialized. Those wishing to participate have " + getMinsToNextFestival() + " minute(s) to sign up."); + } + + sendMessageToAll("Festival Guide", "The main event will start in " + getMinsToNextFestival() + " minutes. Please register now."); + + // Stand by until the allowed signup period has elapsed. + try + { + wait(FESTIVAL_SIGNUP_TIME); + } + catch (InterruptedException e) + { + } + + // Clear past participants, they can no longer register their score if not done so already. + _dawnPreviousParticipants.clear(); + _duskPreviousParticipants.clear(); + + // Get rid of random monsters that avoided deletion after last festival + for (L2DarknessFestival festivalInst : _festivalInstances.values()) + { + festivalInst.unspawnMobs(); + } + + // Start only if participants signed up + _noPartyRegister = true; + + while (_noPartyRegister) + { + if ((_duskFestivalParticipants.isEmpty() && _dawnFestivalParticipants.isEmpty())) + { + try + { + setNextCycleStart(); + setNextFestivalStart(Config.ALT_FESTIVAL_CYCLE_LENGTH - FESTIVAL_SIGNUP_TIME); + wait(Config.ALT_FESTIVAL_CYCLE_LENGTH - FESTIVAL_SIGNUP_TIME); + for (L2DarknessFestival festivalInst : _festivalInstances.values()) + { + if (!festivalInst._npcInsts.isEmpty()) + { + festivalInst.unspawnMobs(); + } + } + } + catch (InterruptedException e) + { + } + } + else + { + _noPartyRegister = false; + } + } + + /* INITIATION */ + // Set the festival timer to 0, as it is just beginning. + long elapsedTime = 0; + + // Create the instances for the festivals in both Oracles, + // but only if they have participants signed up for them. + for (int i = 0; i < FESTIVAL_COUNT; i++) + { + if (_duskFestivalParticipants.get(i) != null) + { + _festivalInstances.put(10 + i, new L2DarknessFestival(SevenSigns.CABAL_DUSK, i)); + } + + if (_dawnFestivalParticipants.get(i) != null) + { + _festivalInstances.put(20 + i, new L2DarknessFestival(SevenSigns.CABAL_DAWN, i)); + } + } + + // Prevent future signups while festival is in progress. + _festivalInitialized = true; + + setNextFestivalStart(Config.ALT_FESTIVAL_CYCLE_LENGTH); + sendMessageToAll("Festival Guide", "The main event is now starting."); + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: The current set of festivals will begin in " + (Config.ALT_FESTIVAL_FIRST_SPAWN / 60000) + " minute(s)."); + } + + // Stand by for a short length of time before starting the festival. + try + { + wait(Config.ALT_FESTIVAL_FIRST_SPAWN); + } + catch (InterruptedException e) + { + } + + elapsedTime = Config.ALT_FESTIVAL_FIRST_SPAWN; + + // Participants can now opt to increase the challenge, if desired. + _festivalInProgress = true; + + /* PROPOGATION */ + // Sequentially set all festivals to begin, spawn the Festival Witch and notify participants. + for (L2DarknessFestival festivalInst : _festivalInstances.values()) + { + festivalInst.festivalStart(); + festivalInst.sendMessageToParticipants("The festival is about to begin!"); + } + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Each of the festivals will end in " + (Config.ALT_FESTIVAL_LENGTH / 60000) + " minutes. New participants can signup then."); + } + + // After a short time period, move all idle spawns to the center of the arena. + try + { + wait(Config.ALT_FESTIVAL_FIRST_SWARM - Config.ALT_FESTIVAL_FIRST_SPAWN); + } + catch (InterruptedException e) + { + } + + elapsedTime += Config.ALT_FESTIVAL_FIRST_SWARM - Config.ALT_FESTIVAL_FIRST_SPAWN; + + for (L2DarknessFestival festivalInst : _festivalInstances.values()) + { + festivalInst.moveMonstersToCenter(); + } + + // Stand by until the time comes for the second spawn. + try + { + wait(Config.ALT_FESTIVAL_SECOND_SPAWN - Config.ALT_FESTIVAL_FIRST_SWARM); + } + catch (InterruptedException e) + { + } + + // Spawn an extra set of monsters (archers) on the free platforms with + // a faster respawn when killed. + for (L2DarknessFestival festivalInst : _festivalInstances.values()) + { + festivalInst.spawnFestivalMonsters(FESTIVAL_DEFAULT_RESPAWN / 2, 2); + festivalInst.sendMessageToParticipants("The festival will end in " + ((Config.ALT_FESTIVAL_LENGTH - Config.ALT_FESTIVAL_SECOND_SPAWN) / 60000) + " minute(s)."); + } + + elapsedTime += Config.ALT_FESTIVAL_SECOND_SPAWN - Config.ALT_FESTIVAL_FIRST_SWARM; + + // After another short time period, again move all idle spawns to the center of the arena. + try + { + wait(Config.ALT_FESTIVAL_SECOND_SWARM - Config.ALT_FESTIVAL_SECOND_SPAWN); + } + catch (InterruptedException e) + { + } + + for (L2DarknessFestival festivalInst : _festivalInstances.values()) + { + festivalInst.moveMonstersToCenter(); + } + + elapsedTime += Config.ALT_FESTIVAL_SECOND_SWARM - Config.ALT_FESTIVAL_SECOND_SPAWN; + + // Stand by until the time comes for the chests to be spawned. + try + { + wait(Config.ALT_FESTIVAL_CHEST_SPAWN - Config.ALT_FESTIVAL_SECOND_SWARM); + } + catch (InterruptedException e) + { + } + + // Spawn the festival chests, which enable the team to gain greater rewards + // for each chest they kill. + for (L2DarknessFestival festivalInst : _festivalInstances.values()) + { + festivalInst.spawnFestivalMonsters(FESTIVAL_DEFAULT_RESPAWN, 3); + festivalInst.sendMessageToParticipants("The chests have spawned! Be quick, the festival will end soon."); + } + + elapsedTime += Config.ALT_FESTIVAL_CHEST_SPAWN - Config.ALT_FESTIVAL_SECOND_SWARM; + + // Stand by and wait until it's time to end the festival. + try + { + wait(Config.ALT_FESTIVAL_LENGTH - elapsedTime); + } + catch (InterruptedException e) + { + } + + // Participants can no longer opt to increase the challenge, as the festival will soon close. + _festivalInProgress = false; + + /* TERMINATION */ + // Sequentially begin the ending sequence for all running festivals. + for (L2DarknessFestival festivalInst : _festivalInstances.values()) + { + festivalInst.festivalEnd(); + } + + // Clear the participants list for the next round of signups. + _dawnFestivalParticipants.clear(); + _duskFestivalParticipants.clear(); + + // Allow signups for the next festival cycle. + _festivalInitialized = false; + + sendMessageToAll("Festival Witch", "That will do! I'll move you to the outside soon."); + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: The next set of festivals begin in " + getMinsToNextFestival() + " minute(s)."); + } + } + catch (Exception e) + { + _log.warning(e.getMessage()); + } + } + + /** + * Returns the running instance of a festival for the given Oracle and festivalID.
    + * A null value is returned if there are no participants in that festival. + * @param oracle + * @param festivalId + * @return L2DarknessFestival festivalInst + */ + public final L2DarknessFestival getFestivalInstance(int oracle, int festivalId) + { + if (!isFestivalInitialized()) + { + return null; + } + + /* + * Compute the offset if a Dusk instance is required. ID: 0 1 2 3 4 Dusk 1:1011121314 Dawn 2:2021222324 + */ + + festivalId += (oracle == SevenSigns.CABAL_DUSK) ? 10 : 20; + return _festivalInstances.get(festivalId); + } + } + + /** + * Each running festival is represented by an L2DarknessFestival class. It contains all the spawn information and data for the running festival. All festivals are managed by the FestivalManager class, which must be initialized first. + * @author Tempy + */ + private class L2DarknessFestival + { + protected final int _cabal; + protected final int _levelRange; + protected boolean _challengeIncreased; + + private FestivalSpawn _startLocation; + private FestivalSpawn _witchSpawn; + + private L2Npc _witchInst; + List _npcInsts; + + private List _participants; + private final Map _originalLocations; + + protected L2DarknessFestival(int cabal, int levelRange) + { + _cabal = cabal; + _levelRange = levelRange; + _originalLocations = new FastMap<>(); + _npcInsts = new FastList<>(); + + if (cabal == SevenSigns.CABAL_DAWN) + { + _participants = _dawnFestivalParticipants.get(levelRange); + _witchSpawn = new FestivalSpawn(FESTIVAL_DAWN_WITCH_SPAWNS[levelRange]); + _startLocation = new FestivalSpawn(FESTIVAL_DAWN_PLAYER_SPAWNS[levelRange]); + } + else + { + _participants = _duskFestivalParticipants.get(levelRange); + _witchSpawn = new FestivalSpawn(FESTIVAL_DUSK_WITCH_SPAWNS[levelRange]); + _startLocation = new FestivalSpawn(FESTIVAL_DUSK_PLAYER_SPAWNS[levelRange]); + } + + // FOR TESTING! + if (_participants == null) + { + _participants = new ArrayList<>(); + } + + festivalInit(); + } + + protected void festivalInit() + { + boolean isPositive; + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Initializing festival for " + SevenSigns.getCabalShortName(_cabal) + " (" + getFestivalName(_levelRange) + ")"); + } + + // Teleport all players to arena and notify them. + if ((_participants != null) && !_participants.isEmpty()) + { + try + { + for (int participantObjId : _participants) + { + L2PcInstance participant = L2World.getInstance().getPlayer(participantObjId); + if (participant == null) + { + continue; + } + + _originalLocations.put(participantObjId, new FestivalSpawn(participant.getX(), participant.getY(), participant.getZ(), participant.getHeading())); + + // Randomize the spawn point around the specific centerpoint for each player. + int x = _startLocation._x; + int y = _startLocation._y; + + isPositive = (Rnd.nextInt(2) == 1); + + if (isPositive) + { + x += Rnd.nextInt(FESTIVAL_MAX_OFFSET_X); + y += Rnd.nextInt(FESTIVAL_MAX_OFFSET_Y); + } + else + { + x -= Rnd.nextInt(FESTIVAL_MAX_OFFSET_X); + y -= Rnd.nextInt(FESTIVAL_MAX_OFFSET_Y); + } + + participant.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); + participant.teleToLocation(x, y, _startLocation._z, true); + + // Remove all buffs from all participants on entry. Works like the skill Cancel. + participant.stopAllEffectsExceptThoseThatLastThroughDeath(); + + // Remove any stray blood offerings in inventory + L2ItemInstance bloodOfferings = participant.getInventory().getItemByItemId(FESTIVAL_OFFERING_ID); + if (bloodOfferings != null) + { + participant.destroyItem("SevenSigns", bloodOfferings, null, true); + } + } + } + catch (NullPointerException e) + { + // deleteMe handling should teleport party out in case of disconnect + } + } + + L2NpcTemplate witchTemplate = NpcTable.getInstance().getTemplate(_witchSpawn._npcId); + + // Spawn the festival witch for this arena + try + { + L2Spawn npcSpawn = new L2Spawn(witchTemplate); + + npcSpawn.setLocx(_witchSpawn._x); + npcSpawn.setLocy(_witchSpawn._y); + npcSpawn.setLocz(_witchSpawn._z); + npcSpawn.setHeading(_witchSpawn._heading); + npcSpawn.setAmount(1); + npcSpawn.setRespawnDelay(1); + + // Needed as doSpawn() is required to be called also for the NpcInstance it returns. + npcSpawn.startRespawn(); + + SpawnTable.getInstance().addNewSpawn(npcSpawn, false); + _witchInst = npcSpawn.doSpawn(); + + if (Config.DEBUG) + { + _log.fine("SevenSignsFestival: Spawned the Festival Witch " + npcSpawn.getNpcid() + " at " + _witchSpawn._x + " " + _witchSpawn._y + " " + _witchSpawn._z); + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "SevenSignsFestival: Error while spawning Festival Witch ID " + _witchSpawn._npcId + ": " + e.getMessage(), e); + } + + // Make it appear as though the Witch has apparated there. + MagicSkillUse msu = new MagicSkillUse(_witchInst, _witchInst, 2003, 1, 1, 0); + _witchInst.broadcastPacket(msu); + + // And another one...:D + msu = new MagicSkillUse(_witchInst, _witchInst, 2133, 1, 1, 0); + _witchInst.broadcastPacket(msu); + + // Send a message to all participants from the witch. + sendMessageToParticipants("The festival will begin in 2 minutes."); + } + + protected void festivalStart() + { + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Starting festival for " + SevenSigns.getCabalShortName(_cabal) + " (" + getFestivalName(_levelRange) + ")"); + } + + spawnFestivalMonsters(FESTIVAL_DEFAULT_RESPAWN, 0); + } + + protected void moveMonstersToCenter() + { + boolean isPositive; + + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Moving spawns to arena center for festival " + SevenSigns.getCabalShortName(_cabal) + " (" + getFestivalName(_levelRange) + ")"); + } + + for (L2FestivalMonsterInstance festivalMob : _npcInsts) + { + if (festivalMob.isDead()) + { + continue; + } + + // Only move monsters that are idle or doing their usual functions. + CtrlIntention currIntention = festivalMob.getAI().getIntention(); + + if ((currIntention != CtrlIntention.AI_INTENTION_IDLE) && (currIntention != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + int x = _startLocation._x; + int y = _startLocation._y; + + /* + * Random X and Y coords around the player start location, up to half of the maximum allowed offset are generated to prevent the mobs from all moving to the exact same place. + */ + isPositive = (Rnd.nextInt(2) == 1); + + if (isPositive) + { + x += Rnd.nextInt(FESTIVAL_MAX_OFFSET_X); + y += Rnd.nextInt(FESTIVAL_MAX_OFFSET_Y); + } + else + { + x -= Rnd.nextInt(FESTIVAL_MAX_OFFSET_X); + y -= Rnd.nextInt(FESTIVAL_MAX_OFFSET_Y); + } + + L2CharPosition moveTo = new L2CharPosition(x, y, _startLocation._z, Rnd.nextInt(65536)); + + festivalMob.setRunning(); + festivalMob.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, moveTo); + } + } + + /** + * Used to spawn monsters unique to the festival.
    + * Valid SpawnTypes:
    + * 0 - All Primary Monsters (starting monsters)
    + * 1 - Same as 0, but without archers/marksmen. (used for challenge increase)
    + * 2 - Secondary Monsters (archers)
    + * 3 - Festival Chests + * @param respawnDelay + * @param spawnType + */ + protected void spawnFestivalMonsters(int respawnDelay, int spawnType) + { + int[][] _npcSpawns = null; + + switch (spawnType) + { + case 0: + case 1: + _npcSpawns = (_cabal == SevenSigns.CABAL_DAWN) ? FESTIVAL_DAWN_PRIMARY_SPAWNS[_levelRange] : FESTIVAL_DUSK_PRIMARY_SPAWNS[_levelRange]; + break; + case 2: + _npcSpawns = (_cabal == SevenSigns.CABAL_DAWN) ? FESTIVAL_DAWN_SECONDARY_SPAWNS[_levelRange] : FESTIVAL_DUSK_SECONDARY_SPAWNS[_levelRange]; + break; + case 3: + _npcSpawns = (_cabal == SevenSigns.CABAL_DAWN) ? FESTIVAL_DAWN_CHEST_SPAWNS[_levelRange] : FESTIVAL_DUSK_CHEST_SPAWNS[_levelRange]; + break; + } + + for (int[] _npcSpawn : _npcSpawns) + { + FestivalSpawn currSpawn = new FestivalSpawn(_npcSpawn); + + // Only spawn archers/marksmen if specified to do so. + if ((spawnType == 1) && isFestivalArcher(currSpawn._npcId)) + { + continue; + } + + L2NpcTemplate npcTemplate = NpcTable.getInstance().getTemplate(currSpawn._npcId); + + try + { + L2Spawn npcSpawn = new L2Spawn(npcTemplate); + + npcSpawn.setLocx(currSpawn._x); + npcSpawn.setLocy(currSpawn._y); + npcSpawn.setLocz(currSpawn._z); + npcSpawn.setHeading(Rnd.nextInt(65536)); + npcSpawn.setAmount(1); + npcSpawn.setRespawnDelay(respawnDelay); + + // Needed as doSpawn() is required to be called also for the NpcInstance it returns. + npcSpawn.startRespawn(); + + SpawnTable.getInstance().addNewSpawn(npcSpawn, false); + L2FestivalMonsterInstance festivalMob = (L2FestivalMonsterInstance) npcSpawn.doSpawn(); + + // Set the offering bonus to 2x or 5x the amount per kill, + // if this spawn is part of an increased challenge or is a festival chest. + if (spawnType == 1) + { + festivalMob.setOfferingBonus(2); + } + else if (spawnType == 3) + { + festivalMob.setOfferingBonus(5); + } + + _npcInsts.add(festivalMob); + + if (Config.DEBUG) + { + _log.fine("SevenSignsFestival: Spawned NPC ID " + currSpawn._npcId + " at " + currSpawn._x + " " + currSpawn._y + " " + currSpawn._z); + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "SevenSignsFestival: Error while spawning NPC ID " + currSpawn._npcId + ": " + e.getMessage(), e); + } + } + } + + protected boolean increaseChallenge() + { + if (_challengeIncreased) + { + return false; + } + + // Set this flag to true to make sure that this can only be done once. + _challengeIncreased = true; + + // Spawn more festival monsters, but this time with a twist. + spawnFestivalMonsters(FESTIVAL_DEFAULT_RESPAWN, 1); + return true; + } + + public void sendMessageToParticipants(String message) + { + if ((_participants != null) && !_participants.isEmpty()) + { + _witchInst.broadcastPacket(new CreatureSay(_witchInst.getObjectId(), Say2.ALL, "Festival Witch", message)); + } + } + + protected void festivalEnd() + { + if (Config.DEBUG) + { + _log.info("SevenSignsFestival: Ending festival for " + SevenSigns.getCabalShortName(_cabal) + " (" + getFestivalName(_levelRange) + ")"); + } + + if ((_participants != null) && !_participants.isEmpty()) + { + for (int participantObjId : _participants) + { + try + { + L2PcInstance participant = L2World.getInstance().getPlayer(participantObjId); + if (participant == null) + { + continue; + } + + relocatePlayer(participant, false); + participant.sendMessage("The festival has ended. Your party leader must now register your score before the next festival takes place."); + } + catch (NullPointerException e) + { + } + } + + if (_cabal == SevenSigns.CABAL_DAWN) + { + _dawnPreviousParticipants.put(_levelRange, _participants); + } + else + { + _duskPreviousParticipants.put(_levelRange, _participants); + } + } + _participants = null; + + unspawnMobs(); + } + + protected void unspawnMobs() + { + // Delete all the NPCs in the current festival arena. + if (_witchInst != null) + { + _witchInst.getSpawn().stopRespawn(); + _witchInst.deleteMe(); + SpawnTable.getInstance().deleteSpawn(_witchInst.getSpawn(), false); + } + + if (_npcInsts != null) + { + for (L2FestivalMonsterInstance monsterInst : _npcInsts) + { + if (monsterInst != null) + { + monsterInst.getSpawn().stopRespawn(); + monsterInst.deleteMe(); + SpawnTable.getInstance().deleteSpawn(monsterInst.getSpawn(), false); + } + } + } + } + + public void relocatePlayer(L2PcInstance participant, boolean isRemoving) + { + try + { + FestivalSpawn origPosition = _originalLocations.get(participant.getObjectId()); + + if (isRemoving) + { + _originalLocations.remove(participant.getObjectId()); + } + + participant.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); + participant.teleToLocation(origPosition._x, origPosition._y, origPosition._z, true); + participant.sendMessage("You have been removed from the festival arena."); + } + catch (Exception e) + { + // If an exception occurs, just move the player to the nearest town. + try + { + participant.teleToLocation(MapRegionTable.TeleportWhereType.Town); + participant.sendMessage("You have been removed from the festival arena."); + } + catch (NullPointerException e2) + { + } + } + } + } + + private static class FestivalSpawn + { + protected final int _x; + protected final int _y; + protected final int _z; + protected final int _heading; + protected final int _npcId; + + protected FestivalSpawn(int x, int y, int z, int heading) + { + _x = x; + _y = y; + _z = z; + + // Generate a random heading if no positive one given. + _heading = (heading < 0) ? Rnd.nextInt(65536) : heading; + + _npcId = -1; + } + + protected FestivalSpawn(int[] spawnData) + { + _x = spawnData[0]; + _y = spawnData[1]; + _z = spawnData[2]; + + _heading = (spawnData[3] < 0) ? Rnd.nextInt(65536) : spawnData[3]; + + if (spawnData.length > 4) + { + _npcId = spawnData[4]; + } + else + { + _npcId = -1; + } + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final SevenSignsFestival _instance = new SevenSignsFestival(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/Shutdown.java b/L2J_Server/java/com/l2jserver/gameserver/Shutdown.java new file mode 100644 index 00000000..9ac16b63 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/Shutdown.java @@ -0,0 +1,594 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.util.Collection; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.datatables.OfflineTradersTable; +import com.l2jserver.gameserver.instancemanager.CastleManorManager; +import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager; +import com.l2jserver.gameserver.instancemanager.GlobalVariablesManager; +import com.l2jserver.gameserver.instancemanager.GrandBossManager; +import com.l2jserver.gameserver.instancemanager.ItemAuctionManager; +import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager; +import com.l2jserver.gameserver.instancemanager.QuestManager; +import com.l2jserver.gameserver.instancemanager.RaidBossSpawnManager; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.entity.Hero; +import com.l2jserver.gameserver.model.olympiad.Olympiad; +import com.l2jserver.gameserver.network.L2GameClient; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.communityserver.CommunityServerThread; +import com.l2jserver.gameserver.network.gameserverpackets.ServerStatus; +import com.l2jserver.gameserver.network.serverpackets.ServerClose; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.util.Broadcast; + +/** + * This class provides the functions for shutting down and restarting the server It closes all open clientconnections and saves all data. + * @version $Revision: 1.2.4.5 $ $Date: 2005/03/27 15:29:09 $ + */ +public class Shutdown extends Thread +{ + private static Logger _log = Logger.getLogger(Shutdown.class.getName()); + private static Shutdown _counterInstance = null; + + private int _secondsShut; + private int _shutdownMode; + public static final int SIGTERM = 0; + public static final int GM_SHUTDOWN = 1; + public static final int GM_RESTART = 2; + public static final int ABORT = 3; + private static final String[] MODE_TEXT = + { + "SIGTERM", + "shutting down", + "restarting", + "aborting" + }; + + /** + * This function starts a shutdown countdown from Telnet (Copied from Function startShutdown()) + * @param ip IP Which Issued shutdown command + * @param seconds seconds untill shutdown + * @param restart true if the server will restart after shutdown + */ + private void SendServerQuit(int seconds) + { + SystemMessage sysm = SystemMessage.getSystemMessage(SystemMessageId.THE_SERVER_WILL_BE_COMING_DOWN_IN_S1_SECONDS); + sysm.addNumber(seconds); + Broadcast.toAllOnlinePlayers(sysm); + } + + public void startTelnetShutdown(String IP, int seconds, boolean restart) + { + _log.warning("IP: " + IP + " issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!"); + // _an.announceToAll("Server is " + _modeText[shutdownMode] + " in "+seconds+ " seconds!"); + + if (restart) + { + _shutdownMode = GM_RESTART; + } + else + { + _shutdownMode = GM_SHUTDOWN; + } + + if (_shutdownMode > 0) + { + switch (seconds) + { + case 540: + case 480: + case 420: + case 360: + case 300: + case 240: + case 180: + case 120: + case 60: + case 30: + case 10: + case 5: + case 4: + case 3: + case 2: + case 1: + break; + default: + SendServerQuit(seconds); + } + } + + if (_counterInstance != null) + { + _counterInstance._abort(); + } + _counterInstance = new Shutdown(seconds, restart); + _counterInstance.start(); + } + + /** + * This function aborts a running countdown + * @param IP IP Which Issued shutdown command + */ + public void telnetAbort(String IP) + { + _log.warning("IP: " + IP + " issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!"); + + if (_counterInstance != null) + { + _counterInstance._abort(); + Announcements _an = Announcements.getInstance(); + _an.announceToAll("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!"); + } + } + + /** + * Default constucter is only used internal to create the shutdown-hook instance + */ + private Shutdown() + { + _secondsShut = -1; + _shutdownMode = SIGTERM; + } + + /** + * This creates a countdown instance of Shutdown. + * @param seconds how many seconds until shutdown + * @param restart true is the server shall restart after shutdown + */ + public Shutdown(int seconds, boolean restart) + { + if (seconds < 0) + { + seconds = 0; + } + _secondsShut = seconds; + if (restart) + { + _shutdownMode = GM_RESTART; + } + else + { + _shutdownMode = GM_SHUTDOWN; + } + } + + /** + * get the shutdown-hook instance the shutdown-hook instance is created by the first call of this function, but it has to be registrered externaly. + * @return instance of Shutdown, to be used as shutdown hook + */ + public static Shutdown getInstance() + { + return SingletonHolder._instance; + } + + /** + * this function is called, when a new thread starts if this thread is the thread of getInstance, then this is the shutdown hook and we save all data and disconnect all clients. after this thread ends, the server will completely exit if this is not the thread of getInstance, then this is a + * countdown thread. we start the countdown, and when we finished it, and it was not aborted, we tell the shutdown-hook why we call exit, and then call exit when the exit status of the server is 1, startServer.sh / startServer.bat will restart the server. + */ + @Override + public void run() + { + if (this == SingletonHolder._instance) + { + try + { + if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS) + { + OfflineTradersTable.storeOffliners(); + } + } + catch (Throwable t) + { + _log.log(Level.WARNING, "Error saving offline shops.", t); + } + + try + { + disconnectAllCharacters(); + _log.info("All players disconnected."); + } + catch (Throwable t) + { + // ignore + } + + // ensure all services are stopped + try + { + GameTimeController.getInstance().stopTimer(); + } + catch (Throwable t) + { + // ignore + } + + // stop all threadpolls + try + { + ThreadPoolManager.getInstance().shutdown(); + } + catch (Throwable t) + { + // ignore + } + + try + { + CommunityServerThread.getInstance().interrupt(); + } + catch (Throwable t) + { + // ignore + } + + try + { + LoginServerThread.getInstance().interrupt(); + } + catch (Throwable t) + { + // ignore + } + + // last byebye, save all data and quit this server + saveData(); + + // saveData sends messages to exit players, so shutdown selector after it + try + { + GameServer.gameServer.getSelectorThread().shutdown(); + } + catch (Throwable t) + { + // ignore + } + + // commit data, last chance + try + { + L2DatabaseFactory.getInstance().shutdown(); + } + catch (Throwable t) + { + + } + + // server will quit, when this function ends. + if (SingletonHolder._instance._shutdownMode == GM_RESTART) + { + Runtime.getRuntime().halt(2); + } + else + { + Runtime.getRuntime().halt(0); + } + } + else + { + // gm shutdown: send warnings and then call exit to start shutdown sequence + countdown(); + // last point where logging is operational :( + _log.warning("GM shutdown countdown is over. " + MODE_TEXT[_shutdownMode] + " NOW!"); + switch (_shutdownMode) + { + case GM_SHUTDOWN: + SingletonHolder._instance.setMode(GM_SHUTDOWN); + System.exit(0); + break; + case GM_RESTART: + SingletonHolder._instance.setMode(GM_RESTART); + System.exit(2); + break; + } + } + } + + /** + * This functions starts a shutdown countdown + * @param activeChar GM who issued the shutdown command + * @param seconds seconds until shutdown + * @param restart true if the server will restart after shutdown + */ + public void startShutdown(L2PcInstance activeChar, int seconds, boolean restart) + { + if (restart) + { + _shutdownMode = GM_RESTART; + } + else + { + _shutdownMode = GM_SHUTDOWN; + } + + _log.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjectId() + ") issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!"); + + if (_shutdownMode > 0) + { + switch (seconds) + { + case 540: + case 480: + case 420: + case 360: + case 300: + case 240: + case 180: + case 120: + case 60: + case 30: + case 10: + case 5: + case 4: + case 3: + case 2: + case 1: + break; + default: + SendServerQuit(seconds); + } + } + + if (_counterInstance != null) + { + _counterInstance._abort(); + } + + // the main instance should only run for shutdown hook, so we start a new instance + _counterInstance = new Shutdown(seconds, restart); + _counterInstance.start(); + } + + /** + * This function aborts a running countdown + * @param activeChar GM who issued the abort command + */ + public void abort(L2PcInstance activeChar) + { + _log.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjectId() + ") issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!"); + if (_counterInstance != null) + { + _counterInstance._abort(); + Announcements _an = Announcements.getInstance(); + _an.announceToAll("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!"); + } + } + + /** + * set the shutdown mode + * @param mode what mode shall be set + */ + private void setMode(int mode) + { + _shutdownMode = mode; + } + + /** + * set shutdown mode to ABORT + */ + private void _abort() + { + _shutdownMode = ABORT; + } + + /** + * this counts the countdown and reports it to all players countdown is aborted if mode changes to ABORT + */ + private void countdown() + { + + try + { + while (_secondsShut > 0) + { + + switch (_secondsShut) + { + case 540: + SendServerQuit(540); + break; + case 480: + SendServerQuit(480); + break; + case 420: + SendServerQuit(420); + break; + case 360: + SendServerQuit(360); + break; + case 300: + SendServerQuit(300); + break; + case 240: + SendServerQuit(240); + break; + case 180: + SendServerQuit(180); + break; + case 120: + SendServerQuit(120); + break; + case 60: + LoginServerThread.getInstance().setServerStatus(ServerStatus.STATUS_DOWN); // avoids new players from logging in + SendServerQuit(60); + break; + case 30: + SendServerQuit(30); + break; + case 10: + SendServerQuit(10); + break; + case 5: + SendServerQuit(5); + break; + case 4: + SendServerQuit(4); + break; + case 3: + SendServerQuit(3); + break; + case 2: + SendServerQuit(2); + break; + case 1: + SendServerQuit(1); + break; + } + + _secondsShut--; + + int delay = 1000; // milliseconds + Thread.sleep(delay); + + if (_shutdownMode == ABORT) + { + break; + } + } + } + catch (InterruptedException e) + { + // this will never happen + } + } + + /** + * this sends a last byebye, disconnects all players and saves data + */ + private void saveData() + { + switch (_shutdownMode) + { + case SIGTERM: + _log.info("SIGTERM received. Shutting down NOW!"); + break; + case GM_SHUTDOWN: + _log.info("GM shutdown received. Shutting down NOW!"); + break; + case GM_RESTART: + _log.info("GM restart received. Restarting NOW!"); + break; + + } + + /* + * if (Config.ACTIVATE_POSITION_RECORDER) Universe.getInstance().implode(true); + */ + + // Seven Signs data is now saved along with Festival data. + if (!SevenSigns.getInstance().isSealValidationPeriod()) + { + SevenSignsFestival.getInstance().saveFestivalData(false); + } + + // Save Seven Signs data before closing. :) + SevenSigns.getInstance().saveSevenSignsData(); + SevenSigns.getInstance().saveSevenSignsStatus(); + + // Save all raidboss and GrandBoss status ^_^ + RaidBossSpawnManager.getInstance().cleanUp(); + _log.info("RaidBossSpawnManager: All raidboss info saved!!"); + GrandBossManager.getInstance().cleanUp(); + _log.info("GrandBossManager: All Grand Boss info saved!!"); + _log.info("TradeController saving data.. This action may take some minutes! Please wait until completed!"); + TradeController.getInstance().dataCountStore(); + _log.info("TradeController: All count Item Saved"); + ItemAuctionManager.getInstance().shutdown(); + Olympiad.getInstance().saveOlympiadStatus(); + _log.info("Olympiad System: Data saved!!"); + Hero.getInstance().shutdown(); + _log.info("Hero System: Data saved!!"); + ClanTable.getInstance().storeClanScore(); + _log.info("Clan System: Data saved!!"); + + // Save Cursed Weapons data before closing. + CursedWeaponsManager.getInstance().saveData(); + + // Save all manor data + CastleManorManager.getInstance().save(); + + // Save all global (non-player specific) Quest data that needs to persist after reboot + QuestManager.getInstance().save(); + + // Save all global variables data + GlobalVariablesManager.getInstance().saveVars(); + + // Save items on ground before closing + if (Config.SAVE_DROPPED_ITEM) + { + ItemsOnGroundManager.getInstance().saveInDb(); + ItemsOnGroundManager.getInstance().cleanUp(); + _log.info("ItemsOnGroundManager: All items on ground saved!!"); + } + + try + { + int delay = 5000; + Thread.sleep(delay); + } + catch (InterruptedException e) + { + // never happens :p + } + } + + /** + * this disconnects all clients from the server + */ + private void disconnectAllCharacters() + { + Collection pls = L2World.getInstance().getAllPlayers().values(); + // synchronized (L2World.getInstance().getAllPlayers()) + { + for (L2PcInstance player : pls) + { + if (player == null) + { + continue; + } + // Logout Character + try + { + L2GameClient client = player.getClient(); + if ((client != null) && !client.isDetached()) + { + client.close(ServerClose.STATIC_PACKET); + client.setActiveChar(null); + player.setClient(null); + } + player.deleteMe(); + } + catch (Throwable t) + { + _log.log(Level.WARNING, "Failed logour char " + player, t); + } + } + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final Shutdown _instance = new Shutdown(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/TaskPriority.java b/L2J_Server/java/com/l2jserver/gameserver/TaskPriority.java new file mode 100644 index 00000000..f983d928 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/TaskPriority.java @@ -0,0 +1,25 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +/** + * @author mkizub + */ +public enum TaskPriority +{ + PR_NORMAL, + PR_HIGH, + PR_URGENT; +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/Territory.java b/L2J_Server/java/com/l2jserver/gameserver/Territory.java new file mode 100644 index 00000000..61b5d7fd --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/Territory.java @@ -0,0 +1,98 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +/* + coded by Balancer + ported to L2JRU by Mr + balancer@balancer.ru + http://balancer.ru + + version 0.1.1, 2005-06-07 + version 0.1, 2005-03-16 + */ + +package com.l2jserver.gameserver; + +import java.util.Map; +import java.util.logging.Logger; + +import javolution.util.FastMap; + +import com.l2jserver.gameserver.model.L2Territory; +import com.l2jserver.util.lib.SqlUtils; + +public class Territory +{ + private static Logger _log = Logger.getLogger(TradeController.class.getName()); + private static Map _territory; + + public static Territory getInstance() + { + return SingletonHolder._instance; + } + + private Territory() + { + // load all data at server start + reload_data(); + } + + public int[] getRandomPoint(int terr) + { + return _territory.get(terr).getRandomPoint(); + } + + public int getProcMax(int terr) + { + return _territory.get(terr).getProcMax(); + } + + public void reload_data() + { + _territory = new FastMap<>(); + + Integer[][] point = SqlUtils.get2DIntArray(new String[] + { + "loc_id", + "loc_x", + "loc_y", + "loc_zmin", + "loc_zmax", + "proc" + }, "locations", "loc_id > 0"); + for (Integer[] row : point) + { + // _log.info("row = "+row[0]); + Integer terr = row[0]; + if (terr == null) + { + _log.warning("Null territory!"); + continue; + } + + if (_territory.get(terr) == null) + { + L2Territory t = new L2Territory(terr); + _territory.put(terr, t); + } + _territory.get(terr).add(row[1], row[2], row[3], row[4], row[5]); + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final Territory _instance = new Territory(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ThreadPoolManager.java b/L2J_Server/java/com/l2jserver/gameserver/ThreadPoolManager.java new file mode 100644 index 00000000..a7ccaa2f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ThreadPoolManager.java @@ -0,0 +1,510 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RunnableScheduledFuture; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; +import javolution.util.FastSet; + +import com.l2jserver.Config; +import com.l2jserver.util.StringUtil; + +/** + *

    + * This class is made to handle all the ThreadPools used in L2j. + *

    + *

    + * Scheduled Tasks can either be sent to a {@link #_generalScheduledThreadPool "general"} or {@link #_effectsScheduledThreadPool "effects"} {@link ScheduledThreadPoolExecutor ScheduledThreadPool}: The "effects" one is used for every effects (skills, hp/mp regen ...) while the "general" one is used + * for everything else that needs to be scheduled.
    + * There also is an {@link #_aiScheduledThreadPool "ai"} {@link ScheduledThreadPoolExecutor ScheduledThreadPool} used for AI Tasks. + *

    + *

    + * Tasks can be sent to {@link ScheduledThreadPoolExecutor ScheduledThreadPool} either with: + *

      + *
    • {@link #scheduleEffect(Runnable, long)} : for effects Tasks that needs to be executed only once.
    • + *
    • {@link #scheduleGeneral(Runnable, long)} : for scheduled Tasks that needs to be executed once.
    • + *
    • {@link #scheduleAi(Runnable, long)} : for AI Tasks that needs to be executed once
    • + *
    + * or + *
      + *
    • {@link #scheduleEffectAtFixedRate(Runnable, long, long)(Runnable, long)} : for effects Tasks that needs to be executed periodicaly.
    • + *
    • {@link #scheduleGeneralAtFixedRate(Runnable, long, long)(Runnable, long)} : for scheduled Tasks that needs to be executed periodicaly.
    • + *
    • {@link #scheduleAiAtFixedRate(Runnable, long, long)(Runnable, long)} : for AI Tasks that needs to be executed periodicaly
    • + *
    + *

    + *

    + * For all Tasks that should be executed with no delay asynchronously in a ThreadPool there also are usual {@link ThreadPoolExecutor ThreadPools} that can grow/shrink according to their load.: + *

      + *
    • {@link #_generalPacketsThreadPool GeneralPackets} where most packets handler are executed.
    • + *
    • {@link #_ioPacketsThreadPool I/O Packets} where all the i/o packets are executed.
    • + *
    • There will be an AI ThreadPool where AI events should be executed
    • + *
    • A general ThreadPool where everything else that needs to run asynchronously with no delay should be executed ({@link com.l2jserver.gameserver.model.actor.knownlist KnownList} updates, SQL updates/inserts...)?
    • + *
    + *

    + * @author -Wooden- + */ +public class ThreadPoolManager +{ + protected static final Logger _log = Logger.getLogger(ThreadPoolManager.class.getName()); + + private static final class RunnableWrapper implements Runnable + { + private final Runnable _r; + + public RunnableWrapper(final Runnable r) + { + _r = r; + } + + @Override + public final void run() + { + try + { + _r.run(); + } + catch (final Throwable e) + { + final Thread t = Thread.currentThread(); + final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler(); + if (h != null) + { + h.uncaughtException(t, e); + } + } + } + } + + protected final ScheduledThreadPoolExecutor _effectsScheduledThreadPool; + protected final ScheduledThreadPoolExecutor _generalScheduledThreadPool; + protected final ScheduledThreadPoolExecutor _aiScheduledThreadPool; + private final ThreadPoolExecutor _generalPacketsThreadPool; + private final ThreadPoolExecutor _ioPacketsThreadPool; + private final ThreadPoolExecutor _generalThreadPool; + + /** temp workaround for VM issue */ + private static final long MAX_DELAY = Long.MAX_VALUE / 1000000 / 2; + + private boolean _shutdown; + + public static ThreadPoolManager getInstance() + { + return SingletonHolder._instance; + } + + private ThreadPoolManager() + { + _effectsScheduledThreadPool = new ScheduledThreadPoolExecutor(Config.THREAD_P_EFFECTS, new PriorityThreadFactory("EffectsSTPool", Thread.NORM_PRIORITY)); + _generalScheduledThreadPool = new ScheduledThreadPoolExecutor(Config.THREAD_P_GENERAL, new PriorityThreadFactory("GeneralSTPool", Thread.NORM_PRIORITY)); + _ioPacketsThreadPool = new ThreadPoolExecutor(Config.IO_PACKET_THREAD_CORE_SIZE, Integer.MAX_VALUE, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue(), new PriorityThreadFactory("I/O Packet Pool", Thread.NORM_PRIORITY + 1)); + _generalPacketsThreadPool = new ThreadPoolExecutor(Config.GENERAL_PACKET_THREAD_CORE_SIZE, Config.GENERAL_PACKET_THREAD_CORE_SIZE + 2, 15L, TimeUnit.SECONDS, new LinkedBlockingQueue(), new PriorityThreadFactory("Normal Packet Pool", Thread.NORM_PRIORITY + 1)); + _generalThreadPool = new ThreadPoolExecutor(Config.GENERAL_THREAD_CORE_SIZE, Config.GENERAL_THREAD_CORE_SIZE + 2, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue(), new PriorityThreadFactory("General Pool", Thread.NORM_PRIORITY)); + _aiScheduledThreadPool = new ScheduledThreadPoolExecutor(Config.AI_MAX_THREAD, new PriorityThreadFactory("AISTPool", Thread.NORM_PRIORITY)); + + scheduleGeneralAtFixedRate(new PurgeTask(), 10 * 60 * 1000l, 5 * 60 * 1000l); + } + + public static long validateDelay(long delay) + { + if (delay < 0) + { + delay = 0; + } + else if (delay > MAX_DELAY) + { + delay = MAX_DELAY; + } + return delay; + } + + public ScheduledFuture scheduleEffect(Runnable r, long delay) + { + try + { + delay = ThreadPoolManager.validateDelay(delay); + return _effectsScheduledThreadPool.schedule(new RunnableWrapper(r), delay, TimeUnit.MILLISECONDS); + } + catch (RejectedExecutionException e) + { + return null; + } + } + + public ScheduledFuture scheduleEffectAtFixedRate(Runnable r, long initial, long delay) + { + try + { + delay = ThreadPoolManager.validateDelay(delay); + initial = ThreadPoolManager.validateDelay(initial); + return _effectsScheduledThreadPool.scheduleAtFixedRate(new RunnableWrapper(r), initial, delay, TimeUnit.MILLISECONDS); + } + catch (RejectedExecutionException e) + { + return null; /* shutdown, ignore */ + } + } + + @Deprecated + public boolean removeEffect(RunnableScheduledFuture r) + { + return _effectsScheduledThreadPool.remove(r); + } + + public ScheduledFuture scheduleGeneral(Runnable r, long delay) + { + try + { + delay = ThreadPoolManager.validateDelay(delay); + return _generalScheduledThreadPool.schedule(new RunnableWrapper(r), delay, TimeUnit.MILLISECONDS); + } + catch (RejectedExecutionException e) + { + return null; /* shutdown, ignore */ + } + } + + public ScheduledFuture scheduleGeneralAtFixedRate(Runnable r, long initial, long delay) + { + try + { + delay = ThreadPoolManager.validateDelay(delay); + initial = ThreadPoolManager.validateDelay(initial); + return _generalScheduledThreadPool.scheduleAtFixedRate(new RunnableWrapper(r), initial, delay, TimeUnit.MILLISECONDS); + } + catch (RejectedExecutionException e) + { + return null; /* shutdown, ignore */ + } + } + + @Deprecated + public boolean removeGeneral(RunnableScheduledFuture r) + { + return _generalScheduledThreadPool.remove(r); + } + + public ScheduledFuture scheduleAi(Runnable r, long delay) + { + try + { + delay = ThreadPoolManager.validateDelay(delay); + return _aiScheduledThreadPool.schedule(new RunnableWrapper(r), delay, TimeUnit.MILLISECONDS); + } + catch (RejectedExecutionException e) + { + return null; /* shutdown, ignore */ + } + } + + public ScheduledFuture scheduleAiAtFixedRate(Runnable r, long initial, long delay) + { + try + { + delay = ThreadPoolManager.validateDelay(delay); + initial = ThreadPoolManager.validateDelay(initial); + return _aiScheduledThreadPool.scheduleAtFixedRate(new RunnableWrapper(r), initial, delay, TimeUnit.MILLISECONDS); + } + catch (RejectedExecutionException e) + { + return null; /* shutdown, ignore */ + } + } + + public void executePacket(Runnable pkt) + { + _generalPacketsThreadPool.execute(pkt); + } + + public void executeCommunityPacket(Runnable r) + { + _generalPacketsThreadPool.execute(r); + } + + public void executeIOPacket(Runnable pkt) + { + _ioPacketsThreadPool.execute(pkt); + } + + public void executeTask(Runnable r) + { + _generalThreadPool.execute(r); + } + + public void executeAi(Runnable r) + { + _aiScheduledThreadPool.execute(new RunnableWrapper(r)); + } + + public String[] getStats() + { + return new String[] + { + "STP:", + " + Effects:", + " |- ActiveThreads: " + _effectsScheduledThreadPool.getActiveCount(), + " |- getCorePoolSize: " + _effectsScheduledThreadPool.getCorePoolSize(), + " |- PoolSize: " + _effectsScheduledThreadPool.getPoolSize(), + " |- MaximumPoolSize: " + _effectsScheduledThreadPool.getMaximumPoolSize(), + " |- CompletedTasks: " + _effectsScheduledThreadPool.getCompletedTaskCount(), + " |- ScheduledTasks: " + (_effectsScheduledThreadPool.getTaskCount() - _effectsScheduledThreadPool.getCompletedTaskCount()), + " | -------", + " + General:", + " |- ActiveThreads: " + _generalScheduledThreadPool.getActiveCount(), + " |- getCorePoolSize: " + _generalScheduledThreadPool.getCorePoolSize(), + " |- PoolSize: " + _generalScheduledThreadPool.getPoolSize(), + " |- MaximumPoolSize: " + _generalScheduledThreadPool.getMaximumPoolSize(), + " |- CompletedTasks: " + _generalScheduledThreadPool.getCompletedTaskCount(), + " |- ScheduledTasks: " + (_generalScheduledThreadPool.getTaskCount() - _generalScheduledThreadPool.getCompletedTaskCount()), + " | -------", + " + AI:", + " |- ActiveThreads: " + _aiScheduledThreadPool.getActiveCount(), + " |- getCorePoolSize: " + _aiScheduledThreadPool.getCorePoolSize(), + " |- PoolSize: " + _aiScheduledThreadPool.getPoolSize(), + " |- MaximumPoolSize: " + _aiScheduledThreadPool.getMaximumPoolSize(), + " |- CompletedTasks: " + _aiScheduledThreadPool.getCompletedTaskCount(), + " |- ScheduledTasks: " + (_aiScheduledThreadPool.getTaskCount() - _aiScheduledThreadPool.getCompletedTaskCount()), + "TP:", + " + Packets:", + " |- ActiveThreads: " + _generalPacketsThreadPool.getActiveCount(), + " |- getCorePoolSize: " + _generalPacketsThreadPool.getCorePoolSize(), + " |- MaximumPoolSize: " + _generalPacketsThreadPool.getMaximumPoolSize(), + " |- LargestPoolSize: " + _generalPacketsThreadPool.getLargestPoolSize(), + " |- PoolSize: " + _generalPacketsThreadPool.getPoolSize(), + " |- CompletedTasks: " + _generalPacketsThreadPool.getCompletedTaskCount(), + " |- QueuedTasks: " + _generalPacketsThreadPool.getQueue().size(), + " | -------", + " + I/O Packets:", + " |- ActiveThreads: " + _ioPacketsThreadPool.getActiveCount(), + " |- getCorePoolSize: " + _ioPacketsThreadPool.getCorePoolSize(), + " |- MaximumPoolSize: " + _ioPacketsThreadPool.getMaximumPoolSize(), + " |- LargestPoolSize: " + _ioPacketsThreadPool.getLargestPoolSize(), + " |- PoolSize: " + _ioPacketsThreadPool.getPoolSize(), + " |- CompletedTasks: " + _ioPacketsThreadPool.getCompletedTaskCount(), + " |- QueuedTasks: " + _ioPacketsThreadPool.getQueue().size(), + " | -------", + " + General Tasks:", + " |- ActiveThreads: " + _generalThreadPool.getActiveCount(), + " |- getCorePoolSize: " + _generalThreadPool.getCorePoolSize(), + " |- MaximumPoolSize: " + _generalThreadPool.getMaximumPoolSize(), + " |- LargestPoolSize: " + _generalThreadPool.getLargestPoolSize(), + " |- PoolSize: " + _generalThreadPool.getPoolSize(), + " |- CompletedTasks: " + _generalThreadPool.getCompletedTaskCount(), + " |- QueuedTasks: " + _generalThreadPool.getQueue().size(), + " | -------", + " + Javolution stats:", + " |- FastList: " + FastList.report(), + " |- FastMap: " + FastMap.report(), + " |- FastSet: " + FastSet.report(), + " | -------" + }; + } + + private static class PriorityThreadFactory implements ThreadFactory + { + private final int _prio; + private final String _name; + private final AtomicInteger _threadNumber = new AtomicInteger(1); + private final ThreadGroup _group; + + public PriorityThreadFactory(String name, int prio) + { + _prio = prio; + _name = name; + _group = new ThreadGroup(_name); + } + + @Override + public Thread newThread(Runnable r) + { + Thread t = new Thread(_group, r); + t.setName(_name + "-" + _threadNumber.getAndIncrement()); + t.setPriority(_prio); + return t; + } + + public ThreadGroup getGroup() + { + return _group; + } + } + + public void shutdown() + { + _shutdown = true; + try + { + _effectsScheduledThreadPool.awaitTermination(1, TimeUnit.SECONDS); + _generalScheduledThreadPool.awaitTermination(1, TimeUnit.SECONDS); + _generalPacketsThreadPool.awaitTermination(1, TimeUnit.SECONDS); + _ioPacketsThreadPool.awaitTermination(1, TimeUnit.SECONDS); + _generalThreadPool.awaitTermination(1, TimeUnit.SECONDS); + _effectsScheduledThreadPool.shutdown(); + _generalScheduledThreadPool.shutdown(); + _generalPacketsThreadPool.shutdown(); + _ioPacketsThreadPool.shutdown(); + _generalThreadPool.shutdown(); + _log.info("All ThreadPools are now stopped"); + + } + catch (InterruptedException e) + { + _log.log(Level.WARNING, "", e); + } + } + + public boolean isShutdown() + { + return _shutdown; + } + + public void purge() + { + _effectsScheduledThreadPool.purge(); + _generalScheduledThreadPool.purge(); + _aiScheduledThreadPool.purge(); + _ioPacketsThreadPool.purge(); + _generalPacketsThreadPool.purge(); + _generalThreadPool.purge(); + } + + public String getPacketStats() + { + final StringBuilder sb = new StringBuilder(1000); + ThreadFactory tf = _generalPacketsThreadPool.getThreadFactory(); + if (tf instanceof PriorityThreadFactory) + { + PriorityThreadFactory ptf = (PriorityThreadFactory) tf; + int count = ptf.getGroup().activeCount(); + Thread[] threads = new Thread[count + 2]; + ptf.getGroup().enumerate(threads); + StringUtil.append(sb, "General Packet Thread Pool:\r\n" + "Tasks in the queue: ", String.valueOf(_generalPacketsThreadPool.getQueue().size()), "\r\n" + "Showing threads stack trace:\r\n" + "There should be ", String.valueOf(count), " Threads\r\n"); + for (Thread t : threads) + { + if (t == null) + { + continue; + } + + StringUtil.append(sb, t.getName(), "\r\n"); + for (StackTraceElement ste : t.getStackTrace()) + { + StringUtil.append(sb, ste.toString(), "\r\n"); + } + } + } + + sb.append("Packet Tp stack traces printed.\r\n"); + + return sb.toString(); + } + + public String getIOPacketStats() + { + final StringBuilder sb = new StringBuilder(1000); + ThreadFactory tf = _ioPacketsThreadPool.getThreadFactory(); + + if (tf instanceof PriorityThreadFactory) + { + PriorityThreadFactory ptf = (PriorityThreadFactory) tf; + int count = ptf.getGroup().activeCount(); + Thread[] threads = new Thread[count + 2]; + ptf.getGroup().enumerate(threads); + StringUtil.append(sb, "I/O Packet Thread Pool:\r\n" + "Tasks in the queue: ", String.valueOf(_ioPacketsThreadPool.getQueue().size()), "\r\n" + "Showing threads stack trace:\r\n" + "There should be ", String.valueOf(count), " Threads\r\n"); + + for (Thread t : threads) + { + if (t == null) + { + continue; + } + + StringUtil.append(sb, t.getName(), "\r\n"); + + for (StackTraceElement ste : t.getStackTrace()) + { + StringUtil.append(sb, ste.toString(), "\r\n"); + } + } + } + + sb.append("Packet Tp stack traces printed.\r\n"); + + return sb.toString(); + } + + public String getGeneralStats() + { + final StringBuilder sb = new StringBuilder(1000); + ThreadFactory tf = _generalThreadPool.getThreadFactory(); + + if (tf instanceof PriorityThreadFactory) + { + PriorityThreadFactory ptf = (PriorityThreadFactory) tf; + int count = ptf.getGroup().activeCount(); + Thread[] threads = new Thread[count + 2]; + ptf.getGroup().enumerate(threads); + StringUtil.append(sb, "General Thread Pool:\r\n" + "Tasks in the queue: ", String.valueOf(_generalThreadPool.getQueue().size()), "\r\n" + "Showing threads stack trace:\r\n" + "There should be ", String.valueOf(count), " Threads\r\n"); + + for (Thread t : threads) + { + if (t == null) + { + continue; + } + + StringUtil.append(sb, t.getName(), "\r\n"); + + for (StackTraceElement ste : t.getStackTrace()) + { + StringUtil.append(sb, ste.toString(), "\r\n"); + } + } + } + + sb.append("Packet Tp stack traces printed.\r\n"); + + return sb.toString(); + } + + protected class PurgeTask implements Runnable + { + @Override + public void run() + { + _effectsScheduledThreadPool.purge(); + _generalScheduledThreadPool.purge(); + _aiScheduledThreadPool.purge(); + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final ThreadPoolManager _instance = new ThreadPoolManager(); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/TradeController.java b/L2J_Server/java/com/l2jserver/gameserver/TradeController.java new file mode 100644 index 00000000..0507ec5c --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/TradeController.java @@ -0,0 +1,330 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.datatables.ItemTable; +import com.l2jserver.gameserver.model.L2TradeList; +import com.l2jserver.gameserver.model.L2TradeList.L2TradeItem; + +/** + * This class ... + * @version $Revision: 1.5.4.13 $ $Date: 2005/04/06 16:13:38 $ + */ +public class TradeController +{ + private static Logger _log = Logger.getLogger(TradeController.class.getName()); + + private int _nextListId; + private final Map _lists = new FastMap<>(); + + /** Task launching the function for restore count of Item (Clan Hall) */ + /* + * public class RestoreCount implements Runnable { private int _timer; public RestoreCount(int time) { _timer = time; } public void run() { restoreCount(_timer); dataTimerSave(_timer); ThreadPoolManager.getInstance().scheduleGeneral(new RestoreCount(_timer), (long)_timer*60*60*1000); } } + */ + + public static TradeController getInstance() + { + return SingletonHolder._instance; + } + + private TradeController() + { + _lists.clear(); + Connection con = null; + + /* + * Initialize Shop buylist + */ + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement1 = con.prepareStatement("SELECT shop_id, npc_id FROM merchant_shopids"); + ResultSet rset1 = statement1.executeQuery(); + + int itemId, price, maxCount, currentCount, time; + long saveTimer; + PreparedStatement statement = con.prepareStatement("SELECT item_id, price, shop_id, " + L2DatabaseFactory.getInstance().safetyString("order") + ", count, currentCount, time, savetimer FROM merchant_buylists WHERE shop_id=? ORDER BY " + L2DatabaseFactory.getInstance().safetyString("order") + " ASC"); + while (rset1.next()) + { + statement.setString(1, String.valueOf(rset1.getInt("shop_id"))); + ResultSet rset = statement.executeQuery(); + statement.clearParameters(); + + int shopId = rset1.getInt("shop_id"); + L2TradeList buy1 = new L2TradeList(shopId); + + while (rset.next()) + { + itemId = rset.getInt("item_id"); + price = rset.getInt("price"); + maxCount = rset.getInt("count"); + currentCount = rset.getInt("currentCount"); + time = rset.getInt("time"); + saveTimer = rset.getLong("saveTimer"); + + L2TradeItem item = new L2TradeItem(shopId, itemId); + if (ItemTable.getInstance().getTemplate(itemId) == null) + { + _log.warning("Skipping itemId: " + itemId + " on buylistId: " + buy1.getListId() + ", missing data for that item."); + continue; + } + + if (price <= -1) + { + price = ItemTable.getInstance().getTemplate(itemId).getReferencePrice(); + } + + if (Config.DEBUG) + { + // debug + double diff = ((double) (price)) / ItemTable.getInstance().getTemplate(itemId).getReferencePrice(); + if ((diff < 0.8) || (diff > 1.2)) + { + _log.severe("PRICING DEBUG: TradeListId: " + buy1.getListId() + " - ItemId: " + itemId + " (" + ItemTable.getInstance().getTemplate(itemId).getName() + ") diff: " + diff + " - Price: " + price + " - Reference: " + ItemTable.getInstance().getTemplate(itemId).getReferencePrice()); + } + } + + item.setPrice(price); + + item.setRestoreDelay(time); + item.setNextRestoreTime(saveTimer); + item.setMaxCount(maxCount); + + if (currentCount > -1) + { + item.setCurrentCount(currentCount); + } + else + { + item.setCurrentCount(maxCount); + } + + buy1.addItem(item); + } + + buy1.setNpcId(rset1.getString("npc_id")); + _lists.put(buy1.getListId(), buy1); + _nextListId = Math.max(_nextListId, buy1.getListId() + 1); + + rset.close(); + } + statement.close(); + rset1.close(); + statement1.close(); + + _log.info("TradeController: Loaded " + _lists.size() + " Buylists."); + } + catch (Exception e) + { + // problem with initializing spawn, go to next one + _log.log(Level.WARNING, "TradeController: Buylists could not be initialized: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + + /* + * If enabled, initialize the custom buylist + */ + if (Config.CUSTOM_MERCHANT_TABLES) + { + try + { + int initialSize = _lists.size(); + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement1 = con.prepareStatement("SELECT shop_id, npc_id FROM custom_merchant_shopids"); + ResultSet rset1 = statement1.executeQuery(); + + int itemId, price, maxCount, currentCount, time; + long saveTimer; + PreparedStatement statement = con.prepareStatement("SELECT item_id, price, shop_id, " + L2DatabaseFactory.getInstance().safetyString("order") + ", count, currentCount, time, savetimer FROM custom_merchant_buylists WHERE shop_id=? ORDER BY " + L2DatabaseFactory.getInstance().safetyString("order") + " ASC"); + while (rset1.next()) + { + statement.setString(1, String.valueOf(rset1.getInt("shop_id"))); + ResultSet rset = statement.executeQuery(); + statement.clearParameters(); + + int shopId = rset1.getInt("shop_id"); + L2TradeList buy1 = new L2TradeList(shopId); + + while (rset.next()) + { + itemId = rset.getInt("item_id"); + price = rset.getInt("price"); + maxCount = rset.getInt("count"); + currentCount = rset.getInt("currentCount"); + time = rset.getInt("time"); + saveTimer = rset.getLong("saveTimer"); + + L2TradeItem item = new L2TradeItem(shopId, itemId); + if (ItemTable.getInstance().getTemplate(itemId) == null) + { + _log.warning("Skipping itemId: " + itemId + " on buylistId: " + buy1.getListId() + ", missing data for that item."); + continue; + } + + if (price <= -1) + { + price = ItemTable.getInstance().getTemplate(itemId).getReferencePrice(); + } + + if (Config.DEBUG) + { + // debug + double diff = ((double) (price)) / ItemTable.getInstance().getTemplate(itemId).getReferencePrice(); + if ((diff < 0.8) || (diff > 1.2)) + { + _log.severe("PRICING DEBUG: TradeListId: " + buy1.getListId() + " - ItemId: " + itemId + " (" + ItemTable.getInstance().getTemplate(itemId).getName() + ") diff: " + diff + " - Price: " + price + " - Reference: " + ItemTable.getInstance().getTemplate(itemId).getReferencePrice()); + } + } + + item.setPrice(price); + + item.setRestoreDelay(time); + item.setNextRestoreTime(saveTimer); + item.setMaxCount(maxCount); + + if (currentCount > -1) + { + item.setCurrentCount(currentCount); + } + else + { + item.setCurrentCount(maxCount); + } + + buy1.addItem(item); + } + + buy1.setNpcId(rset1.getString("npc_id")); + _lists.put(buy1.getListId(), buy1); + _nextListId = Math.max(_nextListId, buy1.getListId() + 1); + + rset.close(); + } + statement.close(); + rset1.close(); + statement1.close(); + + _log.info("TradeController: Loaded " + (_lists.size() - initialSize) + " Custom Buylists."); + + } + catch (Exception e) + { + // problem with initializing spawn, go to next one + _log.log(Level.WARNING, "TradeController: Buylists could not be initialized: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + } + + public L2TradeList getBuyList(int listId) + { + return _lists.get(listId); + } + + public List getBuyListByNpcId(int npcId) + { + List lists = new FastList<>(); + Collection values = _lists.values(); + + for (L2TradeList list : values) + { + String tradeNpcId = list.getNpcId(); + if (tradeNpcId.startsWith("gm")) + { + continue; + } + if (npcId == Integer.parseInt(tradeNpcId)) + { + lists.add(list); + } + } + return lists; + } + + public void dataCountStore() + { + Connection con = null; + int listId; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("UPDATE merchant_buylists SET currentCount=? WHERE item_id=? AND shop_id=?"); + for (L2TradeList list : _lists.values()) + { + if (list.hasLimitedStockItem()) + { + listId = list.getListId(); + + for (L2TradeItem item : list.getItems()) + { + long currentCount; + if (item.hasLimitedStock() && ((currentCount = item.getCurrentCount()) < item.getMaxCount())) + { + statement.setLong(1, currentCount); + statement.setInt(2, item.getItemId()); + statement.setInt(3, listId); + statement.executeUpdate(); + statement.clearParameters(); + } + } + } + } + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "TradeController: Could not store Count Item: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * @return + */ + public synchronized int getNextId() + { + return _nextListId++; + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final TradeController _instance = new TradeController(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/AbstractAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/AbstractAI.java new file mode 100644 index 00000000..fbc2c8fc --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/AbstractAI.java @@ -0,0 +1,896 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE; + +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.gameserver.GameTimeController; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.AutoAttackStart; +import com.l2jserver.gameserver.network.serverpackets.AutoAttackStop; +import com.l2jserver.gameserver.network.serverpackets.Die; +import com.l2jserver.gameserver.network.serverpackets.MoveToLocation; +import com.l2jserver.gameserver.network.serverpackets.MoveToPawn; +import com.l2jserver.gameserver.network.serverpackets.StopMove; +import com.l2jserver.gameserver.network.serverpackets.StopRotation; +import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager; + +/** + * Mother class of all objects AI in the world.
    + *
    + * AbastractAI :
    + *
    + *
  • L2CharacterAI

  • + *
    + */ +abstract class AbstractAI implements Ctrl +{ + protected static final Logger _log = Logger.getLogger(AbstractAI.class.getName()); + + class FollowTask implements Runnable + { + protected int _range = 70; + + public FollowTask() + { + } + + public FollowTask(int range) + { + _range = range; + } + + @Override + public void run() + { + try + { + if (_followTask == null) + { + return; + } + + L2Character followTarget = _followTarget; // copy to prevent NPE + if (followTarget == null) + { + if (_actor instanceof L2Summon) + { + ((L2Summon) _actor).setFollowStatus(false); + } + setIntention(AI_INTENTION_IDLE); + return; + } + + if (!_actor.isInsideRadius(followTarget, _range, true, false)) + { + if (!_actor.isInsideRadius(followTarget, 3000, true, false)) + { + // if the target is too far (maybe also teleported) + if (_actor instanceof L2Summon) + { + ((L2Summon) _actor).setFollowStatus(false); + } + + setIntention(AI_INTENTION_IDLE); + return; + } + + moveToPawn(followTarget, _range); + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + } + } + } + + /** The character that this AI manages */ + protected final L2Character _actor; + + /** An accessor for private methods of the actor */ + protected final L2Character.AIAccessor _accessor; + + /** Current long-term intention */ + protected CtrlIntention _intention = AI_INTENTION_IDLE; + /** Current long-term intention parameter */ + protected Object _intentionArg0 = null; + /** Current long-term intention parameter */ + protected Object _intentionArg1 = null; + + /** Flags about client's state, in order to know which messages to send */ + protected volatile boolean _clientMoving; + /** Flags about client's state, in order to know which messages to send */ + protected volatile boolean _clientAutoAttacking; + /** Flags about client's state, in order to know which messages to send */ + protected int _clientMovingToPawnOffset; + + /** Different targets this AI maintains */ + private L2Object _target; + private L2Character _castTarget; + protected L2Character _attackTarget; + protected L2Character _followTarget; + + /** The skill we are currently casting by INTENTION_CAST */ + L2Skill _skill; + + /** Different internal state flags */ + private int _moveToPawnTimeout; + + protected Future _followTask = null; + private static final int FOLLOW_INTERVAL = 1000; + private static final int ATTACK_FOLLOW_INTERVAL = 500; + + /** + * Constructor of AbstractAI.
    + *
    + * @param accessor The AI accessor of the L2Character + */ + protected AbstractAI(L2Character.AIAccessor accessor) + { + _accessor = accessor; + + // Get the L2Character managed by this Accessor AI + _actor = accessor.getActor(); + } + + /** + * Return the L2Character managed by this Accessor AI.
    + *
    + */ + @Override + public L2Character getActor() + { + return _actor; + } + + /** + * Return the current Intention.
    + *
    + */ + @Override + public CtrlIntention getIntention() + { + return _intention; + } + + protected void setCastTarget(L2Character target) + { + _castTarget = target; + } + + /** + * Return the current cast target.
    + *
    + */ + public L2Character getCastTarget() + { + return _castTarget; + } + + protected void setAttackTarget(L2Character target) + { + _attackTarget = target; + } + + /** + * Return current attack target.
    + *
    + */ + @Override + public L2Character getAttackTarget() + { + return _attackTarget; + } + + /** + * Set the Intention of this AbstractAI.
    + *
    + * Caution : This method is USED by AI classes
    + *
    + * Overridden in :
    + * L2AttackableAI : Create an AI Task executed every 1s (if necessary)
    + * L2PlayerAI : Stores the current AI intention parameters to later restore it if necessary
    + *
    + * @param intention The new Intention to set to the AI + * @param arg0 The first parameter of the Intention + * @param arg1 The second parameter of the Intention + */ + synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1) + { + /* + * if (Config.DEBUG) _log.warning("AbstractAI: changeIntention -> " + intention + " " + arg0 + " " + arg1); + */ + + _intention = intention; + _intentionArg0 = arg0; + _intentionArg1 = arg1; + } + + /** + * Launch the L2CharacterAI onIntention method corresponding to the new Intention.
    + *
    + * Caution : Stop the FOLLOW mode if necessary
    + *
    + * @param intention The new Intention to set to the AI + */ + @Override + public final void setIntention(CtrlIntention intention) + { + setIntention(intention, null, null); + } + + /** + * Launch the L2CharacterAI onIntention method corresponding to the new Intention.
    + *
    + * Caution : Stop the FOLLOW mode if necessary
    + *
    + * @param intention The new Intention to set to the AI + * @param arg0 The first parameter of the Intention (optional target) + */ + @Override + public final void setIntention(CtrlIntention intention, Object arg0) + { + setIntention(intention, arg0, null); + } + + /** + * Launch the L2CharacterAI onIntention method corresponding to the new Intention.
    + *
    + * Caution : Stop the FOLLOW mode if necessary
    + *
    + * @param intention The new Intention to set to the AI + * @param arg0 The first parameter of the Intention (optional target) + * @param arg1 The second parameter of the Intention (optional target) + */ + /* + * public final void informAIIntention(CtrlIntention intent, Object arg0) { ThreadPoolManager.getInstance().executeAi(new InformAIMsg(this, intent, arg0)); } public final void informAIIntention(CtrlIntention intent) { ThreadPoolManager.getInstance().executeAi(new InformAIMsg(this, intent, + * null)); } public class InformAIMsg implements Runnable { private AbstractAI _ai; private CtrlIntention _intent; private Object _arg0; public InformAIMsg(AbstractAI ai, CtrlIntention intention, Object arg0) { _ai=ai; _intent = intention; _arg0 = arg0; } public final void run() { + * _ai.setIntention(_intent, _arg0, null); } } + */ + @Override + public final void setIntention(CtrlIntention intention, Object arg0, Object arg1) + { + /* + * if (Config.DEBUG) _log.warning("AbstractAI: setIntention -> " + intention + " " + arg0 + " " + arg1); + */ + + // Stop the follow mode if necessary + if ((intention != AI_INTENTION_FOLLOW) && (intention != AI_INTENTION_ATTACK)) + { + stopFollow(); + } + + // Launch the onIntention method of the L2CharacterAI corresponding to the new Intention + switch (intention) + { + case AI_INTENTION_IDLE: + onIntentionIdle(); + break; + case AI_INTENTION_ACTIVE: + onIntentionActive(); + break; + case AI_INTENTION_REST: + onIntentionRest(); + break; + case AI_INTENTION_ATTACK: + onIntentionAttack((L2Character) arg0); + break; + case AI_INTENTION_CAST: + onIntentionCast((L2Skill) arg0, (L2Object) arg1); + break; + case AI_INTENTION_MOVE_TO: + onIntentionMoveTo((L2CharPosition) arg0); + break; + case AI_INTENTION_FOLLOW: + onIntentionFollow((L2Character) arg0); + break; + case AI_INTENTION_PICK_UP: + onIntentionPickUp((L2Object) arg0); + break; + case AI_INTENTION_INTERACT: + onIntentionInteract((L2Object) arg0); + break; + } + } + + /** + * Launch the L2CharacterAI onEvt method corresponding to the Event.
    + *
    + * Caution : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)
    + *
    + * @param evt The event whose the AI must be notified + */ + @Override + public final void notifyEvent(CtrlEvent evt) + { + notifyEvent(evt, null, null); + } + + /** + * Launch the L2CharacterAI onEvt method corresponding to the Event.
    + *
    + * Caution : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)
    + *
    + * @param evt The event whose the AI must be notified + * @param arg0 The first parameter of the Event (optional target) + */ + @Override + public final void notifyEvent(CtrlEvent evt, Object arg0) + { + notifyEvent(evt, arg0, null); + } + + /* + * public final void informAIEvent(CtrlEvent evt) { ThreadPoolManager.getInstance().executeAi(new InformAIEvent(this, evt, null, null)); } public final void informAIEvent(CtrlEvent evt, Object arg0) { ThreadPoolManager.getInstance().executeAi(new InformAIEvent(this, evt, arg0, null)); } public + * final void informAIEvent(CtrlEvent evt, Object arg0, Object arg1) { ThreadPoolManager.getInstance().executeAi(new InformAIEvent(this, evt, arg0, arg1)); } public class InformAIEvent implements Runnable { private AbstractAI _ai; private CtrlEvent _evt; private Object _arg0, _arg1; public + * InformAIEvent(AbstractAI ai, CtrlEvent evt, Object arg0, Object arg1) { _ai=ai; _evt = evt; _arg0 = arg0; _arg1 = arg1; } public final void run() { _ai.notifyEvent(_evt, _arg0, _arg1); } } + */ + /** + * Launch the L2CharacterAI onEvt method corresponding to the Event.
    + *
    + * Caution : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)
    + *
    + * @param evt The event whose the AI must be notified + * @param arg0 The first parameter of the Event (optional target) + * @param arg1 The second parameter of the Event (optional target) + */ + @Override + public final void notifyEvent(CtrlEvent evt, Object arg0, Object arg1) + { + if ((!_actor.isVisible() && !_actor.isTeleporting()) || !_actor.hasAI()) + { + return; + } + + /* + * if (Config.DEBUG) _log.warning("AbstractAI: notifyEvent -> " + evt + " " + arg0 + " " + arg1); + */ + + switch (evt) + { + case EVT_THINK: + onEvtThink(); + break; + case EVT_ATTACKED: + onEvtAttacked((L2Character) arg0); + break; + case EVT_AGGRESSION: + onEvtAggression((L2Character) arg0, ((Number) arg1).intValue()); + break; + case EVT_STUNNED: + onEvtStunned((L2Character) arg0); + break; + case EVT_PARALYZED: + onEvtParalyzed((L2Character) arg0); + break; + case EVT_SLEEPING: + onEvtSleeping((L2Character) arg0); + break; + case EVT_ROOTED: + onEvtRooted((L2Character) arg0); + break; + case EVT_CONFUSED: + onEvtConfused((L2Character) arg0); + break; + case EVT_MUTED: + onEvtMuted((L2Character) arg0); + break; + case EVT_EVADED: + onEvtEvaded((L2Character) arg0); + break; + case EVT_READY_TO_ACT: + if (!_actor.isCastingNow() && !_actor.isCastingSimultaneouslyNow()) + { + onEvtReadyToAct(); + } + break; + case EVT_USER_CMD: + onEvtUserCmd(arg0, arg1); + break; + case EVT_ARRIVED: + // happens e.g. from stopmove but we don't process it if we're casting + if (!_actor.isCastingNow() && !_actor.isCastingSimultaneouslyNow()) + { + onEvtArrived(); + } + break; + case EVT_ARRIVED_REVALIDATE: + // this is disregarded if the char is not moving any more + if (_actor.isMoving()) + { + onEvtArrivedRevalidate(); + } + break; + case EVT_ARRIVED_BLOCKED: + onEvtArrivedBlocked((L2CharPosition) arg0); + break; + case EVT_FORGET_OBJECT: + onEvtForgetObject((L2Object) arg0); + break; + case EVT_CANCEL: + onEvtCancel(); + break; + case EVT_DEAD: + onEvtDead(); + break; + case EVT_FAKE_DEATH: + onEvtFakeDeath(); + break; + case EVT_FINISH_CASTING: + onEvtFinishCasting(); + break; + } + } + + protected abstract void onIntentionIdle(); + + protected abstract void onIntentionActive(); + + protected abstract void onIntentionRest(); + + protected abstract void onIntentionAttack(L2Character target); + + protected abstract void onIntentionCast(L2Skill skill, L2Object target); + + protected abstract void onIntentionMoveTo(L2CharPosition destination); + + protected abstract void onIntentionFollow(L2Character target); + + protected abstract void onIntentionPickUp(L2Object item); + + protected abstract void onIntentionInteract(L2Object object); + + protected abstract void onEvtThink(); + + protected abstract void onEvtAttacked(L2Character attacker); + + protected abstract void onEvtAggression(L2Character target, int aggro); + + protected abstract void onEvtStunned(L2Character attacker); + + protected abstract void onEvtParalyzed(L2Character attacker); + + protected abstract void onEvtSleeping(L2Character attacker); + + protected abstract void onEvtRooted(L2Character attacker); + + protected abstract void onEvtConfused(L2Character attacker); + + protected abstract void onEvtMuted(L2Character attacker); + + protected abstract void onEvtEvaded(L2Character attacker); + + protected abstract void onEvtReadyToAct(); + + protected abstract void onEvtUserCmd(Object arg0, Object arg1); + + protected abstract void onEvtArrived(); + + protected abstract void onEvtArrivedRevalidate(); + + protected abstract void onEvtArrivedBlocked(L2CharPosition blocked_at_pos); + + protected abstract void onEvtForgetObject(L2Object object); + + protected abstract void onEvtCancel(); + + protected abstract void onEvtDead(); + + protected abstract void onEvtFakeDeath(); + + protected abstract void onEvtFinishCasting(); + + /** + * Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor.
    + *
    + * Caution : Low level function, used by AI subclasses
    + *
    + */ + protected void clientActionFailed() + { + if (_actor instanceof L2PcInstance) + { + _actor.sendPacket(ActionFailed.STATIC_PACKET); + } + } + + /** + * Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast).
    + *
    + * Caution : Low level function, used by AI subclasses
    + *
    + */ + protected void moveToPawn(L2Object pawn, int offset) + { + // Check if actor can move + if (!_actor.isMovementDisabled()) + { + if (offset < 10) + { + offset = 10; + } + + // prevent possible extra calls to this function (there is none?), + // also don't send movetopawn packets too often + boolean sendPacket = true; + if (_clientMoving && (_target == pawn)) + { + if (_clientMovingToPawnOffset == offset) + { + if (GameTimeController.getGameTicks() < _moveToPawnTimeout) + { + return; + } + sendPacket = false; + } + else if (_actor.isOnGeodataPath()) + { + // minimum time to calculate new route is 2 seconds + if (GameTimeController.getGameTicks() < (_moveToPawnTimeout + 10)) + { + return; + } + } + } + + // Set AI movement data + _clientMoving = true; + _clientMovingToPawnOffset = offset; + _target = pawn; + _moveToPawnTimeout = GameTimeController.getGameTicks(); + _moveToPawnTimeout += 1000 / GameTimeController.MILLIS_IN_TICK; + + if ((pawn == null) || (_accessor == null)) + { + return; + } + + // Calculate movement data for a move to location action and add the actor to movingObjects of GameTimeController + _accessor.moveTo(pawn.getX(), pawn.getY(), pawn.getZ(), offset); + + if (!_actor.isMoving()) + { + _actor.sendPacket(ActionFailed.STATIC_PACKET); + return; + } + + // Send a Server->Client packet MoveToPawn/CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers + if (pawn instanceof L2Character) + { + if (_actor.isOnGeodataPath()) + { + _actor.broadcastPacket(new MoveToLocation(_actor)); + _clientMovingToPawnOffset = 0; + } + else if (sendPacket) + { + _actor.broadcastPacket(new MoveToPawn(_actor, (L2Character) pawn, offset)); + } + } + else + { + _actor.broadcastPacket(new MoveToLocation(_actor)); + } + } + else + { + _actor.sendPacket(ActionFailed.STATIC_PACKET); + } + } + + /** + * Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast).
    + *
    + * Caution : Low level function, used by AI subclasses
    + *
    + */ + protected void moveTo(int x, int y, int z) + { + // Chek if actor can move + if (!_actor.isMovementDisabled()) + { + // Set AI movement data + _clientMoving = true; + _clientMovingToPawnOffset = 0; + + // Calculate movement data for a move to location action and add the actor to movingObjects of GameTimeController + _accessor.moveTo(x, y, z); + + // Send a Server->Client packet CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers + MoveToLocation msg = new MoveToLocation(_actor); + _actor.broadcastPacket(msg); + + } + else + { + _actor.sendPacket(ActionFailed.STATIC_PACKET); + } + } + + /** + * Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast).
    + *
    + * Caution : Low level function, used by AI subclasses
    + *
    + */ + protected void clientStopMoving(L2CharPosition pos) + { + /* + * if (Config.DEBUG) _log.warning("clientStopMoving();"); + */ + + // Stop movement of the L2Character + if (_actor.isMoving()) + { + _accessor.stopMove(pos); + } + + _clientMovingToPawnOffset = 0; + + if (_clientMoving || (pos != null)) + { + _clientMoving = false; + + // Send a Server->Client packet StopMove to the actor and all L2PcInstance in its _knownPlayers + StopMove msg = new StopMove(_actor); + _actor.broadcastPacket(msg); + + if (pos != null) + { + // Send a Server->Client packet StopRotation to the actor and all L2PcInstance in its _knownPlayers + StopRotation sr = new StopRotation(_actor.getObjectId(), pos.heading, 0); + _actor.sendPacket(sr); + _actor.broadcastPacket(sr); + } + } + } + + // Client has already arrived to target, no need to force StopMove packet + protected void clientStoppedMoving() + { + if (_clientMovingToPawnOffset > 0) // movetoPawn needs to be stopped + { + _clientMovingToPawnOffset = 0; + StopMove msg = new StopMove(_actor); + _actor.broadcastPacket(msg); + } + _clientMoving = false; + } + + public boolean isAutoAttacking() + { + return _clientAutoAttacking; + } + + public void setAutoAttacking(boolean isAutoAttacking) + { + if (_actor instanceof L2Summon) + { + L2Summon summon = (L2Summon) _actor; + if (summon.getOwner() != null) + { + summon.getOwner().getAI().setAutoAttacking(isAutoAttacking); + } + return; + } + _clientAutoAttacking = isAutoAttacking; + } + + /** + * Start the actor Auto Attack client side by sending Server->Client packet AutoAttackStart (broadcast).
    + *
    + * Caution : Low level function, used by AI subclasses
    + *
    + */ + public void clientStartAutoAttack() + { + if (_actor instanceof L2Summon) + { + L2Summon summon = (L2Summon) _actor; + if (summon.getOwner() != null) + { + summon.getOwner().getAI().clientStartAutoAttack(); + } + return; + } + if (!isAutoAttacking()) + { + if ((_actor instanceof L2PcInstance) && (((L2PcInstance) _actor).getPet() != null)) + { + ((L2PcInstance) _actor).getPet().broadcastPacket(new AutoAttackStart(((L2PcInstance) _actor).getPet().getObjectId())); + } + // Send a Server->Client packet AutoAttackStart to the actor and all L2PcInstance in its _knownPlayers + _actor.broadcastPacket(new AutoAttackStart(_actor.getObjectId())); + setAutoAttacking(true); + } + AttackStanceTaskManager.getInstance().addAttackStanceTask(_actor); + } + + /** + * Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast).
    + *
    + * Caution : Low level function, used by AI subclasses
    + *
    + */ + public void clientStopAutoAttack() + { + if (_actor instanceof L2Summon) + { + L2Summon summon = (L2Summon) _actor; + if (summon.getOwner() != null) + { + summon.getOwner().getAI().clientStopAutoAttack(); + } + return; + } + if (_actor instanceof L2PcInstance) + { + if (!AttackStanceTaskManager.getInstance().getAttackStanceTask(_actor) && isAutoAttacking()) + { + AttackStanceTaskManager.getInstance().addAttackStanceTask(_actor); + } + } + else if (isAutoAttacking()) + { + _actor.broadcastPacket(new AutoAttackStop(_actor.getObjectId())); + setAutoAttacking(false); + } + } + + /** + * Kill the actor client side by sending Server->Client packet AutoAttackStop, StopMove/StopRotation, Die (broadcast).
    + *
    + * Caution : Low level function, used by AI subclasses
    + *
    + */ + protected void clientNotifyDead() + { + // Send a Server->Client packet Die to the actor and all L2PcInstance in its _knownPlayers + Die msg = new Die(_actor); + _actor.broadcastPacket(msg); + + // Init AI + _intention = AI_INTENTION_IDLE; + _target = null; + _castTarget = null; + _attackTarget = null; + + // Cancel the follow task if necessary + stopFollow(); + } + + /** + * Update the state of this actor client side by sending Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance player.
    + *
    + * Caution : Low level function, used by AI subclasses
    + *
    + * @param player The L2PcIstance to notify with state of this L2Character + */ + public void describeStateToPlayer(L2PcInstance player) + { + if (_clientMoving) + { + if ((_clientMovingToPawnOffset != 0) && (_followTarget != null)) + { + // Send a Server->Client packet MoveToPawn to the actor and all L2PcInstance in its _knownPlayers + MoveToPawn msg = new MoveToPawn(_actor, _followTarget, _clientMovingToPawnOffset); + player.sendPacket(msg); + } + else + { + // Send a Server->Client packet CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers + MoveToLocation msg = new MoveToLocation(_actor); + player.sendPacket(msg); + } + } + } + + /** + * Create and Launch an AI Follow Task to execute every 1s.
    + *
    + * @param target The L2Character to follow + */ + public synchronized void startFollow(L2Character target) + { + if (_followTask != null) + { + _followTask.cancel(false); + _followTask = null; + } + + // Create and Launch an AI Follow Task to execute every 1s + _followTarget = target; + _followTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new FollowTask(), 5, FOLLOW_INTERVAL); + } + + /** + * Create and Launch an AI Follow Task to execute every 0.5s, following at specified range.
    + *
    + * @param target The L2Character to follow + */ + public synchronized void startFollow(L2Character target, int range) + { + if (_followTask != null) + { + _followTask.cancel(false); + _followTask = null; + } + + _followTarget = target; + _followTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new FollowTask(range), 5, ATTACK_FOLLOW_INTERVAL); + } + + /** + * Stop an AI Follow Task.
    + *
    + */ + public synchronized void stopFollow() + { + if (_followTask != null) + { + // Stop the Follow Task + _followTask.cancel(false); + _followTask = null; + } + _followTarget = null; + } + + protected L2Character getFollowTarget() + { + return _followTarget; + } + + protected L2Object getTarget() + { + return _target; + } + + protected void setTarget(L2Object target) + { + _target = target; + } + + /** + * Stop all Ai tasks and futures. + */ + public void stopAITask() + { + stopFollow(); + } + + @Override + public String toString() + { + if (_actor == null) + { + return "Actor: null"; + } + return "Actor: " + _actor; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/Ctrl.java b/L2J_Server/java/com/l2jserver/gameserver/ai/Ctrl.java new file mode 100644 index 00000000..d5a19005 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/Ctrl.java @@ -0,0 +1,55 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import com.l2jserver.gameserver.model.actor.L2Character; + +/** + * Interface of AI and client state. To correctly send messages to client we need it's state. For example, if we've sent 'StartAutoAttack' message, we need to send 'StopAutoAttack' message before any other action. Or if we've sent 'MoveToPawn', we need to send 'StopMove' when the movement of a + * character is canceled (by Root spell or any other reason). Thus, we need to know the state of client, i.e. which messages we've sent and how the client will show the scene. Close to this task is the task of AI. If a player's character is attacking a mob, his ATTACK may be iterrupted by an event, + * that temporary disable attacking. But when the possibility to ATTACK will be enabled, the character must continue the ATTACK. For mobs it may be more complex, since we want them to decide when to use magic, or when to follow the player for physical combat, or when to escape, to help another mob, + * etc. This interface is hiding complexity of server<->client interaction and multiple states of a character. It allows to set a desired, simple "wish" of a character, and the implementation of this interface will take care about the rest. The goal of a character may be like "ATTACK", "random walk" + * and so on. To reach the goal inplementation will split it into several small actions, several steps (possibly repeatable). Like "run to target" then "hit it", then if target is not dead - repeat. This flow of simplier steps may be interrupted by incoming events. Like a character's movement was + * disabled (by Root spell, for instance). Depending on character's ability AI may choose to wait, or to use magic ATTACK and so on. Additionally incoming events are compared with client's state of the character, and required network messages are sent to client's, i.e. if we have incoming event that + * character's movement was disabled, it causes changing if its behavour, and if client's state for the character is "moving" we send messages to clients to stop the avatar/mob. + */ +public interface Ctrl +{ + + /** the character this AI serves */ + L2Character getActor(); + + /** get current intention */ + CtrlIntention getIntention(); + + /** get current ATTACK target */ + L2Character getAttackTarget(); + + /** Set general state/intention for AI, with optional data */ + void setIntention(CtrlIntention intention); + + void setIntention(CtrlIntention intention, Object arg0); + + void setIntention(CtrlIntention intention, Object arg0, Object arg1); + + /** + * Event, that notifies about previous step result, or user command, that does not change current general intention + */ + void notifyEvent(CtrlEvent evt); + + void notifyEvent(CtrlEvent evt, Object arg0); + + void notifyEvent(CtrlEvent evt, Object arg0, Object arg1); +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/CtrlEvent.java b/L2J_Server/java/com/l2jserver/gameserver/ai/CtrlEvent.java new file mode 100644 index 00000000..07c039f4 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/CtrlEvent.java @@ -0,0 +1,82 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +/** + * This class contains an enum of each possibles evenements that can happen on an AI character. + */ + +public enum CtrlEvent +{ + /** + * Something has changed, usually a previous step has being completed or maybe was completed, the AI must thing on next action + */ + EVT_THINK, + /** + * The actor was attacked. This event comes each time a physical or magical attack was done on the actor. NPC may start attack in responce, or ignore this event if they already attack someone, or change target and so on. + */ + EVT_ATTACKED, + /** Increase/decrease aggression towards a target, or reduce global aggression if target is null */ + EVT_AGGRESSION, + /** Actor is in stun state */ + EVT_STUNNED, + /** Actor is paralyzed or petrified */ + EVT_PARALYZED, + /** Actor starts/stops sleeping */ + EVT_SLEEPING, + /** Actor is in rooted state (cannot move) */ + EVT_ROOTED, + /** Actor evaded hit **/ + EVT_EVADED, + /** + * An event that previous action was completed. The action may be an attempt to physically/magically hit an enemy, or an action that discarded attack attempt has finished. + */ + EVT_READY_TO_ACT, + /** + * User's command, like using a combat magic or changing weapon, etc. The command is not intended to change final goal + */ + EVT_USER_CMD, + /** + * The actor arrived to assigned location, or it's a time to modify movement destination (follow, interact, random move and others intentions). + */ + EVT_ARRIVED, + /** + * The actor arrived to an intermidiate point, and needs revalidate destination. This is sent when follow/move to pawn if destination is far away. + */ + EVT_ARRIVED_REVALIDATE, + /** The actor cannot move anymore. */ + EVT_ARRIVED_BLOCKED, + /** Forgets an object (if it's used as attack target, follow target and so on */ + EVT_FORGET_OBJECT, + /** + * Attempt to cancel current step execution, but not change the intention. For example, the actor was putted into a stun, so it's current attack or movement has to be canceled. But after the stun state expired, the actor may try to attack again. Another usage for CANCEL is a user's attempt to + * cancel a cast/bow attack and so on. + */ + EVT_CANCEL, + /** The character is dead */ + EVT_DEAD, + /** The character looks like dead */ + EVT_FAKE_DEATH, + /** The character attack anyone randomly **/ + EVT_CONFUSED, + /** The character cannot cast spells anymore **/ + EVT_MUTED, + /** The character flee in randoms directions **/ + EVT_AFRAID, + /** The character finish casting **/ + EVT_FINISH_CASTING, + /** The character betrayed its master */ + EVT_BETRAYED +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/CtrlIntention.java b/L2J_Server/java/com/l2jserver/gameserver/ai/CtrlIntention.java new file mode 100644 index 00000000..7c7c7ee1 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/CtrlIntention.java @@ -0,0 +1,43 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +/** + * Enumaration of generic intentions of an NPC/PC, an intention may require several steps to be completed + */ + +public enum CtrlIntention +{ + /** Do nothing, disconnect AI of NPC if no players around */ + AI_INTENTION_IDLE, + /** Alerted state without goal : scan attackable targets, random walk, etc */ + AI_INTENTION_ACTIVE, + /** Rest (sit until attacked) */ + AI_INTENTION_REST, + /** + * Attack target (cast combat magic, go to target, combat), may be ignored, if target is locked on another character or a peacefull zone and so on + */ + AI_INTENTION_ATTACK, + /** Cast a spell, depending on the spell - may start or stop attacking */ + AI_INTENTION_CAST, + /** Just move to another location */ + AI_INTENTION_MOVE_TO, + /** Like move, but check target's movement and follow it */ + AI_INTENTION_FOLLOW, + /** PickUp and item, (got to item, pickup it, become idle */ + AI_INTENTION_PICK_UP, + /** Move to target, then interact */ + AI_INTENTION_INTERACT; +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2AirShipAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2AirShipAI.java new file mode 100644 index 00000000..67cf196f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2AirShipAI.java @@ -0,0 +1,73 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.actor.instance.L2AirShipInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.serverpackets.ExMoveToLocationAirShip; +import com.l2jserver.gameserver.network.serverpackets.ExStopMoveAirShip; + +/** + * @author DS + */ +public class L2AirShipAI extends L2VehicleAI +{ + public L2AirShipAI(L2AirShipInstance.AIAccessor accessor) + { + super(accessor); + } + + @Override + protected void moveTo(int x, int y, int z) + { + if (!_actor.isMovementDisabled()) + { + _clientMoving = true; + _accessor.moveTo(x, y, z); + _actor.broadcastPacket(new ExMoveToLocationAirShip(getActor())); + } + } + + @Override + protected void clientStopMoving(L2CharPosition pos) + { + if (_actor.isMoving()) + { + _accessor.stopMove(pos); + } + + if (_clientMoving || (pos != null)) + { + _clientMoving = false; + _actor.broadcastPacket(new ExStopMoveAirShip(getActor())); + } + } + + @Override + public void describeStateToPlayer(L2PcInstance player) + { + if (_clientMoving) + { + player.sendPacket(new ExMoveToLocationAirShip(getActor())); + } + } + + @Override + public L2AirShipInstance getActor() + { + return (L2AirShipInstance) _actor; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2AttackableAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2AttackableAI.java new file mode 100644 index 00000000..a3ed77e0 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2AttackableAI.java @@ -0,0 +1,2840 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE; + +import java.util.Collection; +import java.util.concurrent.Future; +import java.util.logging.Level; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.GameTimeController; +import com.l2jserver.gameserver.GeoData; +import com.l2jserver.gameserver.Territory; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.instancemanager.DimensionalRiftManager; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2Skill.SkillTargetType; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2FestivalMonsterInstance; +import com.l2jserver.gameserver.model.actor.instance.L2FriendlyMobInstance; +import com.l2jserver.gameserver.model.actor.instance.L2GrandBossInstance; +import com.l2jserver.gameserver.model.actor.instance.L2GuardInstance; +import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2RaidBossInstance; +import com.l2jserver.gameserver.model.actor.instance.L2RiftInvaderInstance; +import com.l2jserver.gameserver.model.quest.Quest; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate.AIType; +import com.l2jserver.gameserver.templates.skills.L2EffectType; +import com.l2jserver.gameserver.templates.skills.L2SkillType; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.Rnd; + +/** + * This class manages AI of L2Attackable.
    + *
    + */ +public class L2AttackableAI extends L2CharacterAI implements Runnable +{ + + // protected static final Logger _log = Logger.getLogger(L2AttackableAI.class.getName()); + + private static final int RANDOM_WALK_RATE = 30; // confirmed + // private static final int MAX_DRIFT_RANGE = 300; + private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min + + /** The L2Attackable AI task executed every 1s (call onEvtThink method) */ + private Future _aiTask; + + /** The delay after which the attacked is stopped */ + private int _attackTimeout; + + /** The L2Attackable aggro counter */ + private int _globalAggro; + + /** The flag used to indicate that a thinking action is in progress */ + private boolean _thinking; // to prevent recursive thinking + + private int timepass = 0; + private int chaostime = 0; + private final L2NpcTemplate _skillrender; + int lastBuffTick; + + /** + * Constructor of L2AttackableAI.
    + *
    + * @param accessor The AI accessor of the L2Character + */ + public L2AttackableAI(L2Character.AIAccessor accessor) + { + super(accessor); + _skillrender = NpcTable.getInstance().getTemplate(getActiveChar().getTemplate().npcId); + // _selfAnalysis.init(); + _attackTimeout = Integer.MAX_VALUE; + _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn + } + + @Override + public void run() + { + // Launch actions corresponding to the Event Think + onEvtThink(); + + } + + /** + * Return True if the target is autoattackable (depends on the actor type).
    + *
    + * Actor is a L2GuardInstance :
    + *
    + *
  • The target isn't a Folk or a Door
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • The L2PcInstance target has karma (=PK)
  • The L2MonsterInstance + * target is aggressive

  • + *
    + * Actor is a L2SiegeGuardInstance :
    + *
    + *
  • The target isn't a Folk or a Door
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • A siege is in progress
  • The L2PcInstance target isn't a Defender
  • + *
    + *
    + * Actor is a L2FriendlyMobInstance :
    + *
    + *
  • The target isn't a Folk, a Door or another L2Npc
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • The L2PcInstance target has karma (=PK)

  • + *
    + * Actor is a L2MonsterInstance :
    + *
    + *
  • The target isn't a Folk, a Door or another L2Npc
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • The actor is Aggressive

  • + *
    + * @param target The targeted L2Object + */ + private boolean autoAttackCondition(L2Character target) + { + if ((target == null) || (getActiveChar() == null)) + { + return false; + } + + L2Attackable me = getActiveChar(); + + // Check if the target isn't invulnerable + if (target.isInvul()) + { + // However EffectInvincible requires to check GMs specially + if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isGM()) + { + return false; + } + if ((target instanceof L2Summon) && ((L2Summon) target).getOwner().isGM()) + { + return false; + } + } + + // Check if the target isn't a Folk or a Door + if (target instanceof L2DoorInstance) + { + return false; + } + + // Check if the target isn't dead, is in the Aggro range and is at the same height + if (target.isAlikeDead() || ((target instanceof L2Playable) && !me.isInsideRadius(target, me.getAggroRange(), true, false))) + { + return false; + } + + // Check if the target is a L2PlayableInstance + if (target instanceof L2Playable) + { + // Check if the AI isn't a Raid Boss, can See Silent Moving players and the target isn't in silent move mode + if (!(me.isRaid()) && !(me.canSeeThroughSilentMove()) && ((L2Playable) target).isSilentMoving()) + { + return false; + } + } + + // Check if the target is a L2PcInstance + if (target instanceof L2PcInstance) + { + // Don't take the aggro if the GM has the access level below or equal to GM_DONT_TAKE_AGGRO + if (((L2PcInstance) target).isGM() && !((L2PcInstance) target).getAccessLevel().canTakeAggro()) + { + return false; + } + + // TODO: Ideally, autoattack condition should be called from the AI script. In that case, + // it should only implement the basic behaviors while the script will add more specific + // behaviors (like varka/ketra alliance, etc). Once implemented, remove specialized stuff + // from this location. (Fulminus) + + // Check if player is an ally (comparing mem addr) + if ("varka_silenos_clan".equals(me.getFactionId()) && ((L2PcInstance) target).isAlliedWithVarka()) + { + return false; + } + if ("ketra_orc_clan".equals(me.getFactionId()) && ((L2PcInstance) target).isAlliedWithKetra()) + { + return false; + } + // check if the target is within the grace period for JUST getting up from fake death + if (((L2PcInstance) target).isRecentFakeDeath()) + { + return false; + } + + if (target.isInParty() && target.getParty().isInDimensionalRift()) + { + byte riftType = target.getParty().getDimensionalRift().getType(); + byte riftRoom = target.getParty().getDimensionalRift().getCurrentRoom(); + + if ((me instanceof L2RiftInvaderInstance) && !DimensionalRiftManager.getInstance().getRoom(riftType, riftRoom).checkIfInZone(me.getX(), me.getY(), me.getZ())) + { + return false; + } + } + + // if (_selfAnalysis.cannotMoveOnLand && !target.isInsideZone(L2Character.ZONE_WATER)) + // return false; + } + + // Check if the target is a L2Summon + if (target instanceof L2Summon) + { + L2PcInstance owner = ((L2Summon) target).getOwner(); + if (owner != null) + { + // Don't take the aggro if the GM has the access level below or equal to GM_DONT_TAKE_AGGRO + if (owner.isGM() && (owner.isInvul() || !owner.getAccessLevel().canTakeAggro())) + { + return false; + } + // Check if player is an ally (comparing mem addr) + if ("varka_silenos_clan".equals(me.getFactionId()) && owner.isAlliedWithVarka()) + { + return false; + } + if ("ketra_orc_clan".equals(me.getFactionId()) && owner.isAlliedWithKetra()) + { + return false; + } + } + } + // Check if the actor is a L2GuardInstance + if (getActiveChar() instanceof L2GuardInstance) + { + + // Check if the L2PcInstance target has karma (=PK) + if ((target instanceof L2PcInstance) && (((L2PcInstance) target).getKarma() > 0)) + { + // Los Check + return GeoData.getInstance().canSeeTarget(me, target); + } + + // if (target instanceof L2Summon) + // return ((L2Summon)target).getKarma() > 0; + + // Check if the L2MonsterInstance target is aggressive + if ((target instanceof L2MonsterInstance) && Config.GUARD_ATTACK_AGGRO_MOB) + { + return (((L2MonsterInstance) target).isAggressive() && GeoData.getInstance().canSeeTarget(me, target)); + } + + return false; + } + else if (getActiveChar() instanceof L2FriendlyMobInstance) + { // the actor is a L2FriendlyMobInstance + + // Check if the target isn't another L2Npc + if (target instanceof L2Npc) + { + return false; + } + + // Check if the L2PcInstance target has karma (=PK) + if ((target instanceof L2PcInstance) && (((L2PcInstance) target).getKarma() > 0)) + { + return GeoData.getInstance().canSeeTarget(me, target); // Los Check + } + return false; + } + else + { + if (target instanceof L2Attackable) + { + if ((getActiveChar().getEnemyClan() == null) || (((L2Attackable) target).getClan() == null)) + { + return false; + } + + if (!target.isAutoAttackable(getActiveChar())) + { + return false; + } + + if (getActiveChar().getEnemyClan().equals(((L2Attackable) target).getClan())) + { + if (getActiveChar().isInsideRadius(target, getActiveChar().getEnemyRange(), false, false)) + { + return GeoData.getInstance().canSeeTarget(getActiveChar(), target); + } + return false; + } + if ((getActiveChar().getIsChaos() > 0) && me.isInsideRadius(target, getActiveChar().getIsChaos(), false, false)) + { + if ((getActiveChar().getFactionId() != null) && getActiveChar().getFactionId().equals(((L2Attackable) target).getFactionId())) + { + return false; + } + // Los Check + return GeoData.getInstance().canSeeTarget(me, target); + } + } + + if ((target instanceof L2Attackable) || (target instanceof L2Npc)) + { + return false; + } + + // depending on config, do not allow mobs to attack _new_ players in peacezones, + // unless they are already following those players from outside the peacezone. + if (!Config.ALT_MOB_AGRO_IN_PEACEZONE && target.isInsideZone(L2Character.ZONE_PEACE)) + { + return false; + } + + if (me.isChampion() && Config.L2JMOD_CHAMPION_PASSIVE) + { + return false; + } + + // Check if the actor is Aggressive + return (me.isAggressive() && GeoData.getInstance().canSeeTarget(me, target)); + } + } + + public void startAITask() + { + // If not idle - create an AI task (schedule onEvtThink repeatedly) + if (_aiTask == null) + { + _aiTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(this, 1000, 1000); + } + } + + @Override + public void stopAITask() + { + if (_aiTask != null) + { + _aiTask.cancel(false); + _aiTask = null; + } + super.stopAITask(); + } + + /** + * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
    + *
    + * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
    + *
    + * @param intention The new Intention to set to the AI + * @param arg0 The first parameter of the Intention + * @param arg1 The second parameter of the Intention + */ + @Override + synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1) + { + if ((intention == AI_INTENTION_IDLE) || (intention == AI_INTENTION_ACTIVE)) + { + // Check if actor is not dead + L2Attackable npc = getActiveChar(); + if (!npc.isAlikeDead()) + { + // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE + if (!npc.getKnownList().getKnownPlayers().isEmpty()) + { + intention = AI_INTENTION_ACTIVE; + } + else + { + if (npc.getSpawn() != null) + { + final int range = Config.MAX_DRIFT_RANGE; + if (!npc.isInsideRadius(npc.getSpawn().getLocx(), npc.getSpawn().getLocy(), npc.getSpawn().getLocz(), range + range, true, false)) + { + intention = AI_INTENTION_ACTIVE; + } + } + } + } + + if (intention == AI_INTENTION_IDLE) + { + // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE + super.changeIntention(AI_INTENTION_IDLE, null, null); + + // Stop AI task and detach AI from NPC + if (_aiTask != null) + { + _aiTask.cancel(true); + _aiTask = null; + } + + // Cancel the AI + _accessor.detachAI(); + + return; + } + } + + // Set the Intention of this L2AttackableAI to intention + super.changeIntention(intention, arg0, arg1); + + // If not idle - create an AI task (schedule onEvtThink repeatedly) + startAITask(); + } + + /** + * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
    + *
    + * @param target The L2Character to attack + */ + @Override + protected void onIntentionAttack(L2Character target) + { + // Calculate the attack timeout + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks(); + + // self and buffs + + if ((lastBuffTick + 30) < GameTimeController.getGameTicks()) + { + if (_skillrender.hasBuffSkill()) + { + for (L2Skill sk : _skillrender._buffskills) + { + if (cast(sk)) + { + break; + } + } + } + + lastBuffTick = GameTimeController.getGameTicks(); + } + + // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event + super.onIntentionAttack(target); + } + + private void thinkCast() + { + if (checkTargetLost(getCastTarget())) + { + setCastTarget(null); + return; + } + if (maybeMoveToPawn(getCastTarget(), _actor.getMagicalAttackRange(_skill))) + { + return; + } + clientStopMoving(null); + setIntention(AI_INTENTION_ACTIVE); + _accessor.doCast(_skill); + } + + /** + * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
    + *
    + * Actions :
    + *
    + *
  • Update every 1s the _globalAggro counter to come close to 0
  • If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
  • If the actor is a L2GuardInstance that can't attack, + * order to it to return to its home location
  • If the actor is a L2MonsterInstance that can't attack, order to it to random walk (1/100)

  • + *
    + */ + private void thinkActive() + { + L2Attackable npc = getActiveChar(); + + // Update every 1s the _globalAggro counter to come close to 0 + if (_globalAggro != 0) + { + if (_globalAggro < 0) + { + _globalAggro++; + } + else + { + _globalAggro--; + } + } + + // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate + // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10 + if (_globalAggro >= 0) + { + // Get all visible objects inside its Aggro Range + Collection objs = npc.getKnownList().getKnownObjects().values(); + // synchronized (npc.getKnownList().getKnownObjects()) + { + for (L2Object obj : objs) + { + if (!(obj instanceof L2Character)) + { + continue; + } + L2Character target = (L2Character) obj; + + /* + * Check to see if this is a festival mob spawn. If it is, then check to see if the aggro trigger is a festival participant...if so, move to attack it. + */ + if ((npc instanceof L2FestivalMonsterInstance) && (obj instanceof L2PcInstance)) + { + L2PcInstance targetPlayer = (L2PcInstance) obj; + + if (!(targetPlayer.isFestivalParticipant())) + { + continue; + } + } + + // TODO: The AI Script ought to handle aggro behaviors in onSee. Once implemented, aggro behaviors ought + // to be removed from here. (Fulminus) + // For each L2Character check if the target is autoattackable + if (autoAttackCondition(target)) // check aggression + { + // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList + int hating = npc.getHating(target); + + // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate + if (hating == 0) + { + npc.addDamageHate(target, 0, 0); + } + } + } + } + + // Chose a target from its aggroList + L2Character hated; + if (npc.isConfused()) + { + hated = getAttackTarget(); // effect handles selection + } + else + { + hated = npc.getMostHated(); + } + + // Order to the L2Attackable to attack the target + if ((hated != null) && !npc.isCoreAIDisabled()) + { + // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList + int aggro = npc.getHating(hated); + + if ((aggro + _globalAggro) > 0) + { + // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance + if (!npc.isRunning()) + { + npc.setRunning(); + } + + // Set the AI Intention to AI_INTENTION_ATTACK + setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated); + } + + return; + } + } + + // Chance to forget attackers after some time + if ((npc.getCurrentHp() == npc.getMaxHp()) && (npc.getCurrentMp() == npc.getMaxMp()) && !npc.getAttackByList().isEmpty() && (Rnd.nextInt(500) == 0)) + { + npc.clearAggroList(); + npc.getAttackByList().clear(); + if (npc instanceof L2MonsterInstance) + { + if (((L2MonsterInstance) npc).hasMinions()) + { + ((L2MonsterInstance) npc).getMinionList().deleteReusedMinions(); + } + } + } + + // Check if the actor is a L2GuardInstance + if (npc instanceof L2GuardInstance) + { + // Order to the L2GuardInstance to return to its home location because there's no target to attack + ((L2GuardInstance) npc).returnHome(); + } + + // If this is a festival monster, then it remains in the same location. + if (npc instanceof L2FestivalMonsterInstance) + { + return; + } + + // Check if the mob should not return to spawn point + if (!npc.canReturnToSpawnPoint()) + { + return; + } + + // Minions following leader + final L2Character leader = npc.getLeader(); + if ((leader != null) && !leader.isAlikeDead()) + { + final int offset; + final int minRadius = 30; + + if (npc.isRaidMinion()) + { + offset = 500; // for Raids - need correction + } + else + { + offset = 200; // for normal minions - need correction :) + } + + if (leader.isRunning()) + { + npc.setRunning(); + } + else + { + npc.setWalking(); + } + + if (npc.getPlanDistanceSq(leader) > (offset * offset)) + { + int x1, y1, z1; + x1 = Rnd.get(minRadius * 2, offset * 2); // x + y1 = Rnd.get(x1, offset * 2); // distance + y1 = (int) Math.sqrt((y1 * y1) - (x1 * x1)); // y + if (x1 > (offset + minRadius)) + { + x1 = (leader.getX() + x1) - offset; + } + else + { + x1 = (leader.getX() - x1) + minRadius; + } + if (y1 > (offset + minRadius)) + { + y1 = (leader.getY() + y1) - offset; + } + else + { + y1 = (leader.getY() - y1) + minRadius; + } + + z1 = leader.getZ(); + // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) + moveTo(x1, y1, z1); + return; + } + else if (Rnd.nextInt(RANDOM_WALK_RATE) == 0) + { + if (_skillrender.hasBuffSkill()) + { + for (L2Skill sk : _skillrender._buffskills) + { + if (cast(sk)) + { + return; + } + } + } + } + } + // Order to the L2MonsterInstance to random walk (1/100) + else if ((npc.getSpawn() != null) && (Rnd.nextInt(RANDOM_WALK_RATE) == 0) && !npc.isNoRndWalk()) + { + int x1, y1, z1; + final int range = Config.MAX_DRIFT_RANGE; + + if (_skillrender.hasBuffSkill()) + { + for (L2Skill sk : _skillrender._buffskills) + { + if (cast(sk)) + { + return; + } + } + } + + // If NPC with random coord in territory + if ((npc.getSpawn().getLocx() == 0) && (npc.getSpawn().getLocy() == 0)) + { + // Calculate a destination point in the spawn area + int p[] = Territory.getInstance().getRandomPoint(npc.getSpawn().getLocation()); + x1 = p[0]; + y1 = p[1]; + z1 = p[2]; + + // Calculate the distance between the current position of the L2Character and the target (x,y) + double distance2 = npc.getPlanDistanceSq(x1, y1); + + if (distance2 > ((range + range) * (range + range))) + { + npc.setisReturningToSpawnPoint(true); + float delay = (float) Math.sqrt(distance2) / range; + x1 = npc.getX() + (int) ((x1 - npc.getX()) / delay); + y1 = npc.getY() + (int) ((y1 - npc.getY()) / delay); + } + + // If NPC with random fixed coord, don't move (unless needs to return to spawnpoint) + if ((Territory.getInstance().getProcMax(npc.getSpawn().getLocation()) > 0) && !npc.isReturningToSpawnPoint()) + { + return; + } + } + else + { + // If NPC with fixed coord + x1 = npc.getSpawn().getLocx(); + y1 = npc.getSpawn().getLocy(); + z1 = npc.getSpawn().getLocz(); + + if (!npc.isInsideRadius(x1, y1, range, false)) + { + npc.setisReturningToSpawnPoint(true); + } + else + { + x1 = Rnd.nextInt(range * 2); // x + y1 = Rnd.get(x1, range * 2); // distance + y1 = (int) Math.sqrt((y1 * y1) - (x1 * x1)); // y + x1 += npc.getSpawn().getLocx() - range; + y1 += npc.getSpawn().getLocy() - range; + z1 = npc.getZ(); + } + } + + // _log.debug("Current pos ("+getX()+", "+getY()+"), moving to ("+x1+", "+y1+")."); + // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) + moveTo(x1, y1, z1); + } + } + + /** + * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
    + *
    + * Actions :
    + *
    + *
  • Update the attack timeout if actor is running
  • If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
  • Call all L2Object of its Faction inside the Faction Range
  • Chose a target and order to attack it with magic skill + * or physical attack

  • + *
    + * TODO: Manage casting rules to healer mobs (like Ant Nurses) + */ + private void thinkAttack() + { + final L2Attackable npc = getActiveChar(); + if (npc.isCastingNow()) + { + return; + } + + L2Character originalAttackTarget = getAttackTarget(); + // Check if target is dead or if timeout is expired to stop this attack + if ((originalAttackTarget == null) || originalAttackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getGameTicks())) + { + // Stop hating this target after the attack timeout or if target is dead + if (originalAttackTarget != null) + { + npc.stopHating(originalAttackTarget); + } + + // Set the AI Intention to AI_INTENTION_ACTIVE + setIntention(AI_INTENTION_ACTIVE); + + npc.setWalking(); + return; + } + + final int collision = npc.getTemplate().collisionRadius; + + // Handle all L2Object of its Faction inside the Faction Range + + String faction_id = getActiveChar().getFactionId(); + if ((faction_id != null) && !faction_id.isEmpty()) + { + int factionRange = npc.getClanRange() + collision; + // Go through all L2Object that belong to its faction + Collection objs = npc.getKnownList().getKnownObjects().values(); + // synchronized (_actor.getKnownList().getKnownObjects()) + try + { + for (L2Object obj : objs) + { + if (obj instanceof L2Npc) + { + L2Npc called = (L2Npc) obj; + + // Handle SevenSigns mob Factions + final String npcfaction = called.getFactionId(); + if ((npcfaction == null) || npcfaction.isEmpty()) + { + continue; + } + + boolean sevenSignFaction = false; + + // TODO: Unhardcode this by AI scripts (DrHouse) + // Catacomb mobs should assist lilim and nephilim other than dungeon + if ("c_dungeon_clan".equals(faction_id) && ("c_dungeon_lilim".equals(npcfaction) || "c_dungeon_nephi".equals(npcfaction))) + { + sevenSignFaction = true; + } + else if ("c_dungeon_lilim".equals(faction_id) && "c_dungeon_clan".equals(npcfaction)) + { + sevenSignFaction = true; + } + else if ("c_dungeon_nephi".equals(faction_id) && "c_dungeon_clan".equals(npcfaction)) + { + sevenSignFaction = true; + } + + if (!faction_id.equals(npcfaction) && !sevenSignFaction) + { + continue; + } + + // Check if the L2Object is inside the Faction Range of + // the actor + if (npc.isInsideRadius(called, factionRange, true, false) && called.hasAI()) + { + if ((Math.abs(originalAttackTarget.getZ() - called.getZ()) < 600) && npc.getAttackByList().contains(originalAttackTarget) && ((called.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (called.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE)) && (called.getInstanceId() == npc.getInstanceId())) + // && GeoData.getInstance().canSeeTarget(called, npc)) + { + if (originalAttackTarget instanceof L2Playable) + { + Quest[] quests = called.getTemplate().getEventQuests(Quest.QuestEventType.ON_FACTION_CALL); + if (quests != null) + { + L2PcInstance player = originalAttackTarget.getActingPlayer(); + boolean isSummon = originalAttackTarget instanceof L2Summon; + for (Quest quest : quests) + { + quest.notifyFactionCall(called, getActiveChar(), player, isSummon); + } + } + } + else if ((called instanceof L2Attackable) && (getAttackTarget() != null) && (called.getAI()._intention != CtrlIntention.AI_INTENTION_ATTACK)) + { + ((L2Attackable) called).addDamageHate(getAttackTarget(), 0, npc.getHating(getAttackTarget())); + called.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, getAttackTarget()); + } + } + } + } + } + } + catch (NullPointerException e) + { + _log.log(Level.WARNING, "L2AttackableAI: thinkAttack() faction call failed: " + e.getMessage(), e); + } + } + + if (npc.isCoreAIDisabled()) + { + return; + } + + /* + * if(_actor.getTarget() == null || this.getAttackTarget() == null || this.getAttackTarget().isDead() || ctarget == _actor) AggroReconsider(); + */ + + // ---------------------------------------------------------------- + + // ------------------------------------------------------------------------------ + // Initialize data + L2Character mostHate = npc.getMostHated(); + if (mostHate == null) + { + setIntention(AI_INTENTION_ACTIVE); + return; + } + + setAttackTarget(mostHate); + npc.setTarget(mostHate); + + final int combinedCollision = collision + mostHate.getTemplate().collisionRadius; + + // ------------------------------------------------------ + // In case many mobs are trying to hit from same place, move a bit, + // circling around the target + // Note from Gnacik: + // On l2js because of that sometimes mobs don't attack player only running + // around player without any sense, so decrease chance for now + if (!npc.isMovementDisabled() && (Rnd.nextInt(100) <= 3)) + { + for (L2Object nearby : npc.getKnownList().getKnownObjects().values()) + { + if ((nearby instanceof L2Attackable) && npc.isInsideRadius(nearby, collision, false, false) && (nearby != mostHate)) + { + int newX = combinedCollision + Rnd.get(40); + if (Rnd.nextBoolean()) + { + newX = mostHate.getX() + newX; + } + else + { + newX = mostHate.getX() - newX; + } + int newY = combinedCollision + Rnd.get(40); + if (Rnd.nextBoolean()) + { + newY = mostHate.getY() + newY; + } + else + { + newY = mostHate.getY() - newY; + } + + if (!npc.isInsideRadius(newX, newY, collision, false)) + { + int newZ = npc.getZ() + 30; + if ((Config.GEODATA == 0) || GeoData.getInstance().canMoveFromToTarget(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceId())) + { + moveTo(newX, newY, newZ); + } + } + return; + } + } + } + // Dodge if its needed + if (!npc.isMovementDisabled() && (npc.getCanDodge() > 0)) + { + if (Rnd.get(100) <= npc.getCanDodge()) + { + // Micht: kepping this one otherwise we should do 2 sqrt + double distance2 = npc.getPlanDistanceSq(mostHate.getX(), mostHate.getY()); + if (Math.sqrt(distance2) <= (60 + combinedCollision)) + { + int posX = npc.getX(); + int posY = npc.getY(); + int posZ = npc.getZ() + 30; + + if (Rnd.nextBoolean()) + { + posX = posX + Rnd.get(100); + } + else + { + posX = posX - Rnd.get(100); + } + + if (Rnd.nextBoolean()) + { + posY = posY + Rnd.get(100); + } + else + { + posY = posY - Rnd.get(100); + } + + if ((Config.GEODATA == 0) || GeoData.getInstance().canMoveFromToTarget(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceId())) + { + setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(posX, posY, posZ, 0)); + } + return; + } + } + } + + // ------------------------------------------------------------------------------ + // BOSS/Raid Minion Target Reconsider + if (npc.isRaid() || npc.isRaidMinion()) + { + chaostime++; + if (npc instanceof L2RaidBossInstance) + { + if (!((L2MonsterInstance) npc).hasMinions()) + { + if (chaostime > Config.RAID_CHAOS_TIME) + { + if (Rnd.get(100) <= (100 - ((npc.getCurrentHp() * 100) / npc.getMaxHp()))) + { + aggroReconsider(); + chaostime = 0; + return; + } + } + } + else + { + if (chaostime > Config.RAID_CHAOS_TIME) + { + if (Rnd.get(100) <= (100 - ((npc.getCurrentHp() * 200) / npc.getMaxHp()))) + { + aggroReconsider(); + chaostime = 0; + return; + } + } + } + } + else if (npc instanceof L2GrandBossInstance) + { + if (chaostime > Config.GRAND_CHAOS_TIME) + { + double chaosRate = 100 - ((npc.getCurrentHp() * 300) / npc.getMaxHp()); + if (((chaosRate <= 10) && (Rnd.get(100) <= 10)) || ((chaosRate > 10) && (Rnd.get(100) <= chaosRate))) + { + aggroReconsider(); + chaostime = 0; + return; + } + } + } + else + { + if (chaostime > Config.MINION_CHAOS_TIME) + { + if (Rnd.get(100) <= (100 - ((npc.getCurrentHp() * 200) / npc.getMaxHp()))) + { + aggroReconsider(); + chaostime = 0; + return; + } + } + } + } + + if (_skillrender.hasSkill()) + { + // ------------------------------------------------------------------------------- + // Heal Condition + if (_skillrender.hasHealSkill() && (_skillrender._healskills != null)) + { + double percentage = (npc.getCurrentHp() / npc.getMaxHp()) * 100; + if (npc.isMinion()) + { + L2Character leader = npc.getLeader(); + if ((leader != null) && !leader.isDead() && (Rnd.get(100) > ((leader.getCurrentHp() / leader.getMaxHp()) * 100))) + { + for (L2Skill sk : _skillrender._healskills) + { + if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_SELF) + { + continue; + } + if (((sk.getMpConsume() >= npc.getCurrentMp()) || npc.isSkillDisabled(sk) || (sk.isMagic() && npc.isMuted()) || (!sk.isMagic() && npc.isPhysicalMuted()))) + { + continue; + } + if (!Util.checkIfInRange((sk.getCastRange() + collision + leader.getTemplate().collisionRadius), npc, leader, false) && !isParty(sk) && !npc.isMovementDisabled()) + { + moveToPawn(leader, sk.getCastRange() + collision + leader.getTemplate().collisionRadius); + return; + } + if (GeoData.getInstance().canSeeTarget(npc, leader)) + { + clientStopMoving(null); + npc.setTarget(leader); + clientStopMoving(null); + npc.doCast(sk); + return; + } + } + } + } + if (Rnd.get(100) < ((100 - percentage) / 3)) + { + for (L2Skill sk : _skillrender._healskills) + { + if (((sk.getMpConsume() >= npc.getCurrentMp()) || npc.isSkillDisabled(sk) || (sk.isMagic() && npc.isMuted())) || (!sk.isMagic() && npc.isPhysicalMuted())) + { + continue; + } + clientStopMoving(null); + npc.setTarget(npc); + npc.doCast(sk); + return; + } + } + for (L2Skill sk : _skillrender._healskills) + { + if (((sk.getMpConsume() >= npc.getCurrentMp()) || npc.isSkillDisabled(sk) || (sk.isMagic() && npc.isMuted())) || (!sk.isMagic() && npc.isPhysicalMuted())) + { + continue; + } + if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_ONE) + { + for (L2Character obj : npc.getKnownList().getKnownCharactersInRadius(sk.getCastRange() + collision)) + { + if (!(obj instanceof L2Attackable) || obj.isDead()) + { + continue; + } + + L2Attackable targets = ((L2Attackable) obj); + if ((npc.getFactionId() != null) && !npc.getFactionId().equals(targets.getFactionId())) + { + continue; + } + percentage = (targets.getCurrentHp() / targets.getMaxHp()) * 100; + if (Rnd.get(100) < ((100 - percentage) / 10)) + { + if (GeoData.getInstance().canSeeTarget(npc, targets)) + { + clientStopMoving(null); + npc.setTarget(obj); + npc.doCast(sk); + return; + } + } + } + } + if (isParty(sk)) + { + clientStopMoving(null); + npc.doCast(sk); + return; + } + } + } + // ------------------------------------------------------------------------------- + // Res Skill Condition + if (_skillrender.hasResSkill()) + { + if (npc.isMinion()) + { + L2Character leader = npc.getLeader(); + if ((leader != null) && leader.isDead()) + { + for (L2Skill sk : _skillrender._resskills) + { + if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_SELF) + { + continue; + } + if (((sk.getMpConsume() >= npc.getCurrentMp()) || npc.isSkillDisabled(sk) || (sk.isMagic() && npc.isMuted()) || (!sk.isMagic() && npc.isPhysicalMuted()))) + { + continue; + } + if (!Util.checkIfInRange((sk.getCastRange() + collision + leader.getTemplate().collisionRadius), npc, leader, false) && !isParty(sk) && !npc.isMovementDisabled()) + { + moveToPawn(leader, sk.getCastRange() + collision + leader.getTemplate().collisionRadius); + return; + } + if (GeoData.getInstance().canSeeTarget(npc, leader)) + { + clientStopMoving(null); + npc.setTarget(leader); + npc.doCast(sk); + return; + } + } + } + } + for (L2Skill sk : _skillrender._resskills) + { + if (((sk.getMpConsume() >= npc.getCurrentMp()) || npc.isSkillDisabled(sk) || (sk.isMagic() && npc.isMuted())) || (!sk.isMagic() && npc.isPhysicalMuted())) + { + continue; + } + if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_ONE) + { + for (L2Character obj : npc.getKnownList().getKnownCharactersInRadius(sk.getCastRange() + collision)) + { + if (!(obj instanceof L2Attackable) || !obj.isDead()) + { + continue; + } + + L2Attackable targets = ((L2Attackable) obj); + if ((npc.getFactionId() != null) && !npc.getFactionId().equals(targets.getFactionId())) + { + continue; + } + if (Rnd.get(100) < 10) + { + if (GeoData.getInstance().canSeeTarget(npc, targets)) + { + clientStopMoving(null); + npc.setTarget(obj); + npc.doCast(sk); + return; + } + } + } + } + if (isParty(sk)) + { + clientStopMoving(null); + L2Object target = getAttackTarget(); + npc.setTarget(npc); + npc.doCast(sk); + npc.setTarget(target); + return; + } + } + } + } + + double dist = Math.sqrt(npc.getPlanDistanceSq(mostHate.getX(), mostHate.getY())); + int dist2 = (int) dist - collision; + int range = npc.getPhysicalAttackRange() + combinedCollision; + if (mostHate.isMoving()) + { + range = range + 50; + if (npc.isMoving()) + { + range = range + 50; + } + } + + // ------------------------------------------------------------------------------- + // Immobilize Condition + if ((npc.isMovementDisabled() && ((dist > range) || mostHate.isMoving())) || ((dist > range) && mostHate.isMoving())) + { + movementDisable(); + return; + } + setTimepass(0); + // -------------------------------------------------------------------------------- + // Skill Use + if (_skillrender.hasSkill()) + { + if (Rnd.get(100) <= npc.getSkillChance()) + { + L2Skill skills = _skillrender._generalskills.get(Rnd.nextInt(_skillrender._generalskills.size())); + if (cast(skills)) + { + return; + } + for (L2Skill sk : _skillrender._generalskills) + { + if (cast(sk)) + { + return; + } + } + } + + // -------------------------------------------------------------------------------- + // Long/Short Range skill Usage + if (npc.hasLSkill() || npc.hasSSkill()) + { + if (npc.hasSSkill() && (dist2 <= 150) && (Rnd.get(100) <= npc.getSSkillChance())) + { + sSkillRender(); + if (_skillrender._Srangeskills != null) + { + L2Skill skills = _skillrender._Srangeskills.get(Rnd.nextInt(_skillrender._Srangeskills.size())); + if (cast(skills)) + { + return; + } + for (L2Skill sk : _skillrender._Srangeskills) + { + if (cast(sk)) + { + return; + } + } + } + } + if (npc.hasLSkill() && (dist2 > 150) && (Rnd.get(100) <= npc.getLSkillChance())) + { + lSkillRender(); + if (_skillrender._Lrangeskills != null) + { + L2Skill skills = _skillrender._Lrangeskills.get(Rnd.nextInt(_skillrender._Lrangeskills.size())); + if (cast(skills)) + { + return; + } + for (L2Skill sk : _skillrender._Lrangeskills) + { + if (cast(sk)) + { + return; + } + } + } + } + } + } + + // -------------------------------------------------------------------------------- + // Starts Melee or Primary Skill + if ((dist2 > range) || !GeoData.getInstance().canSeeTarget(npc, mostHate)) + { + if (npc.isMovementDisabled()) + { + targetReconsider(); + return; + } + if (getAttackTarget().isMoving()) + { + range -= 100; + } + if (range < 5) + { + range = 5; + } + moveToPawn(getAttackTarget(), range); + return; + } + melee(npc.getPrimaryAttack()); + } + + private void melee(int type) + { + if (type != 0) + { + switch (type) + { + case -1: + { + if (_skillrender._generalskills != null) + { + for (L2Skill sk : _skillrender._generalskills) + { + if (cast(sk)) + { + return; + } + } + } + break; + } + case 1: + { + if (_skillrender.hasAtkSkill()) + { + for (L2Skill sk : _skillrender._atkskills) + { + if (cast(sk)) + { + return; + } + } + } + break; + } + default: + { + if (_skillrender._generalskills != null) + { + for (L2Skill sk : _skillrender._generalskills) + { + if (sk.getId() == getActiveChar().getPrimaryAttack()) + { + if (cast(sk)) + { + return; + } + } + } + } + } + break; + } + } + + _accessor.doAttack(getAttackTarget()); + } + + private boolean cast(L2Skill sk) + { + if (sk == null) + { + return false; + } + + final L2Attackable caster = getActiveChar(); + + if (caster.isCastingNow() && !sk.isSimultaneousCast()) + { + return false; + } + + if ((sk.getMpConsume() >= caster.getCurrentMp()) || caster.isSkillDisabled(sk) || (sk.isMagic() && caster.isMuted()) || (!sk.isMagic() && caster.isPhysicalMuted())) + { + return false; + } + if (getAttackTarget() == null) + { + if (caster.getMostHated() != null) + { + setAttackTarget(caster.getMostHated()); + } + } + L2Character attackTarget = getAttackTarget(); + if (attackTarget == null) + { + return false; + } + double dist = Math.sqrt(caster.getPlanDistanceSq(attackTarget.getX(), attackTarget.getY())); + double dist2 = dist - attackTarget.getTemplate().collisionRadius; + double range = caster.getPhysicalAttackRange() + caster.getTemplate().collisionRadius + attackTarget.getTemplate().collisionRadius; + double srange = sk.getCastRange() + caster.getTemplate().collisionRadius; + if (attackTarget.isMoving()) + { + dist2 = dist2 - 30; + } + + switch (sk.getSkillType()) + { + + case BUFF: + { + if (caster.getFirstEffect(sk) == null) + { + clientStopMoving(null); + // L2Object target = attackTarget; + caster.setTarget(caster); + caster.doCast(sk); + // _actor.setTarget(target); + return true; + } + // ---------------------------------------- + // If actor already have buff, start looking at others same faction mob to cast + if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_SELF) + { + return false; + } + if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_ONE) + { + L2Character target = effectTargetReconsider(sk, true); + if (target != null) + { + clientStopMoving(null); + L2Object targets = attackTarget; + caster.setTarget(target); + caster.doCast(sk); + caster.setTarget(targets); + return true; + } + } + if (canParty(sk)) + { + clientStopMoving(null); + L2Object targets = attackTarget; + caster.setTarget(caster); + caster.doCast(sk); + caster.setTarget(targets); + return true; + } + break; + } + case HEAL: + case HOT: + case HEAL_PERCENT: + case HEAL_STATIC: + case BALANCE_LIFE: + { + double percentage = (caster.getCurrentHp() / caster.getMaxHp()) * 100; + if (caster.isMinion() && (sk.getTargetType() != L2Skill.SkillTargetType.TARGET_SELF)) + { + L2Character leader = caster.getLeader(); + if ((leader != null) && !leader.isDead() && (Rnd.get(100) > ((leader.getCurrentHp() / leader.getMaxHp()) * 100))) + { + if (!Util.checkIfInRange((sk.getCastRange() + caster.getTemplate().collisionRadius + leader.getTemplate().collisionRadius), caster, leader, false) && !isParty(sk) && !caster.isMovementDisabled()) + { + moveToPawn(leader, sk.getCastRange() + caster.getTemplate().collisionRadius + leader.getTemplate().collisionRadius); + } + if (GeoData.getInstance().canSeeTarget(caster, leader)) + { + clientStopMoving(null); + caster.setTarget(leader); + caster.doCast(sk); + return true; + } + } + } + if (Rnd.get(100) < ((100 - percentage) / 3)) + { + clientStopMoving(null); + caster.setTarget(caster); + caster.doCast(sk); + return true; + } + + if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_ONE) + { + for (L2Character obj : caster.getKnownList().getKnownCharactersInRadius(sk.getCastRange() + caster.getTemplate().collisionRadius)) + { + if (!(obj instanceof L2Attackable) || obj.isDead()) + { + continue; + } + + L2Attackable targets = ((L2Attackable) obj); + if ((caster.getFactionId() != null) && !caster.getFactionId().equals(targets.getFactionId())) + { + continue; + } + percentage = (targets.getCurrentHp() / targets.getMaxHp()) * 100; + if (Rnd.get(100) < ((100 - percentage) / 10)) + { + if (GeoData.getInstance().canSeeTarget(caster, targets)) + { + clientStopMoving(null); + caster.setTarget(obj); + caster.doCast(sk); + return true; + } + } + } + } + if (isParty(sk)) + { + for (L2Character obj : caster.getKnownList().getKnownCharactersInRadius(sk.getSkillRadius() + caster.getTemplate().collisionRadius)) + { + if (!(obj instanceof L2Attackable)) + { + continue; + } + L2Npc targets = ((L2Npc) obj); + if ((caster.getFactionId() != null) && targets.getFactionId().equals(caster.getFactionId())) + { + if ((obj.getCurrentHp() < obj.getMaxHp()) && (Rnd.get(100) <= 20)) + { + clientStopMoving(null); + caster.setTarget(caster); + caster.doCast(sk); + return true; + } + } + } + } + break; + } + case RESURRECT: + { + if (!isParty(sk)) + { + if (caster.isMinion() && (sk.getTargetType() != L2Skill.SkillTargetType.TARGET_SELF)) + { + L2Character leader = caster.getLeader(); + if ((leader != null) && leader.isDead()) + { + if (!Util.checkIfInRange((sk.getCastRange() + caster.getTemplate().collisionRadius + leader.getTemplate().collisionRadius), caster, leader, false) && !isParty(sk) && !caster.isMovementDisabled()) + { + moveToPawn(leader, sk.getCastRange() + caster.getTemplate().collisionRadius + leader.getTemplate().collisionRadius); + } + } + if (GeoData.getInstance().canSeeTarget(caster, leader)) + { + clientStopMoving(null); + caster.setTarget(leader); + caster.doCast(sk); + return true; + } + } + + for (L2Character obj : caster.getKnownList().getKnownCharactersInRadius(sk.getCastRange() + caster.getTemplate().collisionRadius)) + { + if (!(obj instanceof L2Attackable) || !obj.isDead()) + { + continue; + } + + L2Attackable targets = ((L2Attackable) obj); + if ((caster.getFactionId() != null) && !caster.getFactionId().equals(targets.getFactionId())) + { + continue; + } + if (Rnd.get(100) < 10) + { + if (GeoData.getInstance().canSeeTarget(caster, targets)) + { + clientStopMoving(null); + caster.setTarget(obj); + caster.doCast(sk); + return true; + } + } + } + } + else if (isParty(sk)) + { + for (L2Character obj : caster.getKnownList().getKnownCharactersInRadius(sk.getSkillRadius() + caster.getTemplate().collisionRadius)) + { + if (!(obj instanceof L2Attackable)) + { + continue; + } + L2Npc targets = ((L2Npc) obj); + if ((caster.getFactionId() != null) && caster.getFactionId().equals(targets.getFactionId())) + { + if ((obj.getCurrentHp() < obj.getMaxHp()) && (Rnd.get(100) <= 20)) + { + clientStopMoving(null); + caster.setTarget(caster); + caster.doCast(sk); + return true; + } + } + } + } + break; + } + case DEBUFF: + case POISON: + case DOT: + case MDOT: + case BLEED: + { + if (GeoData.getInstance().canSeeTarget(caster, attackTarget) && !canAOE(sk) && !attackTarget.isDead() && (dist2 <= srange)) + { + if (attackTarget.getFirstEffect(sk) == null) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + else if (canAOE(sk)) + { + if ((sk.getTargetType() == SkillTargetType.TARGET_AURA) || (sk.getTargetType() == SkillTargetType.TARGET_BEHIND_AURA) || (sk.getTargetType() == SkillTargetType.TARGET_FRONT_AURA)) + { + clientStopMoving(null); + // L2Object target = attackTarget; + // _actor.setTarget(_actor); + caster.doCast(sk); + // _actor.setTarget(target); + return true; + } + if (((sk.getTargetType() == SkillTargetType.TARGET_AREA) || (sk.getTargetType() == SkillTargetType.TARGET_BEHIND_AREA) || (sk.getTargetType() == SkillTargetType.TARGET_FRONT_AREA)) && GeoData.getInstance().canSeeTarget(caster, attackTarget) && !attackTarget.isDead() && (dist2 <= srange)) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + else if (sk.getTargetType() == SkillTargetType.TARGET_ONE) + { + L2Character target = effectTargetReconsider(sk, false); + if (target != null) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + break; + } + case SLEEP: + { + if (sk.getTargetType() == SkillTargetType.TARGET_ONE) + { + if (!attackTarget.isDead() && (dist2 <= srange)) + { + if ((dist2 > range) || attackTarget.isMoving()) + { + if (attackTarget.getFirstEffect(sk) == null) + { + clientStopMoving(null); + // _actor.setTarget(attackTarget); + caster.doCast(sk); + return true; + } + } + } + + L2Character target = effectTargetReconsider(sk, false); + if (target != null) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + else if (canAOE(sk)) + { + if ((sk.getTargetType() == SkillTargetType.TARGET_AURA) || (sk.getTargetType() == SkillTargetType.TARGET_BEHIND_AURA) || (sk.getTargetType() == SkillTargetType.TARGET_FRONT_AURA)) + { + clientStopMoving(null); + // L2Object target = attackTarget; + // _actor.setTarget(_actor); + caster.doCast(sk); + // _actor.setTarget(target); + return true; + } + if (((sk.getTargetType() == SkillTargetType.TARGET_AREA) || (sk.getTargetType() == SkillTargetType.TARGET_BEHIND_AREA) || (sk.getTargetType() == SkillTargetType.TARGET_FRONT_AREA)) && GeoData.getInstance().canSeeTarget(caster, attackTarget) && !attackTarget.isDead() && (dist2 <= srange)) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + break; + } + case ROOT: + case STUN: + case PARALYZE: + { + if (GeoData.getInstance().canSeeTarget(caster, attackTarget) && !canAOE(sk) && (dist2 <= srange)) + { + if (attackTarget.getFirstEffect(sk) == null) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + else if (canAOE(sk)) + { + if ((sk.getTargetType() == SkillTargetType.TARGET_AURA) || (sk.getTargetType() == SkillTargetType.TARGET_BEHIND_AURA) || (sk.getTargetType() == SkillTargetType.TARGET_FRONT_AURA)) + { + clientStopMoving(null); + // L2Object target = attackTarget; + // _actor.setTarget(_actor); + caster.doCast(sk); + // _actor.setTarget(target); + return true; + } + else if (((sk.getTargetType() == SkillTargetType.TARGET_AREA) || (sk.getTargetType() == SkillTargetType.TARGET_BEHIND_AREA) || (sk.getTargetType() == SkillTargetType.TARGET_FRONT_AREA)) && GeoData.getInstance().canSeeTarget(caster, attackTarget) && !attackTarget.isDead() && (dist2 <= srange)) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + else if (sk.getTargetType() == SkillTargetType.TARGET_ONE) + { + L2Character target = effectTargetReconsider(sk, false); + if (target != null) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + break; + } + case MUTE: + case FEAR: + { + if (GeoData.getInstance().canSeeTarget(caster, attackTarget) && !canAOE(sk) && (dist2 <= srange)) + { + if (attackTarget.getFirstEffect(sk) == null) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + else if (canAOE(sk)) + { + if ((sk.getTargetType() == SkillTargetType.TARGET_AURA) || (sk.getTargetType() == SkillTargetType.TARGET_BEHIND_AURA) || (sk.getTargetType() == SkillTargetType.TARGET_FRONT_AURA)) + { + clientStopMoving(null); + // L2Object target = attackTarget; + // _actor.setTarget(_actor); + caster.doCast(sk); + // _actor.setTarget(target); + return true; + } + if (((sk.getTargetType() == SkillTargetType.TARGET_AREA) || (sk.getTargetType() == SkillTargetType.TARGET_BEHIND_AREA) || (sk.getTargetType() == SkillTargetType.TARGET_FRONT_AREA)) && GeoData.getInstance().canSeeTarget(caster, attackTarget) && !attackTarget.isDead() && (dist2 <= srange)) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + else if (sk.getTargetType() == SkillTargetType.TARGET_ONE) + { + L2Character target = effectTargetReconsider(sk, false); + if (target != null) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + break; + } + case CANCEL: + case NEGATE: + { + // decrease cancel probability + if (Rnd.get(50) != 0) + { + return true; + } + + if (sk.getTargetType() == SkillTargetType.TARGET_ONE) + { + if ((attackTarget.getFirstEffect(L2EffectType.BUFF) != null) && GeoData.getInstance().canSeeTarget(caster, attackTarget) && !attackTarget.isDead() && (dist2 <= srange)) + { + clientStopMoving(null); + // L2Object target = attackTarget; + // _actor.setTarget(_actor); + caster.doCast(sk); + // _actor.setTarget(target); + return true; + } + L2Character target = effectTargetReconsider(sk, false); + if (target != null) + { + clientStopMoving(null); + L2Object targets = attackTarget; + caster.setTarget(target); + caster.doCast(sk); + caster.setTarget(targets); + return true; + } + } + else if (canAOE(sk)) + { + if (((sk.getTargetType() == SkillTargetType.TARGET_AURA) || (sk.getTargetType() == SkillTargetType.TARGET_BEHIND_AURA) || (sk.getTargetType() == SkillTargetType.TARGET_FRONT_AURA)) && GeoData.getInstance().canSeeTarget(caster, attackTarget)) + + { + clientStopMoving(null); + // L2Object target = attackTarget; + // _actor.setTarget(_actor); + caster.doCast(sk); + // _actor.setTarget(target); + return true; + } + else if (((sk.getTargetType() == SkillTargetType.TARGET_AREA) || (sk.getTargetType() == SkillTargetType.TARGET_BEHIND_AREA) || (sk.getTargetType() == SkillTargetType.TARGET_FRONT_AREA)) && GeoData.getInstance().canSeeTarget(caster, attackTarget) && !attackTarget.isDead() && (dist2 <= srange)) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + } + break; + } + case PDAM: + case MDAM: + case BLOW: + case DRAIN: + case CHARGEDAM: + case FATAL: + case DEATHLINK: + case CPDAM: + case MANADAM: + case CPDAMPERCENT: + { + if (!canAura(sk)) + { + if (GeoData.getInstance().canSeeTarget(caster, attackTarget) && !attackTarget.isDead() && (dist2 <= srange)) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + L2Character target = skillTargetReconsider(sk); + if (target != null) + { + clientStopMoving(null); + L2Object targets = attackTarget; + caster.setTarget(target); + caster.doCast(sk); + caster.setTarget(targets); + return true; + } + } + else + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + break; + } + default: + { + if (!canAura(sk)) + { + + if (GeoData.getInstance().canSeeTarget(caster, attackTarget) && !attackTarget.isDead() && (dist2 <= srange)) + { + clientStopMoving(null); + caster.doCast(sk); + return true; + } + L2Character target = skillTargetReconsider(sk); + if (target != null) + { + clientStopMoving(null); + L2Object targets = attackTarget; + caster.setTarget(target); + caster.doCast(sk); + caster.setTarget(targets); + return true; + } + } + else + { + clientStopMoving(null); + // L2Object targets = attackTarget; + // _actor.setTarget(_actor); + caster.doCast(sk); + // _actor.setTarget(targets); + return true; + } + + } + break; + } + + return false; + } + + /** + * This AI task will start when ACTOR cannot move and attack range larger than distance + */ + private void movementDisable() + { + final L2Attackable npc = getActiveChar(); + double dist = 0; + double dist2 = 0; + int range = 0; + try + { + if (npc.getTarget() == null) + { + npc.setTarget(getAttackTarget()); + } + dist = Math.sqrt(npc.getPlanDistanceSq(getAttackTarget().getX(), getAttackTarget().getY())); + dist2 = dist - npc.getTemplate().collisionRadius; + range = npc.getPhysicalAttackRange() + npc.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius; + if (getAttackTarget().isMoving()) + { + dist = dist - 30; + if (npc.isMoving()) + { + dist = dist - 50; + } + } + + // Check if activeChar has any skill + if (_skillrender.hasSkill()) + { + // ------------------------------------------------------------- + // Try to stop the target or disable the target as priority + int random = Rnd.get(100); + if (_skillrender.hasImmobiliseSkill() && !getAttackTarget().isImmobilized() && (random < 2)) + { + for (L2Skill sk : _skillrender._immobiliseskills) + { + if ((sk.getMpConsume() >= npc.getCurrentMp()) || npc.isSkillDisabled(sk) || (((sk.getCastRange() + npc.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius) <= dist2) && !canAura(sk)) || (sk.isMagic() && npc.isMuted()) || (!sk.isMagic() && npc.isPhysicalMuted())) + { + continue; + } + if (!GeoData.getInstance().canSeeTarget(npc, getAttackTarget())) + { + continue; + } + if (getAttackTarget().getFirstEffect(sk) == null) + { + clientStopMoving(null); + // L2Object target = getAttackTarget(); + // _actor.setTarget(_actor); + npc.doCast(sk); + // _actor.setTarget(target); + return; + } + } + } + // ------------------------------------------------------------- + // Same as Above, but with Mute/FEAR etc.... + if (_skillrender.hasCOTSkill() && (random < 5)) + { + for (L2Skill sk : _skillrender._cotskills) + { + if ((sk.getMpConsume() >= npc.getCurrentMp()) || npc.isSkillDisabled(sk) || (((sk.getCastRange() + npc.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius) <= dist2) && !canAura(sk)) || (sk.isMagic() && npc.isMuted()) || (!sk.isMagic() && npc.isPhysicalMuted())) + { + continue; + } + if (!GeoData.getInstance().canSeeTarget(npc, getAttackTarget())) + { + continue; + } + if (getAttackTarget().getFirstEffect(sk) == null) + { + clientStopMoving(null); + // L2Object target = getAttackTarget(); + // _actor.setTarget(_actor); + npc.doCast(sk); + // _actor.setTarget(target); + return; + } + } + } + // ------------------------------------------------------------- + if (_skillrender.hasDebuffSkill() && (random < 8)) + { + for (L2Skill sk : _skillrender._debuffskills) + { + if ((sk.getMpConsume() >= npc.getCurrentMp()) || npc.isSkillDisabled(sk) || (((sk.getCastRange() + npc.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius) <= dist2) && !canAura(sk)) || (sk.isMagic() && npc.isMuted()) || (!sk.isMagic() && npc.isPhysicalMuted())) + { + continue; + } + if (!GeoData.getInstance().canSeeTarget(npc, getAttackTarget())) + { + continue; + } + if (getAttackTarget().getFirstEffect(sk) == null) + { + clientStopMoving(null); + // L2Object target = getAttackTarget(); + // _actor.setTarget(_actor); + npc.doCast(sk); + // _actor.setTarget(target); + return; + } + } + } + // ------------------------------------------------------------- + // Some side effect skill like CANCEL or NEGATE + if (_skillrender.hasNegativeSkill() && (random < 9)) + { + for (L2Skill sk : _skillrender._negativeskills) + { + if ((sk.getMpConsume() >= npc.getCurrentMp()) || npc.isSkillDisabled(sk) || (((sk.getCastRange() + npc.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius) <= dist2) && !canAura(sk)) || (sk.isMagic() && npc.isMuted()) || (!sk.isMagic() && npc.isPhysicalMuted())) + { + continue; + } + if (!GeoData.getInstance().canSeeTarget(npc, getAttackTarget())) + { + continue; + } + if (getAttackTarget().getFirstEffect(L2EffectType.BUFF) != null) + { + clientStopMoving(null); + // L2Object target = getAttackTarget(); + // _actor.setTarget(_actor); + npc.doCast(sk); + // _actor.setTarget(target); + return; + } + } + } + // ------------------------------------------------------------- + // Start ATK SKILL when nothing can be done + if (_skillrender.hasAtkSkill() && (npc.isMovementDisabled() || (npc.getAiType() == AIType.MAGE) || (npc.getAiType() == AIType.HEALER))) + { + for (L2Skill sk : _skillrender._atkskills) + { + if ((sk.getMpConsume() >= npc.getCurrentMp()) || npc.isSkillDisabled(sk) || (((sk.getCastRange() + npc.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius) <= dist2) && !canAura(sk)) || (sk.isMagic() && npc.isMuted()) || (!sk.isMagic() && npc.isPhysicalMuted())) + { + continue; + } + if (!GeoData.getInstance().canSeeTarget(npc, getAttackTarget())) + { + continue; + } + clientStopMoving(null); + // L2Object target = getAttackTarget(); + // _actor.setTarget(_actor); + npc.doCast(sk); + // _actor.setTarget(target); + return; + } + } + // ------------------------------------------------------------- + // if there is no ATK skill to use, then try Universal skill + /* + * if(_skillrender.hasUniversalSkill()) { for(L2Skill sk:_skillrender._universalskills) { if(sk.getMpConsume()>=_actor.getCurrentMp() || _actor.isSkillDisabled(sk.getId()) ||(sk.getCastRange()+ _actor.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius <= + * dist2 && !canAura(sk)) ||(sk.isMagic()&&_actor.isMuted()) ||(!sk.isMagic()&&_actor.isPhysicalMuted())) { continue; } if(!GeoData.getInstance().canSeeTarget(_actor,getAttackTarget())) continue; clientStopMoving(null); L2Object target = getAttackTarget(); //_actor.setTarget(_actor); + * _actor.doCast(sk); //_actor.setTarget(target); return; } } + */ + } + // timepass = timepass + 1; + if (npc.isMovementDisabled()) + { + // timepass = 0; + targetReconsider(); + + return; + } + // else if(timepass>=5) + // { + // timepass = 0; + // AggroReconsider(); + // return; + // } + + if ((dist > range) || !GeoData.getInstance().canSeeTarget(npc, getAttackTarget())) + { + if (getAttackTarget().isMoving()) + { + range -= 100; + } + if (range < 5) + { + range = 5; + } + moveToPawn(getAttackTarget(), range); + return; + + } + + melee(npc.getPrimaryAttack()); + } + catch (NullPointerException e) + { + setIntention(AI_INTENTION_ACTIVE); + _log.log(Level.WARNING, this + " - failed executing movementDisable(): " + e.getMessage(), e); + return; + } + } + + private L2Character effectTargetReconsider(L2Skill sk, boolean positive) + { + if (sk == null) + { + return null; + } + L2Attackable actor = getActiveChar(); + if ((sk.getSkillType() != L2SkillType.NEGATE) || (sk.getSkillType() != L2SkillType.CANCEL)) + { + if (!positive) + { + double dist = 0; + double dist2 = 0; + int range = 0; + + for (L2Character obj : actor.getAttackByList()) + { + if ((obj == null) || obj.isDead() || !GeoData.getInstance().canSeeTarget(actor, obj) || (obj == getAttackTarget())) + { + continue; + } + try + { + actor.setTarget(getAttackTarget()); + dist = Math.sqrt(actor.getPlanDistanceSq(obj.getX(), obj.getY())); + dist2 = dist - actor.getTemplate().collisionRadius; + range = sk.getCastRange() + actor.getTemplate().collisionRadius + obj.getTemplate().collisionRadius; + if (obj.isMoving()) + { + dist2 = dist2 - 70; + } + } + catch (NullPointerException e) + { + continue; + } + if (dist2 <= range) + { + if (getAttackTarget().getFirstEffect(sk) == null) + { + return obj; + } + } + } + + // ---------------------------------------------------------------------- + // If there is nearby Target with aggro, start going on random target that is attackable + for (L2Character obj : actor.getKnownList().getKnownCharactersInRadius(range)) + { + if (obj.isDead() || !GeoData.getInstance().canSeeTarget(actor, obj)) + { + continue; + } + try + { + actor.setTarget(getAttackTarget()); + dist = Math.sqrt(actor.getPlanDistanceSq(obj.getX(), obj.getY())); + dist2 = dist; + range = sk.getCastRange() + actor.getTemplate().collisionRadius + obj.getTemplate().collisionRadius; + if (obj.isMoving()) + { + dist2 = dist2 - 70; + } + } + catch (NullPointerException e) + { + continue; + } + if (obj instanceof L2Attackable) + { + if ((actor.getEnemyClan() != null) && actor.getEnemyClan().equals(((L2Attackable) obj).getClan())) + { + if (dist2 <= range) + { + if (getAttackTarget().getFirstEffect(sk) == null) + { + return obj; + } + } + } + } + if ((obj instanceof L2PcInstance) || (obj instanceof L2Summon)) + { + if (dist2 <= range) + { + if (getAttackTarget().getFirstEffect(sk) == null) + { + return obj; + } + } + } + } + } + else if (positive) + { + double dist = 0; + double dist2 = 0; + int range = 0; + for (L2Character obj : actor.getKnownList().getKnownCharactersInRadius(range)) + { + if (!(obj instanceof L2Attackable) || obj.isDead() || !GeoData.getInstance().canSeeTarget(actor, obj)) + { + continue; + } + + L2Attackable targets = ((L2Attackable) obj); + if ((actor.getFactionId() != null) && !actor.getFactionId().equals(targets.getFactionId())) + { + continue; + } + + try + { + actor.setTarget(getAttackTarget()); + dist = Math.sqrt(actor.getPlanDistanceSq(obj.getX(), obj.getY())); + dist2 = dist - actor.getTemplate().collisionRadius; + range = sk.getCastRange() + actor.getTemplate().collisionRadius + obj.getTemplate().collisionRadius; + if (obj.isMoving()) + { + dist2 = dist2 - 70; + } + } + catch (NullPointerException e) + { + continue; + } + if (dist2 <= range) + { + if (obj.getFirstEffect(sk) == null) + { + return obj; + } + } + } + } + return null; + } + + double dist = 0; + double dist2 = 0; + int range = 0; + range = sk.getCastRange() + actor.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius; + for (L2Character obj : actor.getKnownList().getKnownCharactersInRadius(range)) + { + if ((obj == null) || obj.isDead() || !GeoData.getInstance().canSeeTarget(actor, obj)) + { + continue; + } + try + { + actor.setTarget(getAttackTarget()); + dist = Math.sqrt(actor.getPlanDistanceSq(obj.getX(), obj.getY())); + dist2 = dist - actor.getTemplate().collisionRadius; + range = sk.getCastRange() + actor.getTemplate().collisionRadius + obj.getTemplate().collisionRadius; + if (obj.isMoving()) + { + dist2 = dist2 - 70; + } + } + catch (NullPointerException e) + { + continue; + } + if (obj instanceof L2Attackable) + { + if ((actor.getEnemyClan() != null) && actor.getEnemyClan().equals(((L2Attackable) obj).getClan())) + { + if (dist2 <= range) + { + if (getAttackTarget().getFirstEffect(L2EffectType.BUFF) != null) + { + return obj; + } + } + } + } + if ((obj instanceof L2PcInstance) || (obj instanceof L2Summon)) + { + + if (dist2 <= range) + { + if (getAttackTarget().getFirstEffect(L2EffectType.BUFF) != null) + { + return obj; + } + } + } + } + return null; + } + + private L2Character skillTargetReconsider(L2Skill sk) + { + double dist = 0; + double dist2 = 0; + int range = 0; + L2Attackable actor = getActiveChar(); + if (actor.getHateList() != null) + { + for (L2Character obj : actor.getHateList()) + { + if ((obj == null) || !GeoData.getInstance().canSeeTarget(actor, obj) || obj.isDead()) + { + continue; + } + try + { + actor.setTarget(getAttackTarget()); + dist = Math.sqrt(actor.getPlanDistanceSq(obj.getX(), obj.getY())); + dist2 = dist - actor.getTemplate().collisionRadius; + range = sk.getCastRange() + actor.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius; + // if(obj.isMoving()) + // dist2 = dist2 - 40; + } + catch (NullPointerException e) + { + continue; + } + if (dist2 <= range) + { + return obj; + } + } + } + + if (!(actor instanceof L2GuardInstance)) + { + Collection objs = actor.getKnownList().getKnownObjects().values(); + for (L2Object target : objs) + { + try + { + actor.setTarget(getAttackTarget()); + dist = Math.sqrt(actor.getPlanDistanceSq(target.getX(), target.getY())); + dist2 = dist; + range = sk.getCastRange() + actor.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius; + // if(obj.isMoving()) + // dist2 = dist2 - 40; + } + catch (NullPointerException e) + { + continue; + } + L2Character obj = null; + if (target instanceof L2Character) + { + obj = (L2Character) target; + } + if ((obj == null) || !GeoData.getInstance().canSeeTarget(actor, obj) || (dist2 > range)) + { + continue; + } + if (obj instanceof L2PcInstance) + { + return obj; + + } + if (obj instanceof L2Attackable) + { + if ((actor.getEnemyClan() != null) && actor.getEnemyClan().equals(((L2Attackable) obj).getClan())) + { + return obj; + } + if (actor.getIsChaos() != 0) + { + if ((((L2Attackable) obj).getFactionId() != null) && ((L2Attackable) obj).getFactionId().equals(actor.getFactionId())) + { + continue; + } + return obj; + } + } + if (obj instanceof L2Summon) + { + return obj; + } + + } + } + return null; + } + + private void targetReconsider() + { + double dist = 0; + double dist2 = 0; + int range = 0; + L2Attackable actor = getActiveChar(); + L2Character MostHate = actor.getMostHated(); + if (actor.getHateList() != null) + { + for (L2Character obj : actor.getHateList()) + { + if ((obj == null) || !GeoData.getInstance().canSeeTarget(actor, obj) || obj.isDead() || (obj != MostHate) || (obj == actor)) + { + continue; + } + try + { + dist = Math.sqrt(actor.getPlanDistanceSq(obj.getX(), obj.getY())); + dist2 = dist - actor.getTemplate().collisionRadius; + range = actor.getPhysicalAttackRange() + actor.getTemplate().collisionRadius + obj.getTemplate().collisionRadius; + if (obj.isMoving()) + { + dist2 = dist2 - 70; + } + } + catch (NullPointerException e) + { + continue; + } + + if (dist2 <= range) + { + if (MostHate != null) + { + actor.addDamageHate(obj, 0, actor.getHating(MostHate)); + } + else + { + actor.addDamageHate(obj, 0, 2000); + } + actor.setTarget(obj); + setAttackTarget(obj); + return; + } + } + } + if (!(actor instanceof L2GuardInstance)) + { + Collection objs = actor.getKnownList().getKnownObjects().values(); + for (L2Object target : objs) + { + L2Character obj = null; + if (target instanceof L2Character) + { + obj = (L2Character) target; + } + + if ((obj == null) || !GeoData.getInstance().canSeeTarget(actor, obj) || obj.isDead() || (obj != MostHate) || (obj == actor) || (obj == getAttackTarget())) + { + continue; + } + if (obj instanceof L2PcInstance) + { + if (MostHate != null) + { + actor.addDamageHate(obj, 0, actor.getHating(MostHate)); + } + else + { + actor.addDamageHate(obj, 0, 2000); + } + actor.setTarget(obj); + setAttackTarget(obj); + + } + else if (obj instanceof L2Attackable) + { + if ((actor.getEnemyClan() != null) && actor.getEnemyClan().equals(((L2Attackable) obj).getClan())) + { + actor.addDamageHate(obj, 0, actor.getHating(MostHate)); + actor.setTarget(obj); + } + if (actor.getIsChaos() != 0) + { + if ((((L2Attackable) obj).getFactionId() != null) && ((L2Attackable) obj).getFactionId().equals(actor.getFactionId())) + { + continue; + } + if (MostHate != null) + { + actor.addDamageHate(obj, 0, actor.getHating(MostHate)); + } + else + { + actor.addDamageHate(obj, 0, 2000); + } + actor.setTarget(obj); + setAttackTarget(obj); + } + } + else if (obj instanceof L2Summon) + { + if (MostHate != null) + { + actor.addDamageHate(obj, 0, actor.getHating(MostHate)); + } + else + { + actor.addDamageHate(obj, 0, 2000); + } + actor.setTarget(obj); + setAttackTarget(obj); + } + } + } + } + + @SuppressWarnings("null") + private void aggroReconsider() + { + + L2Attackable actor = getActiveChar(); + L2Character MostHate = actor.getMostHated(); + + if (actor.getHateList() != null) + { + + int rand = Rnd.get(actor.getHateList().size()); + int count = 0; + for (L2Character obj : actor.getHateList()) + { + if (count < rand) + { + count++; + continue; + } + + if ((obj == null) || !GeoData.getInstance().canSeeTarget(actor, obj) || obj.isDead() || (obj == getAttackTarget()) || (obj == actor)) + { + continue; + } + + try + { + actor.setTarget(getAttackTarget()); + } + catch (NullPointerException e) + { + continue; + } + if (MostHate != null) + { + actor.addDamageHate(obj, 0, actor.getHating(MostHate)); + } + else + { + actor.addDamageHate(obj, 0, 2000); + } + actor.setTarget(obj); + setAttackTarget(obj); + return; + + } + } + + if (!(actor instanceof L2GuardInstance)) + { + Collection objs = actor.getKnownList().getKnownObjects().values(); + for (L2Object target : objs) + { + L2Character obj = null; + if (target instanceof L2Character) + { + obj = (L2Character) target; + } + else + { + continue; + } + if ((obj == null) || !GeoData.getInstance().canSeeTarget(actor, obj) || obj.isDead() || (obj != MostHate) || (obj == actor)) + { + continue; + } + if (obj instanceof L2PcInstance) + { + if ((MostHate != null) || !MostHate.isDead()) + { + actor.addDamageHate(obj, 0, actor.getHating(MostHate)); + } + else + { + actor.addDamageHate(obj, 0, 2000); + } + actor.setTarget(obj); + setAttackTarget(obj); + + } + else if (obj instanceof L2Attackable) + { + if ((actor.getEnemyClan() != null) && actor.getEnemyClan().equals(((L2Attackable) obj).getClan())) + { + if (MostHate != null) + { + actor.addDamageHate(obj, 0, actor.getHating(MostHate)); + } + else + { + actor.addDamageHate(obj, 0, 2000); + } + actor.setTarget(obj); + } + if (actor.getIsChaos() != 0) + { + if ((((L2Attackable) obj).getFactionId() != null) && ((L2Attackable) obj).getFactionId().equals(actor.getFactionId())) + { + continue; + } + if (MostHate != null) + { + actor.addDamageHate(obj, 0, actor.getHating(MostHate)); + } + else + { + actor.addDamageHate(obj, 0, 2000); + } + actor.setTarget(obj); + setAttackTarget(obj); + } + } + else if (obj instanceof L2Summon) + { + if (MostHate != null) + { + actor.addDamageHate(obj, 0, actor.getHating(MostHate)); + } + else + { + actor.addDamageHate(obj, 0, 2000); + } + actor.setTarget(obj); + setAttackTarget(obj); + } + } + } + } + + private void lSkillRender() + { + if (_skillrender._Lrangeskills == null) + { + _skillrender._Lrangeskills = getActiveChar().getLrangeSkill(); + } + } + + private void sSkillRender() + { + if (_skillrender._Srangeskills == null) + { + _skillrender._Srangeskills = getActiveChar().getSrangeSkill(); + } + } + + /** + * Manage AI thinking actions of a L2Attackable.
    + *
    + */ + @Override + protected void onEvtThink() + { + // Check if the actor can't use skills and if a thinking action isn't already in progress + if (_thinking || getActiveChar().isAllSkillsDisabled()) + { + return; + } + + // Start thinking action + _thinking = true; + + try + { + // Manage AI thinks of a L2Attackable + switch (getIntention()) + { + case AI_INTENTION_ACTIVE: + thinkActive(); + break; + case AI_INTENTION_ATTACK: + thinkAttack(); + break; + case AI_INTENTION_CAST: + thinkCast(); + break; + } + } + catch (Exception e) + { + _log.log(Level.WARNING, this + " - onEvtThink() failed: " + e.getMessage(), e); + } + finally + { + // Stop thinking action + _thinking = false; + } + } + + /** + * Launch actions corresponding to the Event Attacked.
    + *
    + * Actions :
    + *
    + *
  • Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
  • Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
  • Set the Intention to AI_INTENTION_ATTACK
  • + *
    + *
    + * @param attacker The L2Character that attacks the actor + */ + @Override + protected void onEvtAttacked(L2Character attacker) + { + L2Attackable me = getActiveChar(); + + // Calculate the attack timeout + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks(); + + // Set the _globalAggro to 0 to permit attack even just after spawn + if (_globalAggro < 0) + { + _globalAggro = 0; + } + + // Add the attacker to the _aggroList of the actor + me.addDamageHate(attacker, 0, 1); + + // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance + if (!me.isRunning()) + { + me.setRunning(); + } + + // Set the Intention to AI_INTENTION_ATTACK + if (getIntention() != AI_INTENTION_ATTACK) + { + setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker); + } + else if (me.getMostHated() != getAttackTarget()) + { + setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker); + } + + if (me instanceof L2MonsterInstance) + { + L2MonsterInstance master = (L2MonsterInstance) me; + + if (master.hasMinions()) + { + master.getMinionList().onAssist(me, attacker); + } + + master = master.getLeader(); + if ((master != null) && master.hasMinions()) + { + master.getMinionList().onAssist(me, attacker); + } + } + + super.onEvtAttacked(attacker); + } + + /** + * Launch actions corresponding to the Event Aggression.
    + *
    + * Actions :
    + *
    + *
  • Add the target to the actor _aggroList or update hate if already present
  • Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)

  • + *
    + * @param attacker The L2Character that attacks + * @param aggro The value of hate to add to the actor against the target + */ + @Override + protected void onEvtAggression(L2Character target, int aggro) + { + L2Attackable me = getActiveChar(); + + if (target != null) + { + // Add the target to the actor _aggroList or update hate if already present + me.addDamageHate(target, 0, aggro); + + // Set the actor AI Intention to AI_INTENTION_ATTACK + if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK) + { + // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance + if (!me.isRunning()) + { + me.setRunning(); + } + + setIntention(CtrlIntention.AI_INTENTION_ATTACK, target); + } + + if (me instanceof L2MonsterInstance) + { + L2MonsterInstance master = (L2MonsterInstance) me; + + if (master.hasMinions()) + { + master.getMinionList().onAssist(me, target); + } + + master = master.getLeader(); + if ((master != null) && master.hasMinions()) + { + master.getMinionList().onAssist(me, target); + } + } + } + } + + @Override + protected void onIntentionActive() + { + // Cancel attack timeout + _attackTimeout = Integer.MAX_VALUE; + super.onIntentionActive(); + } + + public void setGlobalAggro(int value) + { + _globalAggro = value; + } + + /** + * @param timepass The timepass to set. + */ + public void setTimepass(int TP) + { + timepass = TP; + } + + /** + * @return Returns the timepass. + */ + public int getTimepass() + { + return timepass; + } + + public L2Attackable getActiveChar() + { + return (L2Attackable) _actor; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2BoatAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2BoatAI.java new file mode 100644 index 00000000..71a1150f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2BoatAI.java @@ -0,0 +1,80 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.actor.instance.L2BoatInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.serverpackets.VehicleDeparture; +import com.l2jserver.gameserver.network.serverpackets.VehicleInfo; +import com.l2jserver.gameserver.network.serverpackets.VehicleStarted; + +/** + * @author DS + */ +public class L2BoatAI extends L2VehicleAI +{ + public L2BoatAI(L2BoatInstance.AIAccessor accessor) + { + super(accessor); + } + + @Override + protected void moveTo(int x, int y, int z) + { + if (!_actor.isMovementDisabled()) + { + if (!_clientMoving) + { + _actor.broadcastPacket(new VehicleStarted(getActor(), 1)); + } + + _clientMoving = true; + _accessor.moveTo(x, y, z); + _actor.broadcastPacket(new VehicleDeparture(getActor())); + } + } + + @Override + protected void clientStopMoving(L2CharPosition pos) + { + if (_actor.isMoving()) + { + _accessor.stopMove(pos); + } + + if (_clientMoving || (pos != null)) + { + _clientMoving = false; + _actor.broadcastPacket(new VehicleStarted(getActor(), 0)); + _actor.broadcastPacket(new VehicleInfo(getActor())); + } + } + + @Override + public void describeStateToPlayer(L2PcInstance player) + { + if (_clientMoving) + { + player.sendPacket(new VehicleDeparture(getActor())); + } + } + + @Override + public L2BoatInstance getActor() + { + return (L2BoatInstance) _actor; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2CharacterAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2CharacterAI.java new file mode 100644 index 00000000..f9adaf18 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2CharacterAI.java @@ -0,0 +1,1611 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_CAST; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_INTERACT; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_MOVE_TO; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_PICK_UP; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_REST; + +import java.util.List; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.GeoData; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2Effect; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2ItemInstance.ItemLocation; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.AutoAttackStop; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.templates.item.L2Weapon; +import com.l2jserver.gameserver.templates.item.L2WeaponType; +import com.l2jserver.gameserver.templates.skills.L2SkillType; +import com.l2jserver.util.Point3D; +import com.l2jserver.util.Rnd; + +/** + * This class manages AI of L2Character.
    + *
    + * L2CharacterAI :
    + *
    + *
  • L2AttackableAI
  • L2DoorAI
  • L2PlayerAI
  • L2SummonAI

  • + *
    + */ +public class L2CharacterAI extends AbstractAI +{ + public static class IntentionCommand + { + protected final CtrlIntention _crtlIntention; + protected final Object _arg0, _arg1; + + protected IntentionCommand(CtrlIntention pIntention, Object pArg0, Object pArg1) + { + _crtlIntention = pIntention; + _arg0 = pArg0; + _arg1 = pArg1; + } + + public CtrlIntention getCtrlIntention() + { + return _crtlIntention; + } + } + + /** + * Constructor of L2CharacterAI.
    + *
    + * @param accessor The AI accessor of the L2Character + */ + public L2CharacterAI(L2Character.AIAccessor accessor) + { + super(accessor); + } + + public IntentionCommand getNextIntention() + { + return null; + } + + @Override + protected void onEvtAttacked(L2Character attacker) + { + if ((attacker instanceof L2Attackable) && !((L2Attackable) attacker).isCoreAIDisabled()) + { + clientStartAutoAttack(); + } + } + + /** + * Manage the Idle Intention : Stop Attack, Movement and Stand Up the actor.
    + *
    + * Actions :
    + *
    + *
  • Set the AI Intention to AI_INTENTION_IDLE
  • Init cast and attack target
  • Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Stop the actor movement server side AND client side by sending Server->Client packet + * StopMove/StopRotation (broadcast)
  • Stand up the actor server side AND client side by sending Server->Client packet ChangeWaitType (broadcast)

  • + *
    + */ + @Override + protected void onIntentionIdle() + { + // Set the AI Intention to AI_INTENTION_IDLE + changeIntention(AI_INTENTION_IDLE, null, null); + + // Init cast and attack target + setCastTarget(null); + setAttackTarget(null); + + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(null); + + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + clientStopAutoAttack(); + + } + + /** + * Manage the Active Intention : Stop Attack, Movement and Launch Think Event.
    + *
    + * Actions : if the Intention is not already Active
    + *
    + *
  • Set the AI Intention to AI_INTENTION_ACTIVE
  • Init cast and attack target
  • Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Stop the actor movement server side AND client side by sending Server->Client packet + * StopMove/StopRotation (broadcast)
  • Launch the Think Event

  • + *
    + */ + @Override + protected void onIntentionActive() + { + // Check if the Intention is not already Active + if (getIntention() != AI_INTENTION_ACTIVE) + { + // Set the AI Intention to AI_INTENTION_ACTIVE + changeIntention(AI_INTENTION_ACTIVE, null, null); + + // Init cast and attack target + setCastTarget(null); + setAttackTarget(null); + + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(null); + + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + clientStopAutoAttack(); + + // Also enable random animations for this L2Character if allowed + // This is only for mobs - town npcs are handled in their constructor + if (_actor instanceof L2Attackable) + { + ((L2Npc) _actor).startRandomAnimationTimer(); + } + + // Launch the Think Event + onEvtThink(); + } + } + + /** + * Manage the Rest Intention.
    + *
    + * Actions :
    + *
    + *
  • Set the AI Intention to AI_INTENTION_IDLE

  • + *
    + */ + @Override + protected void onIntentionRest() + { + // Set the AI Intention to AI_INTENTION_IDLE + setIntention(AI_INTENTION_IDLE); + } + + /** + * Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event.
    + *
    + * Actions :
    + *
    + *
  • Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Set the Intention of this AI to AI_INTENTION_ATTACK
  • Set or change the AI attack target
  • Start the actor Auto Attack client side by sending Server->Client packet + * AutoAttackStart (broadcast)
  • Launch the Think Event

  • + *
    + * Overridden in :
    + *
    + *
  • L2AttackableAI : Calculate attack timeout

  • + *
    + */ + @Override + protected void onIntentionAttack(L2Character target) + { + if (target == null) + { + clientActionFailed(); + return; + } + + if (getIntention() == AI_INTENTION_REST) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + if (_actor.isAllSkillsDisabled() || _actor.isCastingNow() || _actor.isAfraid()) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + // Check if the Intention is already AI_INTENTION_ATTACK + if (getIntention() == AI_INTENTION_ATTACK) + { + // Check if the AI already targets the L2Character + if (getAttackTarget() != target) + { + // Set the AI attack target (change target) + setAttackTarget(target); + + stopFollow(); + + // Launch the Think Event + notifyEvent(CtrlEvent.EVT_THINK, null); + + } + else + { + clientActionFailed(); // else client freezes until cancel target + } + } + else + { + // Set the Intention of this AbstractAI to AI_INTENTION_ATTACK + changeIntention(AI_INTENTION_ATTACK, target, null); + + // Set the AI attack target + setAttackTarget(target); + + stopFollow(); + + // Launch the Think Event + notifyEvent(CtrlEvent.EVT_THINK, null); + } + } + + /** + * Manage the Cast Intention : Stop current Attack, Init the AI in order to cast and Launch Think Event.
    + *
    + * Actions :
    + *
    + *
  • Set the AI cast target
  • Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor
  • Set the AI skill used by INTENTION_CAST
  • + *
  • Set the Intention of this AI to AI_INTENTION_CAST
  • Launch the Think Event

  • + *
    + */ + @Override + protected void onIntentionCast(L2Skill skill, L2Object target) + { + if ((getIntention() == AI_INTENTION_REST) && skill.isMagic()) + { + clientActionFailed(); + _actor.setIsCastingNow(false); + return; + } + + // Set the AI cast target + setCastTarget((L2Character) target); + + // Stop actions client-side to cast the skill + if (skill.getHitTime() > 50) + { + // Abort the attack of the L2Character and send Server->Client ActionFailed packet + _actor.abortAttack(); + + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + // no need for second ActionFailed packet, abortAttack() already sent it + // clientActionFailed(); + } + + // Set the AI skill used by INTENTION_CAST + _skill = skill; + + // Change the Intention of this AbstractAI to AI_INTENTION_CAST + changeIntention(AI_INTENTION_CAST, skill, target); + + // Launch the Think Event + notifyEvent(CtrlEvent.EVT_THINK, null); + } + + /** + * Manage the Move To Intention : Stop current Attack and Launch a Move to Location Task.
    + *
    + * Actions :
    + *
    + *
  • Stop the actor auto-attack server side AND client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Set the Intention of this AI to AI_INTENTION_MOVE_TO
  • Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet + * CharMoveToLocation (broadcast)

  • + *
    + */ + @Override + protected void onIntentionMoveTo(L2CharPosition pos) + { + if (getIntention() == AI_INTENTION_REST) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + if (_actor.isAllSkillsDisabled() || _actor.isCastingNow()) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + // Set the Intention of this AbstractAI to AI_INTENTION_MOVE_TO + changeIntention(AI_INTENTION_MOVE_TO, pos, null); + + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + clientStopAutoAttack(); + + // Abort the attack of the L2Character and send Server->Client ActionFailed packet + _actor.abortAttack(); + + // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) + moveTo(pos.x, pos.y, pos.z); + } + + /** + * Manage the Follow Intention : Stop current Attack and Launch a Follow Task.
    + *
    + * Actions :
    + *
    + *
  • Stop the actor auto-attack server side AND client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Set the Intention of this AI to AI_INTENTION_FOLLOW
  • Create and Launch an AI Follow Task to execute every 1s

  • + *
    + */ + @Override + protected void onIntentionFollow(L2Character target) + { + if (getIntention() == AI_INTENTION_REST) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + if (_actor.isAllSkillsDisabled() || _actor.isCastingNow()) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + if (_actor.isMovementDisabled()) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + // Dead actors can`t follow + if (_actor.isDead()) + { + clientActionFailed(); + return; + } + + // do not follow yourself + if (_actor == target) + { + clientActionFailed(); + return; + } + + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + clientStopAutoAttack(); + + // Set the Intention of this AbstractAI to AI_INTENTION_FOLLOW + changeIntention(AI_INTENTION_FOLLOW, target, null); + + // Create and Launch an AI Follow Task to execute every 1s + startFollow(target); + } + + /** + * Manage the PickUp Intention : Set the pick up target and Launch a Move To Pawn Task (offset=20).
    + *
    + * Actions :
    + *
    + *
  • Set the AI pick up target
  • Set the Intention of this AI to AI_INTENTION_PICK_UP
  • Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast)

  • + *
    + */ + @Override + protected void onIntentionPickUp(L2Object object) + { + if (getIntention() == AI_INTENTION_REST) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + if (_actor.isAllSkillsDisabled() || _actor.isCastingNow()) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + clientStopAutoAttack(); + + if ((object instanceof L2ItemInstance) && (((L2ItemInstance) object).getLocation() != ItemLocation.VOID)) + { + return; + } + + // Set the Intention of this AbstractAI to AI_INTENTION_PICK_UP + changeIntention(AI_INTENTION_PICK_UP, object, null); + + // Set the AI pick up target + setTarget(object); + if ((object.getX() == 0) && (object.getY() == 0)) // TODO: Find the drop&spawn bug + { + _log.warning("Object in coords 0,0 - using a temporary fix"); + object.setXYZ(getActor().getX(), getActor().getY(), getActor().getZ() + 5); + } + + // Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast) + moveToPawn(object, 20); + } + + /** + * Manage the Interact Intention : Set the interact target and Launch a Move To Pawn Task (offset=60).
    + *
    + * Actions :
    + *
    + *
  • Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Set the AI interact target
  • Set the Intention of this AI to AI_INTENTION_INTERACT
  • Move the actor to Pawn server side AND client side by sending Server->Client + * packet MoveToPawn (broadcast)

  • + *
    + */ + @Override + protected void onIntentionInteract(L2Object object) + { + if (getIntention() == AI_INTENTION_REST) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + if (_actor.isAllSkillsDisabled() || _actor.isCastingNow()) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + clientStopAutoAttack(); + + if (getIntention() != AI_INTENTION_INTERACT) + { + // Set the Intention of this AbstractAI to AI_INTENTION_INTERACT + changeIntention(AI_INTENTION_INTERACT, object, null); + + // Set the AI interact target + setTarget(object); + + // Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast) + moveToPawn(object, 60); + } + } + + /** + * Do nothing.
    + *
    + */ + @Override + protected void onEvtThink() + { + // do nothing + } + + /** + * Do nothing.
    + *
    + */ + @Override + protected void onEvtAggression(L2Character target, int aggro) + { + // do nothing + } + + /** + * Launch actions corresponding to the Event Stunned then onAttacked Event.
    + *
    + * Actions :
    + *
    + *
  • Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast)
  • Break an attack and send Server->Client ActionFailed + * packet and a System Message to the L2Character
  • Break a cast and send Server->Client ActionFailed packet and a System Message to the L2Character
  • Launch actions corresponding to the Event onAttacked (only for L2AttackableAI after the stunning periode)

  • + *
    + */ + @Override + protected void onEvtStunned(L2Character attacker) + { + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + _actor.broadcastPacket(new AutoAttackStop(_actor.getObjectId())); + if (AttackStanceTaskManager.getInstance().getAttackStanceTask(_actor)) + { + AttackStanceTaskManager.getInstance().removeAttackStanceTask(_actor); + } + + // Stop Server AutoAttack also + setAutoAttacking(false); + + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(null); + + // Launch actions corresponding to the Event onAttacked (only for L2AttackableAI after the stunning periode) + onEvtAttacked(attacker); + } + + @Override + protected void onEvtParalyzed(L2Character attacker) + { + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + _actor.broadcastPacket(new AutoAttackStop(_actor.getObjectId())); + if (AttackStanceTaskManager.getInstance().getAttackStanceTask(_actor)) + { + AttackStanceTaskManager.getInstance().removeAttackStanceTask(_actor); + } + + // Stop Server AutoAttack also + setAutoAttacking(false); + + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(null); + + // Launch actions corresponding to the Event onAttacked (only for L2AttackableAI after the stunning periode) + onEvtAttacked(attacker); + } + + /** + * Launch actions corresponding to the Event Sleeping.
    + *
    + * Actions :
    + *
    + *
  • Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast)
  • Break an attack and send Server->Client ActionFailed + * packet and a System Message to the L2Character
  • Break a cast and send Server->Client ActionFailed packet and a System Message to the L2Character

  • + *
    + */ + @Override + protected void onEvtSleeping(L2Character attacker) + { + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + _actor.broadcastPacket(new AutoAttackStop(_actor.getObjectId())); + if (AttackStanceTaskManager.getInstance().getAttackStanceTask(_actor)) + { + AttackStanceTaskManager.getInstance().removeAttackStanceTask(_actor); + } + + // stop Server AutoAttack also + setAutoAttacking(false); + + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(null); + } + + /** + * Launch actions corresponding to the Event Rooted.
    + *
    + * Actions :
    + *
    + *
  • Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast)
  • Launch actions corresponding to the Event onAttacked

  • + *
    + */ + @Override + protected void onEvtRooted(L2Character attacker) + { + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + // _actor.broadcastPacket(new AutoAttackStop(_actor.getObjectId())); + // if (AttackStanceTaskManager.getInstance().getAttackStanceTask(_actor)) + // AttackStanceTaskManager.getInstance().removeAttackStanceTask(_actor); + + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(null); + + // Launch actions corresponding to the Event onAttacked + onEvtAttacked(attacker); + + } + + /** + * Launch actions corresponding to the Event Confused.
    + *
    + * Actions :
    + *
    + *
  • Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast)
  • Launch actions corresponding to the Event onAttacked

  • + *
    + */ + @Override + protected void onEvtConfused(L2Character attacker) + { + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(null); + + // Launch actions corresponding to the Event onAttacked + onEvtAttacked(attacker); + } + + /** + * Launch actions corresponding to the Event Muted.
    + *
    + * Actions :
    + *
    + *
  • Break a cast and send Server->Client ActionFailed packet and a System Message to the L2Character

  • + *
    + */ + @Override + protected void onEvtMuted(L2Character attacker) + { + // Break a cast and send Server->Client ActionFailed packet and a System Message to the L2Character + onEvtAttacked(attacker); + } + + /** + * Do nothing.
    + *
    + */ + @Override + protected void onEvtEvaded(L2Character attacker) + { + // do nothing + } + + /** + * Launch actions corresponding to the Event ReadyToAct.
    + *
    + * Actions :
    + *
    + *
  • Launch actions corresponding to the Event Think

  • + *
    + */ + @Override + protected void onEvtReadyToAct() + { + // Launch actions corresponding to the Event Think + onEvtThink(); + } + + /** + * Do nothing.
    + *
    + */ + @Override + protected void onEvtUserCmd(Object arg0, Object arg1) + { + // do nothing + } + + /** + * Launch actions corresponding to the Event Arrived.
    + *
    + * Actions :
    + *
    + *
  • If the Intention was AI_INTENTION_MOVE_TO, set the Intention to AI_INTENTION_ACTIVE
  • Launch actions corresponding to the Event Think

  • + *
    + */ + @Override + protected void onEvtArrived() + { + _accessor.getActor().revalidateZone(true); + + if (_accessor.getActor().moveToNextRoutePoint()) + { + return; + } + + if (_accessor.getActor() instanceof L2Attackable) + { + ((L2Attackable) _accessor.getActor()).setisReturningToSpawnPoint(false); + } + clientStoppedMoving(); + + // If the Intention was AI_INTENTION_MOVE_TO, set the Intention to AI_INTENTION_ACTIVE + if (getIntention() == AI_INTENTION_MOVE_TO) + { + setIntention(AI_INTENTION_ACTIVE); + } + + // Launch actions corresponding to the Event Think + onEvtThink(); + } + + /** + * Launch actions corresponding to the Event ArrivedRevalidate.
    + *
    + * Actions :
    + *
    + *
  • Launch actions corresponding to the Event Think

  • + *
    + */ + @Override + protected void onEvtArrivedRevalidate() + { + // Launch actions corresponding to the Event Think + onEvtThink(); + } + + /** + * Launch actions corresponding to the Event ArrivedBlocked.
    + *
    + * Actions :
    + *
    + *
  • Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast)
  • If the Intention was AI_INTENTION_MOVE_TO, set the Intention to AI_INTENTION_ACTIVE
  • Launch actions corresponding to the Event Think

  • + *
    + */ + @Override + protected void onEvtArrivedBlocked(L2CharPosition blocked_at_pos) + { + // If the Intention was AI_INTENTION_MOVE_TO, set the Intention to AI_INTENTION_ACTIVE + if ((getIntention() == AI_INTENTION_MOVE_TO) || (getIntention() == AI_INTENTION_CAST)) + { + setIntention(AI_INTENTION_ACTIVE); + } + + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(blocked_at_pos); + + /* + * if (Config.ACTIVATE_POSITION_RECORDER && Universe.getInstance().shouldLog(_accessor.getActor().getObjectId())) { if (!_accessor.getActor().isFlying()) Universe.getInstance().registerObstacle(blocked_at_pos.x, blocked_at_pos.y, blocked_at_pos.z); if (_accessor.getActor() instanceof + * L2PcInstance) ((L2PcInstance) _accessor.getActor()).explore(); } + */ + + // Launch actions corresponding to the Event Think + onEvtThink(); + } + + /** + * Launch actions corresponding to the Event ForgetObject.
    + *
    + * Actions :
    + *
    + *
  • If the object was targeted and the Intention was AI_INTENTION_INTERACT or AI_INTENTION_PICK_UP, set the Intention to AI_INTENTION_ACTIVE
  • If the object was targeted to attack, stop the auto-attack, cancel target and set the Intention to AI_INTENTION_ACTIVE
  • If the object + * was targeted to cast, cancel target and set the Intention to AI_INTENTION_ACTIVE
  • If the object was targeted to follow, stop the movement, cancel AI Follow Task and set the Intention to AI_INTENTION_ACTIVE
  • If the targeted object was the actor , cancel AI target, stop AI + * Follow Task, stop the movement and set the Intention to AI_INTENTION_IDLE

  • + *
    + */ + @Override + protected void onEvtForgetObject(L2Object object) + { + // If the object was targeted and the Intention was AI_INTENTION_INTERACT or AI_INTENTION_PICK_UP, set the Intention to AI_INTENTION_ACTIVE + if (getTarget() == object) + { + setTarget(null); + + if (getIntention() == AI_INTENTION_INTERACT) + { + setIntention(AI_INTENTION_ACTIVE); + } + else if (getIntention() == AI_INTENTION_PICK_UP) + { + setIntention(AI_INTENTION_ACTIVE); + } + } + + // Check if the object was targeted to attack + if (getAttackTarget() == object) + { + // Cancel attack target + setAttackTarget(null); + + // Set the Intention of this AbstractAI to AI_INTENTION_ACTIVE + setIntention(AI_INTENTION_ACTIVE); + } + + // Check if the object was targeted to cast + if (getCastTarget() == object) + { + // Cancel cast target + setCastTarget(null); + + // Set the Intention of this AbstractAI to AI_INTENTION_ACTIVE + setIntention(AI_INTENTION_ACTIVE); + } + + // Check if the object was targeted to follow + if (getFollowTarget() == object) + { + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(null); + + // Stop an AI Follow Task + stopFollow(); + + // Set the Intention of this AbstractAI to AI_INTENTION_ACTIVE + setIntention(AI_INTENTION_ACTIVE); + } + + // Check if the targeted object was the actor + if (_actor == object) + { + // Cancel AI target + setTarget(null); + setAttackTarget(null); + setCastTarget(null); + + // Stop an AI Follow Task + stopFollow(); + + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(null); + + // Set the Intention of this AbstractAI to AI_INTENTION_IDLE + changeIntention(AI_INTENTION_IDLE, null, null); + } + } + + /** + * Launch actions corresponding to the Event Cancel.
    + *
    + * Actions :
    + *
    + *
  • Stop an AI Follow Task
  • Launch actions corresponding to the Event Think

  • + *
    + */ + @Override + protected void onEvtCancel() + { + _actor.abortCast(); + + // Stop an AI Follow Task + stopFollow(); + + if (!AttackStanceTaskManager.getInstance().getAttackStanceTask(_actor)) + { + _actor.broadcastPacket(new AutoAttackStop(_actor.getObjectId())); + } + + // Launch actions corresponding to the Event Think + onEvtThink(); + } + + /** + * Launch actions corresponding to the Event Dead.
    + *
    + * Actions :
    + *
    + *
  • Stop an AI Follow Task
  • Kill the actor client side by sending Server->Client packet AutoAttackStop, StopMove/StopRotation, Die (broadcast)

  • + *
    + */ + @Override + protected void onEvtDead() + { + // Stop an AI Tasks + stopAITask(); + + // Kill the actor client side by sending Server->Client packet AutoAttackStop, StopMove/StopRotation, Die (broadcast) + clientNotifyDead(); + + if (!(_actor instanceof L2Playable)) + { + _actor.setWalking(); + } + } + + /** + * Launch actions corresponding to the Event Fake Death.
    + *
    + * Actions :
    + *
    + *
  • Stop an AI Follow Task
  • + */ + @Override + protected void onEvtFakeDeath() + { + // Stop an AI Follow Task + stopFollow(); + + // Stop the actor movement and send Server->Client packet StopMove/StopRotation (broadcast) + clientStopMoving(null); + + // Init AI + _intention = AI_INTENTION_IDLE; + setTarget(null); + setCastTarget(null); + setAttackTarget(null); + } + + /** + * Do nothing.
    + *
    + */ + @Override + protected void onEvtFinishCasting() + { + // do nothing + } + + protected boolean maybeMoveToPosition(Point3D worldPosition, int offset) + { + if (worldPosition == null) + { + _log.warning("maybeMoveToPosition: worldPosition == NULL!"); + return false; + } + + if (offset < 0) + { + return false; // skill radius -1 + } + + if (!_actor.isInsideRadius(worldPosition.getX(), worldPosition.getY(), offset + _actor.getTemplate().collisionRadius, false)) + { + if (_actor.isMovementDisabled()) + { + return true; + } + + if (!_actor.isRunning() && !(this instanceof L2PlayerAI) && !(this instanceof L2SummonAI)) + { + _actor.setRunning(); + } + + stopFollow(); + + int x = _actor.getX(); + int y = _actor.getY(); + + double dx = worldPosition.getX() - x; + double dy = worldPosition.getY() - y; + + double dist = Math.sqrt((dx * dx) + (dy * dy)); + + double sin = dy / dist; + double cos = dx / dist; + + dist -= offset - 5; + + x += (int) (dist * cos); + y += (int) (dist * sin); + + moveTo(x, y, worldPosition.getZ()); + return true; + } + + if (getFollowTarget() != null) + { + stopFollow(); + } + + return false; + } + + /** + * Manage the Move to Pawn action in function of the distance and of the Interact area.
    + *
    + * Actions :
    + *
    + *
  • Get the distance between the current position of the L2Character and the target (x,y)
  • If the distance > offset+20, move the actor (by running) to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast)
  • If the distance <= offset+20, Stop + * the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast)

  • + *
    + * Example of use :
    + *
    + *
  • L2PLayerAI, L2SummonAI

  • + *
    + * @param target The targeted L2Object + * @param offset The Interact area radius + * @return True if a movement must be done + */ + protected boolean maybeMoveToPawn(L2Object target, int offset) + { + // Get the distance between the current position of the L2Character and the target (x,y) + if (target == null) + { + _log.warning("maybeMoveToPawn: target == NULL!"); + return false; + } + if (offset < 0) + { + return false; // skill radius -1 + } + + offset += _actor.getTemplate().collisionRadius; + if (target instanceof L2Character) + { + offset += ((L2Character) target).getTemplate().collisionRadius; + } + + if (!_actor.isInsideRadius(target, offset, false, false)) + { + // Caller should be L2Playable and thinkAttack/thinkCast/thinkInteract/thinkPickUp + if (getFollowTarget() != null) + { + + // allow larger hit range when the target is moving (check is run only once per second) + if (!_actor.isInsideRadius(target, offset + 100, false, false)) + { + return true; + } + stopFollow(); + return false; + } + + if (_actor.isMovementDisabled()) + { + // If player is trying attack target but he cannot move to attack target + // change his intention to idle + if (_actor.getAI().getIntention() == CtrlIntention.AI_INTENTION_ATTACK) + { + _actor.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); + } + + return true; + } + + // while flying there is no move to cast + if ((_actor.getAI().getIntention() == CtrlIntention.AI_INTENTION_CAST) && (_actor instanceof L2PcInstance) && ((L2PcInstance) _actor).isTransformed()) + { + if (!((L2PcInstance) _actor).getTransformation().canStartFollowToCast()) + { + ((L2PcInstance) _actor).sendPacket(SystemMessage.getSystemMessage(SystemMessageId.DIST_TOO_FAR_CASTING_STOPPED)); + ((L2PcInstance) _actor).sendPacket(ActionFailed.STATIC_PACKET); + + return true; + } + } + + // If not running, set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance + if (!_actor.isRunning() && !(this instanceof L2PlayerAI) && !(this instanceof L2SummonAI)) + { + _actor.setRunning(); + } + + stopFollow(); + if ((target instanceof L2Character) && !(target instanceof L2DoorInstance)) + { + if (((L2Character) target).isMoving()) + { + offset -= 100; + } + if (offset < 5) + { + offset = 5; + } + + startFollow((L2Character) target, offset); + } + else + { + // Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast) + moveToPawn(target, offset); + } + return true; + } + + if (getFollowTarget() != null) + { + stopFollow(); + } + + // Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast) + // clientStopMoving(null); + return false; + } + + /** + * Modify current Intention and actions if the target is lost or dead.
    + *
    + * Actions : If the target is lost or dead
    + *
    + *
  • Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast)
  • Set the Intention of this AbstractAI to + * AI_INTENTION_ACTIVE

  • + *
    + * Example of use :
    + *
    + *
  • L2PLayerAI, L2SummonAI

  • + *
    + * @param target The targeted L2Object + * @return True if the target is lost or dead (false if fakedeath) + */ + protected boolean checkTargetLostOrDead(L2Character target) + { + if ((target == null) || target.isAlikeDead()) + { + // check if player is fakedeath + if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isFakeDeath()) + { + target.stopFakeDeath(true); + return false; + } + + // Set the Intention of this AbstractAI to AI_INTENTION_ACTIVE + setIntention(AI_INTENTION_ACTIVE); + return true; + } + return false; + } + + /** + * Modify current Intention and actions if the target is lost.
    + *
    + * Actions : If the target is lost
    + *
    + *
  • Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation (broadcast)
  • Set the Intention of this AbstractAI to + * AI_INTENTION_ACTIVE

  • + *
    + * Example of use :
    + *
    + *
  • L2PLayerAI, L2SummonAI

  • + *
    + * @param target The targeted L2Object + * @return True if the target is lost + */ + protected boolean checkTargetLost(L2Object target) + { + // check if player is fakedeath + if (target instanceof L2PcInstance) + { + L2PcInstance target2 = (L2PcInstance) target; // convert object to chara + + if (target2.isFakeDeath()) + { + target2.stopFakeDeath(true); + return false; + } + } + if (target == null) + { + // Set the Intention of this AbstractAI to AI_INTENTION_ACTIVE + setIntention(AI_INTENTION_ACTIVE); + return true; + } + if ((_actor != null) && (_skill != null) && _skill.isOffensive() && (_skill.getSkillRadius() > 0) && (Config.GEODATA > 0) && !GeoData.getInstance().canSeeTarget(_actor, target)) + { + setIntention(AI_INTENTION_ACTIVE); + return true; + } + return false; + } + + protected class SelfAnalysis + { + public boolean isMage = false; + public boolean isBalanced; + public boolean isArcher = false; + public boolean isHealer = false; + public boolean isFighter = false; + public boolean cannotMoveOnLand = false; + public List generalSkills = new FastList<>(); + public List buffSkills = new FastList<>(); + public int lastBuffTick = 0; + public List debuffSkills = new FastList<>(); + public int lastDebuffTick = 0; + public List cancelSkills = new FastList<>(); + public List healSkills = new FastList<>(); + // public List trickSkills = new FastList(); + public List generalDisablers = new FastList<>(); + public List sleepSkills = new FastList<>(); + public List rootSkills = new FastList<>(); + public List muteSkills = new FastList<>(); + public List resurrectSkills = new FastList<>(); + public boolean hasHealOrResurrect = false; + public boolean hasLongRangeSkills = false; + public boolean hasLongRangeDamageSkills = false; + public int maxCastRange = 0; + + public SelfAnalysis() + { + } + + public void init() + { + switch (((L2NpcTemplate) _actor.getTemplate()).getAIDataStatic().getAiType()) + { + case FIGHTER: + isFighter = true; + break; + case MAGE: + isMage = true; + break; + case CORPSE: + case BALANCED: + isBalanced = true; + break; + case ARCHER: + isArcher = true; + break; + case HEALER: + isHealer = true; + break; + default: + isFighter = true; + break; + } + // water movement analysis + if (_actor instanceof L2Npc) + { + int npcId = ((L2Npc) _actor).getNpcId(); + + switch (npcId) + { + case 20314: // great white shark + case 20849: // Light Worm + cannotMoveOnLand = true; + break; + default: + cannotMoveOnLand = false; + break; + } + } + // skill analysis + for (L2Skill sk : _actor.getAllSkills()) + { + if (sk.isPassive()) + { + continue; + } + int castRange = sk.getCastRange(); + boolean hasLongRangeDamageSkill = false; + switch (sk.getSkillType()) + { + case HEAL: + case HEAL_PERCENT: + case HEAL_STATIC: + case BALANCE_LIFE: + case HOT: + healSkills.add(sk); + hasHealOrResurrect = true; + continue; // won't be considered something for fighting + case BUFF: + buffSkills.add(sk); + continue; // won't be considered something for fighting + case PARALYZE: + case STUN: + // hardcoding petrification until improvements are made to + // EffectTemplate... petrification is totally different for + // AI than paralyze + switch (sk.getId()) + { + case 367: + case 4111: + case 4383: + case 4616: + case 4578: + sleepSkills.add(sk); + break; + default: + generalDisablers.add(sk); + break; + } + break; + case MUTE: + muteSkills.add(sk); + break; + case SLEEP: + sleepSkills.add(sk); + break; + case ROOT: + rootSkills.add(sk); + break; + case FEAR: // could be used as an alternative for healing? + case CONFUSION: + // trickSkills.add(sk); + case DEBUFF: + debuffSkills.add(sk); + break; + case CANCEL: + case NEGATE: + cancelSkills.add(sk); + break; + case RESURRECT: + resurrectSkills.add(sk); + hasHealOrResurrect = true; + break; + case NOTDONE: + case COREDONE: + continue; // won't be considered something for fighting + default: + generalSkills.add(sk); + hasLongRangeDamageSkill = true; + break; + } + if (castRange > 70) + { + hasLongRangeSkills = true; + if (hasLongRangeDamageSkill) + { + hasLongRangeDamageSkills = true; + } + } + if (castRange > maxCastRange) + { + maxCastRange = castRange; + } + + } + // Because of missing skills, some mages/balanced cannot play like mages + if (!hasLongRangeDamageSkills && isMage) + { + isBalanced = true; + isMage = false; + isFighter = false; + } + if (!hasLongRangeSkills && (isMage || isBalanced)) + { + isBalanced = false; + isMage = false; + isFighter = true; + } + if (generalSkills.isEmpty() && isMage) + { + isBalanced = true; + isMage = false; + } + } + } + + protected class TargetAnalysis + { + public L2Character character; + public boolean isMage; + public boolean isBalanced; + public boolean isArcher; + public boolean isFighter; + public boolean isCanceled; + public boolean isSlower; + public boolean isMagicResistant; + + public TargetAnalysis() + { + } + + public void update(L2Character target) + { + // update status once in 4 seconds + if ((target == character) && (Rnd.nextInt(100) > 25)) + { + return; + } + character = target; + if (target == null) + { + return; + } + isMage = false; + isBalanced = false; + isArcher = false; + isFighter = false; + isCanceled = false; + + if (target.getMAtk(null, null) > (1.5 * target.getPAtk(null))) + { + isMage = true; + } + else if (((target.getPAtk(null) * 0.8) < target.getMAtk(null, null)) || ((target.getMAtk(null, null) * 0.8) > target.getPAtk(null))) + { + isBalanced = true; + } + else + { + L2Weapon weapon = target.getActiveWeaponItem(); + if ((weapon != null) && ((weapon.getItemType() == L2WeaponType.BOW) || (weapon.getItemType() == L2WeaponType.CROSSBOW))) + { + isArcher = true; + } + else + { + isFighter = true; + } + } + if (target.getRunSpeed() < (_actor.getRunSpeed() - 3)) + { + isSlower = true; + } + else + { + isSlower = false; + } + if ((target.getMDef(null, null) * 1.2) > _actor.getMAtk(null, null)) + { + isMagicResistant = true; + } + else + { + isMagicResistant = false; + } + if (target.getBuffCount() < 4) + { + isCanceled = true; + } + } + } + + public boolean canAura(L2Skill sk) + { + if ((sk.getTargetType() == L2Skill.SkillTargetType.TARGET_AURA) || (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_BEHIND_AURA) || (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_FRONT_AURA)) + { + for (L2Object target : _actor.getKnownList().getKnownCharactersInRadius(sk.getSkillRadius())) + { + if (target == getAttackTarget()) + { + return true; + } + } + } + return false; + } + + public boolean canAOE(L2Skill sk) + { + if ((sk.getSkillType() != L2SkillType.NEGATE) || (sk.getSkillType() != L2SkillType.CANCEL)) + { + if ((sk.getTargetType() == L2Skill.SkillTargetType.TARGET_AURA) || (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_BEHIND_AURA) || (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_FRONT_AURA)) + { + boolean cancast = true; + for (L2Character target : _actor.getKnownList().getKnownCharactersInRadius(sk.getSkillRadius())) + { + if (!GeoData.getInstance().canSeeTarget(_actor, target)) + { + continue; + } + if (target instanceof L2Attackable) + { + L2Npc targets = ((L2Npc) target); + L2Npc actors = ((L2Npc) _actor); + + if ((targets.getEnemyClan() == null) || (actors.getClan() == null) || !targets.getEnemyClan().equals(actors.getClan()) || ((actors.getClan() == null) && (actors.getIsChaos() == 0))) + { + continue; + } + } + L2Effect[] effects = target.getAllEffects(); + for (int i = 0; (effects != null) && (i < effects.length); i++) + { + L2Effect effect = effects[i]; + if (effect.getSkill() == sk) + { + cancast = false; + break; + } + } + } + if (cancast) + { + return true; + } + } + else if ((sk.getTargetType() == L2Skill.SkillTargetType.TARGET_AREA) || (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_BEHIND_AREA) || (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_FRONT_AREA)) + { + boolean cancast = true; + for (L2Character target : getAttackTarget().getKnownList().getKnownCharactersInRadius(sk.getSkillRadius())) + { + if (!GeoData.getInstance().canSeeTarget(_actor, target) || (target == null)) + { + continue; + } + if (target instanceof L2Attackable) + { + L2Npc targets = ((L2Npc) target); + L2Npc actors = ((L2Npc) _actor); + if ((targets.getEnemyClan() == null) || (actors.getClan() == null) || !targets.getEnemyClan().equals(actors.getClan()) || ((actors.getClan() == null) && (actors.getIsChaos() == 0))) + { + continue; + } + } + L2Effect[] effects = target.getAllEffects(); + if (effects.length > 0) + { + cancast = true; + } + } + if (cancast) + { + return true; + } + } + } + else + { + if ((sk.getTargetType() == L2Skill.SkillTargetType.TARGET_AURA) || (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_BEHIND_AURA) || (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_FRONT_AURA)) + { + boolean cancast = false; + for (L2Character target : _actor.getKnownList().getKnownCharactersInRadius(sk.getSkillRadius())) + { + if (!GeoData.getInstance().canSeeTarget(_actor, target)) + { + continue; + } + if (target instanceof L2Attackable) + { + L2Npc targets = ((L2Npc) target); + L2Npc actors = ((L2Npc) _actor); + if ((targets.getEnemyClan() == null) || (actors.getClan() == null) || !targets.getEnemyClan().equals(actors.getClan()) || ((actors.getClan() == null) && (actors.getIsChaos() == 0))) + { + continue; + } + } + L2Effect[] effects = target.getAllEffects(); + if (effects.length > 0) + { + cancast = true; + } + } + if (cancast) + { + return true; + } + } + else if ((sk.getTargetType() == L2Skill.SkillTargetType.TARGET_AREA) || (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_BEHIND_AREA) || (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_FRONT_AREA)) + { + boolean cancast = true; + for (L2Character target : getAttackTarget().getKnownList().getKnownCharactersInRadius(sk.getSkillRadius())) + { + if (!GeoData.getInstance().canSeeTarget(_actor, target)) + { + continue; + } + if (target instanceof L2Attackable) + { + L2Npc targets = ((L2Npc) target); + L2Npc actors = ((L2Npc) _actor); + if ((targets.getEnemyClan() == null) || (actors.getClan() == null) || !targets.getEnemyClan().equals(actors.getClan()) || ((actors.getClan() == null) && (actors.getIsChaos() == 0))) + { + continue; + } + } + L2Effect[] effects = target.getAllEffects(); + for (int i = 0; (effects != null) && (i < effects.length); i++) + { + L2Effect effect = effects[i]; + if (effect.getSkill() == sk) + { + cancast = false; + break; + } + } + } + if (cancast) + { + return true; + } + } + } + return false; + } + + public boolean canParty(L2Skill sk) + { + if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_PARTY) + { + int count = 0; + int ccount = 0; + for (L2Character target : _actor.getKnownList().getKnownCharactersInRadius(sk.getSkillRadius())) + { + if (!(target instanceof L2Attackable) || !GeoData.getInstance().canSeeTarget(_actor, target)) + { + continue; + } + L2Npc targets = ((L2Npc) target); + L2Npc actors = ((L2Npc) _actor); + if ((actors.getFactionId() != null) && targets.getFactionId().equals(actors.getFactionId())) + { + count++; + L2Effect[] effects = target.getAllEffects(); + for (int i = 0; (effects != null) && (i < effects.length); i++) + { + + L2Effect effect = effects[i]; + if (effect.getSkill() == sk) + { + ccount++; + break; + } + } + } + } + if (ccount < count) + { + return true; + } + + } + return false; + } + + public boolean isParty(L2Skill sk) + { + if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_PARTY) + { + return true; + } + return false; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2ControllableMobAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2ControllableMobAI.java new file mode 100644 index 00000000..5dc8fca3 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2ControllableMobAI.java @@ -0,0 +1,589 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK; + +import java.util.Collection; +import java.util.List; + +import javolution.util.FastList; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.MobGroup; +import com.l2jserver.gameserver.model.MobGroupTable; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Character.AIAccessor; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.instance.L2ControllableMobInstance; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.Rnd; + +/** + * @author littlecrow AI for controllable mobs + */ +public class L2ControllableMobAI extends L2AttackableAI +{ + public static final int AI_IDLE = 1; + public static final int AI_NORMAL = 2; + public static final int AI_FORCEATTACK = 3; + public static final int AI_FOLLOW = 4; + public static final int AI_CAST = 5; + public static final int AI_ATTACK_GROUP = 6; + + private int _alternateAI; + + private boolean _isThinking; // to prevent thinking recursively + private boolean _isNotMoving; + + private L2Character _forcedTarget; + private MobGroup _targetGroup; + + protected void thinkFollow() + { + L2Attackable me = (L2Attackable) _actor; + + if (!Util.checkIfInRange(MobGroupTable.FOLLOW_RANGE, me, getForcedTarget(), true)) + { + int signX = (Rnd.nextInt(2) == 0) ? -1 : 1; + int signY = (Rnd.nextInt(2) == 0) ? -1 : 1; + int randX = Rnd.nextInt(MobGroupTable.FOLLOW_RANGE); + int randY = Rnd.nextInt(MobGroupTable.FOLLOW_RANGE); + + moveTo(getForcedTarget().getX() + (signX * randX), getForcedTarget().getY() + (signY * randY), getForcedTarget().getZ()); + } + } + + @Override + protected void onEvtThink() + { + if (isThinking()) + { + return; + } + + setThinking(true); + + try + { + switch (getAlternateAI()) + { + case AI_IDLE: + if (getIntention() != CtrlIntention.AI_INTENTION_ACTIVE) + { + setIntention(CtrlIntention.AI_INTENTION_ACTIVE); + } + break; + case AI_FOLLOW: + thinkFollow(); + break; + case AI_CAST: + thinkCast(); + break; + case AI_FORCEATTACK: + thinkForceAttack(); + break; + case AI_ATTACK_GROUP: + thinkAttackGroup(); + break; + default: + if (getIntention() == AI_INTENTION_ACTIVE) + { + thinkActive(); + } + else if (getIntention() == AI_INTENTION_ATTACK) + { + thinkAttack(); + } + break; + } + } + finally + { + setThinking(false); + } + } + + protected void thinkCast() + { + L2Attackable npc = (L2Attackable) _actor; + + if ((getAttackTarget() == null) || getAttackTarget().isAlikeDead()) + { + setAttackTarget(findNextRndTarget()); + clientStopMoving(null); + } + + if (getAttackTarget() == null) + { + return; + } + + npc.setTarget(getAttackTarget()); + + L2Skill[] skills = _actor.getAllSkills(); + + if (!_actor.isMuted()) + { + int max_range = 0; + // check distant skills + + for (L2Skill sk : skills) + { + if (Util.checkIfInRange(sk.getCastRange(), _actor, getAttackTarget(), true) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk))) + { + _accessor.doCast(sk); + return; + } + + max_range = Math.max(max_range, sk.getCastRange()); + } + + if (!isNotMoving()) + { + moveToPawn(getAttackTarget(), max_range); + } + + return; + } + } + + protected void thinkAttackGroup() + { + L2Character target = getForcedTarget(); + if ((target == null) || target.isAlikeDead()) + { + // try to get next group target + setForcedTarget(findNextGroupTarget()); + clientStopMoving(null); + } + + if (target == null) + { + return; + } + + _actor.setTarget(target); + L2Skill[] skills = _actor.getAllSkills(); + double dist2 = _actor.getPlanDistanceSq(target.getX(), target.getY()); + int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + target.getTemplate().collisionRadius; + int max_range = range; + + // as a response, we put the target in a forcedattack mode + L2ControllableMobInstance theTarget = (L2ControllableMobInstance) target; + L2ControllableMobAI ctrlAi = (L2ControllableMobAI) theTarget.getAI(); + ctrlAi.forceAttack(_actor); + + if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20)))) + { + // check distant skills + for (L2Skill sk : skills) + { + int castRange = sk.getCastRange(); + + if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk))) + { + _accessor.doCast(sk); + return; + } + + max_range = Math.max(max_range, castRange); + } + + if (!isNotMoving()) + { + moveToPawn(target, range); + } + + return; + } + _accessor.doAttack(target); + } + + protected void thinkForceAttack() + { + if ((getForcedTarget() == null) || getForcedTarget().isAlikeDead()) + { + clientStopMoving(null); + setIntention(AI_INTENTION_ACTIVE); + setAlternateAI(AI_IDLE); + } + + _actor.setTarget(getForcedTarget()); + L2Skill[] skills = _actor.getAllSkills(); + double dist2 = _actor.getPlanDistanceSq(getForcedTarget().getX(), getForcedTarget().getY()); + int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + getForcedTarget().getTemplate().collisionRadius; + int max_range = range; + + if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20)))) + { + // check distant skills + for (L2Skill sk : skills) + { + int castRange = sk.getCastRange(); + + if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk))) + { + _accessor.doCast(sk); + return; + } + + max_range = Math.max(max_range, castRange); + } + + if (!isNotMoving()) + { + moveToPawn(getForcedTarget(), _actor.getPhysicalAttackRange()/* range */); + } + + return; + } + + _accessor.doAttack(getForcedTarget()); + } + + protected void thinkAttack() + { + if ((getAttackTarget() == null) || getAttackTarget().isAlikeDead()) + { + if (getAttackTarget() != null) + { + // stop hating + L2Attackable npc = (L2Attackable) _actor; + npc.stopHating(getAttackTarget()); + } + + setIntention(AI_INTENTION_ACTIVE); + } + else + { + // notify aggression + if (((L2Npc) _actor).getFactionId() != null) + { + String faction_id = ((L2Npc) _actor).getFactionId(); + + Collection objs = _actor.getKnownList().getKnownObjects().values(); + // synchronized (_actor.getKnownList().getKnownObjects()) + { + for (L2Object obj : objs) + { + if (!(obj instanceof L2Npc)) + { + continue; + } + + L2Npc npc = (L2Npc) obj; + + if (!faction_id.equals(npc.getFactionId())) + { + continue; + } + + if (_actor.isInsideRadius(npc, npc.getFactionRange(), false, true) && (Math.abs(getAttackTarget().getZ() - npc.getZ()) < 200)) + { + npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1); + } + } + } + } + + _actor.setTarget(getAttackTarget()); + L2Skill[] skills = _actor.getAllSkills(); + double dist2 = _actor.getPlanDistanceSq(getAttackTarget().getX(), getAttackTarget().getY()); + int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius; + int max_range = range; + + if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20)))) + { + // check distant skills + for (L2Skill sk : skills) + { + int castRange = sk.getCastRange(); + + if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk))) + { + _accessor.doCast(sk); + return; + } + + max_range = Math.max(max_range, castRange); + } + + moveToPawn(getAttackTarget(), range); + return; + } + + // Force mobs to attack anybody if confused. + L2Character hated; + + if (_actor.isConfused()) + { + hated = findNextRndTarget(); + } + else + { + hated = getAttackTarget(); + } + + if (hated == null) + { + setIntention(AI_INTENTION_ACTIVE); + return; + } + + if (hated != getAttackTarget()) + { + setAttackTarget(hated); + } + + if (!_actor.isMuted() && (skills.length > 0) && (Rnd.nextInt(5) == 3)) + { + for (L2Skill sk : skills) + { + int castRange = sk.getCastRange(); + + if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() < _actor.getStat().getMpConsume(sk))) + { + _accessor.doCast(sk); + return; + } + } + } + + _accessor.doAttack(getAttackTarget()); + } + } + + private void thinkActive() + { + setAttackTarget(findNextRndTarget()); + L2Character hated; + + if (_actor.isConfused()) + { + hated = findNextRndTarget(); + } + else + { + hated = getAttackTarget(); + } + + if (hated != null) + { + _actor.setRunning(); + setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated); + } + } + + private boolean autoAttackCondition(L2Character target) + { + if ((target == null) || !(_actor instanceof L2Attackable)) + { + return false; + } + L2Attackable me = (L2Attackable) _actor; + + if ((target instanceof L2NpcInstance) || (target instanceof L2DoorInstance)) + { + return false; + } + + if (target.isAlikeDead() || !me.isInsideRadius(target, me.getAggroRange(), false, false) || (Math.abs(_actor.getZ() - target.getZ()) > 100)) + { + return false; + } + + // Check if the target isn't invulnerable + if (target.isInvul()) + { + return false; + } + + // Spawn protection (only against mobs) + if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isSpawnProtected()) + { + return false; + } + + // Check if the target is a L2PlayableInstance + if (target instanceof L2Playable) + { + // Check if the target isn't in silent move mode + if (((L2Playable) target).isSilentMoving()) + { + return false; + } + } + + if (target instanceof L2Npc) + { + return false; + } + + return me.isAggressive(); + } + + private L2Character findNextRndTarget() + { + int aggroRange = ((L2Attackable) _actor).getAggroRange(); + L2Attackable npc = (L2Attackable) _actor; + int npcX, npcY, targetX, targetY; + double dy, dx; + double dblAggroRange = aggroRange * aggroRange; + + List potentialTarget = new FastList<>(); + + Collection objs = npc.getKnownList().getKnownObjects().values(); + // synchronized (npc.getKnownList().getKnownObjects()) + { + for (L2Object obj : objs) + { + if (!(obj instanceof L2Character)) + { + continue; + } + + npcX = npc.getX(); + npcY = npc.getY(); + targetX = obj.getX(); + targetY = obj.getY(); + + dx = npcX - targetX; + dy = npcY - targetY; + + if (((dx * dx) + (dy * dy)) > dblAggroRange) + { + continue; + } + + L2Character target = (L2Character) obj; + + if (autoAttackCondition(target)) + { + potentialTarget.add(target); + } + } + } + + if (potentialTarget.isEmpty()) + { + return null; + } + + // we choose a random target + int choice = Rnd.nextInt(potentialTarget.size()); + L2Character target = potentialTarget.get(choice); + + return target; + } + + private L2ControllableMobInstance findNextGroupTarget() + { + return getGroupTarget().getRandomMob(); + } + + public L2ControllableMobAI(AIAccessor accessor) + { + super(accessor); + setAlternateAI(AI_IDLE); + } + + public int getAlternateAI() + { + return _alternateAI; + } + + public void setAlternateAI(int _alternateai) + { + _alternateAI = _alternateai; + } + + public void forceAttack(L2Character target) + { + setAlternateAI(AI_FORCEATTACK); + setForcedTarget(target); + } + + public void forceAttackGroup(MobGroup group) + { + setForcedTarget(null); + setGroupTarget(group); + setAlternateAI(AI_ATTACK_GROUP); + } + + public void stop() + { + setAlternateAI(AI_IDLE); + clientStopMoving(null); + } + + public void move(int x, int y, int z) + { + moveTo(x, y, z); + } + + public void follow(L2Character target) + { + setAlternateAI(AI_FOLLOW); + setForcedTarget(target); + } + + public boolean isThinking() + { + return _isThinking; + } + + public boolean isNotMoving() + { + return _isNotMoving; + } + + public void setNotMoving(boolean isNotMoving) + { + _isNotMoving = isNotMoving; + } + + public void setThinking(boolean isThinking) + { + _isThinking = isThinking; + } + + private L2Character getForcedTarget() + { + return _forcedTarget; + } + + private MobGroup getGroupTarget() + { + return _targetGroup; + } + + private void setForcedTarget(L2Character forcedTarget) + { + _forcedTarget = forcedTarget; + } + + private void setGroupTarget(MobGroup targetGroup) + { + _targetGroup = targetGroup; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2DoorAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2DoorAI.java new file mode 100644 index 00000000..bc8d19bf --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2DoorAI.java @@ -0,0 +1,182 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; + +/** + * @author mkizub + */ +public class L2DoorAI extends L2CharacterAI +{ + + public L2DoorAI(L2DoorInstance.AIAccessor accessor) + { + super(accessor); + } + + // rather stupid AI... well, it's for doors :D + @Override + protected void onIntentionIdle() + { + } + + @Override + protected void onIntentionActive() + { + } + + @Override + protected void onIntentionRest() + { + } + + @Override + protected void onIntentionAttack(L2Character target) + { + } + + @Override + protected void onIntentionCast(L2Skill skill, L2Object target) + { + } + + @Override + protected void onIntentionMoveTo(L2CharPosition destination) + { + } + + @Override + protected void onIntentionFollow(L2Character target) + { + } + + @Override + protected void onIntentionPickUp(L2Object item) + { + } + + @Override + protected void onIntentionInteract(L2Object object) + { + } + + @Override + protected void onEvtThink() + { + } + + @Override + protected void onEvtAttacked(L2Character attacker) + { + L2DoorInstance me = (L2DoorInstance) _actor; + ThreadPoolManager.getInstance().executeTask(new onEventAttackedDoorTask(me, attacker)); + } + + @Override + protected void onEvtAggression(L2Character target, int aggro) + { + } + + @Override + protected void onEvtStunned(L2Character attacker) + { + } + + @Override + protected void onEvtSleeping(L2Character attacker) + { + } + + @Override + protected void onEvtRooted(L2Character attacker) + { + } + + @Override + protected void onEvtReadyToAct() + { + } + + @Override + protected void onEvtUserCmd(Object arg0, Object arg1) + { + } + + @Override + protected void onEvtArrived() + { + } + + @Override + protected void onEvtArrivedRevalidate() + { + } + + @Override + protected void onEvtArrivedBlocked(L2CharPosition blocked_at_pos) + { + } + + @Override + protected void onEvtForgetObject(L2Object object) + { + } + + @Override + protected void onEvtCancel() + { + } + + @Override + protected void onEvtDead() + { + } + + private class onEventAttackedDoorTask implements Runnable + { + private final L2DoorInstance _door; + private final L2Character _attacker; + + public onEventAttackedDoorTask(L2DoorInstance door, L2Character attacker) + { + _door = door; + _attacker = attacker; + } + + /* + * (non-Javadoc) + * @see java.lang.Runnable#run() + */ + @Override + public void run() + { + for (L2DefenderInstance guard : _door.getKnownDefenders()) + { + if (_actor.isInsideRadius(guard, guard.getFactionRange(), false, true) && (Math.abs(_attacker.getZ() - guard.getZ()) < 200)) + { + guard.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, _attacker, 15); + } + } + } + } + +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2FortSiegeGuardAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2FortSiegeGuardAI.java new file mode 100644 index 00000000..a0193048 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2FortSiegeGuardAI.java @@ -0,0 +1,980 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE; + +import java.util.concurrent.Future; +import java.util.logging.Logger; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.GameTimeController; +import com.l2jserver.gameserver.GeoData; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.model.L2Effect; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2FortBallistaInstance; +import com.l2jserver.gameserver.model.actor.instance.L2FortCommanderInstance; +import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.templates.skills.L2SkillType; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.Rnd; + +/** + * This class manages AI of L2Attackable.
    + *
    + */ +public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable +{ + + protected static final Logger _log1 = Logger.getLogger(L2FortSiegeGuardAI.class.getName()); + + private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds + + /** The L2Attackable AI task executed every 1s (call onEvtThink method) */ + private Future _aiTask; + + /** For attack AI, analysis of mob and its targets */ + private final SelfAnalysis _selfAnalysis = new SelfAnalysis(); + + /** The delay after which the attacked is stopped */ + private int _attackTimeout; + + /** The L2Attackable aggro counter */ + private int _globalAggro; + + /** The flag used to indicate that a thinking action is in progress */ + private boolean _thinking; // to prevent recursive thinking + + private final int _attackRange; + + /** + * Constructor of L2AttackableAI.
    + *
    + * @param accessor The AI accessor of the L2Character + */ + public L2FortSiegeGuardAI(L2Character.AIAccessor accessor) + { + super(accessor); + _selfAnalysis.init(); + _attackTimeout = Integer.MAX_VALUE; + _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn + _attackRange = ((L2Attackable) _actor).getPhysicalAttackRange(); + } + + @Override + public void run() + { + // Launch actions corresponding to the Event Think + onEvtThink(); + } + + /** + * Return True if the target is autoattackable (depends on the actor type).
    + *
    + * Actor is a L2GuardInstance :
    + *
    + *
  • The target isn't a Folk or a Door
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • The L2PcInstance target has karma (=PK)
  • The L2MonsterInstance + * target is aggressive

  • + *
    + * Actor is a L2SiegeGuardInstance :
    + *
    + *
  • The target isn't a Folk or a Door
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • A siege is in progress
  • The L2PcInstance target isn't a Defender
  • + *
    + *
    + * Actor is a L2FriendlyMobInstance :
    + *
    + *
  • The target isn't a Folk, a Door or another L2NpcInstance
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • The L2PcInstance target has karma (=PK)

  • + *
    + * Actor is a L2MonsterInstance :
    + *
    + *
  • The target isn't a Folk, a Door or another L2NpcInstance
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • The actor is Aggressive

  • + *
    + * @param target The targeted L2Object + */ + private boolean autoAttackCondition(L2Character target) + { + // Check if the target isn't another guard, folk or a door + if ((target == null) || (target instanceof L2DefenderInstance) || (target instanceof L2NpcInstance) || (target instanceof L2DoorInstance) || target.isAlikeDead() || (target instanceof L2FortBallistaInstance) || (target instanceof L2FortCommanderInstance) || (target instanceof L2Playable)) + { + L2PcInstance player = null; + if (target instanceof L2PcInstance) + { + player = ((L2PcInstance) target); + } + else if (target instanceof L2Summon) + { + player = ((L2Summon) target).getOwner(); + } + if ((player == null) || ((player.getClan() != null) && (player.getClan().getHasFort() == ((L2Npc) _actor).getFort().getFortId()))) + { + return false; + } + } + + // Check if the target isn't invulnerable + if (target.isInvul()) + { + // However EffectInvincible requires to check GMs specially + if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isGM()) + { + return false; + } + if ((target instanceof L2Summon) && ((L2Summon) target).getOwner().isGM()) + { + return false; + } + } + + // Get the owner if the target is a summon + if (target instanceof L2Summon) + { + L2PcInstance owner = ((L2Summon) target).getOwner(); + if (_actor.isInsideRadius(owner, 1000, true, false)) + { + target = owner; + } + } + + // Check if the target is a L2PcInstance + if (target instanceof L2Playable) + { + // Check if the target isn't in silent move mode AND too far (>100) + if (((L2Playable) target).isSilentMoving() && !_actor.isInsideRadius(target, 250, false, false)) + { + return false; + } + } + // Los Check Here + return (_actor.isAutoAttackable(target) && GeoData.getInstance().canSeeTarget(_actor, target)); + + } + + /** + * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
    + *
    + * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
    + *
    + * @param intention The new Intention to set to the AI + * @param arg0 The first parameter of the Intention + * @param arg1 The second parameter of the Intention + */ + @Override + synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1) + { + if (Config.DEBUG) + { + _log1.info("L2SiegeAI.changeIntention(" + intention + ", " + arg0 + ", " + arg1 + ")"); + } + + if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present + { + // Check if actor is not dead + if (!_actor.isAlikeDead()) + { + L2Attackable npc = (L2Attackable) _actor; + + // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE + if (!npc.getKnownList().getKnownPlayers().isEmpty()) + { + intention = AI_INTENTION_ACTIVE; + } + else + { + intention = AI_INTENTION_IDLE; + } + } + + if (intention == AI_INTENTION_IDLE) + { + // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE + super.changeIntention(AI_INTENTION_IDLE, null, null); + + // Stop AI task and detach AI from NPC + if (_aiTask != null) + { + _aiTask.cancel(true); + _aiTask = null; + } + + // Cancel the AI + _accessor.detachAI(); + + return; + } + } + + // Set the Intention of this L2AttackableAI to intention + super.changeIntention(intention, arg0, arg1); + + // If not idle - create an AI task (schedule onEvtThink repeatedly) + if (_aiTask == null) + { + _aiTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(this, 1000, 1000); + } + } + + /** + * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
    + *
    + * @param target The L2Character to attack + */ + @Override + protected void onIntentionAttack(L2Character target) + { + // Calculate the attack timeout + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks(); + + // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event + // if (_actor.getTarget() != null) + super.onIntentionAttack(target); + } + + /** + * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
    + *
    + * Actions :
    + *
    + *
  • Update every 1s the _globalAggro counter to come close to 0
  • If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
  • If the actor can't attack, order to it to return to its + * home location
  • + */ + private void thinkActive() + { + L2Attackable npc = (L2Attackable) _actor; + + // Update every 1s the _globalAggro counter to come close to 0 + if (_globalAggro != 0) + { + if (_globalAggro < 0) + { + _globalAggro++; + } + else + { + _globalAggro--; + } + } + + // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate + // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10 + if (_globalAggro >= 0) + { + for (L2Character target : npc.getKnownList().getKnownCharactersInRadius(_attackRange)) + { + if (target == null) + { + continue; + } + if (autoAttackCondition(target)) // check aggression + { + // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList + int hating = npc.getHating(target); + + // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate + if (hating == 0) + { + npc.addDamageHate(target, 0, 1); + } + } + } + + // Chose a target from its aggroList + L2Character hated; + if (_actor.isConfused()) + { + hated = getAttackTarget(); // Force mobs to attack anybody if confused + } + else + { + hated = npc.getMostHated(); + // _mostHatedAnalysis.Update(hated); + } + + // Order to the L2Attackable to attack the target + if (hated != null) + { + // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList + int aggro = npc.getHating(hated); + + if ((aggro + _globalAggro) > 0) + { + // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance + if (!_actor.isRunning()) + { + _actor.setRunning(); + } + + // Set the AI Intention to AI_INTENTION_ATTACK + setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null); + } + + return; + } + + } + // Order to the L2SiegeGuardInstance to return to its home location because there's no target to attack + if (_actor.getWalkSpeed() >= 0) + { + if (_actor instanceof L2DefenderInstance) + { + ((L2DefenderInstance) _actor).returnHome(); + } + else + { + ((L2FortCommanderInstance) _actor).returnHome(); + } + } + } + + /** + * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
    + *
    + * Actions :
    + *
    + *
  • Update the attack timeout if actor is running
  • If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
  • Call all L2Object of its Faction inside the Faction Range
  • Chose a target and order to attack it with magic skill + * or physical attack

  • + *
    + * TODO: Manage casting rules to healer mobs (like Ant Nurses) + */ + private void thinkAttack() + { + if (Config.DEBUG) + { + _log1.info("L2FortSiegeGuardAI.thinkAttack(); timeout=" + (_attackTimeout - GameTimeController.getGameTicks())); + } + + if (_attackTimeout < GameTimeController.getGameTicks()) + { + // Check if the actor is running + if (_actor.isRunning()) + { + // Set the actor movement type to walk and send Server->Client packet ChangeMoveType to all others L2PcInstance + _actor.setWalking(); + + // Calculate a new attack timeout + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks(); + } + } + + L2Character attackTarget = getAttackTarget(); + // Check if target is dead or if timeout is expired to stop this attack + if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getGameTicks())) + { + // Stop hating this target after the attack timeout or if target is dead + if (attackTarget != null) + { + L2Attackable npc = (L2Attackable) _actor; + npc.stopHating(attackTarget); + } + + // Cancel target and timeout + _attackTimeout = Integer.MAX_VALUE; + setAttackTarget(null); + + // Set the AI Intention to AI_INTENTION_ACTIVE + setIntention(AI_INTENTION_ACTIVE, null, null); + + _actor.setWalking(); + return; + } + + factionNotifyAndSupport(); + attackPrepare(); + } + + private final void factionNotifyAndSupport() + { + L2Character target = getAttackTarget(); + // Call all L2Object of its Faction inside the Faction Range + if ((((L2Npc) _actor).getFactionId() == null) || (target == null)) + { + return; + } + + if (target.isInvul()) + { + return; // speeding it up for siege guards + } + + String faction_id = ((L2Npc) _actor).getFactionId(); + + // Go through all L2Character that belong to its faction + // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius)) + for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(1000)) + { + if (cha == null) + { + continue; + } + + if (!(cha instanceof L2Npc)) + { + if (_selfAnalysis.hasHealOrResurrect && (cha instanceof L2PcInstance) && ((L2Npc) _actor).getFort().getSiege().checkIsDefender(((L2PcInstance) cha).getClan())) + { + // heal friends + if (!_actor.isAttackingDisabled() && (cha.getCurrentHp() < (cha.getMaxHp() * 0.6)) && (_actor.getCurrentHp() > (_actor.getMaxHp() / 2)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2)) && cha.isInCombat()) + { + for (L2Skill sk : _selfAnalysis.healSkills) + { + if (_actor.getCurrentMp() < sk.getMpConsume()) + { + continue; + } + if (_actor.isSkillDisabled(sk)) + { + continue; + } + if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true)) + { + continue; + } + + int chance = 5; + if (chance >= Rnd.get(100)) + { + continue; + } + if (!GeoData.getInstance().canSeeTarget(_actor, cha)) + { + break; + } + + L2Object OldTarget = _actor.getTarget(); + _actor.setTarget(cha); + clientStopMoving(null); + _accessor.doCast(sk); + _actor.setTarget(OldTarget); + return; + } + } + } + continue; + } + + L2Npc npc = (L2Npc) cha; + + if (!faction_id.equals(npc.getFactionId())) + { + continue; + } + + if (npc.getAI() != null) // TODO: possibly check not needed + { + if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600) + // && _actor.getAttackByList().contains(getAttackTarget()) + && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE)) + // limiting aggro for siege guards + && target.isInsideRadius(npc, 1500, true, false) && GeoData.getInstance().canSeeTarget(npc, target)) + { + // Notify the L2Object AI with EVT_AGGRESSION + npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1); + return; + } + // heal friends + if (_selfAnalysis.hasHealOrResurrect && !_actor.isAttackingDisabled() && (npc.getCurrentHp() < (npc.getMaxHp() * 0.6)) && (_actor.getCurrentHp() > (_actor.getMaxHp() / 2)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2)) && npc.isInCombat()) + { + for (L2Skill sk : _selfAnalysis.healSkills) + { + if (_actor.getCurrentMp() < sk.getMpConsume()) + { + continue; + } + if (_actor.isSkillDisabled(sk)) + { + continue; + } + if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true)) + { + continue; + } + + int chance = 4; + if (chance >= Rnd.get(100)) + { + continue; + } + if (!GeoData.getInstance().canSeeTarget(_actor, npc)) + { + break; + } + + L2Object OldTarget = _actor.getTarget(); + _actor.setTarget(npc); + clientStopMoving(null); + _accessor.doCast(sk); + _actor.setTarget(OldTarget); + return; + } + } + } + } + } + + private void attackPrepare() + { + // Get all information needed to choose between physical or magical attack + L2Skill[] skills = null; + double dist_2 = 0; + int range = 0; + L2DefenderInstance sGuard; + if (_actor instanceof L2FortCommanderInstance) + { + sGuard = (L2FortCommanderInstance) _actor; + } + else + { + sGuard = (L2DefenderInstance) _actor; + } + L2Character attackTarget = getAttackTarget(); + + try + { + _actor.setTarget(attackTarget); + skills = _actor.getAllSkills(); + dist_2 = _actor.getPlanDistanceSq(attackTarget.getX(), attackTarget.getY()); + range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + attackTarget.getTemplate().collisionRadius; + if (attackTarget.isMoving()) + { + range += 50; + } + } + catch (NullPointerException e) + { + // _log.warning("AttackableAI: Attack target is NULL."); + _actor.setTarget(null); + setIntention(AI_INTENTION_IDLE, null, null); + return; + } + + // never attack defenders + if ((attackTarget instanceof L2PcInstance) && sGuard.getFort().getSiege().checkIsDefender(((L2PcInstance) attackTarget).getClan())) + { + // Cancel the target + sGuard.stopHating(attackTarget); + _actor.setTarget(null); + setIntention(AI_INTENTION_IDLE, null, null); + return; + } + + if (!GeoData.getInstance().canSeeTarget(_actor, attackTarget)) + { + // Siege guards differ from normal mobs currently: + // If target cannot seen, don't attack any more + sGuard.stopHating(attackTarget); + _actor.setTarget(null); + setIntention(AI_INTENTION_IDLE, null, null); + return; + } + + // Check if the actor isn't muted and if it is far from target + if (!_actor.isMuted() && (dist_2 > (range * range))) + { + // check for long ranged skills and heal/buff skills + for (L2Skill sk : skills) + { + int castRange = sk.getCastRange(); + + if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive()) + { + + L2Object OldTarget = _actor.getTarget(); + if ((sk.getSkillType() == L2SkillType.BUFF) || (sk.getSkillType() == L2SkillType.HEAL)) + { + boolean useSkillSelf = true; + if ((sk.getSkillType() == L2SkillType.HEAL) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5))) + { + useSkillSelf = false; + break; + } + if (sk.getSkillType() == L2SkillType.BUFF) + { + L2Effect[] effects = _actor.getAllEffects(); + for (int i = 0; (effects != null) && (i < effects.length); i++) + { + L2Effect effect = effects[i]; + if (effect.getSkill() == sk) + { + useSkillSelf = false; + break; + } + } + } + if (useSkillSelf) + { + _actor.setTarget(_actor); + } + } + + clientStopMoving(null); + _accessor.doCast(sk); + _actor.setTarget(OldTarget); + return; + } + } + + // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run + if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.getKnownList().knowsObject(attackTarget))) + { + // Cancel the target + _actor.getKnownList().removeKnownObject(attackTarget); + _actor.setTarget(null); + setIntention(AI_INTENTION_IDLE, null, null); + } + else + { + double dx = _actor.getX() - attackTarget.getX(); + double dy = _actor.getY() - attackTarget.getY(); + double dz = _actor.getZ() - attackTarget.getZ(); + double homeX = attackTarget.getX() - sGuard.getSpawn().getLocx(); + double homeY = attackTarget.getY() - sGuard.getSpawn().getLocy(); + + // Check if the L2SiegeGuardInstance isn't too far from it's home location + if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800 + && (_actor.getKnownList().knowsObject(attackTarget))) + { + // Cancel the target + _actor.getKnownList().removeKnownObject(attackTarget); + _actor.setTarget(null); + setIntention(AI_INTENTION_IDLE, null, null); + } + else + // Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast) + { + // Temporary hack for preventing guards jumping off towers, + // before replacing this with effective geodata checks and AI modification + if ((dz * dz) < (170 * 170)) // normally 130 if guard z coordinates correct + { + if (_selfAnalysis.isMage) + { + range = _selfAnalysis.maxCastRange - 50; + } + if (_actor.getWalkSpeed() <= 0) + { + return; + } + if (attackTarget.isMoving()) + { + moveToPawn(attackTarget, range - 70); + } + else + { + moveToPawn(attackTarget, range); + } + } + } + } + + return; + + } + // Else, if the actor is muted and far from target, just "move to pawn" + else if (_actor.isMuted() && (dist_2 > (range * range))) + { + // Temporary hack for preventing guards jumping off towers, + // before replacing this with effective geodata checks and AI modification + double dz = _actor.getZ() - attackTarget.getZ(); + if ((dz * dz) < (170 * 170)) // normally 130 if guard z coordinates correct + { + if (_selfAnalysis.isMage) + { + range = _selfAnalysis.maxCastRange - 50; + } + if (_actor.getWalkSpeed() <= 0) + { + return; + } + if (attackTarget.isMoving()) + { + moveToPawn(attackTarget, range - 70); + } + else + { + moveToPawn(attackTarget, range); + } + } + return; + } + // Else, if this is close enough to attack + else if (dist_2 <= (range * range)) + { + // Force mobs to attack anybody if confused + L2Character hated = null; + if (_actor.isConfused()) + { + hated = attackTarget; + } + else + { + hated = ((L2Attackable) _actor).getMostHated(); + } + + if (hated == null) + { + setIntention(AI_INTENTION_ACTIVE, null, null); + return; + } + if (hated != attackTarget) + { + attackTarget = hated; + } + + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks(); + + // check for close combat skills && heal/buff skills + if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5)) + { + for (L2Skill sk : skills) + { + int castRange = sk.getCastRange(); + + if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk)) + { + L2Object OldTarget = _actor.getTarget(); + if ((sk.getSkillType() == L2SkillType.BUFF) || (sk.getSkillType() == L2SkillType.HEAL)) + { + boolean useSkillSelf = true; + if ((sk.getSkillType() == L2SkillType.HEAL) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5))) + { + useSkillSelf = false; + break; + } + if (sk.getSkillType() == L2SkillType.BUFF) + { + L2Effect[] effects = _actor.getAllEffects(); + for (int i = 0; (effects != null) && (i < effects.length); i++) + { + L2Effect effect = effects[i]; + if (effect.getSkill() == sk) + { + useSkillSelf = false; + break; + } + } + } + if (useSkillSelf) + { + _actor.setTarget(_actor); + } + } + + clientStopMoving(null); + _accessor.doCast(sk); + _actor.setTarget(OldTarget); + return; + } + } + } + // Finally, do the physical attack itself + _accessor.doAttack(attackTarget); + } + } + + /** + * Manage AI thinking actions of a L2Attackable.
    + *
    + */ + @Override + protected void onEvtThink() + { + // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers())) + // setIntention(AI_INTENTION_IDLE); + + // Check if the actor can't use skills and if a thinking action isn't already in progress + if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled()) + { + return; + } + + // Start thinking action + _thinking = true; + + try + { + // Manage AI thinks of a L2Attackable + if (getIntention() == AI_INTENTION_ACTIVE) + { + thinkActive(); + } + else if (getIntention() == AI_INTENTION_ATTACK) + { + thinkAttack(); + } + } + finally + { + // Stop thinking action + _thinking = false; + } + } + + /** + * Launch actions corresponding to the Event Attacked.
    + *
    + * Actions :
    + *
    + *
  • Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
  • Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
  • Set the Intention to AI_INTENTION_ATTACK
  • + *
    + *
    + * @param attacker The L2Character that attacks the actor + */ + @Override + protected void onEvtAttacked(L2Character attacker) + { + // Calculate the attack timeout + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks(); + + // Set the _globalAggro to 0 to permit attack even just after spawn + if (_globalAggro < 0) + { + _globalAggro = 0; + } + + // Add the attacker to the _aggroList of the actor + ((L2Attackable) _actor).addDamageHate(attacker, 0, 1); + + // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance + if (!_actor.isRunning()) + { + _actor.setRunning(); + } + + // Set the Intention to AI_INTENTION_ATTACK + if (getIntention() != AI_INTENTION_ATTACK) + { + setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null); + } + + super.onEvtAttacked(attacker); + } + + /** + * Launch actions corresponding to the Event Aggression.
    + *
    + * Actions :
    + *
    + *
  • Add the target to the actor _aggroList or update hate if already present
  • Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)

  • + *
    + * @param attacker The L2Character that attacks + * @param aggro The value of hate to add to the actor against the target + */ + @Override + protected void onEvtAggression(L2Character target, int aggro) + { + if (_actor == null) + { + return; + } + L2Attackable me = (L2Attackable) _actor; + + if (target != null) + { + // Add the target to the actor _aggroList or update hate if already present + me.addDamageHate(target, 0, aggro); + + // Get the hate of the actor against the target + aggro = me.getHating(target); + + if (aggro <= 0) + { + if (me.getMostHated() == null) + { + _globalAggro = -25; + me.clearAggroList(); + setIntention(AI_INTENTION_IDLE, null, null); + } + return; + } + + // Set the actor AI Intention to AI_INTENTION_ATTACK + if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK) + { + // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance + if (!_actor.isRunning()) + { + _actor.setRunning(); + } + + L2DefenderInstance sGuard; + if (_actor instanceof L2FortCommanderInstance) + { + sGuard = (L2FortCommanderInstance) _actor; + } + else + { + sGuard = (L2DefenderInstance) _actor; + } + double homeX = target.getX() - sGuard.getSpawn().getLocx(); + double homeY = target.getY() - sGuard.getSpawn().getLocy(); + + // Check if the L2SiegeGuardInstance is not too far from its home location + if (((homeX * homeX) + (homeY * homeY)) < 3240000) + { + setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null); + } + } + } + else + { + // currently only for setting lower general aggro + if (aggro >= 0) + { + return; + } + + L2Character mostHated = me.getMostHated(); + if (mostHated == null) + { + _globalAggro = -25; + return; + } + for (L2Character aggroed : me.getAggroList().keySet()) + { + me.addDamageHate(aggroed, 0, aggro); + } + + aggro = me.getHating(mostHated); + if (aggro <= 0) + { + _globalAggro = -25; + me.clearAggroList(); + setIntention(AI_INTENTION_IDLE, null, null); + } + } + } + + @Override + public void stopAITask() + { + if (_aiTask != null) + { + _aiTask.cancel(false); + _aiTask = null; + } + _accessor.detachAI(); + super.stopAITask(); + } + +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2NpcWalkerAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2NpcWalkerAI.java new file mode 100644 index 00000000..a1a9a319 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2NpcWalkerAI.java @@ -0,0 +1,240 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import java.util.List; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.NpcWalkerRoutesTable; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2NpcWalkerNode; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2NpcWalkerInstance; + +public class L2NpcWalkerAI extends L2CharacterAI implements Runnable +{ + private static final int DEFAULT_MOVE_DELAY = 0; + + private long _nextMoveTime; + + private boolean _walkingToNextPoint = false; + + /** + * home points for xyz + */ + int _homeX, _homeY, _homeZ; + + /** + * route of the current npc + */ + private List _route; + + /** + * current node + */ + private int _currentPos; + + /** + * Constructor of L2CharacterAI.
    + *
    + * @param accessor The AI accessor of the L2Character + */ + public L2NpcWalkerAI(L2Character.AIAccessor accessor) + { + super(accessor); + + if (!Config.ALLOW_NPC_WALKERS) + { + return; + } + + _route = NpcWalkerRoutesTable.getInstance().getRouteForNpc(getActor().getNpcId()); + + // Here we need 1 second initial delay cause getActor().hasAI() will return null... + // Constructor of L2NpcWalkerAI is called faster then ai object is attached in L2NpcWalkerInstance + if (_route != null) + { + ThreadPoolManager.getInstance().scheduleAiAtFixedRate(this, 1000, 1000); + } + else + { + _log.warning(getClass().getSimpleName() + ": Missing route data! Npc: " + _actor); + } + } + + @Override + public void run() + { + onEvtThink(); + } + + @Override + protected void onEvtThink() + { + if (!Config.ALLOW_NPC_WALKERS) + { + return; + } + + if (isWalkingToNextPoint()) + { + checkArrived(); + return; + } + + if (_nextMoveTime < System.currentTimeMillis()) + { + walkToLocation(); + } + } + + /** + * If npc can't walk to it's target then just teleport to next point + * @param blocked_at_pos ignoring it + */ + @Override + protected void onEvtArrivedBlocked(L2CharPosition blocked_at_pos) + { + _log.warning("NpcWalker ID: " + getActor().getNpcId() + ": Blocked at rote position [" + _currentPos + "], coords: " + blocked_at_pos.x + ", " + blocked_at_pos.y + ", " + blocked_at_pos.z + ". Teleporting to next point"); + + int destinationX = _route.get(_currentPos).getMoveX(); + int destinationY = _route.get(_currentPos).getMoveY(); + int destinationZ = _route.get(_currentPos).getMoveZ(); + + getActor().teleToLocation(destinationX, destinationY, destinationZ, false); + super.onEvtArrivedBlocked(blocked_at_pos); + } + + private void checkArrived() + { + int destinationX = _route.get(_currentPos).getMoveX(); + int destinationY = _route.get(_currentPos).getMoveY(); + int destinationZ = _route.get(_currentPos).getMoveZ(); + + if (getActor().isInsideRadius(destinationX, destinationY, destinationZ, 5, false, false)) + { + int id = _route.get(_currentPos).getChatId(); + String chat = null; + if (id == 0) + { + chat = _route.get(_currentPos).getChatText(); + } + + if ((id > 0) || ((chat != null) && !chat.isEmpty())) + { + getActor().broadcastChat(chat, id); + } + + // time in millis + long delay = _route.get(_currentPos).getDelay() * 1000; + + // sleeps between each move + if (delay <= 0) + { + delay = DEFAULT_MOVE_DELAY; + if (Config.DEVELOPER) + { + _log.warning("Wrong Delay Set in Npc Walker Functions = " + delay + " secs, using default delay: " + DEFAULT_MOVE_DELAY + " secs instead."); + } + } + + _nextMoveTime = System.currentTimeMillis() + delay; + setWalkingToNextPoint(false); + } + } + + private void walkToLocation() + { + if (_currentPos < (_route.size() - 1)) + { + _currentPos++; + } + else + { + _currentPos = 0; + } + + boolean moveType = _route.get(_currentPos).getRunning(); + + /** + * false - walking true - Running + */ + if (moveType) + { + getActor().setRunning(); + } + else + { + getActor().setWalking(); + } + + // now we define destination + int destinationX = _route.get(_currentPos).getMoveX(); + int destinationY = _route.get(_currentPos).getMoveY(); + int destinationZ = _route.get(_currentPos).getMoveZ(); + + // notify AI of MOVE_TO + setWalkingToNextPoint(true); + + setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(destinationX, destinationY, destinationZ, 0)); + } + + @Override + public L2NpcWalkerInstance getActor() + { + return (L2NpcWalkerInstance) super.getActor(); + } + + public int getHomeX() + { + return _homeX; + } + + public int getHomeY() + { + return _homeY; + } + + public int getHomeZ() + { + return _homeZ; + } + + public void setHomeX(int homeX) + { + _homeX = homeX; + } + + public void setHomeY(int homeY) + { + _homeY = homeY; + } + + public void setHomeZ(int homeZ) + { + _homeZ = homeZ; + } + + public boolean isWalkingToNextPoint() + { + return _walkingToNextPoint; + } + + public void setWalkingToNextPoint(boolean value) + { + _walkingToNextPoint = value; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2PlayableAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2PlayableAI.java new file mode 100644 index 00000000..7e78ebf0 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2PlayableAI.java @@ -0,0 +1,136 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Character.AIAccessor; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.network.SystemMessageId; + +/** + * This class manages AI of L2Playable.
    + *
    + * L2PlayableAI :
    + *
    + *
  • L2SummonAI
  • L2PlayerAI

  • + *
    + * @author JIV + */ +public abstract class L2PlayableAI extends L2CharacterAI +{ + + /** + * @param accessor + */ + public L2PlayableAI(AIAccessor accessor) + { + super(accessor); + } + + /** + * @see com.l2jserver.gameserver.ai.L2CharacterAI#onIntentionAttack(com.l2jserver.gameserver.model.actor.L2Character) + */ + @Override + protected void onIntentionAttack(L2Character target) + { + if (target instanceof L2Playable) + { + if (target.getActingPlayer().getProtectionBlessing() && ((_actor.getActingPlayer().getLevel() - target.getActingPlayer().getLevel()) >= 10) && (_actor.getActingPlayer().getKarma() > 0) && !(target.isInsideZone(L2Character.ZONE_PVP))) + { + // If attacker have karma and have level >= 10 than his target and target have + // Newbie Protection Buff, + _actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT); + clientActionFailed(); + return; + } + + if (_actor.getActingPlayer().getProtectionBlessing() && ((target.getActingPlayer().getLevel() - _actor.getActingPlayer().getLevel()) >= 10) && (target.getActingPlayer().getKarma() > 0) && !(target.isInsideZone(L2Character.ZONE_PVP))) + { + // If target have karma and have level >= 10 than his target and actor have + // Newbie Protection Buff, + _actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT); + clientActionFailed(); + return; + } + + if (target.getActingPlayer().isCursedWeaponEquipped() && (_actor.getActingPlayer().getLevel() <= 20)) + { + _actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT); + clientActionFailed(); + return; + } + + if (_actor.getActingPlayer().isCursedWeaponEquipped() && (target.getActingPlayer().getLevel() <= 20)) + { + _actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT); + clientActionFailed(); + return; + } + } + + super.onIntentionAttack(target); + } + + /** + * @see com.l2jserver.gameserver.ai.L2CharacterAI#onIntentionCast(com.l2jserver.gameserver.model.L2Skill, com.l2jserver.gameserver.model.L2Object) + */ + @Override + protected void onIntentionCast(L2Skill skill, L2Object target) + { + if ((target instanceof L2Playable) && skill.isOffensive()) + { + if (target.getActingPlayer().getProtectionBlessing() && ((_actor.getActingPlayer().getLevel() - target.getActingPlayer().getLevel()) >= 10) && (_actor.getActingPlayer().getKarma() > 0) && !(((L2Playable) target).isInsideZone(L2Character.ZONE_PVP))) + { + // If attacker have karma and have level >= 10 than his target and target have + // Newbie Protection Buff, + _actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT); + clientActionFailed(); + _actor.setIsCastingNow(false); + return; + } + + if (_actor.getActingPlayer().getProtectionBlessing() && ((target.getActingPlayer().getLevel() - _actor.getActingPlayer().getLevel()) >= 10) && (target.getActingPlayer().getKarma() > 0) && !(((L2Playable) target).isInsideZone(L2Character.ZONE_PVP))) + { + // If target have karma and have level >= 10 than his target and actor have + // Newbie Protection Buff, + _actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT); + clientActionFailed(); + _actor.setIsCastingNow(false); + return; + } + + if (target.getActingPlayer().isCursedWeaponEquipped() && (_actor.getActingPlayer().getLevel() <= 20)) + { + _actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT); + clientActionFailed(); + _actor.setIsCastingNow(false); + return; + } + + if (_actor.getActingPlayer().isCursedWeaponEquipped() && (target.getActingPlayer().getLevel() <= 20)) + { + _actor.getActingPlayer().sendPacket(SystemMessageId.TARGET_IS_INCORRECT); + clientActionFailed(); + _actor.setIsCastingNow(false); + return; + } + } + + super.onIntentionCast(skill, target); + } + +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2PlayerAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2PlayerAI.java new file mode 100644 index 00000000..2e31a6dd --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2PlayerAI.java @@ -0,0 +1,387 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_CAST; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_INTERACT; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_MOVE_TO; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_PICK_UP; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_REST; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2Skill.SkillTargetType; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Character.AIAccessor; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2StaticObjectInstance; + +public class L2PlayerAI extends L2PlayableAI +{ + + private boolean _thinking; // to prevent recursive thinking + + IntentionCommand _nextIntention = null; + + public L2PlayerAI(AIAccessor accessor) + { + super(accessor); + } + + void saveNextIntention(CtrlIntention intention, Object arg0, Object arg1) + { + _nextIntention = new IntentionCommand(intention, arg0, arg1); + } + + @Override + public IntentionCommand getNextIntention() + { + return _nextIntention; + } + + /** + * Saves the current Intention for this L2PlayerAI if necessary and calls changeIntention in AbstractAI.
    + *
    + * @param intention The new Intention to set to the AI + * @param arg0 The first parameter of the Intention + * @param arg1 The second parameter of the Intention + */ + @Override + synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1) + { + /* + * if (Config.DEBUG) _log.warning("L2PlayerAI: changeIntention -> " + intention + " " + arg0 + " " + arg1); + */ + + // do nothing unless CAST intention + // however, forget interrupted actions when starting to use an offensive skill + if ((intention != AI_INTENTION_CAST) || ((arg0 != null) && ((L2Skill) arg0).isOffensive())) + { + _nextIntention = null; + super.changeIntention(intention, arg0, arg1); + return; + } + + // do nothing if next intention is same as current one. + if ((intention == _intention) && (arg0 == _intentionArg0) && (arg1 == _intentionArg1)) + { + super.changeIntention(intention, arg0, arg1); + return; + } + + // save current intention so it can be used after cast + saveNextIntention(_intention, _intentionArg0, _intentionArg1); + super.changeIntention(intention, arg0, arg1); + } + + /** + * Launch actions corresponding to the Event ReadyToAct.
    + *
    + * Actions :
    + *
    + *
  • Launch actions corresponding to the Event Think

  • + *
    + */ + @Override + protected void onEvtReadyToAct() + { + // Launch actions corresponding to the Event Think + if (_nextIntention != null) + { + setIntention(_nextIntention._crtlIntention, _nextIntention._arg0, _nextIntention._arg1); + _nextIntention = null; + } + super.onEvtReadyToAct(); + } + + /** + * Launch actions corresponding to the Event Cancel.
    + *
    + * Actions :
    + *
    + *
  • Stop an AI Follow Task
  • Launch actions corresponding to the Event Think

  • + *
    + */ + @Override + protected void onEvtCancel() + { + _nextIntention = null; + super.onEvtCancel(); + } + + /** + * Finalize the casting of a skill. This method overrides L2CharacterAI method.
    + *
    + * What it does: Check if actual intention is set to CAST and, if so, retrieves latest intention before the actual CAST and set it as the current intention for the player + */ + @Override + protected void onEvtFinishCasting() + { + if (getIntention() == AI_INTENTION_CAST) + { + // run interrupted or next intention + + IntentionCommand nextIntention = _nextIntention; + if (nextIntention != null) + { + if (nextIntention._crtlIntention != AI_INTENTION_CAST) // previous state shouldn't be casting + { + setIntention(nextIntention._crtlIntention, nextIntention._arg0, nextIntention._arg1); + } + else + { + setIntention(AI_INTENTION_IDLE); + } + } + else + { + /* + * if (Config.DEBUG) _log.warning("L2PlayerAI: no previous intention set... Setting it to IDLE"); + */ + // set intention to idle if skill doesn't change intention. + setIntention(AI_INTENTION_IDLE); + } + } + } + + @Override + protected void onIntentionRest() + { + if (getIntention() != AI_INTENTION_REST) + { + changeIntention(AI_INTENTION_REST, null, null); + setTarget(null); + if (getAttackTarget() != null) + { + setAttackTarget(null); + } + clientStopMoving(null); + } + } + + @Override + protected void onIntentionActive() + { + setIntention(AI_INTENTION_IDLE); + } + + /** + * Manage the Move To Intention : Stop current Attack and Launch a Move to Location Task.
    + *
    + * Actions :
    + *
    + *
  • Stop the actor auto-attack server side AND client side by sending Server->Client packet AutoAttackStop (broadcast)
  • Set the Intention of this AI to AI_INTENTION_MOVE_TO
  • Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet + * CharMoveToLocation (broadcast)

  • + *
    + */ + @Override + protected void onIntentionMoveTo(L2CharPosition pos) + { + if (getIntention() == AI_INTENTION_REST) + { + // Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor + clientActionFailed(); + return; + } + + if (_actor.isAllSkillsDisabled() || _actor.isCastingNow() || _actor.isAttackingNow()) + { + clientActionFailed(); + saveNextIntention(AI_INTENTION_MOVE_TO, pos, null); + return; + } + + // Set the Intention of this AbstractAI to AI_INTENTION_MOVE_TO + changeIntention(AI_INTENTION_MOVE_TO, pos, null); + + // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) + clientStopAutoAttack(); + + // Abort the attack of the L2Character and send Server->Client ActionFailed packet + _actor.abortAttack(); + + // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) + moveTo(pos.x, pos.y, pos.z); + } + + @Override + protected void clientNotifyDead() + { + _clientMovingToPawnOffset = 0; + _clientMoving = false; + + super.clientNotifyDead(); + } + + private void thinkAttack() + { + L2Character target = getAttackTarget(); + if (target == null) + { + return; + } + if (checkTargetLostOrDead(target)) + { + // Notify the target + setAttackTarget(null); + return; + } + if (maybeMoveToPawn(target, _actor.getPhysicalAttackRange())) + { + return; + } + + _accessor.doAttack(target); + } + + private void thinkCast() + { + L2Character target = getCastTarget(); + if (Config.DEBUG) + { + _log.warning("L2PlayerAI: thinkCast -> Start"); + } + + if ((_skill.getTargetType() == SkillTargetType.TARGET_GROUND) && (_actor instanceof L2PcInstance)) + { + if (maybeMoveToPosition(((L2PcInstance) _actor).getCurrentSkillWorldPosition(), _actor.getMagicalAttackRange(_skill))) + { + _actor.setIsCastingNow(false); + return; + } + } + else + { + if (checkTargetLost(target)) + { + if (_skill.isOffensive() && (getAttackTarget() != null)) + { + // Notify the target + setCastTarget(null); + } + _actor.setIsCastingNow(false); + return; + } + if ((target != null) && maybeMoveToPawn(target, _actor.getMagicalAttackRange(_skill))) + { + _actor.setIsCastingNow(false); + return; + } + } + + if ((_skill.getHitTime() > 50) && !_skill.isSimultaneousCast()) + { + clientStopMoving(null); + } + + L2Object oldTarget = _actor.getTarget(); + if ((oldTarget != null) && (target != null) && (oldTarget != target)) + { + // Replace the current target by the cast target + _actor.setTarget(getCastTarget()); + // Launch the Cast of the skill + _accessor.doCast(_skill); + // Restore the initial target + _actor.setTarget(oldTarget); + } + else + { + _accessor.doCast(_skill); + } + } + + private void thinkPickUp() + { + if (_actor.isAllSkillsDisabled() || _actor.isCastingNow()) + { + return; + } + L2Object target = getTarget(); + if (checkTargetLost(target)) + { + return; + } + if (maybeMoveToPawn(target, 36)) + { + return; + } + setIntention(AI_INTENTION_IDLE); + ((L2PcInstance.AIAccessor) _accessor).doPickupItem(target); + } + + private void thinkInteract() + { + if (_actor.isAllSkillsDisabled() || _actor.isCastingNow()) + { + return; + } + L2Object target = getTarget(); + if (checkTargetLost(target)) + { + return; + } + if (maybeMoveToPawn(target, 36)) + { + return; + } + if (!(target instanceof L2StaticObjectInstance)) + { + ((L2PcInstance.AIAccessor) _accessor).doInteract((L2Character) target); + } + setIntention(AI_INTENTION_IDLE); + } + + @Override + protected void onEvtThink() + { + if (_thinking && (getIntention() != AI_INTENTION_CAST)) + { + return; + } + + /* + * if (Config.DEBUG) _log.warning("L2PlayerAI: onEvtThink -> Check intention"); + */ + + _thinking = true; + try + { + if (getIntention() == AI_INTENTION_ATTACK) + { + thinkAttack(); + } + else if (getIntention() == AI_INTENTION_CAST) + { + thinkCast(); + } + else if (getIntention() == AI_INTENTION_PICK_UP) + { + thinkPickUp(); + } + else if (getIntention() == AI_INTENTION_INTERACT) + { + thinkInteract(); + } + } + finally + { + _thinking = false; + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2SiegeGuardAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2SiegeGuardAI.java new file mode 100644 index 00000000..35bd72bd --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2SiegeGuardAI.java @@ -0,0 +1,940 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE; + +import java.util.concurrent.Future; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.GameTimeController; +import com.l2jserver.gameserver.GeoData; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.model.L2Effect; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.templates.skills.L2SkillType; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.Rnd; + +/** + * This class manages AI of L2Attackable.
    + *
    + */ +public class L2SiegeGuardAI extends L2CharacterAI implements Runnable +{ + + // protected static final Logger _log = Logger.getLogger(L2SiegeGuardAI.class.getName()); + + private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds + + /** The L2Attackable AI task executed every 1s (call onEvtThink method) */ + private Future _aiTask; + + /** For attack AI, analysis of mob and its targets */ + private final SelfAnalysis _selfAnalysis = new SelfAnalysis(); + // private TargetAnalysis _mostHatedAnalysis = new TargetAnalysis(); + + /** The delay after which the attacked is stopped */ + private int _attackTimeout; + + /** The L2Attackable aggro counter */ + private int _globalAggro; + + /** The flag used to indicate that a thinking action is in progress */ + private boolean _thinking; // to prevent recursive thinking + + private final int _attackRange; + + /** + * Constructor of L2AttackableAI.
    + *
    + * @param accessor The AI accessor of the L2Character + */ + public L2SiegeGuardAI(L2Character.AIAccessor accessor) + { + super(accessor); + _selfAnalysis.init(); + _attackTimeout = Integer.MAX_VALUE; + _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn + _attackRange = ((L2Attackable) _actor).getPhysicalAttackRange(); + } + + @Override + public void run() + { + // Launch actions corresponding to the Event Think + onEvtThink(); + + } + + /** + * Return True if the target is autoattackable (depends on the actor type).
    + *
    + * Actor is a L2GuardInstance :
    + *
    + *
  • The target isn't a Folk or a Door
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • The L2PcInstance target has karma (=PK)
  • The L2MonsterInstance + * target is aggressive

  • + *
    + * Actor is a L2SiegeGuardInstance :
    + *
    + *
  • The target isn't a Folk or a Door
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • A siege is in progress
  • The L2PcInstance target isn't a Defender
  • + *
    + *
    + * Actor is a L2FriendlyMobInstance :
    + *
    + *
  • The target isn't a Folk, a Door or another L2NpcInstance
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • The L2PcInstance target has karma (=PK)

  • + *
    + * Actor is a L2MonsterInstance :
    + *
    + *
  • The target isn't a Folk, a Door or another L2NpcInstance
  • The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
  • The target is in the actor Aggro range and is at the same height
  • The actor is Aggressive

  • + *
    + * @param target The targeted L2Object + */ + private boolean autoAttackCondition(L2Character target) + { + // Check if the target isn't another guard, folk or a door + if ((target == null) || (target instanceof L2DefenderInstance) || (target instanceof L2NpcInstance) || (target instanceof L2DoorInstance) || target.isAlikeDead()) + { + return false; + } + + // Check if the target isn't invulnerable + if (target.isInvul()) + { + // However EffectInvincible requires to check GMs specially + if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isGM()) + { + return false; + } + if ((target instanceof L2Summon) && ((L2Summon) target).getOwner().isGM()) + { + return false; + } + } + + // Get the owner if the target is a summon + if (target instanceof L2Summon) + { + L2PcInstance owner = ((L2Summon) target).getOwner(); + if (_actor.isInsideRadius(owner, 1000, true, false)) + { + target = owner; + } + } + + // Check if the target is a L2PcInstance + if (target instanceof L2Playable) + { + // Check if the target isn't in silent move mode AND too far (>100) + if (((L2Playable) target).isSilentMoving() && !_actor.isInsideRadius(target, 250, false, false)) + { + return false; + } + } + // Los Check Here + return (_actor.isAutoAttackable(target) && GeoData.getInstance().canSeeTarget(_actor, target)); + + } + + /** + * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
    + *
    + * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
    + *
    + * @param intention The new Intention to set to the AI + * @param arg0 The first parameter of the Intention + * @param arg1 The second parameter of the Intention + */ + @Override + synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1) + { + if (Config.DEBUG) + { + _log.info("L2SiegeAI.changeIntention(" + intention + ", " + arg0 + ", " + arg1 + ")"); + } + + if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present + { + // Check if actor is not dead + if (!_actor.isAlikeDead()) + { + L2Attackable npc = (L2Attackable) _actor; + + // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE + if (!npc.getKnownList().getKnownPlayers().isEmpty()) + { + intention = AI_INTENTION_ACTIVE; + } + else + { + intention = AI_INTENTION_IDLE; + } + } + + if (intention == AI_INTENTION_IDLE) + { + // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE + super.changeIntention(AI_INTENTION_IDLE, null, null); + + // Stop AI task and detach AI from NPC + if (_aiTask != null) + { + _aiTask.cancel(true); + _aiTask = null; + } + + // Cancel the AI + _accessor.detachAI(); + + return; + } + } + + // Set the Intention of this L2AttackableAI to intention + super.changeIntention(intention, arg0, arg1); + + // If not idle - create an AI task (schedule onEvtThink repeatedly) + if (_aiTask == null) + { + _aiTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(this, 1000, 1000); + } + } + + /** + * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
    + *
    + * @param target The L2Character to attack + */ + @Override + protected void onIntentionAttack(L2Character target) + { + // Calculate the attack timeout + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks(); + + // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event + // if (_actor.getTarget() != null) + super.onIntentionAttack(target); + } + + /** + * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
    + *
    + * Actions :
    + *
    + *
  • Update every 1s the _globalAggro counter to come close to 0
  • If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
  • If the actor can't attack, order to it to return to its + * home location
  • + */ + private void thinkActive() + { + L2Attackable npc = (L2Attackable) _actor; + + // Update every 1s the _globalAggro counter to come close to 0 + if (_globalAggro != 0) + { + if (_globalAggro < 0) + { + _globalAggro++; + } + else + { + _globalAggro--; + } + } + + // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate + // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10 + if (_globalAggro >= 0) + { + for (L2Character target : npc.getKnownList().getKnownCharactersInRadius(_attackRange)) + { + if (target == null) + { + continue; + } + if (autoAttackCondition(target)) // check aggression + { + // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList + int hating = npc.getHating(target); + + // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate + if (hating == 0) + { + npc.addDamageHate(target, 0, 1); + } + } + } + + // Chose a target from its aggroList + L2Character hated; + if (_actor.isConfused()) + { + hated = getAttackTarget(); // Force mobs to attack anybody if confused + } + else + { + hated = npc.getMostHated(); + // _mostHatedAnalysis.Update(hated); + } + + // Order to the L2Attackable to attack the target + if (hated != null) + { + // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList + int aggro = npc.getHating(hated); + + if ((aggro + _globalAggro) > 0) + { + // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance + if (!_actor.isRunning()) + { + _actor.setRunning(); + } + + // Set the AI Intention to AI_INTENTION_ATTACK + setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null); + } + + return; + } + + } + // Order to the L2DefenderInstance to return to its home location because there's no target to attack + ((L2DefenderInstance) _actor).returnHome(); + } + + /** + * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
    + *
    + * Actions :
    + *
    + *
  • Update the attack timeout if actor is running
  • If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
  • Call all L2Object of its Faction inside the Faction Range
  • Chose a target and order to attack it with magic skill + * or physical attack

  • + *
    + * TODO: Manage casting rules to healer mobs (like Ant Nurses) + */ + private void thinkAttack() + { + if (Config.DEBUG) + { + _log.info("L2SiegeGuardAI.thinkAttack(); timeout=" + (_attackTimeout - GameTimeController.getGameTicks())); + } + + if (_attackTimeout < GameTimeController.getGameTicks()) + { + // Check if the actor is running + if (_actor.isRunning()) + { + // Set the actor movement type to walk and send Server->Client packet ChangeMoveType to all others L2PcInstance + _actor.setWalking(); + + // Calculate a new attack timeout + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks(); + } + } + + L2Character attackTarget = getAttackTarget(); + // Check if target is dead or if timeout is expired to stop this attack + if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getGameTicks())) + { + // Stop hating this target after the attack timeout or if target is dead + if (attackTarget != null) + { + L2Attackable npc = (L2Attackable) _actor; + npc.stopHating(attackTarget); + } + + // Cancel target and timeout + _attackTimeout = Integer.MAX_VALUE; + setAttackTarget(null); + + // Set the AI Intention to AI_INTENTION_ACTIVE + setIntention(AI_INTENTION_ACTIVE, null, null); + + _actor.setWalking(); + return; + } + + factionNotifyAndSupport(); + attackPrepare(); + } + + private final void factionNotifyAndSupport() + { + L2Character target = getAttackTarget(); + // Call all L2Object of its Faction inside the Faction Range + if ((((L2Npc) _actor).getFactionId() == null) || (target == null)) + { + return; + } + + if (target.isInvul()) + { + return; // speeding it up for siege guards + } + + String faction_id = ((L2Npc) _actor).getFactionId(); + + // Go through all L2Character that belong to its faction + // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius)) + for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(1000)) + { + if (cha == null) + { + continue; + } + + if (!(cha instanceof L2Npc)) + { + if (_selfAnalysis.hasHealOrResurrect && (cha instanceof L2PcInstance) && ((L2Npc) _actor).getCastle().getSiege().checkIsDefender(((L2PcInstance) cha).getClan())) + { + // heal friends + if (!_actor.isAttackingDisabled() && (cha.getCurrentHp() < (cha.getMaxHp() * 0.6)) && (_actor.getCurrentHp() > (_actor.getMaxHp() / 2)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2)) && cha.isInCombat()) + { + for (L2Skill sk : _selfAnalysis.healSkills) + { + if (_actor.getCurrentMp() < sk.getMpConsume()) + { + continue; + } + if (_actor.isSkillDisabled(sk)) + { + continue; + } + if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true)) + { + continue; + } + + int chance = 5; + if (chance >= Rnd.get(100)) + { + continue; + } + if (!GeoData.getInstance().canSeeTarget(_actor, cha)) + { + break; + } + + L2Object OldTarget = _actor.getTarget(); + _actor.setTarget(cha); + clientStopMoving(null); + _accessor.doCast(sk); + _actor.setTarget(OldTarget); + return; + } + } + } + continue; + } + + L2Npc npc = (L2Npc) cha; + + if (!faction_id.equals(npc.getFactionId())) + { + continue; + } + + if (npc.getAI() != null) // TODO: possibly check not needed + { + if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600) + // && _actor.getAttackByList().contains(getAttackTarget()) + && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE)) + // limiting aggro for siege guards + && target.isInsideRadius(npc, 1500, true, false) && GeoData.getInstance().canSeeTarget(npc, target)) + { + // Notify the L2Object AI with EVT_AGGRESSION + npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1); + return; + } + // heal friends + if (_selfAnalysis.hasHealOrResurrect && !_actor.isAttackingDisabled() && (npc.getCurrentHp() < (npc.getMaxHp() * 0.6)) && (_actor.getCurrentHp() > (_actor.getMaxHp() / 2)) && (_actor.getCurrentMp() > (_actor.getMaxMp() / 2)) && npc.isInCombat()) + { + for (L2Skill sk : _selfAnalysis.healSkills) + { + if (_actor.getCurrentMp() < sk.getMpConsume()) + { + continue; + } + if (_actor.isSkillDisabled(sk)) + { + continue; + } + if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true)) + { + continue; + } + + int chance = 4; + if (chance >= Rnd.get(100)) + { + continue; + } + if (!GeoData.getInstance().canSeeTarget(_actor, npc)) + { + break; + } + + L2Object OldTarget = _actor.getTarget(); + _actor.setTarget(npc); + clientStopMoving(null); + _accessor.doCast(sk); + _actor.setTarget(OldTarget); + return; + } + } + } + } + } + + private void attackPrepare() + { + // Get all information needed to choose between physical or magical attack + L2Skill[] skills = null; + double dist_2 = 0; + int range = 0; + L2DefenderInstance sGuard = (L2DefenderInstance) _actor; + L2Character attackTarget = getAttackTarget(); + + try + { + _actor.setTarget(attackTarget); + skills = _actor.getAllSkills(); + dist_2 = _actor.getPlanDistanceSq(attackTarget.getX(), attackTarget.getY()); + range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + attackTarget.getTemplate().collisionRadius; + if (attackTarget.isMoving()) + { + range += 50; + } + } + catch (NullPointerException e) + { + // _log.warning("AttackableAI: Attack target is NULL."); + _actor.setTarget(null); + setIntention(AI_INTENTION_IDLE, null, null); + return; + } + + // never attack defenders + if ((attackTarget instanceof L2PcInstance) && sGuard.getCastle().getSiege().checkIsDefender(((L2PcInstance) attackTarget).getClan())) + { + // Cancel the target + sGuard.stopHating(attackTarget); + _actor.setTarget(null); + setIntention(AI_INTENTION_IDLE, null, null); + return; + } + + if (!GeoData.getInstance().canSeeTarget(_actor, attackTarget)) + { + // Siege guards differ from normal mobs currently: + // If target cannot seen, don't attack any more + sGuard.stopHating(attackTarget); + _actor.setTarget(null); + setIntention(AI_INTENTION_IDLE, null, null); + return; + } + + // Check if the actor isn't muted and if it is far from target + if (!_actor.isMuted() && (dist_2 > (range * range))) + { + // check for long ranged skills and heal/buff skills + for (L2Skill sk : skills) + { + int castRange = sk.getCastRange(); + + if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive()) + { + + L2Object OldTarget = _actor.getTarget(); + if ((sk.getSkillType() == L2SkillType.BUFF) || (sk.getSkillType() == L2SkillType.HEAL)) + { + boolean useSkillSelf = true; + if ((sk.getSkillType() == L2SkillType.HEAL) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5))) + { + useSkillSelf = false; + break; + } + if (sk.getSkillType() == L2SkillType.BUFF) + { + L2Effect[] effects = _actor.getAllEffects(); + for (int i = 0; (effects != null) && (i < effects.length); i++) + { + L2Effect effect = effects[i]; + if (effect.getSkill() == sk) + { + useSkillSelf = false; + break; + } + } + } + if (useSkillSelf) + { + _actor.setTarget(_actor); + } + } + + clientStopMoving(null); + _accessor.doCast(sk); + _actor.setTarget(OldTarget); + return; + } + } + + // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run + if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.getKnownList().knowsObject(attackTarget))) + { + // Cancel the target + _actor.getKnownList().removeKnownObject(attackTarget); + _actor.setTarget(null); + setIntention(AI_INTENTION_IDLE, null, null); + } + else + { + double dx = _actor.getX() - attackTarget.getX(); + double dy = _actor.getY() - attackTarget.getY(); + double dz = _actor.getZ() - attackTarget.getZ(); + double homeX = attackTarget.getX() - sGuard.getSpawn().getLocx(); + double homeY = attackTarget.getY() - sGuard.getSpawn().getLocy(); + + // Check if the L2SiegeGuardInstance isn't too far from it's home location + if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800 + && (_actor.getKnownList().knowsObject(attackTarget))) + { + // Cancel the target + _actor.getKnownList().removeKnownObject(attackTarget); + _actor.setTarget(null); + setIntention(AI_INTENTION_IDLE, null, null); + } + else + // Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast) + { + // Temporary hack for preventing guards jumping off towers, + // before replacing this with effective geodata checks and AI modification + if ((dz * dz) < (170 * 170)) // normally 130 if guard z coordinates correct + { + if (_selfAnalysis.isHealer) + { + return; + } + if (_selfAnalysis.isMage) + { + range = _selfAnalysis.maxCastRange - 50; + } + if (attackTarget.isMoving()) + { + moveToPawn(attackTarget, range - 70); + } + else + { + moveToPawn(attackTarget, range); + } + } + } + } + + return; + + } + // Else, if the actor is muted and far from target, just "move to pawn" + else if (_actor.isMuted() && (dist_2 > (range * range)) && !_selfAnalysis.isHealer) + { + // Temporary hack for preventing guards jumping off towers, + // before replacing this with effective geodata checks and AI modification + double dz = _actor.getZ() - attackTarget.getZ(); + if ((dz * dz) < (170 * 170)) // normally 130 if guard z coordinates correct + { + if (_selfAnalysis.isMage) + { + range = _selfAnalysis.maxCastRange - 50; + } + if (attackTarget.isMoving()) + { + moveToPawn(attackTarget, range - 70); + } + else + { + moveToPawn(attackTarget, range); + } + } + return; + } + // Else, if this is close enough to attack + else if (dist_2 <= (range * range)) + { + // Force mobs to attack anybody if confused + L2Character hated = null; + if (_actor.isConfused()) + { + hated = attackTarget; + } + else + { + hated = ((L2Attackable) _actor).getMostHated(); + } + + if (hated == null) + { + setIntention(AI_INTENTION_ACTIVE, null, null); + return; + } + if (hated != attackTarget) + { + attackTarget = hated; + } + + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks(); + + // check for close combat skills && heal/buff skills + if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5)) + { + for (L2Skill sk : skills) + { + int castRange = sk.getCastRange(); + + if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk)) + { + L2Object OldTarget = _actor.getTarget(); + if ((sk.getSkillType() == L2SkillType.BUFF) || (sk.getSkillType() == L2SkillType.HEAL)) + { + boolean useSkillSelf = true; + if ((sk.getSkillType() == L2SkillType.HEAL) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5))) + { + useSkillSelf = false; + break; + } + if (sk.getSkillType() == L2SkillType.BUFF) + { + L2Effect[] effects = _actor.getAllEffects(); + for (int i = 0; (effects != null) && (i < effects.length); i++) + { + L2Effect effect = effects[i]; + if (effect.getSkill() == sk) + { + useSkillSelf = false; + break; + } + } + } + if (useSkillSelf) + { + _actor.setTarget(_actor); + } + } + + clientStopMoving(null); + _accessor.doCast(sk); + _actor.setTarget(OldTarget); + return; + } + } + } + // Finally, do the physical attack itself + if (!_selfAnalysis.isHealer) + { + _accessor.doAttack(attackTarget); + } + } + } + + /** + * Manage AI thinking actions of a L2Attackable.
    + *
    + */ + @Override + protected void onEvtThink() + { + // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers())) + // setIntention(AI_INTENTION_IDLE); + + // Check if the thinking action is already in progress + if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled()) + { + return; + } + + // Start thinking action + _thinking = true; + + try + { + // Manage AI thinks of a L2Attackable + if (getIntention() == AI_INTENTION_ACTIVE) + { + thinkActive(); + } + else if (getIntention() == AI_INTENTION_ATTACK) + { + thinkAttack(); + } + } + finally + { + // Stop thinking action + _thinking = false; + } + } + + /** + * Launch actions corresponding to the Event Attacked.
    + *
    + * Actions :
    + *
    + *
  • Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
  • Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
  • Set the Intention to AI_INTENTION_ATTACK
  • + *
    + *
    + * @param attacker The L2Character that attacks the actor + */ + @Override + protected void onEvtAttacked(L2Character attacker) + { + // Calculate the attack timeout + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks(); + + // Set the _globalAggro to 0 to permit attack even just after spawn + if (_globalAggro < 0) + { + _globalAggro = 0; + } + + // Add the attacker to the _aggroList of the actor + ((L2Attackable) _actor).addDamageHate(attacker, 0, 1); + + // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance + if (!_actor.isRunning()) + { + _actor.setRunning(); + } + + // Set the Intention to AI_INTENTION_ATTACK + if (getIntention() != AI_INTENTION_ATTACK) + { + setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null); + } + + super.onEvtAttacked(attacker); + } + + /** + * Launch actions corresponding to the Event Aggression.
    + *
    + * Actions :
    + *
    + *
  • Add the target to the actor _aggroList or update hate if already present
  • Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)

  • + *
    + * @param attacker The L2Character that attacks + * @param aggro The value of hate to add to the actor against the target + */ + @Override + protected void onEvtAggression(L2Character target, int aggro) + { + if (_actor == null) + { + return; + } + L2Attackable me = (L2Attackable) _actor; + + if (target != null) + { + // Add the target to the actor _aggroList or update hate if already present + me.addDamageHate(target, 0, aggro); + + // Get the hate of the actor against the target + aggro = me.getHating(target); + + if (aggro <= 0) + { + if (me.getMostHated() == null) + { + _globalAggro = -25; + me.clearAggroList(); + setIntention(AI_INTENTION_IDLE, null, null); + } + return; + } + + // Set the actor AI Intention to AI_INTENTION_ATTACK + if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK) + { + // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance + if (!_actor.isRunning()) + { + _actor.setRunning(); + } + + L2DefenderInstance sGuard = (L2DefenderInstance) _actor; + double homeX = target.getX() - sGuard.getSpawn().getLocx(); + double homeY = target.getY() - sGuard.getSpawn().getLocy(); + + // Check if the L2SiegeGuardInstance is not too far from its home location + if (((homeX * homeX) + (homeY * homeY)) < 3240000) + { + setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null); + } + } + } + else + { + // currently only for setting lower general aggro + if (aggro >= 0) + { + return; + } + + L2Character mostHated = me.getMostHated(); + if (mostHated == null) + { + _globalAggro = -25; + return; + } + for (L2Character aggroed : me.getAggroList().keySet()) + { + me.addDamageHate(aggroed, 0, aggro); + } + + aggro = me.getHating(mostHated); + if (aggro <= 0) + { + _globalAggro = -25; + me.clearAggroList(); + setIntention(AI_INTENTION_IDLE, null, null); + } + } + } + + @Override + public void stopAITask() + { + if (_aiTask != null) + { + _aiTask.cancel(false); + _aiTask = null; + } + _accessor.detachAI(); + super.stopAITask(); + } + +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2SummonAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2SummonAI.java new file mode 100644 index 00000000..e92cbdfd --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2SummonAI.java @@ -0,0 +1,299 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW; +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE; + +import java.util.concurrent.Future; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.GeoData; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Character.AIAccessor; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.util.Rnd; + +public class L2SummonAI extends L2PlayableAI implements Runnable +{ + private static final int AVOID_RADIUS = 70; + + private volatile boolean _thinking; // to prevent recursive thinking + private volatile boolean _startFollow = ((L2Summon) _actor).getFollowStatus(); + private L2Character _lastAttack = null; + + private volatile boolean _startAvoid = false; + private Future _avoidTask = null; + + public L2SummonAI(AIAccessor accessor) + { + super(accessor); + } + + @Override + protected void onIntentionIdle() + { + stopFollow(); + _startFollow = false; + onIntentionActive(); + } + + @Override + protected void onIntentionActive() + { + L2Summon summon = (L2Summon) _actor; + if (_startFollow) + { + setIntention(AI_INTENTION_FOLLOW, summon.getOwner()); + } + else + { + super.onIntentionActive(); + } + } + + @Override + synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1) + { + switch (intention) + { + case AI_INTENTION_ACTIVE: + case AI_INTENTION_FOLLOW: + startAvoidTask(); + break; + default: + stopAvoidTask(); + } + + super.changeIntention(intention, arg0, arg1); + } + + private void thinkAttack() + { + if (checkTargetLostOrDead(getAttackTarget())) + { + setAttackTarget(null); + return; + } + if (maybeMoveToPawn(getAttackTarget(), _actor.getPhysicalAttackRange())) + { + return; + } + clientStopMoving(null); + _accessor.doAttack(getAttackTarget()); + } + + private void thinkCast() + { + L2Summon summon = (L2Summon) _actor; + if (checkTargetLost(getCastTarget())) + { + setCastTarget(null); + return; + } + boolean val = _startFollow; + if (maybeMoveToPawn(getCastTarget(), _actor.getMagicalAttackRange(_skill))) + { + return; + } + clientStopMoving(null); + summon.setFollowStatus(false); + setIntention(AI_INTENTION_IDLE); + _startFollow = val; + _accessor.doCast(_skill); + } + + private void thinkPickUp() + { + if (checkTargetLost(getTarget())) + { + return; + } + if (maybeMoveToPawn(getTarget(), 36)) + { + return; + } + setIntention(AI_INTENTION_IDLE); + ((L2Summon.AIAccessor) _accessor).doPickupItem(getTarget()); + } + + private void thinkInteract() + { + if (checkTargetLost(getTarget())) + { + return; + } + if (maybeMoveToPawn(getTarget(), 36)) + { + return; + } + setIntention(AI_INTENTION_IDLE); + } + + @Override + protected void onEvtThink() + { + if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled()) + { + return; + } + _thinking = true; + try + { + switch (getIntention()) + { + case AI_INTENTION_ATTACK: + thinkAttack(); + break; + case AI_INTENTION_CAST: + thinkCast(); + break; + case AI_INTENTION_PICK_UP: + thinkPickUp(); + break; + case AI_INTENTION_INTERACT: + thinkInteract(); + break; + } + } + finally + { + _thinking = false; + } + } + + @Override + protected void onEvtFinishCasting() + { + if (_lastAttack == null) + { + ((L2Summon) _actor).setFollowStatus(_startFollow); + } + else + { + setIntention(CtrlIntention.AI_INTENTION_ATTACK, _lastAttack); + _lastAttack = null; + } + } + + @Override + protected void onEvtAttacked(L2Character attacker) + { + super.onEvtAttacked(attacker); + + avoidAttack(attacker); + } + + @Override + protected void onEvtEvaded(L2Character attacker) + { + super.onEvtEvaded(attacker); + + avoidAttack(attacker); + } + + private void avoidAttack(L2Character attacker) + { + // trying to avoid if summon near owner + if ((((L2Summon) _actor).getOwner() != null) && (((L2Summon) _actor).getOwner() != attacker) && ((L2Summon) _actor).getOwner().isInsideRadius(_actor, 2 * AVOID_RADIUS, true, false)) + { + _startAvoid = true; + } + } + + @Override + public void run() + { + if (_startAvoid) + { + _startAvoid = false; + + if (!_clientMoving && !_actor.isDead() && !_actor.isMovementDisabled()) + { + final int ownerX = ((L2Summon) _actor).getOwner().getX(); + final int ownerY = ((L2Summon) _actor).getOwner().getY(); + final double angle = Math.toRadians(Rnd.get(-90, 90)) + Math.atan2(ownerY - _actor.getY(), ownerX - _actor.getX()); + + final int targetX = ownerX + (int) (AVOID_RADIUS * Math.cos(angle)); + final int targetY = ownerY + (int) (AVOID_RADIUS * Math.sin(angle)); + if ((Config.GEODATA == 0) || GeoData.getInstance().canMoveFromToTarget(_actor.getX(), _actor.getY(), _actor.getZ(), targetX, targetY, _actor.getZ(), _actor.getInstanceId())) + { + moveTo(targetX, targetY, _actor.getZ()); + } + } + } + } + + public void notifyFollowStatusChange() + { + _startFollow = !_startFollow; + switch (getIntention()) + { + case AI_INTENTION_ACTIVE: + case AI_INTENTION_FOLLOW: + case AI_INTENTION_IDLE: + case AI_INTENTION_MOVE_TO: + case AI_INTENTION_PICK_UP: + ((L2Summon) _actor).setFollowStatus(_startFollow); + } + } + + public void setStartFollowController(boolean val) + { + _startFollow = val; + } + + @Override + protected void onIntentionCast(L2Skill skill, L2Object target) + { + if (getIntention() == AI_INTENTION_ATTACK) + { + _lastAttack = getAttackTarget(); + } + else + { + _lastAttack = null; + } + super.onIntentionCast(skill, target); + } + + private void startAvoidTask() + { + if (_avoidTask == null) + { + _avoidTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(this, 100, 100); + } + } + + private void stopAvoidTask() + { + if (_avoidTask != null) + { + _avoidTask.cancel(false); + _avoidTask = null; + } + } + + @Override + public void stopAITask() + { + stopAvoidTask(); + super.stopAITask(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/ai/L2VehicleAI.java b/L2J_Server/java/com/l2jserver/gameserver/ai/L2VehicleAI.java new file mode 100644 index 00000000..90abd015 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/ai/L2VehicleAI.java @@ -0,0 +1,121 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.ai; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Vehicle; + +public abstract class L2VehicleAI extends L2CharacterAI +{ + /** + * @author DS Simple AI for vehicles + */ + public L2VehicleAI(L2Vehicle.AIAccessor accessor) + { + super(accessor); + } + + @Override + protected void onIntentionAttack(L2Character target) + { + } + + @Override + protected void onIntentionCast(L2Skill skill, L2Object target) + { + } + + @Override + protected void onIntentionFollow(L2Character target) + { + } + + @Override + protected void onIntentionPickUp(L2Object item) + { + } + + @Override + protected void onIntentionInteract(L2Object object) + { + } + + @Override + protected void onEvtAttacked(L2Character attacker) + { + } + + @Override + protected void onEvtAggression(L2Character target, int aggro) + { + } + + @Override + protected void onEvtStunned(L2Character attacker) + { + } + + @Override + protected void onEvtSleeping(L2Character attacker) + { + } + + @Override + protected void onEvtRooted(L2Character attacker) + { + } + + @Override + protected void onEvtForgetObject(L2Object object) + { + } + + @Override + protected void onEvtCancel() + { + } + + @Override + protected void onEvtDead() + { + } + + @Override + protected void onEvtFakeDeath() + { + } + + @Override + protected void onEvtFinishCasting() + { + } + + @Override + protected void clientActionFailed() + { + } + + @Override + protected void moveToPawn(L2Object pawn, int offset) + { + } + + @Override + protected void clientStoppedMoving() + { + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/cache/CrestCache.java b/L2J_Server/java/com/l2jserver/gameserver/cache/CrestCache.java new file mode 100644 index 00000000..0ec4dfaa --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/cache/CrestCache.java @@ -0,0 +1,321 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.cache; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.idfactory.IdFactory; +import com.l2jserver.gameserver.model.L2Clan; + +/** + * @author Layane + */ +public class CrestCache +{ + private static Logger _log = Logger.getLogger(CrestCache.class.getName()); + + private final FastMRUCache _cachePledge = new FastMRUCache<>(); + + private final FastMRUCache _cachePledgeLarge = new FastMRUCache<>(); + + private final FastMRUCache _cacheAlly = new FastMRUCache<>(); + + private int _loadedFiles; + + private long _bytesBuffLen; + + public static CrestCache getInstance() + { + return SingletonHolder._instance; + } + + private CrestCache() + { + convertOldPedgeFiles(); + reload(); + } + + public void reload() + { + FileFilter filter = new BmpFilter(); + + File dir = new File(Config.DATAPACK_ROOT, "data/crests/"); + + File[] files = dir.listFiles(filter); + byte[] content; + synchronized (this) + { + _loadedFiles = 0; + _bytesBuffLen = 0; + + _cachePledge.clear(); + _cachePledgeLarge.clear(); + _cacheAlly.clear(); + } + + FastMap _mapPledge = _cachePledge.getContentMap(); + FastMap _mapPledgeLarge = _cachePledgeLarge.getContentMap(); + FastMap _mapAlly = _cacheAlly.getContentMap(); + + for (File file : files) + { + + synchronized (this) + { + try (RandomAccessFile f = new RandomAccessFile(file, "r")) + { + content = new byte[(int) f.length()]; + f.readFully(content); + + if (file.getName().startsWith("Crest_Large_")) + { + _mapPledgeLarge.put(Integer.valueOf(file.getName().substring(12, file.getName().length() - 4)), content); + } + else if (file.getName().startsWith("Crest_")) + { + _mapPledge.put(Integer.valueOf(file.getName().substring(6, file.getName().length() - 4)), content); + } + else if (file.getName().startsWith("AllyCrest_")) + { + _mapAlly.put(Integer.valueOf(file.getName().substring(10, file.getName().length() - 4)), content); + } + _loadedFiles++; + _bytesBuffLen += content.length; + } + catch (Exception e) + { + _log.log(Level.WARNING, "Problem with crest bmp file " + e.getMessage(), e); + } + } + } + + _log.info("Cache[Crest]: " + String.format("%.3f", getMemoryUsage()) + "MB on " + getLoadedFiles() + " files loaded. (Forget Time: " + (_cachePledge.getForgetTime() / 1000) + "s , Capacity: " + _cachePledge.capacity() + ")"); + } + + public void convertOldPedgeFiles() + { + File dir = new File(Config.DATAPACK_ROOT, "data/crests/"); + + File[] files = dir.listFiles(new OldPledgeFilter()); + + for (File file : files) + { + int clanId = Integer.parseInt(file.getName().substring(7, file.getName().length() - 4)); + + _log.info("Found old crest file \"" + file.getName() + "\" for clanId " + clanId); + + int newId = IdFactory.getInstance().getNextId(); + + L2Clan clan = ClanTable.getInstance().getClan(clanId); + + if (clan != null) + { + removeOldPledgeCrest(clan.getCrestId()); + + file.renameTo(new File(Config.DATAPACK_ROOT, "data/crests/Crest_" + newId + ".bmp")); + _log.info("Renamed Clan crest to new format: Crest_" + newId + ".bmp"); + + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("UPDATE clan_data SET crest_id = ? WHERE clan_id = ?"); + statement.setInt(1, newId); + statement.setInt(2, clan.getClanId()); + statement.executeUpdate(); + statement.close(); + } + catch (SQLException e) + { + _log.log(Level.WARNING, "Could not update the crest id:" + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + + clan.setCrestId(newId); + } + else + { + _log.info("Clan Id: " + clanId + " does not exist in table.. deleting."); + file.delete(); + } + } + } + + public float getMemoryUsage() + { + return ((float) _bytesBuffLen / 1048576); + } + + public int getLoadedFiles() + { + return _loadedFiles; + } + + public byte[] getPledgeCrest(int id) + { + return _cachePledge.get(id); + } + + public byte[] getPledgeCrestLarge(int id) + { + return _cachePledgeLarge.get(id); + } + + public byte[] getAllyCrest(int id) + { + return _cacheAlly.get(id); + } + + public void removePledgeCrest(int id) + { + File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/Crest_" + id + ".bmp"); + _cachePledge.remove(id); + try + { + crestFile.delete(); + } + catch (Exception e) + { + } + } + + public void removePledgeCrestLarge(int id) + { + File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/Crest_Large_" + id + ".bmp"); + _cachePledgeLarge.remove(id); + try + { + crestFile.delete(); + } + catch (Exception e) + { + } + } + + public void removeOldPledgeCrest(int id) + { + File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/Pledge_" + id + ".bmp"); + try + { + crestFile.delete(); + } + catch (Exception e) + { + } + } + + public void removeAllyCrest(int id) + { + File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/AllyCrest_" + id + ".bmp"); + _cacheAlly.remove(id); + try + { + crestFile.delete(); + } + catch (Exception e) + { + } + } + + public boolean savePledgeCrest(int newId, byte[] data) + { + File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/Crest_" + newId + ".bmp"); + try (FileOutputStream out = new FileOutputStream(crestFile)) + { + out.write(data); + _cachePledge.getContentMap().put(newId, data); + return true; + } + catch (IOException e) + { + _log.log(Level.INFO, "Error saving pledge crest" + crestFile + ":", e); + return false; + } + } + + public boolean savePledgeCrestLarge(int newId, byte[] data) + { + File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/Crest_Large_" + newId + ".bmp"); + try (FileOutputStream out = new FileOutputStream(crestFile)) + { + out.write(data); + _cachePledgeLarge.getContentMap().put(newId, data); + return true; + } + catch (IOException e) + { + _log.log(Level.INFO, "Error saving Large pledge crest" + crestFile + ":", e); + return false; + } + } + + public boolean saveAllyCrest(int newId, byte[] data) + { + File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/AllyCrest_" + newId + ".bmp"); + try (FileOutputStream out = new FileOutputStream(crestFile)) + { + out.write(data); + _cacheAlly.getContentMap().put(newId, data); + return true; + } + catch (IOException e) + { + _log.log(Level.INFO, "Error saving ally crest" + crestFile + ":", e); + return false; + } + } + + protected static class BmpFilter implements FileFilter + { + @Override + public boolean accept(File file) + { + return (file.getName().endsWith(".bmp")); + } + } + + protected static class OldPledgeFilter implements FileFilter + { + @Override + public boolean accept(File file) + { + return (file.getName().startsWith("Pledge_")); + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final CrestCache _instance = new CrestCache(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/cache/FastMRUCache.java b/L2J_Server/java/com/l2jserver/gameserver/cache/FastMRUCache.java new file mode 100644 index 00000000..2f7835d0 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/cache/FastMRUCache.java @@ -0,0 +1,226 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.cache; + +import javolution.context.ObjectFactory; +import javolution.lang.Reusable; +import javolution.util.FastCollection; +import javolution.util.FastComparator; +import javolution.util.FastList; +import javolution.util.FastMap; + +/** + * @author Layane + */ +@SuppressWarnings( +{ + "rawtypes" +}) +public class FastMRUCache extends FastCollection implements Reusable +{ + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = 1L; + private static final int DEFAULT_CAPACITY = 50; + private static final int DEFAULT_FORGET_TIME = 300000; // 5 Minutes + + private final FastMap _cache = new FastMap().setKeyComparator(FastComparator.DIRECT); + private FastMap _map; + private final FastList _mruList = new FastList<>(); + private int _cacheSize; + private int _forgetTime; + + class CacheNode + { + long _lastModified; + V _node; + + public CacheNode(V object) + { + _lastModified = System.currentTimeMillis(); + _node = object; + } + + @Override + public boolean equals(Object object) + { + return _node == object; + } + + } + + /** + * Holds the set factory. + */ + private static final ObjectFactory FACTORY = new ObjectFactory() + { + + @Override + public Object create() + { + return new FastMRUCache(); + } + + @Override + public void cleanup(Object obj) + { + ((FastMRUCache) obj).reset(); + } + }; + + /** + * Returns a set allocated from the stack when executing in a {@link javolution.realtime.PoolContext PoolContext}). + * @return a new, pre-allocated or recycled set instance. + */ + public static FastMRUCache newInstance() + { + return (FastMRUCache) FACTORY.object(); + } + + public FastMRUCache() + { + this(new FastMap(), DEFAULT_CAPACITY, DEFAULT_FORGET_TIME); + } + + public FastMRUCache(FastMap map) + { + this(map, DEFAULT_CAPACITY, DEFAULT_FORGET_TIME); + } + + public FastMRUCache(FastMap map, int max) + { + this(map, max, DEFAULT_FORGET_TIME); + } + + public FastMRUCache(FastMap map, int max, int forgetTime) + { + _map = map; + _cacheSize = max; + _forgetTime = forgetTime; + _map.setKeyComparator(FastComparator.DIRECT); + } + + // Implements Reusable. + @Override + public synchronized void reset() + { + _map.reset(); + _cache.reset(); + _mruList.reset(); + _map.setKeyComparator(FastComparator.DIRECT); + _cache.setKeyComparator(FastComparator.DIRECT); + } + + public synchronized V get(K key) + { + V result; + + if (!_cache.containsKey(key)) + { + if (_mruList.size() >= _cacheSize) + { + + _cache.remove(_mruList.getLast()); + _mruList.removeLast(); + } + + result = _map.get(key); + + _cache.put(key, new CacheNode(result)); + _mruList.addFirst(key); + } + else + { + CacheNode current = _cache.get(key); + + if ((current._lastModified + _forgetTime) <= System.currentTimeMillis()) + { + current._lastModified = System.currentTimeMillis(); + current._node = _map.get(key); + _cache.put(key, current); + } + + _mruList.remove(key); + _mruList.addFirst(key); + + result = current._node; + } + + return result; + } + + @Override + public synchronized boolean remove(Object key) + { + _cache.remove(key); + _mruList.remove(key); + return _map.remove(key) == key; + } + + public FastMap getContentMap() + { + return _map; + } + + @Override + public int size() + { + return _mruList.size(); + } + + public int capacity() + { + return _cacheSize; + } + + public int getForgetTime() + { + return _forgetTime; + } + + @Override + public synchronized void clear() + { + _cache.clear(); + _mruList.clear(); + _map.clear(); + } + + // Implements FastCollection abstract method. + @Override + public final Record head() + { + return _mruList.head(); + } + + @Override + public final Record tail() + { + return _mruList.tail(); + } + + @Override + public final Object valueOf(Record record) + { + return ((FastMap.Entry) record).getKey(); + } + + @Override + public final void delete(Record record) + { + remove(((FastMap.Entry) record).getKey()); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/cache/HtmCache.java b/L2J_Server/java/com/l2jserver/gameserver/cache/HtmCache.java new file mode 100644 index 00000000..e5821b49 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/cache/HtmCache.java @@ -0,0 +1,255 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.cache; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.util.Util; + +/** + * @author Layane + */ +public class HtmCache +{ + private static Logger _log = Logger.getLogger(HtmCache.class.getName()); + + private final Map _cache; + + private int _loadedFiles; + private long _bytesBuffLen; + + public static HtmCache getInstance() + { + return SingletonHolder._instance; + } + + private HtmCache() + { + if (Config.LAZY_CACHE) + { + _cache = new FastMap().shared(); + } + else + { + _cache = new FastMap<>(); + } + reload(); + } + + public void reload() + { + reload(Config.DATAPACK_ROOT); + } + + public void reload(File f) + { + if (!Config.LAZY_CACHE) + { + _log.info("Html cache start..."); + parseDir(f); + _log.info("Cache[HTML]: " + String.format("%.3f", getMemoryUsage()) + " megabytes on " + getLoadedFiles() + " files loaded"); + } + else + { + _cache.clear(); + _loadedFiles = 0; + _bytesBuffLen = 0; + _log.info("Cache[HTML]: Running lazy cache"); + } + } + + public void reloadPath(File f) + { + parseDir(f); + _log.info("Cache[HTML]: Reloaded specified path."); + } + + public double getMemoryUsage() + { + return ((float) _bytesBuffLen / 1048576); + } + + public int getLoadedFiles() + { + return _loadedFiles; + } + + protected static class HtmFilter implements FileFilter + { + @Override + public boolean accept(File file) + { + if (!file.isDirectory()) + { + return (file.getName().endsWith(".htm") || file.getName().endsWith(".html")); + } + return true; + } + } + + private void parseDir(File dir) + { + FileFilter filter = new HtmFilter(); + File[] files = dir.listFiles(filter); + + for (File file : files) + { + if (!file.isDirectory()) + { + loadFile(file); + } + else + { + parseDir(file); + } + } + } + + public String loadFile(File file) + { + final String relpath = Util.getRelativePath(Config.DATAPACK_ROOT, file); + final int hashcode = relpath.hashCode(); + + final HtmFilter filter = new HtmFilter(); + + if (file.exists() && filter.accept(file) && !file.isDirectory()) + { + String content; + try (FileInputStream fis = new FileInputStream(file); + BufferedInputStream bis = new BufferedInputStream(fis)) + { + int bytes = bis.available(); + byte[] raw = new byte[bytes]; + + bis.read(raw); + content = new String(raw, "UTF-8"); + content = content.replaceAll("\r\n", "\n"); + + String oldContent = _cache.get(hashcode); + + if (oldContent == null) + { + _bytesBuffLen += bytes; + _loadedFiles++; + } + else + { + _bytesBuffLen = (_bytesBuffLen - oldContent.length()) + bytes; + } + + _cache.put(hashcode, content); + + return content; + } + catch (Exception e) + { + _log.log(Level.WARNING, "Problem with htm file " + e.getMessage(), e); + } + } + + return null; + } + + public String getHtmForce(String prefix, String path) + { + String content = getHtm(prefix, path); + + if (content == null) + { + content = "My text is missing:
    " + path + ""; + _log.warning("Cache[HTML]: Missing HTML page: " + path); + } + + return content; + } + + public String getHtm(String prefix, String path) + { + String newPath = null; + String content; + if ((prefix != null) && !prefix.isEmpty()) + { + newPath = prefix + path; + content = getHtm(newPath); + if (content != null) + { + return content; + } + } + + content = getHtm(path); + if ((content != null) && (newPath != null)) + { + _cache.put(newPath.hashCode(), content); + } + + return content; + } + + private String getHtm(String path) + { + if ((path == null) || path.isEmpty()) + { + return ""; // avoid possible NPE + } + + final int hashCode = path.hashCode(); + String content = _cache.get(hashCode); + + if (Config.LAZY_CACHE && (content == null)) + { + content = loadFile(new File(Config.DATAPACK_ROOT, path)); + } + + return content; + } + + public boolean contains(String path) + { + return _cache.containsKey(path.hashCode()); + } + + /** + * Check if an HTM exists and can be loaded + * @param path The path to the HTM + */ + public boolean isLoadable(String path) + { + File file = new File(path); + HtmFilter filter = new HtmFilter(); + + if (file.exists() && filter.accept(file) && !file.isDirectory()) + { + return true; + } + + return false; + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final HtmCache _instance = new HtmCache(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/cache/WarehouseCacheManager.java b/L2J_Server/java/com/l2jserver/gameserver/cache/WarehouseCacheManager.java new file mode 100644 index 00000000..448dbb30 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/cache/WarehouseCacheManager.java @@ -0,0 +1,75 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.cache; + +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * @author -Nemesiss- + */ +public class WarehouseCacheManager +{ + protected final FastMap _cachedWh; + protected final long _cacheTime; + + public static WarehouseCacheManager getInstance() + { + return SingletonHolder._instance; + } + + private WarehouseCacheManager() + { + _cacheTime = Config.WAREHOUSE_CACHE_TIME * 60000L; // 60*1000 = 60000 + _cachedWh = new FastMap().shared(); + ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new CacheScheduler(), 120000, 60000); + } + + public void addCacheTask(L2PcInstance pc) + { + _cachedWh.put(pc, System.currentTimeMillis()); + } + + public void remCacheTask(L2PcInstance pc) + { + _cachedWh.remove(pc); + } + + public class CacheScheduler implements Runnable + { + @Override + public void run() + { + long cTime = System.currentTimeMillis(); + for (L2PcInstance pc : _cachedWh.keySet()) + { + if ((cTime - _cachedWh.get(pc)) > _cacheTime) + { + pc.clearWarehouse(); + _cachedWh.remove(pc); + } + } + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final WarehouseCacheManager _instance = new WarehouseCacheManager(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/communitybbs/BB/Forum.java b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/BB/Forum.java new file mode 100644 index 00000000..c7a4e34d --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/BB/Forum.java @@ -0,0 +1,299 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.communitybbs.BB; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.communitybbs.Manager.ForumsBBSManager; +import com.l2jserver.gameserver.communitybbs.Manager.TopicBBSManager; + +public class Forum +{ + // type + public static final int ROOT = 0; + public static final int NORMAL = 1; + public static final int CLAN = 2; + public static final int MEMO = 3; + public static final int MAIL = 4; + // perm + public static final int INVISIBLE = 0; + public static final int ALL = 1; + public static final int CLANMEMBERONLY = 2; + public static final int OWNERONLY = 3; + + private static Logger _log = Logger.getLogger(Forum.class.getName()); + private final List _children; + private final Map _topic; + private final int _forumId; + private String _forumName; + // private int _ForumParent; + private int _forumType; + private int _forumPost; + private int _forumPerm; + private final Forum _fParent; + private int _ownerID; + private boolean _loaded = false; + + /** + * Creates new instance of Forum. When you create new forum, use {@link com.l2jserver.gameserver.communitybbs.Manager.ForumsBBSManager# addForum(com.l2jserver.gameserver.communitybbs.BB.Forum)} to add forum to the forums manager. + * @param i + */ + public Forum(int Forumid, Forum FParent) + { + _forumId = Forumid; + _fParent = FParent; + _children = new FastList<>(); + _topic = new FastMap<>(); + + /* + * load(); getChildren(); + */ + } + + /** + * @param name + * @param parent + * @param type + * @param perm + */ + public Forum(String name, Forum parent, int type, int perm, int OwnerID) + { + _forumName = name; + _forumId = ForumsBBSManager.getInstance().getANewID(); + // _ForumParent = parent.getID(); + _forumType = type; + _forumPost = 0; + _forumPerm = perm; + _fParent = parent; + _ownerID = OwnerID; + _children = new FastList<>(); + _topic = new FastMap<>(); + parent._children.add(this); + ForumsBBSManager.getInstance().addForum(this); + _loaded = true; + } + + /** + * + */ + private void load() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT * FROM forums WHERE forum_id=?"); + statement.setInt(1, _forumId); + ResultSet result = statement.executeQuery(); + + if (result.next()) + { + _forumName = result.getString("forum_name"); + // _ForumParent = result.getInt("forum_parent"); + _forumPost = result.getInt("forum_post"); + _forumType = result.getInt("forum_type"); + _forumPerm = result.getInt("forum_perm"); + _ownerID = result.getInt("forum_owner_id"); + } + result.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Data error on Forum " + _forumId + " : " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT * FROM topic WHERE topic_forum_id=? ORDER BY topic_id DESC"); + statement.setInt(1, _forumId); + ResultSet result = statement.executeQuery(); + + while (result.next()) + { + Topic t = new Topic(Topic.ConstructorType.RESTORE, result.getInt("topic_id"), result.getInt("topic_forum_id"), result.getString("topic_name"), result.getLong("topic_date"), result.getString("topic_ownername"), result.getInt("topic_ownerid"), result.getInt("topic_type"), result.getInt("topic_reply")); + _topic.put(t.getID(), t); + if (t.getID() > TopicBBSManager.getInstance().getMaxID(this)) + { + TopicBBSManager.getInstance().setMaxID(t.getID(), this); + } + } + result.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Data error on Forum " + _forumId + " : " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * + */ + private void getChildren() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT forum_id FROM forums WHERE forum_parent=?"); + statement.setInt(1, _forumId); + ResultSet result = statement.executeQuery(); + + while (result.next()) + { + Forum f = new Forum(result.getInt("forum_id"), this); + _children.add(f); + ForumsBBSManager.getInstance().addForum(f); + } + result.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Data error on Forum (children): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + + } + + public int getTopicSize() + { + vload(); + return _topic.size(); + } + + public Topic getTopic(int j) + { + vload(); + return _topic.get(j); + } + + public void addTopic(Topic t) + { + vload(); + _topic.put(t.getID(), t); + } + + /** + * @return + */ + public int getID() + { + return _forumId; + } + + public String getName() + { + vload(); + return _forumName; + } + + public int getType() + { + vload(); + return _forumType; + } + + /** + * @param name + * @return + */ + public Forum getChildByName(String name) + { + vload(); + for (Forum f : _children) + { + if (f.getName().equals(name)) + { + return f; + } + } + return null; + } + + /** + * @param id + */ + public void rmTopicByID(int id) + { + _topic.remove(id); + + } + + /** + * + */ + public void insertIntoDb() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("INSERT INTO forums (forum_id,forum_name,forum_parent,forum_post,forum_type,forum_perm,forum_owner_id) VALUES (?,?,?,?,?,?,?)"); + statement.setInt(1, _forumId); + statement.setString(2, _forumName); + statement.setInt(3, _fParent.getID()); + statement.setInt(4, _forumPost); + statement.setInt(5, _forumType); + statement.setInt(6, _forumPerm); + statement.setInt(7, _ownerID); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Error while saving new Forum to db " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * + */ + public void vload() + { + if (_loaded == false) + { + load(); + getChildren(); + _loaded = true; + } + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/communitybbs/BB/Post.java b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/BB/Post.java new file mode 100644 index 00000000..aaf3bf39 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/BB/Post.java @@ -0,0 +1,205 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.communitybbs.BB; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; + +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.communitybbs.Manager.PostBBSManager; + +/** + * @author Maktakien + */ +public class Post +{ + private static Logger _log = Logger.getLogger(Post.class.getName()); + + public static class CPost + { + public int postId; + public String postOwner; + public int postOwnerId; + public long postDate; + public int postTopicId; + public int postForumId; + public String postTxt; + } + + private final List _post; + + /** + * @param restore + * @param t + */ + // public enum ConstructorType {REPLY, CREATE }; + public Post(String _PostOwner, int _PostOwnerID, long date, int tid, int _PostForumID, String txt) + { + _post = new FastList<>(); + CPost cp = new CPost(); + cp.postId = 0; + cp.postOwner = _PostOwner; + cp.postOwnerId = _PostOwnerID; + cp.postDate = date; + cp.postTopicId = tid; + cp.postForumId = _PostForumID; + cp.postTxt = txt; + _post.add(cp); + insertindb(cp); + + } + + public void insertindb(CPost cp) + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("INSERT INTO posts (post_id,post_owner_name,post_ownerid,post_date,post_topic_id,post_forum_id,post_txt) values (?,?,?,?,?,?,?)"); + statement.setInt(1, cp.postId); + statement.setString(2, cp.postOwner); + statement.setInt(3, cp.postOwnerId); + statement.setLong(4, cp.postDate); + statement.setInt(5, cp.postTopicId); + statement.setInt(6, cp.postForumId); + statement.setString(7, cp.postTxt); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Error while saving new Post to db " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + + } + + public Post(Topic t) + { + _post = new FastList<>(); + load(t); + } + + public CPost getCPost(int id) + { + int i = 0; + for (CPost cp : _post) + { + if (i++ == id) + { + return cp; + } + } + return null; + } + + public void deleteme(Topic t) + { + PostBBSManager.getInstance().delPostByTopic(t); + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("DELETE FROM posts WHERE post_forum_id=? AND post_topic_id=?"); + statement.setInt(1, t.getForumID()); + statement.setInt(2, t.getID()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Error while deleting post: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * @param t + */ + private void load(Topic t) + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT * FROM posts WHERE post_forum_id=? AND post_topic_id=? ORDER BY post_id ASC"); + statement.setInt(1, t.getForumID()); + statement.setInt(2, t.getID()); + ResultSet result = statement.executeQuery(); + while (result.next()) + { + CPost cp = new CPost(); + cp.postId = result.getInt("post_id"); + cp.postOwner = result.getString("post_owner_name"); + cp.postOwnerId = result.getInt("post_ownerid"); + cp.postDate = result.getLong("post_date"); + cp.postTopicId = result.getInt("post_topic_id"); + cp.postForumId = result.getInt("post_forum_id"); + cp.postTxt = result.getString("post_txt"); + _post.add(cp); + } + result.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Data error on Post " + t.getForumID() + "/" + t.getID() + " : " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * @param i + */ + public void updatetxt(int i) + { + Connection con = null; + try + { + CPost cp = getCPost(i); + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("UPDATE posts SET post_txt=? WHERE post_id=? AND post_topic_id=? AND post_forum_id=?"); + statement.setString(1, cp.postTxt); + statement.setInt(2, cp.postId); + statement.setInt(3, cp.postTopicId); + statement.setInt(4, cp.postForumId); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Error while saving new Post to db " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/communitybbs/BB/Topic.java b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/BB/Topic.java new file mode 100644 index 00000000..c2d92ae9 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/BB/Topic.java @@ -0,0 +1,170 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.communitybbs.BB; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.communitybbs.Manager.TopicBBSManager; + +public class Topic +{ + + private static Logger _log = Logger.getLogger(Topic.class.getName()); + public static final int MORMAL = 0; + public static final int MEMO = 1; + + private final int _id; + private final int _forumId; + private final String _topicName; + private final long _date; + private final String _ownerName; + private final int _ownerId; + private final int _type; + private final int _cReply; + + /** + * @param restaure + * @param i + * @param j + * @param string + * @param k + * @param string2 + * @param l + * @param m + * @param n + */ + public Topic(ConstructorType ct, int id, int fid, String name, long date, String oname, int oid, int type, int Creply) + { + _id = id; + _forumId = fid; + _topicName = name; + _date = date; + _ownerName = oname; + _ownerId = oid; + _type = type; + _cReply = Creply; + TopicBBSManager.getInstance().addTopic(this); + + if (ct == ConstructorType.CREATE) + { + + insertindb(); + } + } + + /** + * + */ + public void insertindb() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("INSERT INTO topic (topic_id,topic_forum_id,topic_name,topic_date,topic_ownername,topic_ownerid,topic_type,topic_reply) values (?,?,?,?,?,?,?,?)"); + statement.setInt(1, _id); + statement.setInt(2, _forumId); + statement.setString(3, _topicName); + statement.setLong(4, _date); + statement.setString(5, _ownerName); + statement.setInt(6, _ownerId); + statement.setInt(7, _type); + statement.setInt(8, _cReply); + statement.execute(); + statement.close(); + + } + catch (Exception e) + { + _log.log(Level.WARNING, "Error while saving new Topic to db " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + + } + + public enum ConstructorType + { + RESTORE, + CREATE + } + + /** + * @return + */ + public int getID() + { + return _id; + } + + public int getForumID() + { + return _forumId; + } + + /** + * @return + */ + public String getName() + { + return _topicName; + } + + public String getOwnerName() + { + return _ownerName; + } + + /** + * + */ + public void deleteme(Forum f) + { + TopicBBSManager.getInstance().delTopic(this); + f.rmTopicByID(getID()); + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("DELETE FROM topic WHERE topic_id=? AND topic_forum_id=?"); + statement.setInt(1, getID()); + statement.setInt(2, f.getID()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Error while deleting topic: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * @return + */ + public long getDate() + { + return _date; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/communitybbs/CommunityBoard.java b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/CommunityBoard.java new file mode 100644 index 00000000..e1808f84 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/CommunityBoard.java @@ -0,0 +1,159 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.communitybbs; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.communitybbs.Manager.ClanBBSManager; +import com.l2jserver.gameserver.communitybbs.Manager.PostBBSManager; +import com.l2jserver.gameserver.communitybbs.Manager.RegionBBSManager; +import com.l2jserver.gameserver.communitybbs.Manager.TopBBSManager; +import com.l2jserver.gameserver.communitybbs.Manager.TopicBBSManager; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.L2GameClient; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ShowBoard; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; + +public class CommunityBoard +{ + private CommunityBoard() + { + } + + public static CommunityBoard getInstance() + { + return SingletonHolder._instance; + } + + public void handleCommands(L2GameClient client, String command) + { + L2PcInstance activeChar = client.getActiveChar(); + if (activeChar == null) + { + return; + } + + switch (Config.COMMUNITY_TYPE) + { + default: + case 0: // disabled + activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CB_OFFLINE)); + break; + case 1: // old + RegionBBSManager.getInstance().parsecmd(command, activeChar); + break; + case 2: // new + if (command.startsWith("_bbsclan")) + { + ClanBBSManager.getInstance().parsecmd(command, activeChar); + } + else if (command.startsWith("_bbsmemo")) + { + TopicBBSManager.getInstance().parsecmd(command, activeChar); + } + else if (command.startsWith("_bbstopics")) + { + TopicBBSManager.getInstance().parsecmd(command, activeChar); + } + else if (command.startsWith("_bbsposts")) + { + PostBBSManager.getInstance().parsecmd(command, activeChar); + } + else if (command.startsWith("_bbstop")) + { + TopBBSManager.getInstance().parsecmd(command, activeChar); + } + else if (command.startsWith("_bbshome")) + { + TopBBSManager.getInstance().parsecmd(command, activeChar); + } + else if (command.startsWith("_bbsloc")) + { + RegionBBSManager.getInstance().parsecmd(command, activeChar); + } + else + { + ShowBoard sb = new ShowBoard("

    the command: " + command + " is not implemented yet


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + break; + } + } + + /** + * @param client + * @param url + * @param arg1 + * @param arg2 + * @param arg3 + * @param arg4 + * @param arg5 + */ + public void handleWriteCommands(L2GameClient client, String url, String arg1, String arg2, String arg3, String arg4, String arg5) + { + L2PcInstance activeChar = client.getActiveChar(); + if (activeChar == null) + { + return; + } + + switch (Config.COMMUNITY_TYPE) + { + case 2: + if (url.equals("Topic")) + { + TopicBBSManager.getInstance().parsewrite(arg1, arg2, arg3, arg4, arg5, activeChar); + } + else if (url.equals("Post")) + { + PostBBSManager.getInstance().parsewrite(arg1, arg2, arg3, arg4, arg5, activeChar); + } + else if (url.equals("Region")) + { + RegionBBSManager.getInstance().parsewrite(arg1, arg2, arg3, arg4, arg5, activeChar); + } + else if (url.equals("Notice")) + { + ClanBBSManager.getInstance().parsewrite(arg1, arg2, arg3, arg4, arg5, activeChar); + } + else + { + ShowBoard sb = new ShowBoard("

    the command: " + url + " is not implemented yet


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + break; + case 1: + RegionBBSManager.getInstance().parsewrite(arg1, arg2, arg3, arg4, arg5, activeChar); + break; + default: + case 0: + ShowBoard sb = new ShowBoard("

    The Community board is currently disabled


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + break; + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final CommunityBoard _instance = new CommunityBoard(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/AdminBBSManager.java b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/AdminBBSManager.java new file mode 100644 index 00000000..b20fbd36 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/AdminBBSManager.java @@ -0,0 +1,77 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.communitybbs.Manager; + +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.serverpackets.ShowBoard; + +public class AdminBBSManager extends BaseBBSManager +{ + /** + * @return + */ + public static AdminBBSManager getInstance() + { + return SingletonHolder._instance; + } + + private AdminBBSManager() + { + } + + /** + * @see com.l2jserver.gameserver.communitybbs.Manager.BaseBBSManager#parsecmd(java.lang.String, com.l2jserver.gameserver.model.actor.instance.L2PcInstance) + */ + @Override + public void parsecmd(String command, L2PcInstance activeChar) + { + if (!activeChar.isGM()) + { + return; + } + if (command.startsWith("admin_bbs")) + { + separateAndSend("

    This Page is only an exemple :)

    command=" + command + "
    ", activeChar); + } + else + { + + ShowBoard sb = new ShowBoard("

    the command: " + command + " is not implemented yet


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + + } + + /** + * @see com.l2jserver.gameserver.communitybbs.Manager.BaseBBSManager#parsewrite(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, com.l2jserver.gameserver.model.actor.instance.L2PcInstance) + */ + @Override + public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar) + { + if (!activeChar.isGM()) + { + return; + } + + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final AdminBBSManager _instance = new AdminBBSManager(); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/BaseBBSManager.java b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/BaseBBSManager.java new file mode 100644 index 00000000..e7495b9a --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/BaseBBSManager.java @@ -0,0 +1,105 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.communitybbs.Manager; + +import java.util.List; + +import javolution.util.FastList; + +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.serverpackets.ShowBoard; + +public abstract class BaseBBSManager +{ + public abstract void parsecmd(String command, L2PcInstance activeChar); + + public abstract void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar); + + protected void separateAndSend(String html, L2PcInstance acha) + { + if (html == null) + { + return; + } + if (html.length() < 4090) + { + acha.sendPacket(new ShowBoard(html, "101")); + acha.sendPacket(new ShowBoard(null, "102")); + acha.sendPacket(new ShowBoard(null, "103")); + + } + else if (html.length() < 8180) + { + acha.sendPacket(new ShowBoard(html.substring(0, 4090), "101")); + acha.sendPacket(new ShowBoard(html.substring(4090, html.length()), "102")); + acha.sendPacket(new ShowBoard(null, "103")); + + } + else if (html.length() < 12270) + { + acha.sendPacket(new ShowBoard(html.substring(0, 4090), "101")); + acha.sendPacket(new ShowBoard(html.substring(4090, 8180), "102")); + acha.sendPacket(new ShowBoard(html.substring(8180, html.length()), "103")); + + } + } + + /** + * @param html + */ + protected void send1001(String html, L2PcInstance acha) + { + if (html.length() < 8180) + { + acha.sendPacket(new ShowBoard(html, "1001")); + } + } + + /** + * @param i + */ + protected void send1002(L2PcInstance acha) + { + send1002(acha, " ", " ", "0"); + } + + /** + * @param activeChar + * @param string + * @param string2 + */ + protected void send1002(L2PcInstance activeChar, String string, String string2, String string3) + { + List _arg = new FastList<>(); + _arg.add("0"); + _arg.add("0"); + _arg.add("0"); + _arg.add("0"); + _arg.add("0"); + _arg.add("0"); + _arg.add(activeChar.getName()); + _arg.add(Integer.toString(activeChar.getObjectId())); + _arg.add(activeChar.getAccountName()); + _arg.add("9"); + _arg.add(string2); + _arg.add(string2); + _arg.add(string); + _arg.add(string3); + _arg.add(string3); + _arg.add("0"); + _arg.add("0"); + activeChar.sendPacket(new ShowBoard(_arg)); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/ClanBBSManager.java b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/ClanBBSManager.java new file mode 100644 index 00000000..10b7d6ef --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/ClanBBSManager.java @@ -0,0 +1,276 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.communitybbs.Manager; + +import java.util.StringTokenizer; + +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.util.StringUtil; + +public class ClanBBSManager extends BaseBBSManager +{ + private ClanBBSManager() + { + } + + /** + * @return + */ + public static ClanBBSManager getInstance() + { + return SingletonHolder._instance; + } + + /** + * @param command + * @param activeChar + */ + @Override + public void parsecmd(String command, L2PcInstance activeChar) + { + if (command.equals("_bbsclan")) + { + if ((activeChar.getClan() == null) || (activeChar.getClan().getLevel() < 2)) + { + clanlist(activeChar, 1); + } + else + { + clanhome(activeChar); + } + } + else if (command.startsWith("_bbsclan_clanlist")) + { + if (command.equals("_bbsclan_clanlist")) + { + clanlist(activeChar, 1); + } + else if (command.startsWith("_bbsclan_clanlist;")) + { + StringTokenizer st = new StringTokenizer(command, ";"); + st.nextToken(); + int index = Integer.parseInt(st.nextToken()); + clanlist(activeChar, index); + } + } + else if (command.startsWith("_bbsclan_clanhome")) + { + if (command.equals("_bbsclan_clanhome")) + { + clanhome(activeChar); + } + else if (command.startsWith("_bbsclan_clanhome;")) + { + StringTokenizer st = new StringTokenizer(command, ";"); + st.nextToken(); + int index = Integer.parseInt(st.nextToken()); + clanhome(activeChar, index); + } + } + else if (command.startsWith("_bbsclan_clannotice_edit;")) + { + clanNotice(activeChar, activeChar.getClan().getClanId()); + } + else if (command.startsWith("_bbsclan_clannotice_enable")) + { + activeChar.getClan().setNoticeEnabled(true); + clanNotice(activeChar, activeChar.getClan().getClanId()); + } + else if (command.startsWith("_bbsclan_clannotice_disable")) + { + activeChar.getClan().setNoticeEnabled(false); + clanNotice(activeChar, activeChar.getClan().getClanId()); + } + else + { + separateAndSend("

    Command : " + command + " needs core development


    ", activeChar); + + } + } + + private void clanNotice(L2PcInstance activeChar, int clanId) + { + L2Clan cl = ClanTable.getInstance().getClan(clanId); + if (cl != null) + { + if (cl.getLevel() < 2) + { + activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_CB_IN_MY_CLAN)); + parsecmd("_bbsclan_clanlist", activeChar); + } + else + { + final StringBuilder html = StringUtil.startAppend(2000, "

    " + "" + "
    " + "HOME > " + " CLAN COMMUNITY > " + " &$802; " + "
    "); + if (activeChar.isClanLeader()) + { + StringUtil.append(html, "

    " + "" + "" + ""); + + if (activeChar.getClan().isNoticeEnabled()) + { + StringUtil.append(html, "" + "
    The Clan Notice function allows the clan leader to send messages through a pop-up window to clan members at login.
    Clan Notice Function:   on   /   off"); + } + else + { + StringUtil.append(html, "
    Clan Notice Function:   on   /   off"); + } + + StringUtil.append(html, "
    " + "" + "

    " + "" + "" + "" + "" + "
    Edit Notice:
    " + "" + "
    " + "
    " + "" + "" + "" + "" + "" + "" + "" + "
    " + "
    " + "" + ""); + send1001(html.toString(), activeChar); + send1002(activeChar, activeChar.getClan().getNotice(), " ", "0"); + } + else + { + StringUtil.append(html, "" + "
    " + "" + "" + "
    You are not your clan's leader, and therefore cannot change the clan notice
    "); + if (activeChar.getClan().isNoticeEnabled()) + { + StringUtil.append(html, "" + "" + "" + "" + "" + "" + "" + "" + "
    The current clan notice:
    " + activeChar.getClan().getNotice() + "
    "); + } + StringUtil.append(html, "
    " + "" + ""); + separateAndSend(html.toString(), activeChar); + } + } + } + } + + /** + * @param activeChar + */ + private void clanlist(L2PcInstance activeChar, int index) + { + if (index < 1) + { + index = 1; + } + + // header + final StringBuilder html = StringUtil.startAppend(2000, "

    " + "" + "" + "
     " + " CLAN COMMUNITY " + "
    " + "" + "" + "" + "" + "" + "" + "" + "" + "
    " + "[GO TO MY CLAN]  " + "
    " + "
    " + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
    CLAN NAMECLAN LEADERCLAN LEVELCLAN MEMBERS
    " + ""); + + int i = 0; + for (L2Clan cl : ClanTable.getInstance().getClans()) + { + if (i > ((index + 1) * 7)) + { + break; + } + + if (i++ >= ((index - 1) * 7)) + { + StringUtil.append(html, "" + "" + " " + "" + "" + "" + "" + "" + "" + "" + "" + "
    ", cl.getName(), "", cl.getLeaderName(), "", String.valueOf(cl.getLevel()), "", String.valueOf(cl.getMembersCount()), "
    " + "" + ""); + } + } + + html.append("" + ""); + + if (index == 1) + { + html.append(""); + } + else + { + StringUtil.append(html, ""); + } + + i = 0; + int nbp; + nbp = ClanTable.getInstance().getClans().length / 8; + if ((nbp * 8) != ClanTable.getInstance().getClans().length) + { + nbp++; + } + for (i = 1; i <= nbp; i++) + { + if (i == index) + { + StringUtil.append(html, ""); + } + else + { + StringUtil.append(html, ""); + } + + } + if (index == nbp) + { + html.append(""); + } + else + { + StringUtil.append(html, ""); + } + html.append("
    ", String.valueOf(i), " ", String.valueOf(i), "
    " + "" + "" + "
    " + "" + + // TODO: search (Write in BBS) + "
    " + "
    " + "
    " + "
    " + "" + ""); + separateAndSend(html.toString(), activeChar); + } + + /** + * @param activeChar + */ + private void clanhome(L2PcInstance activeChar) + { + clanhome(activeChar, activeChar.getClan().getClanId()); + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final ClanBBSManager _instance = new ClanBBSManager(); + } + + /** + * @param activeChar + * @param clanId + */ + private void clanhome(L2PcInstance activeChar, int clanId) + { + L2Clan cl = ClanTable.getInstance().getClan(clanId); + if (cl != null) + { + if (cl.getLevel() < 2) + { + activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_CB_IN_MY_CLAN)); + parsecmd("_bbsclan_clanlist", activeChar); + } + else + { + final String html = StringUtil.concat("


    " + "" + "" + "
     " + "HOME > CLAN COMMUNITY > &$802; " + "
    " + "" + "" + "" + "" + "" + "" + "" + "" + "
    " + "[CLAN ANNOUNCEMENT] [CLAN BULLETIN BOARD]" + "[CLAN MAIL]  " + "[CLAN NOTICE]  " + "
    " + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
    " + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + + // ADMINISTRATOR ?? + /* + * html.append(""); html.append(""); html.append(""); html.append(""); + */ + "" + "" + "" + "" + "" + "
    CLAN NAME", cl.getName(), "
    CLAN LEVEL", String.valueOf(cl.getLevel()), "
    CLAN MEMBERS", String.valueOf(cl.getMembersCount()), "
    CLAN LEADER", cl.getLeaderName(), "
    ADMINISTRATOR"+cl.getLeaderName()+"
    ALLIANCE", (cl.getAllyName() != null) ? cl.getAllyName() : "", "
    " + "
    " + + // TODO: the BB for clan :) + // html.append(""); + "" + "" + "
    " + "" + "

    " + "" + ""); + separateAndSend(html, activeChar); + } + } + } + + @Override + public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar) + { + if (ar1.equals("Set")) + { + activeChar.getClan().setNotice(ar4); + parsecmd("_bbsclan_clanhome;" + activeChar.getClan().getClanId(), activeChar); + } + } + +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/ForumsBBSManager.java b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/ForumsBBSManager.java new file mode 100644 index 00000000..108a3735 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/ForumsBBSManager.java @@ -0,0 +1,165 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.communitybbs.Manager; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; + +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.communitybbs.BB.Forum; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +public class ForumsBBSManager extends BaseBBSManager +{ + private static Logger _log = Logger.getLogger(ForumsBBSManager.class.getName()); + private final List _table; + private int _lastid = 1; + + /** + * @return + */ + public static ForumsBBSManager getInstance() + { + return SingletonHolder._instance; + } + + private ForumsBBSManager() + { + _table = new FastList<>(); + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT forum_id FROM forums WHERE forum_type=0"); + ResultSet result = statement.executeQuery(); + while (result.next()) + { + int forumId = result.getInt("forum_id"); + Forum f = new Forum(forumId, null); + addForum(f); + } + result.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Data error on Forum (root): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void initRoot() + { + for (Forum f : _table) + { + f.vload(); + } + _log.info("Loaded " + _table.size() + " forums. Last forum id used: " + _lastid); + } + + public void addForum(Forum ff) + { + if (ff == null) + { + return; + } + + _table.add(ff); + + if (ff.getID() > _lastid) + { + _lastid = ff.getID(); + } + } + + @Override + public void parsecmd(String command, L2PcInstance activeChar) + { + } + + /** + * @param string + * @return + */ + public Forum getForumByName(String Name) + { + for (Forum f : _table) + { + if (f.getName().equals(Name)) + { + return f; + } + } + + return null; + } + + /** + * @param name + * @param forumByName + * @return + */ + public Forum createNewForum(String name, Forum parent, int type, int perm, int oid) + { + Forum forum = new Forum(name, parent, type, perm, oid); + forum.insertIntoDb(); + return forum; + } + + /** + * @return + */ + public int getANewID() + { + return ++_lastid; + } + + /** + * @param idf + * @return + */ + public Forum getForumByID(int idf) + { + for (Forum f : _table) + { + if (f.getID() == idf) + { + return f; + } + } + return null; + } + + @Override + public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar) + { + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final ForumsBBSManager _instance = new ForumsBBSManager(); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/PostBBSManager.java b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/PostBBSManager.java new file mode 100644 index 00000000..66063dc7 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/PostBBSManager.java @@ -0,0 +1,282 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.communitybbs.Manager; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import java.util.StringTokenizer; + +import javolution.util.FastMap; + +import com.l2jserver.gameserver.communitybbs.BB.Forum; +import com.l2jserver.gameserver.communitybbs.BB.Post; +import com.l2jserver.gameserver.communitybbs.BB.Post.CPost; +import com.l2jserver.gameserver.communitybbs.BB.Topic; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.serverpackets.ShowBoard; +import com.l2jserver.util.StringUtil; + +public class PostBBSManager extends BaseBBSManager +{ + + private final Map _postByTopic; + + public static PostBBSManager getInstance() + { + return SingletonHolder._instance; + } + + private PostBBSManager() + { + _postByTopic = new FastMap<>(); + } + + public Post getGPosttByTopic(Topic t) + { + Post post = null; + post = _postByTopic.get(t); + if (post == null) + { + post = load(t); + _postByTopic.put(t, post); + } + return post; + } + + /** + * @param t + */ + public void delPostByTopic(Topic t) + { + _postByTopic.remove(t); + } + + public void addPostByTopic(Post p, Topic t) + { + if (_postByTopic.get(t) == null) + { + _postByTopic.put(t, p); + } + } + + /** + * @param t + * @return + */ + private Post load(Topic t) + { + Post p; + p = new Post(t); + return p; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.communitybbs.Manager.BaseBBSManager#parsecmd(java.lang.String, com.l2jserver.gameserver.model.actor.instance.L2PcInstance) + */ + @Override + public void parsecmd(String command, L2PcInstance activeChar) + { + if (command.startsWith("_bbsposts;read;")) + { + StringTokenizer st = new StringTokenizer(command, ";"); + st.nextToken(); + st.nextToken(); + int idf = Integer.parseInt(st.nextToken()); + int idp = Integer.parseInt(st.nextToken()); + String index = null; + if (st.hasMoreTokens()) + { + index = st.nextToken(); + } + int ind = 0; + if (index == null) + { + ind = 1; + } + else + { + ind = Integer.parseInt(index); + } + + showPost((TopicBBSManager.getInstance().getTopicByID(idp)), ForumsBBSManager.getInstance().getForumByID(idf), activeChar, ind); + } + else if (command.startsWith("_bbsposts;edit;")) + { + StringTokenizer st = new StringTokenizer(command, ";"); + st.nextToken(); + st.nextToken(); + int idf = Integer.parseInt(st.nextToken()); + int idt = Integer.parseInt(st.nextToken()); + int idp = Integer.parseInt(st.nextToken()); + showEditPost((TopicBBSManager.getInstance().getTopicByID(idt)), ForumsBBSManager.getInstance().getForumByID(idf), activeChar, idp); + } + else + { + ShowBoard sb = new ShowBoard("

    the command: " + command + " is not implemented yet


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + } + + /** + * @param topic + * @param forumByID + * @param activeChar + * @param idp + */ + private void showEditPost(Topic topic, Forum forum, L2PcInstance activeChar, int idp) + { + Post p = getGPosttByTopic(topic); + if ((forum == null) || (topic == null) || (p == null)) + { + ShowBoard sb = new ShowBoard("

    Error, this forum, topic or post does not exit !


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + else + { + showHtmlEditPost(topic, activeChar, forum, p); + } + } + + /** + * @param posttByTopic + * @param forumByID + * @param activeChar + * @param ind + * @param idf + */ + private void showPost(Topic topic, Forum forum, L2PcInstance activeChar, int ind) + { + if ((forum == null) || (topic == null)) + { + ShowBoard sb = new ShowBoard("

    Error, this forum is not implemented yet


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + else if (forum.getType() == Forum.MEMO) + { + showMemoPost(topic, activeChar, forum); + } + else + { + ShowBoard sb = new ShowBoard("

    the forum: " + forum.getName() + " is not implemented yet


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + } + + /** + * @param topic + * @param activeChar + * @param forum + * @param p + */ + private void showHtmlEditPost(Topic topic, L2PcInstance activeChar, Forum forum, Post p) + { + final String html = StringUtil.concat("" + "

    " + "
    " + "
    " + "HOME > ", forum.getName(), " Form" + "
    " + "" + "
    " + "" + "" + "
    " + "" + "" + "" + "" + "" + "" + "" + "
    &$413;", topic.getName(), "
    " + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
    &$427;
    " + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
      
    " + "
    " + "" + ""); + send1001(html, activeChar); + send1002(activeChar, p.getCPost(0).postTxt, topic.getName(), DateFormat.getInstance().format(new Date(topic.getDate()))); + } + + /** + * @param topic + * @param activeChar + * @param forum + */ + private void showMemoPost(Topic topic, L2PcInstance activeChar, Forum forum) + { + // + Post p = getGPosttByTopic(topic); + Locale locale = Locale.getDefault(); + DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL, locale); + + String mes = p.getCPost(0).postTxt.replace(">", ">"); + mes = mes.replace("<", "<"); + mes = mes.replace("\n", ""); + + final String html = StringUtil.concat("

    " + "" + "
    " + "HOME > Memo Form" + "
    " + "" + "
    " + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
    &$413; :  ", topic.getName(), "
    &$417; :  ", topic.getOwnerName() + "&$418; :", dateFormat.format(p.getCPost(0).postDate), "
    " + "
    " + "" + "" + "" + "" + "" + "" + "
    ", mes, "
    " + "
    " + "" + "" + "" + "" + "" + "" + "" + "" + "
    " + "" + " " + " " + " " + "
    " + "
    " + "
    " + "
    " + "
    " + "" + ""); + separateAndSend(html, activeChar); + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.communitybbs.Manager.BaseBBSManager#parsewrite(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, com.l2jserver.gameserver.model.actor.instance.L2PcInstance) + */ + @Override + public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar) + { + + StringTokenizer st = new StringTokenizer(ar1, ";"); + int idf = Integer.parseInt(st.nextToken()); + int idt = Integer.parseInt(st.nextToken()); + int idp = Integer.parseInt(st.nextToken()); + + Forum f = ForumsBBSManager.getInstance().getForumByID(idf); + if (f == null) + { + ShowBoard sb = new ShowBoard("

    the forum: " + idf + " does not exist !


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + else + { + Topic t = f.getTopic(idt); + if (t == null) + { + ShowBoard sb = new ShowBoard("

    the topic: " + idt + " does not exist !


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + else + { + CPost cp = null; + Post p = getGPosttByTopic(t); + if (p != null) + { + cp = p.getCPost(idp); + } + if (cp == null) + { + ShowBoard sb = new ShowBoard("

    the post: " + idp + " does not exist !


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + else + { + p.getCPost(idp).postTxt = ar4; + p.updatetxt(idp); + parsecmd("_bbsposts;read;" + f.getID() + ";" + t.getID(), activeChar); + } + } + } + } + + @SuppressWarnings("synthetic-access") + private static class SingletonHolder + { + protected static final PostBBSManager _instance = new PostBBSManager(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/RegionBBSManager.java b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/RegionBBSManager.java new file mode 100644 index 00000000..f92962de --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/communitybbs/Manager/RegionBBSManager.java @@ -0,0 +1,545 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.communitybbs.Manager; + +import java.util.Collections; +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.GameServer; +import com.l2jserver.gameserver.model.BlockList; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.base.Experience; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.clientpackets.Say2; +import com.l2jserver.gameserver.network.serverpackets.CreatureSay; +import com.l2jserver.gameserver.network.serverpackets.ShowBoard; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.util.StringUtil; + +public class RegionBBSManager extends BaseBBSManager +{ + private static Logger _logChat = Logger.getLogger("chat"); + + private RegionBBSManager() + { + } + + /** + * @see com.l2jserver.gameserver.communitybbs.Manager.BaseBBSManager#parsecmd(java.lang.String, com.l2jserver.gameserver.model.actor.instance.L2PcInstance) + */ + @Override + public void parsecmd(String command, L2PcInstance activeChar) + { + if (command.equals("_bbsloc")) + { + showOldCommunity(activeChar, 1); + } + else if (command.startsWith("_bbsloc;page;")) + { + StringTokenizer st = new StringTokenizer(command, ";"); + st.nextToken(); + st.nextToken(); + int page = 0; + try + { + page = Integer.parseInt(st.nextToken()); + } + catch (NumberFormatException nfe) + { + } + + showOldCommunity(activeChar, page); + } + else if (command.startsWith("_bbsloc;playerinfo;")) + { + StringTokenizer st = new StringTokenizer(command, ";"); + st.nextToken(); + st.nextToken(); + String name = st.nextToken(); + + showOldCommunityPI(activeChar, name); + } + else + { + if (Config.COMMUNITY_TYPE == 1) + { + showOldCommunity(activeChar, 1); + } + else + { + ShowBoard sb = new ShowBoard("

    the command: " + command + " is not implemented yet


    ", "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + } + } + + /** + * @param activeChar + * @param name + */ + private void showOldCommunityPI(L2PcInstance activeChar, String name) + { + final StringBuilder htmlCode = StringUtil.startAppend(1000, "
    " + "
    L2J Community Board
    "); + L2PcInstance player = L2World.getInstance().getPlayer(name); + + if (player != null) + { + String sex = "Male"; + if (player.getAppearance().getSex()) + { + sex = "Female"; + } + String levelApprox = "low"; + if (player.getLevel() >= 60) + { + levelApprox = "very high"; + } + else if (player.getLevel() >= 40) + { + levelApprox = "high"; + } + else if (player.getLevel() >= 20) + { + levelApprox = "medium"; + } + + StringUtil.append(htmlCode, "" + "" + ""); + + if ((activeChar != null) && (activeChar.isGM() || (player.getObjectId() == activeChar.getObjectId()) || Config.SHOW_LEVEL_COMMUNITYBOARD)) + { + long nextLevelExp = 0; + long nextLevelExpNeeded = 0; + if (player.getLevel() < (Experience.MAX_LEVEL - 1)) + { + nextLevelExp = Experience.LEVEL[player.getLevel() + 1]; + nextLevelExpNeeded = nextLevelExp - player.getExp(); + } + + StringUtil.append(htmlCode, "" + "" + "" + ""); + } + + int uptime = (int) player.getUptime() / 1000; + int h = uptime / 3600; + int m = (uptime - (h * 3600)) / 60; + int s = ((uptime - (h * 3600)) - (m * 60)); + + StringUtil.append(htmlCode, "" + ""); + + if (player.getClan() != null) + { + StringUtil.append(htmlCode, "" + ""); + } + + StringUtil.append(htmlCode, "
    ", player.getName(), " (", sex, " ", player.getTemplate().className, "):
    Level: ", levelApprox, "

    Level: ", String.valueOf(player.getLevel()), "
    Experience: ", String.valueOf(player.getExp()), "/", String.valueOf(nextLevelExp), "
    Experience needed for level up: ", String.valueOf(nextLevelExpNeeded), "

    Uptime: ", String.valueOf(h), "h ", String.valueOf(m), "m ", String.valueOf(s), "s

    Clan: ", player.getClan().getName(), "


    " + "
    " + ""); + separateAndSend(htmlCode.toString(), activeChar); + } + else + { + ShowBoard sb = new ShowBoard(StringUtil.concat("

    No player with name ", name, "


    "), "101"); + activeChar.sendPacket(sb); + activeChar.sendPacket(new ShowBoard(null, "102")); + activeChar.sendPacket(new ShowBoard(null, "103")); + } + } + + /** + * @param activeChar + */ + private void showOldCommunity(L2PcInstance activeChar, int page) + { + separateAndSend(getCommunityPage(page, activeChar.isGM() ? "gm" : "pl"), activeChar); + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.communitybbs.Manager.BaseBBSManager#parsewrite(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, com.l2jserver.gameserver.model.actor.instance.L2PcInstance) + */ + @Override + public void parsewrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar) + { + if (activeChar == null) + { + return; + } + + if (ar1.equals("PM")) + { + final StringBuilder htmlCode = StringUtil.startAppend(500, "
    " + "
    L2J Community Board
    "); + + try + { + + L2PcInstance receiver = L2World.getInstance().getPlayer(ar2); + if (receiver == null) + { + StringUtil.append(htmlCode, "Player not found!
    "); + separateAndSend(htmlCode.toString(), activeChar); + return; + } + if (Config.JAIL_DISABLE_CHAT && receiver.isInJail()) + { + activeChar.sendMessage("Player is in jail."); + return; + } + if (receiver.isChatBanned()) + { + activeChar.sendMessage("Player is chat banned."); + return; + } + if (activeChar.isInJail() && Config.JAIL_DISABLE_CHAT) + { + activeChar.sendMessage("You can not chat while in jail."); + return; + } + if (activeChar.isChatBanned()) + { + activeChar.sendMessage("You are banned from using chat"); + return; + } + + if (Config.LOG_CHAT) + { + LogRecord record = new LogRecord(Level.INFO, ar3); + record.setLoggerName("chat"); + record.setParameters(new Object[] + { + "TELL", + "[" + activeChar.getName() + " to " + receiver.getName() + "]" + }); + _logChat.log(record); + } + CreatureSay cs = new CreatureSay(activeChar.getObjectId(), Say2.TELL, activeChar.getName(), ar3); + if (!receiver.isSilenceMode() && !BlockList.isBlocked(receiver, activeChar)) + { + receiver.sendPacket(cs); + activeChar.sendPacket(new CreatureSay(activeChar.getObjectId(), Say2.TELL, "->" + receiver.getName(), ar3)); + StringUtil.append(htmlCode, "Message Sent
    " + ""); + sendHtmlMessage(player, msg); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-tax.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%tax%", String.valueOf(getCastle().getTaxPercent())); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("manage_functions")) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-manage.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("products")) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-products.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcId%", String.valueOf(getNpcId())); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("functions")) + { + if (val.equalsIgnoreCase("tele")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if (getCastle().getFunction(Castle.FUNC_TELEPORT) == null) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-nac.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/" + getNpcId() + "-t" + getCastle().getFunction(Castle.FUNC_TELEPORT).getLvl() + ".htm"); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("support")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if (getCastle().getFunction(Castle.FUNC_SUPPORT) == null) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-nac.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/support" + getCastle().getFunction(Castle.FUNC_SUPPORT).getLvl() + ".htm"); + html.replace("%mp%", String.valueOf((int) getCurrentMp())); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("back")) + { + showChatWindow(player); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-functions.htm"); + if (getCastle().getFunction(Castle.FUNC_RESTORE_EXP) != null) + { + html.replace("%xp_regen%", String.valueOf(getCastle().getFunction(Castle.FUNC_RESTORE_EXP).getLvl())); + } + else + { + html.replace("%xp_regen%", "0"); + } + if (getCastle().getFunction(Castle.FUNC_RESTORE_HP) != null) + { + html.replace("%hp_regen%", String.valueOf(getCastle().getFunction(Castle.FUNC_RESTORE_HP).getLvl())); + } + else + { + html.replace("%hp_regen%", "0"); + } + if (getCastle().getFunction(Castle.FUNC_RESTORE_MP) != null) + { + html.replace("%mp_regen%", String.valueOf(getCastle().getFunction(Castle.FUNC_RESTORE_MP).getLvl())); + } + else + { + html.replace("%mp_regen%", "0"); + } + sendHtmlMessage(player, html); + } + return; + } + else if (actualCommand.equalsIgnoreCase("manage")) + { + if ((player.getClanPrivileges() & L2Clan.CP_CS_SET_FUNCTIONS) == L2Clan.CP_CS_SET_FUNCTIONS) + { + if (val.equalsIgnoreCase("recovery")) + { + if (st.countTokens() >= 1) + { + if (getCastle().getOwnerId() == 0) + { + player.sendMessage("This castle have no owner, you cannot change configuration"); + return; + } + val = st.nextToken(); + if (val.equalsIgnoreCase("hp_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-cancel.htm"); + html.replace("%apply%", "recovery hp 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("mp_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-cancel.htm"); + html.replace("%apply%", "recovery mp 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("exp_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-cancel.htm"); + html.replace("%apply%", "recovery exp 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_hp")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-apply.htm"); + html.replace("%name%", "Fireplace (HP Recovery Device)"); + int percent = Integer.parseInt(val); + int cost; + switch (percent) + { + case 80: + cost = Config.CS_HPREG1_FEE; + break; + case 120: + cost = Config.CS_HPREG2_FEE; + break; + case 180: + cost = Config.CS_HPREG3_FEE; + break; + case 240: + cost = Config.CS_HPREG4_FEE; + break; + default: // 300 + cost = Config.CS_HPREG5_FEE; + break; + } + + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CS_HPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Provides additional HP recovery for clan members in the castle." + String.valueOf(percent) + "%"); + html.replace("%apply%", "recovery hp " + String.valueOf(percent)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_mp")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-apply.htm"); + html.replace("%name%", "Carpet (MP Recovery)"); + int percent = Integer.parseInt(val); + int cost; + switch (percent) + { + case 5: + cost = Config.CS_MPREG1_FEE; + break; + case 15: + cost = Config.CS_MPREG2_FEE; + break; + case 30: + cost = Config.CS_MPREG3_FEE; + break; + default: // 40 + cost = Config.CS_MPREG4_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CS_MPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Provides additional MP recovery for clan members in the castle." + String.valueOf(percent) + "%"); + html.replace("%apply%", "recovery mp " + String.valueOf(percent)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_exp")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-apply.htm"); + html.replace("%name%", "Chandelier (EXP Recovery Device)"); + int percent = Integer.parseInt(val); + int cost; + switch (percent) + { + case 15: + cost = Config.CS_EXPREG1_FEE; + break; + case 25: + cost = Config.CS_EXPREG2_FEE; + break; + case 35: + cost = Config.CS_EXPREG3_FEE; + break; + default: // 50 + cost = Config.CS_EXPREG4_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CS_EXPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Restores the Exp of any clan member who is resurrected in the castle." + String.valueOf(percent) + "%"); + html.replace("%apply%", "recovery exp " + String.valueOf(percent)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("hp")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Hp editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-apply_confirmed.htm"); + if (getCastle().getFunction(Castle.FUNC_RESTORE_HP) != null) + { + if (getCastle().getFunction(Castle.FUNC_RESTORE_HP).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-used.htm"); + html.replace("%val%", String.valueOf(val) + "%"); + sendHtmlMessage(player, html); + return; + } + } + int percent = Integer.parseInt(val); + switch (percent) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-cancel_confirmed.htm"); + break; + case 80: + fee = Config.CS_HPREG1_FEE; + break; + case 120: + fee = Config.CS_HPREG2_FEE; + break; + case 180: + fee = Config.CS_HPREG3_FEE; + break; + case 240: + fee = Config.CS_HPREG4_FEE; + break; + default: // 300 + fee = Config.CS_HPREG5_FEE; + break; + } + if (!getCastle().updateFunctions(player, Castle.FUNC_RESTORE_HP, percent, fee, Config.CS_HPREG_FEE_RATIO, (getCastle().getFunction(Castle.FUNC_RESTORE_HP) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/low_adena.htm"); + sendHtmlMessage(player, html); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("mp")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Mp editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-apply_confirmed.htm"); + if (getCastle().getFunction(Castle.FUNC_RESTORE_MP) != null) + { + if (getCastle().getFunction(Castle.FUNC_RESTORE_MP).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-used.htm"); + html.replace("%val%", String.valueOf(val) + "%"); + sendHtmlMessage(player, html); + return; + } + } + int percent = Integer.parseInt(val); + switch (percent) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-cancel_confirmed.htm"); + break; + case 5: + fee = Config.CS_MPREG1_FEE; + break; + case 15: + fee = Config.CS_MPREG2_FEE; + break; + case 30: + fee = Config.CS_MPREG3_FEE; + break; + default: // 40 + fee = Config.CS_MPREG4_FEE; + break; + } + if (!getCastle().updateFunctions(player, Castle.FUNC_RESTORE_MP, percent, fee, Config.CS_MPREG_FEE_RATIO, (getCastle().getFunction(Castle.FUNC_RESTORE_MP) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/low_adena.htm"); + sendHtmlMessage(player, html); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("exp")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Exp editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-apply_confirmed.htm"); + if (getCastle().getFunction(Castle.FUNC_RESTORE_EXP) != null) + { + if (getCastle().getFunction(Castle.FUNC_RESTORE_EXP).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-used.htm"); + html.replace("%val%", String.valueOf(val) + "%"); + sendHtmlMessage(player, html); + return; + } + } + int percent = Integer.parseInt(val); + switch (percent) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-cancel_confirmed.htm"); + break; + case 15: + fee = Config.CS_EXPREG1_FEE; + break; + case 25: + fee = Config.CS_EXPREG2_FEE; + break; + case 35: + fee = Config.CS_EXPREG3_FEE; + break; + default: // 50 + fee = Config.CS_EXPREG4_FEE; + break; + } + if (!getCastle().updateFunctions(player, Castle.FUNC_RESTORE_EXP, percent, fee, Config.CS_EXPREG_FEE_RATIO, (getCastle().getFunction(Castle.FUNC_RESTORE_EXP) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/low_adena.htm"); + sendHtmlMessage(player, html); + } + sendHtmlMessage(player, html); + } + return; + } + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/edit_recovery.htm"); + String hp = "[80%][120%][180%][240%][300%]"; + String exp = "[15%][25%][35%][50%]"; + String mp = "[5%][15%][30%][40%]"; + if (getCastle().getFunction(Castle.FUNC_RESTORE_HP) != null) + { + html.replace("%hp_recovery%", String.valueOf(getCastle().getFunction(Castle.FUNC_RESTORE_HP).getLvl()) + "% (" + String.valueOf(getCastle().getFunction(Castle.FUNC_RESTORE_HP).getLease()) + "Adena /" + String.valueOf(Config.CS_HPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%hp_period%", "Withdraw the fee for the next time at " + format.format(getCastle().getFunction(Castle.FUNC_RESTORE_HP).getEndTime())); + html.replace("%change_hp%", "[Deactivate]" + hp); + } + else + { + html.replace("%hp_recovery%", "none"); + html.replace("%hp_period%", "none"); + html.replace("%change_hp%", hp); + } + if (getCastle().getFunction(Castle.FUNC_RESTORE_EXP) != null) + { + html.replace("%exp_recovery%", String.valueOf(getCastle().getFunction(Castle.FUNC_RESTORE_EXP).getLvl()) + "% (" + String.valueOf(getCastle().getFunction(Castle.FUNC_RESTORE_EXP).getLease()) + "Adena /" + String.valueOf(Config.CS_EXPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%exp_period%", "Withdraw the fee for the next time at " + format.format(getCastle().getFunction(Castle.FUNC_RESTORE_EXP).getEndTime())); + html.replace("%change_exp%", "[Deactivate]" + exp); + } + else + { + html.replace("%exp_recovery%", "none"); + html.replace("%exp_period%", "none"); + html.replace("%change_exp%", exp); + } + if (getCastle().getFunction(Castle.FUNC_RESTORE_MP) != null) + { + html.replace("%mp_recovery%", String.valueOf(getCastle().getFunction(Castle.FUNC_RESTORE_MP).getLvl()) + "% (" + String.valueOf(getCastle().getFunction(Castle.FUNC_RESTORE_MP).getLease()) + "Adena /" + String.valueOf(Config.CS_MPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%mp_period%", "Withdraw the fee for the next time at " + format.format(getCastle().getFunction(Castle.FUNC_RESTORE_MP).getEndTime())); + html.replace("%change_mp%", "[Deactivate]" + mp); + } + else + { + html.replace("%mp_recovery%", "none"); + html.replace("%mp_period%", "none"); + html.replace("%change_mp%", mp); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("other")) + { + if (st.countTokens() >= 1) + { + if (getCastle().getOwnerId() == 0) + { + player.sendMessage("This castle have no owner, you cannot change configuration"); + return; + } + val = st.nextToken(); + if (val.equalsIgnoreCase("tele_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-cancel.htm"); + html.replace("%apply%", "other tele 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("support_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-cancel.htm"); + html.replace("%apply%", "other support 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_support")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-apply.htm"); + html.replace("%name%", "Insignia (Supplementary Magic)"); + int stage = Integer.parseInt(val); + int cost; + switch (stage) + { + case 1: + cost = Config.CS_SUPPORT1_FEE; + break; + case 2: + cost = Config.CS_SUPPORT2_FEE; + break; + case 3: + cost = Config.CS_SUPPORT3_FEE; + break; + default: + cost = Config.CS_SUPPORT4_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CS_SUPPORT_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Enables the use of supplementary magic."); + html.replace("%apply%", "other support " + String.valueOf(stage)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_tele")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-apply.htm"); + html.replace("%name%", "Mirror (Teleportation Device)"); + int stage = Integer.parseInt(val); + int cost; + switch (stage) + { + case 1: + cost = Config.CS_TELE1_FEE; + break; + default: + cost = Config.CS_TELE2_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CS_TELE_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Teleports clan members in a castle to the target Stage " + String.valueOf(stage) + " staging area"); + html.replace("%apply%", "other tele " + String.valueOf(stage)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("tele")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Tele editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-apply_confirmed.htm"); + if (getCastle().getFunction(Castle.FUNC_TELEPORT) != null) + { + if (getCastle().getFunction(Castle.FUNC_TELEPORT).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-used.htm"); + html.replace("%val%", "Stage " + String.valueOf(val)); + sendHtmlMessage(player, html); + return; + } + } + int lvl = Integer.parseInt(val); + switch (lvl) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-cancel_confirmed.htm"); + break; + case 1: + fee = Config.CS_TELE1_FEE; + break; + default: + fee = Config.CS_TELE2_FEE; + break; + } + if (!getCastle().updateFunctions(player, Castle.FUNC_TELEPORT, lvl, fee, Config.CS_TELE_FEE_RATIO, (getCastle().getFunction(Castle.FUNC_TELEPORT) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/low_adena.htm"); + sendHtmlMessage(player, html); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("support")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Support editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-apply_confirmed.htm"); + if (getCastle().getFunction(Castle.FUNC_SUPPORT) != null) + { + if (getCastle().getFunction(Castle.FUNC_SUPPORT).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-used.htm"); + html.replace("%val%", "Stage " + String.valueOf(val)); + sendHtmlMessage(player, html); + return; + } + } + int lvl = Integer.parseInt(val); + switch (lvl) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/functions-cancel_confirmed.htm"); + break; + case 1: + fee = Config.CS_SUPPORT1_FEE; + break; + case 2: + fee = Config.CS_SUPPORT2_FEE; + break; + case 3: + fee = Config.CS_SUPPORT3_FEE; + break; + default: + fee = Config.CS_SUPPORT4_FEE; + break; + } + if (!getCastle().updateFunctions(player, Castle.FUNC_SUPPORT, lvl, fee, Config.CS_SUPPORT_FEE_RATIO, (getCastle().getFunction(Castle.FUNC_SUPPORT) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/low_adena.htm"); + sendHtmlMessage(player, html); + } + else + { + sendHtmlMessage(player, html); + } + } + return; + } + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/edit_other.htm"); + String tele = "[Level 1][Level 2]"; + String support = "[Level 1][Level 2][Level 3][Level 4]"; + if (getCastle().getFunction(Castle.FUNC_TELEPORT) != null) + { + html.replace("%tele%", "Stage " + String.valueOf(getCastle().getFunction(Castle.FUNC_TELEPORT).getLvl()) + " (" + String.valueOf(getCastle().getFunction(Castle.FUNC_TELEPORT).getLease()) + "Adena /" + String.valueOf(Config.CS_TELE_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%tele_period%", "Withdraw the fee for the next time at " + format.format(getCastle().getFunction(Castle.FUNC_TELEPORT).getEndTime())); + html.replace("%change_tele%", "[Deactivate]" + tele); + } + else + { + html.replace("%tele%", "none"); + html.replace("%tele_period%", "none"); + html.replace("%change_tele%", tele); + } + if (getCastle().getFunction(Castle.FUNC_SUPPORT) != null) + { + html.replace("%support%", "Stage " + String.valueOf(getCastle().getFunction(Castle.FUNC_SUPPORT).getLvl()) + " (" + String.valueOf(getCastle().getFunction(Castle.FUNC_SUPPORT).getLease()) + "Adena /" + String.valueOf(Config.CS_SUPPORT_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%support_period%", "Withdraw the fee for the next time at " + format.format(getCastle().getFunction(Castle.FUNC_SUPPORT).getEndTime())); + html.replace("%change_support%", "[Deactivate]" + support); + } + else + { + html.replace("%support%", "none"); + html.replace("%support_period%", "none"); + html.replace("%change_support%", support); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("back")) + { + showChatWindow(player); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/manage.htm"); + sendHtmlMessage(player, html); + } + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-noprivs.htm"); + sendHtmlMessage(player, html); + } + return; + } + else if (actualCommand.equalsIgnoreCase("support")) + { + setTarget(player); + L2Skill skill; + if (val.isEmpty()) + { + return; + } + + try + { + int skill_id = Integer.parseInt(val); + try + { + if (getCastle().getFunction(Castle.FUNC_SUPPORT) == null) + { + return; + } + if (getCastle().getFunction(Castle.FUNC_SUPPORT).getLvl() == 0) + { + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + int skill_lvl = 0; + if (st.countTokens() >= 1) + { + skill_lvl = Integer.parseInt(st.nextToken()); + } + skill = SkillTable.getInstance().getInfo(skill_id, skill_lvl); + if (skill.getSkillType() == L2SkillType.SUMMON) + { + player.doSimultaneousCast(skill); + } + else + { + if (!((skill.getMpConsume() + skill.getMpInitialConsume()) > getCurrentMp())) + { + this.doCast(skill); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/support-no_mana.htm"); + html.replace("%mp%", String.valueOf((int) getCurrentMp())); + sendHtmlMessage(player, html); + return; + } + } + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/support-done.htm"); + html.replace("%mp%", String.valueOf((int) getCurrentMp())); + sendHtmlMessage(player, html); + } + catch (Exception e) + { + player.sendMessage("Invalid skill level, contact your admin!"); + } + } + catch (Exception e) + { + player.sendMessage("Invalid skill level, contact your admin!"); + } + return; + } + else if (actualCommand.equalsIgnoreCase("support_back")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if (getCastle().getFunction(Castle.FUNC_SUPPORT).getLvl() == 0) + { + return; + } + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/support" + getCastle().getFunction(Castle.FUNC_SUPPORT).getLvl() + ".htm"); + html.replace("%mp%", String.valueOf((int) getStatus().getCurrentMp())); + sendHtmlMessage(player, html); + return; + } + else if (actualCommand.equalsIgnoreCase("goto")) + { + int whereTo = Integer.parseInt(val); + doTeleport(player, whereTo); + return; + } + else if (actualCommand.equalsIgnoreCase("siege_change")) // siege day set + { + if (Config.CL_SET_SIEGE_TIME_LIST.isEmpty()) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-noadmin.htm"); + sendHtmlMessage(player, html); + } + else if (player.isClanLeader()) + { + if (getCastle().getSiege().getTimeRegistrationOverDate().getTimeInMillis() < Calendar.getInstance().getTimeInMillis()) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/siegetime1.htm"); + sendHtmlMessage(player, html); + } + else if (getCastle().getSiege().getIsTimeRegistrationOver()) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/siegetime2.htm"); + sendHtmlMessage(player, html); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/siegetime3.htm"); + html.replace("%time%", String.valueOf(getCastle().getSiegeDate().getTime())); + sendHtmlMessage(player, html); + } + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-noprivs.htm"); + sendHtmlMessage(player, html); + } + return; + } + else if (actualCommand.equalsIgnoreCase("siege_time_set")) // set preDay + { + boolean isAfternoon = Config.SIEGE_HOUR_LIST_MORNING.isEmpty(); + switch (Integer.parseInt(val)) + { + case 0: + case 4: + break; + case 1: + _preDay = Integer.parseInt(st.nextToken()); + break; + case 2: + isAfternoon = Boolean.parseBoolean(st.nextToken()); + break; + case 3: + _preHour = Integer.parseInt(st.nextToken()); + break; + default: + break; + } + NpcHtmlMessage html = getNextSiegeTimePage(player.getHtmlPrefix(), Integer.parseInt(val), isAfternoon); + + if (html == null) + { + if (Config.CL_SET_SIEGE_TIME_LIST.contains("day")) + { + getCastle().getSiegeDate().set(Calendar.DAY_OF_WEEK, _preDay); + } + else + { + getCastle().getSiegeDate().set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); + } + if (Config.CL_SET_SIEGE_TIME_LIST.contains("hour")) + { + getCastle().getSiegeDate().set(Calendar.HOUR_OF_DAY, _preHour); + } + if (Config.CL_SET_SIEGE_TIME_LIST.contains("minute")) + { + getCastle().getSiegeDate().set(Calendar.MINUTE, Integer.parseInt(st.nextToken())); + } + // now store the changed time and finished next Siege Time registration + getCastle().getSiege().endTimeRegistration(false); + + html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/siegetime8.htm"); + html.replace("%time%", String.valueOf(getCastle().getSiegeDate().getTime())); + } + sendHtmlMessage(player, html); + return; + } + else if (actualCommand.equals("give_crown")) + { + if (siegeBlocksFunction(player)) + { + return; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + + if (player.isClanLeader()) + { + if (player.getInventory().getItemByItemId(6841) == null) + { + L2ItemInstance crown = player.getInventory().addItem("Castle Crown", 6841, 1, player, this); + + SystemMessage ms = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1); + ms.addItemName(crown); + player.sendPacket(ms); + + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-gavecrown.htm"); + html.replace("%CharName%", String.valueOf(player.getName())); + html.replace("%FeudName%", String.valueOf(getCastle().getName())); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-hascrown.htm"); + } + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-noprivs.htm"); + } + + player.sendPacket(html); + return; + } + else + { + super.onBypassFeedback(player, command); + } + } + } + + private NpcHtmlMessage getNextSiegeTimePage(String htmlPrefix, int now, boolean isAfternoon) + { + NpcHtmlMessage ret = new NpcHtmlMessage(1); + if ((now == 0) && Config.CL_SET_SIEGE_TIME_LIST.contains("day")) + { + ret.setFile(htmlPrefix, "data/html/chamberlain/siegetime4.htm"); + return ret; + } + if ((now < 3) && Config.CL_SET_SIEGE_TIME_LIST.contains("hour")) + { + switch (now) + { + case 0: + case 1: + if (!Config.SIEGE_HOUR_LIST_MORNING.isEmpty() && !Config.SIEGE_HOUR_LIST_AFTERNOON.isEmpty()) + { + ret.setFile(htmlPrefix, "data/html/chamberlain/siegetime5.htm"); + return ret; + } + case 2: + ret.setFile(htmlPrefix, "data/html/chamberlain/siegetime6.htm"); + List list; + int inc = 0; + String ampm = ""; + + if (!isAfternoon) + { + if (Config.SIEGE_HOUR_LIST_AFTERNOON.isEmpty()) + { + ampm = "AM"; + } + list = Config.SIEGE_HOUR_LIST_MORNING; + } + else + { + if (Config.SIEGE_HOUR_LIST_MORNING.isEmpty()) + { + ampm = "PM"; + } + inc = 12; + list = Config.SIEGE_HOUR_LIST_AFTERNOON; + } + + final StringBuilder tList = new StringBuilder(list.size() * 50); + for (Integer hour : list) + { + if (hour == 0) + { + StringUtil.append(tList, "", String.valueOf(hour + 12), ":00 ", ampm, "
    "); + } + else + { + StringUtil.append(tList, "", String.valueOf(hour), ":00 ", ampm, "
    "); + } + } + ret.replace("%links%", tList.toString()); + } + return ret; + } + if ((now < 4) && Config.CL_SET_SIEGE_TIME_LIST.contains("minute")) + { + ret.setFile(htmlPrefix, "data/html/chamberlain/siegetime7.htm"); + return ret; + } + + return null; + } + + private void sendHtmlMessage(L2PcInstance player, String htmlMessage) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setHtml(htmlMessage); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + String filename = "data/html/chamberlain/chamberlain-no.htm"; + + int condition = validateCondition(player); + if (condition > COND_ALL_FALSE) + { + if (condition == COND_BUSY_BECAUSE_OF_SIEGE) + { + filename = "data/html/chamberlain/chamberlain-busy.htm"; // Busy because of siege + } + else if (condition == COND_OWNER) + { + filename = "data/html/chamberlain/chamberlain.htm"; // Owner message window + } + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } + + private void doTeleport(L2PcInstance player, int val) + { + if (Config.DEBUG) + { + _log.warning("doTeleport(L2PcInstance player, int val) is called"); + } + L2TeleportLocation list = TeleportLocationTable.getInstance().getTemplate(val); + if (list != null) + { + if (player.destroyItemByItemId("Teleport", list.getItemId(), list.getPrice(), this, true)) + { + if (Config.DEBUG) + { + _log.warning("Teleporting player " + player.getName() + " for Castle to new location: " + list.getLocX() + ":" + list.getLocY() + ":" + list.getLocZ()); + } + player.teleToLocation(list.getLocX(), list.getLocY(), list.getLocZ()); + } + } + else + { + _log.warning("No teleport destination with id:" + val); + } + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + protected int validateCondition(L2PcInstance player) + { + if ((getCastle() != null) && (getCastle().getCastleId() > 0)) + { + if (player.getClan() != null) + { + if (getCastle().getZone().isActive()) + { + return COND_BUSY_BECAUSE_OF_SIEGE; // Busy because of siege + } + else if (getCastle().getOwnerId() == player.getClanId()) + { + return COND_OWNER; // Owner + } + } + } + return COND_ALL_FALSE; + } + + private boolean validatePrivileges(L2PcInstance player, int privilege) + { + if ((player.getClanPrivileges() & privilege) != privilege) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-noprivs.htm"); + player.sendPacket(html); + return false; + } + return true; + } + + private boolean siegeBlocksFunction(L2PcInstance player) + { + if (getCastle().getSiege().getIsInProgress()) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/chamberlain/chamberlain-busy.htm"); + html.replace("%npcname%", String.valueOf(getName())); + player.sendPacket(html); + return true; + } + return false; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleDoormenInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleDoormenInstance.java new file mode 100644 index 00000000..bd0844dd --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleDoormenInstance.java @@ -0,0 +1,73 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; + +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2CastleDoormenInstance extends L2DoormenInstance +{ + public L2CastleDoormenInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2CastleDoormenInstance); + } + + @Override + protected final void openDoors(L2PcInstance player, String command) + { + StringTokenizer st = new StringTokenizer(command.substring(10), ", "); + st.nextToken(); + + while (st.hasMoreTokens()) + { + getCastle().openDoor(player, Integer.parseInt(st.nextToken())); + } + } + + @Override + protected final void closeDoors(L2PcInstance player, String command) + { + StringTokenizer st = new StringTokenizer(command.substring(11), ", "); + st.nextToken(); + + while (st.hasMoreTokens()) + { + getCastle().closeDoor(player, Integer.parseInt(st.nextToken())); + } + } + + @Override + protected final boolean isOwnerClan(L2PcInstance player) + { + if ((player.getClan() != null) && (getCastle() != null)) + { + // player should have privileges to open doors + if ((player.getClanId() == getCastle().getOwnerId()) && ((player.getClanPrivileges() & L2Clan.CP_CS_OPEN_DOOR) == L2Clan.CP_CS_OPEN_DOOR)) + { + return true; + } + } + return false; + } + + @Override + protected final boolean isUnderSiege() + { + return getCastle().getZone().isActive(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleMagicianInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleMagicianInstance.java new file mode 100644 index 00000000..6542bd35 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleMagicianInstance.java @@ -0,0 +1,584 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.SevenSigns; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.instancemanager.InstanceManager; +import com.l2jserver.gameserver.model.L2SkillLearn; +import com.l2jserver.gameserver.model.L2SquadTrainer; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.entity.TvTEvent; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.templates.skills.L2EffectType; +import com.l2jserver.util.Rnd; + +/** + * @author Kerberos | ZaKaX + */ +public class L2CastleMagicianInstance extends L2NpcInstance implements L2SquadTrainer +{ + protected static final int COND_ALL_FALSE = 0; + protected static final int COND_BUSY_BECAUSE_OF_SIEGE = 1; + protected static final int COND_OWNER = 2; + + /** + * @param objectId + * @param template + */ + public L2CastleMagicianInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2CastleMagicianInstance); + } + + @Override + public void showChatWindow(L2PcInstance player, int val) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + String filename = "data/html/castlemagician/magician-no.htm"; + + int condition = validateCondition(player); + if (condition > COND_ALL_FALSE) + { + if (condition == COND_BUSY_BECAUSE_OF_SIEGE) + { + filename = "data/html/castlemagician/magician-busy.htm"; // Busy because of siege + } + else if (condition == COND_OWNER) // Clan owns castle + { + if (val == 0) + { + filename = "data/html/castlemagician/magician.htm"; + } + else + { + filename = "data/html/castlemagician/magician-" + val + ".htm"; + } + } + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if (command.startsWith("Chat")) + { + int val = 0; + try + { + val = Integer.parseInt(command.substring(5)); + } + catch (IndexOutOfBoundsException ioobe) + { + } + catch (NumberFormatException nfe) + { + } + showChatWindow(player, val); + return; + } + else if (command.startsWith("ExchangeKE")) + { + String filename = null; + int i1; + int item; + int i0 = Rnd.get(100); + if (i0 < 5) + { + i1 = Rnd.get(25); + if (i0 < 5) + { + item = 9931; + } + else if (i1 <= 50) + { + item = 9932; + } + else if (i1 <= 75) + { + item = 10416; + } + else + { + item = 10417; + } + } + else if (i0 <= 15) + { + i1 = Rnd.get(100); + if (i1 <= 20) + { + item = 9917; + } + else if (i1 <= 40) + { + item = 9918; + } + else if (i1 <= 60) + { + item = 9928; + } + else if (i1 <= 80) + { + item = 9929; + } + else + { + item = 9920; + } + } + else if (i0 <= 30) + { + i1 = Rnd.get(100); + if (i1 <= 12) + { + item = 9916; + } + else if (i1 <= 25) + { + item = 9916; + } + else if (i1 <= 37) + { + item = 9924; + } + else if (i1 <= 50) + { + item = 9925; + } + else if (i1 <= 62) + { + item = 9926; + } + else if (i1 <= 75) + { + item = 9927; + } + else if (i1 <= 87) + { + item = 10518; + } + else + { + item = 10424; + } + } + else + { + i1 = Rnd.get(46); + if (i1 == 0) + { + item = 9914; + } + else if (i1 == 1) + { + item = 9915; + } + else if (i1 == 2) + { + item = 9920; + } + else if (i1 == 3) + { + item = 9920; + } + else if (i1 == 4) + { + item = 9921; + } + else if (i1 == 5) + { + item = 9922; + } + else if (i1 == 6) + { + item = 9933; + } + else if (i1 == 7) + { + item = 9934; + } + else if (i1 == 8) + { + item = 9935; + } + else if (i1 == 9) + { + item = 9936; + } + else if (i1 == 10) + { + item = 9937; + } + else if (i1 == 11) + { + item = 9938; + } + else if (i1 == 12) + { + item = 9939; + } + else if (i1 == 13) + { + item = 9940; + } + else if (i1 == 14) + { + item = 9941; + } + else if (i1 == 15) + { + item = 9942; + } + else if (i1 == 16) + { + item = 9943; + } + else if (i1 == 17) + { + item = 9944; + } + else if (i1 == 18) + { + item = 9945; + } + else if (i1 == 19) + { + item = 9946; + } + else if (i1 == 20) + { + item = 9947; + } + else if (i1 == 21) + { + item = 9948; + } + else if (i1 == 22) + { + item = 9949; + } + else if (i1 == 23) + { + item = 9950; + } + else if (i1 == 25) + { + item = 9952; + } + else if (i1 == 26) + { + item = 9953; + } + else if (i1 == 27) + { + item = 9954; + } + else if (i1 == 28) + { + item = 9955; + } + else if (i1 == 29) + { + item = 9956; + } + else if (i1 == 30) + { + item = 9957; + } + else if (i1 == 31) + { + item = 9958; + } + else if (i1 == 32) + { + item = 9959; + } + else if (i1 == 33) + { + item = 9960; + } + else if (i1 == 34) + { + item = 9961; + } + else if (i1 == 35) + { + item = 9962; + } + else if (i1 == 36) + { + item = 9963; + } + else if (i1 == 37) + { + item = 9964; + } + else if (i1 == 24) + { + item = 9965; + } + else if (i1 == 38) + { + item = 10418; + } + else if (i1 == 39) + { + item = 10420; + } + else if (i1 == 40) + { + item = 10519; + } + else if (i1 == 41) + { + item = 10422; + } + else if (i1 == 42) + { + item = 10423; + } + else if (i1 == 43) + { + item = 10419; + } + else + { + item = 10421; + } + } + + if (player.destroyItemByItemId("ExchangeKE", 9912, 10, this, false)) + { + final SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + msg.addItemName(9912); + msg.addNumber(10); + player.sendPacket(msg); + + player.addItem("ExchangeKE", item, 1, player, true); + + filename = "data/html/castlemagician/magician-KE-Exchange.htm"; + } + else + { + filename = "data/html/castlemagician/magician-no-KE.htm"; + } + + showChatWindow(player, filename); + } + else if (command.equals("gotoleader")) + { + if (player.getClan() != null) + { + final L2PcInstance clanLeader = player.getClan().getLeader().getPlayerInstance(); + if (clanLeader == null) + { + return; + } + + if (clanLeader.getFirstEffect(L2EffectType.CLAN_GATE) != null) + { + if (!validateGateCondition(clanLeader, player)) + { + return; + } + + player.teleToLocation(clanLeader.getX(), clanLeader.getY(), clanLeader.getZ(), false); + return; + } + showChatWindow(player, "data/html/castlemagician/magician-nogate.htm"); + } + } + else if (command.equals("subskills")) + { + if (player.isClanLeader()) + { + final FastList skills = SkillTreesData.getInstance().getAvailableSubPledgeSkills(player.getClan()); + final AcquireSkillList asl = new AcquireSkillList(SkillType.SubPledge); + int count = 0; + + for (L2SkillLearn s : skills) + { + if (SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()) != null) + { + asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), 0); + ++count; + } + } + + if (count == 0) + { + player.sendPacket(SystemMessageId.NO_MORE_SKILLS_TO_LEARN); + } + else + { + player.sendPacket(asl); + } + } + else + { + showChatWindow(player, "data/html/castlemagician/magician-nosquad.htm"); + } + } + else + { + super.onBypassFeedback(player, command); + } + } + + protected int validateCondition(L2PcInstance player) + { + if (player.isGM()) + { + return COND_OWNER; + } + if ((getCastle() != null) && (getCastle().getCastleId() > 0)) + { + if (player.getClan() != null) + { + if (getCastle().getZone().isActive()) + { + return COND_BUSY_BECAUSE_OF_SIEGE; // Busy because of siege + } + else if (getCastle().getOwnerId() == player.getClanId()) + { + return COND_OWNER; + } + } + } + return COND_ALL_FALSE; + } + + private static final boolean validateGateCondition(L2PcInstance clanLeader, L2PcInstance player) + { + if (clanLeader.isAlikeDead()) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + + if (clanLeader.isInStoreMode()) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + + if (clanLeader.isRooted() || clanLeader.isInCombat()) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + + if (clanLeader.isInOlympiadMode()) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + + if (clanLeader.isFestivalParticipant()) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + + if (clanLeader.inObserverMode()) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + + if (clanLeader.isInsideZone(L2Character.ZONE_NOSUMMONFRIEND)) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + + if (clanLeader.getInstanceId() > 0) + { + if (!Config.ALLOW_SUMMON_TO_INSTANCE || InstanceManager.getInstance().getInstance(player.getInstanceId()).isSummonAllowed()) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + } + + if (player.isIn7sDungeon()) + { + final int targetCabal = SevenSigns.getInstance().getPlayerCabal(clanLeader.getObjectId()); + if (SevenSigns.getInstance().isSealValidationPeriod()) + { + if (targetCabal != SevenSigns.getInstance().getCabalHighestScore()) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + } + else + { + if (targetCabal == SevenSigns.CABAL_NULL) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + } + } + + if (!TvTEvent.onEscapeUse(player.getObjectId())) + { + player.sendMessage("You on TvT Event, teleporting disabled."); + return false; + } + + if (!TvTEvent.onEscapeUse(clanLeader.getObjectId())) + { + // TODO: Need retail message if there's one. + player.sendMessage("Couldn't teleport to clan leader. The requirements was not meet."); + return false; + } + return true; + } + + @Override + public void showSubUnitSkillList(L2PcInstance player) + { + onBypassFeedback(player, "subskills"); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleTeleporterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleTeleporterInstance.java new file mode 100644 index 00000000..9e19455d --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleTeleporterInstance.java @@ -0,0 +1,152 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.Collection; +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.MapRegionTable; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.NpcSay; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author Kerberos + */ +public final class L2CastleTeleporterInstance extends L2Npc +{ + public static final Logger _log = Logger.getLogger(L2CastleTeleporterInstance.class.getName()); + + private boolean _currentTask = false; + + /** + * @param template + */ + public L2CastleTeleporterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2CastleTeleporterInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + StringTokenizer st = new StringTokenizer(command, " "); + String actualCommand = st.nextToken(); // Get actual command + + if (actualCommand.equalsIgnoreCase("tele")) + { + int delay; + if (!getTask()) + { + if (getCastle().getSiege().getIsInProgress() && (getCastle().getSiege().getControlTowerCount() == 0)) + { + delay = 480000; + } + else + { + delay = 30000; + } + + setTask(true); + ThreadPoolManager.getInstance().scheduleGeneral(new oustAllPlayers(), delay); + } + + String filename = "data/html/castleteleporter/MassGK-1.htm"; + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + player.sendPacket(html); + return; + } + super.onBypassFeedback(player, command); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + String filename; + if (!getTask()) + { + if (getCastle().getSiege().getIsInProgress() && (getCastle().getSiege().getControlTowerCount() == 0)) + { + filename = "data/html/castleteleporter/MassGK-2.htm"; + } + else + { + filename = "data/html/castleteleporter/MassGK.htm"; + } + } + else + { + filename = "data/html/castleteleporter/MassGK-1.htm"; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + + void oustAllPlayers() + { + getCastle().oustAllPlayers(); + } + + class oustAllPlayers implements Runnable + { + @Override + public void run() + { + try + { + NpcSay cs = new NpcSay(getObjectId(), 1, getNpcId(), 1000443); // The defenders of $s1 castle will be teleported to the inner castle. + cs.addStringParameter(getCastle().getName()); + int region = MapRegionTable.getInstance().getMapRegion(getX(), getY()); + Collection pls = L2World.getInstance().getAllPlayers().values(); + // synchronized (L2World.getInstance().getAllPlayers()) + { + for (L2PcInstance player : pls) + { + if (region == MapRegionTable.getInstance().getMapRegion(player.getX(), player.getY())) + { + player.sendPacket(cs); + } + } + } + oustAllPlayers(); + setTask(false); + } + catch (NullPointerException e) + { + _log.log(Level.WARNING, "" + e.getMessage(), e); + } + } + } + + public boolean getTask() + { + return _currentTask; + } + + public void setTask(boolean state) + { + _currentTask = state; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleWarehouseInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleWarehouseInstance.java new file mode 100644 index 00000000..669201ad --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleWarehouseInstance.java @@ -0,0 +1,101 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author l3x + */ +public class L2CastleWarehouseInstance extends L2WarehouseInstance +{ + protected static final int COND_ALL_FALSE = 0; + protected static final int COND_BUSY_BECAUSE_OF_SIEGE = 1; + protected static final int COND_OWNER = 2; + + /** + * @param template + */ + public L2CastleWarehouseInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2CastleWarehouseInstance); + } + + @Override + public boolean isWarehouse() + { + return true; + } + + @Override + public void showChatWindow(L2PcInstance player, int val) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + String filename = "data/html/castlewarehouse/castlewarehouse-no.htm"; + + int condition = validateCondition(player); + + if (condition > COND_ALL_FALSE) + { + if (condition == COND_BUSY_BECAUSE_OF_SIEGE) + { + filename = "data/html/castlewarehouse/castlewarehouse-busy.htm"; + } + else if (condition == COND_OWNER) + { + if (val == 0) + { + filename = "data/html/castlewarehouse/castlewarehouse.htm"; + } + else + { + filename = "data/html/castlewarehouse/castlewarehouse-" + val + ".htm"; + } + } + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } + + protected int validateCondition(L2PcInstance player) + { + if (player.isGM()) + { + return COND_OWNER; + } + + if ((getCastle() != null) && (getCastle().getCastleId() > 0)) + { + if (player.getClan() != null) + { + if (getCastle().getZone().isActive()) + { + return COND_BUSY_BECAUSE_OF_SIEGE; + } + else if (getCastle().getOwnerId() == player.getClanId()) + { + return COND_OWNER; + } + } + } + return COND_ALL_FALSE; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleWyvernManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleWyvernManagerInstance.java new file mode 100644 index 00000000..6186de3c --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CastleWyvernManagerInstance.java @@ -0,0 +1,47 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2CastleWyvernManagerInstance extends L2WyvernManagerInstance +{ + public L2CastleWyvernManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + } + + @Override + public final boolean isOwnerClan(L2PcInstance player) + { + if ((player.getClan() != null) && (getCastle() != null)) + { + if ((player.getClanId() == getCastle().getOwnerId()) && player.isClanLeader()) + { + return true; + } + } + return false; + } + + /** + * @see com.l2jserver.gameserver.model.actor.instance.L2WyvernManagerInstance#isInSiege(com.l2jserver.gameserver.model.actor.instance.L2PcInstance) + */ + @Override + public boolean isInSiege() + { + return getCastle().getZone().isActive(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ChestInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ChestInstance.java new file mode 100644 index 00000000..ae18714f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ChestInstance.java @@ -0,0 +1,251 @@ +/* + *@author Julian + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.Rnd; + +/** + * This class manages all chest. + */ +public final class L2ChestInstance extends L2MonsterInstance +{ + private volatile boolean _isInteracted; + private volatile boolean _specialDrop; + + public L2ChestInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2ChestInstance); + setIsNoRndWalk(true); + _isInteracted = false; + _specialDrop = false; + } + + @Override + public void onSpawn() + { + super.onSpawn(); + _isInteracted = false; + _specialDrop = false; + setMustRewardExpSp(true); + } + + public synchronized boolean isInteracted() + { + return _isInteracted; + } + + public synchronized void setInteracted() + { + _isInteracted = true; + } + + public synchronized boolean isSpecialDrop() + { + return _specialDrop; + } + + public synchronized void setSpecialDrop() + { + _specialDrop = true; + } + + @Override + public void doItemDrop(L2NpcTemplate npcTemplate, L2Character lastAttacker) + { + int id = getTemplate().npcId; + + if (!_specialDrop) + { + if ((id >= 18265) && (id <= 18286)) + { + id += 3536; + } + else if ((id == 18287) || (id == 18288)) + { + id = 21671; + } + else if ((id == 18289) || (id == 18290)) + { + id = 21694; + } + else if ((id == 18291) || (id == 18292)) + { + id = 21717; + } + else if ((id == 18293) || (id == 18294)) + { + id = 21740; + } + else if ((id == 18295) || (id == 18296)) + { + id = 21763; + } + else if ((id == 18297) || (id == 18298)) + { + id = 21786; + } + } + + super.doItemDrop(NpcTable.getInstance().getTemplate(id), lastAttacker); + } + + // cast - trap chest + public void chestTrap(L2Character player) + { + int trapSkillId = 0; + int rnd = Rnd.get(120); + + if (getTemplate().level >= 61) + { + if (rnd >= 90) + { + trapSkillId = 4139;// explosion + } + else if (rnd >= 50) + { + trapSkillId = 4118;// area paralysys + } + else if (rnd >= 20) + { + trapSkillId = 1167;// poison cloud + } + else + { + trapSkillId = 223;// sting + } + } + else if (getTemplate().level >= 41) + { + if (rnd >= 90) + { + trapSkillId = 4139;// explosion + } + else if (rnd >= 60) + { + trapSkillId = 96;// bleed + } + else if (rnd >= 20) + { + trapSkillId = 1167;// poison cloud + } + else + { + trapSkillId = 4118;// area paralysys + } + } + else if (getTemplate().level >= 21) + { + if (rnd >= 80) + { + trapSkillId = 4139;// explosion + } + else if (rnd >= 50) + { + trapSkillId = 96;// bleed + } + else if (rnd >= 20) + { + trapSkillId = 1167;// poison cloud + } + else + { + trapSkillId = 129;// poison + } + } + else + { + if (rnd >= 80) + { + trapSkillId = 4139;// explosion + } + else if (rnd >= 50) + { + trapSkillId = 96;// bleed + } + else + { + trapSkillId = 129;// poison + } + } + + player.sendMessage("There was a trap!"); + handleCast(player, trapSkillId); + } + + // <-- + // cast casse + // <-- + private boolean handleCast(L2Character player, int skillId) + { + int skillLevel = 1; + byte lvl = getTemplate().level; + if ((lvl > 20) && (lvl <= 40)) + { + skillLevel = 3; + } + else if ((lvl > 40) && (lvl <= 60)) + { + skillLevel = 5; + } + else if (lvl > 60) + { + skillLevel = 6; + } + + if (player.isDead() || !player.isVisible() || !player.isInsideRadius(this, getDistanceToWatchObject(player), false, false)) + { + return false; + } + + L2Skill skill = SkillTable.getInstance().getInfo(skillId, skillLevel); + + if (player.getFirstEffect(skill) == null) + { + skill.getEffects(this, player); + broadcastPacket(new MagicSkillUse(this, player, skill.getId(), skillLevel, skill.getHitTime(), 0)); + return true; + } + return false; + } + + @Override + public boolean isMovementDisabled() + { + if (super.isMovementDisabled()) + { + return true; + } + if (isInteracted()) + { + return false; + } + return true; + } + + @Override + public boolean hasRandomAnimation() + { + return false; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClanHallDoormenInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClanHallDoormenInstance.java new file mode 100644 index 00000000..0cc85b09 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClanHallDoormenInstance.java @@ -0,0 +1,207 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.Arrays; +import java.util.StringTokenizer; + +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.instancemanager.ClanHallManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.entity.ClanHall; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.util.Evolve; + +public class L2ClanHallDoormenInstance extends L2DoormenInstance +{ + private boolean _init = false; + private ClanHall _clanHall = null; + private boolean _hasEvolve = false; + + // list of clan halls with evolve function, should be sorted + private static final int[] CH_WITH_EVOLVE = + { + 36, + 37, + 38, + 39, + 40, + 41, + 51, + 52, + 53, + 54, + 55, + 56, + 57 + }; + + public L2ClanHallDoormenInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2ClanHallDoormenInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if (_hasEvolve && command.startsWith("evolve")) + { + if (isOwnerClan(player)) + { + StringTokenizer st = new StringTokenizer(command, " "); + if (st.countTokens() < 2) + { + return; + } + + st.nextToken(); + boolean ok = false; + switch (Integer.parseInt(st.nextToken())) + { + case 1: + ok = Evolve.doEvolve(player, this, 9882, 10307, 55); + break; + case 2: + ok = Evolve.doEvolve(player, this, 4422, 10308, 55); + break; + case 3: + ok = Evolve.doEvolve(player, this, 4423, 10309, 55); + break; + case 4: + ok = Evolve.doEvolve(player, this, 4424, 10310, 55); + break; + case 5: + ok = Evolve.doEvolve(player, this, 10426, 10611, 70); + break; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + if (ok) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallDoormen/evolve-ok.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallDoormen/evolve-no.htm"); + } + player.sendPacket(html); + return; + } + } + super.onBypassFeedback(player, command); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + + if (getClanHall() != null) + { + L2Clan owner = ClanTable.getInstance().getClan(getClanHall().getOwnerId()); + if (isOwnerClan(player)) + { + if (_hasEvolve) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallDoormen/doormen2.htm"); + html.replace("%clanname%", owner.getName()); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallDoormen/doormen1.htm"); + html.replace("%clanname%", owner.getName()); + } + } + else + { + if ((owner != null) && (owner.getLeader() != null)) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallDoormen/doormen-no.htm"); + html.replace("%leadername%", owner.getLeaderName()); + html.replace("%clanname%", owner.getName()); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallDoormen/emptyowner.htm"); + html.replace("%hallname%", getClanHall().getName()); + } + } + } + else + { + return; + } + + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + + @Override + protected final void openDoors(L2PcInstance player, String command) + { + getClanHall().openCloseDoors(true); + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallDoormen/doormen-opened.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + + @Override + protected final void closeDoors(L2PcInstance player, String command) + { + getClanHall().openCloseDoors(false); + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallDoormen/doormen-closed.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + + private final ClanHall getClanHall() + { + if (!_init) + { + synchronized (this) + { + if (!_init) + { + _clanHall = ClanHallManager.getInstance().getNearbyClanHall(getX(), getY(), 500); + if (_clanHall != null) + { + _hasEvolve = Arrays.binarySearch(CH_WITH_EVOLVE, _clanHall.getId()) >= 0; + } + _init = true; + } + } + } + return _clanHall; + } + + @Override + protected final boolean isOwnerClan(L2PcInstance player) + { + if ((player.getClan() != null) && (getClanHall() != null)) + { + if (player.getClanId() == getClanHall().getOwnerId()) + { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClanHallManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClanHallManagerInstance.java new file mode 100644 index 00000000..a3d369a7 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClanHallManagerInstance.java @@ -0,0 +1,1546 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.text.SimpleDateFormat; +import java.util.StringTokenizer; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.TeleportLocationTable; +import com.l2jserver.gameserver.instancemanager.ClanHallManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2TeleportLocation; +import com.l2jserver.gameserver.model.entity.ClanHall; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.AgitDecoInfo; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.templates.skills.L2SkillType; + +public class L2ClanHallManagerInstance extends L2MerchantInstance +{ + protected static final int COND_OWNER_FALSE = 0; + protected static final int COND_ALL_FALSE = 1; + protected static final int COND_BUSY_BECAUSE_OF_SIEGE = 2; + protected static final int COND_OWNER = 3; + private int _clanHallId = -1; + + /** + * @param objectId + * @param template + */ + public L2ClanHallManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2ClanHallManagerInstance); + } + + @Override + public boolean isWarehouse() + { + return true; + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm"); + int condition = validateCondition(player); + if (condition <= COND_ALL_FALSE) + { + return; + } + else if (condition == COND_OWNER) + { + StringTokenizer st = new StringTokenizer(command, " "); + String actualCommand = st.nextToken(); // Get actual command + String val = ""; + if (st.countTokens() >= 1) + { + val = st.nextToken(); + } + + if (actualCommand.equalsIgnoreCase("banish_foreigner")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if ((player.getClanPrivileges() & L2Clan.CP_CH_DISMISS) == L2Clan.CP_CH_DISMISS) + { + if (val.equalsIgnoreCase("list")) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/banish-list.htm"); + } + else if (val.equalsIgnoreCase("banish")) + { + getClanHall().banishForeigners(); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/banish.htm"); + } + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/not_authorized.htm"); + } + sendHtmlMessage(player, html); + return; + } + else if (actualCommand.equalsIgnoreCase("manage_vault")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if ((player.getClanPrivileges() & L2Clan.CP_CL_VIEW_WAREHOUSE) == L2Clan.CP_CL_VIEW_WAREHOUSE) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/vault.htm"); + html.replace("%rent%", String.valueOf(getClanHall().getLease())); + html.replace("%date%", format.format(getClanHall().getPaidUntil())); + sendHtmlMessage(player, html); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/not_authorized.htm"); + sendHtmlMessage(player, html); + } + return; + } + else if (actualCommand.equalsIgnoreCase("door")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if ((player.getClanPrivileges() & L2Clan.CP_CH_OPEN_DOOR) == L2Clan.CP_CH_OPEN_DOOR) + { + if (val.equalsIgnoreCase("open")) + { + getClanHall().openCloseDoors(true); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/door-open.htm"); + } + else if (val.equalsIgnoreCase("close")) + { + getClanHall().openCloseDoors(false); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/door-close.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/door.htm"); + } + sendHtmlMessage(player, html); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/not_authorized.htm"); + sendHtmlMessage(player, html); + } + return; + } + else if (actualCommand.equalsIgnoreCase("functions")) + { + if (val.equalsIgnoreCase("tele")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if (getClanHall().getFunction(ClanHall.FUNC_TELEPORT) == null) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/chamberlain-nac.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/tele" + getClanHall().getLocation() + getClanHall().getFunction(ClanHall.FUNC_TELEPORT).getLvl() + ".htm"); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("item_creation")) + { + if (getClanHall().getFunction(ClanHall.FUNC_ITEM_CREATE) == null) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/chamberlain-nac.htm"); + sendHtmlMessage(player, html); + return; + } + if (st.countTokens() < 1) + { + return; + } + int valbuy = Integer.parseInt(st.nextToken()) + (getClanHall().getFunction(ClanHall.FUNC_ITEM_CREATE).getLvl() * 100000); + showBuyWindow(player, valbuy); + } + else if (val.equalsIgnoreCase("support")) + { + + NpcHtmlMessage html = new NpcHtmlMessage(1); + if (getClanHall().getFunction(ClanHall.FUNC_SUPPORT) == null) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/chamberlain-nac.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/support" + getClanHall().getFunction(ClanHall.FUNC_SUPPORT).getLvl() + ".htm"); + html.replace("%mp%", String.valueOf((int) getCurrentMp())); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("back")) + { + showChatWindow(player); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions.htm"); + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_EXP) != null) + { + html.replace("%xp_regen%", String.valueOf(getClanHall().getFunction(ClanHall.FUNC_RESTORE_EXP).getLvl())); + } + else + { + html.replace("%xp_regen%", "0"); + } + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_HP) != null) + { + html.replace("%hp_regen%", String.valueOf(getClanHall().getFunction(ClanHall.FUNC_RESTORE_HP).getLvl())); + } + else + { + html.replace("%hp_regen%", "0"); + } + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_MP) != null) + { + html.replace("%mp_regen%", String.valueOf(getClanHall().getFunction(ClanHall.FUNC_RESTORE_MP).getLvl())); + } + else + { + html.replace("%mp_regen%", "0"); + } + sendHtmlMessage(player, html); + } + return; + } + else if (actualCommand.equalsIgnoreCase("manage")) + { + if ((player.getClanPrivileges() & L2Clan.CP_CH_SET_FUNCTIONS) == L2Clan.CP_CH_SET_FUNCTIONS) + { + if (val.equalsIgnoreCase("recovery")) + { + if (st.countTokens() >= 1) + { + if (getClanHall().getOwnerId() == 0) + { + player.sendMessage("This clan Hall have no owner, you cannot change configuration"); + return; + } + val = st.nextToken(); + if (val.equalsIgnoreCase("hp_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel.htm"); + html.replace("%apply%", "recovery hp 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("mp_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel.htm"); + html.replace("%apply%", "recovery mp 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("exp_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel.htm"); + html.replace("%apply%", "recovery exp 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_hp")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply.htm"); + html.replace("%name%", "Fireplace (HP Recovery Device)"); + int percent = Integer.parseInt(val); + int cost; + switch (percent) + { + case 20: + cost = Config.CH_HPREG1_FEE; + break; + case 40: + cost = Config.CH_HPREG2_FEE; + break; + case 80: + cost = Config.CH_HPREG3_FEE; + break; + case 100: + cost = Config.CH_HPREG4_FEE; + break; + case 120: + cost = Config.CH_HPREG5_FEE; + break; + case 140: + cost = Config.CH_HPREG6_FEE; + break; + case 160: + cost = Config.CH_HPREG7_FEE; + break; + case 180: + cost = Config.CH_HPREG8_FEE; + break; + case 200: + cost = Config.CH_HPREG9_FEE; + break; + case 220: + cost = Config.CH_HPREG10_FEE; + break; + case 240: + cost = Config.CH_HPREG11_FEE; + break; + case 260: + cost = Config.CH_HPREG12_FEE; + break; + default: + cost = Config.CH_HPREG13_FEE; + break; + } + + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CH_HPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Provides additional HP recovery for clan members in the clan hall." + String.valueOf(percent) + "%"); + html.replace("%apply%", "recovery hp " + String.valueOf(percent)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_mp")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply.htm"); + html.replace("%name%", "Carpet (MP Recovery)"); + int percent = Integer.parseInt(val); + int cost; + switch (percent) + { + case 5: + cost = Config.CH_MPREG1_FEE; + break; + case 10: + cost = Config.CH_MPREG2_FEE; + break; + case 15: + cost = Config.CH_MPREG3_FEE; + break; + case 30: + cost = Config.CH_MPREG4_FEE; + break; + default: + cost = Config.CH_MPREG5_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CH_MPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Provides additional MP recovery for clan members in the clan hall." + String.valueOf(percent) + "%"); + html.replace("%apply%", "recovery mp " + String.valueOf(percent)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_exp")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply.htm"); + html.replace("%name%", "Chandelier (EXP Recovery Device)"); + int percent = Integer.parseInt(val); + int cost; + switch (percent) + { + case 5: + cost = Config.CH_EXPREG1_FEE; + break; + case 10: + cost = Config.CH_EXPREG2_FEE; + break; + case 15: + cost = Config.CH_EXPREG3_FEE; + break; + case 25: + cost = Config.CH_EXPREG4_FEE; + break; + case 35: + cost = Config.CH_EXPREG5_FEE; + break; + case 40: + cost = Config.CH_EXPREG6_FEE; + break; + default: + cost = Config.CH_EXPREG7_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CH_EXPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Restores the Exp of any clan member who is resurrected in the clan hall." + String.valueOf(percent) + "%"); + html.replace("%apply%", "recovery exp " + String.valueOf(percent)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("hp")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Mp editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply_confirmed.htm"); + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_HP) != null) + { + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_HP).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-used.htm"); + html.replace("%val%", String.valueOf(val) + "%"); + sendHtmlMessage(player, html); + return; + } + } + int percent = Integer.parseInt(val); + switch (percent) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel_confirmed.htm"); + break; + case 20: + fee = Config.CH_HPREG1_FEE; + break; + case 40: + fee = Config.CH_HPREG2_FEE; + break; + case 80: + fee = Config.CH_HPREG3_FEE; + break; + case 100: + fee = Config.CH_HPREG4_FEE; + break; + case 120: + fee = Config.CH_HPREG5_FEE; + break; + case 140: + fee = Config.CH_HPREG6_FEE; + break; + case 160: + fee = Config.CH_HPREG7_FEE; + break; + case 180: + fee = Config.CH_HPREG8_FEE; + break; + case 200: + fee = Config.CH_HPREG9_FEE; + break; + case 220: + fee = Config.CH_HPREG10_FEE; + break; + case 240: + fee = Config.CH_HPREG11_FEE; + break; + case 260: + fee = Config.CH_HPREG12_FEE; + break; + default: + fee = Config.CH_HPREG13_FEE; + break; + } + if (!getClanHall().updateFunctions(player, ClanHall.FUNC_RESTORE_HP, percent, fee, Config.CH_HPREG_FEE_RATIO, (getClanHall().getFunction(ClanHall.FUNC_RESTORE_HP) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/low_adena.htm"); + sendHtmlMessage(player, html); + } + else + { + revalidateDeco(player); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("mp")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Mp editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply_confirmed.htm"); + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_MP) != null) + { + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_MP).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-used.htm"); + html.replace("%val%", String.valueOf(val) + "%"); + sendHtmlMessage(player, html); + return; + } + } + int percent = Integer.parseInt(val); + switch (percent) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel_confirmed.htm"); + break; + case 5: + fee = Config.CH_MPREG1_FEE; + break; + case 10: + fee = Config.CH_MPREG2_FEE; + break; + case 15: + fee = Config.CH_MPREG3_FEE; + break; + case 30: + fee = Config.CH_MPREG4_FEE; + break; + default: + fee = Config.CH_MPREG5_FEE; + break; + } + if (!getClanHall().updateFunctions(player, ClanHall.FUNC_RESTORE_MP, percent, fee, Config.CH_MPREG_FEE_RATIO, (getClanHall().getFunction(ClanHall.FUNC_RESTORE_MP) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/low_adena.htm"); + sendHtmlMessage(player, html); + } + else + { + revalidateDeco(player); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("exp")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Exp editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply_confirmed.htm"); + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_EXP) != null) + { + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_EXP).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-used.htm"); + html.replace("%val%", String.valueOf(val) + "%"); + sendHtmlMessage(player, html); + return; + } + } + int percent = Integer.parseInt(val); + switch (percent) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel_confirmed.htm"); + break; + case 5: + fee = Config.CH_EXPREG1_FEE; + break; + case 10: + fee = Config.CH_EXPREG2_FEE; + break; + case 15: + fee = Config.CH_EXPREG3_FEE; + break; + case 25: + fee = Config.CH_EXPREG4_FEE; + break; + case 35: + fee = Config.CH_EXPREG5_FEE; + break; + case 40: + fee = Config.CH_EXPREG6_FEE; + break; + default: + fee = Config.CH_EXPREG7_FEE; + break; + } + if (!getClanHall().updateFunctions(player, ClanHall.FUNC_RESTORE_EXP, percent, fee, Config.CH_EXPREG_FEE_RATIO, (getClanHall().getFunction(ClanHall.FUNC_RESTORE_EXP) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/low_adena.htm"); + sendHtmlMessage(player, html); + } + else + { + revalidateDeco(player); + } + sendHtmlMessage(player, html); + } + return; + } + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/edit_recovery.htm"); + String hp_grade0 = "[20%][40%][220%]"; + String hp_grade1 = "[40%][100%][160%]"; + String hp_grade2 = "[80%][140%][200%][260%]"; + String hp_grade3 = "[80%][120%][180%][240%][300%]"; + String exp_grade0 = "[5%][10%][25%]"; + String exp_grade1 = "[5%][15%][30%]"; + String exp_grade2 = "[5%][15%][25%][40%]"; + String exp_grade3 = "[15%][25%][35%][50%]"; + String mp_grade0 = "[5%][10%][25%]"; + String mp_grade1 = "[5%][15%][25%]"; + String mp_grade2 = "[5%][15%][30%]"; + String mp_grade3 = "[5%][15%][30%][40%]"; + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_HP) != null) + { + html.replace("%hp_recovery%", String.valueOf(getClanHall().getFunction(ClanHall.FUNC_RESTORE_HP).getLvl()) + "% (" + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_RESTORE_HP).getLease()) + "Adena /" + String.valueOf(Config.CH_HPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%hp_period%", "Withdraw the fee for the next time at " + format.format(getClanHall().getFunction(ClanHall.FUNC_RESTORE_HP).getEndTime())); + int grade = getClanHall().getGrade(); + switch (grade) + { + case 0: + html.replace("%change_hp%", "[Deactivate]" + hp_grade0); + break; + case 1: + html.replace("%change_hp%", "[Deactivate]" + hp_grade1); + break; + case 2: + html.replace("%change_hp%", "[Deactivate]" + hp_grade2); + break; + case 3: + html.replace("%change_hp%", "[Deactivate]" + hp_grade3); + break; + } + } + else + { + html.replace("%hp_recovery%", "none"); + html.replace("%hp_period%", "none"); + int grade = getClanHall().getGrade(); + switch (grade) + { + case 0: + html.replace("%change_hp%", hp_grade0); + break; + case 1: + html.replace("%change_hp%", hp_grade1); + break; + case 2: + html.replace("%change_hp%", hp_grade2); + break; + case 3: + html.replace("%change_hp%", hp_grade3); + break; + } + } + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_EXP) != null) + { + html.replace("%exp_recovery%", String.valueOf(getClanHall().getFunction(ClanHall.FUNC_RESTORE_EXP).getLvl()) + "% (" + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_RESTORE_EXP).getLease()) + "Adena /" + String.valueOf(Config.CH_EXPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%exp_period%", "Withdraw the fee for the next time at " + format.format(getClanHall().getFunction(ClanHall.FUNC_RESTORE_EXP).getEndTime())); + int grade = getClanHall().getGrade(); + switch (grade) + { + case 0: + html.replace("%change_exp%", "[Deactivate]" + exp_grade0); + break; + case 1: + html.replace("%change_exp%", "[Deactivate]" + exp_grade1); + break; + case 2: + html.replace("%change_exp%", "[Deactivate]" + exp_grade2); + break; + case 3: + html.replace("%change_exp%", "[Deactivate]" + exp_grade3); + break; + } + } + else + { + html.replace("%exp_recovery%", "none"); + html.replace("%exp_period%", "none"); + int grade = getClanHall().getGrade(); + switch (grade) + { + case 0: + html.replace("%change_exp%", exp_grade0); + break; + case 1: + html.replace("%change_exp%", exp_grade1); + break; + case 2: + html.replace("%change_exp%", exp_grade2); + break; + case 3: + html.replace("%change_exp%", exp_grade3); + break; + } + } + if (getClanHall().getFunction(ClanHall.FUNC_RESTORE_MP) != null) + { + html.replace("%mp_recovery%", String.valueOf(getClanHall().getFunction(ClanHall.FUNC_RESTORE_MP).getLvl()) + "% (" + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_RESTORE_MP).getLease()) + "Adena /" + String.valueOf(Config.CH_MPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%mp_period%", "Withdraw the fee for the next time at " + format.format(getClanHall().getFunction(ClanHall.FUNC_RESTORE_MP).getEndTime())); + int grade = getClanHall().getGrade(); + switch (grade) + { + case 0: + html.replace("%change_mp%", "[Deactivate]" + mp_grade0); + break; + case 1: + html.replace("%change_mp%", "[Deactivate]" + mp_grade1); + break; + case 2: + html.replace("%change_mp%", "[Deactivate]" + mp_grade2); + break; + case 3: + html.replace("%change_mp%", "[Deactivate]" + mp_grade3); + break; + } + } + else + { + html.replace("%mp_recovery%", "none"); + html.replace("%mp_period%", "none"); + int grade = getClanHall().getGrade(); + switch (grade) + { + case 0: + html.replace("%change_mp%", mp_grade0); + break; + case 1: + html.replace("%change_mp%", mp_grade1); + break; + case 2: + html.replace("%change_mp%", mp_grade2); + break; + case 3: + html.replace("%change_mp%", mp_grade3); + break; + } + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("other")) + { + if (st.countTokens() >= 1) + { + if (getClanHall().getOwnerId() == 0) + { + player.sendMessage("This clan Hall have no owner, you cannot change configuration"); + return; + } + val = st.nextToken(); + if (val.equalsIgnoreCase("item_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel.htm"); + html.replace("%apply%", "other item 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("tele_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel.htm"); + html.replace("%apply%", "other tele 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("support_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel.htm"); + html.replace("%apply%", "other support 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_item")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply.htm"); + html.replace("%name%", "Magic Equipment (Item Production Facilities)"); + int stage = Integer.parseInt(val); + int cost; + switch (stage) + { + case 1: + cost = Config.CH_ITEM1_FEE; + break; + case 2: + cost = Config.CH_ITEM2_FEE; + break; + default: + cost = Config.CH_ITEM3_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CH_ITEM_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Allow the purchase of special items at fixed intervals."); + html.replace("%apply%", "other item " + String.valueOf(stage)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_support")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply.htm"); + html.replace("%name%", "Insignia (Supplementary Magic)"); + int stage = Integer.parseInt(val); + int cost; + switch (stage) + { + case 1: + cost = Config.CH_SUPPORT1_FEE; + break; + case 2: + cost = Config.CH_SUPPORT2_FEE; + break; + case 3: + cost = Config.CH_SUPPORT3_FEE; + break; + case 4: + cost = Config.CH_SUPPORT4_FEE; + break; + case 5: + cost = Config.CH_SUPPORT5_FEE; + break; + case 6: + cost = Config.CH_SUPPORT6_FEE; + break; + case 7: + cost = Config.CH_SUPPORT7_FEE; + break; + default: + cost = Config.CH_SUPPORT8_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CH_SUPPORT_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Enables the use of supplementary magic."); + html.replace("%apply%", "other support " + String.valueOf(stage)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_tele")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply.htm"); + html.replace("%name%", "Mirror (Teleportation Device)"); + int stage = Integer.parseInt(val); + int cost; + switch (stage) + { + case 1: + cost = Config.CH_TELE1_FEE; + break; + default: + cost = Config.CH_TELE2_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CH_TELE_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Teleports clan members in a clan hall to the target Stage " + String.valueOf(stage) + " staging area"); + html.replace("%apply%", "other tele " + String.valueOf(stage)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("item")) + { + if (st.countTokens() >= 1) + { + if (getClanHall().getOwnerId() == 0) + { + player.sendMessage("This clan Hall have no owner, you cannot change configuration"); + return; + } + if (Config.DEBUG) + { + _log.warning("Item editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply_confirmed.htm"); + if (getClanHall().getFunction(ClanHall.FUNC_ITEM_CREATE) != null) + { + if (getClanHall().getFunction(ClanHall.FUNC_ITEM_CREATE).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-used.htm"); + html.replace("%val%", "Stage " + String.valueOf(val)); + sendHtmlMessage(player, html); + return; + } + } + int fee; + int lvl = Integer.parseInt(val); + switch (lvl) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel_confirmed.htm"); + break; + case 1: + fee = Config.CH_ITEM1_FEE; + break; + case 2: + fee = Config.CH_ITEM2_FEE; + break; + default: + fee = Config.CH_ITEM3_FEE; + break; + } + if (!getClanHall().updateFunctions(player, ClanHall.FUNC_ITEM_CREATE, lvl, fee, Config.CH_ITEM_FEE_RATIO, (getClanHall().getFunction(ClanHall.FUNC_ITEM_CREATE) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/low_adena.htm"); + sendHtmlMessage(player, html); + } + else + { + revalidateDeco(player); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("tele")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Tele editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply_confirmed.htm"); + if (getClanHall().getFunction(ClanHall.FUNC_TELEPORT) != null) + { + if (getClanHall().getFunction(ClanHall.FUNC_TELEPORT).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-used.htm"); + html.replace("%val%", "Stage " + String.valueOf(val)); + sendHtmlMessage(player, html); + return; + } + } + int lvl = Integer.parseInt(val); + switch (lvl) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel_confirmed.htm"); + break; + case 1: + fee = Config.CH_TELE1_FEE; + break; + default: + fee = Config.CH_TELE2_FEE; + break; + } + if (!getClanHall().updateFunctions(player, ClanHall.FUNC_TELEPORT, lvl, fee, Config.CH_TELE_FEE_RATIO, (getClanHall().getFunction(ClanHall.FUNC_TELEPORT) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/low_adena.htm"); + sendHtmlMessage(player, html); + } + else + { + revalidateDeco(player); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("support")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Support editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply_confirmed.htm"); + if (getClanHall().getFunction(ClanHall.FUNC_SUPPORT) != null) + { + if (getClanHall().getFunction(ClanHall.FUNC_SUPPORT).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-used.htm"); + html.replace("%val%", "Stage " + String.valueOf(val)); + sendHtmlMessage(player, html); + return; + } + } + int lvl = Integer.parseInt(val); + switch (lvl) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel_confirmed.htm"); + break; + case 1: + fee = Config.CH_SUPPORT1_FEE; + break; + case 2: + fee = Config.CH_SUPPORT2_FEE; + break; + case 3: + fee = Config.CH_SUPPORT3_FEE; + break; + case 4: + fee = Config.CH_SUPPORT4_FEE; + break; + case 5: + fee = Config.CH_SUPPORT5_FEE; + break; + case 6: + fee = Config.CH_SUPPORT6_FEE; + break; + case 7: + fee = Config.CH_SUPPORT7_FEE; + break; + default: + fee = Config.CH_SUPPORT8_FEE; + break; + } + if (!getClanHall().updateFunctions(player, ClanHall.FUNC_SUPPORT, lvl, fee, Config.CH_SUPPORT_FEE_RATIO, (getClanHall().getFunction(ClanHall.FUNC_SUPPORT) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/low_adena.htm"); + sendHtmlMessage(player, html); + } + else + { + revalidateDeco(player); + } + sendHtmlMessage(player, html); + } + return; + } + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/edit_other.htm"); + String tele = "[Level 1][Level 2]"; + String support_grade0 = "[Level 1][Level 2]"; + String support_grade1 = "[Level 1][Level 2][Level 4]"; + String support_grade2 = "[Level 3][Level 4][Level 5]"; + String support_grade3 = "[Level 3][Level 5][Level 7][Level 8]"; + String item = "[Level 1][Level 2][Level 3]"; + if (getClanHall().getFunction(ClanHall.FUNC_TELEPORT) != null) + { + html.replace("%tele%", "Stage " + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_TELEPORT).getLvl()) + " (" + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_TELEPORT).getLease()) + "Adena /" + String.valueOf(Config.CH_TELE_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%tele_period%", "Withdraw the fee for the next time at " + format.format(getClanHall().getFunction(ClanHall.FUNC_TELEPORT).getEndTime())); + html.replace("%change_tele%", "[Deactivate]" + tele); + } + else + { + html.replace("%tele%", "none"); + html.replace("%tele_period%", "none"); + html.replace("%change_tele%", tele); + } + if (getClanHall().getFunction(ClanHall.FUNC_SUPPORT) != null) + { + html.replace("%support%", "Stage " + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_SUPPORT).getLvl()) + " (" + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_SUPPORT).getLease()) + "Adena /" + String.valueOf(Config.CH_SUPPORT_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%support_period%", "Withdraw the fee for the next time at " + format.format(getClanHall().getFunction(ClanHall.FUNC_SUPPORT).getEndTime())); + int grade = getClanHall().getGrade(); + switch (grade) + { + case 0: + html.replace("%change_support%", "[Deactivate]" + support_grade0); + break; + case 1: + html.replace("%change_support%", "[Deactivate]" + support_grade1); + break; + case 2: + html.replace("%change_support%", "[Deactivate]" + support_grade2); + break; + case 3: + html.replace("%change_support%", "[Deactivate]" + support_grade3); + break; + } + } + else + { + html.replace("%support%", "none"); + html.replace("%support_period%", "none"); + int grade = getClanHall().getGrade(); + switch (grade) + { + case 0: + html.replace("%change_support%", support_grade0); + break; + case 1: + html.replace("%change_support%", support_grade1); + break; + case 2: + html.replace("%change_support%", support_grade2); + break; + case 3: + html.replace("%change_support%", support_grade3); + break; + } + } + if (getClanHall().getFunction(ClanHall.FUNC_ITEM_CREATE) != null) + { + html.replace("%item%", "Stage " + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_ITEM_CREATE).getLvl()) + " (" + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_ITEM_CREATE).getLease()) + "Adena /" + String.valueOf(Config.CH_ITEM_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%item_period%", "Withdraw the fee for the next time at " + format.format(getClanHall().getFunction(ClanHall.FUNC_ITEM_CREATE).getEndTime())); + html.replace("%change_item%", "[Deactivate]" + item); + } + else + { + html.replace("%item%", "none"); + html.replace("%item_period%", "none"); + html.replace("%change_item%", item); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("deco")) + { + if (st.countTokens() >= 1) + { + if (getClanHall().getOwnerId() == 0) + { + player.sendMessage("This clan Hall have no owner, you cannot change configuration"); + return; + } + val = st.nextToken(); + if (val.equalsIgnoreCase("curtains_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel.htm"); + html.replace("%apply%", "deco curtains 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("fixtures_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel.htm"); + html.replace("%apply%", "deco fixtures 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_curtains")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply.htm"); + html.replace("%name%", "Curtains (Decoration)"); + int stage = Integer.parseInt(val); + int cost; + switch (stage) + { + case 1: + cost = Config.CH_CURTAIN1_FEE; + break; + default: + cost = Config.CH_CURTAIN2_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CH_CURTAIN_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "These curtains can be used to decorate the clan hall."); + html.replace("%apply%", "deco curtains " + String.valueOf(stage)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_fixtures")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply.htm"); + html.replace("%name%", "Front Platform (Decoration)"); + int stage = Integer.parseInt(val); + int cost; + switch (stage) + { + case 1: + cost = Config.CH_FRONT1_FEE; + break; + default: + cost = Config.CH_FRONT2_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.CH_FRONT_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Used to decorate the clan hall."); + html.replace("%apply%", "deco fixtures " + String.valueOf(stage)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("curtains")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Deco curtains editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply_confirmed.htm"); + if (getClanHall().getFunction(ClanHall.FUNC_DECO_CURTAINS) != null) + { + if (getClanHall().getFunction(ClanHall.FUNC_DECO_CURTAINS).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-used.htm"); + html.replace("%val%", "Stage " + String.valueOf(val)); + sendHtmlMessage(player, html); + return; + } + } + int lvl = Integer.parseInt(val); + switch (lvl) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel_confirmed.htm"); + break; + case 1: + fee = Config.CH_CURTAIN1_FEE; + break; + default: + fee = Config.CH_CURTAIN2_FEE; + break; + } + if (!getClanHall().updateFunctions(player, ClanHall.FUNC_DECO_CURTAINS, lvl, fee, Config.CH_CURTAIN_FEE_RATIO, (getClanHall().getFunction(ClanHall.FUNC_DECO_CURTAINS) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/low_adena.htm"); + sendHtmlMessage(player, html); + } + else + { + revalidateDeco(player); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("fixtures")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Deco fixtures editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-apply_confirmed.htm"); + if (getClanHall().getFunction(ClanHall.FUNC_DECO_FRONTPLATEFORM) != null) + { + if (getClanHall().getFunction(ClanHall.FUNC_DECO_FRONTPLATEFORM).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-used.htm"); + html.replace("%val%", "Stage " + String.valueOf(val)); + sendHtmlMessage(player, html); + return; + } + } + int lvl = Integer.parseInt(val); + switch (lvl) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/functions-cancel_confirmed.htm"); + break; + case 1: + fee = Config.CH_FRONT1_FEE; + break; + default: + fee = Config.CH_FRONT2_FEE; + break; + } + if (!getClanHall().updateFunctions(player, ClanHall.FUNC_DECO_FRONTPLATEFORM, lvl, fee, Config.CH_FRONT_FEE_RATIO, (getClanHall().getFunction(ClanHall.FUNC_DECO_FRONTPLATEFORM) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/low_adena.htm"); + sendHtmlMessage(player, html); + } + else + { + revalidateDeco(player); + } + sendHtmlMessage(player, html); + } + return; + } + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/deco.htm"); + String curtains = "[Level 1][Level 2]"; + String fixtures = "[Level 1][Level 2]"; + if (getClanHall().getFunction(ClanHall.FUNC_DECO_CURTAINS) != null) + { + html.replace("%curtain%", "Stage " + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_DECO_CURTAINS).getLvl()) + " (" + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_DECO_CURTAINS).getLease()) + "Adena /" + String.valueOf(Config.CH_CURTAIN_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%curtain_period%", "Withdraw the fee for the next time at " + format.format(getClanHall().getFunction(ClanHall.FUNC_DECO_CURTAINS).getEndTime())); + html.replace("%change_curtain%", "[Deactivate]" + curtains); + } + else + { + html.replace("%curtain%", "none"); + html.replace("%curtain_period%", "none"); + html.replace("%change_curtain%", curtains); + } + if (getClanHall().getFunction(ClanHall.FUNC_DECO_FRONTPLATEFORM) != null) + { + html.replace("%fixture%", "Stage " + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_DECO_FRONTPLATEFORM).getLvl()) + " (" + String.valueOf(getClanHall().getFunction(ClanHall.FUNC_DECO_FRONTPLATEFORM).getLease()) + "Adena /" + String.valueOf(Config.CH_FRONT_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%fixture_period%", "Withdraw the fee for the next time at " + format.format(getClanHall().getFunction(ClanHall.FUNC_DECO_FRONTPLATEFORM).getEndTime())); + html.replace("%change_fixture%", "[Deactivate]" + fixtures); + } + else + { + html.replace("%fixture%", "none"); + html.replace("%fixture_period%", "none"); + html.replace("%change_fixture%", fixtures); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("back")) + { + showChatWindow(player); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/manage.htm"); + sendHtmlMessage(player, html); + } + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/not_authorized.htm"); + sendHtmlMessage(player, html); + } + return; + } + else if (actualCommand.equalsIgnoreCase("support")) + { + if (player.isCursedWeaponEquipped()) + { + // Custom system message + player.sendMessage("The wielder of a cursed weapon cannot receive outside heals or buffs"); + return; + } + setTarget(player); + L2Skill skill; + if (val.isEmpty()) + { + return; + } + + try + { + int skill_id = Integer.parseInt(val); + try + { + int skill_lvl = 0; + if (st.countTokens() >= 1) + { + skill_lvl = Integer.parseInt(st.nextToken()); + } + skill = SkillTable.getInstance().getInfo(skill_id, skill_lvl); + if (skill.getSkillType() == L2SkillType.SUMMON) + { + player.doSimultaneousCast(skill); + } + else + { + final int mpCost = skill.getMpConsume() + skill.getMpInitialConsume(); + // If Clan Hall Buff are free or current MP is greater than MP cost, the skill should be casted. + if ((getCurrentMp() >= mpCost) || Config.CH_BUFF_FREE) + { + doCast(skill); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/support-no_mana.htm"); + html.replace("%mp%", String.valueOf((int) getCurrentMp())); + sendHtmlMessage(player, html); + return; + } + } + if (getClanHall().getFunction(ClanHall.FUNC_SUPPORT) == null) + { + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + if (getClanHall().getFunction(ClanHall.FUNC_SUPPORT).getLvl() == 0) + { + return; + } + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/support-done.htm"); + html.replace("%mp%", String.valueOf((int) getCurrentMp())); + sendHtmlMessage(player, html); + } + catch (Exception e) + { + player.sendMessage("Invalid skill level, contact your admin!"); + } + } + catch (Exception e) + { + player.sendMessage("Invalid skill level, contact your admin!"); + } + return; + } + else if (actualCommand.equalsIgnoreCase("list_back")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/chamberlain.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + sendHtmlMessage(player, html); + return; + } + else if (actualCommand.equalsIgnoreCase("support_back")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if (getClanHall().getFunction(ClanHall.FUNC_SUPPORT).getLvl() == 0) + { + return; + } + html.setFile(player.getHtmlPrefix(), "data/html/clanHallManager/support" + getClanHall().getFunction(ClanHall.FUNC_SUPPORT).getLvl() + ".htm"); + html.replace("%mp%", String.valueOf((int) getStatus().getCurrentMp())); + sendHtmlMessage(player, html); + return; + } + else if (actualCommand.equalsIgnoreCase("goto")) + { + int whereTo = Integer.parseInt(val); + doTeleport(player, whereTo); + return; + } + } + super.onBypassFeedback(player, command); + } + + private void sendHtmlMessage(L2PcInstance player, NpcHtmlMessage html) + { + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcId%", String.valueOf(getNpcId())); + player.sendPacket(html); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + String filename = "data/html/clanHallManager/chamberlain-no.htm"; + + int condition = validateCondition(player); + if (condition == COND_OWNER) + { + filename = "data/html/clanHallManager/chamberlain.htm";// Owner message window + } + if (condition == COND_OWNER_FALSE) + { + filename = "data/html/clanHallManager/chamberlain-of.htm"; + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcId%", String.valueOf(getNpcId())); + player.sendPacket(html); + } + + protected int validateCondition(L2PcInstance player) + { + if (getClanHall() == null) + { + return COND_ALL_FALSE; + } + if (player.isGM()) + { + return COND_OWNER; + } + if (player.getClan() != null) + { + if (getClanHall().getOwnerId() == player.getClanId()) + { + return COND_OWNER; + } + return COND_OWNER_FALSE; + } + return COND_ALL_FALSE; + } + + /** Return the L2ClanHall this L2NpcInstance belongs to. */ + public final ClanHall getClanHall() + { + if (_clanHallId < 0) + { + ClanHall temp = ClanHallManager.getInstance().getNearbyClanHall(getX(), getY(), 500); + + if (temp != null) + { + _clanHallId = temp.getId(); + } + + if (_clanHallId < 0) + { + return null; + } + } + return ClanHallManager.getInstance().getClanHallById(_clanHallId); + } + + private void doTeleport(L2PcInstance player, int val) + { + if (Config.DEBUG) + { + _log.warning("doTeleport(L2PcInstance player, int val) is called"); + } + L2TeleportLocation list = TeleportLocationTable.getInstance().getTemplate(val); + if (list != null) + { + if (player.isCombatFlagEquipped()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_TELEPORT_WHILE_IN_POSSESSION_OF_A_WARD)); + return; + } + else if (player.destroyItemByItemId("Teleport", list.getItemId(), list.getPrice(), this, true)) + { + if (Config.DEBUG) + { + _log.warning("Teleporting player " + player.getName() + " for CH to new location: " + list.getLocX() + ":" + list.getLocY() + ":" + list.getLocZ()); + } + player.teleToLocation(list.getLocX(), list.getLocY(), list.getLocZ()); + } + } + else + { + _log.warning("No teleport destination with id:" + val); + } + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + private void revalidateDeco(L2PcInstance player) + { + ClanHall ch = ClanHallManager.getInstance().getClanHallByOwner(player.getClan()); + if (ch == null) + { + return; + } + AgitDecoInfo bl = new AgitDecoInfo(ch); + player.sendPacket(bl); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClanTraderInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClanTraderInstance.java new file mode 100644 index 00000000..f6a17f93 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClanTraderInstance.java @@ -0,0 +1,146 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.PledgeShowInfoUpdate; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2ClanTraderInstance extends L2Npc +{ + public L2ClanTraderInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2ClanTraderInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + + if (command.equalsIgnoreCase("crp")) + { + if (player.getClan().getLevel() > 4) + { + html.setFile(player.getHtmlPrefix(), "data/html/clantrader/" + getNpcId() + "-2.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clantrader/" + getNpcId() + "-1.htm"); + } + + sendHtmlMessage(player, html); + return; + } + else if (command.startsWith("exchange")) + { + int itemId = Integer.parseInt(command.substring(9).trim()); + + int reputation = 0; + int itemCount = 0; + + L2ItemInstance item = player.getInventory().getItemByItemId(itemId); + long playerItemCount = item == null ? 0 : item.getCount(); + + switch (itemId) + { + case 9911: + reputation = Config.BLOODALLIANCE_POINTS; + itemCount = 1; + break; + case 9910: + reputation = Config.BLOODOATH_POINTS; + itemCount = 10; + break; + case 9912: + reputation = Config.KNIGHTSEPAULETTE_POINTS; + itemCount = 100; + break; + } + + if (playerItemCount >= itemCount) + { + player.destroyItemByItemId("exchange", itemId, itemCount, player, true); + + player.getClan().addReputationScore(reputation, true); + player.getClan().broadcastToOnlineMembers(new PledgeShowInfoUpdate(player.getClan())); + + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.CLAN_ADDED_S1S_POINTS_TO_REPUTATION_SCORE); + sm.addNumber(reputation); + player.sendPacket(sm); + + html.setFile(player.getHtmlPrefix(), "data/html/clantrader/" + getNpcId() + "-ExchangeSuccess.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/clantrader/" + getNpcId() + "-ExchangeFailed.htm"); + } + + sendHtmlMessage(player, html); + return; + } + else + { + super.onBypassFeedback(player, command); + } + } + + private void sendHtmlMessage(L2PcInstance player, NpcHtmlMessage html) + { + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + String filename = "data/html/clantrader/" + getNpcId() + "-no.htm"; + + if (player.isClanLeader()) + { + filename = "data/html/clantrader/" + getNpcId() + ".htm"; + } + + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/clantrader/" + pom + ".htm"; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClassMasterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClassMasterInstance.java new file mode 100644 index 00000000..4a34922e --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ClassMasterInstance.java @@ -0,0 +1,498 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.datatables.CharTemplateTable; +import com.l2jserver.gameserver.datatables.ItemTable; +import com.l2jserver.gameserver.instancemanager.QuestManager; +import com.l2jserver.gameserver.model.base.ClassId; +import com.l2jserver.gameserver.model.quest.Quest; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.network.serverpackets.TutorialCloseHtml; +import com.l2jserver.gameserver.network.serverpackets.TutorialShowHtml; +import com.l2jserver.gameserver.network.serverpackets.TutorialShowQuestionMark; +import com.l2jserver.gameserver.network.serverpackets.UserInfo; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.StringUtil; + +/** + * This class ... + * @version $Revision: 1.4.2.1.2.7 $ $Date: 2005/03/27 15:29:32 $ + */ +public final class L2ClassMasterInstance extends L2MerchantInstance +{ + /** + * @param template + */ + public L2ClassMasterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2ClassMasterInstance); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/classmaster/" + pom + ".htm"; + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if (command.startsWith("1stClass")) + { + showHtmlMenu(player, getObjectId(), 1); + } + else if (command.startsWith("2ndClass")) + { + showHtmlMenu(player, getObjectId(), 2); + } + else if (command.startsWith("3rdClass")) + { + showHtmlMenu(player, getObjectId(), 3); + } + else if (command.startsWith("change_class")) + { + int val = Integer.parseInt(command.substring(13)); + + if (checkAndChangeClass(player, val)) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/classmaster/ok.htm"); + html.replace("%name%", CharTemplateTable.getInstance().getClassNameById(val)); + player.sendPacket(html); + } + } + else if (command.startsWith("become_noble")) + { + if (!player.isNoble()) + { + player.setNoble(true); + player.sendPacket(new UserInfo(player)); + player.sendPacket(new ExBrExtraUserInfo(player)); + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/classmaster/nobleok.htm"); + player.sendPacket(html); + } + } + else if (command.startsWith("learn_skills")) + { + player.giveAvailableSkills(Config.AUTO_LEARN_FS_SKILLS, true); + } + else if (command.startsWith("increase_clan_level")) + { + if (!player.isClanLeader()) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/classmaster/noclanleader.htm"); + player.sendPacket(html); + } + else if (player.getClan().getLevel() >= 5) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/classmaster/noclanlevel.htm"); + player.sendPacket(html); + } + else + { + player.getClan().changeLevel(5); + } + } + else + { + super.onBypassFeedback(player, command); + } + } + + public static final void onTutorialLink(L2PcInstance player, String request) + { + if (!Config.ALTERNATE_CLASS_MASTER || (request == null) || !request.startsWith("CO")) + { + return; + } + + if (!player.getFloodProtectors().getServerBypass().tryPerformAction("changeclass")) + { + return; + } + + try + { + int val = Integer.parseInt(request.substring(2)); + checkAndChangeClass(player, val); + } + catch (NumberFormatException e) + { + } + player.sendPacket(new TutorialCloseHtml()); + } + + public static final void onTutorialQuestionMark(L2PcInstance player, int number) + { + if (!Config.ALTERNATE_CLASS_MASTER || (number != 1001)) + { + return; + } + + showTutorialHtml(player); + } + + public static final void showQuestionMark(L2PcInstance player) + { + if (!Config.ALTERNATE_CLASS_MASTER) + { + return; + } + + final ClassId classId = player.getClassId(); + if (getMinLevel(classId.level()) > player.getLevel()) + { + return; + } + + if (!Config.CLASS_MASTER_SETTINGS.isAllowed(classId.level() + 1)) + { + return; + } + + player.sendPacket(new TutorialShowQuestionMark(1001)); + } + + private static final void showHtmlMenu(L2PcInstance player, int objectId, int level) + { + NpcHtmlMessage html = new NpcHtmlMessage(objectId); + + if (!Config.ALLOW_CLASS_MASTERS) + { + html.setFile(player.getHtmlPrefix(), "data/html/classmaster/disabled.htm"); + } + else if (!Config.CLASS_MASTER_SETTINGS.isAllowed(level)) + { + int jobLevel = player.getClassId().level(); + final StringBuilder sb = new StringBuilder(100); + sb.append(""); + switch (jobLevel) + { + case 0: + if (Config.CLASS_MASTER_SETTINGS.isAllowed(1)) + { + sb.append("Come back here when you reached level 20 to change your class.
    "); + } + else if (Config.CLASS_MASTER_SETTINGS.isAllowed(2)) + { + sb.append("Come back after your first occupation change.
    "); + } + else if (Config.CLASS_MASTER_SETTINGS.isAllowed(3)) + { + sb.append("Come back after your second occupation change.
    "); + } + else + { + sb.append("I can't change your occupation.
    "); + } + break; + case 1: + if (Config.CLASS_MASTER_SETTINGS.isAllowed(2)) + { + sb.append("Come back here when you reached level 40 to change your class.
    "); + } + else if (Config.CLASS_MASTER_SETTINGS.isAllowed(3)) + { + sb.append("Come back after your second occupation change.
    "); + } + else + { + sb.append("I can't change your occupation.
    "); + } + break; + case 2: + if (Config.CLASS_MASTER_SETTINGS.isAllowed(3)) + { + sb.append("Come back here when you reached level 76 to change your class.
    "); + } + else + { + sb.append("I can't change your occupation.
    "); + } + break; + case 3: + sb.append("There is no class change available for you anymore.
    "); + break; + } + sb.append(""); + html.setHtml(sb.toString()); + } + else + { + final ClassId currentClassId = player.getClassId(); + if (currentClassId.level() >= level) + { + html.setFile(player.getHtmlPrefix(), "data/html/classmaster/nomore.htm"); + } + else + { + final int minLevel = getMinLevel(currentClassId.level()); + if ((player.getLevel() >= minLevel) || Config.ALLOW_ENTIRE_TREE) + { + final StringBuilder menu = new StringBuilder(100); + for (ClassId cid : ClassId.values()) + { + if ((cid == ClassId.inspector) && (player.getTotalSubClasses() < 2)) + { + continue; + } + if (validateClassId(currentClassId, cid) && (cid.level() == level)) + { + StringUtil.append(menu, "", CharTemplateTable.getInstance().getClassNameById(cid.getId()), "
    "); + } + } + + if (menu.length() > 0) + { + html.setFile(player.getHtmlPrefix(), "data/html/classmaster/template.htm"); + html.replace("%name%", CharTemplateTable.getInstance().getClassNameById(currentClassId.getId())); + html.replace("%menu%", menu.toString()); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/classmaster/comebacklater.htm"); + html.replace("%level%", String.valueOf(getMinLevel(level - 1))); + } + } + else + { + if (minLevel < Integer.MAX_VALUE) + { + html.setFile(player.getHtmlPrefix(), "data/html/classmaster/comebacklater.htm"); + html.replace("%level%", String.valueOf(minLevel)); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/classmaster/nomore.htm"); + } + } + } + } + + html.replace("%objectId%", String.valueOf(objectId)); + html.replace("%req_items%", getRequiredItems(level)); + player.sendPacket(html); + } + + private static final void showTutorialHtml(L2PcInstance player) + { + final ClassId currentClassId = player.getClassId(); + if ((getMinLevel(currentClassId.level()) > player.getLevel()) && !Config.ALLOW_ENTIRE_TREE) + { + return; + } + + String msg = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/classmaster/tutorialtemplate.htm"); + + msg = msg.replaceAll("%name%", CharTemplateTable.getInstance().getClassNameById(currentClassId.getId())); + + final StringBuilder menu = new StringBuilder(100); + for (ClassId cid : ClassId.values()) + { + if ((cid == ClassId.inspector) && (player.getTotalSubClasses() < 2)) + { + continue; + } + if (validateClassId(currentClassId, cid)) + { + StringUtil.append(menu, "", CharTemplateTable.getInstance().getClassNameById(cid.getId()), "
    "); + } + } + + msg = msg.replaceAll("%menu%", menu.toString()); + msg = msg.replace("%req_items%", getRequiredItems(currentClassId.level() + 1)); + player.sendPacket(new TutorialShowHtml(msg)); + } + + private static final boolean checkAndChangeClass(L2PcInstance player, int val) + { + final ClassId currentClassId = player.getClassId(); + if ((getMinLevel(currentClassId.level()) > player.getLevel()) && !Config.ALLOW_ENTIRE_TREE) + { + return false; + } + + if (!validateClassId(currentClassId, val)) + { + return false; + } + + int newJobLevel = currentClassId.level() + 1; + + // Weight/Inventory check + if (!Config.CLASS_MASTER_SETTINGS.getRewardItems(newJobLevel).isEmpty()) + { + if ((player.getWeightPenalty() >= 3) || ((player.getInventoryLimit() * 0.8) <= player.getInventory().getSize(false))) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.INVENTORY_LESS_THAN_80_PERCENT)); + return false; + } + } + + // check if player have all required items for class transfer + for (int _itemId : Config.CLASS_MASTER_SETTINGS.getRequireItems(newJobLevel).keys()) + { + int _count = Config.CLASS_MASTER_SETTINGS.getRequireItems(newJobLevel).get(_itemId); + if (player.getInventory().getInventoryItemCount(_itemId, -1) < _count) + { + player.sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS); + return false; + } + } + + // get all required items for class transfer + for (int _itemId : Config.CLASS_MASTER_SETTINGS.getRequireItems(newJobLevel).keys()) + { + int _count = Config.CLASS_MASTER_SETTINGS.getRequireItems(newJobLevel).get(_itemId); + if (!player.destroyItemByItemId("ClassMaster", _itemId, _count, player, true)) + { + return false; + } + } + + // reward player with items + for (int _itemId : Config.CLASS_MASTER_SETTINGS.getRewardItems(newJobLevel).keys()) + { + int _count = Config.CLASS_MASTER_SETTINGS.getRewardItems(newJobLevel).get(_itemId); + player.addItem("ClassMaster", _itemId, _count, player, true); + } + + player.setClassId(val); + + if (player.isSubClassActive()) + { + player.getSubClasses().get(player.getClassIndex()).setClassId(player.getActiveClass()); + } + else + { + player.setBaseClass(player.getActiveClass()); + } + + Quest q = QuestManager.getInstance().getQuest("SkillTransfer"); + if (q != null) + { + q.startQuestTimer("givePormanders", 1, null, player); + } + + player.broadcastUserInfo(); + + if (Config.CLASS_MASTER_SETTINGS.isAllowed(player.getClassId().level() + 1) && Config.ALTERNATE_CLASS_MASTER && (((player.getClassId().level() == 1) && (player.getLevel() >= 40)) || ((player.getClassId().level() == 2) && (player.getLevel() >= 76)))) + { + showQuestionMark(player); + } + + return true; + } + + /** + * Returns minimum player level required for next class transfer + * @param level - current skillId level (0 - start, 1 - first, etc) + */ + private static final int getMinLevel(int level) + { + switch (level) + { + case 0: + return 20; + case 1: + return 40; + case 2: + return 76; + default: + return Integer.MAX_VALUE; + } + } + + /** + * Returns true if class change is possible + * @param oldCID current player ClassId + * @param val new class index + * @return + */ + private static final boolean validateClassId(ClassId oldCID, int val) + { + try + { + return validateClassId(oldCID, ClassId.values()[val]); + } + catch (Exception e) + { + // possible ArrayOutOfBoundsException + } + return false; + } + + /** + * Returns true if class change is possible + * @param oldCID current player ClassId + * @param newCID new ClassId + * @return true if class change is possible + */ + private static final boolean validateClassId(ClassId oldCID, ClassId newCID) + { + if ((newCID == null) || (newCID.getRace() == null)) + { + return false; + } + + if (oldCID.equals(newCID.getParent())) + { + return true; + } + + if (Config.ALLOW_ENTIRE_TREE && newCID.childOf(oldCID)) + { + return true; + } + + return false; + } + + private static String getRequiredItems(int level) + { + if ((Config.CLASS_MASTER_SETTINGS.getRequireItems(level) == null) || Config.CLASS_MASTER_SETTINGS.getRequireItems(level).isEmpty()) + { + return "none"; + } + StringBuilder sb = new StringBuilder(); + for (int _itemId : Config.CLASS_MASTER_SETTINGS.getRequireItems(level).keys()) + { + int _count = Config.CLASS_MASTER_SETTINGS.getRequireItems(level).get(_itemId); + sb.append("" + _count + "" + ItemTable.getInstance().getTemplate(_itemId).getName() + ""); + } + return sb.toString(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ControlTowerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ControlTowerInstance.java new file mode 100644 index 00000000..bc10d3b2 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ControlTowerInstance.java @@ -0,0 +1,153 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.List; + +import javolution.util.FastList; + +import com.l2jserver.gameserver.GeoData; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.model.L2Spawn; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2ControlTowerInstance extends L2Npc +{ + private List _guards; + + public L2ControlTowerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2ControlTowerInstance); + setIsInvul(false); + } + + @Override + public boolean isAttackable() + { + // Attackable during siege by attacker only + return ((getCastle() != null) && (getCastle().getCastleId() > 0) && getCastle().getSiege().getIsInProgress()); + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + // Attackable during siege by attacker only + return ((attacker != null) && (attacker instanceof L2PcInstance) && (getCastle() != null) && (getCastle().getCastleId() > 0) && getCastle().getSiege().getIsInProgress() && getCastle().getSiege().checkIsAttacker(((L2PcInstance) attacker).getClan())); + } + + @Override + public void onForcedAttack(L2PcInstance player) + { + onAction(player); + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if (!canTarget(player)) + { + return; + } + + // Check if the L2PcInstance already target the L2NpcInstance + if (this != player.getTarget()) + { + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player + MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()); + player.sendPacket(my); + + // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_HP, (int) getStatus().getCurrentHp()); + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); + player.sendPacket(su); + + // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + if (isAutoAttackable(player) && (Math.abs(player.getZ() - getZ()) < 100 // Less then max height difference, delete check when geo + ) && GeoData.getInstance().canSeeTarget(player, this)) + { + // Notify the L2PcInstance AI with AI_INTENTION_INTERACT + player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); + } + } + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public boolean doDie(L2Character killer) + { + if (getCastle().getSiege().getIsInProgress()) + { + getCastle().getSiege().killedCT(this); + + if ((_guards != null) && !_guards.isEmpty()) + { + for (L2Spawn spawn : _guards) + { + if (spawn == null) + { + continue; + } + try + { + spawn.stopRespawn(); + // spawn.getLastSpawn().doDie(spawn.getLastSpawn()); + } + catch (Exception e) + { + } + } + _guards.clear(); + } + } + return super.doDie(killer); + } + + public void registerGuard(L2Spawn guard) + { + getGuards().add(guard); + } + + public final List getGuards() + { + if (_guards == null) + { + synchronized (this) + { + if (_guards == null) + { + _guards = new FastList<>(); + } + } + } + + return _guards; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ControllableAirShipInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ControllableAirShipInstance.java new file mode 100644 index 00000000..8a9efeee --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ControllableAirShipInstance.java @@ -0,0 +1,350 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.concurrent.Future; +import java.util.logging.Level; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.idfactory.IdFactory; +import com.l2jserver.gameserver.model.actor.stat.ControllableAirShipStat; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.DeleteObject; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2CharTemplate; + +public class L2ControllableAirShipInstance extends L2AirShipInstance +{ + private static final int HELM = 13556; + private static final int LOW_FUEL = 40; + + private int _fuel = 0; + private int _maxFuel = 0; + + private final int _ownerId; + private int _helmId; + private L2PcInstance _captain = null; + + private Future _consumeFuelTask; + private Future _checkTask; + + public L2ControllableAirShipInstance(int objectId, L2CharTemplate template, int ownerId) + { + super(objectId, template); + setInstanceType(InstanceType.L2ControllableAirShipInstance); + _ownerId = ownerId; + _helmId = IdFactory.getInstance().getNextId(); // not forget to release ! + } + + @Override + public ControllableAirShipStat getStat() + { + return (ControllableAirShipStat) super.getStat(); + } + + @Override + public void initCharStat() + { + setStat(new ControllableAirShipStat(this)); + } + + @Override + public boolean canBeControlled() + { + return super.canBeControlled() && !isInDock(); + } + + @Override + public boolean isOwner(L2PcInstance player) + { + if (_ownerId == 0) + { + return false; + } + + return (player.getClanId() == _ownerId) || (player.getObjectId() == _ownerId); + } + + @Override + public int getOwnerId() + { + return _ownerId; + } + + @Override + public boolean isCaptain(L2PcInstance player) + { + return (_captain != null) && (player == _captain); + } + + @Override + public int getCaptainId() + { + return _captain != null ? _captain.getObjectId() : 0; + } + + @Override + public int getHelmObjectId() + { + return _helmId; + } + + @Override + public int getHelmItemId() + { + return HELM; + } + + @Override + public boolean setCaptain(L2PcInstance player) + { + if (player == null) + { + _captain = null; + } + else + { + if ((_captain == null) && (player.getAirShip() == this)) + { + final int x = player.getInVehiclePosition().getX() - 0x16e; + final int y = player.getInVehiclePosition().getY(); + final int z = player.getInVehiclePosition().getZ() - 0x6b; + if (((x * x) + (y * y) + (z * z)) > 2500) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANT_CONTROL_TOO_FAR)); + return false; + } + // TODO: Missing message ID: 2739 Message: You cannot control the helm because you do not meet the requirements. + else if (player.isInCombat()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_CONTROL_THE_HELM_WHILE_IN_A_BATTLE)); + return false; + } + else if (player.isSitting()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_CONTROL_THE_HELM_WHILE_IN_A_SITTING_POSITION)); + return false; + } + else if (player.isParalyzed()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_CONTROL_THE_HELM_WHILE_YOU_ARE_PETRIFIED)); + return false; + } + else if (player.isCursedWeaponEquipped()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_CONTROL_THE_HELM_WHILE_A_CURSED_WEAPON_IS_EQUIPPED)); + return false; + } + else if (player.isFishing()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_CONTROL_THE_HELM_WHILE_FISHING)); + return false; + } + else if (player.isDead() || player.isFakeDeath()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_CONTROL_THE_HELM_WHEN_YOU_ARE_DEAD)); + return false; + } + else if (player.isCastingNow()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_CONTROL_THE_HELM_WHILE_USING_A_SKILL)); + return false; + } + else if (player.isTransformed()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_CONTROL_THE_HELM_WHILE_TRANSFORMED)); + return false; + } + else if (player.isCombatFlagEquipped()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_CONTROL_THE_HELM_WHILE_HOLDING_A_FLAG)); + return false; + } + else if (player.isInDuel()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_CONTROL_THE_HELM_WHILE_IN_A_DUEL)); + return false; + } + _captain = player; + player.broadcastUserInfo(); + } + else + { + return false; + } + } + updateAbnormalEffect(); + return true; + } + + @Override + public int getFuel() + { + return _fuel; + } + + @Override + public void setFuel(int f) + { + + final int old = _fuel; + if (f < 0) + { + _fuel = 0; + } + else if (f > _maxFuel) + { + _fuel = _maxFuel; + } + else + { + _fuel = f; + } + + if ((_fuel == 0) && (old > 0)) + { + broadcastToPassengers(SystemMessage.getSystemMessage(SystemMessageId.THE_AIRSHIP_FUEL_RUN_OUT)); + } + else if (_fuel < LOW_FUEL) + { + broadcastToPassengers(SystemMessage.getSystemMessage(SystemMessageId.THE_AIRSHIP_FUEL_SOON_RUN_OUT)); + } + } + + @Override + public int getMaxFuel() + { + return _maxFuel; + } + + @Override + public void setMaxFuel(int mf) + { + _maxFuel = mf; + } + + @Override + public void oustPlayer(L2PcInstance player) + { + if (player == _captain) + { + setCaptain(null); // no need to broadcast userinfo here + } + + super.oustPlayer(player); + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + player.sendPacket(new MyTargetSelected(_helmId, 0)); + super.onAction(player, interact); + } + + @Override + public void onSpawn() + { + super.onSpawn(); + _checkTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new CheckTask(), 60000, 10000); + _consumeFuelTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new ConsumeFuelTask(), 60000, 60000); + } + + @Override + public void deleteMe() + { + super.deleteMe(); + + if (_checkTask != null) + { + _checkTask.cancel(false); + _checkTask = null; + } + if (_consumeFuelTask != null) + { + _consumeFuelTask.cancel(false); + _consumeFuelTask = null; + } + + try + { + broadcastPacket(new DeleteObject(_helmId)); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Failed decayMe():" + e.getMessage()); + } + } + + @Override + public void refreshID() + { + super.refreshID(); + IdFactory.getInstance().releaseId(_helmId); + _helmId = IdFactory.getInstance().getNextId(); + } + + @Override + public void sendInfo(L2PcInstance activeChar) + { + super.sendInfo(activeChar); + if (_captain != null) + { + _captain.sendInfo(activeChar); + } + } + + protected final class ConsumeFuelTask implements Runnable + { + @Override + public void run() + { + int fuel = getFuel(); + if (fuel > 0) + { + fuel -= 10; + if (fuel < 0) + { + fuel = 0; + } + + setFuel(fuel); + updateAbnormalEffect(); + } + } + } + + protected final class CheckTask implements Runnable + { + @Override + public void run() + { + if (isVisible() && isEmpty() && !isInDock()) + { + // deleteMe() can't be called from CheckTask because task should not cancel itself + ThreadPoolManager.getInstance().executeTask(new DecayTask()); + } + } + } + + protected final class DecayTask implements Runnable + { + @Override + public void run() + { + deleteMe(); + } + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ControllableMobInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ControllableMobInstance.java new file mode 100644 index 00000000..02ef9be4 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ControllableMobInstance.java @@ -0,0 +1,127 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.ai.L2CharacterAI; +import com.l2jserver.gameserver.ai.L2ControllableMobAI; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author littlecrow + */ +public class L2ControllableMobInstance extends L2MonsterInstance +{ + private boolean _isInvul; + private L2ControllableMobAI _aiBackup; // to save ai, avoiding beeing detached + + protected class ControllableAIAcessor extends AIAccessor + { + @Override + public void detachAI() + { + // do nothing, AI of controllable mobs can't be detached automatically + } + } + + @Override + public boolean isAggressive() + { + return true; + } + + @Override + public int getAggroRange() + { + // force mobs to be aggro + return 500; + } + + public L2ControllableMobInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2ControllableMobInstance); + } + + @Override + public L2CharacterAI getAI() + { + L2CharacterAI ai = _ai; // copy handle + if (ai == null) + { + synchronized (this) + { + if ((_ai == null) && (_aiBackup == null)) + { + _ai = new L2ControllableMobAI(new ControllableAIAcessor()); + _aiBackup = (L2ControllableMobAI) _ai; + } + else + { + _ai = _aiBackup; + } + return _ai; + } + } + return ai; + } + + @Override + public boolean isInvul() + { + return _isInvul; + } + + public void setInvul(boolean isInvul) + { + _isInvul = isInvul; + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + + removeAI(); + return true; + } + + @Override + public void deleteMe() + { + removeAI(); + super.deleteMe(); + } + + /** + * Definitively remove AI + */ + protected void removeAI() + { + synchronized (this) + { + if (_aiBackup != null) + { + _aiBackup.setIntention(CtrlIntention.AI_INTENTION_IDLE); + _aiBackup = null; + _ai = null; + } + } + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CubicInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CubicInstance.java new file mode 100644 index 00000000..46ff6797 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2CubicInstance.java @@ -0,0 +1,1156 @@ +/* + * This program is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If + * not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.List; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.CtrlEvent; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.handler.ISkillHandler; +import com.l2jserver.gameserver.handler.SkillHandler; +import com.l2jserver.gameserver.instancemanager.DuelManager; +import com.l2jserver.gameserver.model.L2Effect; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Party; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.entity.TvTEvent; +import com.l2jserver.gameserver.model.entity.TvTEventTeam; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.skills.Formulas; +import com.l2jserver.gameserver.skills.l2skills.L2SkillDrain; +import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager; +import com.l2jserver.gameserver.templates.skills.L2SkillType; +import com.l2jserver.util.Rnd; + +public class L2CubicInstance +{ + protected static final Logger _log = Logger.getLogger(L2CubicInstance.class.getName()); + + // Type of Cubics + public static final int STORM_CUBIC = 1; + public static final int VAMPIRIC_CUBIC = 2; + public static final int LIFE_CUBIC = 3; + public static final int VIPER_CUBIC = 4; + public static final int POLTERGEIST_CUBIC = 5; + public static final int BINDING_CUBIC = 6; + public static final int AQUA_CUBIC = 7; + public static final int SPARK_CUBIC = 8; + public static final int ATTRACT_CUBIC = 9; + public static final int SMART_CUBIC_EVATEMPLAR = 10; + public static final int SMART_CUBIC_SHILLIENTEMPLAR = 11; + public static final int SMART_CUBIC_ARCANALORD = 12; + public static final int SMART_CUBIC_ELEMENTALMASTER = 13; + public static final int SMART_CUBIC_SPECTRALMASTER = 14; + + // Max range of cubic skills + // TODO: Check/fix the max range + public static final int MAX_MAGIC_RANGE = 900; + + // Cubic skills + public static final int SKILL_CUBIC_HEAL = 4051; + public static final int SKILL_CUBIC_CURE = 5579; + + protected L2PcInstance _owner; + protected L2Character _target; + + protected int _id; + protected int _matk; + protected int _activationtime; + protected int _activationchance; + protected int _maxcount; + protected int _currentcount; + protected boolean _active; + private final boolean _givenByOther; + + protected List _skills = new FastList<>(); + + private Future _disappearTask; + private Future _actionTask; + + public L2CubicInstance(L2PcInstance owner, int id, int level, int mAtk, int activationtime, int activationchance, int maxcount, int totallifetime, boolean givenByOther) + { + _owner = owner; + _id = id; + _matk = mAtk; + _activationtime = activationtime * 1000; + _activationchance = activationchance; + _maxcount = maxcount; + _currentcount = 0; + _active = false; + _givenByOther = givenByOther; + + switch (_id) + { + case STORM_CUBIC: + _skills.add(SkillTable.getInstance().getInfo(4049, level)); + break; + case VAMPIRIC_CUBIC: + _skills.add(SkillTable.getInstance().getInfo(4050, level)); + break; + case LIFE_CUBIC: + _skills.add(SkillTable.getInstance().getInfo(4051, level)); + doAction(); + break; + case VIPER_CUBIC: + _skills.add(SkillTable.getInstance().getInfo(4052, level)); + break; + case POLTERGEIST_CUBIC: + _skills.add(SkillTable.getInstance().getInfo(4053, level)); + _skills.add(SkillTable.getInstance().getInfo(4054, level)); + _skills.add(SkillTable.getInstance().getInfo(4055, level)); + break; + case BINDING_CUBIC: + _skills.add(SkillTable.getInstance().getInfo(4164, level)); + break; + case AQUA_CUBIC: + _skills.add(SkillTable.getInstance().getInfo(4165, level)); + break; + case SPARK_CUBIC: + _skills.add(SkillTable.getInstance().getInfo(4166, level)); + break; + case ATTRACT_CUBIC: + _skills.add(SkillTable.getInstance().getInfo(5115, level)); + _skills.add(SkillTable.getInstance().getInfo(5116, level)); + break; + case SMART_CUBIC_ARCANALORD: + // _skills.add(SkillTable.getInstance().getInfo(4049,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4050,7)); no animation + _skills.add(SkillTable.getInstance().getInfo(4051, 7)); // have animation + // _skills.add(SkillTable.getInstance().getInfo(4052,6)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4053,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4054,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4055,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4164,9)); no animation + _skills.add(SkillTable.getInstance().getInfo(4165, 9)); // have animation + // _skills.add(SkillTable.getInstance().getInfo(4166,9)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5115,4)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5116,4)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5579,4)); no need to add to the + // cubic skills list + break; + case SMART_CUBIC_ELEMENTALMASTER: + _skills.add(SkillTable.getInstance().getInfo(4049, 8)); // have animation + // _skills.add(SkillTable.getInstance().getInfo(4050,7)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4051,7)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4052,6)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4053,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4054,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4055,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4164,9)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4165,9)); no animation + _skills.add(SkillTable.getInstance().getInfo(4166, 9)); // have animation + // _skills.add(SkillTable.getInstance().getInfo(5115,4)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5116,4)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5579,4)); no need to add to the + // cubic skills list + break; + case SMART_CUBIC_SPECTRALMASTER: + _skills.add(SkillTable.getInstance().getInfo(4049, 8)); // have animation + // _skills.add(SkillTable.getInstance().getInfo(4050,7)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4051,7)); no animation + _skills.add(SkillTable.getInstance().getInfo(4052, 6)); // have animation + // _skills.add(SkillTable.getInstance().getInfo(4053,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4054,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4055,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4164,9)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4165,9)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4166,9)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5115,4)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5116,4)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5579,4)); no need to add to the + // cubic skills list + break; + case SMART_CUBIC_EVATEMPLAR: + // _skills.add(SkillTable.getInstance().getInfo(4049,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4050,7)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4051,7)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4052,6)); no animation + _skills.add(SkillTable.getInstance().getInfo(4053, 8)); // have animation + // _skills.add(SkillTable.getInstance().getInfo(4054,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4055,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4164,9)); no animation + _skills.add(SkillTable.getInstance().getInfo(4165, 9)); // have animation + // _skills.add(SkillTable.getInstance().getInfo(4166,9)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5115,4)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5116,4)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5579,4)); no need to add to the + // cubic skills list + break; + case SMART_CUBIC_SHILLIENTEMPLAR: + _skills.add(SkillTable.getInstance().getInfo(4049, 8)); // have animation + // _skills.add(SkillTable.getInstance().getInfo(4050,7)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4051,7)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4052,6)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4053,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4054,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4055,8)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4164,9)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4165,9)); no animation + // _skills.add(SkillTable.getInstance().getInfo(4166,9)); no animation + _skills.add(SkillTable.getInstance().getInfo(5115, 4)); // have animation + // _skills.add(SkillTable.getInstance().getInfo(5116,4)); no animation + // _skills.add(SkillTable.getInstance().getInfo(5579,4)); no need to add to the + // cubic skills list + break; + } + _disappearTask = ThreadPoolManager.getInstance().scheduleGeneral(new Disappear(), totallifetime); // disappear + } + + public synchronized void doAction() + { + if (_active) + { + return; + } + _active = true; + + switch (_id) + { + case AQUA_CUBIC: + case BINDING_CUBIC: + case SPARK_CUBIC: + case STORM_CUBIC: + case POLTERGEIST_CUBIC: + case VAMPIRIC_CUBIC: + case VIPER_CUBIC: + case ATTRACT_CUBIC: + case SMART_CUBIC_ARCANALORD: + case SMART_CUBIC_ELEMENTALMASTER: + case SMART_CUBIC_SPECTRALMASTER: + case SMART_CUBIC_EVATEMPLAR: + case SMART_CUBIC_SHILLIENTEMPLAR: + _actionTask = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new Action(_activationchance), 0, _activationtime); + break; + case LIFE_CUBIC: + _actionTask = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new Heal(), 0, _activationtime); + break; + } + } + + public int getId() + { + return _id; + } + + public L2PcInstance getOwner() + { + return _owner; + } + + public final int getMCriticalHit(L2Character target, L2Skill skill) + { + // TODO: Temporary now mcrit for cubics is the baseMCritRate of its owner + return _owner.getTemplate().baseMCritRate; + } + + public int getMAtk() + { + return _matk; + } + + public void stopAction() + { + _target = null; + if (_actionTask != null) + { + _actionTask.cancel(true); + _actionTask = null; + } + _active = false; + } + + public void cancelDisappear() + { + if (_disappearTask != null) + { + _disappearTask.cancel(true); + _disappearTask = null; + } + } + + /** this sets the enemy target for a cubic */ + public void getCubicTarget() + { + try + { + _target = null; + L2Object ownerTarget = _owner.getTarget(); + if (ownerTarget == null) + { + return; + } + // TvT event targeting + if (TvTEvent.isStarted() && TvTEvent.isPlayerParticipant(_owner.getObjectId())) + { + TvTEventTeam enemyTeam = TvTEvent.getParticipantEnemyTeam(_owner.getObjectId()); + + if (ownerTarget.getActingPlayer() != null) + { + L2PcInstance target = ownerTarget.getActingPlayer(); + if (enemyTeam.containsPlayer(target.getObjectId()) && !(target.isDead())) + { + _target = (L2Character) ownerTarget; + } + } + return; + } + // Duel targeting + if (_owner.isInDuel()) + { + L2PcInstance PlayerA = DuelManager.getInstance().getDuel(_owner.getDuelId()).getPlayerA(); + L2PcInstance PlayerB = DuelManager.getInstance().getDuel(_owner.getDuelId()).getPlayerB(); + + if (DuelManager.getInstance().getDuel(_owner.getDuelId()).isPartyDuel()) + { + L2Party partyA = PlayerA.getParty(); + L2Party partyB = PlayerB.getParty(); + L2Party partyEnemy = null; + + if (partyA != null) + { + if (partyA.getPartyMembers().contains(_owner)) + { + if (partyB != null) + { + partyEnemy = partyB; + } + else + { + _target = PlayerB; + } + } + else + { + partyEnemy = partyA; + } + } + else + { + if (PlayerA == _owner) + { + if (partyB != null) + { + partyEnemy = partyB; + } + else + { + _target = PlayerB; + } + } + else + { + _target = PlayerA; + } + } + if ((_target == PlayerA) || (_target == PlayerB)) + { + if (_target == ownerTarget) + { + return; + } + } + if (partyEnemy != null) + { + if (partyEnemy.getPartyMembers().contains(ownerTarget)) + { + _target = (L2Character) ownerTarget; + } + return; + } + } + if ((PlayerA != _owner) && (ownerTarget == PlayerA)) + { + _target = PlayerA; + return; + } + if ((PlayerB != _owner) && (ownerTarget == PlayerB)) + { + _target = PlayerB; + return; + } + _target = null; + return; + } + // Olympiad targeting + if (_owner.isInOlympiadMode()) + { + if (_owner.isOlympiadStart()) + { + if (ownerTarget instanceof L2Playable) + { + final L2PcInstance targetPlayer = ownerTarget.getActingPlayer(); + if ((targetPlayer != null) && (targetPlayer.getOlympiadGameId() == _owner.getOlympiadGameId()) && (targetPlayer.getOlympiadSide() != _owner.getOlympiadSide())) + { + _target = (L2Character) ownerTarget; + } + } + } + return; + } + // test owners target if it is valid then use it + if ((ownerTarget instanceof L2Character) && (ownerTarget != _owner.getPet()) && (ownerTarget != _owner)) + { + // target mob which has aggro on you or your summon + if (ownerTarget instanceof L2Attackable) + { + if ((((L2Attackable) ownerTarget).getAggroList().get(_owner) != null) && !((L2Attackable) ownerTarget).isDead()) + { + _target = (L2Character) ownerTarget; + return; + } + if (_owner.getPet() != null) + { + if ((((L2Attackable) ownerTarget).getAggroList().get(_owner.getPet()) != null) && !((L2Attackable) ownerTarget).isDead()) + { + _target = (L2Character) ownerTarget; + return; + } + } + } + + // get target in pvp or in siege + L2PcInstance enemy = null; + + if (((_owner.getPvpFlag() > 0) && !_owner.isInsideZone(L2Character.ZONE_PEACE)) || _owner.isInsideZone(L2Character.ZONE_PVP)) + { + if (!((L2Character) ownerTarget).isDead()) + { + enemy = ownerTarget.getActingPlayer(); + } + + if (enemy != null) + { + boolean targetIt = true; + + if (_owner.getParty() != null) + { + if (_owner.getParty().getPartyMembers().contains(enemy)) + { + targetIt = false; + } + else if (_owner.getParty().getCommandChannel() != null) + { + if (_owner.getParty().getCommandChannel().getMembers().contains(enemy)) + { + targetIt = false; + } + } + } + if ((_owner.getClan() != null) && !_owner.isInsideZone(L2Character.ZONE_PVP)) + { + if (_owner.getClan().isMember(enemy.getObjectId())) + { + targetIt = false; + } + if ((_owner.getAllyId() > 0) && (enemy.getAllyId() > 0)) + { + if (_owner.getAllyId() == enemy.getAllyId()) + { + targetIt = false; + } + } + } + if ((enemy.getPvpFlag() == 0) && !enemy.isInsideZone(L2Character.ZONE_PVP)) + { + targetIt = false; + } + if (enemy.isInsideZone(L2Character.ZONE_PEACE)) + { + targetIt = false; + } + if ((_owner.getSiegeState() > 0) && (_owner.getSiegeState() == enemy.getSiegeState())) + { + targetIt = false; + } + if (!enemy.isVisible()) + { + targetIt = false; + } + + if (targetIt) + { + _target = enemy; + return; + } + } + } + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + + private class Action implements Runnable + { + private final int _chance; + + Action(int chance) + { + _chance = chance; + // run task + } + + @Override + public void run() + { + try + { + if (_owner.isDead() || !_owner.isOnline()) + { + stopAction(); + _owner.delCubic(_id); + _owner.broadcastUserInfo(); + cancelDisappear(); + return; + } + if (!AttackStanceTaskManager.getInstance().getAttackStanceTask(_owner)) + { + if (_owner.getPet() != null) + { + if (!AttackStanceTaskManager.getInstance().getAttackStanceTask(_owner.getPet())) + { + stopAction(); + return; + } + } + else + { + stopAction(); + return; + } + } + // The cubic has already reached its limit and it will stay idle until its lifetime ends. + if ((_maxcount > -1) && (_currentcount >= _maxcount)) + { + stopAction(); + return; + } + // Smart Cubic debuff cancel is 100% + boolean UseCubicCure = false; + L2Skill skill = null; + + if ((_id >= SMART_CUBIC_EVATEMPLAR) && (_id <= SMART_CUBIC_SPECTRALMASTER)) + { + L2Effect[] effects = _owner.getAllEffects(); + + for (L2Effect e : effects) + { + if ((e != null) && e.getSkill().isDebuff() && e.getSkill().canBeDispeled()) + { + UseCubicCure = true; + e.exit(); + } + } + } + + if (UseCubicCure) + { + // Smart Cubic debuff cancel is needed, no other skill is used in this + // activation period + MagicSkillUse msu = new MagicSkillUse(_owner, _owner, SKILL_CUBIC_CURE, 1, 0, 0); + _owner.broadcastPacket(msu); + + // The cubic has done an action, increase the currentcount + _currentcount++; + } + else if (Rnd.get(1, 100) < _chance) + { + skill = _skills.get(Rnd.get(_skills.size())); + if (skill != null) + { + if (skill.getId() == SKILL_CUBIC_HEAL) + { + // friendly skill, so we look a target in owner's party + cubicTargetForHeal(); + } + else + { + // offensive skill, we look for an enemy target + getCubicTarget(); + if (!isInCubicRange(_owner, _target)) + { + _target = null; + } + } + L2Character target = _target; // copy to avoid npe + if ((target != null) && (!target.isDead())) + { + if (Config.DEBUG) + { + _log.info("L2CubicInstance: Action.run();"); + _log.info("Cubic Id: " + _id + " Target: " + target.getName() + " distance: " + Math.sqrt(target.getDistanceSq(_owner.getX(), _owner.getY(), _owner.getZ()))); + } + + _owner.broadcastPacket(new MagicSkillUse(_owner, target, skill.getId(), skill.getLevel(), 0, 0)); + + L2SkillType type = skill.getSkillType(); + ISkillHandler handler = SkillHandler.getInstance().getSkillHandler(skill.getSkillType()); + L2Character[] targets = + { + target + }; + + if ((type == L2SkillType.PARALYZE) || (type == L2SkillType.STUN) || (type == L2SkillType.ROOT) || (type == L2SkillType.AGGDAMAGE)) + { + if (Config.DEBUG) + { + _log.info("L2CubicInstance: Action.run() handler " + type); + } + useCubicDisabler(type, L2CubicInstance.this, skill, targets); + } + else if (type == L2SkillType.MDAM) + { + if (Config.DEBUG) + { + _log.info("L2CubicInstance: Action.run() handler " + type); + } + useCubicMdam(L2CubicInstance.this, skill, targets); + } + else if ((type == L2SkillType.POISON) || (type == L2SkillType.DEBUFF) || (type == L2SkillType.DOT)) + { + if (Config.DEBUG) + { + _log.info("L2CubicInstance: Action.run() handler " + type); + } + useCubicContinuous(L2CubicInstance.this, skill, targets); + } + else if (type == L2SkillType.DRAIN) + { + if (Config.DEBUG) + { + _log.info("L2CubicInstance: Action.run() skill " + type); + } + ((L2SkillDrain) skill).useCubicSkill(L2CubicInstance.this, targets); + } + else + { + handler.useSkill(_owner, skill, targets); + if (Config.DEBUG) + { + _log.info("L2CubicInstance: Action.run(); other handler"); + } + } + + // The cubic has done an action, increase the currentcount + _currentcount++; + } + } + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } + + public void useCubicContinuous(L2CubicInstance activeCubic, L2Skill skill, L2Object[] targets) + { + for (L2Character target : (L2Character[]) targets) + { + if ((target == null) || target.isDead()) + { + continue; + } + + if (skill.isOffensive()) + { + byte shld = Formulas.calcShldUse(activeCubic.getOwner(), target, skill); + boolean acted = Formulas.calcCubicSkillSuccess(activeCubic, target, skill, shld); + if (!acted) + { + activeCubic.getOwner().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ATTACK_FAILED)); + continue; + } + + } + + // if this is a debuff let the duel manager know about it + // so the debuff can be removed after the duel + // (player & target must be in the same duel) + if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isInDuel() && (skill.getSkillType() == L2SkillType.DEBUFF) && (activeCubic.getOwner().getDuelId() == ((L2PcInstance) target).getDuelId())) + { + DuelManager dm = DuelManager.getInstance(); + for (L2Effect debuff : skill.getEffects(activeCubic.getOwner(), target)) + { + if (debuff != null) + { + dm.onBuff(((L2PcInstance) target), debuff); + } + } + } + else + { + skill.getEffects(activeCubic, target, null); + } + } + } + + public void useCubicMdam(L2CubicInstance activeCubic, L2Skill skill, L2Object[] targets) + { + for (L2Character target : (L2Character[]) targets) + { + if (target == null) + { + continue; + } + + if (target.isAlikeDead()) + { + if (target instanceof L2PcInstance) + { + target.stopFakeDeath(true); + } + else + { + continue; + } + } + + boolean mcrit = Formulas.calcMCrit(activeCubic.getMCriticalHit(target, skill)); + byte shld = Formulas.calcShldUse(activeCubic.getOwner(), target, skill); + int damage = (int) Formulas.calcMagicDam(activeCubic, target, skill, mcrit, shld); + + /* + * If target is reflecting the skill then no damage is done Ignoring vengance-like reflections + */ + if ((Formulas.calcSkillReflect(target, skill) & Formulas.SKILL_REFLECT_SUCCEED) > 0) + { + damage = 0; + } + + if (Config.DEBUG) + { + _log.info("L2SkillMdam: useCubicSkill() -> damage = " + damage); + } + + if (damage > 0) + { + // Manage attack or cast break of the target (calculating rate, sending message...) + if (!target.isRaid() && Formulas.calcAtkBreak(target, damage)) + { + target.breakAttack(); + target.breakCast(); + } + + activeCubic.getOwner().sendDamageMessage(target, damage, mcrit, false, false); + + if (skill.hasEffects()) + { + // activate attacked effects, if any + target.stopSkillEffects(skill.getId()); + if (target.getFirstEffect(skill) != null) + { + target.removeEffect(target.getFirstEffect(skill)); + } + if (Formulas.calcCubicSkillSuccess(activeCubic, target, skill, shld)) + { + skill.getEffects(activeCubic, target, null); + } + } + + target.reduceCurrentHp(damage, activeCubic.getOwner(), skill); + } + } + } + + public void useCubicDisabler(L2SkillType type, L2CubicInstance activeCubic, L2Skill skill, L2Object[] targets) + { + if (Config.DEBUG) + { + _log.info("Disablers: useCubicSkill()"); + } + + for (L2Character target : (L2Character[]) targets) + { + if ((target == null) || target.isDead()) + { + continue; + } + + byte shld = Formulas.calcShldUse(activeCubic.getOwner(), target, skill); + + switch (type) + { + case STUN: + { + if (Formulas.calcCubicSkillSuccess(activeCubic, target, skill, shld)) + { + // if this is a debuff let the duel manager know about it + // so the debuff can be removed after the duel + // (player & target must be in the same duel) + if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isInDuel() && (skill.getSkillType() == L2SkillType.DEBUFF) && (activeCubic.getOwner().getDuelId() == ((L2PcInstance) target).getDuelId())) + { + DuelManager dm = DuelManager.getInstance(); + for (L2Effect debuff : skill.getEffects(activeCubic.getOwner(), target)) + { + if (debuff != null) + { + dm.onBuff(((L2PcInstance) target), debuff); + } + } + } + else + { + skill.getEffects(activeCubic, target, null); + } + if (Config.DEBUG) + { + _log.info("Disablers: useCubicSkill() -> success"); + } + } + else + { + if (Config.DEBUG) + { + _log.info("Disablers: useCubicSkill() -> failed"); + } + } + break; + } + case PARALYZE: // use same as root for now + { + if (Formulas.calcCubicSkillSuccess(activeCubic, target, skill, shld)) + { + // if this is a debuff let the duel manager know about it + // so the debuff can be removed after the duel + // (player & target must be in the same duel) + if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isInDuel() && (skill.getSkillType() == L2SkillType.DEBUFF) && (activeCubic.getOwner().getDuelId() == ((L2PcInstance) target).getDuelId())) + { + DuelManager dm = DuelManager.getInstance(); + for (L2Effect debuff : skill.getEffects(activeCubic.getOwner(), target)) + { + if (debuff != null) + { + dm.onBuff(((L2PcInstance) target), debuff); + } + } + } + else + { + skill.getEffects(activeCubic, target, null); + } + + if (Config.DEBUG) + { + _log.info("Disablers: useCubicSkill() -> success"); + } + } + else + { + if (Config.DEBUG) + { + _log.info("Disablers: useCubicSkill() -> failed"); + } + } + break; + } + case CANCEL_DEBUFF: + { + L2Effect[] effects = target.getAllEffects(); + + if ((effects == null) || (effects.length == 0)) + { + break; + } + + int count = (skill.getMaxNegatedEffects() > 0) ? 0 : -2; + for (L2Effect e : effects) + { + if (e.getSkill().isDebuff() && (count < skill.getMaxNegatedEffects())) + { + // Do not remove raid curse skills + if ((e.getSkill().getId() != 4215) && (e.getSkill().getId() != 4515) && (e.getSkill().getId() != 4082)) + { + e.exit(); + if (count > -1) + { + count++; + } + } + } + } + + break; + } + case ROOT: + { + if (Formulas.calcCubicSkillSuccess(activeCubic, target, skill, shld)) + { + // if this is a debuff let the duel manager know about it + // so the debuff can be removed after the duel + // (player & target must be in the same duel) + if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isInDuel() && (skill.getSkillType() == L2SkillType.DEBUFF) && (activeCubic.getOwner().getDuelId() == ((L2PcInstance) target).getDuelId())) + { + DuelManager dm = DuelManager.getInstance(); + for (L2Effect debuff : skill.getEffects(activeCubic.getOwner(), target)) + { + if (debuff != null) + { + dm.onBuff(((L2PcInstance) target), debuff); + } + } + } + else + { + skill.getEffects(activeCubic, target, null); + } + + if (Config.DEBUG) + { + _log.info("Disablers: useCubicSkill() -> success"); + } + } + else + { + if (Config.DEBUG) + { + _log.info("Disablers: useCubicSkill() -> failed"); + } + } + break; + } + case AGGDAMAGE: + { + if (Formulas.calcCubicSkillSuccess(activeCubic, target, skill, shld)) + { + if (target instanceof L2Attackable) + { + target.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, activeCubic.getOwner(), (int) ((150 * skill.getPower()) / (target.getLevel() + 7))); + } + skill.getEffects(activeCubic, target, null); + + if (Config.DEBUG) + { + _log.info("Disablers: useCubicSkill() -> success"); + } + } + else + { + if (Config.DEBUG) + { + _log.info("Disablers: useCubicSkill() -> failed"); + } + } + break; + } + } + } + } + + /** returns true if the target is inside of the owner's max Cubic range */ + public boolean isInCubicRange(L2Character owner, L2Character target) + { + if ((owner == null) || (target == null)) + { + return false; + } + + int x, y, z; + // temporary range check until real behavior of cubics is known/coded + int range = MAX_MAGIC_RANGE; + + x = (owner.getX() - target.getX()); + y = (owner.getY() - target.getY()); + z = (owner.getZ() - target.getZ()); + + return (((x * x) + (y * y) + (z * z)) <= (range * range)); + } + + /** this sets the friendly target for a cubic */ + public void cubicTargetForHeal() + { + L2Character target = null; + double percentleft = 100.0; + L2Party party = _owner.getParty(); + + // if owner is in a duel but not in a party duel, then it is the same as he does not have a + // party + if (_owner.isInDuel()) + { + if (!DuelManager.getInstance().getDuel(_owner.getDuelId()).isPartyDuel()) + { + party = null; + } + } + + if ((party != null) && !_owner.isInOlympiadMode()) + { + // Get all visible objects in a spheric area near the L2Character + // Get a list of Party Members + List partyList = party.getPartyMembers(); + for (L2Character partyMember : partyList) + { + if (!partyMember.isDead()) + { + // if party member not dead, check if he is in castrange of heal cubic + if (isInCubicRange(_owner, partyMember)) + { + // member is in cubic casting range, check if he need heal and if he have + // the lowest HP + if (partyMember.getCurrentHp() < partyMember.getMaxHp()) + { + if (percentleft > (partyMember.getCurrentHp() / partyMember.getMaxHp())) + { + percentleft = (partyMember.getCurrentHp() / partyMember.getMaxHp()); + target = partyMember; + } + } + } + } + if (partyMember.getPet() != null) + { + if (partyMember.getPet().isDead()) + { + continue; + } + + // if party member's pet not dead, check if it is in castrange of heal cubic + if (!isInCubicRange(_owner, partyMember.getPet())) + { + continue; + } + + // member's pet is in cubic casting range, check if he need heal and if he have + // the lowest HP + if (partyMember.getPet().getCurrentHp() < partyMember.getPet().getMaxHp()) + { + if (percentleft > (partyMember.getPet().getCurrentHp() / partyMember.getPet().getMaxHp())) + { + percentleft = (partyMember.getPet().getCurrentHp() / partyMember.getPet().getMaxHp()); + target = partyMember.getPet(); + } + } + } + } + } + else + { + if (_owner.getCurrentHp() < _owner.getMaxHp()) + { + percentleft = (_owner.getCurrentHp() / _owner.getMaxHp()); + target = _owner; + } + if (_owner.getPet() != null) + { + if (!_owner.getPet().isDead() && (_owner.getPet().getCurrentHp() < _owner.getPet().getMaxHp()) && (percentleft > (_owner.getPet().getCurrentHp() / _owner.getPet().getMaxHp())) && isInCubicRange(_owner, _owner.getPet())) + { + target = _owner.getPet(); + } + } + } + + _target = target; + } + + public boolean givenByOther() + { + return _givenByOther; + } + + private class Heal implements Runnable + { + + Heal() + { + // run task + } + + @Override + public void run() + { + if (_owner.isDead() || !_owner.isOnline()) + { + stopAction(); + _owner.delCubic(_id); + _owner.broadcastUserInfo(); + cancelDisappear(); + return; + } + try + { + L2Skill skill = null; + for (L2Skill sk : _skills) + { + if (sk.getId() == SKILL_CUBIC_HEAL) + { + skill = sk; + break; + } + } + + if (skill != null) + { + cubicTargetForHeal(); + L2Character target = _target; + if ((target != null) && !target.isDead()) + { + if ((target.getMaxHp() - target.getCurrentHp()) > skill.getPower()) + { + L2Character[] targets = + { + target + }; + ISkillHandler handler = SkillHandler.getInstance().getSkillHandler(skill.getSkillType()); + if (handler != null) + { + handler.useSkill(_owner, skill, targets); + } + else + { + skill.useSkill(_owner, targets); + } + + MagicSkillUse msu = new MagicSkillUse(_owner, target, skill.getId(), skill.getLevel(), 0, 0); + _owner.broadcastPacket(msu); + } + } + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } + + private class Disappear implements Runnable + { + Disappear() + { + // run task + } + + @Override + public void run() + { + stopAction(); + _owner.delCubic(_id); + _owner.broadcastUserInfo(); + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DawnPriestInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DawnPriestInstance.java new file mode 100644 index 00000000..8684d121 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DawnPriestInstance.java @@ -0,0 +1,140 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.SevenSigns; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2DawnPriestInstance extends L2SignsPriestInstance +{ + public L2DawnPriestInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2DawnPriestInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if (command.startsWith("Chat")) + { + showChatWindow(player); + } + else + { + super.onBypassFeedback(player, command); + } + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + String filename = SevenSigns.SEVEN_SIGNS_HTML_PATH; + int sealGnosisOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_GNOSIS); + int playerCabal = SevenSigns.getInstance().getPlayerCabal(player.getObjectId()); + boolean isSealValidationPeriod = SevenSigns.getInstance().isSealValidationPeriod(); + boolean isCompResultsPeriod = SevenSigns.getInstance().isCompResultsPeriod(); + int recruitPeriod = SevenSigns.getInstance().getCurrentPeriod(); + int compWinner = SevenSigns.getInstance().getCabalHighestScore(); + + switch (playerCabal) + { + case SevenSigns.CABAL_DAWN: + if (isCompResultsPeriod) + { + filename += "dawn_priest_5.htm"; + } + else if (recruitPeriod == 0) + { + filename += "dawn_priest_6.htm"; + } + else if (isSealValidationPeriod) + { + if (compWinner == SevenSigns.CABAL_DAWN) + { + if (compWinner != sealGnosisOwner) + { + filename += "dawn_priest_2c.htm"; + } + else + { + filename += "dawn_priest_2a.htm"; + } + } + else if (compWinner == SevenSigns.CABAL_NULL) + { + filename += "dawn_priest_2d.htm"; + } + else + { + filename += "dawn_priest_2b.htm"; + } + } + else + { + filename += "dawn_priest_1b.htm"; + } + break; + case SevenSigns.CABAL_DUSK: + if (isSealValidationPeriod) + { + filename += "dawn_priest_3a.htm"; + } + else + { + filename += "dawn_priest_3b.htm"; + } + break; + default: + if (isCompResultsPeriod) + { + filename += "dawn_priest_5.htm"; + } + else if (recruitPeriod == 0) + { + filename += "dawn_priest_6.htm"; + } + else if (isSealValidationPeriod) + { + if (compWinner == SevenSigns.CABAL_DAWN) + { + filename += "dawn_priest_4.htm"; + } + else if (compWinner == SevenSigns.CABAL_NULL) + { + filename += "dawn_priest_2d.htm"; + } + else + { + filename += "dawn_priest_2b.htm"; + } + } + else + { + filename += "dawn_priest_1a.htm"; + } + break; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DecoyInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DecoyInstance.java new file mode 100644 index 00000000..7f665f1f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DecoyInstance.java @@ -0,0 +1,174 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.concurrent.Future; +import java.util.logging.Level; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Decoy; +import com.l2jserver.gameserver.model.actor.knownlist.DecoyKnownList; +import com.l2jserver.gameserver.skills.l2skills.L2SkillDecoy; +import com.l2jserver.gameserver.taskmanager.DecayTaskManager; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2DecoyInstance extends L2Decoy +{ + private int _totalLifeTime; + private int _timeRemaining; + private Future _DecoyLifeTask; + private Future _HateSpam; + + public L2DecoyInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, L2Skill skill) + { + super(objectId, template, owner); + setInstanceType(InstanceType.L2DecoyInstance); + if (skill != null) + { + _totalLifeTime = ((L2SkillDecoy) skill).getTotalLifeTime(); + } + else + { + _totalLifeTime = 20000; + } + _timeRemaining = _totalLifeTime; + int delay = 1000; + int skilllevel = getTemplate().idTemplate - 13070; + _DecoyLifeTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new DecoyLifetime(getOwner(), this), delay, delay); + _HateSpam = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new HateSpam(this, SkillTable.getInstance().getInfo(5272, skilllevel)), 2000, 5000); + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + if (_HateSpam != null) + { + _HateSpam.cancel(true); + _HateSpam = null; + } + _totalLifeTime = 0; + DecayTaskManager.getInstance().addDecayTask(this); + return true; + } + + @Override + public DecoyKnownList getKnownList() + { + return (DecoyKnownList) super.getKnownList(); + } + + @Override + public void initKnownList() + { + setKnownList(new DecoyKnownList(this)); + } + + static class DecoyLifetime implements Runnable + { + private final L2PcInstance _activeChar; + + private final L2DecoyInstance _Decoy; + + DecoyLifetime(L2PcInstance activeChar, L2DecoyInstance Decoy) + { + _activeChar = activeChar; + _Decoy = Decoy; + } + + @Override + public void run() + { + try + { + double newTimeRemaining; + _Decoy.decTimeRemaining(1000); + newTimeRemaining = _Decoy.getTimeRemaining(); + if (newTimeRemaining < 0) + { + _Decoy.unSummon(_activeChar); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Decoy Error: ", e); + } + } + } + + static class HateSpam implements Runnable + { + private final L2DecoyInstance _activeChar; + + private final L2Skill _skill; + + HateSpam(L2DecoyInstance activeChar, L2Skill Hate) + { + _activeChar = activeChar; + _skill = Hate; + } + + @Override + public void run() + { + try + { + _activeChar.setTarget(_activeChar); + _activeChar.doCast(_skill); + } + catch (Throwable e) + { + _log.log(Level.SEVERE, "Decoy Error: ", e); + } + } + } + + @Override + public void unSummon(L2PcInstance owner) + { + if (_DecoyLifeTask != null) + { + _DecoyLifeTask.cancel(true); + _DecoyLifeTask = null; + } + if (_HateSpam != null) + { + _HateSpam.cancel(true); + _HateSpam = null; + } + super.unSummon(owner); + } + + public void decTimeRemaining(int value) + { + _timeRemaining -= value; + } + + public int getTimeRemaining() + { + return _timeRemaining; + } + + public int getTotalLifeTime() + { + return _totalLifeTime; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DefenderInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DefenderInstance.java new file mode 100644 index 00000000..c2f8d8a9 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DefenderInstance.java @@ -0,0 +1,249 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.ai.L2CharacterAI; +import com.l2jserver.gameserver.ai.L2FortSiegeGuardAI; +import com.l2jserver.gameserver.ai.L2SiegeGuardAI; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.instancemanager.FortManager; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.knownlist.DefenderKnownList; +import com.l2jserver.gameserver.model.entity.Castle; +import com.l2jserver.gameserver.model.entity.Fort; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2DefenderInstance extends L2Attackable +{ + private Castle _castle = null; // the castle which the instance should defend + private Fort _fort = null; // the fortress which the instance should defend + + public L2DefenderInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2DefenderInstance); + } + + @Override + public DefenderKnownList getKnownList() + { + return (DefenderKnownList) super.getKnownList(); + } + + @Override + public void initKnownList() + { + setKnownList(new DefenderKnownList(this)); + } + + @Override + public L2CharacterAI getAI() + { + L2CharacterAI ai = _ai; // copy handle + if (ai == null) + { + synchronized (this) + { + if (_ai == null) + { + if (getCastle(10000) == null) + { + _ai = new L2FortSiegeGuardAI(new AIAccessor()); + } + else + { + _ai = new L2SiegeGuardAI(new AIAccessor()); + } + } + return _ai; + } + } + return ai; + } + + /** + * Return True if a siege is in progress and the L2Character attacker isn't a Defender.
    + *
    + * @param attacker The L2Character that the L2SiegeGuardInstance try to attack + */ + @Override + public boolean isAutoAttackable(L2Character attacker) + { + // Attackable during siege by all except defenders + if (!(attacker instanceof L2Playable)) + { + return false; + } + + L2PcInstance player = attacker.getActingPlayer(); + + // Check if siege is in progress + if (((_fort != null) && _fort.getZone().isActive()) || ((_castle != null) && _castle.getZone().isActive())) + { + int activeSiegeId = (_fort != null ? _fort.getFortId() : (_castle != null ? _castle.getCastleId() : 0)); + + // Check if player is an enemy of this defender npc + if ((player != null) && (((player.getSiegeState() == 2) && !player.isRegisteredOnThisSiegeField(activeSiegeId)) || ((player.getSiegeState() == 1) && !TerritoryWarManager.getInstance().isAllyField(player, activeSiegeId)) || (player.getSiegeState() == 0))) + { + return true; + } + } + return false; + } + + @Override + public boolean hasRandomAnimation() + { + return false; + } + + /** + * This method forces guard to return to home location previously set + */ + @Override + public void returnHome() + { + if (getWalkSpeed() <= 0) + { + return; + } + if (getSpawn() == null) + { + return; + } + if (!isInsideRadius(getSpawn().getLocx(), getSpawn().getLocy(), 40, false)) + { + if (Config.DEBUG) + { + _log.info(getObjectId() + ": moving home"); + } + setisReturningToSpawnPoint(true); + clearAggroList(); + + if (hasAI()) + { + getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(getSpawn().getLocx(), getSpawn().getLocy(), getSpawn().getLocz(), 0)); + } + } + } + + @Override + public void onSpawn() + { + super.onSpawn(); + + _fort = FortManager.getInstance().getFort(getX(), getY(), getZ()); + _castle = CastleManager.getInstance().getCastle(getX(), getY(), getZ()); + if ((_fort == null) && (_castle == null)) + { + _log.warning("L2DefenderInstance spawned outside of Fortress or Castle Zone! NpcId: " + getNpcId() + " x=" + getX() + " y=" + getY() + " z=" + getZ()); + } + } + + /** + * Custom onAction behaviour. Note that super() is not called because guards need extra check to see if a player should interact or ATTACK them when clicked. + */ + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if (!canTarget(player)) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + return; + } + + // Check if the L2PcInstance already target the L2NpcInstance + if (this != player.getTarget()) + { + if (Config.DEBUG) + { + _log.info("new target selected:" + getObjectId()); + } + + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player + MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()); + player.sendPacket(my); + + // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_HP, (int) getStatus().getCurrentHp()); + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); + player.sendPacket(su); + + // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + if (isAutoAttackable(player) && !isAlikeDead()) + { + if (Math.abs(player.getZ() - getZ()) < 600) // this max heigth difference might need some tweaking + { + player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); + } + } + if (!isAutoAttackable(player)) + { + if (!canInteract(player)) + { + // Notify the L2PcInstance AI with AI_INTENTION_INTERACT + player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this); + } + } + } + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public void addDamageHate(L2Character attacker, int damage, int aggro) + { + if (attacker == null) + { + return; + } + + if (!(attacker instanceof L2DefenderInstance)) + { + if ((damage == 0) && (aggro <= 1) && (attacker instanceof L2Playable)) + { + L2PcInstance player = attacker.getActingPlayer(); + // Check if siege is in progress + if (((_fort != null) && _fort.getZone().isActive()) || ((_castle != null) && _castle.getZone().isActive())) + { + int activeSiegeId = (_fort != null ? _fort.getFortId() : (_castle != null ? _castle.getCastleId() : 0)); + if ((player != null) && (((player.getSiegeState() == 2) && player.isRegisteredOnThisSiegeField(activeSiegeId)) || ((player.getSiegeState() == 1) && TerritoryWarManager.getInstance().isAllyField(player, activeSiegeId)))) + { + return; + } + } + } + super.addDamageHate(attacker, damage, aggro); + } + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DoorInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DoorInstance.java new file mode 100644 index 00000000..8f6eece9 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DoorInstance.java @@ -0,0 +1,781 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.Collection; +import java.util.concurrent.ScheduledFuture; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.L2CharacterAI; +import com.l2jserver.gameserver.ai.L2DoorAI; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.instancemanager.FortManager; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.knownlist.DoorKnownList; +import com.l2jserver.gameserver.model.actor.stat.DoorStat; +import com.l2jserver.gameserver.model.actor.status.DoorStatus; +import com.l2jserver.gameserver.model.entity.Castle; +import com.l2jserver.gameserver.model.entity.ClanHall; +import com.l2jserver.gameserver.model.entity.Fort; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.DoorStatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.OnEventTrigger; +import com.l2jserver.gameserver.network.serverpackets.StaticObject; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2CharTemplate; +import com.l2jserver.gameserver.templates.item.L2Weapon; + +/** + * This class ... + * @version $Revision: 1.3.2.2.2.5 $ $Date: 2005/03/27 15:29:32 $ + */ +public class L2DoorInstance extends L2Character +{ + protected static final Logger log = Logger.getLogger(L2DoorInstance.class.getName()); + + /** The castle index in the array of L2Castle this L2NpcInstance belongs to */ + private int _castleIndex = -2; + private int _mapRegion = -1; + /** The fort index in the array of L2Fort this L2NpcInstance belongs to */ + private int _fortIndex = -2; + + // when door is closed, the dimensions are + private int _rangeXMin = 0; + private int _rangeYMin = 0; + private int _rangeZMin = 0; + private int _rangeXMax = 0; + private int _rangeYMax = 0; + private int _rangeZMax = 0; + + // these variables assist in see-through calculation only + private int _A = 0; + private int _B = 0; + private int _C = 0; + private int _D = 0; + + protected final int _doorId; + protected final String _name; + private boolean _open; + private boolean _isCommanderDoor; + private final boolean _unlockable; + private boolean _isAttackableDoor = false; + private boolean _isWall = false; // is castle wall ? + private boolean _ShowHp = false; + private int _meshindex = 1; + private int _emitter = 0; + private boolean _targetable = true; + + private ClanHall _clanHall; + + protected int _autoActionDelay = -1; + private ScheduledFuture _autoActionTask; + + /** This class may be created only by L2Character and only for AI */ + public class AIAccessor extends L2Character.AIAccessor + { + protected AIAccessor() + { + } + + @Override + public L2DoorInstance getActor() + { + return L2DoorInstance.this; + } + + @Override + public void moveTo(int x, int y, int z, int offset) + { + } + + @Override + public void moveTo(int x, int y, int z) + { + } + + @Override + public void stopMove(L2CharPosition pos) + { + } + + @Override + public void doAttack(L2Character target) + { + } + + @Override + public void doCast(L2Skill skill) + { + } + } + + @Override + public L2CharacterAI getAI() + { + L2CharacterAI ai = _ai; // copy handle + if (ai == null) + { + synchronized (this) + { + if (_ai == null) + { + _ai = new L2DoorAI(new AIAccessor()); + } + return _ai; + } + } + return ai; + } + + class CloseTask implements Runnable + { + @Override + public void run() + { + try + { + onClose(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } + + /** + * Manages the auto open and closing of a door. + */ + class AutoOpenClose implements Runnable + { + @Override + public void run() + { + try + { + String doorAction; + + if (!getOpen()) + { + doorAction = "opened"; + openMe(); + } + else + { + doorAction = "closed"; + closeMe(); + } + + if (Config.DEBUG) + { + _log.info("Auto " + doorAction + " door ID " + _doorId + " (" + _name + ") for " + (_autoActionDelay / 60000) + " minute(s)."); + } + } + catch (Exception e) + { + _log.warning("Could not auto open/close door ID " + _doorId + " (" + _name + ")"); + } + } + } + + /** + */ + public L2DoorInstance(int objectId, L2CharTemplate template, int doorId, String name, boolean unlockable) + { + super(objectId, template); + setInstanceType(InstanceType.L2DoorInstance); + setIsInvul(false); + _doorId = doorId; + _name = name; + _unlockable = unlockable; + } + + @Override + public final DoorKnownList getKnownList() + { + return (DoorKnownList) super.getKnownList(); + } + + @Override + public void initKnownList() + { + setKnownList(new DoorKnownList(this)); + } + + @Override + public final DoorStat getStat() + { + return (DoorStat) super.getStat(); + } + + @Override + public void initCharStat() + { + setStat(new DoorStat(this)); + } + + @Override + public final DoorStatus getStatus() + { + return (DoorStatus) super.getStatus(); + } + + @Override + public void initCharStatus() + { + setStatus(new DoorStatus(this)); + } + + public final boolean isUnlockable() + { + return _unlockable; + } + + @Override + public final int getLevel() + { + return 1; + } + + /** + * @return Returns the doorId. + */ + public int getDoorId() + { + return _doorId; + } + + /** + * @return Returns the open. + */ + public boolean getOpen() + { + return _open; + } + + /** + * @param open The open to set. + */ + public void setOpen(boolean open) + { + _open = open; + } + + /** + * @param val Used for Fortresses to determine if doors can be attacked during siege or not + */ + public void setIsCommanderDoor(boolean val) + { + _isCommanderDoor = val; + } + + /** + * @return Doors that cannot be attacked during siege these doors will be auto opened if u take control of all commanders buildings + */ + public boolean getIsCommanderDoor() + { + return _isCommanderDoor; + } + + public boolean getIsAttackableDoor() + { + return _isAttackableDoor; + } + + public boolean getIsShowHp() + { + return _ShowHp; + } + + public void setIsAttackableDoor(boolean val) + { + _isAttackableDoor = val; + } + + public void setIsShowHp(boolean val) + { + _ShowHp = val; + } + + /** + * Sets the delay in milliseconds for automatic opening/closing of this door instance.
    + * Note: A value of -1 cancels the auto open/close task. + * @param int actionDelay + */ + public void setAutoActionDelay(int actionDelay) + { + if (_autoActionDelay == actionDelay) + { + return; + } + + if (actionDelay > -1) + { + AutoOpenClose ao = new AutoOpenClose(); + ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(ao, actionDelay, actionDelay); + } + else + { + if (_autoActionTask != null) + { + _autoActionTask.cancel(false); + } + } + + _autoActionDelay = actionDelay; + } + + public int getDamage() + { + int dmg = 6 - (int) Math.ceil((getCurrentHp() / getMaxHp()) * 6); + if (dmg > 6) + { + return 6; + } + if (dmg < 0) + { + return 0; + } + return dmg; + } + + public final Castle getCastle() + { + if (_castleIndex < 0) + { + _castleIndex = CastleManager.getInstance().getCastleIndex(this); + } + if (_castleIndex < 0) + { + return null; + } + return CastleManager.getInstance().getCastles().get(_castleIndex); + } + + public final Fort getFort() + { + if (_fortIndex < 0) + { + _fortIndex = FortManager.getInstance().getFortIndex(this); + } + if (_fortIndex < 0) + { + return null; + } + return FortManager.getInstance().getForts().get(_fortIndex); + } + + public void setClanHall(ClanHall clanhall) + { + _clanHall = clanhall; + } + + public ClanHall getClanHall() + { + return _clanHall; + } + + public boolean isEnemy() + { + if ((getCastle() != null) && (getCastle().getCastleId() > 0) && getCastle().getZone().isActive()) + { + return true; + } + if ((getFort() != null) && (getFort().getFortId() > 0) && getFort().getZone().isActive() && !getIsCommanderDoor()) + { + return true; + } + return false; + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + if (isUnlockable() && (getFort() == null)) + { + return true; + } + + // Doors can`t be attacked by NPCs + if (!(attacker instanceof L2Playable)) + { + return false; + } + + if (getClanHall() != null) + { + return false; + } + + // Attackable only during siege by everyone (not owner) + boolean isCastle = ((getCastle() != null) && (getCastle().getCastleId() > 0) && getCastle().getZone().isActive()); + boolean isFort = ((getFort() != null) && (getFort().getFortId() > 0) && getFort().getZone().isActive() && !getIsCommanderDoor()); + int activeSiegeId = (getFort() != null ? getFort().getFortId() : (getCastle() != null ? getCastle().getCastleId() : 0)); + L2PcInstance actingPlayer = attacker.getActingPlayer(); + + if (TerritoryWarManager.getInstance().isTWInProgress()) + { + return !TerritoryWarManager.getInstance().isAllyField(actingPlayer, activeSiegeId); + } + else if (isFort) + { + L2Clan clan = actingPlayer.getClan(); + if ((clan != null) && (clan == getFort().getOwnerClan())) + { + return false; + } + } + else if (isCastle) + { + L2Clan clan = actingPlayer.getClan(); + if ((clan != null) && (clan.getClanId() == getCastle().getOwnerId())) + { + return false; + } + } + return (isCastle || isFort || getIsAttackableDoor()); + } + + public boolean isAttackable(L2Character attacker) + { + return isAutoAttackable(attacker); + } + + @Override + public void updateAbnormalEffect() + { + } + + public int getDistanceToWatchObject(L2Object object) + { + if (!(object instanceof L2PcInstance)) + { + return 0; + } + return 3000; + } + + /** + * Return the distance after which the object must be remove from _knownObject according to the type of the object.
    + *
    + * Values :
    + *
    + *
  • object is a L2PcInstance : 4000
  • object is not a L2PcInstance : 0

  • + *
    + */ + public int getDistanceToForgetObject(L2Object object) + { + if (!(object instanceof L2PcInstance)) + { + return 0; + } + + return 4000; + } + + /** + * Return null.
    + *
    + */ + @Override + public L2ItemInstance getActiveWeaponInstance() + { + return null; + } + + @Override + public L2Weapon getActiveWeaponItem() + { + return null; + } + + @Override + public L2ItemInstance getSecondaryWeaponInstance() + { + return null; + } + + @Override + public L2Weapon getSecondaryWeaponItem() + { + return null; + } + + @Override + public void broadcastStatusUpdate() + { + Collection knownPlayers = getKnownList().getKnownPlayers().values(); + if ((knownPlayers == null) || knownPlayers.isEmpty()) + { + return; + } + + StaticObject su = new StaticObject(this, false); + DoorStatusUpdate dsu = new DoorStatusUpdate(this); + OnEventTrigger oe = null; + if (_emitter > 0) + { + oe = new OnEventTrigger(this, getOpen()); + } + // synchronized (getKnownList().getKnownPlayers()) + { + for (L2PcInstance player : knownPlayers) + { + if (((getCastle() != null) && (getCastle().getCastleId() > 0)) || ((getFort() != null) && (getFort().getFortId() > 0) && !getIsCommanderDoor())) + { + su = new StaticObject(this, true); + } + + player.sendPacket(su); + player.sendPacket(dsu); + if (oe != null) + { + player.sendPacket(oe); + } + } + } + } + + public void onOpen() + { + ThreadPoolManager.getInstance().scheduleGeneral(new CloseTask(), 60000); + } + + public void onClose() + { + closeMe(); + } + + public final void closeMe() + { + setOpen(false); + broadcastStatusUpdate(); + } + + public final void openMe() + { + setOpen(true); + broadcastStatusUpdate(); + } + + @Override + public String toString() + { + return "door " + _doorId; + } + + public String getDoorName() + { + return _name; + } + + public int getXMin() + { + return _rangeXMin; + } + + public int getYMin() + { + return _rangeYMin; + } + + public int getZMin() + { + return _rangeZMin; + } + + public int getXMax() + { + return _rangeXMax; + } + + public int getYMax() + { + return _rangeYMax; + } + + public int getZMax() + { + return _rangeZMax; + } + + public void setRange(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax) + { + _rangeXMin = xMin; + _rangeYMin = yMin; + _rangeZMin = zMin; + + _rangeXMax = xMax; + _rangeYMax = yMax; + _rangeZMax = zMax; + + _A = (_rangeYMax * (_rangeZMax - _rangeZMin)) + (_rangeYMin * (_rangeZMin - _rangeZMax)); + _B = (_rangeZMin * (_rangeXMax - _rangeXMin)) + (_rangeZMax * (_rangeXMin - _rangeXMax)); + _C = (_rangeXMin * (_rangeYMax - _rangeYMin)) + (_rangeXMin * (_rangeYMin - _rangeYMax)); + _D = -1 * ((_rangeXMin * ((_rangeYMax * _rangeZMax) - (_rangeYMin * _rangeZMax))) + (_rangeXMax * ((_rangeYMin * _rangeZMin) - (_rangeYMin * _rangeZMax))) + (_rangeXMin * ((_rangeYMin * _rangeZMax) - (_rangeYMax * _rangeZMin)))); + } + + public int getMapRegion() + { + return _mapRegion; + } + + public void setMapRegion(int region) + { + _mapRegion = region; + } + + public Collection getKnownDefenders() + { + FastList result = new FastList<>(); + + Collection objs = getKnownList().getKnownObjects().values(); + // synchronized (getKnownList().getKnownObjects()) + { + for (L2Object obj : objs) + { + if (obj instanceof L2DefenderInstance) + { + result.add((L2DefenderInstance) obj); + } + } + } + return result; + } + + public int getA() + { + return _A; + } + + public int getB() + { + return _B; + } + + public int getC() + { + return _C; + } + + public int getD() + { + return _D; + } + + /** + * Set this door as a castle wall, can be damaged by siege golem only. + */ + public void setIsWall(boolean b) + { + _isWall = b; + } + + /** + * @return true if door is a castle wall and can be damaged by siege golem only. + */ + public boolean isWall() + { + return _isWall; + } + + public void setMeshIndex(int mesh) + { + _meshindex = mesh; + } + + public int getMeshIndex() + { + return _meshindex; + } + + public void setEmitter(int emitter) + { + _emitter = emitter; + } + + public int getEmitter() + { + return _emitter; + } + + public void setTargetable(boolean targetable) + { + _targetable = targetable; + } + + public boolean getTargetable() + { + return _targetable; + } + + @Override + public void reduceCurrentHp(double damage, L2Character attacker, boolean awake, boolean isDOT, L2Skill skill) + { + if (_isWall && !(attacker instanceof L2SiegeSummonInstance)) + { + return; + } + + super.reduceCurrentHp(damage, attacker, awake, isDOT, skill); + } + + @Override + public void reduceCurrentHpByDOT(double i, L2Character attacker, L2Skill skill) + { + // doors can't be damaged by DOTs + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + + boolean isFort = ((getFort() != null) && (getFort().getFortId() > 0) && getFort().getSiege().getIsInProgress()) && !getIsCommanderDoor(); + boolean isCastle = ((getCastle() != null) && (getCastle().getCastleId() > 0) && getCastle().getSiege().getIsInProgress()); + + if (isFort || isCastle) + { + broadcastPacket(SystemMessage.getSystemMessage(SystemMessageId.CASTLE_GATE_BROKEN_DOWN)); + } + return true; + } + + @Override + public void sendInfo(L2PcInstance activeChar) + { + if (_emitter > 0) + { + activeChar.sendPacket(new OnEventTrigger(this, getOpen())); + } + + activeChar.sendPacket(new StaticObject(this, false)); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DoormenInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DoormenInstance.java new file mode 100644 index 00000000..11e9fe71 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DoormenInstance.java @@ -0,0 +1,169 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; + +import com.l2jserver.gameserver.datatables.DoorTable; +import com.l2jserver.gameserver.datatables.TeleportLocationTable; +import com.l2jserver.gameserver.model.L2TeleportLocation; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * This class ... + * @version $Revision$ $Date$ + */ +public class L2DoormenInstance extends L2NpcInstance +{ + /** + * @param template + */ + public L2DoormenInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2DoormenInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if (command.startsWith("Chat")) + { + showChatWindow(player); + return; + } + else if (command.startsWith("open_doors")) + { + if (isOwnerClan(player)) + { + if (isUnderSiege()) + { + cannotManageDoors(player); + } + else + { + openDoors(player, command); + } + } + return; + } + else if (command.startsWith("close_doors")) + { + if (isOwnerClan(player)) + { + if (isUnderSiege()) + { + cannotManageDoors(player); + } + else + { + closeDoors(player, command); + } + } + return; + } + else if (command.startsWith("tele")) + { + if (isOwnerClan(player)) + { + doTeleport(player, command); + } + return; + } + super.onBypassFeedback(player, command); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + + if (!isOwnerClan(player)) + { + html.setFile(player.getHtmlPrefix(), "data/html/doormen/" + getTemplate().npcId + "-no.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/doormen/" + getTemplate().npcId + ".htm"); + } + + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + + protected void openDoors(L2PcInstance player, String command) + { + StringTokenizer st = new StringTokenizer(command.substring(10), ", "); + st.nextToken(); + + while (st.hasMoreTokens()) + { + DoorTable.getInstance().getDoor(Integer.parseInt(st.nextToken())).openMe(); + } + } + + protected void closeDoors(L2PcInstance player, String command) + { + StringTokenizer st = new StringTokenizer(command.substring(11), ", "); + st.nextToken(); + + while (st.hasMoreTokens()) + { + DoorTable.getInstance().getDoor(Integer.parseInt(st.nextToken())).closeMe(); + } + } + + protected void cannotManageDoors(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/doormen/" + getTemplate().npcId + "-busy.htm"); + player.sendPacket(html); + } + + protected void doTeleport(L2PcInstance player, String command) + { + final int whereTo = Integer.parseInt(command.substring(5).trim()); + L2TeleportLocation list = TeleportLocationTable.getInstance().getTemplate(whereTo); + if (list != null) + { + if (!player.isAlikeDead()) + { + player.teleToLocation(list.getLocX(), list.getLocY(), list.getLocZ(), false); + } + } + else + { + _log.warning("No teleport destination with id:" + whereTo); + } + + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + protected boolean isOwnerClan(L2PcInstance player) + { + return true; + } + + protected boolean isUnderSiege() + { + return false; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DungeonGatekeeperInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DungeonGatekeeperInstance.java new file mode 100644 index 00000000..542417ed --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DungeonGatekeeperInstance.java @@ -0,0 +1,192 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; + +import com.l2jserver.gameserver.SevenSigns; +import com.l2jserver.gameserver.datatables.TeleportLocationTable; +import com.l2jserver.gameserver.model.L2TeleportLocation; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2DungeonGatekeeperInstance extends L2Npc +{ + public L2DungeonGatekeeperInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2DungeonGatekeeperInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + StringTokenizer st = new StringTokenizer(command, " "); + String actualCommand = st.nextToken(); // Get actual command + + String filename = SevenSigns.SEVEN_SIGNS_HTML_PATH; + int sealAvariceOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_AVARICE); + int sealGnosisOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_GNOSIS); + int playerCabal = SevenSigns.getInstance().getPlayerCabal(player.getObjectId()); + boolean isSealValidationPeriod = SevenSigns.getInstance().isSealValidationPeriod(); + int compWinner = SevenSigns.getInstance().getCabalHighestScore(); + + if (actualCommand.startsWith("necro")) + { + boolean canPort = true; + if (isSealValidationPeriod) + { + if ((compWinner == SevenSigns.CABAL_DAWN) && ((playerCabal != SevenSigns.CABAL_DAWN) || (sealAvariceOwner != SevenSigns.CABAL_DAWN))) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CAN_BE_USED_BY_DAWN)); + canPort = false; + } + else if ((compWinner == SevenSigns.CABAL_DUSK) && ((playerCabal != SevenSigns.CABAL_DUSK) || (sealAvariceOwner != SevenSigns.CABAL_DUSK))) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CAN_BE_USED_BY_DUSK)); + canPort = false; + } + else if ((compWinner == SevenSigns.CABAL_NULL) && (playerCabal != SevenSigns.CABAL_NULL)) + { + canPort = true; + } + else if (playerCabal == SevenSigns.CABAL_NULL) + { + canPort = false; + } + } + else + { + if (playerCabal == SevenSigns.CABAL_NULL) + { + canPort = false; + } + } + + if (!canPort) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + filename += "necro_no.htm"; + html.setFile(player.getHtmlPrefix(), filename); + player.sendPacket(html); + } + else + { + doTeleport(player, Integer.parseInt(st.nextToken())); + player.setIsIn7sDungeon(true); + } + } + else if (actualCommand.startsWith("cata")) + { + boolean canPort = true; + if (isSealValidationPeriod) + { + if ((compWinner == SevenSigns.CABAL_DAWN) && ((playerCabal != SevenSigns.CABAL_DAWN) || (sealGnosisOwner != SevenSigns.CABAL_DAWN))) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CAN_BE_USED_BY_DAWN)); + canPort = false; + } + else if ((compWinner == SevenSigns.CABAL_DUSK) && ((playerCabal != SevenSigns.CABAL_DUSK) || (sealGnosisOwner != SevenSigns.CABAL_DUSK))) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CAN_BE_USED_BY_DUSK)); + canPort = false; + } + else if ((compWinner == SevenSigns.CABAL_NULL) && (playerCabal != SevenSigns.CABAL_NULL)) + { + canPort = true; + } + else if (playerCabal == SevenSigns.CABAL_NULL) + { + canPort = false; + } + } + else + { + if (playerCabal == SevenSigns.CABAL_NULL) + { + canPort = false; + } + } + + if (!canPort) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + filename += "cata_no.htm"; + html.setFile(player.getHtmlPrefix(), filename); + player.sendPacket(html); + } + else + { + doTeleport(player, Integer.parseInt(st.nextToken())); + player.setIsIn7sDungeon(true); + } + } + else if (actualCommand.startsWith("exit")) + { + doTeleport(player, Integer.parseInt(st.nextToken())); + player.setIsIn7sDungeon(false); + } + else if (actualCommand.startsWith("goto")) + { + doTeleport(player, Integer.parseInt(st.nextToken())); + } + else + { + super.onBypassFeedback(player, command); + } + } + + private void doTeleport(L2PcInstance player, int val) + { + L2TeleportLocation list = TeleportLocationTable.getInstance().getTemplate(val); + if (list != null) + { + if (player.isAlikeDead()) + { + return; + } + + player.teleToLocation(list.getLocX(), list.getLocY(), list.getLocZ(), true); + } + else + { + _log.warning("No teleport destination with id:" + val); + } + + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/teleporter/" + pom + ".htm"; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DuskPriestInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DuskPriestInstance.java new file mode 100644 index 00000000..a77578bc --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2DuskPriestInstance.java @@ -0,0 +1,141 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.SevenSigns; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2DuskPriestInstance extends L2SignsPriestInstance +{ + public L2DuskPriestInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2DuskPriestInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if (command.startsWith("Chat")) + { + showChatWindow(player); + } + else + { + super.onBypassFeedback(player, command); + } + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + String filename = SevenSigns.SEVEN_SIGNS_HTML_PATH; + int sealGnosisOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_GNOSIS); + int playerCabal = SevenSigns.getInstance().getPlayerCabal(player.getObjectId()); + boolean isSealValidationPeriod = SevenSigns.getInstance().isSealValidationPeriod(); + boolean isCompResultsPeriod = SevenSigns.getInstance().isCompResultsPeriod(); + int recruitPeriod = SevenSigns.getInstance().getCurrentPeriod(); + int compWinner = SevenSigns.getInstance().getCabalHighestScore(); + + switch (playerCabal) + { + case SevenSigns.CABAL_DUSK: + if (isCompResultsPeriod) + { + filename += "dusk_priest_5.htm"; + } + else if (recruitPeriod == 0) + { + filename += "dusk_priest_6.htm"; + } + else if (isSealValidationPeriod) + { + if (compWinner == SevenSigns.CABAL_DUSK) + { + if (compWinner != sealGnosisOwner) + { + filename += "dusk_priest_2c.htm"; + } + else + { + filename += "dusk_priest_2a.htm"; + } + } + else if (compWinner == SevenSigns.CABAL_NULL) + { + filename += "dusk_priest_2d.htm"; + } + else + { + filename += "dusk_priest_2b.htm"; + } + } + else + { + filename += "dusk_priest_1b.htm"; + } + break; + case SevenSigns.CABAL_DAWN: + if (isSealValidationPeriod) + { + filename += "dusk_priest_3a.htm"; + } + else + { + filename += "dusk_priest_3b.htm"; + } + break; + default: + if (isCompResultsPeriod) + { + filename += "dusk_priest_5.htm"; + } + else if (recruitPeriod == 0) + { + filename += "dusk_priest_6.htm"; + } + else if (isSealValidationPeriod) + { + if (compWinner == SevenSigns.CABAL_DUSK) + { + filename += "dusk_priest_4.htm"; + } + else if (compWinner == SevenSigns.CABAL_NULL) + { + filename += "dusk_priest_2d.htm"; + } + else + { + filename += "dusk_priest_2b.htm"; + } + } + else + { + filename += "dusk_priest_1a.htm"; + } + break; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2EffectPointInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2EffectPointInstance.java new file mode 100644 index 00000000..8fa06e64 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2EffectPointInstance.java @@ -0,0 +1,62 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2EffectPointInstance extends L2Npc +{ + private final L2PcInstance _owner; + + public L2EffectPointInstance(int objectId, L2NpcTemplate template, L2Character owner) + { + super(objectId, template); + setInstanceType(InstanceType.L2EffectPointInstance); + setIsInvul(false); + _owner = owner.getActingPlayer(); + setInstanceId(owner.getInstanceId()); + } + + @Override + public L2PcInstance getActingPlayer() + { + return _owner; + } + + /** + * this is called when a player interacts with this NPC + * @param player + */ + @Override + public void onAction(L2PcInstance player, boolean interact) + { + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public void onActionShift(L2PcInstance player) + { + if (player == null) + { + return; + } + + player.sendPacket(ActionFailed.STATIC_PACKET); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2EventChestInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2EventChestInstance.java new file mode 100644 index 00000000..4ab61f8e --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2EventChestInstance.java @@ -0,0 +1,95 @@ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.Collection; + +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.network.serverpackets.AbstractNpcInfo; +import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + ** @author Gnacik + */ +public final class L2EventChestInstance extends L2EventMonsterInstance +{ + private final boolean _isVisible = false; + private boolean _isTriggered = false; + + public L2EventChestInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + + setIsNoRndWalk(true); + disableCoreAI(true); + + eventSetDropOnGround(true); + eventSetBlockOffensiveSkills(true); + } + + public boolean canSee(L2Character cha) + { + if (cha == null) + { + return false; + } + if (cha.isGM()) + { + return true; + } + return _isVisible; + } + + public void trigger() + { + _isTriggered = true; + broadcastPacket(new AbstractNpcInfo.NpcInfo(this, null)); + } + + @Override + public void sendInfo(L2PcInstance activeChar) + { + if (_isTriggered || canSee(activeChar)) + { + activeChar.sendPacket(new AbstractNpcInfo.NpcInfo(this, activeChar)); + } + } + + @Override + public void broadcastPacket(L2GameServerPacket mov) + { + Collection plrs = getKnownList().getKnownPlayers().values(); + for (L2PcInstance player : plrs) + { + if ((player != null) && (_isTriggered || canSee(player))) + { + player.sendPacket(mov); + } + } + } + + @Override + public void broadcastPacket(L2GameServerPacket mov, int radiusInKnownlist) + { + Collection plrs = getKnownList().getKnownPlayers().values(); + for (L2PcInstance player : plrs) + { + if (player == null) + { + continue; + } + if (isInsideRadius(player, radiusInKnownlist, false, false)) + { + if (_isTriggered || canSee(player)) + { + player.sendPacket(mov); + } + } + } + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return !canSee(attacker); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2EventMonsterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2EventMonsterInstance.java new file mode 100644 index 00000000..8a1578db --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2EventMonsterInstance.java @@ -0,0 +1,59 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + ** @author Gnacik + */ +public class L2EventMonsterInstance extends L2MonsterInstance +{ + // Block offensive skills usage on event mobs + // mainly for AoE skills, disallow kill many event mobs + // with one skill + public boolean block_skill_attack = false; + + // Event mobs should drop items to ground + // but item pickup must be protected to killer + // Todo: Some mobs need protect drop for spawner + public boolean drop_on_ground = false; + + public L2EventMonsterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2EventMobInstance); + } + + public void eventSetBlockOffensiveSkills(boolean value) + { + block_skill_attack = value; + } + + public void eventSetDropOnGround(boolean value) + { + drop_on_ground = value; + } + + public boolean eventDropOnGround() + { + return drop_on_ground; + } + + public boolean eventSkillAttackBlocked() + { + return block_skill_attack; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FameManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FameManagerInstance.java new file mode 100644 index 00000000..920eb3d7 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FameManagerInstance.java @@ -0,0 +1,126 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; + +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.network.serverpackets.UserInfo; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * Reputation score manager + * @author Kerberos + */ +public class L2FameManagerInstance extends L2Npc +{ + public L2FameManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FameManagerInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + StringTokenizer st = new StringTokenizer(command, " "); + String actualCommand = st.nextToken(); + if (actualCommand.equalsIgnoreCase("PK_Count")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if ((player.getFame() >= 5000) && (player.getClassId().level() >= 2) && (player.getClan() != null) && (player.getClan().getLevel() >= 5)) + { + if (player.getPkKills() > 0) + { + player.setFame(player.getFame() - 5000); + player.setPkKills(player.getPkKills() - 1); + player.sendPacket(new UserInfo(player)); + player.sendPacket(new ExBrExtraUserInfo(player)); + html.setFile(player.getHtmlPrefix(), "data/html/famemanager/" + getNpcId() + "-3.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/famemanager/" + getNpcId() + "-4.htm"); + } + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/famemanager/" + getNpcId() + "-lowfame.htm"); + } + + sendHtmlMessage(player, html); + return; + } + else if (actualCommand.equalsIgnoreCase("CRP")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if ((player.getClan() != null) && (player.getClan().getLevel() >= 5)) + { + if ((player.getFame() >= 1000) && (player.getClassId().level() >= 2)) + { + player.setFame(player.getFame() - 1000); + player.sendPacket(new UserInfo(player)); + player.sendPacket(new ExBrExtraUserInfo(player)); + player.getClan().addReputationScore(50, true); + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ACQUIRED_50_CLAN_FAME_POINTS)); + html.setFile(player.getHtmlPrefix(), "data/html/famemanager/" + getNpcId() + "-5.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/famemanager/" + getNpcId() + "-lowfame.htm"); + } + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/famemanager/" + getNpcId() + "-noclan.htm"); + } + + sendHtmlMessage(player, html); + return; + } + else + { + super.onBypassFeedback(player, command); + } + } + + private void sendHtmlMessage(L2PcInstance player, NpcHtmlMessage html) + { + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + String filename = "data/html/famemanager/" + getNpcId() + "-lowfame.htm"; + + if (player.getFame() > 0) + { + filename = "data/html/famemanager/" + getNpcId() + ".htm"; + } + + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FeedableBeastInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FeedableBeastInstance.java new file mode 100644 index 00000000..85cc6e26 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FeedableBeastInstance.java @@ -0,0 +1,32 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +// This class is here mostly for convinience and for avoidance of hardcoded IDs. +// It refers to Beast (mobs) that can be attacked but can also be fed +// For example, the Beast Farm's Alpen Buffalo. +// This class is only trully used by the handlers in order to check the correctness +// of the target. However, no additional tasks are needed, since they are all +// handled by scripted AI. +public class L2FeedableBeastInstance extends L2MonsterInstance +{ + public L2FeedableBeastInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FeedableBeastInstance); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FestivalGuideInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FestivalGuideInstance.java new file mode 100644 index 00000000..36aeb038 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FestivalGuideInstance.java @@ -0,0 +1,246 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.SevenSigns; +import com.l2jserver.gameserver.SevenSignsFestival; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.StringUtil; + +/** + * Festival of Darkness Guide (Seven Signs) + * @author Tempy + */ +public final class L2FestivalGuideInstance extends L2Npc +{ + private final int _festivalType; + private final int _festivalOracle; + private final int _blueStonesNeeded; + private final int _greenStonesNeeded; + private final int _redStonesNeeded; + + /** + * @param template + */ + public L2FestivalGuideInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FestivalGiudeInstance); + + switch (getNpcId()) + { + case 31127: + case 31132: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_31; + _festivalOracle = SevenSigns.CABAL_DAWN; + _blueStonesNeeded = 900; + _greenStonesNeeded = 540; + _redStonesNeeded = 270; + break; + case 31128: + case 31133: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_42; + _festivalOracle = SevenSigns.CABAL_DAWN; + _blueStonesNeeded = 1500; + _greenStonesNeeded = 900; + _redStonesNeeded = 450; + break; + case 31129: + case 31134: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_53; + _festivalOracle = SevenSigns.CABAL_DAWN; + _blueStonesNeeded = 3000; + _greenStonesNeeded = 1800; + _redStonesNeeded = 900; + break; + case 31130: + case 31135: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_64; + _festivalOracle = SevenSigns.CABAL_DAWN; + _blueStonesNeeded = 4500; + _greenStonesNeeded = 2700; + _redStonesNeeded = 1350; + break; + case 31131: + case 31136: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_NONE; + _festivalOracle = SevenSigns.CABAL_DAWN; + _blueStonesNeeded = 6000; + _greenStonesNeeded = 3600; + _redStonesNeeded = 1800; + break; + + case 31137: + case 31142: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_31; + _festivalOracle = SevenSigns.CABAL_DUSK; + _blueStonesNeeded = 900; + _greenStonesNeeded = 540; + _redStonesNeeded = 270; + break; + case 31138: + case 31143: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_42; + _festivalOracle = SevenSigns.CABAL_DUSK; + _blueStonesNeeded = 1500; + _greenStonesNeeded = 900; + _redStonesNeeded = 450; + break; + case 31139: + case 31144: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_53; + _festivalOracle = SevenSigns.CABAL_DUSK; + _blueStonesNeeded = 3000; + _greenStonesNeeded = 1800; + _redStonesNeeded = 900; + break; + case 31140: + case 31145: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_64; + _festivalOracle = SevenSigns.CABAL_DUSK; + _blueStonesNeeded = 4500; + _greenStonesNeeded = 2700; + _redStonesNeeded = 1350; + break; + case 31141: + case 31146: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_NONE; + _festivalOracle = SevenSigns.CABAL_DUSK; + _blueStonesNeeded = 6000; + _greenStonesNeeded = 3600; + _redStonesNeeded = 1800; + break; + default: + _festivalType = SevenSignsFestival.FESTIVAL_LEVEL_MAX_NONE; + _festivalOracle = SevenSigns.CABAL_NULL; + _blueStonesNeeded = 0; + _greenStonesNeeded = 0; + _redStonesNeeded = 0; + } + } + + public int getFestivalType() + { + return _festivalType; + } + + public int getFestivalOracle() + { + return _festivalOracle; + } + + public int getStoneCount(int stoneType) + { + switch (stoneType) + { + case SevenSigns.SEAL_STONE_BLUE_ID: + return _blueStonesNeeded; + case SevenSigns.SEAL_STONE_GREEN_ID: + return _greenStonesNeeded; + case SevenSigns.SEAL_STONE_RED_ID: + return _redStonesNeeded; + default: + return -1; + } + } + + public final void showChatWindow(L2PcInstance player, int val, String suffix, boolean isDescription) + { + String filename = SevenSigns.SEVEN_SIGNS_HTML_PATH + "festival/"; + filename += (isDescription) ? "desc_" : "festival_"; + filename += (suffix != null) ? val + suffix + ".htm" : val + ".htm"; + + // Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%festivalType%", SevenSignsFestival.getFestivalName(_festivalType)); + html.replace("%cycleMins%", String.valueOf(SevenSignsFestival.getInstance().getMinsToNextCycle())); + if (!isDescription && "2b".equals(val + suffix)) + { + html.replace("%minFestivalPartyMembers%", String.valueOf(Config.ALT_FESTIVAL_MIN_PLAYER)); + } + + // If the stats or bonus table is required, construct them. + if (val == 5) + { + html.replace("%statsTable%", getStatsTable()); + } + if (val == 6) + { + html.replace("%bonusTable%", getBonusTable()); + } + + // festival's fee + if (val == 1) + { + html.replace("%blueStoneNeeded%", String.valueOf(_blueStonesNeeded)); + html.replace("%greenStoneNeeded%", String.valueOf(_greenStonesNeeded)); + html.replace("%redStoneNeeded%", String.valueOf(_redStonesNeeded)); + } + + player.sendPacket(html); + + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + private static final String getStatsTable() + { + final StringBuilder tableHtml = new StringBuilder(1000); + + // Get the scores for each of the festival level ranges (types). + for (int i = 0; i < 5; i++) + { + int dawnScore = SevenSignsFestival.getInstance().getHighestScore(SevenSigns.CABAL_DAWN, i); + int duskScore = SevenSignsFestival.getInstance().getHighestScore(SevenSigns.CABAL_DUSK, i); + String festivalName = SevenSignsFestival.getFestivalName(i); + String winningCabal = "Children of Dusk"; + + if (dawnScore > duskScore) + { + winningCabal = "Children of Dawn"; + } + else if (dawnScore == duskScore) + { + winningCabal = "None"; + } + + StringUtil.append(tableHtml, "", festivalName, "", String.valueOf(duskScore), "", String.valueOf(dawnScore), "", winningCabal, ""); + } + + return tableHtml.toString(); + } + + private static final String getBonusTable() + { + final StringBuilder tableHtml = new StringBuilder(500); + + // Get the accumulated scores for each of the festival level ranges (types). + for (int i = 0; i < 5; i++) + { + int accumScore = SevenSignsFestival.getInstance().getAccumulatedBonus(i); + String festivalName = SevenSignsFestival.getFestivalName(i); + + StringUtil.append(tableHtml, "", festivalName, "", String.valueOf(accumScore), ""); + } + + return tableHtml.toString(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FestivalMonsterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FestivalMonsterInstance.java new file mode 100644 index 00000000..b457a6ab --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FestivalMonsterInstance.java @@ -0,0 +1,126 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.SevenSignsFestival; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Party; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * L2FestivalMonsterInstance This class manages all attackable festival NPCs, spawned during the Festival of Darkness. + * @author Tempy + */ +public class L2FestivalMonsterInstance extends L2MonsterInstance +{ + protected int _bonusMultiplier = 1; + + /** + * Constructor of L2FestivalMonsterInstance (use L2Character and L2NpcInstance constructor).
    + *
    + * Actions :
    + *
    + *
  • Call the L2Character constructor to set the _template of the L2FestivalMonsterInstance (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR)
  • Set the name of the L2MonsterInstance
  • Create a RandomAnimation Task that will be launched after the + * calculated delay if the server allow it

  • + *
    + * @param objectId Identifier of the object to initialized + * @param L2NpcTemplate Template to apply to the NPC + */ + public L2FestivalMonsterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FestivalMonsterInstance); + } + + public void setOfferingBonus(int bonusMultiplier) + { + _bonusMultiplier = bonusMultiplier; + } + + /** + * Return True if the attacker is not another L2FestivalMonsterInstance.
    + *
    + */ + @Override + public boolean isAutoAttackable(L2Character attacker) + { + if (attacker instanceof L2FestivalMonsterInstance) + { + return false; + } + + return true; + } + + /** + * All mobs in the festival are aggressive, and have high aggro range. + */ + @Override + public boolean isAggressive() + { + return true; + } + + /** + * All mobs in the festival really don't need random animation. + */ + @Override + public boolean hasRandomAnimation() + { + return false; + } + + /** + * Actions:
  • Check if the killing object is a player, and then find the party they belong to.
  • Add a blood offering item to the leader of the party.
  • Update the party leader's inventory to show the new item addition.
  • + */ + @Override + public void doItemDrop(L2Character lastAttacker) + { + L2PcInstance killingChar = null; + + if (!(lastAttacker instanceof L2PcInstance)) + { + return; + } + + killingChar = (L2PcInstance) lastAttacker; + L2Party associatedParty = killingChar.getParty(); + + if (associatedParty == null) + { + return; + } + + L2PcInstance partyLeader = associatedParty.getLeader(); + L2ItemInstance addedOfferings = partyLeader.getInventory().addItem("Sign", SevenSignsFestival.FESTIVAL_OFFERING_ID, _bonusMultiplier, partyLeader, this); + + InventoryUpdate iu = new InventoryUpdate(); + + if (addedOfferings.getCount() != _bonusMultiplier) + { + iu.addModifiedItem(addedOfferings); + } + else + { + iu.addNewItem(addedOfferings); + } + + partyLeader.sendPacket(iu); + + super.doItemDrop(lastAttacker); // Normal drop + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FishermanInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FishermanInstance.java new file mode 100644 index 00000000..8adc9e70 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FishermanInstance.java @@ -0,0 +1,106 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import javolution.util.FastList; + +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2SkillLearn; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2FishermanInstance extends L2MerchantInstance +{ + public L2FishermanInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FishermanInstance); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/fisherman/" + pom + ".htm"; + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if (command.equalsIgnoreCase("FishSkillList")) + { + showFishSkillList(player); + } + else + { + super.onBypassFeedback(player, command); + } + } + + public static void showFishSkillList(L2PcInstance player) + { + final FastList skills = SkillTreesData.getInstance().getAvailableFishingSkills(player); + final AcquireSkillList asl = new AcquireSkillList(SkillType.Fishing); + + int count = 0; + + for (L2SkillLearn s : skills) + { + final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()); + + if (sk == null) + { + continue; + } + count++; + asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), 1); + } + + if (count == 0) + { + final int minlLevel = SkillTreesData.getInstance().getMinLevelForNewSkill(player, SkillTreesData.getInstance().getFishingSkillTree()); + + if (minlLevel > 0) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DO_NOT_HAVE_FURTHER_SKILLS_TO_LEARN_S1); + sm.addNumber(minlLevel); + player.sendPacket(sm); + } + else + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_MORE_SKILLS_TO_LEARN)); + } + } + else + { + player.sendPacket(asl); + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlameTowerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlameTowerInstance.java new file mode 100644 index 00000000..56323f11 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlameTowerInstance.java @@ -0,0 +1,111 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.GeoData; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * class for Flame Control Tower + * @author JIV + */ + +public class L2FlameTowerInstance extends L2Npc +{ + public L2FlameTowerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FlameTowerInstance); + setIsInvul(false); + } + + @Override + public boolean isAttackable() + { + // Attackable during siege by attacker only + return ((getCastle() != null) && (getCastle().getCastleId() > 0) && getCastle().getSiege().getIsInProgress()); + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + // Attackable during siege by attacker only + return ((attacker != null) && (attacker instanceof L2PcInstance) && (getCastle() != null) && (getCastle().getCastleId() > 0) && getCastle().getSiege().getIsInProgress() && getCastle().getSiege().checkIsAttacker(((L2PcInstance) attacker).getClan())); + } + + @Override + public void onForcedAttack(L2PcInstance player) + { + onAction(player); + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if (!canTarget(player)) + { + return; + } + + // Check if the L2PcInstance already target the L2NpcInstance + if (this != player.getTarget()) + { + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player + MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()); + player.sendPacket(my); + + // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_HP, (int) getStatus().getCurrentHp()); + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); + player.sendPacket(su); + + // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + if (isAutoAttackable(player) && (Math.abs(player.getZ() - getZ()) < 100 // Less then max height difference, delete check when geo + ) && GeoData.getInstance().canSeeTarget(player, this)) + { + // Notify the L2PcInstance AI with AI_INTENTION_INTERACT + player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); + } + } + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public boolean doDie(L2Character killer) + { + if (getCastle().getSiege().getIsInProgress()) + { + getCastle().getSiege().disableTraps(); + + } + return super.doDie(killer); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyMonsterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyMonsterInstance.java new file mode 100644 index 00000000..c6e521c1 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyMonsterInstance.java @@ -0,0 +1,34 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2FlyMonsterInstance extends L2MonsterInstance +{ + + public L2FlyMonsterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FlyMonsterInstance); + } + + @Override + public void onSpawn() + { + setIsFlying(true); + super.onSpawn(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyNpcInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyNpcInstance.java new file mode 100644 index 00000000..f7206250 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyNpcInstance.java @@ -0,0 +1,34 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2FlyNpcInstance extends L2NpcInstance +{ + + public L2FlyNpcInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FlyNpcInstance); + } + + @Override + public void onSpawn() + { + setIsFlying(true); + super.onSpawn(); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyRaidBossInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyRaidBossInstance.java new file mode 100644 index 00000000..0de3365d --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyRaidBossInstance.java @@ -0,0 +1,34 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2FlyRaidBossInstance extends L2RaidBossInstance +{ + + public L2FlyRaidBossInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FlyRaidBossInstance); + } + + @Override + public void onSpawn() + { + setIsFlying(true); + super.onSpawn(); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyTerrainObjectInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyTerrainObjectInstance.java new file mode 100644 index 00000000..277c6963 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FlyTerrainObjectInstance.java @@ -0,0 +1,56 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2FlyTerrainObjectInstance extends L2Npc +{ + + public L2FlyTerrainObjectInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FlyTerrainObjectInstance); + _isHideName = true; + } + + @Override + public void onSpawn() + { + setIsFlying(true); + super.onSpawn(); + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public void onActionShift(L2PcInstance player) + { + if (player.isGM()) + { + super.onActionShift(player); + } + else + { + player.sendPacket(ActionFailed.STATIC_PACKET); + } + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortBallistaInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortBallistaInstance.java new file mode 100644 index 00000000..34103127 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortBallistaInstance.java @@ -0,0 +1,112 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2FortBallistaInstance extends L2Npc +{ + public L2FortBallistaInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FortBallistaInstance); + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return true; + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + + if (getFort().getSiege().getIsInProgress()) + { + if (killer instanceof L2PcInstance) + { + L2PcInstance player = ((L2PcInstance) killer); + if ((player.getClan() != null) && (player.getClan().getLevel() >= 5)) + { + player.getClan().addReputationScore(Config.BALLISTA_POINTS, true); + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.BALLISTA_DESTROYED_CLAN_REPU_INCREASED)); + } + } + } + + return true; + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if (!canTarget(player)) + { + return; + } + + // Check if the L2PcInstance already target the L2NpcInstance + if (this != player.getTarget()) + { + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player + MyTargetSelected my = new MyTargetSelected(getObjectId(), 0); + player.sendPacket(my); + + // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + if (isAutoAttackable(player) && !isAlikeDead()) + { + if (Math.abs(player.getZ() - getZ()) < 600) // this max heigth difference might need some tweaking + { + player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); + } + } + // Calculate the distance between the L2PcInstance and the L2NpcInstance + if (!canInteract(player)) + { + // Notify the L2PcInstance AI with AI_INTENTION_INTERACT + player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this); + } + } + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public boolean hasRandomAnimation() + { + return false; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortCommanderInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortCommanderInstance.java new file mode 100644 index 00000000..fcec33f4 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortCommanderInstance.java @@ -0,0 +1,185 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.instancemanager.FortSiegeManager; +import com.l2jserver.gameserver.instancemanager.FortSiegeManager.SiegeSpawn; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2Spawn; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.network.serverpackets.NpcSay; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2FortCommanderInstance extends L2DefenderInstance +{ + + private boolean _canTalk; + + public L2FortCommanderInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FortCommanderInstance); + _canTalk = true; + } + + /** + * Return True if a siege is in progress and the L2Character attacker isn't a Defender.
    + *
    + * @param attacker The L2Character that the L2CommanderInstance try to attack + */ + @Override + public boolean isAutoAttackable(L2Character attacker) + { + if ((attacker == null) || !(attacker instanceof L2PcInstance)) + { + return false; + } + + boolean isFort = ((getFort() != null) && (getFort().getFortId() > 0) && getFort().getSiege().getIsInProgress() && !getFort().getSiege().checkIsDefender(((L2PcInstance) attacker).getClan())); + + // Attackable during siege by all except defenders + return (isFort); + } + + @Override + public void addDamageHate(L2Character attacker, int damage, int aggro) + { + if (attacker == null) + { + return; + } + + if (!(attacker instanceof L2FortCommanderInstance)) + { + super.addDamageHate(attacker, damage, aggro); + } + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + + if (getFort().getSiege().getIsInProgress()) + { + getFort().getSiege().killedCommander(this); + + } + + return true; + } + + /** + * This method forces guard to return to home location previously set + */ + @Override + public void returnHome() + { + if (!isInsideRadius(getSpawn().getLocx(), getSpawn().getLocy(), 200, false)) + { + if (Config.DEBUG) + { + _log.info(getObjectId() + ": moving home"); + } + setisReturningToSpawnPoint(true); + clearAggroList(); + + if (hasAI()) + { + getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(getSpawn().getLocx(), getSpawn().getLocy(), getSpawn().getLocz(), 0)); + } + } + } + + @Override + public final void addDamage(L2Character attacker, int damage, L2Skill skill) + { + L2Spawn spawn = getSpawn(); + if ((spawn != null) && canTalk()) + { + FastList commanders = FortSiegeManager.getInstance().getCommanderSpawnList(getFort().getFortId()); + for (SiegeSpawn spawn2 : commanders) + { + if (spawn2.getNpcId() == spawn.getNpcid()) + { + String text = ""; + switch (spawn2.getId()) + { + case 1: + text = "Attacking the enemy's reinforcements is necesary. Time to Die!"; + break; + case 2: + if (attacker instanceof L2Summon) + { + attacker = ((L2Summon) attacker).getOwner(); + } + text = "Everyone, concentrate your attacks on " + attacker.getName() + "! Show the enemy your resolve!"; + break; + case 3: + text = "Spirit of Fire, unleash your power! Burn the enemy!!"; + break; + } + if (!text.isEmpty()) + { + broadcastPacket(new NpcSay(getObjectId(), 1, getNpcId(), text)); + setCanTalk(false); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleTalkTask(), 10000); + } + } + } + } + super.addDamage(attacker, damage, skill); + } + + private class ScheduleTalkTask implements Runnable + { + + public ScheduleTalkTask() + { + } + + @Override + public void run() + { + setCanTalk(true); + } + } + + void setCanTalk(boolean val) + { + _canTalk = val; + } + + private boolean canTalk() + { + return _canTalk; + } + + @Override + public boolean hasRandomAnimation() + { + return false; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortDoormenInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortDoormenInstance.java new file mode 100644 index 00000000..11a546be --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortDoormenInstance.java @@ -0,0 +1,97 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; + +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2FortDoormenInstance extends L2DoormenInstance +{ + public L2FortDoormenInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2FortDoormenInstance); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + + if (!isOwnerClan(player)) + { + html.setFile(player.getHtmlPrefix(), "data/html/doormen/" + getTemplate().npcId + "-no.htm"); + } + else if (isUnderSiege()) + { + html.setFile(player.getHtmlPrefix(), "data/html/doormen/" + getTemplate().npcId + "-busy.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/doormen/" + getTemplate().npcId + ".htm"); + } + + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + + @Override + protected final void openDoors(L2PcInstance player, String command) + { + StringTokenizer st = new StringTokenizer(command.substring(10), ", "); + st.nextToken(); + + while (st.hasMoreTokens()) + { + getFort().openDoor(player, Integer.parseInt(st.nextToken())); + } + } + + @Override + protected final void closeDoors(L2PcInstance player, String command) + { + StringTokenizer st = new StringTokenizer(command.substring(11), ", "); + st.nextToken(); + + while (st.hasMoreTokens()) + { + getFort().closeDoor(player, Integer.parseInt(st.nextToken())); + } + } + + @Override + protected final boolean isOwnerClan(L2PcInstance player) + { + if ((player.getClan() != null) && (getFort() != null) && (getFort().getOwnerClan() != null)) + { + if (player.getClanId() == getFort().getOwnerClan().getClanId()) + { + return true; + } + } + return false; + } + + @Override + protected final boolean isUnderSiege() + { + return getFort().getZone().isActive(); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortEnvoyInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortEnvoyInstance.java new file mode 100644 index 00000000..d39b142f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortEnvoyInstance.java @@ -0,0 +1,108 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; + +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2FortEnvoyInstance extends L2Npc +{ + public L2FortEnvoyInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2FortEnvoyInstance); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + String filename; + + if (!player.isClanLeader() || (getFort().getFortId() != player.getClan().getHasFort())) + { + filename = "data/html/fortress/envoy-noclan.htm"; + } + else if (getFort().getFortState() == 0) + { + filename = "data/html/fortress/envoy.htm"; + } + else + { + filename = "data/html/fortress/envoy-no.htm"; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%castleName%", String.valueOf(CastleManager.getInstance().getCastleById(getFort().getCastleIdFromEnvoy(getNpcId())).getName())); + player.sendPacket(html); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + StringTokenizer st = new StringTokenizer(command, " "); + String actualCommand = st.nextToken(); // Get actual command + + String par = ""; + if (st.countTokens() >= 1) + { + par = st.nextToken(); + } + + if (actualCommand.equalsIgnoreCase("select")) + { + int val = 0; + try + { + val = Integer.parseInt(par); + } + catch (IndexOutOfBoundsException ioobe) + { + } + catch (NumberFormatException nfe) + { + } + + int castleId = 0; + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + if (val == 2) + { + castleId = getFort().getCastleIdFromEnvoy(getNpcId()); + if (CastleManager.getInstance().getCastleById(castleId).getOwnerId() < 1) + { + html.setHtml("Contact is currently not possible, " + CastleManager.getInstance().getCastleById(castleId).getName() + " Castle isn't currently owned by clan."); + player.sendPacket(html); + return; + } + } + getFort().setFortState(val, castleId); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/envoy-ok.htm"); + html.replace("%castleName%", String.valueOf(CastleManager.getInstance().getCastleById(getFort().getCastleIdFromEnvoy(getNpcId())).getName())); + player.sendPacket(html); + } + else + { + super.onBypassFeedback(player, command); + } + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortLogisticsInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortLogisticsInstance.java new file mode 100644 index 00000000..b6ac832d --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortLogisticsInstance.java @@ -0,0 +1,254 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; + +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.idfactory.IdFactory; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author Vice + */ +public class L2FortLogisticsInstance extends L2MerchantInstance +{ + private static final int BLOOD_OATH = 9910; + private static final int[] SUPPLY_BOX_IDS = + { + 35665, + 35697, + 35734, + 35766, + 35803, + 35834, + 35866, + 35903, + 35935, + 35973, + 36010, + 36042, + 36080, + 36117, + 36148, + 36180, + 36218, + 36256, + 36293, + 36325, + 36363 + }; + + public L2FortLogisticsInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2FortLogisticsInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + // BypassValidation Exploit plug. + if (player.getLastFolkNPC().getObjectId() != getObjectId()) + { + return; + } + + StringTokenizer st = new StringTokenizer(command, " "); + String actualCommand = st.nextToken(); // Get actual command + + String par = ""; + if (st.countTokens() >= 1) + { + par = st.nextToken(); + } + + if (actualCommand.equalsIgnoreCase("Chat")) + { + int val = 0; + try + { + val = Integer.parseInt(par); + } + catch (IndexOutOfBoundsException ioobe) + { + } + catch (NumberFormatException nfe) + { + } + + showMessageWindow(player, val); + } + else if (actualCommand.equalsIgnoreCase("rewards")) + { + if (player.isClanLeader() && (getFort().getOwnerClan() != null) && (player.getClan() == getFort().getOwnerClan())) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-rewards.htm"); + int blood = getFort().getBloodOathReward(); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%bloodoath%", String.valueOf(blood)); + player.sendPacket(html); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-noprivs.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("blood")) + { + if ((player.getClan() != null) && (getFort().getOwnerClan() != null) && (player.getClan() == getFort().getOwnerClan()) && player.isClanLeader()) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + int blood = getFort().getBloodOathReward(); + if (blood > 0) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-blood.htm"); + player.addItem("Quest", BLOOD_OATH, blood, this, true); + getFort().setBloodOathReward(0); + getFort().saveFortVariables(); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-noblood.htm"); + } + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-noprivs.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("supplylvl")) + { + if ((player.getClan() != null) && (getFort().getOwnerClan() != null) && (player.getClan() == getFort().getOwnerClan()) && (getFort().getFortState() == 2)) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + if (player.isClanLeader()) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-supplylvl.htm"); + html.replace("%supplylvl%", String.valueOf(getFort().getSupplyLvL())); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-noprivs.htm"); + } + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-1.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("supply")) + { + if ((player.getClan() != null) && (getFort().getOwnerClan() != null) && (player.getClan() == getFort().getOwnerClan()) && player.isClanLeader()) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + if (getFort().getSiege().getIsInProgress()) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-siege.htm"); + } + else + { + int level = getFort().getSupplyLvL(); + if (level > 0) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-supply.htm"); + // spawn box + L2NpcTemplate BoxTemplate = NpcTable.getInstance().getTemplate(SUPPLY_BOX_IDS[level - 1]); + L2MonsterInstance box = new L2MonsterInstance(IdFactory.getInstance().getNextId(), BoxTemplate); + box.setCurrentHp(box.getMaxHp()); + box.setCurrentMp(box.getMaxMp()); + box.setHeading(0); + // L2World.getInstance().storeObject(box); + box.spawnMe(getX() - 23, getY() + 41, getZ()); + + getFort().setSupplyLvL(0); + getFort().saveFortVariables(); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-nosupply.htm"); + } + } + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/logistics-noprivs.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + else + { + super.onBypassFeedback(player, command); + } + } + + @Override + public void showChatWindow(L2PcInstance player) + { + showMessageWindow(player, 0); + } + + private void showMessageWindow(L2PcInstance player, int val) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + String filename; + + if (val == 0) + { + filename = "data/html/fortress/logistics.htm"; + } + else + { + filename = "data/html/fortress/logistics-" + val + ".htm"; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcId%", String.valueOf(getNpcId())); + if (getFort().getOwnerClan() != null) + { + html.replace("%clanname%", getFort().getOwnerClan().getName()); + } + else + { + html.replace("%clanname%", "NPC"); + } + player.sendPacket(html); + } + + @Override + public boolean hasRandomAnimation() + { + return false; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortManagerInstance.java new file mode 100644 index 00000000..5f2e8946 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortManagerInstance.java @@ -0,0 +1,1028 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.text.SimpleDateFormat; +import java.util.StringTokenizer; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.TeleportLocationTable; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2TeleportLocation; +import com.l2jserver.gameserver.model.entity.Fort; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SortedWareHouseWithdrawalList; +import com.l2jserver.gameserver.network.serverpackets.SortedWareHouseWithdrawalList.WarehouseListType; +import com.l2jserver.gameserver.network.serverpackets.WareHouseDepositList; +import com.l2jserver.gameserver.network.serverpackets.WareHouseWithdrawalList; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.templates.skills.L2SkillType; + +/** + * Fortress Foreman implementation used for: Area Teleports, Support Magic, Clan Warehouse, Exp Loss Reduction + */ +public class L2FortManagerInstance extends L2MerchantInstance +{ + protected static final int COND_ALL_FALSE = 0; + protected static final int COND_BUSY_BECAUSE_OF_SIEGE = 1; + protected static final int COND_OWNER = 2; + + public L2FortManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FortManagerInstance); + } + + @Override + public boolean isWarehouse() + { + return true; + } + + private void sendHtmlMessage(L2PcInstance player, NpcHtmlMessage html) + { + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcId%", String.valueOf(getNpcId())); + player.sendPacket(html); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + // BypassValidation Exploit plug. + if (player.getLastFolkNPC().getObjectId() != getObjectId()) + { + return; + } + SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm"); + int condition = validateCondition(player); + if (condition <= COND_ALL_FALSE) + { + return; + } + else if (condition == COND_BUSY_BECAUSE_OF_SIEGE) + { + return; + } + else if (condition == COND_OWNER) + { + StringTokenizer st = new StringTokenizer(command, " "); + String actualCommand = st.nextToken(); // Get actual command + + String val = ""; + if (st.countTokens() >= 1) + { + val = st.nextToken(); + } + if (actualCommand.equalsIgnoreCase("expel")) + { + if ((player.getClanPrivileges() & L2Clan.CP_CS_DISMISS) == L2Clan.CP_CS_DISMISS) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-expel.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-noprivs.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("banish_foreigner")) + { + if ((player.getClanPrivileges() & L2Clan.CP_CS_DISMISS) == L2Clan.CP_CS_DISMISS) + { + getFort().banishForeigners(); // Move non-clan members off fortress area + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-expeled.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-noprivs.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("receive_report")) + { + if (getFort().getFortState() < 2) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-report.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + if (Config.FS_MAX_OWN_TIME > 0) + { + int hour = (int) Math.floor(getFort().getTimeTillRebelArmy() / 3600); + int minutes = (int) (Math.floor(getFort().getTimeTillRebelArmy() - (hour * 3600)) / 60); + html.replace("%hr%", String.valueOf(hour)); + html.replace("%min%", String.valueOf(minutes)); + } + else + { + int hour = (int) Math.floor(getFort().getOwnedTime() / 3600); + int minutes = (int) (Math.floor(getFort().getOwnedTime() - (hour * 3600)) / 60); + html.replace("%hr%", String.valueOf(hour)); + html.replace("%min%", String.valueOf(minutes)); + } + player.sendPacket(html); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-castlereport.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + int hour, minutes; + if (Config.FS_MAX_OWN_TIME > 0) + { + hour = (int) Math.floor(getFort().getTimeTillRebelArmy() / 3600); + minutes = (int) (Math.floor(getFort().getTimeTillRebelArmy() - (hour * 3600)) / 60); + html.replace("%hr%", String.valueOf(hour)); + html.replace("%min%", String.valueOf(minutes)); + } + else + { + hour = (int) Math.floor(getFort().getOwnedTime() / 3600); + minutes = (int) (Math.floor(getFort().getOwnedTime() - (hour * 3600)) / 60); + html.replace("%hr%", String.valueOf(hour)); + html.replace("%min%", String.valueOf(minutes)); + } + hour = (int) Math.floor(getFort().getTimeTillNextFortUpdate() / 3600); + minutes = (int) (Math.floor(getFort().getTimeTillNextFortUpdate() - (hour * 3600)) / 60); + html.replace("%castle%", CastleManager.getInstance().getCastleById(getFort().getCastleId()).getName()); + html.replace("%hr2%", String.valueOf(hour)); + html.replace("%min2%", String.valueOf(minutes)); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("operate_door")) // door + // control + { + if ((player.getClanPrivileges() & L2Clan.CP_CS_OPEN_DOOR) == L2Clan.CP_CS_OPEN_DOOR) + { + if (!val.isEmpty()) + { + boolean open = (Integer.parseInt(val) == 1); + while (st.hasMoreTokens()) + { + getFort().openCloseDoor(player, Integer.parseInt(st.nextToken()), open); + } + if (open) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-opened.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-closed.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/" + getTemplate().npcId + "-d.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-noprivs.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("manage_vault")) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + if ((player.getClanPrivileges() & L2Clan.CP_CL_VIEW_WAREHOUSE) == L2Clan.CP_CL_VIEW_WAREHOUSE) + { + if (val.equalsIgnoreCase("deposit")) + { + showVaultWindowDeposit(player); + } + else if (val.equalsIgnoreCase("withdraw")) + { + if (Config.L2JMOD_ENABLE_WAREHOUSESORTING_CLAN) + { + String htmFile = "data/html/mods/WhSortedC.htm"; + String htmContent = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), htmFile); + if (htmContent != null) + { + NpcHtmlMessage npcHtmlMessage = new NpcHtmlMessage(getObjectId()); + npcHtmlMessage.setHtml(htmContent); + npcHtmlMessage.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(npcHtmlMessage); + } + else + { + _log.warning("Missing htm: " + htmFile + " !"); + } + } + else + { + showVaultWindowWithdraw(player, null, (byte) 0); + } + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-vault.htm"); + sendHtmlMessage(player, html); + } + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-noprivs.htm"); + sendHtmlMessage(player, html); + } + return; + } + else if (actualCommand.startsWith("WithdrawSortedC")) + { + String param[] = command.split("_"); + if (param.length > 2) + { + showVaultWindowWithdraw(player, WarehouseListType.valueOf(param[1]), SortedWareHouseWithdrawalList.getOrder(param[2])); + } + else if (param.length > 1) + { + showVaultWindowWithdraw(player, WarehouseListType.valueOf(param[1]), SortedWareHouseWithdrawalList.A2Z); + } + else + { + showVaultWindowWithdraw(player, WarehouseListType.ALL, SortedWareHouseWithdrawalList.A2Z); + } + return; + } + else if (actualCommand.equalsIgnoreCase("functions")) + { + if (val.equalsIgnoreCase("tele")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if (getFort().getFunction(Fort.FUNC_TELEPORT) == null) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-nac.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/" + getNpcId() + "-t" + getFort().getFunction(Fort.FUNC_TELEPORT).getLvl() + ".htm"); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("support")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if (getFort().getFunction(Fort.FUNC_SUPPORT) == null) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-nac.htm"); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/support" + getFort().getFunction(Fort.FUNC_SUPPORT).getLvl() + ".htm"); + html.replace("%mp%", String.valueOf((int) getCurrentMp())); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("back")) + { + showChatWindow(player); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-functions.htm"); + if (getFort().getFunction(Fort.FUNC_RESTORE_EXP) != null) + { + html.replace("%xp_regen%", String.valueOf(getFort().getFunction(Fort.FUNC_RESTORE_EXP).getLvl())); + } + else + { + html.replace("%xp_regen%", "0"); + } + if (getFort().getFunction(Fort.FUNC_RESTORE_HP) != null) + { + html.replace("%hp_regen%", String.valueOf(getFort().getFunction(Fort.FUNC_RESTORE_HP).getLvl())); + } + else + { + html.replace("%hp_regen%", "0"); + } + if (getFort().getFunction(Fort.FUNC_RESTORE_MP) != null) + { + html.replace("%mp_regen%", String.valueOf(getFort().getFunction(Fort.FUNC_RESTORE_MP).getLvl())); + } + else + { + html.replace("%mp_regen%", "0"); + } + sendHtmlMessage(player, html); + } + return; + } + else if (actualCommand.equalsIgnoreCase("manage")) + { + if ((player.getClanPrivileges() & L2Clan.CP_CS_SET_FUNCTIONS) == L2Clan.CP_CS_SET_FUNCTIONS) + { + if (val.equalsIgnoreCase("recovery")) + { + if (st.countTokens() >= 1) + { + if (getFort().getOwnerClan() == null) + { + player.sendMessage("This fortress have no owner, you cannot change configuration"); + return; + } + val = st.nextToken(); + if (val.equalsIgnoreCase("hp_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-cancel.htm"); + html.replace("%apply%", "recovery hp 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("mp_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-cancel.htm"); + html.replace("%apply%", "recovery mp 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("exp_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-cancel.htm"); + html.replace("%apply%", "recovery exp 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_hp")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-apply.htm"); + html.replace("%name%", "(HP Recovery Device)"); + int percent = Integer.parseInt(val); + int cost; + switch (percent) + { + case 300: + cost = Config.FS_HPREG1_FEE; + break; + default: // 400 + cost = Config.FS_HPREG2_FEE; + break; + } + + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.FS_HPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Provides additional HP recovery for clan members in the fortress." + String.valueOf(percent) + "%"); + html.replace("%apply%", "recovery hp " + String.valueOf(percent)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_mp")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-apply.htm"); + html.replace("%name%", "(MP Recovery)"); + int percent = Integer.parseInt(val); + int cost; + switch (percent) + { + case 40: + cost = Config.FS_MPREG1_FEE; + break; + default: // 50 + cost = Config.FS_MPREG2_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.FS_MPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Provides additional MP recovery for clan members in the fortress." + String.valueOf(percent) + "%"); + html.replace("%apply%", "recovery mp " + String.valueOf(percent)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_exp")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-apply.htm"); + html.replace("%name%", "(EXP Recovery Device)"); + int percent = Integer.parseInt(val); + int cost; + switch (percent) + { + case 45: + cost = Config.FS_EXPREG1_FEE; + break; + default: // 50 + cost = Config.FS_EXPREG2_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.FS_EXPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Restores the Exp of any clan member who is resurrected in the fortress." + String.valueOf(percent) + "%"); + html.replace("%apply%", "recovery exp " + String.valueOf(percent)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("hp")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Mp editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-apply_confirmed.htm"); + if (getFort().getFunction(Fort.FUNC_RESTORE_HP) != null) + { + if (getFort().getFunction(Fort.FUNC_RESTORE_HP).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-used.htm"); + html.replace("%val%", String.valueOf(val) + "%"); + sendHtmlMessage(player, html); + return; + } + } + int percent = Integer.parseInt(val); + switch (percent) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-cancel_confirmed.htm"); + break; + case 300: + fee = Config.FS_HPREG1_FEE; + break; + default: // 400 + fee = Config.FS_HPREG2_FEE; + break; + } + if (!getFort().updateFunctions(player, Fort.FUNC_RESTORE_HP, percent, fee, Config.FS_HPREG_FEE_RATIO, (getFort().getFunction(Fort.FUNC_RESTORE_HP) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/low_adena.htm"); + sendHtmlMessage(player, html); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("mp")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Mp editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-apply_confirmed.htm"); + if (getFort().getFunction(Fort.FUNC_RESTORE_MP) != null) + { + if (getFort().getFunction(Fort.FUNC_RESTORE_MP).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-used.htm"); + html.replace("%val%", String.valueOf(val) + "%"); + sendHtmlMessage(player, html); + return; + } + } + int percent = Integer.parseInt(val); + switch (percent) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-cancel_confirmed.htm"); + break; + case 40: + fee = Config.FS_MPREG1_FEE; + break; + default: // 50 + fee = Config.FS_MPREG2_FEE; + break; + } + if (!getFort().updateFunctions(player, Fort.FUNC_RESTORE_MP, percent, fee, Config.FS_MPREG_FEE_RATIO, (getFort().getFunction(Fort.FUNC_RESTORE_MP) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/low_adena.htm"); + sendHtmlMessage(player, html); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("exp")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Exp editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-apply_confirmed.htm"); + if (getFort().getFunction(Fort.FUNC_RESTORE_EXP) != null) + { + if (getFort().getFunction(Fort.FUNC_RESTORE_EXP).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-used.htm"); + html.replace("%val%", String.valueOf(val) + "%"); + sendHtmlMessage(player, html); + return; + } + } + int percent = Integer.parseInt(val); + switch (percent) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-cancel_confirmed.htm"); + break; + case 45: + fee = Config.FS_EXPREG1_FEE; + break; + default: // 50 + fee = Config.FS_EXPREG2_FEE; + break; + } + if (!getFort().updateFunctions(player, Fort.FUNC_RESTORE_EXP, percent, fee, Config.FS_EXPREG_FEE_RATIO, (getFort().getFunction(Fort.FUNC_RESTORE_EXP) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/low_adena.htm"); + sendHtmlMessage(player, html); + } + sendHtmlMessage(player, html); + } + return; + } + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/edit_recovery.htm"); + String hp = "[300%][400%]"; + String exp = "[45%][50%]"; + String mp = "[40%][50%]"; + if (getFort().getFunction(Fort.FUNC_RESTORE_HP) != null) + { + html.replace("%hp_recovery%", String.valueOf(getFort().getFunction(Fort.FUNC_RESTORE_HP).getLvl()) + "% (" + String.valueOf(getFort().getFunction(Fort.FUNC_RESTORE_HP).getLease()) + "Adena /" + String.valueOf(Config.FS_HPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%hp_period%", "Withdraw the fee for the next time at " + format.format(getFort().getFunction(Fort.FUNC_RESTORE_HP).getEndTime())); + html.replace("%change_hp%", "[Deactivate]" + hp); + } + else + { + html.replace("%hp_recovery%", "none"); + html.replace("%hp_period%", "none"); + html.replace("%change_hp%", hp); + } + if (getFort().getFunction(Fort.FUNC_RESTORE_EXP) != null) + { + html.replace("%exp_recovery%", String.valueOf(getFort().getFunction(Fort.FUNC_RESTORE_EXP).getLvl()) + "% (" + String.valueOf(getFort().getFunction(Fort.FUNC_RESTORE_EXP).getLease()) + "Adena /" + String.valueOf(Config.FS_EXPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%exp_period%", "Withdraw the fee for the next time at " + format.format(getFort().getFunction(Fort.FUNC_RESTORE_EXP).getEndTime())); + html.replace("%change_exp%", "[Deactivate]" + exp); + } + else + { + html.replace("%exp_recovery%", "none"); + html.replace("%exp_period%", "none"); + html.replace("%change_exp%", exp); + } + if (getFort().getFunction(Fort.FUNC_RESTORE_MP) != null) + { + html.replace("%mp_recovery%", String.valueOf(getFort().getFunction(Fort.FUNC_RESTORE_MP).getLvl()) + "% (" + String.valueOf(getFort().getFunction(Fort.FUNC_RESTORE_MP).getLease()) + "Adena /" + String.valueOf(Config.FS_MPREG_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%mp_period%", "Withdraw the fee for the next time at " + format.format(getFort().getFunction(Fort.FUNC_RESTORE_MP).getEndTime())); + html.replace("%change_mp%", "[Deactivate]" + mp); + } + else + { + html.replace("%mp_recovery%", "none"); + html.replace("%mp_period%", "none"); + html.replace("%change_mp%", mp); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("other")) + { + if (st.countTokens() >= 1) + { + if (getFort().getOwnerClan() == null) + { + player.sendMessage("This fortress have no owner, you cannot change configuration"); + return; + } + val = st.nextToken(); + if (val.equalsIgnoreCase("tele_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-cancel.htm"); + html.replace("%apply%", "other tele 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("support_cancel")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-cancel.htm"); + html.replace("%apply%", "other support 0"); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_support")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-apply.htm"); + html.replace("%name%", "Insignia (Supplementary Magic)"); + int stage = Integer.parseInt(val); + int cost; + switch (stage) + { + case 1: + cost = Config.FS_SUPPORT1_FEE; + break; + default: + cost = Config.FS_SUPPORT2_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.FS_SUPPORT_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Enables the use of supplementary magic."); + html.replace("%apply%", "other support " + String.valueOf(stage)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("edit_tele")) + { + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-apply.htm"); + html.replace("%name%", "Mirror (Teleportation Device)"); + int stage = Integer.parseInt(val); + int cost; + switch (stage) + { + case 1: + cost = Config.FS_TELE1_FEE; + break; + default: + cost = Config.FS_TELE2_FEE; + break; + } + html.replace("%cost%", String.valueOf(cost) + "Adena /" + String.valueOf(Config.FS_TELE_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%use%", "Teleports clan members in a fort to the target Stage " + String.valueOf(stage) + " staging area"); + html.replace("%apply%", "other tele " + String.valueOf(stage)); + sendHtmlMessage(player, html); + return; + } + else if (val.equalsIgnoreCase("tele")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Tele editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-apply_confirmed.htm"); + if (getFort().getFunction(Fort.FUNC_TELEPORT) != null) + { + if (getFort().getFunction(Fort.FUNC_TELEPORT).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-used.htm"); + html.replace("%val%", "Stage " + String.valueOf(val)); + sendHtmlMessage(player, html); + return; + } + } + int lvl = Integer.parseInt(val); + switch (lvl) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-cancel_confirmed.htm"); + break; + case 1: + fee = Config.FS_TELE1_FEE; + break; + default: + fee = Config.FS_TELE2_FEE; + break; + } + if (!getFort().updateFunctions(player, Fort.FUNC_TELEPORT, lvl, fee, Config.FS_TELE_FEE_RATIO, (getFort().getFunction(Fort.FUNC_TELEPORT) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/low_adena.htm"); + sendHtmlMessage(player, html); + } + sendHtmlMessage(player, html); + } + return; + } + else if (val.equalsIgnoreCase("support")) + { + if (st.countTokens() >= 1) + { + int fee; + if (Config.DEBUG) + { + _log.warning("Support editing invoked"); + } + val = st.nextToken(); + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-apply_confirmed.htm"); + if (getFort().getFunction(Fort.FUNC_SUPPORT) != null) + { + if (getFort().getFunction(Fort.FUNC_SUPPORT).getLvl() == Integer.parseInt(val)) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-used.htm"); + html.replace("%val%", "Stage " + String.valueOf(val)); + sendHtmlMessage(player, html); + return; + } + } + int lvl = Integer.parseInt(val); + switch (lvl) + { + case 0: + fee = 0; + html.setFile(player.getHtmlPrefix(), "data/html/fortress/functions-cancel_confirmed.htm"); + break; + case 1: + fee = Config.FS_SUPPORT1_FEE; + break; + default: + fee = Config.FS_SUPPORT2_FEE; + break; + } + if (!getFort().updateFunctions(player, Fort.FUNC_SUPPORT, lvl, fee, Config.FS_SUPPORT_FEE_RATIO, (getFort().getFunction(Fort.FUNC_SUPPORT) == null))) + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/low_adena.htm"); + sendHtmlMessage(player, html); + } + else + { + sendHtmlMessage(player, html); + } + } + return; + } + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/edit_other.htm"); + String tele = "[Level 1][Level 2]"; + String support = "[Level 1][Level 2]"; + if (getFort().getFunction(Fort.FUNC_TELEPORT) != null) + { + html.replace("%tele%", "Stage " + String.valueOf(getFort().getFunction(Fort.FUNC_TELEPORT).getLvl()) + " (" + String.valueOf(getFort().getFunction(Fort.FUNC_TELEPORT).getLease()) + "Adena /" + String.valueOf(Config.FS_TELE_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%tele_period%", "Withdraw the fee for the next time at " + format.format(getFort().getFunction(Fort.FUNC_TELEPORT).getEndTime())); + html.replace("%change_tele%", "[Deactivate]" + tele); + } + else + { + html.replace("%tele%", "none"); + html.replace("%tele_period%", "none"); + html.replace("%change_tele%", tele); + } + if (getFort().getFunction(Fort.FUNC_SUPPORT) != null) + { + html.replace("%support%", "Stage " + String.valueOf(getFort().getFunction(Fort.FUNC_SUPPORT).getLvl()) + " (" + String.valueOf(getFort().getFunction(Fort.FUNC_SUPPORT).getLease()) + "Adena /" + String.valueOf(Config.FS_SUPPORT_FEE_RATIO / 1000 / 60 / 60 / 24) + " Day)"); + html.replace("%support_period%", "Withdraw the fee for the next time at " + format.format(getFort().getFunction(Fort.FUNC_SUPPORT).getEndTime())); + html.replace("%change_support%", "[Deactivate]" + support); + } + else + { + html.replace("%support%", "none"); + html.replace("%support_period%", "none"); + html.replace("%change_support%", support); + } + sendHtmlMessage(player, html); + } + else if (val.equalsIgnoreCase("back")) + { + showChatWindow(player); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/manage.htm"); + sendHtmlMessage(player, html); + } + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-noprivs.htm"); + sendHtmlMessage(player, html); + } + return; + } + else if (actualCommand.equalsIgnoreCase("support")) + { + setTarget(player); + L2Skill skill; + if (val.isEmpty()) + { + return; + } + + try + { + int skill_id = Integer.parseInt(val); + try + { + if (getFort().getFunction(Fort.FUNC_SUPPORT) == null) + { + return; + } + if (getFort().getFunction(Fort.FUNC_SUPPORT).getLvl() == 0) + { + return; + } + NpcHtmlMessage html = new NpcHtmlMessage(1); + int skill_lvl = 0; + if (st.countTokens() >= 1) + { + skill_lvl = Integer.parseInt(st.nextToken()); + } + skill = SkillTable.getInstance().getInfo(skill_id, skill_lvl); + if (skill.getSkillType() == L2SkillType.SUMMON) + { + player.doCast(skill); + } + else + { + if (!((skill.getMpConsume() + skill.getMpInitialConsume()) > getCurrentMp())) + { + this.doCast(skill); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/fortress/support-no_mana.htm"); + html.replace("%mp%", String.valueOf((int) getCurrentMp())); + sendHtmlMessage(player, html); + return; + } + } + html.setFile(player.getHtmlPrefix(), "data/html/fortress/support-done.htm"); + html.replace("%mp%", String.valueOf((int) getCurrentMp())); + sendHtmlMessage(player, html); + } + catch (Exception e) + { + player.sendMessage("Invalid skill level, contact your admin!"); + } + } + catch (Exception e) + { + player.sendMessage("Invalid skill level, contact your admin!"); + } + return; + } + else if (actualCommand.equalsIgnoreCase("support_back")) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + if (getFort().getFunction(Fort.FUNC_SUPPORT).getLvl() == 0) + { + return; + } + html.setFile(player.getHtmlPrefix(), "data/html/fortress/support" + getFort().getFunction(Fort.FUNC_SUPPORT).getLvl() + ".htm"); + html.replace("%mp%", String.valueOf((int) getStatus().getCurrentMp())); + sendHtmlMessage(player, html); + return; + } + else if (actualCommand.equalsIgnoreCase("goto")) + { + int whereTo = Integer.parseInt(val); + doTeleport(player, whereTo); + return; + } + super.onBypassFeedback(player, command); + } + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + String filename = "data/html/fortress/foreman-no.htm"; + + int condition = validateCondition(player); + if (condition > COND_ALL_FALSE) + { + if (condition == COND_BUSY_BECAUSE_OF_SIEGE) + { + filename = "data/html/fortress/foreman-busy.htm"; // Busy because of siege + } + else if (condition == COND_OWNER) + { + filename = "data/html/fortress/foreman.htm"; // Owner message window + } + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } + + private void doTeleport(L2PcInstance player, int val) + { + if (Config.DEBUG) + { + _log.warning("doTeleport(L2PcInstance player, int val) is called"); + } + L2TeleportLocation list = TeleportLocationTable.getInstance().getTemplate(val); + if (list != null) + { + if (player.destroyItemByItemId("Teleport", list.getItemId(), list.getPrice(), this, true)) + { + if (Config.DEBUG) + { + _log.warning("Teleporting player " + player.getName() + " for Fortress to new location: " + list.getLocX() + ":" + list.getLocY() + ":" + list.getLocZ()); + } + player.teleToLocation(list.getLocX(), list.getLocY(), list.getLocZ()); + } + } + else + { + _log.warning("No teleport destination with id:" + val); + } + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + protected int validateCondition(L2PcInstance player) + { + if ((getFort() != null) && (getFort().getFortId() > 0)) + { + if (player.getClan() != null) + { + if (getFort().getZone().isActive()) + { + return COND_BUSY_BECAUSE_OF_SIEGE; // Busy because of siege + } + else if ((getFort().getOwnerClan() != null) && (getFort().getOwnerClan().getClanId() == player.getClanId())) + { + return COND_OWNER; // Owner + } + } + } + return COND_ALL_FALSE; + } + + private void showVaultWindowDeposit(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + player.setActiveWarehouse(player.getClan().getWarehouse()); + player.sendPacket(new WareHouseDepositList(player, WareHouseDepositList.CLAN)); + } + + private void showVaultWindowWithdraw(L2PcInstance player, WarehouseListType itemtype, byte sortorder) + { + if (player.isClanLeader() || ((player.getClanPrivileges() & L2Clan.CP_CL_VIEW_WAREHOUSE) == L2Clan.CP_CL_VIEW_WAREHOUSE)) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + player.setActiveWarehouse(player.getClan().getWarehouse()); + if (itemtype != null) + { + player.sendPacket(new SortedWareHouseWithdrawalList(player, WareHouseWithdrawalList.CLAN, itemtype, sortorder)); + } + else + { + player.sendPacket(new WareHouseWithdrawalList(player, WareHouseWithdrawalList.CLAN)); + } + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/foreman-noprivs.htm"); + sendHtmlMessage(player, html); + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortSiegeNpcInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortSiegeNpcInstance.java new file mode 100644 index 00000000..fd1a56d1 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortSiegeNpcInstance.java @@ -0,0 +1,68 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author Vice + */ +public class L2FortSiegeNpcInstance extends L2NpcWalkerInstance +{ + public L2FortSiegeNpcInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2FortSiegeNpcInstance); + } + + @Override + public void showChatWindow(L2PcInstance player, int val) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + String filename; + + if (val == 0) + { + filename = "data/html/fortress/merchant.htm"; + } + else + { + filename = "data/html/fortress/merchant-" + val + ".htm"; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcId%", String.valueOf(getNpcId())); + if (getFort().getOwnerClan() != null) + { + html.replace("%clanname%", getFort().getOwnerClan().getName()); + } + else + { + html.replace("%clanname%", "NPC"); + } + player.sendPacket(html); + } + + @Override + public boolean hasRandomAnimation() + { + return false; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortSupportCaptainInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortSupportCaptainInstance.java new file mode 100644 index 00000000..91c4daa8 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortSupportCaptainInstance.java @@ -0,0 +1,244 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; + +import javolution.util.FastList; + +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.model.L2SkillLearn; +import com.l2jserver.gameserver.model.L2SquadTrainer; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.Rnd; + +/** + * @author Vice + */ +public class L2FortSupportCaptainInstance extends L2MerchantInstance implements L2SquadTrainer +{ + public L2FortSupportCaptainInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2FortSupportCaptainInstance); + } + + private final static int[] TalismanIds = + { + 9914, + 9915, + 9917, + 9918, + 9919, + 9920, + 9921, + 9922, + 9923, + 9924, + 9926, + 9927, + 9928, + 9930, + 9931, + 9932, + 9933, + 9934, + 9935, + 9936, + 9937, + 9938, + 9939, + 9940, + 9941, + 9942, + 9943, + 9944, + 9945, + 9946, + 9947, + 9948, + 9949, + 9950, + 9951, + 9952, + 9953, + 9954, + 9955, + 9956, + 9957, + 9958, + 9959, + 9960, + 9961, + 9962, + 9963, + 9964, + 9965, + 9966, + 10141, + 10142, + 10158 + }; + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + // BypassValidation Exploit plug. + if (player.getLastFolkNPC().getObjectId() != getObjectId()) + { + return; + } + + StringTokenizer st = new StringTokenizer(command, " "); + String actualCommand = st.nextToken(); // Get actual command + + String par = ""; + if (st.countTokens() >= 1) + { + par = st.nextToken(); + } + + if (actualCommand.equalsIgnoreCase("Chat")) + { + int val = 0; + try + { + val = Integer.parseInt(par); + } + catch (IndexOutOfBoundsException ioobe) + { + } + catch (NumberFormatException nfe) + { + } + showMessageWindow(player, val); + } + else if (actualCommand.equalsIgnoreCase("ExchangeKE")) + { + int item = TalismanIds[Rnd.get(TalismanIds.length)]; + + if (player.destroyItemByItemId("FortSupportUnit", 9912, 10, this, false)) + { + final SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + msg.addItemName(9912); + msg.addNumber(10); + player.sendPacket(msg); + + player.addItem("FortSupportUnit", item, 1, player, true); + + showChatWindow(player, "data/html/fortress/supportunit-talisman.htm"); + } + else + { + showChatWindow(player, "data/html/fortress/supportunit-noepau.htm"); + } + } + else if (command.equals("subskills")) + { + if (player.isClanLeader()) + { + final FastList skills = SkillTreesData.getInstance().getAvailableSubPledgeSkills(player.getClan()); + final AcquireSkillList asl = new AcquireSkillList(SkillType.SubPledge); + int count = 0; + + for (L2SkillLearn s : skills) + { + if (SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()) != null) + { + asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), 0); + count++; + } + } + + if (count == 0) + { + player.sendPacket(SystemMessageId.NO_MORE_SKILLS_TO_LEARN); + } + else + { + player.sendPacket(asl); + } + } + else + { + showChatWindow(player, "data/html/fortress/supportunit-nosquad.htm"); + } + } + else + { + super.onBypassFeedback(player, command); + } + } + + @Override + public void showChatWindow(L2PcInstance player) + { + if ((player.getClan() == null) || (getFort().getOwnerClan() == null) || (player.getClan() != getFort().getOwnerClan())) + { + final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/fortress/supportunit-noclan.htm"); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + return; + } + showMessageWindow(player, 0); + } + + private void showMessageWindow(L2PcInstance player, int val) + { + String filename; + + if (val == 0) + { + filename = "data/html/fortress/supportunit.htm"; + } + else + { + filename = "data/html/fortress/supportunit-" + val + ".htm"; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcId%", String.valueOf(getNpcId())); + if (getFort().getOwnerClan() != null) + { + html.replace("%clanname%", getFort().getOwnerClan().getName()); + } + else + { + html.replace("%clanname%", "NPC"); + } + player.sendPacket(html); + } + + @Override + public boolean hasRandomAnimation() + { + return false; + } + + @Override + public void showSubUnitSkillList(L2PcInstance player) + { + onBypassFeedback(player, "subskills"); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortWyvernManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortWyvernManagerInstance.java new file mode 100644 index 00000000..e5f80f89 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FortWyvernManagerInstance.java @@ -0,0 +1,47 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2FortWyvernManagerInstance extends L2WyvernManagerInstance +{ + public L2FortWyvernManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + } + + @Override + public final boolean isOwnerClan(L2PcInstance player) + { + if ((player.getClan() != null) && (getFort() != null) && (getFort().getOwnerClan() != null)) + { + if ((player.getClanId() == getFort().getOwnerClan().getClanId()) && player.isClanLeader()) + { + return true; + } + } + return false; + } + + /** + * @see com.l2jserver.gameserver.model.actor.instance.L2WyvernManagerInstance#isInSiege(com.l2jserver.gameserver.model.actor.instance.L2PcInstance) + */ + @Override + public boolean isInSiege() + { + return getFort().getZone().isActive(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FriendlyMobInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FriendlyMobInstance.java new file mode 100644 index 00000000..a7664af2 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2FriendlyMobInstance.java @@ -0,0 +1,61 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.knownlist.FriendlyMobKnownList; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * This class represents Friendly Mobs lying over the world. These friendly mobs should only attack players with karma > 0 and it is always aggro, since it just attacks players with karma + * @version $Revision: 1.20.4.6 $ $Date: 2005/07/23 16:13:39 $ + */ +public class L2FriendlyMobInstance extends L2Attackable +{ + public L2FriendlyMobInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2FriendlyMobInstance); + } + + @Override + public final FriendlyMobKnownList getKnownList() + { + return (FriendlyMobKnownList) super.getKnownList(); + } + + @Override + public void initKnownList() + { + setKnownList(new FriendlyMobKnownList(this)); + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + if (attacker instanceof L2PcInstance) + { + return ((L2PcInstance) attacker).getKarma() > 0; + } + return false; + } + + @Override + public boolean isAggressive() + { + return true; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2GrandBossInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2GrandBossInstance.java new file mode 100644 index 00000000..4db8af78 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2GrandBossInstance.java @@ -0,0 +1,133 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.instancemanager.RaidBossPointsManager; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.entity.Hero; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.Rnd; + +/** + * This class manages all Grand Bosses. + * @version $Revision: 1.0.0.0 $ $Date: 2006/06/16 $ + */ +public final class L2GrandBossInstance extends L2MonsterInstance +{ + private static final int BOSS_MAINTENANCE_INTERVAL = 10000; + private boolean _useRaidCurse = true; + + /** + * Constructor for L2GrandBossInstance. This represent all grandbosses. + * @param objectId ID of the instance + * @param template L2NpcTemplate of the instance + */ + public L2GrandBossInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2GrandBossInstance); + setIsRaid(true); + } + + @Override + protected int getMaintenanceInterval() + { + return BOSS_MAINTENANCE_INTERVAL; + } + + @Override + public void onSpawn() + { + setIsNoRndWalk(true); + super.onSpawn(); + } + + /** + * @see com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance#doDie(com.l2jserver.gameserver.model.actor.L2Character) + */ + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + L2PcInstance player = null; + + if (killer instanceof L2PcInstance) + { + player = (L2PcInstance) killer; + } + else if (killer instanceof L2Summon) + { + player = ((L2Summon) killer).getOwner(); + } + + if (player != null) + { + broadcastPacket(SystemMessage.getSystemMessage(SystemMessageId.RAID_WAS_SUCCESSFUL)); + if (player.getParty() != null) + { + for (L2PcInstance member : player.getParty().getPartyMembers()) + { + RaidBossPointsManager.getInstance().addPoints(member, getNpcId(), (getLevel() / 2) + Rnd.get(-5, 5)); + if (member.isNoble()) + { + Hero.getInstance().setRBkilled(member.getObjectId(), getNpcId()); + } + } + } + else + { + RaidBossPointsManager.getInstance().addPoints(player, getNpcId(), (getLevel() / 2) + Rnd.get(-5, 5)); + if (player.isNoble()) + { + Hero.getInstance().setRBkilled(player.getObjectId(), getNpcId()); + } + } + } + return true; + } + + @Override + public float getVitalityPoints(int damage) + { + return -super.getVitalityPoints(damage) / 100; + } + + @Override + public boolean useVitalityRate() + { + return false; + } + + public void setUseRaidCurse(boolean val) + { + _useRaidCurse = val; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.actor.L2Character#giveRaidCurse() + */ + @Override + public boolean giveRaidCurse() + { + return _useRaidCurse; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2GuardInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2GuardInstance.java new file mode 100644 index 00000000..8cdce73b --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2GuardInstance.java @@ -0,0 +1,254 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.logging.Logger; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.ai.L2AttackableAI; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.L2WorldRegion; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.knownlist.GuardKnownList; +import com.l2jserver.gameserver.model.quest.Quest; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.SocialAction; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.Rnd; + +/** + * This class manages all Guards in the world. It inherits all methods from L2Attackable and adds some more such as tracking PK and aggressive L2MonsterInstance.
    + *
    + * @version $Revision: 1.11.2.1.2.7 $ $Date: 2005/04/06 16:13:40 $ + */ +public final class L2GuardInstance extends L2Attackable +{ + private static Logger _log = Logger.getLogger(L2GuardInstance.class.getName()); + + private static final int RETURN_INTERVAL = 60000; + + public class ReturnTask implements Runnable + { + @Override + public void run() + { + if (getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE) + { + returnHome(); + } + } + } + + /** + * Constructor of L2GuardInstance (use L2Character and L2NpcInstance constructor).
    + *
    + * Actions :
    + *
    + *
  • Call the L2Character constructor to set the _template of the L2GuardInstance (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR)
  • Set the name of the L2GuardInstance
  • Create a RandomAnimation Task that will be launched after the calculated + * delay if the server allow it

  • + *
    + * @param objectId Identifier of the object to initialized + * @param L2NpcTemplate Template to apply to the NPC + */ + public L2GuardInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2GuardInstance); + + ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new ReturnTask(), RETURN_INTERVAL, RETURN_INTERVAL + Rnd.nextInt(60000)); + } + + @Override + public final GuardKnownList getKnownList() + { + return (GuardKnownList) super.getKnownList(); + } + + @Override + public void initKnownList() + { + setKnownList(new GuardKnownList(this)); + } + + /** + * Return True if hte attacker is a L2MonsterInstance.
    + *
    + */ + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return attacker instanceof L2MonsterInstance; + } + + /** + * Notify the L2GuardInstance to return to its home location (AI_INTENTION_MOVE_TO) and clear its _aggroList.
    + *
    + */ + @Override + public void returnHome() + { + if (!isInsideRadius(getSpawn().getLocx(), getSpawn().getLocy(), 150, false)) + { + clearAggroList(); + + getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(getSpawn().getLocx(), getSpawn().getLocy(), getSpawn().getLocz(), 0)); + } + } + + /** + * Set the home location of its L2GuardInstance.
    + *
    + */ + @Override + public void onSpawn() + { + setIsNoRndWalk(true); + super.onSpawn(); + + // check the region where this mob is, do not activate the AI if region is inactive. + L2WorldRegion region = L2World.getInstance().getRegion(getX(), getY()); + if ((region != null) && (!region.isActive())) + { + ((L2AttackableAI) getAI()).stopAITask(); + } + } + + /** + * Return the pathfile of the selected HTML file in function of the L2GuardInstance Identifier and of the page number.
    + *
    + * Format of the pathfile :
    + *
    + *
  • if page number = 0 : data/html/guard/12006.htm (npcId-page number)
  • if page number > 0 : data/html/guard/12006-1.htm (npcId-page number)

  • + *
    + * @param npcId The Identifier of the L2NpcInstance whose text must be display + * @param val The number of the page to display + */ + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + return "data/html/guard/" + pom + ".htm"; + } + + /** + * Manage actions when a player click on the L2GuardInstance.
    + *
    + * Actions on first click on the L2GuardInstance (Select it) :
    + *
    + *
  • Set the L2GuardInstance as target of the L2PcInstance player (if necessary)
  • Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)
  • Set the L2PcInstance Intention to AI_INTENTION_IDLE
  • Send a Server->Client packet + * ValidateLocation to correct the L2GuardInstance position and heading on the client

  • + *
    + * Actions on second click on the L2GuardInstance (Attack it/Interact with it) :
    + *
    + *
  • If L2PcInstance is in the _aggroList of the L2GuardInstance, set the L2PcInstance Intention to AI_INTENTION_ATTACK
  • If L2PcInstance is NOT in the _aggroList of the L2GuardInstance, set the L2PcInstance Intention to AI_INTENTION_INTERACT (after a distance verification) and show + * message

  • + *
    + * Example of use :
    + *
    + *
  • Client packet : Action, AttackRequest

  • + *
    + * @param player The L2PcInstance that start an action on the L2GuardInstance + */ + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if (!canTarget(player)) + { + return; + } + + // Check if the L2PcInstance already target the L2GuardInstance + if (getObjectId() != player.getTargetId()) + { + if (Config.DEBUG) + { + _log.fine(player.getObjectId() + ": Targetted guard " + getObjectId()); + } + + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player + // The color to display in the select window is White + MyTargetSelected my = new MyTargetSelected(getObjectId(), 0); + player.sendPacket(my); + + // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + // Check if the L2PcInstance is in the _aggroList of the L2GuardInstance + if (containsTarget(player)) + { + if (Config.DEBUG) + { + _log.fine(player.getObjectId() + ": Attacked guard " + getObjectId()); + } + + // Set the L2PcInstance Intention to AI_INTENTION_ATTACK + player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); + } + else + { + // Calculate the distance between the L2PcInstance and the L2NpcInstance + if (!canInteract(player)) + { + // Set the L2PcInstance Intention to AI_INTENTION_INTERACT + player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this); + } + else + { + // Send a Server->Client packet SocialAction to the all L2PcInstance on the _knownPlayer of the L2NpcInstance + // to display a social action of the L2GuardInstance on their client + SocialAction sa = new SocialAction(this, Rnd.nextInt(8)); + broadcastPacket(sa); + + // Open a chat window on client with the text of the L2GuardInstance + Quest[] qlsa = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START); + if ((qlsa != null) && (qlsa.length > 0)) + { + player.setLastQuestNpcObject(getObjectId()); + } + Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.ON_FIRST_TALK); + if ((qlst != null) && (qlst.length == 1)) + { + qlst[0].notifyFirstTalk(this, player); + } + else + { + showChatWindow(player, 0); + } + } + } + } + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ManorManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ManorManagerInstance.java new file mode 100644 index 00000000..5a879a3e --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ManorManagerInstance.java @@ -0,0 +1,52 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.instancemanager.CastleManorManager; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2ManorManagerInstance extends L2MerchantInstance +{ + public L2ManorManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2ManorManagerInstance); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + return "data/html/manormanager/manager.htm"; + } + + @Override + public void showChatWindow(L2PcInstance player) + { + if (CastleManorManager.getInstance().isDisabled()) + { + showChatWindow(player, "data/html/npcdefault.htm"); + return; + } + + if (!player.isGM() && (getCastle() != null) && (getCastle().getCastleId() > 0) && player.isClanLeader() && (getCastle().getOwnerId() == player.getClanId())) + { + showChatWindow(player, "data/html/manormanager/manager-lord.htm"); + } + else + { + showChatWindow(player, "data/html/manormanager/manager.htm"); + } + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MercManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MercManagerInstance.java new file mode 100644 index 00000000..846160b9 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MercManagerInstance.java @@ -0,0 +1,119 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; + +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2MercManagerInstance extends L2MerchantInstance +{ + private static final int COND_ALL_FALSE = 0; + private static final int COND_BUSY_BECAUSE_OF_SIEGE = 1; + private static final int COND_OWNER = 2; + + public L2MercManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2MercManagerInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + int condition = validateCondition(player); + if (condition <= COND_ALL_FALSE) + { + return; + } + + if (condition == COND_BUSY_BECAUSE_OF_SIEGE) + { + return; + } + else if (condition == COND_OWNER) + { + StringTokenizer st = new StringTokenizer(command, " "); + String actualCommand = st.nextToken(); // Get actual command + + String val = ""; + if (st.countTokens() >= 1) + { + val = st.nextToken(); + } + + if (actualCommand.equalsIgnoreCase("hire")) + { + if (val.isEmpty()) + { + return; + } + + showBuyWindow(player, Integer.parseInt(val)); + return; + } + } + + super.onBypassFeedback(player, command); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + String filename = "data/html/mercmanager/mercmanager-no.htm"; + + int condition = validateCondition(player); + if (condition == COND_BUSY_BECAUSE_OF_SIEGE) + { + filename = "data/html/mercmanager/mercmanager-busy.htm"; // Busy because of siege + } + else if (condition == COND_OWNER) + { + filename = "data/html/mercmanager/mercmanager.htm"; // Owner message window + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcId%", String.valueOf(getNpcId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } + + private int validateCondition(L2PcInstance player) + { + if ((getCastle() != null) && (getCastle().getCastleId() > 0)) + { + if (player.getClan() != null) + { + if (getCastle().getSiege().getIsInProgress()) + { + return COND_BUSY_BECAUSE_OF_SIEGE; // Busy because of siege + } + else if (getCastle().getOwnerId() == player.getClanId()) // Clan owns castle + { + if ((player.getClanPrivileges() & L2Clan.CP_CS_MERCENARIES) == L2Clan.CP_CS_MERCENARIES) + { + return COND_OWNER; + } + } + } + } + + return COND_ALL_FALSE; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MercenaryManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MercenaryManagerInstance.java new file mode 100644 index 00000000..9b5462b6 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MercenaryManagerInstance.java @@ -0,0 +1,89 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * NPC that gives information about territory wars + * @author GodKratos + */ +public class L2MercenaryManagerInstance extends L2Npc +{ + // private int[] TW_BADGE_IDS = { 13757, 13758, 13759, 13760, 13761, 13762, 13763, 13764, 13765 }; + /** + * @param objectId + * @param template + */ + public L2MercenaryManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2MercenaryManagerInstance); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + if ((player.getLevel() < 40) || (player.getClassId().level() < 2)) + { + super.showChatWindow(player, 2); + } + else if (TerritoryWarManager.getInstance().isTWInProgress()) + { + super.showChatWindow(player, 10); + } + else + { + super.showChatWindow(player, 0); + } + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String temp = ""; + if (val == 0) + { + temp = "data/html/mercmanager/" + npcId + ".htm"; + } + else + { + temp = "data/html/mercmanager/" + npcId + "-" + val + ".htm"; + } + + if (!Config.LAZY_CACHE) + { + // If not running lazy cache the file must be in the cache or it doesnt exist + if (HtmCache.getInstance().contains(temp)) + { + return temp; + } + } + else + { + if (HtmCache.getInstance().isLoadable(temp)) + { + return temp; + } + } + + // If the file is not found, the standard message "I have nothing to say to you" is returned + return "data/html/npcdefault.htm"; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MerchantInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MerchantInstance.java new file mode 100644 index 00000000..01c32941 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MerchantInstance.java @@ -0,0 +1,104 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.TradeController; +import com.l2jserver.gameserver.datatables.MerchantPriceConfigTable; +import com.l2jserver.gameserver.datatables.MerchantPriceConfigTable.MerchantPriceConfig; +import com.l2jserver.gameserver.model.L2TradeList; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.BuyList; +import com.l2jserver.gameserver.network.serverpackets.ExBuySellListPacket; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * This class ... + * @version $Revision: 1.10.4.9 $ $Date: 2005/04/11 10:06:08 $ + */ +public class L2MerchantInstance extends L2NpcInstance +{ + private MerchantPriceConfig _mpc; + + /** + * @param template + */ + public L2MerchantInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2MerchantInstance); + } + + @Override + public void onSpawn() + { + super.onSpawn(); + _mpc = MerchantPriceConfigTable.getInstance().getMerchantPriceConfig(this); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/merchant/" + pom + ".htm"; + } + + /** + * @return Returns the mpc. + */ + public MerchantPriceConfig getMpc() + { + return _mpc; + } + + public final void showBuyWindow(L2PcInstance player, int val) + { + double taxRate = 0; + + taxRate = getMpc().getTotalTaxRate(); + + player.tempInventoryDisable(); + + if (Config.DEBUG) + { + _log.fine("Showing buylist"); + } + + L2TradeList list = TradeController.getInstance().getBuyList(val); + + if ((list != null) && list.getNpcId().equals(String.valueOf(getNpcId()))) + { + player.sendPacket(new BuyList(list, player.getAdena(), taxRate)); + player.sendPacket(new ExBuySellListPacket(player, list, taxRate, false)); + } + else + { + _log.warning("possible client hacker: " + player.getName() + " attempting to buy from GM shop! < Ban him!"); + _log.warning("buylist id:" + val); + } + + player.sendPacket(ActionFailed.STATIC_PACKET); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MerchantSummonInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MerchantSummonInstance.java new file mode 100644 index 00000000..a8fab4c0 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MerchantSummonInstance.java @@ -0,0 +1,262 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.TradeController; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.ai.L2CharacterAI; +import com.l2jserver.gameserver.model.L2Party; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2TradeList; +import com.l2jserver.gameserver.model.L2WorldRegion; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.BuyList; +import com.l2jserver.gameserver.network.serverpackets.ExBuySellListPacket; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SellList; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author Kerberos + */ +public class L2MerchantSummonInstance extends L2SummonInstance +{ + public L2MerchantSummonInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, L2Skill skill) + { + super(objectId, template, owner, skill); + setInstanceType(InstanceType.L2MerchantSummonInstance); + } + + @Override + public boolean hasAI() + { + return false; + } + + @Override + public L2CharacterAI getAI() + { + return null; + } + + @Override + public void deleteMe(L2PcInstance owner) + { + + } + + @Override + public void unSummon(L2PcInstance owner) + { + if (isVisible()) + { + stopAllEffects(); + L2WorldRegion oldRegion = getWorldRegion(); + decayMe(); + if (oldRegion != null) + { + oldRegion.removeFromZones(this); + } + getKnownList().removeAllKnownObjects(); + setTarget(null); + } + } + + @Override + public void setFollowStatus(boolean state) + { + + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return false; + } + + @Override + public boolean isInvul() + { + return true; + } + + @Override + public L2Party getParty() + { + return null; + } + + @Override + public boolean isInParty() + { + return false; + } + + @Override + public boolean useMagic(L2Skill skill, boolean forceUse, boolean dontMove) + { + return false; + } + + @Override + public void doCast(L2Skill skill) + { + + } + + @Override + public boolean isInCombat() + { + return false; + } + + @Override + public final void sendDamageMessage(L2Character target, int damage, boolean mcrit, boolean pcrit, boolean miss) + { + + } + + @Override + public void reduceCurrentHp(double i, L2Character attacker, boolean awake, boolean isDOT, L2Skill skill) + { + + } + + @Override + public void updateAndBroadcastStatus(int val) + { + + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if (player.isOutOfControl()) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + return; + } + + // Check if the L2PcInstance already target the L2NpcInstance + if (this != player.getTarget()) + { + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player + final MyTargetSelected my = new MyTargetSelected(getObjectId(), 0); + player.sendPacket(my); + + // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + // Calculate the distance between the L2PcInstance and the L2NpcInstance + if (!isInsideRadius(player, 150, false, false)) + { + // Notify the L2PcInstance AI with AI_INTENTION_INTERACT + player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this); + } + else + { + showMessageWindow(player); + } + } + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + public void onBypassFeedback(L2PcInstance player, String command) + { + final StringTokenizer st = new StringTokenizer(command, " "); + final String actualCommand = st.nextToken(); // Get actual command + + if (actualCommand.equalsIgnoreCase("Buy")) + { + if (st.countTokens() < 1) + { + return; + } + + final int val = Integer.parseInt(st.nextToken()); + showBuyWindow(player, val); + } + else if (actualCommand.equalsIgnoreCase("Sell")) + { + showSellWindow(player); + } + } + + protected final void showBuyWindow(L2PcInstance player, int val) + { + double taxRate = 50; + + player.tempInventoryDisable(); + + if (Config.DEBUG) + { + _log.fine("Showing buylist"); + } + + L2TradeList list = TradeController.getInstance().getBuyList(val); + + if ((list != null) && list.getNpcId().equals(String.valueOf(getNpcId()))) + { + player.sendPacket(new BuyList(list, player.getAdena(), taxRate)); + player.sendPacket(new ExBuySellListPacket(player, list, taxRate, false)); + } + else + { + _log.warning("possible client hacker: " + player.getName() + " attempting to buy from GM shop! < Ban him!"); + _log.warning("buylist id:" + val); + } + + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + protected final void showSellWindow(L2PcInstance player) + { + if (Config.DEBUG) + { + _log.fine("Showing selllist"); + } + + player.sendPacket(new SellList(player)); + + if (Config.DEBUG) + { + _log.fine("Showing sell window"); + } + + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + private void showMessageWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + final String filename = "data/html/merchant/" + getNpcId() + ".htm"; + final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MonsterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MonsterInstance.java new file mode 100644 index 00000000..7b44dcf7 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2MonsterInstance.java @@ -0,0 +1,229 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.concurrent.ScheduledFuture; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.knownlist.MonsterKnownList; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.util.MinionList; +import com.l2jserver.util.Rnd; + +/** + * This class manages all Monsters. L2MonsterInstance :
    + *
    + *
  • L2MinionInstance
  • L2RaidBossInstance
  • L2GrandBossInstance
  • + * @version $Revision: 1.20.4.6 $ $Date: 2005/04/06 16:13:39 $ + */ +public class L2MonsterInstance extends L2Attackable +{ + // private static Logger _log = Logger.getLogger(L2MonsterInstance.class.getName()); + + private boolean _enableMinions = true; + + private L2MonsterInstance _master = null; + private MinionList _minionList = null; + + protected ScheduledFuture _maintenanceTask = null; + + private static final int MONSTER_MAINTENANCE_INTERVAL = 1000; + + /** + * Constructor of L2MonsterInstance (use L2Character and L2NpcInstance constructor).
    + *
    + * Actions :
    + *
    + *
  • Call the L2Character constructor to set the _template of the L2MonsterInstance (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR)
  • Set the name of the L2MonsterInstance
  • Create a RandomAnimation Task that will be launched after the calculated + * delay if the server allow it

  • + *
    + * @param objectId Identifier of the object to initialized + * @param L2NpcTemplate Template to apply to the NPC + */ + public L2MonsterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2MonsterInstance); + setAutoAttackable(true); + } + + @Override + public final MonsterKnownList getKnownList() + { + return (MonsterKnownList) super.getKnownList(); + } + + @Override + public void initKnownList() + { + setKnownList(new MonsterKnownList(this)); + } + + /** + * Return True if the attacker is not another L2MonsterInstance.
    + *
    + */ + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return super.isAutoAttackable(attacker) && !isEventMob; + } + + /** + * Return True if the L2MonsterInstance is Agressive (aggroRange > 0).
    + *
    + */ + @Override + public boolean isAggressive() + { + return (getTemplate().aggroRange > 0) && !isEventMob; + } + + @Override + public void onSpawn() + { + if (!isTeleporting()) + { + if (getLeader() != null) + { + setIsNoRndWalk(true); + setIsRaidMinion(getLeader().isRaid()); + getLeader().getMinionList().onMinionSpawn(this); + } + + // delete spawned minions before dynamic minions spawned by script + if (hasMinions()) + { + getMinionList().onMasterSpawn(); + } + + startMaintenanceTask(); + } + + // dynamic script-based minions spawned here, after all preparations. + super.onSpawn(); + } + + @Override + public void onTeleported() + { + super.onTeleported(); + + if (hasMinions()) + { + getMinionList().onMasterTeleported(); + } + } + + protected int getMaintenanceInterval() + { + return MONSTER_MAINTENANCE_INTERVAL; + } + + /** + * Spawn all minions at a regular interval + */ + protected void startMaintenanceTask() + { + // maintenance task now used only for minions spawn + if (getTemplate().getMinionData() == null) + { + return; + } + + if (_maintenanceTask == null) + { + _maintenanceTask = ThreadPoolManager.getInstance().scheduleGeneral(() -> + { + if (_enableMinions) + { + getMinionList().spawnMinions(); + } + }, getMaintenanceInterval() + Rnd.get(1000)); + } + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + + if (_maintenanceTask != null) + { + _maintenanceTask.cancel(false); // doesn't do it? + _maintenanceTask = null; + } + + return true; + } + + @Override + public void deleteMe() + { + if (_maintenanceTask != null) + { + _maintenanceTask.cancel(false); + _maintenanceTask = null; + } + + if (hasMinions()) + { + getMinionList().onMasterDie(true); + } + + if (getLeader() != null) + { + getLeader().getMinionList().onMinionDie(this, 0); + } + + super.deleteMe(); + } + + @Override + public L2MonsterInstance getLeader() + { + return _master; + } + + public void setLeader(L2MonsterInstance leader) + { + _master = leader; + } + + public void enableMinions(boolean b) + { + _enableMinions = b; + } + + public boolean hasMinions() + { + return _minionList != null; + } + + public MinionList getMinionList() + { + if (_minionList == null) + { + _minionList = new MinionList(this); + } + + return _minionList; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2NpcBufferInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2NpcBufferInstance.java new file mode 100644 index 00000000..a4026b0d --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2NpcBufferInstance.java @@ -0,0 +1,225 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import gnu.trove.TIntIntHashMap; + +import java.util.logging.Logger; + +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.datatables.NpcBufferTable; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * The Class L2NpcBufferInstance. + */ +public class L2NpcBufferInstance extends L2Npc +{ + static final Logger _log = Logger.getLogger(L2NpcBufferInstance.class.getName()); + + private static TIntIntHashMap pageVal = new TIntIntHashMap(); + + /** + * Instantiates a new l2 npc buffer instance. + * @param objectId the object id + * @param template the template + */ + public L2NpcBufferInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2NpcBufferInstance); + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.actor.L2Npc#showChatWindow(com.l2jserver.gameserver.model.actor.instance.L2PcInstance, int) + */ + @Override + public void showChatWindow(L2PcInstance playerInstance, int val) + { + if (playerInstance == null) + { + return; + } + + String htmContent = HtmCache.getInstance().getHtm(playerInstance.getHtmlPrefix(), "data/html/mods/NpcBuffer.htm"); + + if (val > 0) + { + htmContent = HtmCache.getInstance().getHtm(playerInstance.getHtmlPrefix(), "data/html/mods/NpcBuffer-" + val + ".htm"); + } + + if (htmContent != null) + { + NpcHtmlMessage npcHtmlMessage = new NpcHtmlMessage(getObjectId()); + + npcHtmlMessage.setHtml(htmContent); + npcHtmlMessage.replace("%objectId%", String.valueOf(getObjectId())); + playerInstance.sendPacket(npcHtmlMessage); + } + + playerInstance.sendPacket(ActionFailed.STATIC_PACKET); + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.actor.L2Npc#onBypassFeedback(com.l2jserver.gameserver.model.actor.instance.L2PcInstance, java.lang.String) + */ + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + // BypassValidation Exploit plug. + if ((player == null) || (player.getLastFolkNPC() == null) || (player.getLastFolkNPC().getObjectId() != getObjectId())) + { + return; + } + + L2Character target = player; + + if (command.startsWith("Pet")) + { + L2Summon pet = player.getPet(); + if (pet == null) + { + player.sendMessage("You do not have your pet summoned."); + showChatWindow(player, 0); // 0 = main window + return; + } + target = pet; + } + + int npcId = getNpcId(); + + if (command.startsWith("Chat")) + { + int val = Integer.parseInt(command.substring(5)); + + pageVal.put(player.getObjectId(), val); + + showChatWindow(player, val); + } + else if (command.startsWith("Buff") || command.startsWith("PetBuff")) + { + String[] buffGroupArray = command.substring(command.indexOf("Buff") + 5).split(" "); + + for (String buffGroupList : buffGroupArray) + { + if (buffGroupList == null) + { + _log.warning("NPC Buffer Warning: npcId = " + npcId + " has no buffGroup set in the bypass for the buff selected."); + return; + } + + int buffGroup = Integer.parseInt(buffGroupList); + + int[] npcBuffGroupInfo = NpcBufferTable.getInstance().getSkillInfo(npcId, buffGroup); + + if (npcBuffGroupInfo == null) + { + _log.warning("NPC Buffer Warning: npcId = " + npcId + " Location: " + getX() + ", " + getY() + ", " + getZ() + " Player: " + player.getName() + " has tried to use skill group (" + buffGroup + ") not assigned to the NPC Buffer!"); + return; + } + + int skillId = npcBuffGroupInfo[0]; + int skillLevel = npcBuffGroupInfo[1]; + int skillFeeId = npcBuffGroupInfo[2]; + int skillFeeAmount = npcBuffGroupInfo[3]; + + if (skillFeeId != 0) + { + L2ItemInstance itemInstance = player.getInventory().getItemByItemId(skillFeeId); + + if ((itemInstance == null) || (!itemInstance.isStackable() && (player.getInventory().getInventoryItemCount(skillFeeId, -1) < skillFeeAmount))) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THERE_ARE_NOT_ENOUGH_NECESSARY_ITEMS_TO_USE_THE_SKILL); + player.sendPacket(sm); + continue; + } + + if (itemInstance.isStackable()) + { + if (!player.destroyItemByItemId("Npc Buffer", skillFeeId, skillFeeAmount, player.getTarget(), true)) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THERE_ARE_NOT_ENOUGH_NECESSARY_ITEMS_TO_USE_THE_SKILL); + player.sendPacket(sm); + continue; + } + } + else + { + for (int i = 0; i < skillFeeAmount; ++i) + { + player.destroyItemByItemId("Npc Buffer", skillFeeId, 1, player.getTarget(), true); + } + } + } + + L2Skill skill; + skill = SkillTable.getInstance().getInfo(skillId, skillLevel); + + if (skill != null) + { + skill.getEffects(player, target); + } + } + + showChatWindow(player, pageVal.get(player.getObjectId())); + } + else if (command.startsWith("Heal") || command.startsWith("PetHeal")) + { + if (!target.isInCombat() && !AttackStanceTaskManager.getInstance().getAttackStanceTask(target)) + { + String[] healArray = command.substring(command.indexOf("Heal") + 5).split(" "); + + for (String healType : healArray) + { + if (healType.equalsIgnoreCase("HP")) + { + target.setCurrentHp(target.getMaxHp()); + } + else if (healType.equalsIgnoreCase("MP")) + { + target.setCurrentMp(target.getMaxMp()); + } + else if (healType.equalsIgnoreCase("CP")) + { + target.setCurrentCp(target.getMaxCp()); + } + } + } + showChatWindow(player, pageVal.get(player.getObjectId())); + } + else if (command.startsWith("RemoveBuffs") || command.startsWith("PetRemoveBuffs")) + { + target.stopAllEffectsExceptThoseThatLastThroughDeath(); + showChatWindow(player, pageVal.get(player.getObjectId())); + } + else + { + super.onBypassFeedback(player, command); + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2NpcInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2NpcInstance.java new file mode 100644 index 00000000..708ba814 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2NpcInstance.java @@ -0,0 +1,192 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.model.L2Effect; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2SkillLearn; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.status.FolkStatus; +import com.l2jserver.gameserver.model.base.ClassId; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.skills.effects.EffectBuff; +import com.l2jserver.gameserver.skills.effects.EffectDebuff; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.StringUtil; + +public class L2NpcInstance extends L2Npc +{ + public L2NpcInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2NpcInstance); + setIsInvul(false); + } + + @Override + public FolkStatus getStatus() + { + return (FolkStatus) super.getStatus(); + } + + @Override + public void initCharStatus() + { + setStatus(new FolkStatus(this)); + } + + @Override + public void addEffect(L2Effect newEffect) + { + if ((newEffect instanceof EffectDebuff) || (newEffect instanceof EffectBuff)) + { + super.addEffect(newEffect); + } + else if (newEffect != null) + { + newEffect.stopEffectTask(); + } + } + + public ClassId[] getClassesToTeach() + { + return getTemplate().getTeachInfo(); + } + + /** + * Displays Skill Tree for a given player, npc and class Id. + * @param player the active character. + * @param npc the last folk. + * @param classId player's active class id. + */ + public static void showSkillList(L2PcInstance player, L2Npc npc, ClassId classId) + { + if (Config.DEBUG) + { + _log.fine("SkillList activated on: " + npc.getObjectId()); + } + + final int npcId = npc.getTemplate().npcId; + + if (npcId == 32611) // Tolonis (Officer) + { + final FastList skills = SkillTreesData.getInstance().getAvailableCollectSkills(player); + final AcquireSkillList asl = new AcquireSkillList(SkillType.Collect); + + int counts = 0; + + for (L2SkillLearn s : skills) + { + final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()); + + if (sk != null) + { + counts++; + asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), 0, 1); + } + } + + if (counts == 0) // No more skills to learn, come back when you level. + { + final int minLevel = SkillTreesData.getInstance().getMinLevelForNewSkill(player, SkillTreesData.getInstance().getCollectSkillTree()); + if (minLevel > 0) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DO_NOT_HAVE_FURTHER_SKILLS_TO_LEARN_S1); + sm.addNumber(minLevel); + player.sendPacket(sm); + } + else + { + player.sendPacket(SystemMessageId.NO_MORE_SKILLS_TO_LEARN); + } + } + else + { + player.sendPacket(asl); + } + return; + } + + if (!npc.getTemplate().canTeach(classId)) + { + npc.showNoTeachHtml(player); + return; + } + + if (((L2NpcInstance) npc).getClassesToTeach() == null) + { + NpcHtmlMessage html = new NpcHtmlMessage(npc.getObjectId()); + final String sb = StringUtil.concat("" + "I cannot teach you. My class list is empty.
    Ask admin to fix it. Need add my npcid and classes to skill_learn.sql.
    NpcId:", String.valueOf(npcId), ", Your classId:", String.valueOf(player.getClassId().getId()), "
    " + ""); + html.setHtml(sb); + player.sendPacket(html); + return; + } + + // Normal skills, No LearnedByFS, no AutoGet skills. + final FastList skills = SkillTreesData.getInstance().getAvailableSkills(player, classId, false, false); + final AcquireSkillList asl = new AcquireSkillList(AcquireSkillList.SkillType.ClassTransform); + int count = 0; + + player.setLearningClass(classId); + for (L2SkillLearn s : skills) + { + if (SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()) != null) + { + asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getCalculatedLevelUpSp(player.getClassId(), classId), 0); + count++; + } + } + + if (count == 0) + { + final FastMap skillTree = SkillTreesData.getInstance().getCompleteClassSkillTree(classId); + + final int minLevel = SkillTreesData.getInstance().getMinLevelForNewSkill(player, skillTree); + if (minLevel > 0) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DO_NOT_HAVE_FURTHER_SKILLS_TO_LEARN_S1); + sm.addNumber(minLevel); + player.sendPacket(sm); + } + else + { + if (player.getClassId().level() == 1) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.NO_SKILLS_TO_LEARN_RETURN_AFTER_S1_CLASS_CHANGE); + sm.addNumber(2); + player.sendPacket(sm); + } + else + { + player.sendPacket(SystemMessageId.NO_MORE_SKILLS_TO_LEARN); + } + } + } + else + { + player.sendPacket(asl); + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2NpcWalkerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2NpcWalkerInstance.java new file mode 100644 index 00000000..24c00956 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2NpcWalkerInstance.java @@ -0,0 +1,126 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.ai.L2CharacterAI; +import com.l2jserver.gameserver.ai.L2NpcWalkerAI; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.clientpackets.Say2; +import com.l2jserver.gameserver.network.serverpackets.NpcSay; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.util.Broadcast; + +/** + * This class manages some npcs can walk in the city.
    + * It inherits all methods from L2NpcInstance.
    + *
    + * @original author Rayan RPG for L2Emu Project + * @since 819 + */ +public class L2NpcWalkerInstance extends L2Npc +{ + /** + * Constructor of L2NpcWalkerInstance (use L2Character and L2NpcInstance constructor).
    + *
    + * @param objectId given object id + * @param template L2NpcTemplateForThisAi + */ + public L2NpcWalkerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2NpcWalkerInstance); + setAI(new L2NpcWalkerAI(new L2NpcWalkerAIAccessor())); + } + + /** + * AI can't be deattached, npc must move always with the same AI instance. + * @param newAI AI to set for this L2NpcWalkerInstance + */ + @Override + public void setAI(L2CharacterAI newAI) + { + if (!(_ai instanceof L2NpcWalkerAI)) + { + _ai = newAI; + } + } + + @Override + public void onSpawn() + { + getAI().setHomeX(getX()); + getAI().setHomeY(getY()); + getAI().setHomeZ(getZ()); + } + + /** + * Sends a chat to all _knowObjects + * @param chat message to say + */ + public void broadcastChat(String chat, int id) + { + NpcSay cs; + if (id == 0) + { + cs = new NpcSay(getObjectId(), Say2.ALL, getNpcId(), chat); + } + else + { + cs = new NpcSay(getObjectId(), Say2.ALL, getNpcId(), id); + } + Broadcast.toKnownPlayers(this, cs); + } + + /** + * NPCs are immortal + * @param i ignore it + * @param attacker ignore it + * @param awake ignore it + */ + @Override + public void reduceCurrentHp(double i, L2Character attacker, boolean awake, boolean isDOT, L2Skill skill) + { + } + + /** + * NPCs are immortal + * @param killer ignore it + * @return false + */ + @Override + public boolean doDie(L2Character killer) + { + return false; + } + + @Override + public L2NpcWalkerAI getAI() + { + return (L2NpcWalkerAI) _ai; + } + + protected class L2NpcWalkerAIAccessor extends L2Character.AIAccessor + { + /** + * AI can't be deattached. + */ + @Override + public void detachAI() + { + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ObservationInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ObservationInstance.java new file mode 100644 index 00000000..ba92a02f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2ObservationInstance.java @@ -0,0 +1,66 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author NightMarez + * @version $Revision: 1.3.2.2.2.5 $ $Date: 2005/03/27 15:29:32 $ + */ +public final class L2ObservationInstance extends L2Npc +{ + public L2ObservationInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2ObservationInstance); + } + + @Override + public void showChatWindow(L2PcInstance player, int val) + { + String filename = null; + + if (isInsideRadius(-79884, 86529, 50, true) || isInsideRadius(-78858, 111358, 50, true) || isInsideRadius(-76973, 87136, 50, true) || isInsideRadius(-75850, 111968, 50, true)) + { + if (val == 0) + { + filename = "data/html/observation/" + getNpcId() + "-Oracle.htm"; + } + else + { + filename = "data/html/observation/" + getNpcId() + "-Oracle-" + val + ".htm"; + } + } + else + { + if (val == 0) + { + filename = "data/html/observation/" + getNpcId() + ".htm"; + } + else + { + filename = "data/html/observation/" + getNpcId() + "-" + val + ".htm"; + } + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2OlympiadManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2OlympiadManagerInstance.java new file mode 100644 index 00000000..a3835aa0 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2OlympiadManagerInstance.java @@ -0,0 +1,47 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.olympiad.Olympiad; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * Olympiad Npc's Instance + * @author godson + */ +public class L2OlympiadManagerInstance extends L2Npc +{ + public L2OlympiadManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2OlympiadManagerInstance); + } + + public void showChatWindow(L2PcInstance player, int val, String suffix) + { + String filename = Olympiad.OLYMPIAD_HTML_PATH; + + filename += "noble_desc" + val; + filename += (suffix != null) ? suffix + ".htm" : ".htm"; + + if (filename.equals(Olympiad.OLYMPIAD_HTML_PATH + "noble_desc0.htm")) + { + filename = Olympiad.OLYMPIAD_HTML_PATH + "noble_main.htm"; + } + + showChatWindow(player, filename); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java new file mode 100644 index 00000000..ccad38ef --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java @@ -0,0 +1,16550 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; + +import javolution.util.FastList; +import javolution.util.FastMap; +import javolution.util.FastSet; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.Announcements; +import com.l2jserver.gameserver.GameTimeController; +import com.l2jserver.gameserver.GeoData; +import com.l2jserver.gameserver.GmListTable; +import com.l2jserver.gameserver.ItemsAutoDestroy; +import com.l2jserver.gameserver.LoginServerThread; +import com.l2jserver.gameserver.RecipeController; +import com.l2jserver.gameserver.SevenSigns; +import com.l2jserver.gameserver.SevenSignsFestival; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.ai.L2CharacterAI; +import com.l2jserver.gameserver.ai.L2PlayerAI; +import com.l2jserver.gameserver.ai.L2SummonAI; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.cache.WarehouseCacheManager; +import com.l2jserver.gameserver.communitybbs.BB.Forum; +import com.l2jserver.gameserver.communitybbs.Manager.ForumsBBSManager; +import com.l2jserver.gameserver.communitybbs.Manager.RegionBBSManager; +import com.l2jserver.gameserver.datatables.AccessLevels; +import com.l2jserver.gameserver.datatables.AdminCommandAccessRights; +import com.l2jserver.gameserver.datatables.CharNameTable; +import com.l2jserver.gameserver.datatables.CharTemplateTable; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.datatables.EnchantGroupsTable; +import com.l2jserver.gameserver.datatables.FishTable; +import com.l2jserver.gameserver.datatables.HennaTable; +import com.l2jserver.gameserver.datatables.HeroSkillTable; +import com.l2jserver.gameserver.datatables.ItemTable; +import com.l2jserver.gameserver.datatables.MapRegionTable; +import com.l2jserver.gameserver.datatables.NobleSkillTable; +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.datatables.PetDataTable; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.handler.IItemHandler; +import com.l2jserver.gameserver.handler.ItemHandler; +import com.l2jserver.gameserver.instancemanager.AntiFeedManager; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.instancemanager.CoupleManager; +import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager; +import com.l2jserver.gameserver.instancemanager.DimensionalRiftManager; +import com.l2jserver.gameserver.instancemanager.DuelManager; +import com.l2jserver.gameserver.instancemanager.FortManager; +import com.l2jserver.gameserver.instancemanager.FortSiegeManager; +import com.l2jserver.gameserver.instancemanager.GrandBossManager; +import com.l2jserver.gameserver.instancemanager.HandysBlockCheckerManager; +import com.l2jserver.gameserver.instancemanager.InstanceManager; +import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager; +import com.l2jserver.gameserver.instancemanager.QuestManager; +import com.l2jserver.gameserver.instancemanager.SiegeManager; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; +import com.l2jserver.gameserver.model.BlockList; +import com.l2jserver.gameserver.model.CharEffectList; +import com.l2jserver.gameserver.model.Elementals; +import com.l2jserver.gameserver.model.FishData; +import com.l2jserver.gameserver.model.L2AccessLevel; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2ClanMember; +import com.l2jserver.gameserver.model.L2Effect; +import com.l2jserver.gameserver.model.L2EnchantSkillLearn; +import com.l2jserver.gameserver.model.L2Fishing; +import com.l2jserver.gameserver.model.L2HennaInstance; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Macro; +import com.l2jserver.gameserver.model.L2ManufactureItem; +import com.l2jserver.gameserver.model.L2ManufactureList; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Party; +import com.l2jserver.gameserver.model.L2PetData; +import com.l2jserver.gameserver.model.L2PetLevelData; +import com.l2jserver.gameserver.model.L2PremiumItem; +import com.l2jserver.gameserver.model.L2Radar; +import com.l2jserver.gameserver.model.L2RecipeList; +import com.l2jserver.gameserver.model.L2Request; +import com.l2jserver.gameserver.model.L2ShortCut; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2Skill.SkillTargetType; +import com.l2jserver.gameserver.model.L2SkillLearn; +import com.l2jserver.gameserver.model.L2Transformation; +import com.l2jserver.gameserver.model.L2UIKeysSettings; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.L2WorldRegion; +import com.l2jserver.gameserver.model.Location; +import com.l2jserver.gameserver.model.MacroList; +import com.l2jserver.gameserver.model.PartyMatchRoom; +import com.l2jserver.gameserver.model.PartyMatchRoomList; +import com.l2jserver.gameserver.model.PartyMatchWaitingList; +import com.l2jserver.gameserver.model.ShortCuts; +import com.l2jserver.gameserver.model.TerritoryWard; +import com.l2jserver.gameserver.model.TradeList; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Decoy; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.actor.L2Trap; +import com.l2jserver.gameserver.model.actor.L2Vehicle; +import com.l2jserver.gameserver.model.actor.appearance.PcAppearance; +import com.l2jserver.gameserver.model.actor.knownlist.PcKnownList; +import com.l2jserver.gameserver.model.actor.position.PcPosition; +import com.l2jserver.gameserver.model.actor.stat.PcStat; +import com.l2jserver.gameserver.model.actor.status.PcStatus; +import com.l2jserver.gameserver.model.base.ClassId; +import com.l2jserver.gameserver.model.base.ClassLevel; +import com.l2jserver.gameserver.model.base.Experience; +import com.l2jserver.gameserver.model.base.PlayerClass; +import com.l2jserver.gameserver.model.base.Race; +import com.l2jserver.gameserver.model.base.SubClass; +import com.l2jserver.gameserver.model.entity.Castle; +import com.l2jserver.gameserver.model.entity.Duel; +import com.l2jserver.gameserver.model.entity.Fort; +import com.l2jserver.gameserver.model.entity.Hero; +import com.l2jserver.gameserver.model.entity.Instance; +import com.l2jserver.gameserver.model.entity.L2Event; +import com.l2jserver.gameserver.model.entity.Siege; +import com.l2jserver.gameserver.model.entity.TvTEvent; +import com.l2jserver.gameserver.model.itemcontainer.Inventory; +import com.l2jserver.gameserver.model.itemcontainer.ItemContainer; +import com.l2jserver.gameserver.model.itemcontainer.PcInventory; +import com.l2jserver.gameserver.model.itemcontainer.PcRefund; +import com.l2jserver.gameserver.model.itemcontainer.PcWarehouse; +import com.l2jserver.gameserver.model.itemcontainer.PetInventory; +import com.l2jserver.gameserver.model.multisell.PreparedListContainer; +import com.l2jserver.gameserver.model.olympiad.OlympiadGameManager; +import com.l2jserver.gameserver.model.olympiad.OlympiadGameTask; +import com.l2jserver.gameserver.model.olympiad.OlympiadManager; +import com.l2jserver.gameserver.model.quest.Quest; +import com.l2jserver.gameserver.model.quest.QuestState; +import com.l2jserver.gameserver.model.quest.State; +import com.l2jserver.gameserver.model.zone.type.L2BossZone; +import com.l2jserver.gameserver.network.L2GameClient; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.communityserver.CommunityServerThread; +import com.l2jserver.gameserver.network.communityserver.writepackets.WorldInfo; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.ChangeWaitType; +import com.l2jserver.gameserver.network.serverpackets.CharInfo; +import com.l2jserver.gameserver.network.serverpackets.ConfirmDlg; +import com.l2jserver.gameserver.network.serverpackets.EtcStatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.ExAutoSoulShot; +import com.l2jserver.gameserver.network.serverpackets.ExBasicActionList; +import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo; +import com.l2jserver.gameserver.network.serverpackets.ExDominionWarStart; +import com.l2jserver.gameserver.network.serverpackets.ExDuelUpdateUserInfo; +import com.l2jserver.gameserver.network.serverpackets.ExFishingEnd; +import com.l2jserver.gameserver.network.serverpackets.ExFishingStart; +import com.l2jserver.gameserver.network.serverpackets.ExGetBookMarkInfoPacket; +import com.l2jserver.gameserver.network.serverpackets.ExGetOnAirShip; +import com.l2jserver.gameserver.network.serverpackets.ExOlympiadMode; +import com.l2jserver.gameserver.network.serverpackets.ExPrivateStoreSetWholeMsg; +import com.l2jserver.gameserver.network.serverpackets.ExSetCompassZoneCode; +import com.l2jserver.gameserver.network.serverpackets.ExSpawnEmitter; +import com.l2jserver.gameserver.network.serverpackets.ExStartScenePlayer; +import com.l2jserver.gameserver.network.serverpackets.ExStorageMaxCount; +import com.l2jserver.gameserver.network.serverpackets.ExVitalityPointInfo; +import com.l2jserver.gameserver.network.serverpackets.ExVoteSystemInfo; +import com.l2jserver.gameserver.network.serverpackets.FriendStatusPacket; +import com.l2jserver.gameserver.network.serverpackets.GameGuardQuery; +import com.l2jserver.gameserver.network.serverpackets.GetOnVehicle; +import com.l2jserver.gameserver.network.serverpackets.HennaInfo; +import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate; +import com.l2jserver.gameserver.network.serverpackets.ItemList; +import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket; +import com.l2jserver.gameserver.network.serverpackets.LeaveWorld; +import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse; +import com.l2jserver.gameserver.network.serverpackets.NicknameChanged; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.ObservationMode; +import com.l2jserver.gameserver.network.serverpackets.ObservationReturn; +import com.l2jserver.gameserver.network.serverpackets.PartySmallWindowUpdate; +import com.l2jserver.gameserver.network.serverpackets.PetInventoryUpdate; +import com.l2jserver.gameserver.network.serverpackets.PlaySound; +import com.l2jserver.gameserver.network.serverpackets.PledgeShowMemberListDelete; +import com.l2jserver.gameserver.network.serverpackets.PledgeShowMemberListUpdate; +import com.l2jserver.gameserver.network.serverpackets.PrivateStoreListBuy; +import com.l2jserver.gameserver.network.serverpackets.PrivateStoreListSell; +import com.l2jserver.gameserver.network.serverpackets.PrivateStoreManageListBuy; +import com.l2jserver.gameserver.network.serverpackets.PrivateStoreManageListSell; +import com.l2jserver.gameserver.network.serverpackets.PrivateStoreMsgBuy; +import com.l2jserver.gameserver.network.serverpackets.PrivateStoreMsgSell; +import com.l2jserver.gameserver.network.serverpackets.RecipeShopMsg; +import com.l2jserver.gameserver.network.serverpackets.RecipeShopSellList; +import com.l2jserver.gameserver.network.serverpackets.RelationChanged; +import com.l2jserver.gameserver.network.serverpackets.Ride; +import com.l2jserver.gameserver.network.serverpackets.ServerClose; +import com.l2jserver.gameserver.network.serverpackets.SetupGauge; +import com.l2jserver.gameserver.network.serverpackets.ShortBuffStatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.ShortCutInit; +import com.l2jserver.gameserver.network.serverpackets.SkillCoolTime; +import com.l2jserver.gameserver.network.serverpackets.SkillList; +import com.l2jserver.gameserver.network.serverpackets.Snoop; +import com.l2jserver.gameserver.network.serverpackets.SocialAction; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.StopMove; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.network.serverpackets.TargetSelected; +import com.l2jserver.gameserver.network.serverpackets.TargetUnselected; +import com.l2jserver.gameserver.network.serverpackets.TradeDone; +import com.l2jserver.gameserver.network.serverpackets.TradeOtherDone; +import com.l2jserver.gameserver.network.serverpackets.TradeStart; +import com.l2jserver.gameserver.network.serverpackets.UserInfo; +import com.l2jserver.gameserver.skills.AbnormalEffect; +import com.l2jserver.gameserver.skills.Env; +import com.l2jserver.gameserver.skills.Formulas; +import com.l2jserver.gameserver.skills.Stats; +import com.l2jserver.gameserver.skills.l2skills.L2SkillSiegeFlag; +import com.l2jserver.gameserver.skills.l2skills.L2SkillSummon; +import com.l2jserver.gameserver.skills.l2skills.L2SkillTrap; +import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager; +import com.l2jserver.gameserver.templates.chars.L2PcTemplate; +import com.l2jserver.gameserver.templates.effects.EffectTemplate; +import com.l2jserver.gameserver.templates.item.L2Armor; +import com.l2jserver.gameserver.templates.item.L2ArmorType; +import com.l2jserver.gameserver.templates.item.L2EtcItemType; +import com.l2jserver.gameserver.templates.item.L2Henna; +import com.l2jserver.gameserver.templates.item.L2Item; +import com.l2jserver.gameserver.templates.item.L2Weapon; +import com.l2jserver.gameserver.templates.item.L2WeaponType; +import com.l2jserver.gameserver.templates.skills.L2EffectType; +import com.l2jserver.gameserver.templates.skills.L2SkillType; +import com.l2jserver.gameserver.util.FloodProtectors; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.Point3D; +import com.l2jserver.util.Rnd; + +/** + * This class represents all player characters in the world. There is always a client-thread connected to this (except if a player-store is activated upon logout).
    + *
    + * @version $Revision: 1.66.2.41.2.33 $ $Date: 2005/04/11 10:06:09 $ + */ +public final class L2PcInstance extends L2Playable +{ + // Character Skill SQL String Definitions: + private static final String RESTORE_SKILLS_FOR_CHAR = "SELECT skill_id,skill_level FROM character_skills WHERE charId=? AND class_index=?"; + private static final String ADD_NEW_SKILL = "INSERT INTO character_skills (charId,skill_id,skill_level,class_index) VALUES (?,?,?,?)"; + private static final String UPDATE_CHARACTER_SKILL_LEVEL = "UPDATE character_skills SET skill_level=? WHERE skill_id=? AND charId=? AND class_index=?"; + private static final String DELETE_SKILL_FROM_CHAR = "DELETE FROM character_skills WHERE skill_id=? AND charId=? AND class_index=?"; + private static final String DELETE_CHAR_SKILLS = "DELETE FROM character_skills WHERE charId=? AND class_index=?"; + + // Character Skill Save SQL String Definitions: + private static final String ADD_SKILL_SAVE = "INSERT INTO character_skills_save (charId,skill_id,skill_level,effect_count,effect_cur_time,reuse_delay,systime,restore_type,class_index,buff_index) VALUES (?,?,?,?,?,?,?,?,?,?)"; + private static final String RESTORE_SKILL_SAVE = "SELECT skill_id,skill_level,effect_count,effect_cur_time, reuse_delay, systime, restore_type FROM character_skills_save WHERE charId=? AND class_index=? ORDER BY buff_index ASC"; + private static final String DELETE_SKILL_SAVE = "DELETE FROM character_skills_save WHERE charId=? AND class_index=?"; + + // Character Character SQL String Definitions: + private static final String INSERT_CHARACTER = "INSERT INTO characters (account_name,charId,char_name,level,maxHp,curHp,maxCp,curCp,maxMp,curMp,face,hairStyle,hairColor,sex,exp,sp,karma,fame,pvpkills,pkkills,clanid,race,classid,deletetime,cancraft,title,title_color,accesslevel,online,isin7sdungeon,clan_privs,wantspeace,base_class,newbie,nobless,power_grade,createTime) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + private static final String UPDATE_CHARACTER = "UPDATE characters SET level=?,maxHp=?,curHp=?,maxCp=?,curCp=?,maxMp=?,curMp=?,face=?,hairStyle=?,hairColor=?,sex=?,heading=?,x=?,y=?,z=?,exp=?,expBeforeDeath=?,sp=?,karma=?,fame=?,pvpkills=?,pkkills=?,clanid=?,race=?,classid=?,deletetime=?,title=?,title_color=?,accesslevel=?,online=?,isin7sdungeon=?,clan_privs=?,wantspeace=?,base_class=?,onlinetime=?,punish_level=?,punish_timer=?,newbie=?,nobless=?,power_grade=?,subpledge=?,lvl_joined_academy=?,apprentice=?,sponsor=?,varka_ketra_ally=?,clan_join_expiry_time=?,clan_create_expiry_time=?,char_name=?,death_penalty_level=?,bookmarkslot=?,vitality_points=?,language=? WHERE charId=?"; + private static final String RESTORE_CHARACTER = "SELECT account_name, charId, char_name, level, maxHp, curHp, maxCp, curCp, maxMp, curMp, face, hairStyle, hairColor, sex, heading, x, y, z, exp, expBeforeDeath, sp, karma, fame, pvpkills, pkkills, clanid, race, classid, deletetime, cancraft, title, title_color, accesslevel, online, char_slot, lastAccess, clan_privs, wantspeace, base_class, onlinetime, isin7sdungeon, punish_level, punish_timer, newbie, nobless, power_grade, subpledge, lvl_joined_academy, apprentice, sponsor, varka_ketra_ally,clan_join_expiry_time,clan_create_expiry_time,death_penalty_level,bookmarkslot,vitality_points,createTime,language FROM characters WHERE charId=?"; + + // Character Teleport Bookmark: + private static final String INSERT_TP_BOOKMARK = "INSERT INTO character_tpbookmark (charId,Id,x,y,z,icon,tag,name) values (?,?,?,?,?,?,?,?)"; + private static final String UPDATE_TP_BOOKMARK = "UPDATE character_tpbookmark SET icon=?,tag=?,name=? where charId=? AND Id=?"; + private static final String RESTORE_TP_BOOKMARK = "SELECT Id,x,y,z,icon,tag,name FROM character_tpbookmark WHERE charId=?"; + private static final String DELETE_TP_BOOKMARK = "DELETE FROM character_tpbookmark WHERE charId=? AND Id=?"; + + // Character Subclass SQL String Definitions: + private static final String RESTORE_CHAR_SUBCLASSES = "SELECT class_id,exp,sp,level,class_index FROM character_subclasses WHERE charId=? ORDER BY class_index ASC"; + private static final String ADD_CHAR_SUBCLASS = "INSERT INTO character_subclasses (charId,class_id,exp,sp,level,class_index) VALUES (?,?,?,?,?,?)"; + private static final String UPDATE_CHAR_SUBCLASS = "UPDATE character_subclasses SET exp=?,sp=?,level=?,class_id=? WHERE charId=? AND class_index =?"; + private static final String DELETE_CHAR_SUBCLASS = "DELETE FROM character_subclasses WHERE charId=? AND class_index=?"; + + // Character Henna SQL String Definitions: + private static final String RESTORE_CHAR_HENNAS = "SELECT slot,symbol_id FROM character_hennas WHERE charId=? AND class_index=?"; + private static final String ADD_CHAR_HENNA = "INSERT INTO character_hennas (charId,symbol_id,slot,class_index) VALUES (?,?,?,?)"; + private static final String DELETE_CHAR_HENNA = "DELETE FROM character_hennas WHERE charId=? AND slot=? AND class_index=?"; + private static final String DELETE_CHAR_HENNAS = "DELETE FROM character_hennas WHERE charId=? AND class_index=?"; + + // Character Shortcut SQL String Definitions: + private static final String DELETE_CHAR_SHORTCUTS = "DELETE FROM character_shortcuts WHERE charId=? AND class_index=?"; + + // Character Transformation SQL String Definitions: + private static final String SELECT_CHAR_TRANSFORM = "SELECT transform_id FROM characters WHERE charId=?"; + private static final String UPDATE_CHAR_TRANSFORM = "UPDATE characters SET transform_id=? WHERE charId=?"; + + public static final int REQUEST_TIMEOUT = 15; + public static final int STORE_PRIVATE_NONE = 0; + public static final int STORE_PRIVATE_SELL = 1; + public static final int STORE_PRIVATE_BUY = 3; + public static final int STORE_PRIVATE_MANUFACTURE = 5; + public static final int STORE_PRIVATE_PACKAGE_SELL = 8; + + public class AIAccessor extends L2Character.AIAccessor + { + protected AIAccessor() + { + + } + + public L2PcInstance getPlayer() + { + return L2PcInstance.this; + } + + public void doPickupItem(L2Object object) + { + L2PcInstance.this.doPickupItem(object); + } + + public void doInteract(L2Character target) + { + L2PcInstance.this.doInteract(target); + } + + @Override + public void doAttack(L2Character target) + { + super.doAttack(target); + + // cancel the recent fake-death protection instantly if the player attacks or casts spells + getPlayer().setRecentFakeDeath(false); + } + + @Override + public void doCast(L2Skill skill) + { + super.doCast(skill); + + // cancel the recent fake-death protection instantly if the player attacks or casts spells + getPlayer().setRecentFakeDeath(false); + } + } + + private L2GameClient _client; + + private String _accountName; + private long _deleteTimer; + private long _creationTime; + + private String _lang = null; + private String _htmlPrefix = null; + + private volatile boolean _isOnline = false; + private long _onlineTime; + private long _onlineBeginTime; + private long _lastAccess; + private long _uptime; + + private final ReentrantLock _subclassLock = new ReentrantLock(); + protected int _baseClass; + protected int _activeClass; + protected int _classIndex = 0; + + /** data for mounted pets */ + private int _controlItemId; + private L2PetData _data; + private L2PetLevelData _leveldata; + private int _curFeed; + protected Future _mountFeedTask; + private ScheduledFuture _dismountTask; + private boolean _petItems = false; + + /** The list of sub-classes this character has. */ + private Map _subClasses; + + private PcAppearance _appearance; + + /** The Identifier of the L2PcInstance */ + private int _charId = 0x00030b7a; + + /** The Experience of the L2PcInstance before the last Death Penalty */ + private long _expBeforeDeath; + + /** The Karma of the L2PcInstance (if higher than 0, the name of the L2PcInstance appears in red) */ + private int _karma; + + /** The number of player killed during a PvP (the player killed was PvP Flagged) */ + private int _pvpKills; + + /** The PK counter of the L2PcInstance (= Number of non PvP Flagged player killed) */ + private int _pkKills; + + /** The PvP Flag state of the L2PcInstance (0=White, 1=Purple) */ + private byte _pvpFlag; + + /** The Fame of this L2PcInstance */ + private int _fame; + private ScheduledFuture _fameTask; + + /** Vitality recovery task */ + private ScheduledFuture _vitalityTask; + + private ScheduledFuture _teleportWatchdog; + + /** The Siege state of the L2PcInstance */ + private byte _siegeState = 0; + + /** The id of castle/fort which the L2PcInstance is registered for siege */ + private int _siegeSide = 0; + + private int _curWeightPenalty = 0; + + private int _lastCompassZone; // the last compass zone update send to the client + + private boolean _isIn7sDungeon = false; + + private int _bookmarkslot = 0; // The Teleport Bookmark Slot + + private List tpbookmark = new FastList<>(); + + private PunishLevel _punishLevel = PunishLevel.NONE; + private long _punishTimer = 0; + private ScheduledFuture _punishTask; + + public enum PunishLevel + { + NONE(0, ""), + CHAT(1, "chat banned"), + JAIL(2, "jailed"), + CHAR(3, "banned"), + ACC(4, "banned"); + + private final int punValue; + private final String punString; + + PunishLevel(int value, String string) + { + punValue = value; + punString = string; + } + + public int value() + { + return punValue; + } + + public String string() + { + return punString; + } + } + + /** Olympiad */ + private boolean _inOlympiadMode = false; + private boolean _OlympiadStart = false; + private int _olympiadGameId = -1; + private int _olympiadSide = -1; + public int olyBuff = 0; + + /** Duel */ + private boolean _isInDuel = false; + private int _duelState = Duel.DUELSTATE_NODUEL; + private int _duelId = 0; + private SystemMessageId _noDuelReason = SystemMessageId.THERE_IS_NO_OPPONENT_TO_RECEIVE_YOUR_CHALLENGE_FOR_A_DUEL; + + /** Boat and AirShip */ + private L2Vehicle _vehicle = null; + private Point3D _inVehiclePosition; + + public ScheduledFuture _taskforfish; + private int _mountType; + private int _mountNpcId; + private int _mountLevel; + /** Store object used to summon the strider you are mounting **/ + private int _mountObjectID = 0; + + public int _telemode = 0; + + private boolean _inCrystallize; + private boolean _inCraftMode; + + private long _offlineShopStart = 0; + + private L2Transformation _transformation; + private int _transformationId = 0; + + /** The table containing all L2RecipeList of the L2PcInstance */ + private final Map _dwarvenRecipeBook = new FastMap<>(); + private final Map _commonRecipeBook = new FastMap<>(); + + /** Premium Items */ + private final Map _premiumItems = new FastMap<>(); + + /** True if the L2PcInstance is sitting */ + private boolean _waitTypeSitting; + + /** Location before entering Observer Mode */ + private int _lastX; + private int _lastY; + private int _lastZ; + private boolean _observerMode = false; + + /** Stored from last ValidatePosition **/ + private final Point3D _lastServerPosition = new Point3D(0, 0, 0); + + /** The number of recommendation obtained by the L2PcInstance */ + private int _recomHave; // how much I was recommended by others + /** The number of recommendation that the L2PcInstance can give */ + private int _recomLeft; // how many recommendations I can give to others + /** Recommendation Bonus task **/ + private ScheduledFuture _recoBonusTask; + /** Recommendation task **/ + private ScheduledFuture _recoGiveTask; + /** Recommendation Two Hours bonus **/ + protected boolean _recoTwoHoursGiven = false; + + private final PcInventory _inventory = new PcInventory(this); + private PcWarehouse _warehouse; + private PcRefund _refund; + + /** The Private Store type of the L2PcInstance (STORE_PRIVATE_NONE=0, STORE_PRIVATE_SELL=1, sellmanage=2, STORE_PRIVATE_BUY=3, buymanage=4, STORE_PRIVATE_MANUFACTURE=5) */ + private int _privatestore; + + private TradeList _activeTradeList; + private ItemContainer _activeWarehouse; + private L2ManufactureList _createList; + private TradeList _sellList; + private TradeList _buyList; + + // Multisell + private PreparedListContainer _currentMultiSell = null; + + /** Bitmask used to keep track of one-time/newbie quest rewards */ + private int _newbie; + + private boolean _noble = false; + private boolean _hero = false; + + /** The L2FolkInstance corresponding to the last Folk wich one the player talked. */ + private L2Npc _lastFolkNpc = null; + + /** Last NPC Id talked on a quest */ + private int _questNpcObject = 0; + + /** The table containing all Quests began by the L2PcInstance */ + private final Map _quests = new FastMap<>(); + + /** The list containing all shortCuts of this L2PcInstance */ + private final ShortCuts _shortCuts = new ShortCuts(this); + + /** The list containing all macroses of this L2PcInstance */ + private final MacroList _macroses = new MacroList(this); + + private final List _snoopListener = new FastList<>(); + private final List _snoopedPlayer = new FastList<>(); + + // hennas + private final L2HennaInstance[] _henna = new L2HennaInstance[3]; + private int _hennaSTR; + private int _hennaINT; + private int _hennaDEX; + private int _hennaMEN; + private int _hennaWIT; + private int _hennaCON; + + /** The L2Summon of the L2PcInstance */ + private L2Summon _summon = null; + /** The L2Decoy of the L2PcInstance */ + private L2Decoy _decoy = null; + /** The L2Trap of the L2PcInstance */ + private L2Trap _trap = null; + /** The L2Agathion of the L2PcInstance */ + private int _agathionId = 0; + // apparently, a L2PcInstance CAN have both a summon AND a tamed beast at the same time!! + // after Freya players can control more than one tamed beast + private List _tamedBeast = null; + + private boolean _minimapAllowed = false; + + // client radar + // TODO: This needs to be better intergrated and saved/loaded + private L2Radar _radar; + + // Party matching + // private int _partymatching = 0; + private int _partyroom = 0; + // private int _partywait = 0; + + // Clan related attributes + /** The Clan Identifier of the L2PcInstance */ + private int _clanId; + + /** The Clan object of the L2PcInstance */ + private L2Clan _clan; + + /** Apprentice and Sponsor IDs */ + private int _apprentice = 0; + private int _sponsor = 0; + + private long _clanJoinExpiryTime; + private long _clanCreateExpiryTime; + + private int _powerGrade = 0; + private int _clanPrivileges = 0; + + /** L2PcInstance's pledge class (knight, Baron, etc.) */ + private int _pledgeClass = 0; + private int _pledgeType = 0; + + /** Level at which the player joined the clan as an academy member */ + private int _lvlJoinedAcademy = 0; + + private int _wantsPeace = 0; + + // Death Penalty Buff Level + private int _deathPenaltyBuffLevel = 0; + + // charges + private final AtomicInteger _charges = new AtomicInteger(); + private ScheduledFuture _chargeTask = null; + + // Absorbed Souls + private int _souls = 0; + private ScheduledFuture _soulTask = null; + + // WorldPosition used by TARGET_SIGNET_GROUND + private Point3D _currentSkillWorldPosition; + + private L2AccessLevel _accessLevel; + + private boolean _messageRefusal = false; // message refusal mode + + private boolean _silenceMode = false; // silence mode + private boolean _dietMode = false; // ignore weight penalty + private boolean _tradeRefusal = false; // Trade refusal + private boolean _exchangeRefusal = false; // Exchange refusal + + private L2Party _party; + + // this is needed to find the inviting player for Party response + // there can only be one active party request at once + private L2PcInstance _activeRequester; + private long _requestExpireTime = 0; + private final L2Request _request = new L2Request(this); + private L2ItemInstance _arrowItem; + private L2ItemInstance _boltItem; + + // Used for protection after teleport + private long _protectEndTime = 0; + + public boolean isSpawnProtected() + { + return _protectEndTime > GameTimeController.getGameTicks(); + } + + private long _teleportProtectEndTime = 0; + + public boolean isTeleportProtected() + { + return _teleportProtectEndTime > GameTimeController.getGameTicks(); + } + + // protects a char from agro mobs when getting up from fake death + private long _recentFakeDeathEndTime = 0; + private boolean _isFakeDeath; + + /** The fists L2Weapon of the L2PcInstance (used when no weapon is equiped) */ + private L2Weapon _fistsWeaponItem; + + private final Map _chars = new FastMap<>(); + + // private byte _updateKnownCounter = 0; + + private int _expertiseArmorPenalty = 0; + private int _expertiseWeaponPenalty = 0; + + private boolean _isEnchanting = false; + private L2ItemInstance _activeEnchantItem = null; + private L2ItemInstance _activeEnchantSupportItem = null; + private L2ItemInstance _activeEnchantAttrItem = null; + private long _activeEnchantTimestamp = 0; + + protected boolean _inventoryDisable = false; + + protected Map _cubics = new FastMap().shared(); + + /** Active shots. */ + protected FastSet _activeSoulShots = new FastSet().shared(); + + public final ReentrantLock soulShotLock = new ReentrantLock(); + + /** Event parameters */ + public int eventX; + public int eventY; + public int eventZ; + public int eventkarma; + public int eventpvpkills; + public int eventpkkills; + public String eventTitle; + public LinkedList kills = new LinkedList<>(); + public boolean eventSitForced = false; + public boolean atEvent = false; + + private byte _handysBlockCheckerEventArena = -1; + + /** new loto ticket **/ + private final int _loto[] = new int[5]; + // public static int _loto_nums[] = {0,1,2,3,4,5,6,7,8,9,}; + /** new race ticket **/ + private final int _race[] = new int[2]; + + private final BlockList _blockList = new BlockList(this); + + private int _team = 0; + + /** + * lvl of alliance with ketra orcs or varka silenos, used in quests and aggro checks [-5,-1] varka, 0 neutral, [1,5] ketra + */ + private int _alliedVarkaKetra = 0; + + private L2Fishing _fishCombat; + private boolean _fishing = false; + private int _fishx = 0; + private int _fishy = 0; + private int _fishz = 0; + + private int[] _transformAllowedSkills = {}; + private ScheduledFuture _taskRentPet; + private ScheduledFuture _taskWater; + + /** Bypass validations */ + private final List _validBypass = new FastList<>(); + private final List _validBypass2 = new FastList<>(); + + private Forum _forumMail; + private Forum _forumMemo; + + /** + * Current skill in use. Note that L2Character has _lastSkillCast, but this has the button presses + */ + private SkillDat _currentSkill; + private SkillDat _currentPetSkill; + + /** Skills queued because a skill is already in progress */ + private SkillDat _queuedSkill; + + private int _cursedWeaponEquippedId = 0; + private boolean _combatFlagEquippedId = false; + + private int _reviveRequested = 0; + private double _revivePower = 0; + private boolean _revivePet = false; + + private double _cpUpdateIncCheck = .0; + private double _cpUpdateDecCheck = .0; + private double _cpUpdateInterval = .0; + private double _mpUpdateIncCheck = .0; + private double _mpUpdateDecCheck = .0; + private double _mpUpdateInterval = .0; + + /* + * not used any more private boolean _isRidingFenrirWolf = false; private boolean _isRidingWFenrirWolf = false; private boolean _isRidingGreatSnowWolf = false; + */ + private boolean _isRidingStrider = false; + private boolean _isFlyingMounted = false; + + /** Char Coords from Client */ + private int _clientX; + private int _clientY; + private int _clientZ; + private int _clientHeading; + + // during fall validations will be disabled for 10 ms. + private static final int FALLING_VALIDATION_DELAY = 10000; + private volatile long _fallingTimestamp = 0; + + private int _multiSocialTarget = 0; + private int _multiSociaAction = 0; + + private int _movieId = 0; + + private String _adminConfirmCmd = null; + + private volatile long _lastItemAuctionInfoRequest = 0; + + private Future _PvPRegTask; + + private long _pvpFlagLasts; + + public void setPvpFlagLasts(long time) + { + _pvpFlagLasts = time; + } + + public long getPvpFlagLasts() + { + return _pvpFlagLasts; + } + + public void startPvPFlag() + { + updatePvPFlag(1); + + _PvPRegTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new PvPFlag(), 1000, 1000); + } + + public void stopPvpRegTask() + { + if (_PvPRegTask != null) + { + _PvPRegTask.cancel(true); + } + } + + public void stopPvPFlag() + { + stopPvpRegTask(); + + updatePvPFlag(0); + + _PvPRegTask = null; + } + + /** Task lauching the function stopPvPFlag() */ + private class PvPFlag implements Runnable + { + public PvPFlag() + { + + } + + @Override + public void run() + { + try + { + if (System.currentTimeMillis() > getPvpFlagLasts()) + { + stopPvPFlag(); + } + else if (System.currentTimeMillis() > (getPvpFlagLasts() - 20000)) + { + updatePvPFlag(2); + } + else + { + updatePvPFlag(1); + // Start a new PvP timer check + // checkPvPFlag(); + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "error in pvp flag task:", e); + } + } + } + + // Character UI + private L2UIKeysSettings _uiKeySettings; + + /** Herbs Task Time **/ + private int _herbstask = 0; + + /** Task for Herbs */ + + private class HerbTask implements Runnable + { + private final String _process; + private final int _itemId; + private final long _count; + private final L2Object _reference; + private final boolean _sendMessage; + + HerbTask(String process, int itemId, long count, L2Object reference, boolean sendMessage) + { + _process = process; + _itemId = itemId; + _count = count; + _reference = reference; + _sendMessage = sendMessage; + } + + @Override + public void run() + { + try + { + addItem(_process, _itemId, _count, _reference, _sendMessage); + } + catch (Exception e) + { + _log.log(Level.WARNING, "", e); + } + } + } + + /** ShortBuff clearing Task */ + ScheduledFuture _shortBuffTask = null; + + protected class ShortBuffTask implements Runnable + { + @Override + public void run() + { + L2PcInstance.this.sendPacket(new ShortBuffStatusUpdate(0, 0, 0)); + setShortBuffTaskSkillId(0); + } + } + + // L2JMOD Wedding + private boolean _married = false; + private int _partnerId = 0; + private int _coupleId = 0; + private boolean _engagerequest = false; + private int _engageid = 0; + private boolean _marryrequest = false; + private boolean _marryaccepted = false; + + /** Skill casting information (used to queue when several skills are cast in a short time) **/ + public static class SkillDat + { + private final L2Skill _skill; + private final boolean _ctrlPressed; + private final boolean _shiftPressed; + + protected SkillDat(L2Skill skill, boolean ctrlPressed, boolean shiftPressed) + { + _skill = skill; + _ctrlPressed = ctrlPressed; + _shiftPressed = shiftPressed; + } + + public boolean isCtrlPressed() + { + return _ctrlPressed; + } + + public boolean isShiftPressed() + { + return _shiftPressed; + } + + public L2Skill getSkill() + { + return _skill; + } + + public int getSkillId() + { + return (getSkill() != null) ? getSkill().getId() : -1; + } + } + + // summon friend + private final SummonRequest _summonRequest = new SummonRequest(); + + protected static class SummonRequest + { + private L2PcInstance _target = null; + private L2Skill _skill = null; + + public void setTarget(L2PcInstance destination, L2Skill skill) + { + _target = destination; + _skill = skill; + } + + public L2PcInstance getTarget() + { + return _target; + } + + public L2Skill getSkill() + { + return _skill; + } + } + + // open/close gates + private final GatesRequest _gatesRequest = new GatesRequest(); + + protected static class GatesRequest + { + private L2DoorInstance _target = null; + + public void setTarget(L2DoorInstance door) + { + _target = door; + } + + public L2DoorInstance getDoor() + { + return _target; + } + } + + /** + * Create a new L2PcInstance and add it in the characters table of the database.
    + *
    + * Actions :
    + *
    + *
  • Create a new L2PcInstance with an account name
  • Set the name, the Hair Style, the Hair Color and the Face type of the L2PcInstance
  • Add the player in the characters table of the database

  • + *
    + * @param objectId Identifier of the object to initialized + * @param template The L2PcTemplate to apply to the L2PcInstance + * @param accountName The name of the L2PcInstance + * @param name The name of the L2PcInstance + * @param hairStyle The hair style Identifier of the L2PcInstance + * @param hairColor The hair color Identifier of the L2PcInstance + * @param face The face type Identifier of the L2PcInstance + * @return The L2PcInstance added to the database or null + */ + public static L2PcInstance create(int objectId, L2PcTemplate template, String accountName, String name, byte hairStyle, byte hairColor, byte face, boolean sex) + { + // Create a new L2PcInstance with an account name + PcAppearance app = new PcAppearance(face, hairColor, hairStyle, sex); + L2PcInstance player = new L2PcInstance(objectId, template, accountName, app); + + // Set the name of the L2PcInstance + player.setName(name); + + // Set Character's create time + player.setCreateTime(System.currentTimeMillis()); + + // Set the base class ID to that of the actual class ID. + player.setBaseClass(player.getClassId()); + // Kept for backwards compabitility. + player.setNewbie(1); + // Add the player in the characters table of the database + boolean ok = player.createDb(); + + if (!ok) + { + return null; + } + + return player; + } + + public static L2PcInstance createDummyPlayer(int objectId, String name) + { + // Create a new L2PcInstance with an account name + L2PcInstance player = new L2PcInstance(objectId); + player.setName(name); + + return player; + } + + public String getAccountName() + { + if (getClient() == null) + { + return getAccountNamePlayer(); + } + return getClient().getAccountName(); + } + + public String getAccountNamePlayer() + { + return _accountName; + } + + public Map getAccountChars() + { + return _chars; + } + + public int getRelation(L2PcInstance target) + { + int result = 0; + + if (getClan() != null) + { + result |= RelationChanged.RELATION_CLAN_MEMBER; + if (getClan() == target.getClan()) + { + result |= RelationChanged.RELATION_CLAN_MATE; + } + if (getAllyId() != 0) + { + result |= RelationChanged.RELATION_ALLY_MEMBER; + } + } + if (isClanLeader()) + { + result |= RelationChanged.RELATION_LEADER; + } + if ((getParty() != null) && (getParty() == target.getParty())) + { + result |= RelationChanged.RELATION_HAS_PARTY; + for (int i = 0; i < getParty().getPartyMembers().size(); i++) + { + if (getParty().getPartyMembers().get(i) != this) + { + continue; + } + switch (i) + { + case 0: + result |= RelationChanged.RELATION_PARTYLEADER; // 0x10 + break; + case 1: + result |= RelationChanged.RELATION_PARTY4; // 0x8 + break; + case 2: + result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY2 + RelationChanged.RELATION_PARTY1; // 0x7 + break; + case 3: + result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY2; // 0x6 + break; + case 4: + result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY1; // 0x5 + break; + case 5: + result |= RelationChanged.RELATION_PARTY3; // 0x4 + break; + case 6: + result |= RelationChanged.RELATION_PARTY2 + RelationChanged.RELATION_PARTY1; // 0x3 + break; + case 7: + result |= RelationChanged.RELATION_PARTY2; // 0x2 + break; + case 8: + result |= RelationChanged.RELATION_PARTY1; // 0x1 + break; + } + } + } + if (getSiegeState() != 0) + { + if (TerritoryWarManager.getInstance().getRegisteredTerritoryId(this) != 0) + { + result |= RelationChanged.RELATION_TERRITORY_WAR; + } + else + { + result |= RelationChanged.RELATION_INSIEGE; + if (getSiegeState() != target.getSiegeState()) + { + result |= RelationChanged.RELATION_ENEMY; + } + else + { + result |= RelationChanged.RELATION_ALLY; + } + if (getSiegeState() == 1) + { + result |= RelationChanged.RELATION_ATTACKER; + } + } + } + if ((getClan() != null) && (target.getClan() != null)) + { + if ((target.getPledgeType() != L2Clan.SUBUNIT_ACADEMY) && (getPledgeType() != L2Clan.SUBUNIT_ACADEMY) && target.getClan().isAtWarWith(getClan().getClanId())) + { + result |= RelationChanged.RELATION_1SIDED_WAR; + if (getClan().isAtWarWith(target.getClan().getClanId())) + { + result |= RelationChanged.RELATION_MUTUAL_WAR; + } + } + } + if (getBlockCheckerArena() != -1) + { + result |= RelationChanged.RELATION_INSIEGE; + HandysBlockCheckerManager.ArenaParticipantsHolder holder = HandysBlockCheckerManager.getInstance().getHolder(getBlockCheckerArena()); + if (holder.getPlayerTeam(this) == 0) + { + result |= RelationChanged.RELATION_ENEMY; + } + else + { + result |= RelationChanged.RELATION_ALLY; + } + result |= RelationChanged.RELATION_ATTACKER; + } + return result; + } + + /** + * Retrieve a L2PcInstance from the characters table of the database and add it in _allObjects of the L2world (call restore method).
    + *
    + * Actions :
    + *
    + *
  • Retrieve the L2PcInstance from the characters table of the database
  • Add the L2PcInstance object in _allObjects
  • Set the x,y,z position of the L2PcInstance and make it invisible
  • Update the overloaded status of the L2PcInstance

  • + *
    + * @param objectId Identifier of the object to initialized + * @return The L2PcInstance loaded from the database + */ + public static L2PcInstance load(int objectId) + { + return restore(objectId); + } + + private void initPcStatusUpdateValues() + { + _cpUpdateInterval = getMaxCp() / 352.0; + _cpUpdateIncCheck = getMaxCp(); + _cpUpdateDecCheck = getMaxCp() - _cpUpdateInterval; + _mpUpdateInterval = getMaxMp() / 352.0; + _mpUpdateIncCheck = getMaxMp(); + _mpUpdateDecCheck = getMaxMp() - _mpUpdateInterval; + } + + /** + * Constructor of L2PcInstance (use L2Character constructor).
    + *
    + * Actions :
    + *
    + *
  • Call the L2Character constructor to create an empty _skills slot and copy basic Calculator set to this L2PcInstance
  • Set the name of the L2PcInstance

  • + *
    + * Caution : This method SET the level of the L2PcInstance to 1
    + *
    + * @param objectId Identifier of the object to initialized + * @param template The L2PcTemplate to apply to the L2PcInstance + * @param accountName The name of the account including this L2PcInstance + */ + private L2PcInstance(int objectId, L2PcTemplate template, String accountName, PcAppearance app) + { + super(objectId, template); + setInstanceType(InstanceType.L2PcInstance); + super.initCharStatusUpdateValues(); + initPcStatusUpdateValues(); + + _accountName = accountName; + app.setOwner(this); + _appearance = app; + + // Create an AI + _ai = new L2PlayerAI(new L2PcInstance.AIAccessor()); + + // Create a L2Radar object + _radar = new L2Radar(this); + + startVitalityTask(); + } + + private L2PcInstance(int objectId) + { + super(objectId, null); + setInstanceType(InstanceType.L2PcInstance); + super.initCharStatusUpdateValues(); + initPcStatusUpdateValues(); + } + + @Override + public final PcKnownList getKnownList() + { + return (PcKnownList) super.getKnownList(); + } + + @Override + public void initKnownList() + { + setKnownList(new PcKnownList(this)); + } + + @Override + public final PcStat getStat() + { + return (PcStat) super.getStat(); + } + + @Override + public void initCharStat() + { + setStat(new PcStat(this)); + } + + @Override + public final PcStatus getStatus() + { + return (PcStatus) super.getStatus(); + } + + @Override + public void initCharStatus() + { + setStatus(new PcStatus(this)); + } + + @Override + public PcPosition getPosition() + { + return (PcPosition) super.getPosition(); + } + + @Override + public void initPosition() + { + setObjectPosition(new PcPosition(this)); + } + + public final PcAppearance getAppearance() + { + return _appearance; + } + + /** + * Return the base L2PcTemplate link to the L2PcInstance.
    + *
    + */ + public final L2PcTemplate getBaseTemplate() + { + return CharTemplateTable.getInstance().getTemplate(_baseClass); + } + + /** Return the L2PcTemplate link to the L2PcInstance. */ + @Override + public final L2PcTemplate getTemplate() + { + return (L2PcTemplate) super.getTemplate(); + } + + public void setTemplate(ClassId newclass) + { + super.setTemplate(CharTemplateTable.getInstance().getTemplate(newclass)); + } + + /** + * Return the AI of the L2PcInstance (create it if necessary).
    + *
    + */ + @Override + public L2CharacterAI getAI() + { + L2CharacterAI ai = _ai; // copy handle + if (ai == null) + { + synchronized (this) + { + if (_ai == null) + { + _ai = new L2PlayerAI(new L2PcInstance.AIAccessor()); + } + return _ai; + } + } + return ai; + } + + /** Return the Level of the L2PcInstance. */ + @Override + public final int getLevel() + { + return getStat().getLevel(); + } + + /** + * Return the _newbie rewards state of the L2PcInstance.
    + *
    + */ + public int getNewbie() + { + return _newbie; + } + + /** + * Set the _newbie rewards state of the L2PcInstance.
    + *
    + * @param newbieRewards The Identifier of the _newbie state
    + *
    + */ + public void setNewbie(int newbieRewards) + { + _newbie = newbieRewards; + } + + public void setBaseClass(int baseClass) + { + _baseClass = baseClass; + } + + public void setBaseClass(ClassId classId) + { + _baseClass = classId.ordinal(); + } + + public boolean isInStoreMode() + { + return (getPrivateStoreType() > 0); + } + + // public boolean isInCraftMode() { return (getPrivateStoreType() == STORE_PRIVATE_MANUFACTURE); } + + public boolean isInCraftMode() + { + return _inCraftMode; + } + + public void isInCraftMode(boolean b) + { + _inCraftMode = b; + } + + /** + * Manage Logout Task:
  • Remove player from world
    + * {@link L2PcInstance#deleteMe(boolean)}
  • Save player data into DB
    + * {@link L2GameClient#saveCharToDisk(L2PcInstance, boolean)}

  • + *
    + */ + public void logout() + { + logout(true); + } + + /** + * Manage Logout Task:
  • Remove player from world
    + * {@link L2PcInstance#deleteMe(boolean)}
  • Save player data into DB
    + * {@link L2GameClient#saveCharToDisk(L2PcInstance, boolean)}

  • + *
    + * @param closeClient + */ + public void logout(boolean closeClient) + { + try + { + closeNetConnection(closeClient); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception on logout(): " + e.getMessage(), e); + } + } + + /** + * Return a table containing all Common L2RecipeList of the L2PcInstance.
    + *
    + */ + public L2RecipeList[] getCommonRecipeBook() + { + return _commonRecipeBook.values().toArray(new L2RecipeList[_commonRecipeBook.values().size()]); + } + + /** + * Return a table containing all Dwarf L2RecipeList of the L2PcInstance.
    + *
    + */ + public L2RecipeList[] getDwarvenRecipeBook() + { + return _dwarvenRecipeBook.values().toArray(new L2RecipeList[_dwarvenRecipeBook.values().size()]); + } + + /** + * Add a new L2RecipList to the table _commonrecipebook containing all L2RecipeList of the L2PcInstance
    + *
    + * @param recipe The L2RecipeList to add to the _recipebook + */ + public void registerCommonRecipeList(L2RecipeList recipe, boolean saveToDb) + { + _commonRecipeBook.put(recipe.getId(), recipe); + + if (saveToDb) + { + insertNewRecipeData(recipe.getId(), false); + } + } + + /** + * Add a new L2RecipList to the table _recipebook containing all L2RecipeList of the L2PcInstance
    + *
    + * @param recipe The L2RecipeList to add to the _recipebook + */ + public void registerDwarvenRecipeList(L2RecipeList recipe, boolean saveToDb) + { + _dwarvenRecipeBook.put(recipe.getId(), recipe); + + if (saveToDb) + { + insertNewRecipeData(recipe.getId(), true); + } + } + + /** + * @param RecipeID The Identifier of the L2RecipeList to check in the player's recipe books + * @return TRUE if player has the recipe on Common or Dwarven Recipe book else returns FALSE + */ + public boolean hasRecipeList(int recipeId) + { + if (_dwarvenRecipeBook.containsKey(recipeId)) + { + return true; + } + else if (_commonRecipeBook.containsKey(recipeId)) + { + return true; + } + else + { + return false; + } + } + + /** + * Tries to remove a L2RecipList from the table _DwarvenRecipeBook or from table _CommonRecipeBook, those table contain all L2RecipeList of the L2PcInstance
    + *
    + * @param RecipeID The Identifier of the L2RecipeList to remove from the _recipebook + */ + public void unregisterRecipeList(int recipeId) + { + if (_dwarvenRecipeBook.remove(recipeId) != null) + { + deleteRecipeData(recipeId, true); + } + else if (_commonRecipeBook.remove(recipeId) != null) + { + deleteRecipeData(recipeId, false); + } + else + { + _log.warning("Attempted to remove unknown RecipeList: " + recipeId); + } + + L2ShortCut[] allShortCuts = getAllShortCuts(); + + for (L2ShortCut sc : allShortCuts) + { + if ((sc != null) && (sc.getId() == recipeId) && (sc.getType() == L2ShortCut.TYPE_RECIPE)) + { + deleteShortCut(sc.getSlot(), sc.getPage()); + } + } + } + + private void insertNewRecipeData(int recipeId, boolean isDwarf) + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("INSERT INTO character_recipebook (charId, id, classIndex, type) values(?,?,?,?)"); + statement.setInt(1, getObjectId()); + statement.setInt(2, recipeId); + statement.setInt(3, isDwarf ? _classIndex : 0); + statement.setInt(4, isDwarf ? 1 : 0); + statement.execute(); + statement.close(); + } + catch (SQLException e) + { + if (_log.isLoggable(Level.SEVERE)) + { + _log.log(Level.SEVERE, "SQL exception while inserting recipe: " + recipeId + " from character " + getObjectId(), e); + } + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void deleteRecipeData(int recipeId, boolean isDwarf) + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("DELETE FROM character_recipebook WHERE charId=? AND id=? AND classIndex=?"); + statement.setInt(1, getObjectId()); + statement.setInt(2, recipeId); + statement.setInt(3, isDwarf ? _classIndex : 0); + statement.execute(); + statement.close(); + } + catch (SQLException e) + { + if (_log.isLoggable(Level.SEVERE)) + { + _log.log(Level.SEVERE, "SQL exception while deleting recipe: " + recipeId + " from character " + getObjectId(), e); + } + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Returns the Id for the last talked quest NPC.
    + *
    + */ + public int getLastQuestNpcObject() + { + return _questNpcObject; + } + + public void setLastQuestNpcObject(int npcId) + { + _questNpcObject = npcId; + } + + /** + * Return the QuestState object corresponding to the quest name.
    + *
    + * @param quest The name of the quest + */ + public QuestState getQuestState(String quest) + { + return _quests.get(quest); + } + + /** + * Add a QuestState to the table _quest containing all quests began by the L2PcInstance.
    + *
    + * @param qs The QuestState to add to _quest + */ + public void setQuestState(QuestState qs) + { + _quests.put(qs.getQuestName(), qs); + } + + /** + * Remove a QuestState from the table _quest containing all quests began by the L2PcInstance.
    + *
    + * @param quest The name of the quest + */ + public void delQuestState(String quest) + { + _quests.remove(quest); + } + + private QuestState[] addToQuestStateArray(QuestState[] questStateArray, QuestState state) + { + int len = questStateArray.length; + QuestState[] tmp = new QuestState[len + 1]; + for (int i = 0; i < len; i++) + { + tmp[i] = questStateArray[i]; + } + tmp[len] = state; + return tmp; + } + + /** + * Return a table containing all Quest in progress from the table _quests.
    + *
    + */ + public Quest[] getAllActiveQuests() + { + FastList quests = new FastList<>(); + + for (QuestState qs : _quests.values()) + { + if (qs == null) + { + continue; + } + + if (qs.getQuest() == null) + { + continue; + } + + int questId = qs.getQuest().getQuestIntId(); + if ((questId > 19999) || (questId < 1)) + { + continue; + } + + if (!qs.isStarted() && !Config.DEVELOPER) + { + continue; + } + + quests.add(qs.getQuest()); + } + + return quests.toArray(new Quest[quests.size()]); + } + + /** + * Return a table containing all QuestState to modify after a L2Attackable killing.
    + *
    + * @param npcId The Identifier of the L2Attackable attacked + */ + public QuestState[] getQuestsForAttacks(L2Npc npc) + { + // Create a QuestState table that will contain all QuestState to modify + QuestState[] states = null; + + // Go through the QuestState of the L2PcInstance quests + for (Quest quest : npc.getTemplate().getEventQuests(Quest.QuestEventType.ON_ATTACK)) + { + // Check if the Identifier of the L2Attackable attck is needed for the current quest + if (getQuestState(quest.getName()) != null) + { + // Copy the current L2PcInstance QuestState in the QuestState table + if (states == null) + { + states = new QuestState[] + { + getQuestState(quest.getName()) + }; + } + else + { + states = addToQuestStateArray(states, getQuestState(quest.getName())); + } + } + } + + // Return a table containing all QuestState to modify + return states; + } + + /** + * Return a table containing all QuestState to modify after a L2Attackable killing.
    + *
    + * @param npcId The Identifier of the L2Attackable killed + */ + public QuestState[] getQuestsForKills(L2Npc npc) + { + // Create a QuestState table that will contain all QuestState to modify + QuestState[] states = null; + + // Go through the QuestState of the L2PcInstance quests + for (Quest quest : npc.getTemplate().getEventQuests(Quest.QuestEventType.ON_KILL)) + { + // Check if the Identifier of the L2Attackable killed is needed for the current quest + if (getQuestState(quest.getName()) != null) + { + // Copy the current L2PcInstance QuestState in the QuestState table + if (states == null) + { + states = new QuestState[] + { + getQuestState(quest.getName()) + }; + } + else + { + states = addToQuestStateArray(states, getQuestState(quest.getName())); + } + } + } + + // Return a table containing all QuestState to modify + return states; + } + + /** + * Return a table containing all QuestState from the table _quests in which the L2PcInstance must talk to the NPC.
    + *
    + * @param npcId The Identifier of the NPC + */ + public QuestState[] getQuestsForTalk(int npcId) + { + // Create a QuestState table that will contain all QuestState to modify + QuestState[] states = null; + + // Go through the QuestState of the L2PcInstance quests + Quest[] quests = NpcTable.getInstance().getTemplate(npcId).getEventQuests(Quest.QuestEventType.ON_TALK); + if (quests != null) + { + for (Quest quest : quests) + { + if (quest != null) + { + // Copy the current L2PcInstance QuestState in the QuestState table + if (getQuestState(quest.getName()) != null) + { + if (states == null) + { + states = new QuestState[] + { + getQuestState(quest.getName()) + }; + } + else + { + states = addToQuestStateArray(states, getQuestState(quest.getName())); + } + } + } + } + } + + // Return a table containing all QuestState to modify + return states; + } + + public QuestState processQuestEvent(String quest, String event) + { + QuestState retval = null; + if (event == null) + { + event = ""; + } + QuestState qs = getQuestState(quest); + if ((qs == null) && (event.length() == 0)) + { + return retval; + } + if (qs == null) + { + Quest q = QuestManager.getInstance().getQuest(quest); + if (q == null) + { + return retval; + } + qs = q.newQuestState(this); + } + if (qs != null) + { + if (getLastQuestNpcObject() > 0) + { + L2Object object = L2World.getInstance().findObject(getLastQuestNpcObject()); + if ((object instanceof L2Npc) && isInsideRadius(object, L2Npc.INTERACTION_DISTANCE, false, false)) + { + L2Npc npc = (L2Npc) object; + QuestState[] states = getQuestsForTalk(npc.getNpcId()); + + if (states != null) + { + for (QuestState state : states) + { + if (state.getQuest().getName().equals(qs.getQuest().getName())) + { + if (qs.getQuest().notifyEvent(event, npc, this)) + { + showQuestWindow(quest, State.getStateName(qs.getState())); + } + + retval = qs; + } + } + } + } + } + } + + return retval; + } + + private void showQuestWindow(String questId, String stateId) + { + String path = "data/scripts/quests/" + questId + "/" + stateId + ".htm"; + String content = HtmCache.getInstance().getHtm(getHtmlPrefix(), path); // TODO path for quests html + + if (content != null) + { + if (Config.DEBUG) + { + _log.fine("Showing quest window for quest " + questId + " state " + stateId + " html path: " + path); + } + + NpcHtmlMessage npcReply = new NpcHtmlMessage(5); + npcReply.setHtml(content); + sendPacket(npcReply); + } + + sendPacket(ActionFailed.STATIC_PACKET); + } + + /** List of all QuestState instance that needs to be notified of this L2PcInstance's or its pet's death */ + private List _notifyQuestOfDeathList; + + /** + * Add QuestState instance that is to be notified of L2PcInstance's death.
    + *
    + * @param qs The QuestState that subscribe to this event + */ + public void addNotifyQuestOfDeath(QuestState qs) + { + if (qs == null) + { + return; + } + + if (!getNotifyQuestOfDeath().contains(qs)) + { + getNotifyQuestOfDeath().add(qs); + } + } + + /** + * Remove QuestState instance that is to be notified of L2PcInstance's death.
    + *
    + * @param qs The QuestState that subscribe to this event + */ + public void removeNotifyQuestOfDeath(QuestState qs) + { + if ((qs == null) || (_notifyQuestOfDeathList == null)) + { + return; + } + + _notifyQuestOfDeathList.remove(qs); + } + + /** + * Return a list of QuestStates which registered for notify of death of this L2PcInstance.
    + *
    + */ + public final List getNotifyQuestOfDeath() + { + if (_notifyQuestOfDeathList == null) + { + synchronized (this) + { + if (_notifyQuestOfDeathList == null) + { + _notifyQuestOfDeathList = new FastList<>(); + } + } + } + + return _notifyQuestOfDeathList; + } + + public final boolean isNotifyQuestOfDeathEmpty() + { + return (_notifyQuestOfDeathList == null) || _notifyQuestOfDeathList.isEmpty(); + } + + /** + * Return a table containing all L2ShortCut of the L2PcInstance.
    + *
    + */ + public L2ShortCut[] getAllShortCuts() + { + return _shortCuts.getAllShortCuts(); + } + + /** + * Return the L2ShortCut of the L2PcInstance corresponding to the position (page-slot).
    + *
    + * @param slot The slot in wich the shortCuts is equiped + * @param page The page of shortCuts containing the slot + */ + public L2ShortCut getShortCut(int slot, int page) + { + return _shortCuts.getShortCut(slot, page); + } + + /** + * Add a L2shortCut to the L2PcInstance _shortCuts
    + *
    + */ + public void registerShortCut(L2ShortCut shortcut) + { + _shortCuts.registerShortCut(shortcut); + } + + /** + * Delete the L2ShortCut corresponding to the position (page-slot) from the L2PcInstance _shortCuts.
    + *
    + */ + public void deleteShortCut(int slot, int page) + { + _shortCuts.deleteShortCut(slot, page); + } + + /** + * Add a L2Macro to the L2PcInstance _macroses
    + *
    + */ + public void registerMacro(L2Macro macro) + { + _macroses.registerMacro(macro); + } + + /** + * Delete the L2Macro corresponding to the Identifier from the L2PcInstance _macroses.
    + *
    + */ + public void deleteMacro(int id) + { + _macroses.deleteMacro(id); + } + + /** + * Return all L2Macro of the L2PcInstance.
    + *
    + */ + public MacroList getMacroses() + { + return _macroses; + } + + /** + * Set the siege state of the L2PcInstance.
    + *
    + * 1 = attacker, 2 = defender, 0 = not involved + */ + public void setSiegeState(byte siegeState) + { + _siegeState = siegeState; + } + + /** + * Get the siege state of the L2PcInstance.
    + *
    + * 1 = attacker, 2 = defender, 0 = not involved + */ + public byte getSiegeState() + { + return _siegeState; + } + + /** + * Set the siege Side of the L2PcInstance.
    + *
    + */ + public void setSiegeSide(int val) + { + _siegeSide = val; + } + + public boolean isRegisteredOnThisSiegeField(int val) + { + if ((_siegeSide != val) && ((_siegeSide < 81) || (_siegeSide > 89))) + { + return false; + } + return true; + } + + public int getSiegeSide() + { + return _siegeSide; + } + + /** + * Set the PvP Flag of the L2PcInstance.
    + *
    + */ + public void setPvpFlag(int pvpFlag) + { + _pvpFlag = (byte) pvpFlag; + } + + @Override + public byte getPvpFlag() + { + return _pvpFlag; + } + + @Override + public void updatePvPFlag(int value) + { + if (getPvpFlag() == value) + { + return; + } + setPvpFlag(value); + + sendPacket(new UserInfo(this)); + sendPacket(new ExBrExtraUserInfo(this)); + + // If this player has a pet update the pets pvp flag as well + if (getPet() != null) + { + sendPacket(new RelationChanged(getPet(), getRelation(this), false)); + } + + Collection plrs = getKnownList().getKnownPlayers().values(); + // synchronized (getKnownList().getKnownPlayers()) + { + for (L2PcInstance target : plrs) + { + target.sendPacket(new RelationChanged(this, getRelation(target), isAutoAttackable(target))); + if (getPet() != null) + { + target.sendPacket(new RelationChanged(getPet(), getRelation(target), isAutoAttackable(target))); + } + } + } + } + + @Override + public void revalidateZone(boolean force) + { + // Cannot validate if not in a world region (happens during teleport) + if (getWorldRegion() == null) + { + return; + } + + // This function is called too often from movement code + if (force) + { + _zoneValidateCounter = 4; + } + else + { + _zoneValidateCounter--; + if (_zoneValidateCounter < 0) + { + _zoneValidateCounter = 4; + } + else + { + return; + } + } + + getWorldRegion().revalidateZones(this); + + if (Config.ALLOW_WATER) + { + checkWaterState(); + } + + if (isInsideZone(ZONE_ALTERED)) + { + if (_lastCompassZone == ExSetCompassZoneCode.ALTEREDZONE) + { + return; + } + _lastCompassZone = ExSetCompassZoneCode.ALTEREDZONE; + ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.ALTEREDZONE); + sendPacket(cz); + } + else if (isInsideZone(ZONE_SIEGE)) + { + if (_lastCompassZone == ExSetCompassZoneCode.SIEGEWARZONE2) + { + return; + } + _lastCompassZone = ExSetCompassZoneCode.SIEGEWARZONE2; + ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.SIEGEWARZONE2); + sendPacket(cz); + } + else if (isInsideZone(ZONE_PVP)) + { + if (_lastCompassZone == ExSetCompassZoneCode.PVPZONE) + { + return; + } + _lastCompassZone = ExSetCompassZoneCode.PVPZONE; + ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.PVPZONE); + sendPacket(cz); + } + else if (isIn7sDungeon()) + { + if (_lastCompassZone == ExSetCompassZoneCode.SEVENSIGNSZONE) + { + return; + } + _lastCompassZone = ExSetCompassZoneCode.SEVENSIGNSZONE; + ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.SEVENSIGNSZONE); + sendPacket(cz); + } + else if (isInsideZone(ZONE_PEACE)) + { + if (_lastCompassZone == ExSetCompassZoneCode.PEACEZONE) + { + return; + } + _lastCompassZone = ExSetCompassZoneCode.PEACEZONE; + ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.PEACEZONE); + sendPacket(cz); + } + else + { + if (_lastCompassZone == ExSetCompassZoneCode.GENERALZONE) + { + return; + } + if (_lastCompassZone == ExSetCompassZoneCode.SIEGEWARZONE2) + { + updatePvPStatus(); + } + _lastCompassZone = ExSetCompassZoneCode.GENERALZONE; + ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.GENERALZONE); + sendPacket(cz); + } + } + + /** + * Return True if the L2PcInstance can Craft Dwarven Recipes.
    + *
    + */ + public boolean hasDwarvenCraft() + { + return getSkillLevel(L2Skill.SKILL_CREATE_DWARVEN) >= 1; + } + + public int getDwarvenCraft() + { + return getSkillLevel(L2Skill.SKILL_CREATE_DWARVEN); + } + + /** + * Return True if the L2PcInstance can Craft Dwarven Recipes.
    + *
    + */ + public boolean hasCommonCraft() + { + return getSkillLevel(L2Skill.SKILL_CREATE_COMMON) >= 1; + } + + public int getCommonCraft() + { + return getSkillLevel(L2Skill.SKILL_CREATE_COMMON); + } + + /** + * Return the PK counter of the L2PcInstance.
    + *
    + */ + public int getPkKills() + { + return _pkKills; + } + + /** + * Set the PK counter of the L2PcInstance.
    + *
    + */ + public void setPkKills(int pkKills) + { + _pkKills = pkKills; + } + + /** + * Return the _deleteTimer of the L2PcInstance.
    + *
    + */ + public long getDeleteTimer() + { + return _deleteTimer; + } + + /** + * Set the _deleteTimer of the L2PcInstance.
    + *
    + */ + public void setDeleteTimer(long deleteTimer) + { + _deleteTimer = deleteTimer; + } + + /** + * Return the current weight of the L2PcInstance.
    + *
    + */ + public int getCurrentLoad() + { + return _inventory.getTotalWeight(); + } + + /** + * Return the number of recommandation obtained by the L2PcInstance.
    + *
    + */ + public int getRecomHave() + { + return _recomHave; + } + + /** + * Increment the number of recommandation obtained by the L2PcInstance (Max : 255).
    + *
    + */ + protected void incRecomHave() + { + if (_recomHave < 255) + { + _recomHave++; + } + } + + /** + * Set the number of recommandation obtained by the L2PcInstance (Max : 255).
    + *
    + */ + public void setRecomHave(int value) + { + if (value > 255) + { + _recomHave = 255; + } + else if (value < 0) + { + _recomHave = 0; + } + else + { + _recomHave = value; + } + } + + /** + * Set the number of recommandation obtained by the L2PcInstance (Max : 255).
    + *
    + */ + public void setRecomLeft(int value) + { + if (value > 255) + { + _recomLeft = 255; + } + else if (value < 0) + { + _recomLeft = 0; + } + else + { + _recomLeft = value; + } + } + + /** + * Return the number of recommandation that the L2PcInstance can give.
    + *
    + */ + public int getRecomLeft() + { + return _recomLeft; + } + + /** + * Increment the number of recommandation that the L2PcInstance can give.
    + *
    + */ + protected void decRecomLeft() + { + if (_recomLeft > 0) + { + _recomLeft--; + } + } + + public void giveRecom(L2PcInstance target) + { + target.incRecomHave(); + decRecomLeft(); + } + + /** + * Set the exp of the L2PcInstance before a death + * @param exp + */ + public void setExpBeforeDeath(long exp) + { + _expBeforeDeath = exp; + } + + public long getExpBeforeDeath() + { + return _expBeforeDeath; + } + + /** + * Return the Karma of the L2PcInstance.
    + *
    + */ + @Override + public int getKarma() + { + return _karma; + } + + /** + * Set the Karma of the L2PcInstance and send a Server->Client packet StatusUpdate (broadcast).
    + *
    + */ + public void setKarma(int karma) + { + if (karma < 0) + { + karma = 0; + } + if ((_karma == 0) && (karma > 0)) + { + Collection objs = getKnownList().getKnownObjects().values(); + // synchronized (getKnownList().getKnownObjects()) + { + for (L2Object object : objs) + { + if (!(object instanceof L2GuardInstance)) + { + continue; + } + + if (((L2GuardInstance) object).getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE) + { + ((L2GuardInstance) object).getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null); + } + } + } + } + else if ((_karma > 0) && (karma == 0)) + { + // Send a Server->Client StatusUpdate packet with Karma and PvP Flag to the L2PcInstance and all L2PcInstance to inform (broadcast) + setKarmaFlag(0); + } + + _karma = karma; + broadcastKarma(); + } + + /** + * Return the max weight that the L2PcInstance can load.
    + *
    + */ + public int getMaxLoad() + { + // Weight Limit = (CON Modifier*69000)*Skills + // Source http://l2p.bravehost.com/weightlimit.html (May 2007) + // Fitted exponential curve to the data + int con = getCON(); + if (con < 1) + { + return 31000; + } + if (con > 59) + { + return 176000; + } + double baseLoad = Math.pow(1.029993928, con) * 30495.627366; + return (int) calcStat(Stats.MAX_LOAD, baseLoad * Config.ALT_WEIGHT_LIMIT, this, null); + } + + public int getExpertiseArmorPenalty() + { + return _expertiseArmorPenalty; + } + + public int getExpertiseWeaponPenalty() + { + return _expertiseWeaponPenalty; + } + + public int getWeightPenalty() + { + if (_dietMode) + { + return 0; + } + return _curWeightPenalty; + } + + /** + * Update the overloaded status of the L2PcInstance.
    + *
    + */ + public void refreshOverloaded() + { + int maxLoad = getMaxLoad(); + if (maxLoad > 0) + { + long weightproc = ((long) getCurrentLoad() * 1000) / maxLoad; + weightproc *= calcStat(Stats.WEIGHT_LIMIT, 1, this, null); + int newWeightPenalty; + if ((weightproc < 500) || _dietMode) + { + newWeightPenalty = 0; + } + else if (weightproc < 666) + { + newWeightPenalty = 1; + } + else if (weightproc < 800) + { + newWeightPenalty = 2; + } + else if (weightproc < 1000) + { + newWeightPenalty = 3; + } + else + { + newWeightPenalty = 4; + } + + if (_curWeightPenalty != newWeightPenalty) + { + _curWeightPenalty = newWeightPenalty; + if ((newWeightPenalty > 0) && !_dietMode) + { + super.addSkill(SkillTable.getInstance().getInfo(4270, newWeightPenalty)); + setIsOverloaded(getCurrentLoad() > maxLoad); + } + else + { + super.removeSkill(getKnownSkill(4270)); + setIsOverloaded(false); + } + sendPacket(new UserInfo(this)); + sendPacket(new EtcStatusUpdate(this)); + broadcastPacket(new CharInfo(this)); + broadcastPacket(new ExBrExtraUserInfo(this)); + } + } + } + + public void refreshExpertisePenalty() + { + if (!Config.EXPERTISE_PENALTY) + { + return; + } + + int armorPenalty = 0; + int weaponPenalty = 0; + + for (L2ItemInstance item : getInventory().getItems()) + { + if ((item != null) && item.isEquipped() && ((item.getItemType() != L2EtcItemType.ARROW) && (item.getItemType() != L2EtcItemType.BOLT))) + { + int crystaltype = item.getItem().getCrystalType(); + + if (crystaltype > getExpertiseLevel()) + { + if (item.isWeapon() && (crystaltype > weaponPenalty)) + { + weaponPenalty = crystaltype; + } + else if (crystaltype > armorPenalty) + { + armorPenalty = crystaltype; + } + } + } + } + + boolean changed = false; + + // calc armor penalty + armorPenalty = armorPenalty - getExpertiseLevel(); + + if (armorPenalty < 0) + { + armorPenalty = 0; + } + else if (armorPenalty > 4) + { + armorPenalty = 4; + } + + if ((getExpertiseArmorPenalty() != armorPenalty) || (getSkillLevel(6213) != armorPenalty)) + { + _expertiseArmorPenalty = armorPenalty; + + if (_expertiseArmorPenalty > 0) + { + super.addSkill(SkillTable.getInstance().getInfo(6213, _expertiseArmorPenalty)); // level used to be newPenalty + } + else + { + super.removeSkill(getKnownSkill(6213)); + } + + changed = true; + } + + // calc weapon penalty + weaponPenalty = weaponPenalty - getExpertiseLevel(); + if (weaponPenalty < 0) + { + weaponPenalty = 0; + } + else if (weaponPenalty > 4) + { + weaponPenalty = 4; + } + + if ((getExpertiseWeaponPenalty() != weaponPenalty) || (getSkillLevel(6209) != weaponPenalty)) + { + _expertiseWeaponPenalty = weaponPenalty; + + if (_expertiseWeaponPenalty > 0) + { + super.addSkill(SkillTable.getInstance().getInfo(6209, _expertiseWeaponPenalty)); // level used to be newPenalty + } + else + { + super.removeSkill(getKnownSkill(6209)); + } + + changed = true; + } + + if (changed) + { + sendPacket(new EtcStatusUpdate(this)); + } + } + + public void checkIfWeaponIsAllowed() + { + // Override for Gamemasters + if (isGM()) + { + return; + } + + // Iterate through all effects currently on the character. + for (L2Effect currenteffect : getAllEffects()) + { + L2Skill effectSkill = currenteffect.getSkill(); + + // Ignore all buff skills that are party related (ie. songs, dances) while still remaining weapon dependant on cast though. + if (!effectSkill.isOffensive() && !((effectSkill.getTargetType() == SkillTargetType.TARGET_PARTY) && (effectSkill.getSkillType() == L2SkillType.BUFF))) + { + // Check to rest to assure current effect meets weapon requirements. + if (!effectSkill.getWeaponDependancy(this)) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED); + sm.addSkillName(effectSkill); + sendPacket(sm); + + if (Config.DEBUG) + { + _log.info(" | Skill " + effectSkill.getName() + " has been disabled for (" + getName() + "); Reason: Incompatible Weapon Type."); + } + + currenteffect.exit(); + } + } + + continue; + } + } + + public void checkSShotsMatch(L2ItemInstance equipped, L2ItemInstance unequipped) + { + if (unequipped == null) + { + return; + } + + unequipped.setChargedSoulshot(L2ItemInstance.CHARGED_NONE); + unequipped.setChargedSpiritshot(L2ItemInstance.CHARGED_NONE); + + // on retail auto shots never disabled on uneqip + /* + * if (unequipped.getItem().getType2() == L2Item.TYPE2_WEAPON && (equipped == null ? true : equipped.getItem().getItemGradeSPlus() != unequipped.getItem().getItemGradeSPlus())) { disableAutoShotByCrystalType(unequipped.getItem().getItemGradeSPlus()); } + */ + } + + public void useEquippableItem(L2ItemInstance item, boolean abortAttack) + { + // Equip or unEquip + L2ItemInstance[] items = null; + final boolean isEquiped = item.isEquipped(); + final int oldInvLimit = getInventoryLimit(); + SystemMessage sm = null; + if ((item.getItem().getBodyPart() & L2Item.SLOT_MULTI_ALLWEAPON) != 0) + { + L2ItemInstance old = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND); + checkSShotsMatch(item, old); + } + + if (isEquiped) + { + if (item.getEnchantLevel() > 0) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED); + sm.addNumber(item.getEnchantLevel()); + sm.addItemName(item); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISARMED); + sm.addItemName(item); + } + sendPacket(sm); + + int slot = getInventory().getSlotFromItem(item); + // we cant unequip talisman by body slot + if (slot == L2Item.SLOT_DECO) + { + items = getInventory().unEquipItemInSlotAndRecord(item.getLocationSlot()); + } + else + { + items = getInventory().unEquipItemInBodySlotAndRecord(slot); + } + } + else + { + items = getInventory().equipItemAndRecord(item); + + if (item.isEquipped()) + { + if (item.getEnchantLevel() > 0) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_S2_EQUIPPED); + sm.addNumber(item.getEnchantLevel()); + sm.addItemName(item); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_EQUIPPED); + sm.addItemName(item); + } + sendPacket(sm); + + // Consume mana - will start a task if required; returns if item is not a shadow item + item.decreaseMana(false); + + if ((item.getItem().getBodyPart() & L2Item.SLOT_MULTI_ALLWEAPON) != 0) + { + rechargeAutoSoulShot(true, true, false); + } + } + else + { + sendPacket(SystemMessageId.CANNOT_EQUIP_ITEM_DUE_TO_BAD_CONDITION); + } + } + refreshExpertisePenalty(); + + broadcastUserInfo(); + + InventoryUpdate iu = new InventoryUpdate(); + iu.addItems(Arrays.asList(items)); + sendPacket(iu); + + if (abortAttack) + { + abortAttack(); + } + + if (getInventoryLimit() != oldInvLimit) + { + sendPacket(new ExStorageMaxCount(this)); + } + } + + /** + * Return the the PvP Kills of the L2PcInstance (Number of player killed during a PvP).
    + *
    + */ + public int getPvpKills() + { + return _pvpKills; + } + + /** + * Set the the PvP Kills of the L2PcInstance (Number of player killed during a PvP).
    + *
    + */ + public void setPvpKills(int pvpKills) + { + _pvpKills = pvpKills; + } + + /** + * Return the Fame of this L2PcInstance
    + *
    + * @return + */ + public int getFame() + { + return _fame; + } + + /** + * Set the Fame of this L2PcInstane
    + *
    + * @param fame + */ + public void setFame(int fame) + { + if (fame > Config.MAX_PERSONAL_FAME_POINTS) + { + _fame = Config.MAX_PERSONAL_FAME_POINTS; + } + else + { + _fame = fame; + } + } + + /** + * Return the ClassId object of the L2PcInstance contained in L2PcTemplate.
    + *
    + */ + public ClassId getClassId() + { + return getTemplate().classId; + } + + /** + * Set the template of the L2PcInstance.
    + *
    + * @param Id The Identifier of the L2PcTemplate to set to the L2PcInstance + */ + public void setClassId(int Id) + { + if (!_subclassLock.tryLock()) + { + return; + } + + try + { + if ((getLvlJoinedAcademy() != 0) && (_clan != null) && (PlayerClass.values()[Id].getLevel() == ClassLevel.Third)) + { + if (getLvlJoinedAcademy() <= 16) + { + _clan.addReputationScore(Config.JOIN_ACADEMY_MAX_REP_SCORE, true); + } + else if (getLvlJoinedAcademy() >= 39) + { + _clan.addReputationScore(Config.JOIN_ACADEMY_MIN_REP_SCORE, true); + } + else + { + _clan.addReputationScore((Config.JOIN_ACADEMY_MAX_REP_SCORE - ((getLvlJoinedAcademy() - 16) * 20)), true); + } + setLvlJoinedAcademy(0); + // oust pledge member from the academy, cuz he has finished his 2nd class transfer + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.CLAN_MEMBER_S1_EXPELLED); + msg.addPcName(this); + _clan.broadcastToOnlineMembers(msg); + _clan.broadcastToOnlineMembers(new PledgeShowMemberListDelete(getName())); + _clan.removeClanMember(getObjectId(), 0); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ACADEMY_MEMBERSHIP_TERMINATED)); + + // receive graduation gift + getInventory().addItem("Gift", 8181, 1, this, null); // give academy circlet + } + if (isSubClassActive()) + { + getSubClasses().get(_classIndex).setClassId(Id); + } + setTarget(this); + broadcastPacket(new MagicSkillUse(this, 5103, 1, 1000, 0)); + setClassTemplate(Id); + if (getClassId().level() == 3) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.THIRD_CLASS_TRANSFER)); + } + else + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CLASS_TRANSFER)); + } + + // Update class icon in party and clan + if (isInParty()) + { + getParty().broadcastToPartyMembers(new PartySmallWindowUpdate(this)); + } + + if (getClan() != null) + { + getClan().broadcastToOnlineMembers(new PledgeShowMemberListUpdate(this)); + } + + // Add AutoGet skills and normal skills and/or learnByFS depending on configurations. + rewardSkills(); + + if (!isGM() && Config.DECREASE_SKILL_LEVEL) + { + checkPlayerSkills(); + } + } + finally + { + _subclassLock.unlock(); + } + } + + /** + * Used for AltGameSkillLearn to set a custom skill learning class Id. + */ + private ClassId _learningClass = getClassId(); + + /** + * @return the custom skill learning class Id. + */ + public ClassId getLearningClass() + { + return _learningClass; + } + + /** + * @param learningClass the custom skill learning class Id to set. + */ + public void setLearningClass(ClassId learningClass) + { + _learningClass = learningClass; + } + + /** + * @return the Experience of the L2PcInstance. + */ + public long getExp() + { + return getStat().getExp(); + } + + public void setActiveEnchantAttrItem(L2ItemInstance stone) + { + _activeEnchantAttrItem = stone; + } + + public L2ItemInstance getActiveEnchantAttrItem() + { + return _activeEnchantAttrItem; + } + + public void setActiveEnchantItem(L2ItemInstance scroll) + { + // If we dont have a Enchant Item, we are not enchanting. + if (scroll == null) + { + setActiveEnchantSupportItem(null); + setActiveEnchantTimestamp(0); + setIsEnchanting(false); + } + _activeEnchantItem = scroll; + } + + public L2ItemInstance getActiveEnchantItem() + { + return _activeEnchantItem; + } + + public void setActiveEnchantSupportItem(L2ItemInstance item) + { + _activeEnchantSupportItem = item; + } + + public L2ItemInstance getActiveEnchantSupportItem() + { + return _activeEnchantSupportItem; + } + + public long getActiveEnchantTimestamp() + { + return _activeEnchantTimestamp; + } + + public void setActiveEnchantTimestamp(long val) + { + _activeEnchantTimestamp = val; + } + + public void setIsEnchanting(boolean val) + { + _isEnchanting = val; + } + + public boolean isEnchanting() + { + return _isEnchanting; + } + + /** + * Set the fists weapon of the L2PcInstance (used when no weapon is equiped).
    + *
    + * @param weaponItem The fists L2Weapon to set to the L2PcInstance + */ + public void setFistsWeaponItem(L2Weapon weaponItem) + { + _fistsWeaponItem = weaponItem; + } + + /** + * Return the fists weapon of the L2PcInstance (used when no weapon is equiped).
    + *
    + */ + public L2Weapon getFistsWeaponItem() + { + return _fistsWeaponItem; + } + + /** + * Return the fists weapon of the L2PcInstance Class (used when no weapon is equiped).
    + *
    + */ + public L2Weapon findFistsWeaponItem(int classId) + { + L2Weapon weaponItem = null; + if ((classId >= 0x00) && (classId <= 0x09)) + { + // human fighter fists + L2Item temp = ItemTable.getInstance().getTemplate(246); + weaponItem = (L2Weapon) temp; + } + else if ((classId >= 0x0a) && (classId <= 0x11)) + { + // human mage fists + L2Item temp = ItemTable.getInstance().getTemplate(251); + weaponItem = (L2Weapon) temp; + } + else if ((classId >= 0x12) && (classId <= 0x18)) + { + // elven fighter fists + L2Item temp = ItemTable.getInstance().getTemplate(244); + weaponItem = (L2Weapon) temp; + } + else if ((classId >= 0x19) && (classId <= 0x1e)) + { + // elven mage fists + L2Item temp = ItemTable.getInstance().getTemplate(249); + weaponItem = (L2Weapon) temp; + } + else if ((classId >= 0x1f) && (classId <= 0x25)) + { + // dark elven fighter fists + L2Item temp = ItemTable.getInstance().getTemplate(245); + weaponItem = (L2Weapon) temp; + } + else if ((classId >= 0x26) && (classId <= 0x2b)) + { + // dark elven mage fists + L2Item temp = ItemTable.getInstance().getTemplate(250); + weaponItem = (L2Weapon) temp; + } + else if ((classId >= 0x2c) && (classId <= 0x30)) + { + // orc fighter fists + L2Item temp = ItemTable.getInstance().getTemplate(248); + weaponItem = (L2Weapon) temp; + } + else if ((classId >= 0x31) && (classId <= 0x34)) + { + // orc mage fists + L2Item temp = ItemTable.getInstance().getTemplate(252); + weaponItem = (L2Weapon) temp; + } + else if ((classId >= 0x35) && (classId <= 0x39)) + { + // dwarven fists + L2Item temp = ItemTable.getInstance().getTemplate(247); + weaponItem = (L2Weapon) temp; + } + + return weaponItem; + } + + /** + * This method reward all AutoGet skills and Normal skills if Auto-Learn configuration is true.
    + */ + public void rewardSkills() + { + // Give all normal skills if activated Auto-Learn is activated, included AutoGet skills. + if (Config.AUTO_LEARN_SKILLS) + { + giveAvailableSkills(Config.AUTO_LEARN_FS_SKILLS, true); + } + else + { + giveAvailableAutoGetSkills(); + } + + checkItemRestriction(); + sendSkillList(); + } + + /** + * Re-give all skills which aren't saved to database, like Noble, Hero, Clan Skills.
    + */ + public void regiveTemporarySkills() + { + // Do not call this on enterworld or char load + + // Add noble skills if noble + if (isNoble()) + { + setNoble(true); + } + + // Add Hero skills if hero + if (isHero()) + { + setHero(true); + } + + // Add clan skills + if (getClan() != null) + { + L2Clan clan = getClan(); + clan.addSkillEffects(this); + + if ((clan.getLevel() >= SiegeManager.getInstance().getSiegeClanMinLevel()) && isClanLeader()) + { + SiegeManager.getInstance().addSiegeSkills(this); + } + if (getClan().getHasCastle() > 0) + { + CastleManager.getInstance().getCastleByOwner(getClan()).giveResidentialSkills(this); + } + if (getClan().getHasFort() > 0) + { + FortManager.getInstance().getFortByOwner(getClan()).giveResidentialSkills(this); + } + } + + // Reload passive skills from armors / jewels / weapons + getInventory().reloadEquippedItems(); + + // Add Death Penalty Buff Level + restoreDeathPenaltyBuffLevel(); + } + + /** + * Give all available skills to the player.
    + * @param includedByFs + * @param includeAutoGet + * @return skillCounter, the amount of new skills added. + */ + public int giveAvailableSkills(boolean includedByFs, boolean includeAutoGet) + { + int unLearnable = 0; + int skillCounter = 0; + + // Get available skills + FastList skills = SkillTreesData.getInstance().getAvailableSkills(this, getClassId(), includedByFs, includeAutoGet); + + while (skills.size() > unLearnable) + { + for (L2SkillLearn s : skills) + { + L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()); + if ((sk == null) || ((sk.getId() == L2Skill.SKILL_DIVINE_INSPIRATION) && !Config.AUTO_LEARN_DIVINE_INSPIRATION && !isGM())) + { + unLearnable++; + continue; + } + + if (getSkillLevel(sk.getId()) == -1) + { + skillCounter++; + } + + // fix when learning toggle skills + if (sk.isToggle()) + { + L2Effect toggleEffect = getFirstEffect(sk.getId()); + if (toggleEffect != null) + { + // stop old toggle skill effect, and give new toggle skill effect back + toggleEffect.exit(); + sk.getEffects(this, this); + } + } + addSkill(sk, true); + } + + // Get new available skills, some skills depend of previous skills to be available. + skills = SkillTreesData.getInstance().getAvailableSkills(this, getClassId(), includedByFs, includeAutoGet); + } + + sendMessage("You have learned " + skillCounter + " new skills."); + return skillCounter; + } + + /** + * Give all available AutoGet skills to the player.
    + */ + public void giveAvailableAutoGetSkills() + { + // Get available skills + final FastList autoGetSkills = SkillTreesData.getInstance().getAvailableAutoGetSkills(this); + + if ((autoGetSkills != null) && !autoGetSkills.isEmpty()) + { + for (L2SkillLearn s : autoGetSkills) + { + final L2Skill skill = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()); + if (skill != null) + { + addSkill(skill, true); + } + else + { + _log.warning("Skipped null autoGet Skill for player:" + getName() + "[" + getObjectId() + "]"); + } + } + } + } + + /** Set the Experience value of the L2PcInstance. */ + public void setExp(long exp) + { + if (exp < 0) + { + exp = 0; + } + + getStat().setExp(exp); + } + + /** + * Return the Race object of the L2PcInstance.
    + *
    + */ + public Race getRace() + { + if (!isSubClassActive()) + { + return getTemplate().race; + } + + L2PcTemplate charTemp = CharTemplateTable.getInstance().getTemplate(_baseClass); + return charTemp.race; + } + + public L2Radar getRadar() + { + return _radar; + } + + /* Return true if Hellbound minimap allowed */ + public boolean isMinimapAllowed() + { + return _minimapAllowed; + } + + /* Enable or disable minimap on Hellbound */ + public void setMinimapAllowed(boolean b) + { + _minimapAllowed = b; + } + + /** Return the SP amount of the L2PcInstance. */ + public int getSp() + { + return getStat().getSp(); + } + + /** Set the SP amount of the L2PcInstance. */ + public void setSp(int sp) + { + if (sp < 0) + { + sp = 0; + } + + super.getStat().setSp(sp); + } + + /** + * Return true if this L2PcInstance is a clan leader in ownership of the passed castle + */ + public boolean isCastleLord(int castleId) + { + L2Clan clan = getClan(); + + // player has clan and is the clan leader, check the castle info + if ((clan != null) && (clan.getLeader().getPlayerInstance() == this)) + { + // if the clan has a castle and it is actually the queried castle, return true + Castle castle = CastleManager.getInstance().getCastleByOwner(clan); + if ((castle != null) && (castle == CastleManager.getInstance().getCastleById(castleId))) + { + return true; + } + } + + return false; + } + + /** + * Return the Clan Identifier of the L2PcInstance.
    + *
    + */ + public int getClanId() + { + return _clanId; + } + + /** + * Return the Clan Crest Identifier of the L2PcInstance or 0.
    + *
    + */ + public int getClanCrestId() + { + if (_clan != null) + { + return _clan.getCrestId(); + } + + return 0; + } + + /** + * @return The Clan CrestLarge Identifier or 0 + */ + public int getClanCrestLargeId() + { + if (_clan != null) + { + return _clan.getCrestLargeId(); + } + + return 0; + } + + public long getClanJoinExpiryTime() + { + return _clanJoinExpiryTime; + } + + public void setClanJoinExpiryTime(long time) + { + _clanJoinExpiryTime = time; + } + + public long getClanCreateExpiryTime() + { + return _clanCreateExpiryTime; + } + + public void setClanCreateExpiryTime(long time) + { + _clanCreateExpiryTime = time; + } + + public void setOnlineTime(long time) + { + _onlineTime = time; + _onlineBeginTime = System.currentTimeMillis(); + } + + /** + * Return the PcInventory Inventory of the L2PcInstance contained in _inventory.
    + *
    + */ + @Override + public PcInventory getInventory() + { + return _inventory; + } + + /** + * Delete a ShortCut of the L2PcInstance _shortCuts.
    + *
    + */ + public void removeItemFromShortCut(int objectId) + { + _shortCuts.deleteShortCutByObjectId(objectId); + } + + /** + * Return True if the L2PcInstance is sitting.
    + *
    + */ + public boolean isSitting() + { + return _waitTypeSitting; + } + + /** + * Set _waitTypeSitting to given value + */ + public void setIsSitting(boolean state) + { + _waitTypeSitting = state; + } + + /** + * Sit down the L2PcInstance, set the AI Intention to AI_INTENTION_REST and send a Server->Client ChangeWaitType packet (broadcast)
    + *
    + */ + public void sitDown() + { + sitDown(true); + } + + public void sitDown(boolean checkCast) + { + if (checkCast && isCastingNow()) + { + sendMessage("Cannot sit while casting"); + return; + } + + if (!_waitTypeSitting && !isAttackingDisabled() && !isOutOfControl() && !isImmobilized()) + { + breakAttack(); + setIsSitting(true); + broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_SITTING)); + // Schedule a sit down task to wait for the animation to finish + ThreadPoolManager.getInstance().scheduleGeneral(new SitDownTask(), 2500); + setIsParalyzed(true); + } + } + + /** + * Sit down Task + */ + protected class SitDownTask implements Runnable + { + @Override + public void run() + { + setIsParalyzed(false); + getAI().setIntention(CtrlIntention.AI_INTENTION_REST); + } + } + + /** + * Stand up Task + */ + protected class StandUpTask implements Runnable + { + @Override + public void run() + { + setIsSitting(false); + getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); + } + } + + /** + * Stand up the L2PcInstance, set the AI Intention to AI_INTENTION_IDLE and send a Server->Client ChangeWaitType packet (broadcast)
    + *
    + */ + public void standUp() + { + if (L2Event.active && eventSitForced) + { + sendMessage("A dark force beyond your mortal understanding makes your knees to shake when you try to stand up ..."); + } + else if (_waitTypeSitting && !isInStoreMode() && !isAlikeDead()) + { + if (_effects.isAffected(CharEffectList.EFFECT_FLAG_RELAXING)) + { + stopEffects(L2EffectType.RELAXING); + } + + broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_STANDING)); + // Schedule a stand up task to wait for the animation to finish + ThreadPoolManager.getInstance().scheduleGeneral(new StandUpTask(), 2500); + } + } + + /** + * Return the PcWarehouse object of the L2PcInstance.
    + *
    + */ + public PcWarehouse getWarehouse() + { + if (_warehouse == null) + { + _warehouse = new PcWarehouse(this); + _warehouse.restore(); + } + if (Config.WAREHOUSE_CACHE) + { + WarehouseCacheManager.getInstance().addCacheTask(this); + } + return _warehouse; + } + + /** + * Free memory used by Warehouse + */ + public void clearWarehouse() + { + if (_warehouse != null) + { + _warehouse.deleteMe(); + } + _warehouse = null; + } + + /** + * Returns true if refund list is not empty + */ + public boolean hasRefund() + { + return (_refund != null) && (_refund.getSize() > 0) && Config.ALLOW_REFUND; + } + + /** + * Returns refund object or create new if not exist + */ + public PcRefund getRefund() + { + if (_refund == null) + { + _refund = new PcRefund(this); + } + return _refund; + } + + /** + * Clear refund + */ + public void clearRefund() + { + if (_refund != null) + { + _refund.deleteMe(); + } + _refund = null; + } + + /** + * Return the Identifier of the L2PcInstance.
    + *
    + */ + @Deprecated + public int getCharId() + { + return _charId; + } + + /** + * Set the Identifier of the L2PcInstance.
    + *
    + */ + public void setCharId(int charId) + { + _charId = charId; + } + + /** + * Return the Adena amount of the L2PcInstance.
    + *
    + */ + public long getAdena() + { + return _inventory.getAdena(); + } + + /** + * Return the Ancient Adena amount of the L2PcInstance.
    + *
    + */ + public long getAncientAdena() + { + return _inventory.getAncientAdena(); + } + + /** + * Add adena to Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param count : int Quantity of adena to be added + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + */ + public void addAdena(String process, long count, L2Object reference, boolean sendMessage) + { + if (sendMessage) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S1_ADENA); + sm.addItemNumber(count); + sendPacket(sm); + } + + if (count > 0) + { + _inventory.addAdena(process, count, this, reference); + + // Send update packet + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate iu = new InventoryUpdate(); + iu.addItem(_inventory.getAdenaInstance()); + sendPacket(iu); + } + else + { + sendPacket(new ItemList(this, false)); + } + } + } + + /** + * Reduce adena in Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param count : long Quantity of adena to be reduced + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @return boolean informing if the action was successfull + */ + public boolean reduceAdena(String process, long count, L2Object reference, boolean sendMessage) + { + if (count > getAdena()) + { + if (sendMessage) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA)); + } + return false; + } + + if (count > 0) + { + L2ItemInstance adenaItem = _inventory.getAdenaInstance(); + if (!_inventory.reduceAdena(process, count, this, reference)) + { + return false; + } + + // Send update packet + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate iu = new InventoryUpdate(); + iu.addItem(adenaItem); + sendPacket(iu); + } + else + { + sendPacket(new ItemList(this, false)); + } + + if (sendMessage) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED_ADENA); + sm.addItemNumber(count); + sendPacket(sm); + } + } + + return true; + } + + /** + * Add ancient adena to Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param count : int Quantity of ancient adena to be added + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + */ + public void addAncientAdena(String process, long count, L2Object reference, boolean sendMessage) + { + if (sendMessage) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S); + sm.addItemName(PcInventory.ANCIENT_ADENA_ID); + sm.addItemNumber(count); + sendPacket(sm); + } + + if (count > 0) + { + _inventory.addAncientAdena(process, count, this, reference); + + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate iu = new InventoryUpdate(); + iu.addItem(_inventory.getAncientAdenaInstance()); + sendPacket(iu); + } + else + { + sendPacket(new ItemList(this, false)); + } + } + } + + /** + * Reduce ancient adena in Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param count : long Quantity of ancient adena to be reduced + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @return boolean informing if the action was successfull + */ + public boolean reduceAncientAdena(String process, long count, L2Object reference, boolean sendMessage) + { + if (count > getAncientAdena()) + { + if (sendMessage) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA)); + } + + return false; + } + + if (count > 0) + { + L2ItemInstance ancientAdenaItem = _inventory.getAncientAdenaInstance(); + if (!_inventory.reduceAncientAdena(process, count, this, reference)) + { + return false; + } + + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate iu = new InventoryUpdate(); + iu.addItem(ancientAdenaItem); + sendPacket(iu); + } + else + { + sendPacket(new ItemList(this, false)); + } + + if (sendMessage) + { + if (count > 1) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + sm.addItemName(PcInventory.ANCIENT_ADENA_ID); + sm.addItemNumber(count); + sendPacket(sm); + } + else + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); + sm.addItemName(PcInventory.ANCIENT_ADENA_ID); + sendPacket(sm); + } + } + } + + return true; + } + + /** + * Adds item to inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param item : L2ItemInstance to be added + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + */ + public void addItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage) + { + if (item.getCount() > 0) + { + // Sends message to client if requested + if (sendMessage) + { + if (item.getCount() > 1) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_PICKED_UP_S1_S2); + sm.addItemName(item); + sm.addItemNumber(item.getCount()); + sendPacket(sm); + } + else if (item.getEnchantLevel() > 0) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_PICKED_UP_A_S1_S2); + sm.addNumber(item.getEnchantLevel()); + sm.addItemName(item); + sendPacket(sm); + } + else + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_PICKED_UP_S1); + sm.addItemName(item); + sendPacket(sm); + } + } + + // Add the item to inventory + L2ItemInstance newitem = _inventory.addItem(process, item, this, reference); + + // Send inventory update packet + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate playerIU = new InventoryUpdate(); + playerIU.addItem(newitem); + sendPacket(playerIU); + } + else + { + sendPacket(new ItemList(this, false)); + } + + // Update current load as well + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad()); + sendPacket(su); + + // If over capacity, drop the item + if (!isGM() && !_inventory.validateCapacity(0, item.isQuestItem()) && newitem.isDropable() && (!newitem.isStackable() || (newitem.getLastChange() != L2ItemInstance.MODIFIED))) + { + dropItem("InvDrop", newitem, null, true, true); + } + else if (CursedWeaponsManager.getInstance().isCursed(newitem.getItemId())) + { + CursedWeaponsManager.getInstance().activate(this, newitem); + } + + // Combat Flag + else if (FortSiegeManager.getInstance().isCombat(item.getItemId())) + { + if (FortSiegeManager.getInstance().activateCombatFlag(this, item)) + { + Fort fort = FortManager.getInstance().getFort(this); + fort.getSiege().announceToPlayer(SystemMessage.getSystemMessage(SystemMessageId.C1_ACQUIRED_THE_FLAG), getName()); + } + } + // Territory Ward + else if ((item.getItemId() >= 13560) && (item.getItemId() <= 13568)) + { + TerritoryWard ward = TerritoryWarManager.getInstance().getTerritoryWard(item.getItemId() - 13479); + if (ward != null) + { + ward.activate(this, item); + } + } + } + } + + /** + * Adds item to Inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param itemId : int Item Identifier of the item to be added + * @param count : long Quantity of items to be added + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + */ + public L2ItemInstance addItem(String process, int itemId, long count, L2Object reference, boolean sendMessage) + { + if (count > 0) + { + L2ItemInstance item = null; + if (ItemTable.getInstance().getTemplate(itemId) != null) + { + item = ItemTable.getInstance().createDummyItem(itemId); + } + else + { + _log.log(Level.SEVERE, "Item doesn't exist so cannot be added. Item ID: " + itemId); + return null; + } + // Sends message to client if requested + if (sendMessage && ((!isCastingNow() && (item.getItemType() == L2EtcItemType.HERB)) || (item.getItemType() != L2EtcItemType.HERB))) + { + if (count > 1) + { + if (process.equalsIgnoreCase("sweep") || process.equalsIgnoreCase("Quest")) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S); + sm.addItemName(itemId); + sm.addItemNumber(count); + sendPacket(sm); + } + else + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_PICKED_UP_S1_S2); + sm.addItemName(itemId); + sm.addItemNumber(count); + sendPacket(sm); + } + } + else + { + if (process.equalsIgnoreCase("sweep") || process.equalsIgnoreCase("Quest")) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1); + sm.addItemName(itemId); + sendPacket(sm); + } + else + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_PICKED_UP_S1); + sm.addItemName(itemId); + sendPacket(sm); + } + } + } + // Auto use herbs - autoloot + if (item.getItemType() == L2EtcItemType.HERB) // If item is herb dont add it to iv :] + { + if (!isCastingNow()) + { + L2ItemInstance herb = new L2ItemInstance(_charId, itemId); + IItemHandler handler = ItemHandler.getInstance().getItemHandler(herb.getEtcItem()); + if (handler == null) + { + _log.warning("No item handler registered for Herb - item ID " + herb.getItemId() + "."); + } + else + { + handler.useItem(this, herb, false); + if (_herbstask >= 100) + { + _herbstask -= 100; + } + } + } + else + { + _herbstask += 100; + ThreadPoolManager.getInstance().scheduleAi(new HerbTask(process, itemId, count, reference, sendMessage), _herbstask); + } + } + else + { + // Add the item to inventory + L2ItemInstance createdItem = _inventory.addItem(process, itemId, count, this, reference); + + // If over capacity, drop the item + if (!isGM() && !_inventory.validateCapacity(0, item.isQuestItem()) && createdItem.isDropable() && (!createdItem.isStackable() || (createdItem.getLastChange() != L2ItemInstance.MODIFIED))) + { + dropItem("InvDrop", createdItem, null, true); + } + else if (CursedWeaponsManager.getInstance().isCursed(createdItem.getItemId())) + { + CursedWeaponsManager.getInstance().activate(this, createdItem); + } + else if (FortSiegeManager.getInstance().isCombat(createdItem.getItemId())) + { + if (FortSiegeManager.getInstance().activateCombatFlag(this, item)) + { + Fort fort = FortManager.getInstance().getFort(this); + fort.getSiege().announceToPlayer(SystemMessage.getSystemMessage(SystemMessageId.C1_ACQUIRED_THE_FLAG), getName()); + } + } + // Territory Ward + else if ((createdItem.getItemId() >= 13560) && (createdItem.getItemId() <= 13568)) + { + TerritoryWard ward = TerritoryWarManager.getInstance().getTerritoryWard(createdItem.getItemId() - 13479); + if (ward != null) + { + ward.activate(this, createdItem); + } + } + return createdItem; + } + } + return null; + } + + /** + * Destroy item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param item : L2ItemInstance to be destroyed + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @return boolean informing if the action was successfull + */ + public boolean destroyItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage) + { + return this.destroyItem(process, item, item.getCount(), reference, sendMessage); + } + + /** + * Destroy item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param item : L2ItemInstance to be destroyed + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @return boolean informing if the action was successfull + */ + public boolean destroyItem(String process, L2ItemInstance item, long count, L2Object reference, boolean sendMessage) + { + item = _inventory.destroyItem(process, item, count, this, reference); + + if (item == null) + { + if (sendMessage) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS)); + } + return false; + } + + // Send inventory update packet + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate playerIU = new InventoryUpdate(); + playerIU.addItem(item); + sendPacket(playerIU); + } + else + { + sendPacket(new ItemList(this, false)); + } + + // Update current load as well + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad()); + sendPacket(su); + + // Sends message to client if requested + if (sendMessage) + { + if (count > 1) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + sm.addItemName(item); + sm.addItemNumber(count); + sendPacket(sm); + } + else + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); + sm.addItemName(item); + sendPacket(sm); + } + } + + return true; + } + + /** + * Destroys item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param objectId : int Item Instance identifier of the item to be destroyed + * @param count : int Quantity of items to be destroyed + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @return boolean informing if the action was successfull + */ + @Override + public boolean destroyItem(String process, int objectId, long count, L2Object reference, boolean sendMessage) + { + L2ItemInstance item = _inventory.getItemByObjectId(objectId); + + if (item == null) + { + if (sendMessage) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS)); + } + + return false; + } + return this.destroyItem(process, item, count, reference, sendMessage); + } + + /** + * Destroys shots from inventory without logging and only occasional saving to database. Sends a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param objectId : int Item Instance identifier of the item to be destroyed + * @param count : int Quantity of items to be destroyed + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @return boolean informing if the action was successfull + */ + public boolean destroyItemWithoutTrace(String process, int objectId, long count, L2Object reference, boolean sendMessage) + { + L2ItemInstance item = _inventory.getItemByObjectId(objectId); + + if ((item == null) || (item.getCount() < count)) + { + if (sendMessage) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS)); + } + + return false; + } + + return this.destroyItem(null, item, count, reference, sendMessage); + } + + /** + * Destroy item from inventory by using its itemId and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param itemId : int Item identifier of the item to be destroyed + * @param count : int Quantity of items to be destroyed + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @return boolean informing if the action was successfull + */ + @Override + public boolean destroyItemByItemId(String process, int itemId, long count, L2Object reference, boolean sendMessage) + { + if (itemId == 57) + { + return reduceAdena(process, count, reference, sendMessage); + } + + L2ItemInstance item = _inventory.getItemByItemId(itemId); + + if ((item == null) || (item.getCount() < count) || (_inventory.destroyItemByItemId(process, itemId, count, this, reference) == null)) + { + if (sendMessage) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS)); + } + + return false; + } + + // Send inventory update packet + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate playerIU = new InventoryUpdate(); + playerIU.addItem(item); + sendPacket(playerIU); + } + else + { + sendPacket(new ItemList(this, false)); + } + + // Update current load as well + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad()); + sendPacket(su); + + // Sends message to client if requested + if (sendMessage) + { + if (count > 1) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + sm.addItemName(itemId); + sm.addItemNumber(count); + sendPacket(sm); + } + else + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); + sm.addItemName(itemId); + sendPacket(sm); + } + } + + return true; + } + + /** + * Transfers item to another ItemContainer and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param itemId : int Item Identifier of the item to be transfered + * @param count : long Quantity of items to be transfered + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @return L2ItemInstance corresponding to the new item or the updated item in inventory + */ + public L2ItemInstance transferItem(String process, int objectId, long count, Inventory target, L2Object reference) + { + L2ItemInstance oldItem = checkItemManipulation(objectId, count, "transfer"); + if (oldItem == null) + { + return null; + } + L2ItemInstance newItem = getInventory().transferItem(process, objectId, count, target, this, reference); + if (newItem == null) + { + return null; + } + + // Send inventory update packet + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate playerIU = new InventoryUpdate(); + + if ((oldItem.getCount() > 0) && (oldItem != newItem)) + { + playerIU.addModifiedItem(oldItem); + } + else + { + playerIU.addRemovedItem(oldItem); + } + + sendPacket(playerIU); + } + else + { + sendPacket(new ItemList(this, false)); + } + + // Update current load as well + StatusUpdate playerSU = new StatusUpdate(this); + playerSU.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad()); + sendPacket(playerSU); + + // Send target update packet + if (target instanceof PcInventory) + { + L2PcInstance targetPlayer = ((PcInventory) target).getOwner(); + + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate playerIU = new InventoryUpdate(); + + if (newItem.getCount() > count) + { + playerIU.addModifiedItem(newItem); + } + else + { + playerIU.addNewItem(newItem); + } + + targetPlayer.sendPacket(playerIU); + } + else + { + targetPlayer.sendPacket(new ItemList(targetPlayer, false)); + } + + // Update current load as well + playerSU = new StatusUpdate(targetPlayer); + playerSU.addAttribute(StatusUpdate.CUR_LOAD, targetPlayer.getCurrentLoad()); + targetPlayer.sendPacket(playerSU); + } + else if (target instanceof PetInventory) + { + PetInventoryUpdate petIU = new PetInventoryUpdate(); + + if (newItem.getCount() > count) + { + petIU.addModifiedItem(newItem); + } + else + { + petIU.addNewItem(newItem); + } + + ((PetInventory) target).getOwner().getOwner().sendPacket(petIU); + } + return newItem; + } + + /** + * Drop item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param item : L2ItemInstance to be dropped + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @param protectItem: whether or not dropped item must be protected temporary against other players + * @return boolean informing if the action was successfull + */ + public boolean dropItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage, boolean protectItem) + { + item = _inventory.dropItem(process, item, this, reference); + + if (item == null) + { + if (sendMessage) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS)); + } + + return false; + } + + item.dropMe(this, (getX() + Rnd.get(50)) - 25, (getY() + Rnd.get(50)) - 25, getZ() + 20); + + if ((Config.AUTODESTROY_ITEM_AFTER > 0) && Config.DESTROY_DROPPED_PLAYER_ITEM && !Config.LIST_PROTECTED_ITEMS.contains(item.getItemId())) + { + if ((item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM) || !item.isEquipable()) + { + ItemsAutoDestroy.getInstance().addItem(item); + } + } + + // protection against auto destroy dropped item + if (Config.DESTROY_DROPPED_PLAYER_ITEM) + { + if (!item.isEquipable() || (item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM)) + { + item.setProtected(false); + } + else + { + item.setProtected(true); + } + } + else + { + item.setProtected(true); + } + + // retail drop protection + if (protectItem) + { + item.getDropProtection().protect(this); + } + + // Send inventory update packet + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate playerIU = new InventoryUpdate(); + playerIU.addItem(item); + sendPacket(playerIU); + } + else + { + sendPacket(new ItemList(this, false)); + } + + // Update current load as well + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad()); + sendPacket(su); + + // Sends message to client if requested + if (sendMessage) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_DROPPED_S1); + sm.addItemName(item); + sendPacket(sm); + } + + return true; + } + + public boolean dropItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage) + { + return dropItem(process, item, reference, sendMessage, false); + } + + /** + * Drop item from inventory by using its objectID and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param objectId : int Item Instance identifier of the item to be dropped + * @param count : long Quantity of items to be dropped + * @param x : int coordinate for drop X + * @param y : int coordinate for drop Y + * @param z : int coordinate for drop Z + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @return L2ItemInstance corresponding to the new item or the updated item in inventory + */ + public L2ItemInstance dropItem(String process, int objectId, long count, int x, int y, int z, L2Object reference, boolean sendMessage, boolean protectItem) + { + L2ItemInstance invitem = _inventory.getItemByObjectId(objectId); + L2ItemInstance item = _inventory.dropItem(process, objectId, count, this, reference); + + if (item == null) + { + if (sendMessage) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS)); + } + + return null; + } + + item.dropMe(this, x, y, z); + + if ((Config.AUTODESTROY_ITEM_AFTER > 0) && Config.DESTROY_DROPPED_PLAYER_ITEM && !Config.LIST_PROTECTED_ITEMS.contains(item.getItemId())) + { + if ((item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM) || !item.isEquipable()) + { + ItemsAutoDestroy.getInstance().addItem(item); + } + } + if (Config.DESTROY_DROPPED_PLAYER_ITEM) + { + if (!item.isEquipable() || (item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM)) + { + item.setProtected(false); + } + else + { + item.setProtected(true); + } + } + else + { + item.setProtected(true); + } + + // retail drop protection + if (protectItem) + { + item.getDropProtection().protect(this); + } + + // Send inventory update packet + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate playerIU = new InventoryUpdate(); + playerIU.addItem(invitem); + sendPacket(playerIU); + } + else + { + sendPacket(new ItemList(this, false)); + } + + // Update current load as well + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad()); + sendPacket(su); + + // Sends message to client if requested + if (sendMessage) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_DROPPED_S1); + sm.addItemName(item); + sendPacket(sm); + } + + return item; + } + + public L2ItemInstance checkItemManipulation(int objectId, long count, String action) + { + // TODO: if we remove objects that are not visisble from the L2World, we'll have to remove this check + if (L2World.getInstance().findObject(objectId) == null) + { + _log.finest(getObjectId() + ": player tried to " + action + " item not available in L2World"); + return null; + } + + L2ItemInstance item = getInventory().getItemByObjectId(objectId); + + if ((item == null) || (item.getOwnerId() != getObjectId())) + { + _log.finest(getObjectId() + ": player tried to " + action + " item he is not owner of"); + return null; + } + + if ((count < 0) || ((count > 1) && !item.isStackable())) + { + _log.finest(getObjectId() + ": player tried to " + action + " item with invalid count: " + count); + return null; + } + + if (count > item.getCount()) + { + _log.finest(getObjectId() + ": player tried to " + action + " more items than he owns"); + return null; + } + + // Pet is summoned and not the item that summoned the pet AND not the buggle from strider you're mounting + if (((getPet() != null) && (getPet().getControlObjectId() == objectId)) || (getMountObjectID() == objectId)) + { + if (Config.DEBUG) + { + _log.finest(getObjectId() + ": player tried to " + action + " item controling pet"); + } + + return null; + } + + if ((getActiveEnchantItem() != null) && (getActiveEnchantItem().getObjectId() == objectId)) + { + if (Config.DEBUG) + { + _log.finest(getObjectId() + ":player tried to " + action + " an enchant scroll he was using"); + } + + return null; + } + + // We cannot put a Weapon with Augmention in WH while casting (Possible Exploit) + if (item.isAugmented() && (isCastingNow() || isCastingSimultaneouslyNow())) + { + return null; + } + + return item; + } + + /** + * Set _protectEndTime according settings. + */ + public void setProtection(boolean protect) + { + if (Config.DEVELOPER && (protect || (_protectEndTime > 0))) + { + _log.warning(getName() + ": Protection " + (protect ? "ON " + (GameTimeController.getGameTicks() + (Config.PLAYER_SPAWN_PROTECTION * GameTimeController.TICKS_PER_SECOND)) : "OFF") + " (currently " + GameTimeController.getGameTicks() + ")"); + } + + _protectEndTime = protect ? GameTimeController.getGameTicks() + (Config.PLAYER_SPAWN_PROTECTION * GameTimeController.TICKS_PER_SECOND) : 0; + } + + public void setTeleportProtection(boolean protect) + { + if (Config.DEVELOPER && (protect || (_teleportProtectEndTime > 0))) + { + _log.warning(getName() + ": Tele Protection " + (protect ? "ON " + (GameTimeController.getGameTicks() + (Config.PLAYER_TELEPORT_PROTECTION * GameTimeController.TICKS_PER_SECOND)) : "OFF") + " (currently " + GameTimeController.getGameTicks() + ")"); + } + + _teleportProtectEndTime = protect ? GameTimeController.getGameTicks() + (Config.PLAYER_TELEPORT_PROTECTION * GameTimeController.TICKS_PER_SECOND) : 0; + } + + /** + * Set protection from agro mobs when getting up from fake death, according settings. + */ + public void setRecentFakeDeath(boolean protect) + { + _recentFakeDeathEndTime = protect ? GameTimeController.getGameTicks() + (Config.PLAYER_FAKEDEATH_UP_PROTECTION * GameTimeController.TICKS_PER_SECOND) : 0; + } + + public boolean isRecentFakeDeath() + { + return _recentFakeDeathEndTime > GameTimeController.getGameTicks(); + } + + public final boolean isFakeDeath() + { + return _isFakeDeath; + } + + public final void setIsFakeDeath(boolean value) + { + _isFakeDeath = value; + } + + @Override + public final boolean isAlikeDead() + { + if (super.isAlikeDead()) + { + return true; + } + + return isFakeDeath(); + } + + /** + * Get the client owner of this char.
    + *
    + */ + public L2GameClient getClient() + { + return _client; + } + + public void setClient(L2GameClient client) + { + _client = client; + } + + /** + * Close the active connection with the client.
    + *
    + */ + private void closeNetConnection(boolean closeClient) + { + L2GameClient client = _client; + if (client != null) + { + if (client.isDetached()) + { + client.cleanMe(true); + } + else + { + if (!client.getConnection().isClosed()) + { + if (closeClient) + { + client.close(LeaveWorld.STATIC_PACKET); + } + else + { + client.close(ServerClose.STATIC_PACKET); + } + } + } + } + } + + public Point3D getCurrentSkillWorldPosition() + { + return _currentSkillWorldPosition; + } + + public void setCurrentSkillWorldPosition(Point3D worldPosition) + { + _currentSkillWorldPosition = worldPosition; + } + + /** + * @see com.l2jserver.gameserver.model.actor.L2Character#enableSkill(com.l2jserver.gameserver.model.L2Skill) + */ + @Override + public void enableSkill(L2Skill skill) + { + super.enableSkill(skill); + _reuseTimeStamps.remove(skill.getReuseHashCode()); + } + + /** + * @see com.l2jserver.gameserver.model.actor.L2Character#checkDoCastConditions(com.l2jserver.gameserver.model.L2Skill) + */ + @Override + protected boolean checkDoCastConditions(L2Skill skill) + { + if (!super.checkDoCastConditions(skill)) + { + return false; + } + + switch (skill.getSkillType()) + { + case SUMMON_TRAP: + { + if (isInsideZone(ZONE_PEACE)) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.A_MALICIOUS_SKILL_CANNOT_BE_USED_IN_PEACE_ZONE)); + return false; + } + if ((getTrap() != null) && (getTrap().getSkill().getId() == ((L2SkillTrap) skill).getTriggerSkillId())) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED); + sm.addSkillName(skill); + sendPacket(sm); + return false; + } + break; + } + case SUMMON: + { + if (!((L2SkillSummon) skill).isCubic() && ((getPet() != null) || isMounted())) + { + if (Config.DEBUG) + { + _log.fine("player has a pet already. ignore summon skill"); + } + + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ALREADY_HAVE_A_PET)); + return false; + } + } + } + + // TODO: Should possibly be checked only in L2PcInstance's useMagic + // Can't use Hero and resurrect skills during Olympiad + if (isInOlympiadMode() && (skill.isHeroSkill() || (skill.getSkillType() == L2SkillType.RESURRECT))) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THIS_SKILL_IS_NOT_AVAILABLE_FOR_THE_OLYMPIAD_EVENT); + sendPacket(sm); + return false; + } + + final int charges = getCharges(); + // Check if the spell using charges or not in AirShip + if (((skill.getMaxCharges() == 0) && (charges < skill.getNumCharges())) || (isInAirShip() && (skill.getSkillType() != L2SkillType.REFUEL))) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED); + sm.addSkillName(skill); + sendPacket(sm); + return false; + } + + return true; + } + + /** + * Returns true if cp update should be done, false if not + * @return boolean + */ + private boolean needCpUpdate(int barPixels) + { + double currentCp = getCurrentCp(); + + if ((currentCp <= 1.0) || (getMaxCp() < barPixels)) + { + return true; + } + + if ((currentCp <= _cpUpdateDecCheck) || (currentCp >= _cpUpdateIncCheck)) + { + if (currentCp == getMaxCp()) + { + _cpUpdateIncCheck = currentCp + 1; + _cpUpdateDecCheck = currentCp - _cpUpdateInterval; + } + else + { + double doubleMulti = currentCp / _cpUpdateInterval; + int intMulti = (int) doubleMulti; + + _cpUpdateDecCheck = _cpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti); + _cpUpdateIncCheck = _cpUpdateDecCheck + _cpUpdateInterval; + } + + return true; + } + + return false; + } + + /** + * Returns true if mp update should be done, false if not + * @return boolean + */ + private boolean needMpUpdate(int barPixels) + { + double currentMp = getCurrentMp(); + + if ((currentMp <= 1.0) || (getMaxMp() < barPixels)) + { + return true; + } + + if ((currentMp <= _mpUpdateDecCheck) || (currentMp >= _mpUpdateIncCheck)) + { + if (currentMp == getMaxMp()) + { + _mpUpdateIncCheck = currentMp + 1; + _mpUpdateDecCheck = currentMp - _mpUpdateInterval; + } + else + { + double doubleMulti = currentMp / _mpUpdateInterval; + int intMulti = (int) doubleMulti; + + _mpUpdateDecCheck = _mpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti); + _mpUpdateIncCheck = _mpUpdateDecCheck + _mpUpdateInterval; + } + + return true; + } + + return false; + } + + /** + * Send packet StatusUpdate with current HP,MP and CP to the L2PcInstance and only current HP, MP and Level to all other L2PcInstance of the Party.
    + *
    + * Actions :
    + *
    + *
  • Send the Server->Client packet StatusUpdate with current HP, MP and CP to this L2PcInstance

  • + *
  • Send the Server->Client packet PartySmallWindowUpdate with current HP, MP and Level to all other L2PcInstance of the Party

  • + *
    + * Caution : This method DOESN'T SEND current HP and MP to all L2PcInstance of the _statusListener
    + *
    + */ + @Override + public void broadcastStatusUpdate() + { + // TODO We mustn't send these informations to other players + // Send the Server->Client packet StatusUpdate with current HP and MP to all L2PcInstance that must be informed of HP/MP updates of this L2PcInstance + // super.broadcastStatusUpdate(); + + // Send the Server->Client packet StatusUpdate with current HP, MP and CP to this L2PcInstance + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp()); + su.addAttribute(StatusUpdate.CUR_MP, (int) getCurrentMp()); + su.addAttribute(StatusUpdate.CUR_CP, (int) getCurrentCp()); + su.addAttribute(StatusUpdate.MAX_CP, getMaxCp()); + sendPacket(su); + + final boolean needCpUpdate = needCpUpdate(352); + final boolean needHpUpdate = needHpUpdate(352); + // Check if a party is in progress and party window update is usefull + L2Party party = _party; + if ((party != null) && (needCpUpdate || needHpUpdate || needMpUpdate(352))) + { + if (Config.DEBUG) + { + _log.fine("Send status for party window of " + getObjectId() + "(" + getName() + ") to his party. CP: " + getCurrentCp() + " HP: " + getCurrentHp() + " MP: " + getCurrentMp()); + } + // Send the Server->Client packet PartySmallWindowUpdate with current HP, MP and Level to all other L2PcInstance of the Party + PartySmallWindowUpdate update = new PartySmallWindowUpdate(this); + party.broadcastToPartyMembers(this, update); + } + + if (isInOlympiadMode() && isOlympiadStart() && (needCpUpdate || needHpUpdate)) + { + final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(getOlympiadGameId()); + if ((game != null) && game.isBattleStarted()) + { + game.getZone().broadcastStatusUpdate(this); + } + } + + // In duel MP updated only with CP or HP + if (isInDuel() && (needCpUpdate || needHpUpdate)) + { + ExDuelUpdateUserInfo update = new ExDuelUpdateUserInfo(this); + DuelManager.getInstance().broadcastToOppositTeam(this, update); + } + } + + /** + * Send a Server->Client packet UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers.
    + *
    + * Concept :
    + *
    + * Others L2PcInstance in the detection area of the L2PcInstance are identified in _knownPlayers. In order to inform other players of this L2PcInstance state modifications, server just need to go through _knownPlayers to send Server->Client Packet
    + *
    + * Actions :
    + *
    + *
  • Send a Server->Client packet UserInfo to this L2PcInstance (Public and Private Data)
  • Send a Server->Client packet CharInfo to all L2PcInstance in _KnownPlayers of the L2PcInstance (Public data only)

  • + *
    + * Caution : DON'T SEND UserInfo packet to other players instead of CharInfo packet. Indeed, UserInfo packet contains PRIVATE DATA as MaxHP, STR, DEX...
    + *
    + */ + public final void broadcastUserInfo() + { + // Send a Server->Client packet UserInfo to this L2PcInstance + sendPacket(new UserInfo(this)); + + // Send a Server->Client packet CharInfo to all L2PcInstance in _KnownPlayers of the L2PcInstance + if (Config.DEBUG) + { + _log.fine("players to notify:" + getKnownList().getKnownPlayers().size() + " packet: [S] 03 CharInfo"); + } + + broadcastPacket(new CharInfo(this)); + broadcastPacket(new ExBrExtraUserInfo(this)); + if (TerritoryWarManager.getInstance().isTWInProgress() && (TerritoryWarManager.getInstance().checkIsRegistered(-1, getObjectId()) || TerritoryWarManager.getInstance().checkIsRegistered(-1, getClan()))) + { + broadcastPacket(new ExDominionWarStart(this)); + } + } + + public final void broadcastTitleInfo() + { + // Send a Server->Client packet UserInfo to this L2PcInstance + sendPacket(new UserInfo(this)); + sendPacket(new ExBrExtraUserInfo(this)); + + // Send a Server->Client packet TitleUpdate to all L2PcInstance in _KnownPlayers of the L2PcInstance + if (Config.DEBUG) + { + _log.fine("players to notify:" + getKnownList().getKnownPlayers().size() + " packet: [S] cc TitleUpdate"); + } + + broadcastPacket(new NicknameChanged(this)); + } + + @Override + public final void broadcastPacket(L2GameServerPacket mov) + { + if (!(mov instanceof CharInfo)) + { + sendPacket(mov); + } + + mov.setInvisible(getAppearance().getInvisible()); + + Collection plrs = getKnownList().getKnownPlayers().values(); + // synchronized (getKnownList().getKnownPlayers()) + { + for (L2PcInstance player : plrs) + { + if (player == null) + { + continue; + } + player.sendPacket(mov); + if (mov instanceof CharInfo) + { + int relation = getRelation(player); + Integer oldrelation = getKnownList().getKnownRelations().get(player.getObjectId()); + if ((oldrelation != null) && (oldrelation != relation)) + { + player.sendPacket(new RelationChanged(this, relation, isAutoAttackable(player))); + if (getPet() != null) + { + player.sendPacket(new RelationChanged(getPet(), relation, isAutoAttackable(player))); + } + } + } + } + } + } + + @Override + public void broadcastPacket(L2GameServerPacket mov, int radiusInKnownlist) + { + if (!(mov instanceof CharInfo)) + { + sendPacket(mov); + } + + mov.setInvisible(getAppearance().getInvisible()); + + Collection plrs = getKnownList().getKnownPlayers().values(); + // synchronized (getKnownList().getKnownPlayers()) + { + for (L2PcInstance player : plrs) + { + if (player == null) + { + continue; + } + if (isInsideRadius(player, radiusInKnownlist, false, false)) + { + player.sendPacket(mov); + if (mov instanceof CharInfo) + { + int relation = getRelation(player); + Integer oldrelation = getKnownList().getKnownRelations().get(player.getObjectId()); + if ((oldrelation != null) && (oldrelation != relation)) + { + player.sendPacket(new RelationChanged(this, relation, isAutoAttackable(player))); + if (getPet() != null) + { + player.sendPacket(new RelationChanged(getPet(), relation, isAutoAttackable(player))); + } + } + } + } + } + } + } + + /** + * Return the Alliance Identifier of the L2PcInstance.
    + *
    + */ + public int getAllyId() + { + return _clan == null ? 0 : _clan.getAllyId(); + } + + public int getAllyCrestId() + { + if (getClanId() == 0) + { + return 0; + } + if (getClan().getAllyId() == 0) + { + return 0; + } + return getClan().getAllyCrestId(); + } + + /** + * Manage hit process (called by Hit Task of L2Character).
    + *
    + * Actions :
    + *
    + *
  • If the attacker/target is dead or use fake death, notify the AI with EVT_CANCEL and send a Server->Client packet ActionFailed (if attacker is a L2PcInstance)
  • If attack isn't aborted, send a message system (critical hit, missed...) to attacker/target if they are L2PcInstance
  • + *
  • If attack isn't aborted and hit isn't missed, reduce HP of the target and calculate reflection damage to reduce HP of attacker if necessary
  • if attack isn't aborted and hit isn't missed, manage attack or cast break of the target (calculating rate, sending message...)

  • + *
    + * @param target The L2Character targeted + * @param damage Nb of HP to reduce + * @param crit True if hit is critical + * @param miss True if hit is missed + * @param soulshot True if SoulShot are charged + * @param shld True if shield is efficient + */ + @Override + protected void onHitTimer(L2Character target, int damage, boolean crit, boolean miss, boolean soulshot, byte shld) + { + super.onHitTimer(target, damage, crit, miss, soulshot, shld); + } + + public void queryGameGuard() + { + getClient().setGameGuardOk(false); + this.sendPacket(new GameGuardQuery()); + if (Config.GAMEGUARD_ENFORCE) + { + ThreadPoolManager.getInstance().scheduleGeneral(new GameGuardCheck(), 30 * 1000); + } + } + + protected class GameGuardCheck implements Runnable + { + @Override + public void run() + { + L2GameClient client = getClient(); + if ((client != null) && !client.isAuthedGG() && isOnline()) + { + GmListTable.broadcastMessageToGMs("Client " + client + " failed to reply GameGuard query and is being kicked!"); + _log.info("Client " + client + " failed to reply GameGuard query and is being kicked!"); + client.close(LeaveWorld.STATIC_PACKET); + } + } + } + + /** + * Send a Server->Client packet StatusUpdate to the L2PcInstance.
    + *
    + */ + @Override + public void sendPacket(L2GameServerPacket packet) + { + if (_client != null) + { + _client.sendPacket(packet); + } + } + + /** + * Send SystemMessage packet.
    + *
    + * @param id: SystemMessageId + */ + public void sendPacket(SystemMessageId id) + { + sendPacket(SystemMessage.getSystemMessage(id)); + } + + /** + * Manage Interact Task with another L2PcInstance.
    + *
    + * Actions :
    + *
    + *
  • If the private store is a STORE_PRIVATE_SELL, send a Server->Client PrivateBuyListSell packet to the L2PcInstance
  • If the private store is a STORE_PRIVATE_BUY, send a Server->Client PrivateBuyListBuy packet to the L2PcInstance
  • If the private store is a + * STORE_PRIVATE_MANUFACTURE, send a Server->Client RecipeShopSellList packet to the L2PcInstance

  • + *
    + * @param target The L2Character targeted + */ + public void doInteract(L2Character target) + { + if (target instanceof L2PcInstance) + { + L2PcInstance temp = (L2PcInstance) target; + sendPacket(ActionFailed.STATIC_PACKET); + + if ((temp.getPrivateStoreType() == STORE_PRIVATE_SELL) || (temp.getPrivateStoreType() == STORE_PRIVATE_PACKAGE_SELL)) + { + sendPacket(new PrivateStoreListSell(this, temp)); + } + else if (temp.getPrivateStoreType() == STORE_PRIVATE_BUY) + { + sendPacket(new PrivateStoreListBuy(this, temp)); + } + else if (temp.getPrivateStoreType() == STORE_PRIVATE_MANUFACTURE) + { + sendPacket(new RecipeShopSellList(this, temp)); + } + + } + else + { + // _interactTarget=null should never happen but one never knows ^^; + if (target != null) + { + target.onAction(this); + } + } + } + + /** + * Manage AutoLoot Task.
    + *
    + * Actions :
    + *
    + *
  • Send a System Message to the L2PcInstance : YOU_PICKED_UP_S1_ADENA or YOU_PICKED_UP_S1_S2
  • Add the Item to the L2PcInstance inventory
  • Send a Server->Client packet InventoryUpdate to this L2PcInstance with NewItem (use a new slot) or ModifiedItem (increase amount)
  • + * Send a Server->Client packet StatusUpdate to this L2PcInstance with current weight

  • + *
    + * Caution : If a Party is in progress, distribute Items between party members
    + *
    + * @param target The L2ItemInstance dropped + */ + public void doAutoLoot(L2Attackable target, L2Attackable.RewardItem item) + { + if (isInParty() && (ItemTable.getInstance().getTemplate(item.getItemId()).getItemType() != L2EtcItemType.HERB)) + { + getParty().distributeItem(this, item, false, target); + } + else if (item.getItemId() == 57) + { + addAdena("Loot", item.getCount(), target, true); + } + else + { + addItem("Loot", item.getItemId(), item.getCount(), target, true); + } + } + + /** + * Manage Pickup Task.
    + *
    + * Actions :
    + *
    + *
  • Send a Server->Client packet StopMove to this L2PcInstance
  • Remove the L2ItemInstance from the world and send server->client GetItem packets
  • Send a System Message to the L2PcInstance : YOU_PICKED_UP_S1_ADENA or YOU_PICKED_UP_S1_S2
  • Add the Item to the L2PcInstance + * inventory
  • Send a Server->Client packet InventoryUpdate to this L2PcInstance with NewItem (use a new slot) or ModifiedItem (increase amount)
  • Send a Server->Client packet StatusUpdate to this L2PcInstance with current weight

  • + *
    + * Caution : If a Party is in progress, distribute Items between party members
    + *
    + * @param object The L2ItemInstance to pick up + */ + protected void doPickupItem(L2Object object) + { + if (isAlikeDead() || isFakeDeath()) + { + return; + } + + // Set the AI Intention to AI_INTENTION_IDLE + getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); + + // Check if the L2Object to pick up is a L2ItemInstance + if (!(object instanceof L2ItemInstance)) + { + // dont try to pickup anything that is not an item :) + _log.warning(this + " trying to pickup wrong target." + getTarget()); + return; + } + + L2ItemInstance target = (L2ItemInstance) object; + + // Send a Server->Client packet ActionFailed to this L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + + // Send a Server->Client packet StopMove to this L2PcInstance + StopMove sm = new StopMove(this); + if (Config.DEBUG) + { + _log.fine("pickup pos: " + target.getX() + " " + target.getY() + " " + target.getZ()); + } + sendPacket(sm); + + synchronized (target) + { + // Check if the target to pick up is visible + if (!target.isVisible()) + { + // Send a Server->Client packet ActionFailed to this L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return; + } + + if (!target.getDropProtection().tryPickUp(this)) + { + sendPacket(ActionFailed.STATIC_PACKET); + SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1); + smsg.addItemName(target); + sendPacket(smsg); + return; + } + + if (((isInParty() && (getParty().getLootDistribution() == L2Party.ITEM_LOOTER)) || !isInParty()) && !_inventory.validateCapacity(target)) + { + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SLOTS_FULL)); + return; + } + + if (isInvul() && !isGM()) + { + sendPacket(ActionFailed.STATIC_PACKET); + SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1); + smsg.addItemName(target); + sendPacket(smsg); + return; + } + + if ((target.getOwnerId() != 0) && (target.getOwnerId() != getObjectId()) && !isInLooterParty(target.getOwnerId())) + { + sendPacket(ActionFailed.STATIC_PACKET); + + if (target.getItemId() == 57) + { + SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1_ADENA); + smsg.addItemNumber(target.getCount()); + sendPacket(smsg); + } + else if (target.getCount() > 1) + { + SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S2_S1_S); + smsg.addItemName(target); + smsg.addItemNumber(target.getCount()); + sendPacket(smsg); + } + else + { + SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1); + smsg.addItemName(target); + sendPacket(smsg); + } + + return; + } + + // You can pickup only 1 combat flag + if (FortSiegeManager.getInstance().isCombat(target.getItemId())) + { + if (!FortSiegeManager.getInstance().checkIfCanPickup(this)) + { + return; + } + } + + if ((target.getItemLootShedule() != null) && ((target.getOwnerId() == getObjectId()) || isInLooterParty(target.getOwnerId()))) + { + target.resetOwnerTimer(); + } + + // Remove the L2ItemInstance from the world and send server->client GetItem packets + target.pickupMe(this); + if (Config.SAVE_DROPPED_ITEM) + { + ItemsOnGroundManager.getInstance().removeObject(target); + } + + } + + // Auto use herbs - pick up + if (target.getItemType() == L2EtcItemType.HERB) + { + IItemHandler handler = ItemHandler.getInstance().getItemHandler(target.getEtcItem()); + if (handler == null) + { + _log.fine("No item handler registered for item ID " + target.getItemId() + "."); + } + else + { + handler.useItem(this, target, false); + } + ItemTable.getInstance().destroyItem("Consume", target, this, null); + } + // Cursed Weapons are not distributed + else if (CursedWeaponsManager.getInstance().isCursed(target.getItemId())) + { + addItem("Pickup", target, null, true); + } + else if (FortSiegeManager.getInstance().isCombat(target.getItemId())) + { + addItem("Pickup", target, null, true); + } + else + { + // if item is instance of L2ArmorType or L2WeaponType broadcast an "Attention" system message + if ((target.getItemType() instanceof L2ArmorType) || (target.getItemType() instanceof L2WeaponType)) + { + if (target.getEnchantLevel() > 0) + { + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.ANNOUNCEMENT_C1_PICKED_UP_S2_S3); + msg.addPcName(this); + msg.addNumber(target.getEnchantLevel()); + msg.addItemName(target.getItemId()); + broadcastPacket(msg, 1400); + } + else + { + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.ANNOUNCEMENT_C1_PICKED_UP_S2); + msg.addPcName(this); + msg.addItemName(target.getItemId()); + broadcastPacket(msg, 1400); + } + } + + // Check if a Party is in progress + if (isInParty()) + { + getParty().distributeItem(this, target); + } + else if ((target.getItemId() == 57) && (getInventory().getAdenaInstance() != null)) + { + addAdena("Pickup", target.getCount(), null, true); + ItemTable.getInstance().destroyItem("Pickup", target, this, null); + } + else + { + addItem("Pickup", target, null, true); + } + } + } + + public boolean canOpenPrivateStore() + { + return !isAlikeDead() && !isInOlympiadMode() && !isMounted() && !isInsideZone(ZONE_NOSTORE) && !isCastingNow(); + } + + public void tryOpenPrivateBuyStore() + { + // Player shouldn't be able to set stores if he/she is alike dead (dead or fake death) + if (canOpenPrivateStore()) + { + if ((getPrivateStoreType() == L2PcInstance.STORE_PRIVATE_BUY) || (getPrivateStoreType() == (L2PcInstance.STORE_PRIVATE_BUY + 1))) + { + setPrivateStoreType(L2PcInstance.STORE_PRIVATE_NONE); + } + if (getPrivateStoreType() == L2PcInstance.STORE_PRIVATE_NONE) + { + if (isSitting()) + { + standUp(); + } + setPrivateStoreType(L2PcInstance.STORE_PRIVATE_BUY + 1); + sendPacket(new PrivateStoreManageListBuy(this)); + } + } + else + { + if (isInsideZone(ZONE_NOSTORE)) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_PRIVATE_STORE_HERE)); + } + sendPacket(ActionFailed.STATIC_PACKET); + } + } + + public void tryOpenPrivateSellStore(boolean isPackageSale) + { + // Player shouldn't be able to set stores if he/she is alike dead (dead or fake death) + if (canOpenPrivateStore()) + { + if ((getPrivateStoreType() == L2PcInstance.STORE_PRIVATE_SELL) || (getPrivateStoreType() == (L2PcInstance.STORE_PRIVATE_SELL + 1)) || (getPrivateStoreType() == L2PcInstance.STORE_PRIVATE_PACKAGE_SELL)) + { + setPrivateStoreType(L2PcInstance.STORE_PRIVATE_NONE); + } + + if (getPrivateStoreType() == L2PcInstance.STORE_PRIVATE_NONE) + { + if (isSitting()) + { + standUp(); + } + setPrivateStoreType(L2PcInstance.STORE_PRIVATE_SELL + 1); + sendPacket(new PrivateStoreManageListSell(this, isPackageSale)); + } + } + else + { + if (isInsideZone(ZONE_NOSTORE)) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_PRIVATE_STORE_HERE)); + } + sendPacket(ActionFailed.STATIC_PACKET); + } + } + + public final PreparedListContainer getMultiSell() + { + return _currentMultiSell; + } + + public final void setMultiSell(PreparedListContainer list) + { + _currentMultiSell = list; + } + + @Override + public boolean isTransformed() + { + return (_transformation != null) && !_transformation.isStance(); + } + + public boolean isInStance() + { + return (_transformation != null) && _transformation.isStance(); + } + + public void transform(L2Transformation transformation) + { + if (_transformation != null) + { + // You already polymorphed and cannot polymorph again. + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.YOU_ALREADY_POLYMORPHED_AND_CANNOT_POLYMORPH_AGAIN); + sendPacket(msg); + return; + } + setQueuedSkill(null, false, false); + if (isMounted()) + { + // Get off the strider or something else if character is mounted + dismount(); + } + + _transformation = transformation; + stopAllToggles(); + transformation.onTransform(); + sendSkillList(); + sendPacket(new SkillCoolTime(this)); + sendPacket(ExBasicActionList.getStaticPacket(this)); + broadcastUserInfo(); + } + + @Override + public void untransform() + { + if (_transformation != null) + { + setTransformAllowedSkills(new int[] {}); + _transformation.onUntransform(); + _transformation = null; + stopEffects(L2EffectType.TRANSFORMATION); + sendSkillList(); + sendPacket(new SkillCoolTime(this)); + sendPacket(ExBasicActionList.getStaticPacket(this)); + broadcastUserInfo(); + } + } + + public L2Transformation getTransformation() + { + return _transformation; + } + + /** + * This returns the transformation Id of the current transformation. For example, if a player is transformed as a Buffalo, and then picks up the Zariche, the transform Id returned will be that of the Zariche, and NOT the Buffalo. + * @return Transformation Id + */ + public int getTransformationId() + { + return (_transformation == null ? 0 : _transformation.getId()); + } + + /** + * This returns the transformation Id stored inside the character table, selected by the method: transformSelectInfo() For example, if a player is transformed as a Buffalo, and then picks up the Zariche, the transform Id returned will be that of the Buffalo, and NOT the Zariche. + * @return Transformation Id + */ + public int transformId() + { + return _transformationId; + } + + /** + * This is a simple query that inserts the transform Id into the character table for future reference. + */ + public void transformInsertInfo() + { + _transformationId = getTransformationId(); + + if ((_transformationId == L2Transformation.TRANSFORM_AKAMANAH) || (_transformationId == L2Transformation.TRANSFORM_ZARICHE)) + { + return; + } + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(UPDATE_CHAR_TRANSFORM); + + statement.setInt(1, _transformationId); + statement.setInt(2, getObjectId()); + + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Transformation insert info: ", e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * This selects the current + * @return transformation Id + */ + public int transformSelectInfo() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(SELECT_CHAR_TRANSFORM); + + statement.setInt(1, getObjectId()); + ResultSet rset = statement.executeQuery(); + rset.next(); + _transformationId = rset.getInt("transform_id"); + + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Transformation select info: ", e); + } + finally + { + L2DatabaseFactory.close(con); + } + return _transformationId; + } + + /** + * Set a target.
    + *
    + * Actions :
    + *
    + *
  • Remove the L2PcInstance from the _statusListener of the old target if it was a L2Character
  • Add the L2PcInstance to the _statusListener of the new target if it's a L2Character
  • Target the new L2Object (add the target to the L2PcInstance _target, _knownObject and + * L2PcInstance to _KnownObject of the L2Object)

  • + *
    + * @param newTarget The L2Object to target + */ + @Override + public void setTarget(L2Object newTarget) + { + + if (newTarget != null) + { + boolean isParty = (((newTarget instanceof L2PcInstance) && isInParty() && getParty().getPartyMembers().contains(newTarget))); + + // Check if the new target is visible + if (!isParty && !newTarget.isVisible()) + { + newTarget = null; + } + + // Prevents /target exploiting + if ((newTarget != null) && !isParty && (Math.abs(newTarget.getZ() - getZ()) > 1000)) + { + newTarget = null; + } + } + if (!isGM()) + { + // Can't target and attack festival monsters if not participant + if ((newTarget instanceof L2FestivalMonsterInstance) && !isFestivalParticipant()) + { + newTarget = null; + } + else if (newTarget instanceof L2Vehicle) + { + newTarget = null; + } + else if (isInParty() && getParty().isInDimensionalRift()) + { + byte riftType = getParty().getDimensionalRift().getType(); + byte riftRoom = getParty().getDimensionalRift().getCurrentRoom(); + + if ((newTarget != null) && !DimensionalRiftManager.getInstance().getRoom(riftType, riftRoom).checkIfInZone(newTarget.getX(), newTarget.getY(), newTarget.getZ())) + { + newTarget = null; + } + } + } + + // Get the current target + L2Object oldTarget = getTarget(); + + if (oldTarget != null) + { + if (oldTarget.equals(newTarget)) + { + return; // no target change + } + + // Remove the L2PcInstance from the _statusListener of the old target if it was a L2Character + if (oldTarget instanceof L2Character) + { + ((L2Character) oldTarget).removeStatusListener(this); + } + } + + // Add the L2PcInstance to the _statusListener of the new target if it's a L2Character + if (newTarget instanceof L2Character) + { + ((L2Character) newTarget).addStatusListener(this); + TargetSelected my = new TargetSelected(getObjectId(), newTarget.getObjectId(), getX(), getY(), getZ()); + broadcastPacket(my); + } + if ((newTarget == null) && (getTarget() != null)) + { + broadcastPacket(new TargetUnselected(this)); + } + + // Target the new L2Object (add the target to the L2PcInstance _target, _knownObject and L2PcInstance to _KnownObject of the L2Object) + super.setTarget(newTarget); + } + + /** + * Return the active weapon instance (always equiped in the right hand).
    + *
    + */ + @Override + public L2ItemInstance getActiveWeaponInstance() + { + return getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND); + } + + /** + * Return the active weapon item (always equiped in the right hand).
    + *
    + */ + @Override + public L2Weapon getActiveWeaponItem() + { + L2ItemInstance weapon = getActiveWeaponInstance(); + + if (weapon == null) + { + return getFistsWeaponItem(); + } + + return (L2Weapon) weapon.getItem(); + } + + public L2ItemInstance getChestArmorInstance() + { + return getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST); + } + + public L2ItemInstance getLegsArmorInstance() + { + return getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEGS); + } + + public L2Armor getActiveChestArmorItem() + { + L2ItemInstance armor = getChestArmorInstance(); + + if (armor == null) + { + return null; + } + + return (L2Armor) armor.getItem(); + } + + public L2Armor getActiveLegsArmorItem() + { + L2ItemInstance legs = getLegsArmorInstance(); + + if (legs == null) + { + return null; + } + + return (L2Armor) legs.getItem(); + } + + public boolean isWearingHeavyArmor() + { + L2ItemInstance legs = getLegsArmorInstance(); + L2ItemInstance armor = getChestArmorInstance(); + + if ((armor != null) && (legs != null)) + { + if (((L2ArmorType) legs.getItemType() == L2ArmorType.HEAVY) && ((L2ArmorType) armor.getItemType() == L2ArmorType.HEAVY)) + { + return true; + } + } + if (armor != null) + { + if (((getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR) && ((L2ArmorType) armor.getItemType() == L2ArmorType.HEAVY))) + { + return true; + } + } + return false; + } + + public boolean isWearingLightArmor() + { + L2ItemInstance legs = getLegsArmorInstance(); + L2ItemInstance armor = getChestArmorInstance(); + + if ((armor != null) && (legs != null)) + { + if (((L2ArmorType) legs.getItemType() == L2ArmorType.LIGHT) && ((L2ArmorType) armor.getItemType() == L2ArmorType.LIGHT)) + { + return true; + } + } + if (armor != null) + { + if (((getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR) && ((L2ArmorType) armor.getItemType() == L2ArmorType.LIGHT))) + { + return true; + } + } + return false; + } + + public boolean isWearingMagicArmor() + { + L2ItemInstance legs = getLegsArmorInstance(); + L2ItemInstance armor = getChestArmorInstance(); + + if ((armor != null) && (legs != null)) + { + if (((L2ArmorType) legs.getItemType() == L2ArmorType.MAGIC) && ((L2ArmorType) armor.getItemType() == L2ArmorType.MAGIC)) + { + return true; + } + } + if (armor != null) + { + if (((getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR) && ((L2ArmorType) armor.getItemType() == L2ArmorType.MAGIC))) + { + return true; + } + } + return false; + } + + public boolean isMarried() + { + return _married; + } + + public void setMarried(boolean state) + { + _married = state; + } + + public boolean isEngageRequest() + { + return _engagerequest; + } + + public void setEngageRequest(boolean state, int playerid) + { + _engagerequest = state; + _engageid = playerid; + } + + public void setMarryRequest(boolean state) + { + _marryrequest = state; + } + + public boolean isMarryRequest() + { + return _marryrequest; + } + + public void setMarryAccepted(boolean state) + { + _marryaccepted = state; + } + + public boolean isMarryAccepted() + { + return _marryaccepted; + } + + public int getEngageId() + { + return _engageid; + } + + public int getPartnerId() + { + return _partnerId; + } + + public void setPartnerId(int partnerid) + { + _partnerId = partnerid; + } + + public int getCoupleId() + { + return _coupleId; + } + + public void setCoupleId(int coupleId) + { + _coupleId = coupleId; + } + + public void engageAnswer(int answer) + { + if (_engagerequest == false) + { + return; + } + else if (_engageid == 0) + { + return; + } + else + { + L2PcInstance ptarget = L2World.getInstance().getPlayer(_engageid); + setEngageRequest(false, 0); + if (ptarget != null) + { + if (answer == 1) + { + CoupleManager.getInstance().createCouple(ptarget, L2PcInstance.this); + ptarget.sendMessage("Request to Engage has been >ACCEPTED<"); + } + else + { + ptarget.sendMessage("Request to Engage has been >DENIED + *
    + */ + @Override + public L2ItemInstance getSecondaryWeaponInstance() + { + return getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); + } + + /** + * Return the secondary L2Item item (always equiped in the left hand).
    + * Arrows, Shield..
    + */ + @Override + public L2Item getSecondaryWeaponItem() + { + L2ItemInstance item = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); + if (item != null) + { + return item.getItem(); + } + return null; + } + + /** + * Kill the L2Character, Apply Death Penalty, Manage gain/loss Karma and Item Drop.
    + *
    + * Actions :
    + *
    + *
  • Reduce the Experience of the L2PcInstance in function of the calculated Death Penalty
  • If necessary, unsummon the Pet of the killed L2PcInstance
  • Manage Karma gain for attacker and Karam loss for the killed L2PcInstance
  • If the killed L2PcInstance has Karma, manage + * Drop Item
  • Kill the L2PcInstance

  • + *
    + * @param i The HP decrease value + * @param attacker The L2Character who attacks + * @see com.l2jserver.gameserver.model.actor.L2Playable#doDie(com.l2jserver.gameserver.model.actor.L2Character) + */ + @Override + public boolean doDie(L2Character killer) + { + // Kill the L2PcInstance + if (!super.doDie(killer)) + { + return false; + } + + if (isMounted()) + { + stopFeed(); + } + synchronized (this) + { + if (isFakeDeath()) + { + stopFakeDeath(true); + } + } + + if (killer != null) + { + L2PcInstance pk = killer.getActingPlayer(); + + TvTEvent.onKill(killer, this); + + if (atEvent && (pk != null)) + { + pk.kills.add(getName()); + } + + // announce pvp/pk + if (Config.ANNOUNCE_PK_PVP && (pk != null) && !pk.isGM()) + { + String msg = ""; + if (getPvpFlag() == 0) + { + msg = Config.ANNOUNCE_PK_MSG.replace("$killer", pk.getName()).replace("$target", getName()); + if (Config.ANNOUNCE_PK_PVP_NORMAL_MESSAGE) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1); + sm.addString(msg); + Announcements.getInstance().announceToAll(sm); + } + else + { + Announcements.getInstance().announceToAll(msg); + } + } + else if (getPvpFlag() != 0) + { + msg = Config.ANNOUNCE_PVP_MSG.replace("$killer", pk.getName()).replace("$target", getName()); + if (Config.ANNOUNCE_PK_PVP_NORMAL_MESSAGE) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1); + sm.addString(msg); + Announcements.getInstance().announceToAll(sm); + } + else + { + Announcements.getInstance().announceToAll(msg); + } + } + } + + broadcastStatusUpdate(); + // Clear resurrect xp calculation + setExpBeforeDeath(0); + + // Issues drop of Cursed Weapon. + if (isCursedWeaponEquipped()) + { + CursedWeaponsManager.getInstance().drop(_cursedWeaponEquippedId, killer); + } + else if (isCombatFlagEquipped()) + { + // TODO: Fort siege during TW?? + if (TerritoryWarManager.getInstance().isTWInProgress()) + { + TerritoryWarManager.getInstance().dropCombatFlag(this, true, false); + } + else + { + Fort fort = FortManager.getInstance().getFort(this); + if (fort != null) + { + FortSiegeManager.getInstance().dropCombatFlag(this, fort.getFortId()); + } + else + { + int slot = getInventory().getSlotFromItem(getInventory().getItemByItemId(9819)); + getInventory().unEquipItemInBodySlot(slot); + destroyItem("CombatFlag", getInventory().getItemByItemId(9819), null, true); + } + } + } + else + { + if ((pk == null) || !pk.isCursedWeaponEquipped()) + { + onDieDropItem(killer); // Check if any item should be dropped + + if (!(isInsideZone(ZONE_PVP) && !isInsideZone(ZONE_SIEGE))) + { + if ((pk != null) && (pk.getClan() != null) && (getClan() != null) && !isAcademyMember() && !(pk.isAcademyMember())) + { + if ((_clan.isAtWarWith(pk.getClanId()) && pk.getClan().isAtWarWith(_clan.getClanId())) || (isInSiege() && pk.isInSiege())) + { + if (AntiFeedManager.getInstance().check(killer, this)) + { + // when your reputation score is 0 or below, the other clan cannot acquire any reputation points + if (getClan().getReputationScore() > 0) + { + pk.getClan().addReputationScore(Config.REPUTATION_SCORE_PER_KILL, false); + } + // when the opposing sides reputation score is 0 or below, your clans reputation score does not decrease + if (pk.getClan().getReputationScore() > 0) + { + _clan.takeReputationScore(Config.REPUTATION_SCORE_PER_KILL, false); + } + } + } + } + } + // If player is Lucky shouldn't get penalized. + if (Config.ALT_GAME_DELEVEL && !isLucky()) + { + // Reduce the Experience of the L2PcInstance in function of the calculated Death Penalty + // NOTE: deathPenalty +- Exp will update karma + // Penalty is lower if the player is at war with the pk (war has to be declared) + final boolean siegeNpc = (killer instanceof L2DefenderInstance) || (killer instanceof L2FortCommanderInstance); + final boolean atWar = (pk != null) && (getClan() != null) && (getClan().isAtWarWith(pk.getClanId())); + deathPenalty(atWar, (pk != null), siegeNpc); + } + else if (!(isInsideZone(ZONE_PVP) && !isInSiege()) || (pk == null)) + { + onDieUpdateKarma(); // Update karma if delevel is not allowed + } + } + } + } + + setPvpFlag(0); // Clear the pvp flag + + // Unsummon Cubics + if (!_cubics.isEmpty()) + { + for (L2CubicInstance cubic : _cubics.values()) + { + cubic.stopAction(); + cubic.cancelDisappear(); + } + + _cubics.clear(); + } + + if (_fusionSkill != null) + { + abortCast(); + } + + for (L2Character character : getKnownList().getKnownCharacters()) + { + if ((character.getFusionSkill() != null) && (character.getFusionSkill().getTarget() == this)) + { + character.abortCast(); + } + } + + if (isInParty() && getParty().isInDimensionalRift()) + { + getParty().getDimensionalRift().getDeadMemberList().add(this); + } + + if (getAgathionId() != 0) + { + setAgathionId(0); + } + + // calculate death penalty buff + calculateDeathPenaltyBuffLevel(killer); + + stopRentPet(); + stopWaterTask(); + + AntiFeedManager.getInstance().setLastDeathTime(getObjectId()); + + if (isPhoenixBlessed()) + { + reviveRequest(this, null, false); + } + else if (isAffected(CharEffectList.EFFECT_FLAG_CHARM_OF_COURAGE) && isInSiege()) + { + reviveRequest(this, null, false); + } + return true; + } + + private void onDieDropItem(L2Character killer) + { + if (atEvent || (killer == null)) + { + return; + } + + L2PcInstance pk = killer.getActingPlayer(); + if ((getKarma() <= 0) && (pk != null) && (pk.getClan() != null) && (getClan() != null) && (pk.getClan().isAtWarWith(getClanId()) + // || this.getClan().isAtWarWith(((L2PcInstance)killer).getClanId()) + )) + { + return; + } + + if ((!isInsideZone(ZONE_PVP) || (pk == null)) && (!isGM() || Config.KARMA_DROP_GM)) + { + boolean isKarmaDrop = false; + boolean isKillerNpc = (killer instanceof L2Npc); + int pkLimit = Config.KARMA_PK_LIMIT; + + int dropEquip = 0; + int dropEquipWeapon = 0; + int dropItem = 0; + int dropLimit = 0; + int dropPercent = 0; + + if ((getKarma() > 0) && (getPkKills() >= pkLimit)) + { + isKarmaDrop = true; + dropPercent = Config.KARMA_RATE_DROP; + dropEquip = Config.KARMA_RATE_DROP_EQUIP; + dropEquipWeapon = Config.KARMA_RATE_DROP_EQUIP_WEAPON; + dropItem = Config.KARMA_RATE_DROP_ITEM; + dropLimit = Config.KARMA_DROP_LIMIT; + } + else if (isKillerNpc && (getLevel() > 4) && !isFestivalParticipant()) + { + dropPercent = Config.PLAYER_RATE_DROP; + dropEquip = Config.PLAYER_RATE_DROP_EQUIP; + dropEquipWeapon = Config.PLAYER_RATE_DROP_EQUIP_WEAPON; + dropItem = Config.PLAYER_RATE_DROP_ITEM; + dropLimit = Config.PLAYER_DROP_LIMIT; + } + + if ((dropPercent > 0) && (Rnd.get(100) < dropPercent)) + { + int dropCount = 0; + + int itemDropPercent = 0; + + for (L2ItemInstance itemDrop : getInventory().getItems()) + { + // Don't drop + if (itemDrop.isShadowItem() || // Dont drop Shadow Items + itemDrop.isTimeLimitedItem() || // Dont drop Time Limited Items + !itemDrop.isDropable() || (itemDrop.getItemId() == 57) || // Adena + (itemDrop.getItem().getType2() == L2Item.TYPE2_QUEST) || // Quest Items + ((getPet() != null) && (getPet().getControlObjectId() == itemDrop.getItemId())) || // Control Item of active pet + (Arrays.binarySearch(Config.KARMA_LIST_NONDROPPABLE_ITEMS, itemDrop.getItemId()) >= 0) || // Item listed in the non droppable item list + (Arrays.binarySearch(Config.KARMA_LIST_NONDROPPABLE_PET_ITEMS, itemDrop.getItemId()) >= 0 // Item listed in the non droppable pet item list + )) + { + continue; + } + + if (itemDrop.isEquipped()) + { + // Set proper chance according to Item type of equipped Item + itemDropPercent = itemDrop.getItem().getType2() == L2Item.TYPE2_WEAPON ? dropEquipWeapon : dropEquip; + getInventory().unEquipItemInSlot(itemDrop.getLocationSlot()); + } + else + { + itemDropPercent = dropItem; // Item in inventory + } + + // NOTE: Each time an item is dropped, the chance of another item being dropped gets lesser (dropCount * 2) + if (Rnd.get(100) < itemDropPercent) + { + dropItem("DieDrop", itemDrop, killer, true); + + if (isKarmaDrop) + { + _log.warning(getName() + " has karma and dropped id = " + itemDrop.getItemId() + ", count = " + itemDrop.getCount()); + } + else + { + _log.warning(getName() + " dropped id = " + itemDrop.getItemId() + ", count = " + itemDrop.getCount()); + } + + if (++dropCount >= dropLimit) + { + break; + } + } + } + } + } + } + + private void onDieUpdateKarma() + { + // Karma lose for server that does not allow delevel + if (getKarma() > 0) + { + // this formula seems to work relatively well: + // baseKarma * thisLVL * (thisLVL/100) + // Calculate the new Karma of the attacker : newKarma = baseKarma*pkCountMulti*lvlDiffMulti + double karmaLost = Config.KARMA_LOST_BASE; + karmaLost *= getLevel(); // multiply by char lvl + karmaLost *= (getLevel() / 100.0); // divide by 0.charLVL + karmaLost = Math.round(karmaLost); + if (karmaLost < 0) + { + karmaLost = 1; + } + + // Decrease Karma of the L2PcInstance and Send it a Server->Client StatusUpdate packet with Karma and PvP Flag if necessary + setKarma(getKarma() - (int) karmaLost); + } + } + + public void onKillUpdatePvPKarma(L2Character target) + { + if (target == null) + { + return; + } + if (!(target instanceof L2Playable)) + { + return; + } + + L2PcInstance targetPlayer = target.getActingPlayer(); + + if (targetPlayer == null) + { + return; // Target player is null + } + if (targetPlayer == this) + { + return; // Target player is self + } + + if (isCursedWeaponEquipped()) + { + CursedWeaponsManager.getInstance().increaseKills(_cursedWeaponEquippedId); + // Custom message for time left + // CursedWeapon cw = CursedWeaponsManager.getInstance().getCursedWeapon(_cursedWeaponEquipedId); + // SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.THERE_IS_S1_HOUR_AND_S2_MINUTE_LEFT_OF_THE_FIXED_USAGE_TIME); + // int timeLeftInHours = (int)(((cw.getTimeLeft()/60000)/60)); + // msg.addItemName(_cursedWeaponEquipedId); + // msg.addNumber(timeLeftInHours); + // sendPacket(msg); + return; + } + + // If in duel and you kill (only can kill l2summon), do nothing + if (isInDuel() && targetPlayer.isInDuel()) + { + return; + } + + // If in Arena, do nothing + if (isInsideZone(ZONE_PVP) || targetPlayer.isInsideZone(ZONE_PVP)) + { + return; + } + + // Check if it's pvp + if ((checkIfPvP(target) && // Can pvp and + (targetPlayer.getPvpFlag() != 0 // Target player has pvp flag set + )) || // or + (isInsideZone(ZONE_PVP) && // Player is inside pvp zone and + targetPlayer.isInsideZone(ZONE_PVP) // Target player is inside pvp zone + )) + { + increasePvpKills(target); + } + else + // Target player doesn't have pvp flag set + { + // check about wars + if ((targetPlayer.getClan() != null) && (getClan() != null) && getClan().isAtWarWith(targetPlayer.getClanId()) && targetPlayer.getClan().isAtWarWith(getClanId()) && (targetPlayer.getPledgeType() != L2Clan.SUBUNIT_ACADEMY) && (getPledgeType() != L2Clan.SUBUNIT_ACADEMY)) + { + // 'Both way war' -> 'PvP Kill' + increasePvpKills(target); + return; + } + + // 'No war' or 'One way war' -> 'Normal PK' + if (targetPlayer.getKarma() > 0) // Target player has karma + { + if (Config.KARMA_AWARD_PK_KILL) + { + increasePvpKills(target); + } + } + else if (targetPlayer.getPvpFlag() == 0) // Target player doesn't have karma + { + increasePkKillsAndKarma(target); + // Unequip adventurer items + checkItemRestriction(); + } + } + } + + /** + * Increase the pvp kills count and send the info to the player + */ + public void increasePvpKills(L2Character target) + { + if ((target instanceof L2PcInstance) && AntiFeedManager.getInstance().check(this, target)) + { + // Add karma to attacker and increase its PK counter + setPvpKills(getPvpKills() + 1); + + // Send a Server->Client UserInfo packet to attacker with its Karma and PK Counter + sendPacket(new UserInfo(this)); + sendPacket(new ExBrExtraUserInfo(this)); + } + } + + /** + * Increase pk count, karma and send the info to the player + * @param targLVL : level of the killed player + * @param increasePk : true if PK counter should be increased too + */ + public void increasePkKillsAndKarma(L2Character target) + { + int baseKarma = Config.KARMA_MIN_KARMA; + int newKarma = baseKarma; + int karmaLimit = Config.KARMA_MAX_KARMA; + + int pkLVL = getLevel(); + int pkPKCount = getPkKills(); + + int targLVL = target.getLevel(); + + int lvlDiffMulti = 0; + int pkCountMulti = 0; + + // Check if the attacker has a PK counter greater than 0 + if (pkPKCount > 0) + { + pkCountMulti = pkPKCount / 2; + } + else + { + pkCountMulti = 1; + } + + if (pkCountMulti < 1) + { + pkCountMulti = 1; + } + + // Calculate the level difference Multiplier between attacker and killed L2PcInstance + if (pkLVL > targLVL) + { + lvlDiffMulti = pkLVL / targLVL; + } + else + { + lvlDiffMulti = 1; + } + + if (lvlDiffMulti < 1) + { + lvlDiffMulti = 1; + } + + // Calculate the new Karma of the attacker : newKarma = baseKarma*pkCountMulti*lvlDiffMulti + newKarma *= pkCountMulti; + newKarma *= lvlDiffMulti; + + // Make sure newKarma is less than karmaLimit and higher than baseKarma + if (newKarma < baseKarma) + { + newKarma = baseKarma; + } + if (newKarma > karmaLimit) + { + newKarma = karmaLimit; + } + + // Fix to prevent overflow (=> karma has a max value of 2 147 483 647) + if (getKarma() > (Integer.MAX_VALUE - newKarma)) + { + newKarma = Integer.MAX_VALUE - getKarma(); + } + + // Add karma to attacker and increase its PK counter + setKarma(getKarma() + newKarma); + if ((target instanceof L2PcInstance) && AntiFeedManager.getInstance().check(this, target)) + { + setPkKills(getPkKills() + 1); + } + + // Send a Server->Client UserInfo packet to attacker with its Karma and PK Counter + sendPacket(new UserInfo(this)); + sendPacket(new ExBrExtraUserInfo(this)); + } + + public int calculateKarmaLost(long exp) + { + // KARMA LOSS + // When a PKer gets killed by another player or a L2MonsterInstance, it loses a certain amount of Karma based on their level. + // this (with defaults) results in a level 1 losing about ~2 karma per death, and a lvl 70 loses about 11760 karma per death... + // You lose karma as long as you were not in a pvp zone and you did not kill urself. + // NOTE: exp for death (if delevel is allowed) is based on the players level + + long expGained = Math.abs(exp); + expGained /= Config.KARMA_XP_DIVIDER; + + // FIXME Micht : Maybe this code should be fixed and karma set to a long value + int karmaLost = 0; + if (expGained > Integer.MAX_VALUE) + { + karmaLost = Integer.MAX_VALUE; + } + else + { + karmaLost = (int) expGained; + } + + if (karmaLost < Config.KARMA_LOST_BASE) + { + karmaLost = Config.KARMA_LOST_BASE; + } + if (karmaLost > getKarma()) + { + karmaLost = getKarma(); + } + + return karmaLost; + } + + public void updatePvPStatus() + { + if (isInsideZone(ZONE_PVP)) + { + return; + } + setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_NORMAL_TIME); + + if (getPvpFlag() == 0) + { + startPvPFlag(); + } + } + + public void updatePvPStatus(L2Character target) + { + L2PcInstance player_target = target.getActingPlayer(); + + if (player_target == null) + { + return; + } + + if ((isInDuel() && (player_target.getDuelId() == getDuelId()))) + { + return; + } + if ((!isInsideZone(ZONE_PVP) || !player_target.isInsideZone(ZONE_PVP)) && (player_target.getKarma() == 0)) + { + if (checkIfPvP(player_target)) + { + setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_PVP_TIME); + } + else + { + setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_NORMAL_TIME); + } + if (getPvpFlag() == 0) + { + startPvPFlag(); + } + } + } + + /** + * TODO: Unhardcode by implementing Lucky effect (Support for effects on passive skills required). + * @return Returns {@code true} if player has Lucky skill and is level 9 or less. + */ + public boolean isLucky() + { + return ((getLevel() <= 9) && (getKnownSkill(194) != null)); + } + + /** + * Restore the specified % of experience this L2PcInstance has lost and sends a Server->Client StatusUpdate packet.
    + *
    + */ + public void restoreExp(double restorePercent) + { + if (getExpBeforeDeath() > 0) + { + // Restore the specified % of lost experience. + getStat().addExp(Math.round(((getExpBeforeDeath() - getExp()) * restorePercent) / 100)); + setExpBeforeDeath(0); + } + } + + /** + * Reduce the Experience (and level if necessary) of the L2PcInstance in function of the calculated Death Penalty.
    + *
    + * Actions :
    + *
    + *
  • Calculate the Experience loss
  • Set the value of _expBeforeDeath
  • Set the new Experience value of the L2PcInstance and Decrease its level if necessary
  • Send a Server->Client StatusUpdate packet with its new Experience

  • + *
    + */ + public void deathPenalty(boolean atwar, boolean killed_by_pc, boolean killed_by_siege_npc) + { + // TODO Need Correct Penalty + // Get the level of the L2PcInstance + final int lvl = getLevel(); + + int clan_luck = getSkillLevel(L2Skill.SKILL_CLAN_LUCK); + + double clan_luck_modificator = 1.0; + + if (!killed_by_pc) + { + switch (clan_luck) + { + case 3: + clan_luck_modificator = 0.8; + break; + case 2: + clan_luck_modificator = 0.8; + break; + case 1: + clan_luck_modificator = 0.88; + break; + default: + clan_luck_modificator = 1.0; + break; + } + } + else + { + switch (clan_luck) + { + case 3: + clan_luck_modificator = 0.5; + break; + case 2: + clan_luck_modificator = 0.5; + break; + case 1: + clan_luck_modificator = 0.5; + break; + default: + clan_luck_modificator = 1.0; + break; + } + } + + // The death steal you some Exp + double percentLost = Config.PLAYER_XP_PERCENT_LOST[getLevel()] * clan_luck_modificator; + + if (getKarma() > 0) + { + percentLost *= Config.RATE_KARMA_EXP_LOST; + } + + if (isFestivalParticipant() || atwar) + { + percentLost /= 4.0; + } + + // Calculate the Experience loss + long lostExp = 0; + if (!atEvent) + { + if (lvl < Experience.MAX_LEVEL) + { + lostExp = Math.round(((getStat().getExpForLevel(lvl + 1) - getStat().getExpForLevel(lvl)) * percentLost) / 100); + } + else + { + lostExp = Math.round(((getStat().getExpForLevel(Experience.MAX_LEVEL) - getStat().getExpForLevel(Experience.MAX_LEVEL - 1)) * percentLost) / 100); + } + } + + // Get the Experience before applying penalty + setExpBeforeDeath(getExp()); + + // No xp loss inside pvp zone unless + // - it's a siege zone and you're NOT participating + // - you're killed by a non-pc whose not belong to the siege + if (isInsideZone(ZONE_PVP)) + { + // No xp loss for siege participants inside siege zone + if (isInsideZone(ZONE_SIEGE)) + { + if (isInSiege() && (killed_by_pc || killed_by_siege_npc)) + { + lostExp = 0; + } + } + else if (killed_by_pc) + { + lostExp = 0; + } + } + + if (Config.DEBUG) + { + _log.fine(getName() + " died and lost " + lostExp + " experience."); + } + + // Set the new Experience value of the L2PcInstance + getStat().addExp(-lostExp); + } + + public boolean isPartyWaiting() + { + return PartyMatchWaitingList.getInstance().getPlayers().contains(this); + } + + public void setPartyRoom(int id) + { + _partyroom = id; + } + + public int getPartyRoom() + { + return _partyroom; + } + + public boolean isInPartyMatchRoom() + { + return _partyroom > 0; + } + + /** + * Stop the HP/MP/CP Regeneration task.
    + *
    + * Actions :
    + *
    + *
  • Set the RegenActive flag to False
  • Stop the HP/MP/CP Regeneration task

  • + *
    + */ + public void stopAllTimers() + { + stopHpMpRegeneration(); + stopWarnUserTakeBreak(); + stopWaterTask(); + stopFeed(); + clearPetData(); + storePetFood(_mountNpcId); + stopRentPet(); + stopPvpRegTask(); + stopPunishTask(true); + stopSoulTask(); + stopChargeTask(); + stopFameTask(); + stopVitalityTask(); + stopRecoBonusTask(); + stopRecoGiveTask(); + } + + /** + * Return the L2Summon of the L2PcInstance or null.
    + *
    + */ + @Override + public L2Summon getPet() + { + return _summon; + } + + /** + * Return the L2Decoy of the L2PcInstance or null.
    + *
    + */ + public L2Decoy getDecoy() + { + return _decoy; + } + + /** + * Return the L2Trap of the L2PcInstance or null.
    + *
    + */ + public L2Trap getTrap() + { + return _trap; + } + + /** + * Set the L2Summon of the L2PcInstance.
    + *
    + */ + public void setPet(L2Summon summon) + { + _summon = summon; + // update attack element value display + if (((_summon == null) || (_summon instanceof L2SummonInstance)) && getClassId().isSummoner() && (getAttackElement() != Elementals.NONE)) + { + sendPacket(new UserInfo(this)); + } + + } + + /** + * Set the L2Decoy of the L2PcInstance.
    + *
    + */ + public void setDecoy(L2Decoy decoy) + { + _decoy = decoy; + } + + /** + * Set the L2Trap of this L2PcInstance
    + *
    + * @param trap + */ + public void setTrap(L2Trap trap) + { + _trap = trap; + } + + /** + * Return the L2Summon of the L2PcInstance or null.
    + *
    + */ + public List getTrainedBeasts() + { + return _tamedBeast; + } + + /** + * Set the L2Summon of the L2PcInstance.
    + *
    + */ + public void addTrainedBeast(L2TamedBeastInstance tamedBeast) + { + if (_tamedBeast == null) + { + _tamedBeast = new FastList<>(); + } + _tamedBeast.add(tamedBeast); + } + + /** + * Return the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).
    + *
    + */ + public L2Request getRequest() + { + return _request; + } + + /** + * Set the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).
    + *
    + */ + public void setActiveRequester(L2PcInstance requester) + { + _activeRequester = requester; + } + + /** + * Return the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).
    + *
    + */ + public L2PcInstance getActiveRequester() + { + L2PcInstance requester = _activeRequester; + if (requester != null) + { + if (requester.isRequestExpired() && (_activeTradeList == null)) + { + _activeRequester = null; + } + } + return _activeRequester; + } + + /** + * Return True if a transaction is in progress.
    + *
    + */ + public boolean isProcessingRequest() + { + return (getActiveRequester() != null) || (_requestExpireTime > GameTimeController.getGameTicks()); + } + + /** + * Return True if a transaction is in progress.
    + *
    + */ + public boolean isProcessingTransaction() + { + return (getActiveRequester() != null) || (_activeTradeList != null) || (_requestExpireTime > GameTimeController.getGameTicks()); + } + + /** + * Select the Warehouse to be used in next activity.
    + *
    + */ + public void onTransactionRequest(L2PcInstance partner) + { + _requestExpireTime = GameTimeController.getGameTicks() + (REQUEST_TIMEOUT * GameTimeController.TICKS_PER_SECOND); + partner.setActiveRequester(this); + } + + /** + * Return true if last request is expired. + * @return + */ + public boolean isRequestExpired() + { + return !(_requestExpireTime > GameTimeController.getGameTicks()); + } + + /** + * Select the Warehouse to be used in next activity.
    + *
    + */ + public void onTransactionResponse() + { + _requestExpireTime = 0; + } + + /** + * Select the Warehouse to be used in next activity.
    + *
    + */ + public void setActiveWarehouse(ItemContainer warehouse) + { + _activeWarehouse = warehouse; + } + + /** + * Return active Warehouse.
    + *
    + */ + public ItemContainer getActiveWarehouse() + { + return _activeWarehouse; + } + + /** + * Select the TradeList to be used in next activity.
    + *
    + */ + public void setActiveTradeList(TradeList tradeList) + { + _activeTradeList = tradeList; + } + + /** + * Return active TradeList.
    + *
    + */ + public TradeList getActiveTradeList() + { + return _activeTradeList; + } + + public void onTradeStart(L2PcInstance partner) + { + _activeTradeList = new TradeList(this); + _activeTradeList.setPartner(partner); + + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.BEGIN_TRADE_WITH_C1); + msg.addPcName(partner); + sendPacket(msg); + sendPacket(new TradeStart(this)); + } + + public void onTradeConfirm(L2PcInstance partner) + { + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.C1_CONFIRMED_TRADE); + msg.addPcName(partner); + sendPacket(msg); + sendPacket(new TradeOtherDone()); + } + + public void onTradeCancel(L2PcInstance partner) + { + if (_activeTradeList == null) + { + return; + } + + _activeTradeList.lock(); + _activeTradeList = null; + + sendPacket(new TradeDone(0)); + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.C1_CANCELED_TRADE); + msg.addPcName(partner); + sendPacket(msg); + } + + public void onTradeFinish(boolean successfull) + { + _activeTradeList = null; + sendPacket(new TradeDone(1)); + if (successfull) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TRADE_SUCCESSFUL)); + } + } + + public void startTrade(L2PcInstance partner) + { + onTradeStart(partner); + partner.onTradeStart(this); + } + + public void cancelActiveTrade() + { + if (_activeTradeList == null) + { + return; + } + + L2PcInstance partner = _activeTradeList.getPartner(); + if (partner != null) + { + partner.onTradeCancel(this); + } + onTradeCancel(this); + } + + /** + * Return the _createList object of the L2PcInstance.
    + *
    + */ + public L2ManufactureList getCreateList() + { + return _createList; + } + + /** + * Set the _createList object of the L2PcInstance.
    + *
    + */ + public void setCreateList(L2ManufactureList x) + { + _createList = x; + } + + /** + * Return the _buyList object of the L2PcInstance.
    + *
    + */ + public TradeList getSellList() + { + if (_sellList == null) + { + _sellList = new TradeList(this); + } + return _sellList; + } + + /** + * Return the _buyList object of the L2PcInstance.
    + *
    + */ + public TradeList getBuyList() + { + if (_buyList == null) + { + _buyList = new TradeList(this); + } + return _buyList; + } + + /** + * Set the Private Store type of the L2PcInstance.
    + *
    + * Values :
    + *
    + *
  • 0 : STORE_PRIVATE_NONE
  • 1 : STORE_PRIVATE_SELL
  • 2 : sellmanage

  • + *
  • 3 : STORE_PRIVATE_BUY

  • + *
  • 4 : buymanage

  • + *
  • 5 : STORE_PRIVATE_MANUFACTURE

  • + */ + public void setPrivateStoreType(int type) + { + _privatestore = type; + + if (Config.OFFLINE_DISCONNECT_FINISHED && (_privatestore == STORE_PRIVATE_NONE) && ((getClient() == null) || getClient().isDetached())) + { + deleteMe(); + } + } + + /** + * Return the Private Store type of the L2PcInstance.
    + *
    + * Values :
    + *
    + *
  • 0 : STORE_PRIVATE_NONE
  • 1 : STORE_PRIVATE_SELL
  • 2 : sellmanage

  • + *
  • 3 : STORE_PRIVATE_BUY

  • + *
  • 4 : buymanage

  • + *
  • 5 : STORE_PRIVATE_MANUFACTURE

  • + */ + public int getPrivateStoreType() + { + return _privatestore; + } + + /** + * Set the _clan object, _clanId, _clanLeader Flag and title of the L2PcInstance.
    + *
    + */ + public void setClan(L2Clan clan) + { + _clan = clan; + setTitle(""); + + if (clan == null) + { + _clanId = 0; + _clanPrivileges = 0; + _pledgeType = 0; + _powerGrade = 0; + _lvlJoinedAcademy = 0; + _apprentice = 0; + _sponsor = 0; + _activeWarehouse = null; + if (_isOnline) + { + CommunityServerThread.getInstance().sendPacket(new WorldInfo(this, null, WorldInfo.TYPE_UPDATE_PLAYER_DATA)); + } + return; + } + + if (!clan.isMember(getObjectId())) + { + // char has been kicked from clan + setClan(null); + return; + } + + _clanId = clan.getClanId(); + if (_isOnline) + { + CommunityServerThread.getInstance().sendPacket(new WorldInfo(this, null, WorldInfo.TYPE_UPDATE_PLAYER_DATA)); + } + } + + /** + * Return the _clan object of the L2PcInstance.
    + *
    + */ + public L2Clan getClan() + { + return _clan; + } + + /** + * Return True if the L2PcInstance is the leader of its clan.
    + *
    + */ + public boolean isClanLeader() + { + return (getClan() != null) && (getObjectId() == getClan().getLeaderId()); + } + + /** + * Reduce the number of arrows/bolts owned by the L2PcInstance and send it Server->Client Packet InventoryUpdate or ItemList (to unequip if the last arrow was consummed).
    + *
    + */ + @Override + protected void reduceArrowCount(boolean bolts) + { + L2ItemInstance arrows = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); + + if (arrows == null) + { + getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND); + if (bolts) + { + _boltItem = null; + } + else + { + _arrowItem = null; + } + sendPacket(new ItemList(this, false)); + return; + } + + // Adjust item quantity + if (arrows.getCount() > 1) + { + synchronized (arrows) + { + arrows.changeCountWithoutTrace(-1, this, null); + arrows.setLastChange(L2ItemInstance.MODIFIED); + + // could do also without saving, but let's save approx 1 of 10 + if ((GameTimeController.getGameTicks() % 10) == 0) + { + arrows.updateDatabase(); + } + _inventory.refreshWeight(); + } + } + else + { + // Destroy entire item and save to database + _inventory.destroyItem("Consume", arrows, this, null); + + getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND); + if (bolts) + { + _boltItem = null; + } + else + { + _arrowItem = null; + } + + if (Config.DEBUG) + { + _log.fine("removed arrows count"); + } + sendPacket(new ItemList(this, false)); + return; + } + + if (!Config.FORCE_INVENTORY_UPDATE) + { + InventoryUpdate iu = new InventoryUpdate(); + iu.addModifiedItem(arrows); + sendPacket(iu); + } + else + { + sendPacket(new ItemList(this, false)); + } + } + + /** + * Equip arrows needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True.
    + *
    + */ + @Override + protected boolean checkAndEquipArrows() + { + // Check if nothing is equiped in left hand + if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null) + { + // Get the L2ItemInstance of the arrows needed for this bow + _arrowItem = getInventory().findArrowForBow(getActiveWeaponItem()); + + if (_arrowItem != null) + { + // Equip arrows needed in left hand + getInventory().setPaperdollItem(Inventory.PAPERDOLL_LHAND, _arrowItem); + + // Send a Server->Client packet ItemList to this L2PcINstance to update left hand equipement + ItemList il = new ItemList(this, false); + sendPacket(il); + } + } + else + { + // Get the L2ItemInstance of arrows equiped in left hand + _arrowItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); + } + + return _arrowItem != null; + } + + /** + * Equip bolts needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True.
    + *
    + */ + @Override + protected boolean checkAndEquipBolts() + { + // Check if nothing is equiped in left hand + if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null) + { + // Get the L2ItemInstance of the arrows needed for this bow + _boltItem = getInventory().findBoltForCrossBow(getActiveWeaponItem()); + + if (_boltItem != null) + { + // Equip arrows needed in left hand + getInventory().setPaperdollItem(Inventory.PAPERDOLL_LHAND, _boltItem); + + // Send a Server->Client packet ItemList to this L2PcINstance to update left hand equipement + ItemList il = new ItemList(this, false); + sendPacket(il); + } + } + else + { + // Get the L2ItemInstance of arrows equiped in left hand + _boltItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); + } + + return _boltItem != null; + } + + /** + * Disarm the player's weapon.
    + *
    + */ + public boolean disarmWeapons() + { + // Don't allow disarming a cursed weapon + if (isCursedWeaponEquipped()) + { + return false; + } + + // Don't allow disarming a Combat Flag or Territory Ward + if (isCombatFlagEquipped()) + { + return false; + } + + // Unequip the weapon + L2ItemInstance wpn = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND); + if (wpn != null) + { + L2ItemInstance[] unequiped = getInventory().unEquipItemInBodySlotAndRecord(wpn.getItem().getBodyPart()); + InventoryUpdate iu = new InventoryUpdate(); + for (L2ItemInstance itm : unequiped) + { + iu.addModifiedItem(itm); + } + sendPacket(iu); + + abortAttack(); + broadcastUserInfo(); + + // this can be 0 if the user pressed the right mousebutton twice very fast + if (unequiped.length > 0) + { + SystemMessage sm = null; + if (unequiped[0].getEnchantLevel() > 0) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED); + sm.addNumber(unequiped[0].getEnchantLevel()); + sm.addItemName(unequiped[0]); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISARMED); + sm.addItemName(unequiped[0]); + } + sendPacket(sm); + } + } + return true; + } + + /** + * Disarm the player's shield.
    + *
    + */ + public boolean disarmShield() + { + L2ItemInstance sld = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); + if (sld != null) + { + L2ItemInstance[] unequiped = getInventory().unEquipItemInBodySlotAndRecord(sld.getItem().getBodyPart()); + InventoryUpdate iu = new InventoryUpdate(); + for (L2ItemInstance itm : unequiped) + { + iu.addModifiedItem(itm); + } + sendPacket(iu); + + abortAttack(); + broadcastUserInfo(); + + // this can be 0 if the user pressed the right mousebutton twice very fast + if (unequiped.length > 0) + { + SystemMessage sm = null; + if (unequiped[0].getEnchantLevel() > 0) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED); + sm.addNumber(unequiped[0].getEnchantLevel()); + sm.addItemName(unequiped[0]); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISARMED); + sm.addItemName(unequiped[0]); + } + sendPacket(sm); + } + } + return true; + } + + public boolean mount(L2Summon pet) + { + if (!disarmWeapons()) + { + return false; + } + if (!disarmShield()) + { + return false; + } + if (isTransformed()) + { + return false; + } + + stopAllToggles(); + Ride mount = new Ride(this, true, pet.getTemplate().npcId); + setMount(pet.getNpcId(), pet.getLevel(), mount.getMountType()); + setMountObjectID(pet.getControlObjectId()); + clearPetData(); + startFeed(pet.getNpcId()); + broadcastPacket(mount); + + // Notify self and others about speed change + broadcastUserInfo(); + + pet.unSummon(this); + + return true; + } + + public boolean mount(int npcId, int controlItemObjId, boolean useFood) + { + if (!disarmWeapons()) + { + return false; + } + if (!disarmShield()) + { + return false; + } + if (isTransformed()) + { + return false; + } + + stopAllToggles(); + Ride mount = new Ride(this, true, npcId); + if (setMount(npcId, getLevel(), mount.getMountType())) + { + clearPetData(); + setMountObjectID(controlItemObjId); + broadcastPacket(mount); + + // Notify self and others about speed change + broadcastUserInfo(); + if (useFood) + { + startFeed(npcId); + } + return true; + } + return false; + } + + public boolean mountPlayer(L2Summon pet) + { + if ((pet != null) && pet.isMountable() && !isMounted() && !isBetrayed()) + { + if (isDead()) + { + // A strider cannot be ridden when dead + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.STRIDER_CANT_BE_RIDDEN_WHILE_DEAD)); + return false; + } + else if (pet.isDead()) + { + // A dead strider cannot be ridden. + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.DEAD_STRIDER_CANT_BE_RIDDEN)); + return false; + } + else if (pet.isInCombat() || pet.isRooted()) + { + // A strider in battle cannot be ridden + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.STRIDER_IN_BATLLE_CANT_BE_RIDDEN)); + return false; + + } + else if (isInCombat()) + { + // A strider cannot be ridden while in battle + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.STRIDER_CANT_BE_RIDDEN_WHILE_IN_BATTLE)); + return false; + } + else if (isSitting()) + { + // A strider can be ridden only when standing + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.STRIDER_CAN_BE_RIDDEN_ONLY_WHILE_STANDING)); + return false; + } + else if (isFishing()) + { + // You can't mount, dismount, break and drop items while fishing + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_DO_WHILE_FISHING_2)); + return false; + } + else if (isTransformed() || isCursedWeaponEquipped()) + { + // no message needed, player while transformed doesn't have mount action + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + else if (getInventory().getItemByItemId(9819) != null) + { + sendPacket(ActionFailed.STATIC_PACKET); + // FIXME: Wrong Message + sendMessage("You cannot mount a steed while holding a flag."); + return false; + } + else if (pet.isHungry()) + { + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.HUNGRY_STRIDER_NOT_MOUNT)); + return false; + } + else if (!Util.checkIfInRange(200, this, pet, true)) + { + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TOO_FAR_AWAY_FROM_FENRIR_TO_MOUNT)); + return false; + } + else if (!pet.isDead() && !isMounted()) + { + mount(pet); + } + } + else if (isRentedPet()) + { + stopRentPet(); + } + else if (isMounted()) + { + if ((getMountType() == 2) && isInsideZone(L2Character.ZONE_NOLANDING)) + { + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_DISMOUNT_HERE)); + return false; + } + else if (isHungry()) + { + sendPacket(ActionFailed.STATIC_PACKET); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.HUNGRY_STRIDER_NOT_MOUNT)); + return false; + } + else + { + dismount(); + } + } + return true; + } + + public boolean dismount() + { + boolean wasFlying = isFlying(); + + sendPacket(new SetupGauge(3, 0, 0)); + int petId = _mountNpcId; + if (setMount(0, 0, 0)) + { + stopFeed(); + clearPetData(); + if (wasFlying) + { + removeSkill(SkillTable.FrequentSkill.WYVERN_BREATH.getSkill()); + } + Ride dismount = new Ride(this, false, 0); + broadcastPacket(dismount); + setMountObjectID(0); + storePetFood(petId); + // Notify self and others about speed change + broadcastUserInfo(); + return true; + } + return false; + } + + /** + * Return True if the L2PcInstance use a dual weapon.
    + *
    + */ + @Override + public boolean isUsingDualWeapon() + { + L2Weapon weaponItem = getActiveWeaponItem(); + if (weaponItem == null) + { + return false; + } + + if (weaponItem.getItemType() == L2WeaponType.DUAL) + { + return true; + } + else if (weaponItem.getItemType() == L2WeaponType.DUALFIST) + { + return true; + } + else if (weaponItem.getItemType() == L2WeaponType.DUALDAGGER) + { + return true; + } + else + { + return false; + } + } + + public void setUptime(long time) + { + _uptime = time; + } + + public long getUptime() + { + return System.currentTimeMillis() - _uptime; + } + + /** + * Return True if the L2PcInstance is invulnerable.
    + *
    + */ + @Override + public boolean isInvul() + { + return super.isInvul() || (_teleportProtectEndTime > GameTimeController.getGameTicks()); + } + + /** + * Return True if the L2PcInstance has a Party in progress.
    + *
    + */ + @Override + public boolean isInParty() + { + return _party != null; + } + + /** + * Set the _party object of the L2PcInstance (without joining it).
    + *
    + */ + public void setParty(L2Party party) + { + _party = party; + } + + /** + * Set the _party object of the L2PcInstance AND join it.
    + *
    + */ + public void joinParty(L2Party party) + { + if (party != null) + { + // First set the party otherwise this wouldn't be considered + // as in a party into the L2Character.updateEffectIcons() call. + _party = party; + party.addPartyMember(this); + } + } + + /** + * Manage the Leave Party task of the L2PcInstance.
    + *
    + */ + public void leaveParty() + { + if (isInParty()) + { + _party.removePartyMember(this); + _party = null; + } + } + + /** + * Return the _party object of the L2PcInstance.
    + *
    + */ + @Override + public L2Party getParty() + { + return _party; + } + + /** + * Return True if the L2PcInstance is a GM.
    + *
    + */ + @Override + public boolean isGM() + { + return getAccessLevel().isGm(); + } + + /** + * Set the _accessLevel of the L2PcInstance.
    + *
    + */ + public void setAccessLevel(int level) + { + if (level == AccessLevels._masterAccessLevelNum) + { + _log.warning("Master access level set for character " + getName() + "! Just a warning to be careful ;)"); + _accessLevel = AccessLevels._masterAccessLevel; + } + else if (level == AccessLevels._userAccessLevelNum) + { + _accessLevel = AccessLevels._userAccessLevel; + } + else + { + L2AccessLevel accessLevel = AccessLevels.getInstance().getAccessLevel(level); + + if (accessLevel == null) + { + if (level < 0) + { + AccessLevels.getInstance().addBanAccessLevel(level); + _accessLevel = AccessLevels.getInstance().getAccessLevel(level); + } + else + { + _log.warning("Tryed to set unregistered access level " + level + " to character " + getName() + ". Setting access level without privileges!"); + _accessLevel = AccessLevels._userAccessLevel; + } + } + else + { + _accessLevel = accessLevel; + } + } + + getAppearance().setNameColor(_accessLevel.getNameColor()); + getAppearance().setTitleColor(_accessLevel.getTitleColor()); + broadcastUserInfo(); + + CharNameTable.getInstance().addName(this); + } + + public void setAccountAccesslevel(int level) + { + LoginServerThread.getInstance().sendAccessLevel(getAccountName(), level); + } + + /** + * Return the _accessLevel of the L2PcInstance.
    + *
    + */ + public L2AccessLevel getAccessLevel() + { + if (Config.EVERYBODY_HAS_ADMIN_RIGHTS) + { + return AccessLevels._masterAccessLevel; + } + else if (_accessLevel == null) + { + setAccessLevel(AccessLevels._userAccessLevelNum); + } + + return _accessLevel; + } + + @Override + public double getLevelMod() + { + return ((100.0 - 11) + getLevel()) / 100.0; + } + + /** + * Update Stats of the L2PcInstance client side by sending Server->Client packet UserInfo/StatusUpdate to this L2PcInstance and CharInfo/StatusUpdate to all L2PcInstance in its _KnownPlayers (broadcast).
    + *
    + */ + public void updateAndBroadcastStatus(int broadcastType) + { + refreshOverloaded(); + refreshExpertisePenalty(); + // Send a Server->Client packet UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers (broadcast) + if (broadcastType == 1) + { + sendPacket(new UserInfo(this)); + sendPacket(new ExBrExtraUserInfo(this)); + } + if (broadcastType == 2) + { + broadcastUserInfo(); + } + } + + /** + * Send a Server->Client StatusUpdate packet with Karma and PvP Flag to the L2PcInstance and all L2PcInstance to inform (broadcast).
    + *
    + */ + public void setKarmaFlag(int flag) + { + sendPacket(new UserInfo(this)); + sendPacket(new ExBrExtraUserInfo(this)); + Collection plrs = getKnownList().getKnownPlayers().values(); + // synchronized (getKnownList().getKnownPlayers()) + { + for (L2PcInstance player : plrs) + { + player.sendPacket(new RelationChanged(this, getRelation(player), isAutoAttackable(player))); + if (getPet() != null) + { + player.sendPacket(new RelationChanged(getPet(), getRelation(player), isAutoAttackable(player))); + } + } + } + } + + /** + * Send a Server->Client StatusUpdate packet with Karma to the L2PcInstance and all L2PcInstance to inform (broadcast).
    + *
    + */ + public void broadcastKarma() + { + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.KARMA, getKarma()); + sendPacket(su); + + Collection plrs = getKnownList().getKnownPlayers().values(); + // synchronized (getKnownList().getKnownPlayers()) + { + for (L2PcInstance player : plrs) + { + player.sendPacket(new RelationChanged(this, getRelation(player), isAutoAttackable(player))); + if (getPet() != null) + { + player.sendPacket(new RelationChanged(getPet(), getRelation(player), isAutoAttackable(player))); + } + } + } + } + + /** + * Set the online Flag to True or False and update the characters table of the database with online status and lastAccess (called when login and logout).
    + *
    + */ + public void setOnlineStatus(boolean isOnline, boolean updateInDb) + { + if (_isOnline != isOnline) + { + _isOnline = isOnline; + } + + // Update the characters table of the database with online status and lastAccess (called when login and logout) + if (updateInDb) + { + updateOnlineStatus(); + } + } + + public void setIsIn7sDungeon(boolean isIn7sDungeon) + { + _isIn7sDungeon = isIn7sDungeon; + } + + /** + * Update the characters table of the database with online status and lastAccess of this L2PcInstance (called when login and logout).
    + *
    + */ + public void updateOnlineStatus() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("UPDATE characters SET online=?, lastAccess=? WHERE charId=?"); + statement.setInt(1, isOnlineInt()); + statement.setLong(2, System.currentTimeMillis()); + statement.setInt(3, getObjectId()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Failed updating character online status.", e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Create a new player in the characters table of the database.
    + *
    + */ + private boolean createDb() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(INSERT_CHARACTER); + + statement.setString(1, _accountName); + statement.setInt(2, getObjectId()); + statement.setString(3, getName()); + statement.setInt(4, getLevel()); + statement.setInt(5, getMaxHp()); + statement.setDouble(6, getCurrentHp()); + statement.setInt(7, getMaxCp()); + statement.setDouble(8, getCurrentCp()); + statement.setInt(9, getMaxMp()); + statement.setDouble(10, getCurrentMp()); + statement.setInt(11, getAppearance().getFace()); + statement.setInt(12, getAppearance().getHairStyle()); + statement.setInt(13, getAppearance().getHairColor()); + statement.setInt(14, getAppearance().getSex() ? 1 : 0); + statement.setLong(15, getExp()); + statement.setInt(16, getSp()); + statement.setInt(17, getKarma()); + statement.setInt(18, getFame()); + statement.setInt(19, getPvpKills()); + statement.setInt(20, getPkKills()); + statement.setInt(21, getClanId()); + statement.setInt(22, getRace().ordinal()); + statement.setInt(23, getClassId().getId()); + statement.setLong(24, getDeleteTimer()); + statement.setInt(25, hasDwarvenCraft() ? 1 : 0); + statement.setString(26, getTitle()); + statement.setInt(27, getAppearance().getTitleColor()); + statement.setInt(28, getAccessLevel().getLevel()); + statement.setInt(29, isOnlineInt()); + statement.setInt(30, isIn7sDungeon() ? 1 : 0); + statement.setInt(31, getClanPrivileges()); + statement.setInt(32, getWantsPeace()); + statement.setInt(33, getBaseClass()); + statement.setInt(34, getNewbie()); + statement.setInt(35, isNoble() ? 1 : 0); + statement.setLong(36, 0); + statement.setLong(37, getCreateTime()); + + statement.executeUpdate(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not insert char data: " + e.getMessage(), e); + return false; + } + finally + { + L2DatabaseFactory.close(con); + } + return true; + } + + /** + * Retrieve a L2PcInstance from the characters table of the database and add it in _allObjects of the L2world.
    + *
    + * Actions :
    + *
    + *
  • Retrieve the L2PcInstance from the characters table of the database
  • Add the L2PcInstance object in _allObjects
  • Set the x,y,z position of the L2PcInstance and make it invisible
  • Update the overloaded status of the L2PcInstance

  • + *
    + * @param objectId Identifier of the object to initialized + * @return The L2PcInstance loaded from the database + */ + private static L2PcInstance restore(int objectId) + { + L2PcInstance player = null; + Connection con = null; + + try + { + // Retrieve the L2PcInstance from the characters table of the database + con = L2DatabaseFactory.getInstance().getConnection(); + + PreparedStatement statement = con.prepareStatement(RESTORE_CHARACTER); + statement.setInt(1, objectId); + ResultSet rset = statement.executeQuery(); + + double currentCp = 0; + double currentHp = 0; + double currentMp = 0; + + while (rset.next()) + { + final int activeClassId = rset.getInt("classid"); + final boolean female = rset.getInt("sex") != 0; + final L2PcTemplate template = CharTemplateTable.getInstance().getTemplate(activeClassId); + PcAppearance app = new PcAppearance(rset.getByte("face"), rset.getByte("hairColor"), rset.getByte("hairStyle"), female); + + player = new L2PcInstance(objectId, template, rset.getString("account_name"), app); + player.setName(rset.getString("char_name")); + player._lastAccess = rset.getLong("lastAccess"); + + player.getStat().setExp(rset.getLong("exp")); + player.setExpBeforeDeath(rset.getLong("expBeforeDeath")); + player.getStat().setLevel(rset.getByte("level")); + player.getStat().setSp(rset.getInt("sp")); + + player.setWantsPeace(rset.getInt("wantspeace")); + + player.setHeading(rset.getInt("heading")); + + player.setKarma(rset.getInt("karma")); + player.setFame(rset.getInt("fame")); + player.setPvpKills(rset.getInt("pvpkills")); + player.setPkKills(rset.getInt("pkkills")); + player.setOnlineTime(rset.getLong("onlinetime")); + player.setNewbie(rset.getInt("newbie")); + player.setNoble(rset.getInt("nobless") == 1); + + player.setClanJoinExpiryTime(rset.getLong("clan_join_expiry_time")); + if (player.getClanJoinExpiryTime() < System.currentTimeMillis()) + { + player.setClanJoinExpiryTime(0); + } + player.setClanCreateExpiryTime(rset.getLong("clan_create_expiry_time")); + if (player.getClanCreateExpiryTime() < System.currentTimeMillis()) + { + player.setClanCreateExpiryTime(0); + } + + int clanId = rset.getInt("clanid"); + player.setPowerGrade((int) rset.getLong("power_grade")); + player.setPledgeType(rset.getInt("subpledge")); + // player.setApprentice(rset.getInt("apprentice")); + + if (clanId > 0) + { + player.setClan(ClanTable.getInstance().getClan(clanId)); + } + + if (player.getClan() != null) + { + if (player.getClan().getLeaderId() != player.getObjectId()) + { + if (player.getPowerGrade() == 0) + { + player.setPowerGrade(5); + } + player.setClanPrivileges(player.getClan().getRankPrivs(player.getPowerGrade())); + } + else + { + player.setClanPrivileges(L2Clan.CP_ALL); + player.setPowerGrade(1); + } + int pledgeClass = 0; + + pledgeClass = player.getClan().getClanMember(objectId).calculatePledgeClass(player); + + if (player.isNoble() && (pledgeClass < 5)) + { + pledgeClass = 5; + } + + if (player.isHero() && (pledgeClass < 8)) + { + pledgeClass = 8; + } + + player.setPledgeClass(pledgeClass); + } + else + { + player.setClanPrivileges(L2Clan.CP_NOTHING); + } + + player.setDeleteTimer(rset.getLong("deletetime")); + + player.setTitle(rset.getString("title")); + player.getAppearance().setTitleColor(rset.getInt("title_color")); + player.setAccessLevel(rset.getInt("accesslevel")); + player.setFistsWeaponItem(player.findFistsWeaponItem(activeClassId)); + player.setUptime(System.currentTimeMillis()); + + currentHp = rset.getDouble("curHp"); + currentCp = rset.getDouble("curCp"); + currentMp = rset.getDouble("curMp"); + + player._classIndex = 0; + try + { + player.setBaseClass(rset.getInt("base_class")); + } + catch (Exception e) + { + player.setBaseClass(activeClassId); + } + + // Restore Subclass Data (cannot be done earlier in function) + if (restoreSubClassData(player)) + { + if (activeClassId != player.getBaseClass()) + { + for (SubClass subClass : player.getSubClasses().values()) + { + if (subClass.getClassId() == activeClassId) + { + player._classIndex = subClass.getClassIndex(); + } + } + } + } + if ((player.getClassIndex() == 0) && (activeClassId != player.getBaseClass())) + { + // Subclass in use but doesn't exist in DB - + // a possible restart-while-modifysubclass cheat has been attempted. + // Switching to use base class + player.setClassId(player.getBaseClass()); + _log.warning("Player " + player.getName() + " reverted to base class. Possibly has tried a relogin exploit while subclassing."); + } + else + { + player._activeClass = activeClassId; + } + + player.setApprentice(rset.getInt("apprentice")); + player.setSponsor(rset.getInt("sponsor")); + player.setLvlJoinedAcademy(rset.getInt("lvl_joined_academy")); + player.setIsIn7sDungeon(rset.getInt("isin7sdungeon") == 1); + player.setPunishLevel(rset.getInt("punish_level")); + if (player.getPunishLevel() != PunishLevel.NONE) + { + player.setPunishTimer(rset.getLong("punish_timer")); + } + else + { + player.setPunishTimer(0); + } + + CursedWeaponsManager.getInstance().checkPlayer(player); + + player.setAllianceWithVarkaKetra(rset.getInt("varka_ketra_ally")); + + player.setDeathPenaltyBuffLevel(rset.getInt("death_penalty_level")); + + player.setVitalityPoints(rset.getInt("vitality_points"), true); + + // Add the L2PcInstance object in _allObjects + // L2World.getInstance().storeObject(player); + + // Set the x,y,z position of the L2PcInstance and make it invisible + player.setXYZInvisible(rset.getInt("x"), rset.getInt("y"), rset.getInt("z")); + + // Set Teleport Bookmark Slot + player.setBookMarkSlot(rset.getInt("BookmarkSlot")); + + // character creation Time + player.setCreateTime(rset.getLong("createTime")); + + // Language + player.setLang(rset.getString("language")); + + // Retrieve the name and ID of the other characters assigned to this account. + PreparedStatement stmt = con.prepareStatement("SELECT charId, char_name FROM characters WHERE account_name=? AND charId<>?"); + stmt.setString(1, player._accountName); + stmt.setInt(2, objectId); + ResultSet chars = stmt.executeQuery(); + + while (chars.next()) + { + Integer charId = chars.getInt("charId"); + String charName = chars.getString("char_name"); + player._chars.put(charId, charName); + } + + chars.close(); + stmt.close(); + break; + } + + rset.close(); + statement.close(); + + if (player == null) + { + return null; + } + + // Set Hero status if it applies + if ((Hero.getInstance().getHeroes() != null) && Hero.getInstance().getHeroes().containsKey(objectId)) + { + player.setHero(true); + } + + // Retrieve from the database all skills of this L2PcInstance and add them to _skills + // Retrieve from the database all items of this L2PcInstance and add them to _inventory + player.getInventory().restore(); + if (!Config.WAREHOUSE_CACHE) + { + player.getWarehouse(); + } + + // Retrieve from the database all secondary data of this L2PcInstance + // and reward expertise/lucky skills if necessary. + // Note that Clan, Noblesse and Hero skills are given separately and not here. + player.restoreCharData(); + player.rewardSkills(); + + // buff and status icons + if (Config.STORE_SKILL_COOLTIME) + { + player.restoreEffects(); + } + + // Restore current Cp, HP and MP values + player.setCurrentCp(currentCp); + player.setCurrentHp(currentHp); + player.setCurrentMp(currentMp); + + if (currentHp < 0.5) + { + player.setIsDead(true); + player.stopHpMpRegeneration(); + } + + // Restore pet if exists in the world + player.setPet(L2World.getInstance().getPet(player.getObjectId())); + if (player.getPet() != null) + { + player.getPet().setOwner(player); + } + + // Update the overloaded status of the L2PcInstance + player.refreshOverloaded(); + // Update the expertise status of the L2PcInstance + player.refreshExpertisePenalty(); + + player.restoreFriendList(); + + if (Config.STORE_UI_SETTINGS) + { + player.restoreUISettings(); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Failed loading character.", e); + } + finally + { + L2DatabaseFactory.close(con); + } + + return player; + } + + /** + * @return + */ + public Forum getMail() + { + if (_forumMail == null) + { + setMail(ForumsBBSManager.getInstance().getForumByName("MailRoot").getChildByName(getName())); + + if (_forumMail == null) + { + ForumsBBSManager.getInstance().createNewForum(getName(), ForumsBBSManager.getInstance().getForumByName("MailRoot"), Forum.MAIL, Forum.OWNERONLY, getObjectId()); + setMail(ForumsBBSManager.getInstance().getForumByName("MailRoot").getChildByName(getName())); + } + } + + return _forumMail; + } + + /** + * @param forum + */ + public void setMail(Forum forum) + { + _forumMail = forum; + } + + /** + * @return + */ + public Forum getMemo() + { + if (_forumMemo == null) + { + setMemo(ForumsBBSManager.getInstance().getForumByName("MemoRoot").getChildByName(_accountName)); + + if (_forumMemo == null) + { + ForumsBBSManager.getInstance().createNewForum(_accountName, ForumsBBSManager.getInstance().getForumByName("MemoRoot"), Forum.MEMO, Forum.OWNERONLY, getObjectId()); + setMemo(ForumsBBSManager.getInstance().getForumByName("MemoRoot").getChildByName(_accountName)); + } + } + + return _forumMemo; + } + + /** + * @param forum + */ + public void setMemo(Forum forum) + { + _forumMemo = forum; + } + + /** + * Restores sub-class data for the L2PcInstance, used to check the current class index for the character. + */ + private static boolean restoreSubClassData(L2PcInstance player) + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_SUBCLASSES); + statement.setInt(1, player.getObjectId()); + + ResultSet rset = statement.executeQuery(); + + while (rset.next()) + { + SubClass subClass = new SubClass(); + subClass.setClassId(rset.getInt("class_id")); + subClass.setLevel(rset.getByte("level")); + subClass.setExp(rset.getLong("exp")); + subClass.setSp(rset.getInt("sp")); + subClass.setClassIndex(rset.getInt("class_index")); + + // Enforce the correct indexing of _subClasses against their class indexes. + player.getSubClasses().put(subClass.getClassIndex(), subClass); + } + + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not restore classes for " + player.getName() + ": " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + + return true; + } + + /** + * Restores secondary data for the L2PcInstance, based on the current class index. + */ + private void restoreCharData() + { + // Retrieve from the database all skills of this L2PcInstance and add them to _skills. + restoreSkills(); + + // Retrieve from the database all macroses of this L2PcInstance and add them to _macroses. + _macroses.restore(); + + // Retrieve from the database all shortCuts of this L2PcInstance and add them to _shortCuts. + _shortCuts.restore(); + + // Retrieve from the database all henna of this L2PcInstance and add them to _henna. + restoreHenna(); + + // Retrieve from the database all teleport bookmark of this L2PcInstance and add them to _tpbookmark. + restoreTeleportBookmark(); + + // Retrieve from the database the recipe book of this L2PcInstance. + restoreRecipeBook(true); + + // Restore Recipe Shop list + if (Config.STORE_RECIPE_SHOPLIST) + { + restoreRecipeShopList(); + } + + // Load Premium Item List + loadPremiumItemList(); + + // Check for items in pet inventory + checkPetInvItems(); + } + + /** + * Restore recipe book data for this L2PcInstance. + */ + private void restoreRecipeBook(boolean loadCommon) + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + String sql = loadCommon ? "SELECT id, type, classIndex FROM character_recipebook WHERE charId=?" : "SELECT id FROM character_recipebook WHERE charId=? AND classIndex=? AND type = 1"; + PreparedStatement statement = con.prepareStatement(sql); + statement.setInt(1, getObjectId()); + if (!loadCommon) + { + statement.setInt(2, _classIndex); + } + ResultSet rset = statement.executeQuery(); + + _dwarvenRecipeBook.clear(); + + L2RecipeList recipe; + while (rset.next()) + { + recipe = RecipeController.getInstance().getRecipeList(rset.getInt("id")); + + if (loadCommon) + { + if (rset.getInt(2) == 1) + { + if (rset.getInt(3) == _classIndex) + { + registerDwarvenRecipeList(recipe, false); + } + } + else + { + registerCommonRecipeList(recipe, false); + } + } + else + { + registerDwarvenRecipeList(recipe, false); + } + } + + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not restore recipe book data:" + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public Map getPremiumItemList() + { + return _premiumItems; + } + + private void loadPremiumItemList() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + String sql = "SELECT itemNum, itemId, itemCount, itemSender FROM character_premium_items WHERE charId=?"; + PreparedStatement statement = con.prepareStatement(sql); + statement.setInt(1, getObjectId()); + ResultSet rset = statement.executeQuery(); + while (rset.next()) + { + int itemNum = rset.getInt("itemNum"); + int itemId = rset.getInt("itemId"); + long itemCount = rset.getLong("itemCount"); + String itemSender = rset.getString("itemSender"); + _premiumItems.put(itemNum, new L2PremiumItem(itemId, itemCount, itemSender)); + } + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not restore premium items: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void updatePremiumItem(int itemNum, long newcount) + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("UPDATE character_premium_items SET itemCount=? WHERE charId=? AND itemNum=? "); + statement.setLong(1, newcount); + statement.setInt(2, getObjectId()); + statement.setInt(3, itemNum); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not update premium items: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void deletePremiumItem(int itemNum) + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("DELETE FROM character_premium_items WHERE charId=? AND itemNum=? "); + statement.setInt(1, getObjectId()); + statement.setInt(2, itemNum); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not delete premium item: " + e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Update L2PcInstance stats in the characters table of the database.
    + *
    + */ + public synchronized void store(boolean storeActiveEffects) + { + // update client coords, if these look like true + // if (isInsideRadius(getClientX(), getClientY(), 1000, true)) + // setXYZ(getClientX(), getClientY(), getClientZ()); + + storeCharBase(); + storeCharSub(); + storeEffect(storeActiveEffects); + transformInsertInfo(); + if (Config.STORE_RECIPE_SHOPLIST) + { + storeRecipeShopList(); + } + if (Config.STORE_UI_SETTINGS) + { + storeUISettings(); + } + SevenSigns.getInstance().saveSevenSignsData(getObjectId()); + } + + public void store() + { + store(true); + } + + private void storeCharBase() + { + Connection con = null; + + try + { + // Get the exp, level, and sp of base class to store in base table + int currentClassIndex = getClassIndex(); + _classIndex = 0; + long exp = getStat().getExp(); + int level = getStat().getLevel(); + int sp = getStat().getSp(); + _classIndex = currentClassIndex; + + con = L2DatabaseFactory.getInstance().getConnection(); + // Update base class + PreparedStatement statement = con.prepareStatement(UPDATE_CHARACTER); + + statement.setInt(1, level); + statement.setInt(2, getMaxHp()); + statement.setDouble(3, getCurrentHp()); + statement.setInt(4, getMaxCp()); + statement.setDouble(5, getCurrentCp()); + statement.setInt(6, getMaxMp()); + statement.setDouble(7, getCurrentMp()); + statement.setInt(8, getAppearance().getFace()); + statement.setInt(9, getAppearance().getHairStyle()); + statement.setInt(10, getAppearance().getHairColor()); + statement.setInt(11, getAppearance().getSex() ? 1 : 0); + statement.setInt(12, getHeading()); + statement.setInt(13, _observerMode ? _lastX : getX()); + statement.setInt(14, _observerMode ? _lastY : getY()); + statement.setInt(15, _observerMode ? _lastZ : getZ()); + statement.setLong(16, exp); + statement.setLong(17, getExpBeforeDeath()); + statement.setInt(18, sp); + statement.setInt(19, getKarma()); + statement.setInt(20, getFame()); + statement.setInt(21, getPvpKills()); + statement.setInt(22, getPkKills()); + statement.setInt(23, getClanId()); + statement.setInt(24, getRace().ordinal()); + statement.setInt(25, getClassId().getId()); + statement.setLong(26, getDeleteTimer()); + statement.setString(27, getTitle()); + statement.setInt(28, getAppearance().getTitleColor()); + statement.setInt(29, getAccessLevel().getLevel()); + statement.setInt(30, isOnlineInt()); + statement.setInt(31, isIn7sDungeon() ? 1 : 0); + statement.setInt(32, getClanPrivileges()); + statement.setInt(33, getWantsPeace()); + statement.setInt(34, getBaseClass()); + + long totalOnlineTime = _onlineTime; + + if (_onlineBeginTime > 0) + { + totalOnlineTime += (System.currentTimeMillis() - _onlineBeginTime) / 1000; + } + + statement.setLong(35, totalOnlineTime); + statement.setInt(36, getPunishLevel().value()); + statement.setLong(37, getPunishTimer()); + statement.setInt(38, getNewbie()); + statement.setInt(39, isNoble() ? 1 : 0); + statement.setLong(40, getPowerGrade()); + statement.setInt(41, getPledgeType()); + statement.setInt(42, getLvlJoinedAcademy()); + statement.setLong(43, getApprentice()); + statement.setLong(44, getSponsor()); + statement.setInt(45, getAllianceWithVarkaKetra()); + statement.setLong(46, getClanJoinExpiryTime()); + statement.setLong(47, getClanCreateExpiryTime()); + statement.setString(48, getName()); + statement.setLong(49, getDeathPenaltyBuffLevel()); + statement.setInt(50, getBookMarkSlot()); + statement.setInt(51, getVitalityPoints()); + statement.setString(52, getLang()); + statement.setInt(53, getObjectId()); + + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not store char base data: " + this + " - " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void storeCharSub() + { + if (getTotalSubClasses() <= 0) + { + return; + } + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(UPDATE_CHAR_SUBCLASS); + + for (SubClass subClass : getSubClasses().values()) + { + statement.setLong(1, subClass.getExp()); + statement.setInt(2, subClass.getSp()); + statement.setInt(3, subClass.getLevel()); + statement.setInt(4, subClass.getClassId()); + statement.setInt(5, getObjectId()); + statement.setInt(6, subClass.getClassIndex()); + + statement.execute(); + statement.clearParameters(); + } + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not store sub class data for " + getName() + ": " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void storeEffect(boolean storeEffects) + { + if (!Config.STORE_SKILL_COOLTIME) + { + return; + } + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + // Delete all current stored effects for char to avoid dupe + PreparedStatement statement = con.prepareStatement(DELETE_SKILL_SAVE); + + statement.setInt(1, getObjectId()); + statement.setInt(2, getClassIndex()); + statement.execute(); + statement.close(); + + int buff_index = 0; + + final List storedSkills = new FastList<>(); + + // Store all effect data along with calulated remaining + // reuse delays for matching skills. 'restore_type'= 0. + statement = con.prepareStatement(ADD_SKILL_SAVE); + + if (storeEffects) + { + for (L2Effect effect : getAllEffects()) + { + if (effect == null) + { + continue; + } + + switch (effect.getEffectType()) + { + case HEAL_OVER_TIME: + case COMBAT_POINT_HEAL_OVER_TIME: + // TODO: Fix me. + case HIDE: + continue; + } + + L2Skill skill = effect.getSkill(); + if (storedSkills.contains(skill.getReuseHashCode())) + { + continue; + } + + storedSkills.add(skill.getReuseHashCode()); + + if (!effect.isHerbEffect() && effect.getInUse() && !skill.isToggle()) + { + + statement.setInt(1, getObjectId()); + statement.setInt(2, skill.getId()); + statement.setInt(3, skill.getLevel()); + statement.setInt(4, effect.getCount()); + statement.setInt(5, effect.getTime()); + + if (_reuseTimeStamps.containsKey(skill.getReuseHashCode())) + { + TimeStamp t = _reuseTimeStamps.get(skill.getReuseHashCode()); + statement.setLong(6, t.hasNotPassed() ? t.getReuse() : 0); + statement.setDouble(7, t.hasNotPassed() ? t.getStamp() : 0); + } + else + { + statement.setLong(6, 0); + statement.setDouble(7, 0); + } + + statement.setInt(8, 0); + statement.setInt(9, getClassIndex()); + statement.setInt(10, ++buff_index); + statement.execute(); + } + } + } + + // Store the reuse delays of remaining skills which + // lost effect but still under reuse delay. 'restore_type' 1. + for (int hash : _reuseTimeStamps.keySet()) + { + if (storedSkills.contains(hash)) + { + continue; + } + + TimeStamp t = _reuseTimeStamps.get(hash); + if ((t != null) && t.hasNotPassed()) + { + storedSkills.add(hash); + + statement.setInt(1, getObjectId()); + statement.setInt(2, t.getSkillId()); + statement.setInt(3, t.getSkillLvl()); + statement.setInt(4, -1); + statement.setInt(5, -1); + statement.setLong(6, t.getReuse()); + statement.setDouble(7, t.getStamp()); + statement.setInt(8, 1); + statement.setInt(9, getClassIndex()); + statement.setInt(10, ++buff_index); + statement.execute(); + } + } + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not store char effect data: ", e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Return True if the L2PcInstance is on line.
    + *
    + */ + public boolean isOnline() + { + return _isOnline; + } + + public int isOnlineInt() + { + if (_isOnline && (getClient() != null)) + { + return getClient().isDetached() ? 2 : 1; + } + return 0; + } + + public boolean isIn7sDungeon() + { + return _isIn7sDungeon; + } + + /** + * Add a skill to the L2PcInstance _skills and its Func objects to the calculator set of the L2PcInstance and save update in the character_skills table of the database.
    + *
    + * Concept :
    + *
    + * All skills own by a L2PcInstance are identified in _skills
    + *
    + * Actions :
    + *
    + *
  • Replace oldSkill by newSkill or Add the newSkill
  • If an old skill has been replaced, remove all its Func objects of L2Character calculator set
  • Add Func objects of newSkill to the calculator set of the L2Character

  • + *
    + * @param newSkill The L2Skill to add to the L2Character + * @return The L2Skill replaced or null if just added a new L2Skill + */ + public L2Skill addSkill(L2Skill newSkill, boolean store) + { + // Add a skill to the L2PcInstance _skills and its Func objects to the calculator set of the L2PcInstance + L2Skill oldSkill = super.addSkill(newSkill); + + // Add or update a L2PcInstance skill in the character_skills table of the database + if (store) + { + storeSkill(newSkill, oldSkill, -1); + } + return oldSkill; + } + + @Override + public L2Skill removeSkill(L2Skill skill, boolean store) + { + if (store) + { + return removeSkill(skill); + } + return super.removeSkill(skill, true); + } + + public L2Skill removeSkill(L2Skill skill, boolean store, boolean cancelEffect) + { + if (store) + { + return removeSkill(skill); + } + return super.removeSkill(skill, cancelEffect); + } + + /** + * Remove a skill from the L2Character and its Func objects from calculator set of the L2Character and save update in the character_skills table of the database.
    + *
    + * Concept :
    + *
    + * All skills own by a L2Character are identified in _skills
    + *
    + * Actions :
    + *
    + *
  • Remove the skill from the L2Character _skills
  • Remove all its Func objects from the L2Character calculator set

  • + *
    + * Overridden in :
    + *
    + *
  • L2PcInstance : Save update in the character_skills table of the database

  • + *
    + * @param skill The L2Skill to remove from the L2Character + * @return The L2Skill removed + */ + @Override + public L2Skill removeSkill(L2Skill skill) + { + // Remove a skill from the L2Character and its Func objects from calculator set of the L2Character + final L2Skill oldSkill = super.removeSkill(skill); + + if (oldSkill != null) + { + Connection con = null; + try + { + // Remove or update a L2PcInstance skill from the character_skills table of the database + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(DELETE_SKILL_FROM_CHAR); + + statement.setInt(1, oldSkill.getId()); + statement.setInt(2, getObjectId()); + statement.setInt(3, getClassIndex()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Error could not delete skill: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + if ((transformId() > 0) || isCursedWeaponEquipped()) + { + return oldSkill; + } + + L2ShortCut[] allShortCuts = getAllShortCuts(); + + for (L2ShortCut sc : allShortCuts) + { + if ((sc != null) && (skill != null) && (sc.getId() == skill.getId()) && (sc.getType() == L2ShortCut.TYPE_SKILL)) + { + deleteShortCut(sc.getSlot(), sc.getPage()); + } + } + + return oldSkill; + } + + /** + * Add or update a L2PcInstance skill in the character_skills table of the database.
    + * If newClassIndex > -1, the skill will be stored with that class index, not the current one. + */ + private void storeSkill(L2Skill newSkill, L2Skill oldSkill, int newClassIndex) + { + int classIndex = _classIndex; + + if (newClassIndex > -1) + { + classIndex = newClassIndex; + } + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + if ((oldSkill != null) && (newSkill != null)) + { + statement = con.prepareStatement(UPDATE_CHARACTER_SKILL_LEVEL); + statement.setInt(1, newSkill.getLevel()); + statement.setInt(2, oldSkill.getId()); + statement.setInt(3, getObjectId()); + statement.setInt(4, classIndex); + statement.execute(); + statement.close(); + } + else if (newSkill != null) + { + statement = con.prepareStatement(ADD_NEW_SKILL); + statement.setInt(1, getObjectId()); + statement.setInt(2, newSkill.getId()); + statement.setInt(3, newSkill.getLevel()); + statement.setInt(4, classIndex); + statement.execute(); + statement.close(); + } + else + { + _log.warning("could not store new skill. its NULL"); + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "Error could not store char skills: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Retrieve from the database all skills of this L2PcInstance and add them to _skills. + */ + private void restoreSkills() + { + Connection con = null; + try + { + // Retrieve all skills of this L2PcInstance from the database + con = L2DatabaseFactory.getInstance().getConnection(); + final PreparedStatement statement = con.prepareStatement(RESTORE_SKILLS_FOR_CHAR); + + statement.setInt(1, getObjectId()); + statement.setInt(2, getClassIndex()); + final ResultSet rset = statement.executeQuery(); + + // Go though the recordset of this SQL query + while (rset.next()) + { + final int id = rset.getInt("skill_id"); + final int level = rset.getInt("skill_level"); + + // Create a L2Skill object for each record + final L2Skill skill = SkillTable.getInstance().getInfo(id, level); + + if (skill == null) + { + _log.warning("Skipped null skill Id: " + id + " Level: " + level + " while restoring player skills for playerObjId: " + getObjectId()); + continue; + } + + // Add the L2Skill object to the L2Character _skills and its Func objects to the calculator set of the L2Character + super.addSkill(skill); + + if (Config.SKILL_CHECK_ENABLE && (!isGM() || Config.SKILL_CHECK_GM)) + { + if (!SkillTreesData.getInstance().isSkillAllowed(this, skill)) + { + Util.handleIllegalPlayerAction(this, "Player " + getName() + " has invalid skill " + skill.getName() + " (" + skill.getId() + "/" + skill.getLevel() + "), class:" + getTemplate().className, 1); + if (Config.SKILL_CHECK_REMOVE) + { + removeSkill(skill); + } + } + } + } + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not restore character " + this + " skills: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Retrieve from the database all skill effects of this L2PcInstance and add them to the player. + */ + public void restoreEffects() + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + ResultSet rset; + + statement = con.prepareStatement(RESTORE_SKILL_SAVE); + statement.setInt(1, getObjectId()); + statement.setInt(2, getClassIndex()); + rset = statement.executeQuery(); + + while (rset.next()) + { + int effectCount = rset.getInt("effect_count"); + int effectCurTime = rset.getInt("effect_cur_time"); + long reuseDelay = rset.getLong("reuse_delay"); + long systime = rset.getLong("systime"); + int restoreType = rset.getInt("restore_type"); + + final L2Skill skill = SkillTable.getInstance().getInfo(rset.getInt("skill_id"), rset.getInt("skill_level")); + if (skill == null) + { + continue; + } + + final long remainingTime = systime - System.currentTimeMillis(); + if (remainingTime > 10) + { + disableSkill(skill, remainingTime); + addTimeStamp(skill, reuseDelay, systime); + } + + /** + * Restore Type 1 The remaning skills lost effect upon logout but were still under a high reuse delay. + */ + if (restoreType > 0) + { + continue; + } + + /** + * Restore Type 0 These skill were still in effect on the character upon logout. Some of which were self casted and might still have had a long reuse delay which also is restored. + */ + if (skill.hasEffects()) + { + Env env = new Env(); + env.player = this; + env.target = this; + env.skill = skill; + L2Effect ef; + for (EffectTemplate et : skill.getEffectTemplates()) + { + ef = et.getEffect(env); + if (ef != null) + { + ef.setCount(effectCount); + ef.setFirstTime(effectCurTime); + ef.scheduleEffect(); + } + } + } + } + + rset.close(); + statement.close(); + + statement = con.prepareStatement(DELETE_SKILL_SAVE); + statement.setInt(1, getObjectId()); + statement.setInt(2, getClassIndex()); + statement.executeUpdate(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not restore " + this + " active effect data: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Retrieve from the database all Henna of this L2PcInstance, add them to _henna and calculate stats of the L2PcInstance.
    + *
    + */ + private void restoreHenna() + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_HENNAS); + statement.setInt(1, getObjectId()); + statement.setInt(2, getClassIndex()); + ResultSet rset = statement.executeQuery(); + + for (int i = 0; i < 3; i++) + { + _henna[i] = null; + } + + while (rset.next()) + { + int slot = rset.getInt("slot"); + + if ((slot < 1) || (slot > 3)) + { + continue; + } + + int symbol_id = rset.getInt("symbol_id"); + + L2HennaInstance sym = null; + + if (symbol_id != 0) + { + L2Henna tpl = HennaTable.getInstance().getTemplate(symbol_id); + + if (tpl != null) + { + sym = new L2HennaInstance(tpl); + _henna[slot - 1] = sym; + } + } + } + + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Failed restoing character " + this + " hennas.", e); + } + finally + { + L2DatabaseFactory.close(con); + } + + // Calculate Henna modifiers of this L2PcInstance + recalcHennaStats(); + } + + /** + * Return the number of Henna empty slot of the L2PcInstance.
    + *
    + */ + public int getHennaEmptySlots() + { + int totalSlots = 0; + if (getClassId().level() == 1) + { + totalSlots = 2; + } + else + { + totalSlots = 3; + } + + for (int i = 0; i < 3; i++) + { + if (_henna[i] != null) + { + totalSlots--; + } + } + + if (totalSlots <= 0) + { + return 0; + } + + return totalSlots; + } + + /** + * Remove a Henna of the L2PcInstance, save update in the character_hennas table of the database and send Server->Client HennaInfo/UserInfo packet to this L2PcInstance.
    + *
    + */ + public boolean removeHenna(int slot) + { + if ((slot < 1) || (slot > 3)) + { + return false; + } + + slot--; + + if (_henna[slot] == null) + { + return false; + } + + L2HennaInstance henna = _henna[slot]; + _henna[slot] = null; + + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA); + + statement.setInt(1, getObjectId()); + statement.setInt(2, slot + 1); + statement.setInt(3, getClassIndex()); + + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Failed remocing character henna.", e); + } + finally + { + L2DatabaseFactory.close(con); + } + + // Calculate Henna modifiers of this L2PcInstance + recalcHennaStats(); + + // Send Server->Client HennaInfo packet to this L2PcInstance + sendPacket(new HennaInfo(this)); + + // Send Server->Client UserInfo packet to this L2PcInstance + sendPacket(new UserInfo(this)); + sendPacket(new ExBrExtraUserInfo(this)); + // Add the recovered dyes to the player's inventory and notify them. + getInventory().addItem("Henna", henna.getItemIdDye(), henna.getAmountDyeRequire() / 2, this, null); + + reduceAdena("Henna", henna.getPrice() / 5, this, false); + + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S); + sm.addItemName(henna.getItemIdDye()); + sm.addItemNumber(henna.getAmountDyeRequire() / 2); + sendPacket(sm); + + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SYMBOL_DELETED)); + + return true; + } + + /** + * Add a Henna to the L2PcInstance, save update in the character_hennas table of the database and send Server->Client HennaInfo/UserInfo packet to this L2PcInstance.
    + *
    + */ + public boolean addHenna(L2HennaInstance henna) + { + for (int i = 0; i < 3; i++) + { + if (_henna[i] == null) + { + _henna[i] = henna; + + // Calculate Henna modifiers of this L2PcInstance + recalcHennaStats(); + + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(ADD_CHAR_HENNA); + + statement.setInt(1, getObjectId()); + statement.setInt(2, henna.getSymbolId()); + statement.setInt(3, i + 1); + statement.setInt(4, getClassIndex()); + + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Failed saving character henna.", e); + } + finally + { + L2DatabaseFactory.close(con); + } + + // Send Server->Client HennaInfo packet to this L2PcInstance + sendPacket(new HennaInfo(this)); + + // Send Server->Client UserInfo packet to this L2PcInstance + sendPacket(new UserInfo(this)); + sendPacket(new ExBrExtraUserInfo(this)); + + return true; + } + } + return false; + } + + /** + * Calculate Henna modifiers of this L2PcInstance.
    + *
    + */ + private void recalcHennaStats() + { + _hennaINT = 0; + _hennaSTR = 0; + _hennaCON = 0; + _hennaMEN = 0; + _hennaWIT = 0; + _hennaDEX = 0; + + for (int i = 0; i < 3; i++) + { + if (_henna[i] == null) + { + continue; + } + _hennaINT += _henna[i].getStatINT(); + _hennaSTR += _henna[i].getStatSTR(); + _hennaMEN += _henna[i].getStatMEM(); + _hennaCON += _henna[i].getStatCON(); + _hennaWIT += _henna[i].getStatWIT(); + _hennaDEX += _henna[i].getStatDEX(); + } + + if (_hennaINT > 5) + { + _hennaINT = 5; + } + if (_hennaSTR > 5) + { + _hennaSTR = 5; + } + if (_hennaMEN > 5) + { + _hennaMEN = 5; + } + if (_hennaCON > 5) + { + _hennaCON = 5; + } + if (_hennaWIT > 5) + { + _hennaWIT = 5; + } + if (_hennaDEX > 5) + { + _hennaDEX = 5; + } + } + + /** + * Return the Henna of this L2PcInstance corresponding to the selected slot.
    + *
    + */ + public L2HennaInstance getHenna(int slot) + { + if ((slot < 1) || (slot > 3)) + { + return null; + } + + return _henna[slot - 1]; + } + + /** + * Return the INT Henna modifier of this L2PcInstance.
    + *
    + */ + public int getHennaStatINT() + { + return _hennaINT; + } + + /** + * Return the STR Henna modifier of this L2PcInstance.
    + *
    + */ + public int getHennaStatSTR() + { + return _hennaSTR; + } + + /** + * Return the CON Henna modifier of this L2PcInstance.
    + *
    + */ + public int getHennaStatCON() + { + return _hennaCON; + } + + /** + * Return the MEN Henna modifier of this L2PcInstance.
    + *
    + */ + public int getHennaStatMEN() + { + return _hennaMEN; + } + + /** + * Return the WIT Henna modifier of this L2PcInstance.
    + *
    + */ + public int getHennaStatWIT() + { + return _hennaWIT; + } + + /** + * Return the DEX Henna modifier of this L2PcInstance.
    + *
    + */ + public int getHennaStatDEX() + { + return _hennaDEX; + } + + /** + * Return True if the L2PcInstance is autoAttackable.
    + *
    + * Actions :
    + *
    + *
  • Check if the attacker isn't the L2PcInstance Pet
  • Check if the attacker is L2MonsterInstance
  • If the attacker is a L2PcInstance, check if it is not in the same party
  • Check if the L2PcInstance has Karma
  • If the attacker is a L2PcInstance, check if it is not + * in the same siege clan (Attacker, Defender)

  • + *
    + */ + @Override + public boolean isAutoAttackable(L2Character attacker) + { + // Check if the attacker isn't the L2PcInstance Pet + if ((attacker == this) || (attacker == getPet())) + { + return false; + } + + // TODO: check for friendly mobs + // Check if the attacker is a L2MonsterInstance + if (attacker instanceof L2MonsterInstance) + { + return true; + } + + // Check if the attacker is not in the same party + if ((getParty() != null) && getParty().getPartyMembers().contains(attacker)) + { + return false; + } + + // Check if the attacker is in olympia and olympia start + if ((attacker instanceof L2PcInstance) && ((L2PcInstance) attacker).isInOlympiadMode()) + { + return isInOlympiadMode() && isOlympiadStart() && (((L2PcInstance) attacker).getOlympiadGameId() == getOlympiadGameId()); + } + + // Check if the attacker is in TvT and TvT is started + if (TvTEvent.isStarted() && TvTEvent.isPlayerParticipant(getObjectId())) + { + return true; + } + + // Check if the attacker is not in the same clan + if ((getClan() != null) && (attacker != null) && getClan().isMember(attacker.getObjectId())) + { + return false; + } + + if ((attacker instanceof L2Playable) && isInsideZone(ZONE_PEACE)) + { + return false; + } + + // Check if the L2PcInstance has Karma + if ((getKarma() > 0) || (getPvpFlag() > 0)) + { + return true; + } + + // Check if the attacker is a L2Playable + if (attacker instanceof L2Playable) + { + // Get L2PcInstance + L2PcInstance cha = attacker.getActingPlayer(); + + // is AutoAttackable if both players are in the same duel and the duel is still going on + if ((getDuelState() == Duel.DUELSTATE_DUELLING) && (getDuelId() == cha.getDuelId())) + { + return true; + } + // Check if the L2PcInstance is in an arena or a siege area + if (isInsideZone(ZONE_PVP) && cha.isInsideZone(ZONE_PVP)) + { + return true; + } + + if (getClan() != null) + { + Siege siege = SiegeManager.getInstance().getSiege(getX(), getY(), getZ()); + if (siege != null) + { + // Check if a siege is in progress and if attacker and the L2PcInstance aren't in the Defender clan + if (siege.checkIsDefender(cha.getClan()) && siege.checkIsDefender(getClan())) + { + return false; + } + + // Check if a siege is in progress and if attacker and the L2PcInstance aren't in the Attacker clan + if (siege.checkIsAttacker(cha.getClan()) && siege.checkIsAttacker(getClan())) + { + return false; + } + } + + // Check if clan is at war + if ((getClan() != null) && (cha.getClan() != null) && getClan().isAtWarWith(cha.getClanId()) && cha.getClan().isAtWarWith(getClanId()) && (getWantsPeace() == 0) && (cha.getWantsPeace() == 0) && !isAcademyMember()) + { + return true; + } + } + } + else if (attacker instanceof L2DefenderInstance) + { + if (getClan() != null) + { + Siege siege = SiegeManager.getInstance().getSiege(this); + return ((siege != null) && siege.checkIsAttacker(getClan())); + } + } + + return false; + } + + /** + * Check if the active L2Skill can be casted.
    + *
    + * Actions :
    + *
    + *
  • Check if the skill isn't toggle and is offensive
  • Check if the target is in the skill cast range
  • Check if the skill is Spoil type and if the target isn't already spoiled
  • Check if the caster owns enought consummed Item, enough HP and MP to cast the skill
  • + * Check if the caster isn't sitting
  • Check if all skills are enabled and this skill is enabled

  • + *
    + *
  • Check if the caster own the weapon needed

  • + *
    + *
  • Check if the skill is active

  • + *
    + *
  • Check if all casting conditions are completed

  • + *
    + *
  • Notify the AI with AI_INTENTION_CAST and target

  • + *
    + * @param skill The L2Skill to use + * @param forceUse used to force ATTACK on players + * @param dontMove used to prevent movement, if not in range + */ + @Override + public boolean useMagic(L2Skill skill, boolean forceUse, boolean dontMove) + { + // Check if the skill is active + if (skill.isPassive()) + { + // just ignore the passive skill request. why does the client send it anyway ?? + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + // ************************************* Check Casting in Progress ******************************************* + + // If a skill is currently being used, queue this one if this is not the same + if (isCastingNow()) + { + SkillDat currentSkill = getCurrentSkill(); + // Check if new skill different from current skill in progress + if ((currentSkill != null) && (skill.getId() == currentSkill.getSkillId())) + { + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + if (Config.DEBUG && (getQueuedSkill() != null)) + { + _log.info(getQueuedSkill().getSkill().getName() + " is already queued for " + getName() + "."); + } + + // Create a new SkillDat object and queue it in the player _queuedSkill + setQueuedSkill(skill, forceUse, dontMove); + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + setIsCastingNow(true); + // Create a new SkillDat object and set the player _currentSkill + // This is used mainly to save & queue the button presses, since L2Character has + // _lastSkillCast which could otherwise replace it + setCurrentSkill(skill, forceUse, dontMove); + + if (getQueuedSkill() != null) + { + setQueuedSkill(null, false, false); + } + + if (!checkUseMagicConditions(skill, forceUse, dontMove)) + { + setIsCastingNow(false); + return false; + } + + // Check if the target is correct and Notify the AI with AI_INTENTION_CAST and target + L2Object target = null; + + switch (skill.getTargetType()) + { + case TARGET_AURA: // AURA, SELF should be cast even if no target has been found + case TARGET_FRONT_AURA: + case TARGET_BEHIND_AURA: + case TARGET_GROUND: + case TARGET_SELF: + target = this; + break; + default: + + // Get the first target of the list + target = skill.getFirstOfTargetList(this); + break; + } + + // Notify the AI with AI_INTENTION_CAST and target + getAI().setIntention(CtrlIntention.AI_INTENTION_CAST, skill, target); + return true; + } + + private boolean checkUseMagicConditions(L2Skill skill, boolean forceUse, boolean dontMove) + { + L2SkillType sklType = skill.getSkillType(); + + // ************************************* Check Player State ******************************************* + + // Abnormal effects(ex : Stun, Sleep...) are checked in L2Character useMagic() + + if (isOutOfControl() || isParalyzed() || isStunned() || isSleeping()) + { + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + // Check if the player is dead + if (isDead()) + { + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + if (isFishing() && ((sklType != L2SkillType.PUMPING) && (sklType != L2SkillType.REELING) && (sklType != L2SkillType.FISHING))) + { + // Only fishing skills are available + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ONLY_FISHING_SKILLS_NOW)); + return false; + } + + if (inObserverMode()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE)); + abortCast(); + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + // Check if the caster is sitting + if (isSitting()) + { + // Send a System Message to the caster + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANT_MOVE_SITTING)); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + // Check if the skill type is TOGGLE + if (skill.isToggle()) + { + // Get effects of the skill + L2Effect effect = getFirstEffect(skill.getId()); + + if (effect != null) + { + effect.exit(); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + + // Check if the player uses "Fake Death" skill + // Note: do not check this before TOGGLE reset + if (isFakeDeath()) + { + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + // ************************************* Check Target ******************************************* + // Create and set a L2Object containing the target of the skill + L2Object target = null; + SkillTargetType sklTargetType = skill.getTargetType(); + Point3D worldPosition = getCurrentSkillWorldPosition(); + + if ((sklTargetType == SkillTargetType.TARGET_GROUND) && (worldPosition == null)) + { + _log.info("WorldPosition is null for skill: " + skill.getName() + ", player: " + getName() + "."); + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + switch (sklTargetType) + { + // Target the player if skill type is AURA, PARTY, CLAN or SELF + case TARGET_AURA: + case TARGET_FRONT_AURA: + case TARGET_BEHIND_AURA: + case TARGET_PARTY: + case TARGET_ALLY: + case TARGET_CLAN: + case TARGET_PARTY_CLAN: + case TARGET_GROUND: + case TARGET_SELF: + case TARGET_AREA_SUMMON: + target = this; + break; + case TARGET_PET: + case TARGET_SUMMON: + target = getPet(); + break; + default: + target = getTarget(); + break; + } + + // Check the validity of the target + if (target == null) + { + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + // skills can be used on Walls and Doors only during siege + if (target instanceof L2DoorInstance) + { + boolean isCastle = ((((L2DoorInstance) target).getCastle() != null) && (((L2DoorInstance) target).getCastle().getCastleId() > 0) && ((L2DoorInstance) target).getCastle().getSiege().getIsInProgress()); + boolean isFort = ((((L2DoorInstance) target).getFort() != null) && (((L2DoorInstance) target).getFort().getFortId() > 0) && ((L2DoorInstance) target).getFort().getSiege().getIsInProgress() && !((L2DoorInstance) target).getIsCommanderDoor()); + if ((!isCastle && !isFort) && (((L2DoorInstance) target).isUnlockable() && (skill.getSkillType() != L2SkillType.UNLOCK))) + { + return false; + } + } + + // Are the target and the player in the same duel? + if (isInDuel()) + { + // Get L2PcInstance + if (target instanceof L2Playable) + { + // Get L2PcInstance + L2PcInstance cha = target.getActingPlayer(); + if (cha.getDuelId() != getDuelId()) + { + sendMessage("You cannot do this while duelling."); + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + } + + // ************************************* Check skill availability ******************************************* + + // Check if it's ok to summon + // siege golem (13), Wild Hog Cannon (299), Swoop Cannon (448) + if (((skill.getId() == 13) || (skill.getId() == 299) || (skill.getId() == 448)) && ((!SiegeManager.getInstance().checkIfOkToSummon(this, false) && !FortSiegeManager.getInstance().checkIfOkToSummon(this, false)) || (SevenSigns.getInstance().checkSummonConditions(this)))) + { + return false; + } + + // Check if this skill is enabled (ex : reuse time) + if (isSkillDisabled(skill)) + { + SystemMessage sm = null; + + if (_reuseTimeStamps.containsKey(skill.getReuseHashCode())) + { + int remainingTime = (int) (_reuseTimeStamps.get(skill.getReuseHashCode()).getRemaining() / 1000); + int hours = remainingTime / 3600; + int minutes = (remainingTime % 3600) / 60; + int seconds = (remainingTime % 60); + if (hours > 0) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S2_HOURS_S3_MINUTES_S4_SECONDS_REMAINING_FOR_REUSE_S1); + sm.addSkillName(skill); + sm.addNumber(hours); + sm.addNumber(minutes); + } + else if (minutes > 0) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S2_MINUTES_S3_SECONDS_REMAINING_FOR_REUSE_S1); + sm.addSkillName(skill); + sm.addNumber(minutes); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S2_SECONDS_REMAINING_FOR_REUSE_S1); + sm.addSkillName(skill); + } + + sm.addNumber(seconds); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_PREPARED_FOR_REUSE); + sm.addSkillName(skill); + } + + sendPacket(sm); + return false; + } + + // ************************************* Check Consumables ******************************************* + + // Check if spell consumes a Soul + if (skill.getSoulConsumeCount() > 0) + { + if (getSouls() < skill.getSoulConsumeCount()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.THERE_IS_NOT_ENOUGH_SOUL)); + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + // ************************************* Check casting conditions ******************************************* + + // Check if all casting conditions are completed + if (!skill.checkCondition(this, target, false)) + { + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + // ************************************* Check Skill Type ******************************************* + + // Check if this is offensive magic skill + if (skill.isOffensive()) + { + if ((isInsidePeaceZone(this, target)) && !getAccessLevel().allowPeaceAttack()) + { + // If L2Character or target is in a peace zone, send a system message TARGET_IN_PEACEZONE a Server->Client packet ActionFailed + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IN_PEACEZONE)); + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + if (isInOlympiadMode() && !isOlympiadStart()) + { + // if L2PcInstance is in Olympia and the match isn't already start, send a Server->Client packet ActionFailed + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + if ((target.getActingPlayer() != null) && (getSiegeState() > 0) && isInsideZone(L2Character.ZONE_SIEGE) && (target.getActingPlayer().getSiegeState() == getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getSiegeSide())) + { + // + if (TerritoryWarManager.getInstance().isTWInProgress()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_ATTACK_A_MEMBER_OF_THE_SAME_TERRITORY)); + } + else + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FORCED_ATTACK_IS_IMPOSSIBLE_AGAINST_SIEGE_SIDE_TEMPORARY_ALLIED_MEMBERS)); + } + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + // Check if the target is attackable + if (!target.isAttackable() && !getAccessLevel().allowPeaceAttack()) + { + // If target is not attackable, send a Server->Client packet ActionFailed + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + // Check for Event Mob's + if ((target instanceof L2EventMonsterInstance) && ((L2EventMonsterInstance) target).eventSkillAttackBlocked()) + { + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + // Check if a Forced ATTACK is in progress on non-attackable target + if (!target.isAutoAttackable(this) && !forceUse) + { + switch (sklTargetType) + { + case TARGET_AURA: + case TARGET_FRONT_AURA: + case TARGET_BEHIND_AURA: + case TARGET_CLAN: + case TARGET_PARTY_CLAN: + case TARGET_ALLY: + case TARGET_PARTY: + case TARGET_SELF: + case TARGET_GROUND: + case TARGET_AREA_SUMMON: + break; + default: // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + + // Check if the target is in the skill cast range + if (dontMove) + { + // Calculate the distance between the L2PcInstance and the target + if (sklTargetType == SkillTargetType.TARGET_GROUND) + { + if (!isInsideRadius(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(), skill.getCastRange() + getTemplate().collisionRadius, false, false)) + { + // Send a System Message to the caster + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_TOO_FAR)); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + else if ((skill.getCastRange() > 0) && !isInsideRadius(target, skill.getCastRange() + getTemplate().collisionRadius, false, false)) + { + // Send a System Message to the caster + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_TOO_FAR)); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + } + + if (skill.getSkillType() == L2SkillType.INSTANT_JUMP) + { + // You cannot jump while rooted right ;) + if (isMovementDisabled()) + { + // Sends message that skill cannot be used... + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED); + sm.addSkillName(skill.getId()); + sendPacket(sm); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + + return false; + } + // And this skill cannot be used in peace zone, not even on NPCs! + if (isInsideZone(L2Character.ZONE_PEACE)) + { + // Sends a sys msg to client + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IN_PEACEZONE)); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + + return false; + } + + } + // Check if the skill is defensive + if (!skill.isOffensive() && (target instanceof L2MonsterInstance) && !forceUse && !skill.isNeutral()) + { + // check if the target is a monster and if force attack is set.. if not then we don't want to cast. + switch (sklTargetType) + { + case TARGET_PET: + case TARGET_SUMMON: + case TARGET_AURA: + case TARGET_FRONT_AURA: + case TARGET_BEHIND_AURA: + case TARGET_CLAN: + case TARGET_PARTY_CLAN: + case TARGET_SELF: + case TARGET_PARTY: + case TARGET_ALLY: + case TARGET_CORPSE_MOB: + case TARGET_AREA_CORPSE_MOB: + case TARGET_GROUND: + break; + default: + { + switch (sklType) + { + case BEAST_FEED: + case DELUXE_KEY_UNLOCK: + case UNLOCK: + break; + default: + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + break; + } + } + } + + // Check if the skill is Spoil type and if the target isn't already spoiled + if (sklType == L2SkillType.SPOIL) + { + if (!(target instanceof L2MonsterInstance)) + { + // Send a System Message to the L2PcInstance + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.INCORRECT_TARGET)); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + + // Check if the skill is Sweep type and if conditions not apply + if ((sklType == L2SkillType.SWEEP) && (target instanceof L2Attackable)) + { + int spoilerId = ((L2Attackable) target).getIsSpoiledBy(); + + if (((L2Attackable) target).isDead()) + { + if (!((L2Attackable) target).isSpoil()) + { + // Send a System Message to the L2PcInstance + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SWEEPER_FAILED_TARGET_NOT_SPOILED)); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + + if ((getObjectId() != spoilerId) && !isInLooterParty(spoilerId)) + { + // Send a System Message to the L2PcInstance + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SWEEP_NOT_ALLOWED)); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + } + + // Check if the skill is Drain Soul (Soul Crystals) and if the target is a MOB + if (sklType == L2SkillType.DRAIN_SOUL) + { + if (!(target instanceof L2MonsterInstance)) + { + // Send a System Message to the L2PcInstance + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.INCORRECT_TARGET)); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + + // Check if this is a Pvp skill and target isn't a non-flagged/non-karma player + switch (sklTargetType) + { + case TARGET_PARTY: + case TARGET_ALLY: // For such skills, checkPvpSkill() is called from L2Skill.getTargetList() + case TARGET_CLAN: // For such skills, checkPvpSkill() is called from L2Skill.getTargetList() + case TARGET_PARTY_CLAN: // For such skills, checkPvpSkill() is called from L2Skill.getTargetList() + case TARGET_AURA: + case TARGET_FRONT_AURA: + case TARGET_BEHIND_AURA: + case TARGET_GROUND: + case TARGET_SELF: + break; + default: + if (!checkPvpSkill(target, skill) && !getAccessLevel().allowPeaceAttack()) + { + // Send a System Message to the L2PcInstance + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT)); + + // Send a Server->Client packet ActionFailed to the L2PcInstance + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + + // TODO: Unhardcode skillId 844 which is the outpost construct skill + if (((sklTargetType == SkillTargetType.TARGET_HOLY) && !checkIfOkToCastSealOfRule(CastleManager.getInstance().getCastle(this), false, skill)) || ((sklTargetType == SkillTargetType.TARGET_FLAGPOLE) && !checkIfOkToCastFlagDisplay(FortManager.getInstance().getFort(this), false, skill)) || ((sklType == L2SkillType.SIEGEFLAG) && !L2SkillSiegeFlag.checkIfOkToPlaceFlag(this, false, skill.getId() == 844)) || ((sklType == L2SkillType.STRSIEGEASSAULT) && !checkIfOkToUseStriderSiegeAssault()) || ((sklType == L2SkillType.SUMMON_FRIEND) && !(checkSummonerStatus(this) && checkSummonTargetStatus(target, this)))) + { + sendPacket(ActionFailed.STATIC_PACKET); + abortCast(); + return false; + } + + // GeoData Los Check here + if (skill.getCastRange() > 0) + { + if (sklTargetType == SkillTargetType.TARGET_GROUND) + { + if (!GeoData.getInstance().canSeeTarget(this, worldPosition)) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANT_SEE_TARGET)); + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + else if (!GeoData.getInstance().canSeeTarget(this, target)) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANT_SEE_TARGET)); + sendPacket(ActionFailed.STATIC_PACKET); + return false; + } + } + // finally, after passing all conditions + return true; + } + + public boolean checkIfOkToUseStriderSiegeAssault() + { + Castle castle = CastleManager.getInstance().getCastle(this); + Fort fort = FortManager.getInstance().getFort(this); + + if ((castle == null) && (fort == null)) + { + return false; + } + + if (castle != null) + { + return checkIfOkToUseStriderSiegeAssault(castle); + } + return checkIfOkToUseStriderSiegeAssault(fort); + + } + + public boolean checkIfOkToUseStriderSiegeAssault(Castle castle) + { + String text = ""; + + if ((castle == null) || (castle.getCastleId() <= 0)) + { + text = "You must be on castle ground to use strider siege assault"; + } + else if (!castle.getSiege().getIsInProgress()) + { + text = "You can only use strider siege assault during a siege."; + } + else if (!(getTarget() instanceof L2DoorInstance)) + { + text = "You can only use strider siege assault on doors and walls."; + } + else if (!isRidingStrider()) + { + text = "You can only use strider siege assault when on strider."; + } + else + { + return true; + } + + sendMessage(text); + + return false; + } + + public boolean checkIfOkToUseStriderSiegeAssault(Fort fort) + { + String text = ""; + + if ((fort == null) || (fort.getFortId() <= 0)) + { + text = "You must be on fort ground to use strider siege assault"; + } + else if (!fort.getSiege().getIsInProgress()) + { + text = "You can only use strider siege assault during a siege."; + } + else if (!(getTarget() instanceof L2DoorInstance)) + { + text = "You can only use strider siege assault on doors and walls."; + } + else if (!isRidingStrider()) + { + text = "You can only use strider siege assault when on strider."; + } + else + { + return true; + } + + sendMessage(text); + + return false; + } + + public boolean checkIfOkToCastSealOfRule(Castle castle, boolean isCheckOnly, L2Skill skill) + { + SystemMessage sm; + + if ((castle == null) || (castle.getCastleId() <= 0)) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED); + sm.addSkillName(skill); + } + else if (!castle.getArtefacts().contains(getTarget())) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.INCORRECT_TARGET); + } + else if (!castle.getSiege().getIsInProgress()) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED); + sm.addSkillName(skill); + } + else if (!Util.checkIfInRange(200, this, getTarget(), true)) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.DIST_TOO_FAR_CASTING_STOPPED); + } + else if (castle.getSiege().getAttackerClan(getClan()) == null) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED); + sm.addSkillName(skill); + } + else + { + if (!isCheckOnly) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.OPPONENT_STARTED_ENGRAVING); + castle.getSiege().announceToPlayer(sm, false); + } + return true; + } + + sendPacket(sm); + return false; + } + + public boolean checkIfOkToCastFlagDisplay(Fort fort, boolean isCheckOnly, L2Skill skill) + { + SystemMessage sm; + + if ((fort == null) || (fort.getFortId() <= 0)) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED); + sm.addSkillName(skill); + } + else if (fort.getFlagPole() != getTarget()) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.INCORRECT_TARGET); + } + else if (!fort.getSiege().getIsInProgress()) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED); + sm.addSkillName(skill); + } + else if (!Util.checkIfInRange(200, this, getTarget(), true)) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.DIST_TOO_FAR_CASTING_STOPPED); + } + else if (fort.getSiege().getAttackerClan(getClan()) == null) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED); + sm.addSkillName(skill); + } + else + { + if (!isCheckOnly) + { + fort.getSiege().announceToPlayer(SystemMessage.getSystemMessage(SystemMessageId.S1_TRYING_RAISE_FLAG), getClan().getName()); + } + return true; + } + + sendPacket(sm); + return false; + } + + public boolean isInLooterParty(int LooterId) + { + L2PcInstance looter = L2World.getInstance().getPlayer(LooterId); + + // if L2PcInstance is in a CommandChannel + if (isInParty() && getParty().isInCommandChannel() && (looter != null)) + { + return getParty().getCommandChannel().getMembers().contains(looter); + } + + if (isInParty() && (looter != null)) + { + return getParty().getPartyMembers().contains(looter); + } + + return false; + } + + /** + * Check if the requested casting is a Pc->Pc skill cast and if it's a valid pvp condition + * @param target L2Object instance containing the target + * @param skill L2Skill instance with the skill being casted + * @return False if the skill is a pvpSkill and target is not a valid pvp target + */ + public boolean checkPvpSkill(L2Object target, L2Skill skill) + { + return checkPvpSkill(target, skill, false); + } + + /** + * Check if the requested casting is a Pc->Pc skill cast and if it's a valid pvp condition + * @param target L2Object instance containing the target + * @param skill L2Skill instance with the skill being casted + * @param srcIsSummon is L2Summon - caster? + * @return False if the skill is a pvpSkill and target is not a valid pvp target + */ + public boolean checkPvpSkill(L2Object target, L2Skill skill, boolean srcIsSummon) + { + // check for PC->PC Pvp status + if (target instanceof L2Summon) + { + target = target.getActingPlayer(); + } + if ((target != null) && // target not null and + (target != this) && // target is not self and + (target instanceof L2PcInstance) && // target is L2PcInstance and + !(isInDuel() && (((L2PcInstance) target).getDuelId() == getDuelId())) && // self is not in a duel and attacking opponent + !isInsideZone(ZONE_PVP) && // Pc is not in PvP zone + !((L2PcInstance) target).isInsideZone(ZONE_PVP) // target is not in PvP zone + ) + { + SkillDat skilldat = getCurrentSkill(); + SkillDat skilldatpet = getCurrentPetSkill(); + if (skill.isPvpSkill()) // pvp skill + { + if ((getClan() != null) && (((L2PcInstance) target).getClan() != null)) + { + if (getClan().isAtWarWith(((L2PcInstance) target).getClan().getClanId()) && ((L2PcInstance) target).getClan().isAtWarWith(getClan().getClanId())) + { + return true; // in clan war player can attack whites even with sleep etc. + } + } + if ((((L2PcInstance) target).getPvpFlag() == 0) && // target's pvp flag is not set and + (((L2PcInstance) target).getKarma() == 0 // target has no karma + )) + { + return false; + } + } + else if (((skilldat != null) && !skilldat.isCtrlPressed() && skill.isOffensive() && !srcIsSummon) || ((skilldatpet != null) && !skilldatpet.isCtrlPressed() && skill.isOffensive() && srcIsSummon)) + { + if ((getClan() != null) && (((L2PcInstance) target).getClan() != null)) + { + if (getClan().isAtWarWith(((L2PcInstance) target).getClan().getClanId()) && ((L2PcInstance) target).getClan().isAtWarWith(getClan().getClanId())) + { + return true; // in clan war player can attack whites even without ctrl + } + } + if ((((L2PcInstance) target).getPvpFlag() == 0) && // target's pvp flag is not set and + (((L2PcInstance) target).getKarma() == 0 // target has no karma + )) + { + return false; + } + } + } + + return true; + } + + /** + * Return True if the L2PcInstance is a Mage.
    + *
    + */ + public boolean isMageClass() + { + return getClassId().isMage(); + } + + public boolean isMounted() + { + return _mountType > 0; + } + + /** + * Set the type of Pet mounted (0 : none, 1 : Stridder, 2 : Wyvern) and send a Server->Client packet InventoryUpdate to the L2PcInstance.
    + *
    + */ + public boolean checkLandingState() + { + // Check if char is in a no landing zone + if (isInsideZone(ZONE_NOLANDING)) + { + return true; + } + else + // if this is a castle that is currently being sieged, and the rider is NOT a castle owner + // he cannot land. + // castle owner is the leader of the clan that owns the castle where the pc is + if (isInsideZone(ZONE_SIEGE) && !((getClan() != null) && (CastleManager.getInstance().getCastle(this) == CastleManager.getInstance().getCastleByOwner(getClan())) && (this == getClan().getLeader().getPlayerInstance()))) + { + return true; + } + + return false; + } + + // returns false if the change of mount type fails. + public boolean setMount(int npcId, int npcLevel, int mountType) + { + switch (mountType) + { + case 0: + setIsFlying(false); + /* + * not used any more setIsRidingFenrirWolf(false); setIsRidingWFenrirWolf(false); setIsRidingGreatSnowWolf(false); + */ + setIsRidingStrider(false); + break; // Dismounted + case 1: + setIsRidingStrider(true); + if (isNoble()) + { + L2Skill striderAssaultSkill = SkillTable.FrequentSkill.STRIDER_SIEGE_ASSAULT.getSkill(); + addSkill(striderAssaultSkill, false); // not saved to DB + } + break; + case 2: + setIsFlying(true); + break; // Flying Wyvern + case 3: + /* + * not used any more switch (npcId) { case 16041: setIsRidingFenrirWolf(true); break; case 16042: setIsRidingWFenrirWolf(true); break; case 16037: setIsRidingGreatSnowWolf(true); break; } + */ + break; + } + + _mountType = mountType; + _mountNpcId = npcId; + _mountLevel = npcLevel; + + return true; + } + + /** + * Return the type of Pet mounted (0 : none, 1 : Strider, 2 : Wyvern, 3: Wolf).
    + *
    + */ + public int getMountType() + { + return _mountType; + } + + @Override + public final void stopAllEffects() + { + super.stopAllEffects(); + updateAndBroadcastStatus(2); + } + + @Override + public final void stopAllEffectsExceptThoseThatLastThroughDeath() + { + super.stopAllEffectsExceptThoseThatLastThroughDeath(); + updateAndBroadcastStatus(2); + } + + /** + * Stop all toggle-type effects + */ + public final void stopAllToggles() + { + _effects.stopAllToggles(); + } + + public final void stopCubics() + { + if (getCubics() != null) + { + boolean removed = false; + for (L2CubicInstance cubic : getCubics().values()) + { + cubic.stopAction(); + delCubic(cubic.getId()); + removed = true; + } + if (removed) + { + broadcastUserInfo(); + } + } + } + + public final void stopCubicsByOthers() + { + if (getCubics() != null) + { + boolean removed = false; + for (L2CubicInstance cubic : getCubics().values()) + { + if (cubic.givenByOther()) + { + cubic.stopAction(); + delCubic(cubic.getId()); + removed = true; + } + } + if (removed) + { + broadcastUserInfo(); + } + } + } + + /** + * Send a Server->Client packet UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers.
    + *
    + * Concept :
    + *
    + * Others L2PcInstance in the detection area of the L2PcInstance are identified in _knownPlayers. In order to inform other players of this L2PcInstance state modifications, server just need to go through _knownPlayers to send Server->Client Packet
    + *
    + * Actions :
    + *
    + *
  • Send a Server->Client packet UserInfo to this L2PcInstance (Public and Private Data)
  • Send a Server->Client packet CharInfo to all L2PcInstance in _KnownPlayers of the L2PcInstance (Public data only)

  • + *
    + * Caution : DON'T SEND UserInfo packet to other players instead of CharInfo packet. Indeed, UserInfo packet contains PRIVATE DATA as MaxHP, STR, DEX...
    + *
    + */ + @Override + public void updateAbnormalEffect() + { + broadcastUserInfo(); + } + + /** + * Disable the Inventory and create a new task to enable it after 1.5s.
    + *
    + */ + public void tempInventoryDisable() + { + _inventoryDisable = true; + + ThreadPoolManager.getInstance().scheduleGeneral(new InventoryEnable(), 1500); + } + + /** + * Return True if the Inventory is disabled.
    + *
    + */ + public boolean isInventoryDisabled() + { + return _inventoryDisable; + } + + protected class InventoryEnable implements Runnable + { + @Override + public void run() + { + _inventoryDisable = false; + } + } + + public Map getCubics() + { + return _cubics; + } + + /** + * Add a L2CubicInstance to the L2PcInstance _cubics.
    + *
    + */ + public void addCubic(int id, int level, double matk, int activationtime, int activationchance, int maxcount, int totalLifetime, boolean givenByOther) + { + if (Config.DEBUG) + { + _log.info("L2PcInstance(" + getName() + "): addCubic(" + id + "|" + level + "|" + matk + ")"); + } + L2CubicInstance cubic = new L2CubicInstance(this, id, level, (int) matk, activationtime, activationchance, maxcount, totalLifetime, givenByOther); + + _cubics.put(id, cubic); + } + + /** + * Remove a L2CubicInstance from the L2PcInstance _cubics.
    + *
    + */ + public void delCubic(int id) + { + _cubics.remove(id); + } + + /** + * Return the L2CubicInstance corresponding to the Identifier of the L2PcInstance _cubics.
    + *
    + */ + public L2CubicInstance getCubic(int id) + { + return _cubics.get(id); + } + + /** + * Return the modifier corresponding to the Enchant Effect of the Active Weapon (Min : 127).
    + *
    + */ + public int getEnchantEffect() + { + L2ItemInstance wpn = getActiveWeaponInstance(); + + if (wpn == null) + { + return 0; + } + + return Math.min(127, wpn.getEnchantLevel()); + } + + /** + * Set the _lastFolkNpc of the L2PcInstance corresponding to the last Folk wich one the player talked.
    + *
    + */ + public void setLastFolkNPC(L2Npc folkNpc) + { + _lastFolkNpc = folkNpc; + } + + /** + * Return the _lastFolkNpc of the L2PcInstance corresponding to the last Folk wich one the player talked.
    + *
    + */ + public L2Npc getLastFolkNPC() + { + return _lastFolkNpc; + } + + /** + * Return True if L2PcInstance is a participant in the Festival of Darkness.
    + *
    + */ + public boolean isFestivalParticipant() + { + return SevenSignsFestival.getInstance().isParticipant(this); + } + + public void addAutoSoulShot(int itemId) + { + _activeSoulShots.add(itemId); + } + + public boolean removeAutoSoulShot(int itemId) + { + return _activeSoulShots.remove(itemId); + } + + public Set getAutoSoulShot() + { + return _activeSoulShots; + } + + public void rechargeAutoSoulShot(boolean physical, boolean magic, boolean summon) + { + L2ItemInstance item; + IItemHandler handler; + + if ((_activeSoulShots == null) || _activeSoulShots.isEmpty()) + { + return; + } + + try + { + for (int itemId : _activeSoulShots) + { + item = getInventory().getItemByItemId(itemId); + + if (item != null) + { + if (magic) + { + if (!summon) + { + switch (itemId) + { + case 2509: + case 2510: + case 2511: + case 2512: + case 2513: + case 2514: + case 3947: + case 3948: + case 3949: + case 3950: + case 3951: + case 3952: + case 5790: + case 22072: + case 22073: + case 22074: + case 22075: + case 22076: + case 22077: + case 22078: + case 22079: + case 22080: + case 22081: + handler = ItemHandler.getInstance().getItemHandler(item.getEtcItem()); + + if (handler != null) + { + handler.useItem(this, item, false); + } + + break; + } + } + else + { + switch (itemId) + { + case 6646: + case 6647: + case 20333: + case 20334: + handler = ItemHandler.getInstance().getItemHandler(item.getEtcItem()); + + if (handler != null) + { + handler.useItem(this, item, false); + } + break; + } + } + } + + if (physical) + { + if (!summon) + { + switch (itemId) + { + case 1463: + case 1464: + case 1465: + case 1466: + case 1467: + case 1835: + case 5789: + case 22082: + case 22083: + case 22084: + case 22085: + case 22086: + + handler = ItemHandler.getInstance().getItemHandler(item.getEtcItem()); + + if (handler != null) + { + handler.useItem(this, item, false); + } + + break; + } + } + else + { + if ((itemId == 6645) || (itemId == 20332)) + { + handler = ItemHandler.getInstance().getItemHandler(item.getEtcItem()); + + if (handler != null) + { + handler.useItem(this, item, false); + } + } + } + } + } + else + { + removeAutoSoulShot(itemId); + } + } + } + catch (NullPointerException npe) + { + _log.log(Level.WARNING, toString(), npe); + } + } + + /** + * Cancel autoshot for all shots matching crystaltype
    + * {@link L2Item#getCrystalType()} + * @param crystalType int type to disable + */ + public void disableAutoShotByCrystalType(int crystalType) + { + for (int itemId : _activeSoulShots) + { + if (ItemTable.getInstance().getTemplate(itemId).getCrystalType() == crystalType) + { + disableAutoShot(itemId); + } + } + } + + /** + * Cancel autoshot use for shot itemId + * @param itemId int id to disable + * @return true if canceled. + */ + public boolean disableAutoShot(int itemId) + { + if (_activeSoulShots.contains(itemId)) + { + removeAutoSoulShot(itemId); + sendPacket(new ExAutoSoulShot(itemId, 0)); + + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.AUTO_USE_OF_S1_CANCELLED); + sm.addString(ItemTable.getInstance().getTemplate(itemId).getName()); + sendPacket(sm); + return true; + } + return false; + } + + /** + * Cancel all autoshots for player + */ + public void disableAutoShotsAll() + { + for (int itemId : _activeSoulShots) + { + sendPacket(new ExAutoSoulShot(itemId, 0)); + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.AUTO_USE_OF_S1_CANCELLED); + sm.addString(ItemTable.getInstance().getTemplate(itemId).getName()); + sendPacket(sm); + } + _activeSoulShots.clear(); + } + + private ScheduledFuture _taskWarnUserTakeBreak; + + protected class WarnUserTakeBreak implements Runnable + { + @Override + public void run() + { + if (isOnline()) + { + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.PLAYING_FOR_LONG_TIME); + L2PcInstance.this.sendPacket(msg); + } + else + { + stopWarnUserTakeBreak(); + } + } + } + + protected class RentPetTask implements Runnable + { + @Override + public void run() + { + stopRentPet(); + } + } + + protected class WaterTask implements Runnable + { + @Override + public void run() + { + double reduceHp = getMaxHp() / 100.0; + + if (reduceHp < 1) + { + reduceHp = 1; + } + + reduceCurrentHp(reduceHp, L2PcInstance.this, false, false, null); + // reduced hp, becouse not rest + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DROWN_DAMAGE_S1); + sm.addNumber((int) reduceHp); + sendPacket(sm); + + } + } + + private class LookingForFishTask implements Runnable + { + boolean _isNoob, _isUpperGrade; + int _fishType, _fishGutsCheck; + long _endTaskTime; + + protected LookingForFishTask(int fishWaitTime, int fishGutsCheck, int fishType, boolean isNoob, boolean isUpperGrade) + { + _fishGutsCheck = fishGutsCheck; + _endTaskTime = System.currentTimeMillis() + fishWaitTime + 10000; + _fishType = fishType; + _isNoob = isNoob; + _isUpperGrade = isUpperGrade; + } + + @Override + public void run() + { + if (System.currentTimeMillis() >= _endTaskTime) + { + endFishing(false); + return; + } + if (_fishType == -1) + { + return; + } + int check = Rnd.get(1000); + if (_fishGutsCheck > check) + { + stopLookingForFishTask(); + startFishCombat(_isNoob, _isUpperGrade); + } + } + + } + + public int getClanPrivileges() + { + return _clanPrivileges; + } + + public void setClanPrivileges(int n) + { + _clanPrivileges = n; + } + + // baron etc + public void setPledgeClass(int classId) + { + _pledgeClass = classId; + checkItemRestriction(); + } + + public int getPledgeClass() + { + return _pledgeClass; + } + + public void setPledgeType(int typeId) + { + _pledgeType = typeId; + } + + public int getPledgeType() + { + return _pledgeType; + } + + public int getApprentice() + { + return _apprentice; + } + + public void setApprentice(int apprentice_id) + { + _apprentice = apprentice_id; + } + + public int getSponsor() + { + return _sponsor; + } + + public void setSponsor(int sponsor_id) + { + _sponsor = sponsor_id; + } + + public int getBookMarkSlot() + { + return _bookmarkslot; + } + + public void setBookMarkSlot(int slot) + { + _bookmarkslot = slot; + sendPacket(new ExGetBookMarkInfoPacket(this)); + } + + @Override + public void sendMessage(String message) + { + sendPacket(SystemMessage.sendString(message)); + } + + public void enterObserverMode(int x, int y, int z) + { + _lastX = getX(); + _lastY = getY(); + _lastZ = getZ(); + + _observerMode = true; + setTarget(null); + setIsParalyzed(true); + startParalyze(); + setIsInvul(true); + getAppearance().setInvisible(); + // sendPacket(new GMHide(1)); + sendPacket(new ObservationMode(x, y, z)); + getKnownList().removeAllKnownObjects(); // reinit knownlist + setXYZ(x, y, z); + broadcastUserInfo(); + } + + public void setLastCords(int x, int y, int z) + { + _lastX = getX(); + _lastY = getY(); + _lastZ = getZ(); + } + + public void enterOlympiadObserverMode(Location loc, int id) + { + if (getPet() != null) + { + getPet().unSummon(this); + } + + if (!getCubics().isEmpty()) + { + for (L2CubicInstance cubic : getCubics().values()) + { + cubic.stopAction(); + cubic.cancelDisappear(); + } + + getCubics().clear(); + } + + if (getParty() != null) + { + getParty().removePartyMember(this); + } + + _olympiadGameId = id; + if (isSitting()) + { + standUp(); + } + if (!_observerMode) + { + _lastX = getX(); + _lastY = getY(); + _lastZ = getZ(); + } + + _observerMode = true; + setTarget(null); + setIsInvul(true); + getAppearance().setInvisible(); + // sendPacket(new GMHide(1)); + teleToLocation(loc, false); + sendPacket(new ExOlympiadMode(3)); + + broadcastUserInfo(); + } + + public void leaveObserverMode() + { + setTarget(null); + getKnownList().removeAllKnownObjects(); // reinit knownlist + setXYZ(_lastX, _lastY, _lastZ); + setIsParalyzed(false); + stopParalyze(false); + // sendPacket(new GMHide(0)); + if (!AdminCommandAccessRights.getInstance().hasAccess("admin_invis", getAccessLevel())) + { + getAppearance().setVisible(); + } + if (!AdminCommandAccessRights.getInstance().hasAccess("admin_invul", getAccessLevel())) + { + setIsInvul(false); + } + if (getAI() != null) + { + getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); + } + + setFalling(); // prevent receive falling damage + _observerMode = false; + setLastCords(0, 0, 0); + sendPacket(new ObservationReturn(this)); + broadcastUserInfo(); + } + + public void leaveOlympiadObserverMode() + { + if (_olympiadGameId == -1) + { + return; + } + _olympiadGameId = -1; + _observerMode = false; + setTarget(null); + sendPacket(new ExOlympiadMode(0)); + teleToLocation(_lastX, _lastY, _lastZ, true); + // sendPacket(new GMHide(0)); + if (!AdminCommandAccessRights.getInstance().hasAccess("admin_invis", getAccessLevel())) + { + getAppearance().setVisible(); + } + if (!AdminCommandAccessRights.getInstance().hasAccess("admin_invul", getAccessLevel())) + { + setIsInvul(false); + } + if (getAI() != null) + { + getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); + } + setLastCords(0, 0, 0); + broadcastUserInfo(); + } + + public void setOlympiadSide(int i) + { + _olympiadSide = i; + } + + public int getOlympiadSide() + { + return _olympiadSide; + } + + public void setOlympiadGameId(int id) + { + _olympiadGameId = id; + } + + public int getOlympiadGameId() + { + return _olympiadGameId; + } + + public int getLastX() + { + return _lastX; + } + + public int getLastY() + { + return _lastY; + } + + public int getLastZ() + { + return _lastZ; + } + + public boolean inObserverMode() + { + return _observerMode; + } + + public int getTeleMode() + { + return _telemode; + } + + public void setTeleMode(int mode) + { + _telemode = mode; + } + + public void setLoto(int i, int val) + { + _loto[i] = val; + } + + public int getLoto(int i) + { + return _loto[i]; + } + + public void setRace(int i, int val) + { + _race[i] = val; + } + + public int getRace(int i) + { + return _race[i]; + } + + public boolean getMessageRefusal() + { + return _messageRefusal; + } + + public void setMessageRefusal(boolean mode) + { + _messageRefusal = mode; + sendPacket(new EtcStatusUpdate(this)); + } + + public void setDietMode(boolean mode) + { + _dietMode = mode; + } + + public boolean getDietMode() + { + return _dietMode; + } + + public void setTradeRefusal(boolean mode) + { + _tradeRefusal = mode; + } + + public boolean getTradeRefusal() + { + return _tradeRefusal; + } + + public void setExchangeRefusal(boolean mode) + { + _exchangeRefusal = mode; + } + + public boolean getExchangeRefusal() + { + return _exchangeRefusal; + } + + public BlockList getBlockList() + { + return _blockList; + } + + public void setHero(boolean hero) + { + if (hero && (_baseClass == _activeClass)) + { + for (L2Skill s : HeroSkillTable.getHeroSkills()) + { + addSkill(s, false); // Dont Save Hero skills to database + } + } + else + { + for (L2Skill s : HeroSkillTable.getHeroSkills()) + { + super.removeSkill(s); // Just Remove skills from nonHero characters + } + } + _hero = hero; + + sendSkillList(); + } + + public void setIsInOlympiadMode(boolean b) + { + _inOlympiadMode = b; + } + + public void setIsOlympiadStart(boolean b) + { + _OlympiadStart = b; + } + + public boolean isOlympiadStart() + { + return _OlympiadStart; + } + + public boolean isHero() + { + return _hero; + } + + public boolean isInOlympiadMode() + { + return _inOlympiadMode; + } + + public boolean isInDuel() + { + return _isInDuel; + } + + public int getDuelId() + { + return _duelId; + } + + public void setDuelState(int mode) + { + _duelState = mode; + } + + public int getDuelState() + { + return _duelState; + } + + /** + * Sets up the duel state using a non 0 duelId. + * @param duelId 0=not in a duel + */ + public void setIsInDuel(int duelId) + { + if (duelId > 0) + { + _isInDuel = true; + _duelState = Duel.DUELSTATE_DUELLING; + _duelId = duelId; + } + else + { + if (_duelState == Duel.DUELSTATE_DEAD) + { + enableAllSkills(); + getStatus().startHpMpRegeneration(); + } + _isInDuel = false; + _duelState = Duel.DUELSTATE_NODUEL; + _duelId = 0; + } + } + + /** + * This returns a SystemMessage stating why the player is not available for duelling. + * @return S1_CANNOT_DUEL... message + */ + public SystemMessage getNoDuelReason() + { + SystemMessage sm = SystemMessage.getSystemMessage(_noDuelReason); + sm.addPcName(this); + _noDuelReason = SystemMessageId.THERE_IS_NO_OPPONENT_TO_RECEIVE_YOUR_CHALLENGE_FOR_A_DUEL; + return sm; + } + + /** + * Checks if this player might join / start a duel. To get the reason use getNoDuelReason() after calling this function. + * @return true if the player might join/start a duel. + */ + public boolean canDuel() + { + if (isInCombat() || (getPunishLevel() == PunishLevel.JAIL)) + { + _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_ENGAGED_IN_BATTLE; + return false; + } + if (isDead() || isAlikeDead() || ((getCurrentHp() < (getMaxHp() / 2)) || (getCurrentMp() < (getMaxMp() / 2)))) + { + _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_HP_OR_MP_IS_BELOW_50_PERCENT; + return false; + } + if (isInDuel()) + { + _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_ALREADY_ENGAGED_IN_A_DUEL; + return false; + } + if (isInOlympiadMode()) + { + _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_PARTICIPATING_IN_THE_OLYMPIAD; + return false; + } + if (isCursedWeaponEquipped()) + { + _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_IN_A_CHAOTIC_STATE; + return false; + } + if (getPrivateStoreType() != STORE_PRIVATE_NONE) + { + _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_ENGAGED_IN_A_PRIVATE_STORE_OR_MANUFACTURE; + return false; + } + if (isMounted() || isInBoat()) + { + _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_RIDING_A_BOAT_WYVERN_OR_STRIDER; + return false; + } + if (isFishing()) + { + _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_FISHING; + return false; + } + if (isInsideZone(ZONE_PVP) || isInsideZone(ZONE_PEACE) || isInsideZone(ZONE_SIEGE)) + { + _noDuelReason = SystemMessageId.C1_CANNOT_MAKE_A_CHALLANGE_TO_A_DUEL_BECAUSE_C1_IS_CURRENTLY_IN_A_DUEL_PROHIBITED_AREA; + return false; + } + return true; + } + + public boolean isNoble() + { + return _noble; + } + + public void setNoble(boolean val) + { + if (val) + { + for (L2Skill s : NobleSkillTable.getInstance().getNobleSkills()) + { + addSkill(s, false); // Dont Save Noble skills to Sql + } + } + else + { + for (L2Skill s : NobleSkillTable.getInstance().getNobleSkills()) + { + super.removeSkill(s); // Just Remove skills without deleting from Sql + } + } + _noble = val; + + sendSkillList(); + } + + public void setLvlJoinedAcademy(int lvl) + { + _lvlJoinedAcademy = lvl; + } + + public int getLvlJoinedAcademy() + { + return _lvlJoinedAcademy; + } + + public boolean isAcademyMember() + { + return _lvlJoinedAcademy > 0; + } + + public void setTeam(int team) + { + _team = team; + if (getPet() != null) + { + getPet().broadcastStatusUpdate(); + } + } + + public int getTeam() + { + return _team; + } + + public void setWantsPeace(int wantsPeace) + { + _wantsPeace = wantsPeace; + } + + public int getWantsPeace() + { + return _wantsPeace; + } + + public boolean isFishing() + { + return _fishing; + } + + public void setFishing(boolean fishing) + { + _fishing = fishing; + } + + public void setAllianceWithVarkaKetra(int sideAndLvlOfAlliance) + { + // [-5,-1] varka, 0 neutral, [1,5] ketra + _alliedVarkaKetra = sideAndLvlOfAlliance; + } + + public int getAllianceWithVarkaKetra() + { + return _alliedVarkaKetra; + } + + public boolean isAlliedWithVarka() + { + return (_alliedVarkaKetra < 0); + } + + public boolean isAlliedWithKetra() + { + return (_alliedVarkaKetra > 0); + } + + public void sendSkillList() + { + sendSkillList(this); + } + + public void sendSkillList(L2PcInstance player) + { + boolean isDisabled = false; + SkillList sl = new SkillList(); + if (player != null) + { + for (L2Skill s : player.getAllSkills()) + { + if (s == null) + { + continue; + } + if ((s.getId() > 9000) && (s.getId() < 9007)) + { + continue; // Fake skills to change base stats + } + if ((_transformation != null) && (!containsAllowedTransformSkill(s.getId()) && !s.allowOnTransform())) + { + continue; + } + if (player.getClan() != null) + { + isDisabled = s.isClanSkill() && (player.getClan().getReputationScore() < 0); + } + + boolean isEnchantable = SkillTable.getInstance().isEnchantable(s.getId()); + if (isEnchantable) + { + L2EnchantSkillLearn esl = EnchantGroupsTable.getInstance().getSkillEnchantmentBySkillId(s.getId()); + if (esl != null) + { + // if player dont have min level to enchant + if (s.getLevel() < esl.getBaseLevel()) + { + isEnchantable = false; + } + } + // if no enchant data + else + { + isEnchantable = false; + } + } + + sl.addSkill(s.getId(), s.getLevel(), s.isPassive(), isDisabled, isEnchantable); + } + } + + sendPacket(sl); + } + + /** + * 1. Add the specified class ID as a subclass (up to the maximum number of three) for this character.
    + * 2. This method no longer changes the active _classIndex of the player. This is only done by the calling of setActiveClass() method as that should be the only way to do so. + * @param int classId + * @param int classIndex + * @return boolean subclassAdded + */ + public boolean addSubClass(int classId, int classIndex) + { + if (!_subclassLock.tryLock()) + { + return false; + } + + try + { + if ((getTotalSubClasses() == Config.MAX_SUBCLASS) || (classIndex == 0)) + { + return false; + } + + if (getSubClasses().containsKey(classIndex)) + { + return false; + } + + // Note: Never change _classIndex in any method other than setActiveClass(). + + SubClass newClass = new SubClass(); + newClass.setClassId(classId); + newClass.setClassIndex(classIndex); + + Connection con = null; + PreparedStatement statement = null; + + try + { + // Store the basic info about this new sub-class. + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement(ADD_CHAR_SUBCLASS); + + statement.setInt(1, getObjectId()); + statement.setInt(2, newClass.getClassId()); + statement.setLong(3, newClass.getExp()); + statement.setInt(4, newClass.getSp()); + statement.setInt(5, newClass.getLevel()); + statement.setInt(6, newClass.getClassIndex()); // <-- Added + + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "WARNING: Could not add character sub class for " + getName() + ": " + e.getMessage(), e); + return false; + } + finally + { + L2DatabaseFactory.close(con); + } + + // Commit after database INSERT incase exception is thrown. + getSubClasses().put(newClass.getClassIndex(), newClass); + + if (Config.DEBUG) + { + _log.info(getName() + " added class ID " + classId + " as a sub class at index " + classIndex + "."); + } + + final ClassId subTemplate = ClassId.values()[classId]; + final FastMap skillTree = SkillTreesData.getInstance().getCompleteClassSkillTree(subTemplate); + final FastMap prevSkillList = new FastMap<>(); + + for (L2SkillLearn skillInfo : skillTree.values()) + { + if (skillInfo.getGetLevel() <= 40) + { + L2Skill prevSkill = prevSkillList.get(skillInfo.getSkillId()); + L2Skill newSkill = SkillTable.getInstance().getInfo(skillInfo.getSkillId(), skillInfo.getSkillLevel()); + + if ((prevSkill != null) && (prevSkill.getLevel() > newSkill.getLevel())) + { + continue; + } + + prevSkillList.put(newSkill.getId(), newSkill); + storeSkill(newSkill, prevSkill, classIndex); + } + } + + if (Config.DEBUG) + { + _log.info(getName() + " was given " + getAllSkills().length + " skills for their new sub class."); + } + + return true; + } + finally + { + _subclassLock.unlock(); + } + } + + /** + * 1. Completely erase all existance of the subClass linked to the classIndex.
    + * 2. Send over the newClassId to addSubClass()to create a new instance on this classIndex.
    + * 3. Upon Exception, revert the player to their BaseClass to avoid further problems.
    + * @param int classIndex + * @param int newClassId + * @return boolean subclassAdded + */ + public boolean modifySubClass(int classIndex, int newClassId) + { + if (!_subclassLock.tryLock()) + { + return false; + } + + try + { + int oldClassId = getSubClasses().get(classIndex).getClassId(); + + if (Config.DEBUG) + { + _log.info(getName() + " has requested to modify sub class index " + classIndex + " from class ID " + oldClassId + " to " + newClassId + "."); + } + + Connection con = null; + PreparedStatement statement = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + // Remove all henna info stored for this sub-class. + statement = con.prepareStatement(DELETE_CHAR_HENNAS); + statement.setInt(1, getObjectId()); + statement.setInt(2, classIndex); + statement.execute(); + statement.close(); + + // Remove all shortcuts info stored for this sub-class. + statement = con.prepareStatement(DELETE_CHAR_SHORTCUTS); + statement.setInt(1, getObjectId()); + statement.setInt(2, classIndex); + statement.execute(); + statement.close(); + + // Remove all effects info stored for this sub-class. + statement = con.prepareStatement(DELETE_SKILL_SAVE); + statement.setInt(1, getObjectId()); + statement.setInt(2, classIndex); + statement.execute(); + statement.close(); + + // Remove all skill info stored for this sub-class. + statement = con.prepareStatement(DELETE_CHAR_SKILLS); + statement.setInt(1, getObjectId()); + statement.setInt(2, classIndex); + statement.execute(); + statement.close(); + + // Remove all basic info stored about this sub-class. + statement = con.prepareStatement(DELETE_CHAR_SUBCLASS); + statement.setInt(1, getObjectId()); + statement.setInt(2, classIndex); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not modify sub class for " + getName() + " to class index " + classIndex + ": " + e.getMessage(), e); + + // This must be done in order to maintain data consistency. + getSubClasses().remove(classIndex); + return false; + } + finally + { + L2DatabaseFactory.close(con); + } + + getSubClasses().remove(classIndex); + } + finally + { + _subclassLock.unlock(); + } + + return addSubClass(newClassId, classIndex); + } + + public boolean isSubClassActive() + { + return _classIndex > 0; + } + + public Map getSubClasses() + { + if (_subClasses == null) + { + _subClasses = new FastMap<>(); + } + + return _subClasses; + } + + public int getTotalSubClasses() + { + return getSubClasses().size(); + } + + public int getBaseClass() + { + return _baseClass; + } + + public int getActiveClass() + { + return _activeClass; + } + + public int getClassIndex() + { + return _classIndex; + } + + private void setClassTemplate(int classId) + { + _activeClass = classId; + + L2PcTemplate t = CharTemplateTable.getInstance().getTemplate(classId); + + if (t == null) + { + _log.severe("Missing template for classId: " + classId); + throw new Error(); + } + + // Set the template of the L2PcInstance + setTemplate(t); + } + + /** + * Changes the character's class based on the given class index.
    + *
    + * An index of zero specifies the character's original (base) class, while indexes 1-3 specifies the character's sub-classes respectively.
    + *
    + * WARNING: Use only on subclase change + * @param classIndex + */ + public boolean setActiveClass(int classIndex) + { + if (!_subclassLock.tryLock()) + { + return false; + } + + try + { + // Cannot switch or change subclasses while transformed + if (_transformation != null) + { + return false; + } + + // Remove active item skills before saving char to database + // because next time when choosing this class, weared items can + // be different + for (L2ItemInstance item : getInventory().getAugmentedItems()) + { + if ((item != null) && item.isEquipped()) + { + item.getAugmentation().removeBonus(this); + } + } + + // abort any kind of cast. + abortCast(); + + // Stop casting for any player that may be casting a force buff on this l2pcinstance. + for (L2Character character : getKnownList().getKnownCharacters()) + { + if ((character.getFusionSkill() != null) && (character.getFusionSkill().getTarget() == this)) + { + character.abortCast(); + } + } + + /* + * 1. Call store() before modifying _classIndex to avoid skill effects rollover. 2. Register the correct _classId against applied 'classIndex'. + */ + store(Config.SUBCLASS_STORE_SKILL_COOLTIME); + _reuseTimeStamps.clear(); + + // clear charges + _charges.set(0); + stopChargeTask(); + + if (classIndex == 0) + { + setClassTemplate(getBaseClass()); + } + else + { + try + { + setClassTemplate(getSubClasses().get(classIndex).getClassId()); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not switch " + getName() + "'s sub class to class index " + classIndex + ": " + e.getMessage(), e); + return false; + } + } + _classIndex = classIndex; + + if (isInParty()) + { + getParty().recalculatePartyLevel(); + } + + /* + * Update the character's change in class status. 1. Remove any active cubics from the player. 2. Renovate the characters table in the database with the new class info, storing also buff/effect data. 3. Remove all existing skills. 4. Restore all the learned skills for the current class + * from the database. 5. Restore effect/buff data for the new class. 6. Restore henna data for the class, applying the new stat modifiers while removing existing ones. 7. Reset HP/MP/CP stats and send Server->Client character status packet to reflect changes. 8. Restore shortcut data + * related to this class. 9. Resend a class change animation effect to broadcast to all nearby players. 10.Unsummon any active servitor from the player. + */ + + if (getPet() instanceof L2SummonInstance) + { + getPet().unSummon(this); + } + + for (L2Skill oldSkill : getAllSkills()) + { + super.removeSkill(oldSkill); + } + + stopAllEffectsExceptThoseThatLastThroughDeath(); + stopCubics(); + + restoreRecipeBook(false); + + // Restore any Death Penalty Buff + restoreDeathPenaltyBuffLevel(); + + restoreSkills(); + rewardSkills(); + regiveTemporarySkills(); + + // Prevents some issues when changing between subclases that shares skills + if ((_disabledSkills != null) && !_disabledSkills.isEmpty()) + { + _disabledSkills.clear(); + } + + restoreEffects(); + updateEffectIcons(); + sendPacket(new EtcStatusUpdate(this)); + + // if player has quest 422: Repent Your Sins, remove it + QuestState st = getQuestState("422_RepentYourSins"); + if (st != null) + { + st.exitQuest(true); + } + + for (int i = 0; i < 3; i++) + { + _henna[i] = null; + } + + restoreHenna(); + sendPacket(new HennaInfo(this)); + + if (getCurrentHp() > getMaxHp()) + { + setCurrentHp(getMaxHp()); + } + if (getCurrentMp() > getMaxMp()) + { + setCurrentMp(getMaxMp()); + } + if (getCurrentCp() > getMaxCp()) + { + setCurrentCp(getMaxCp()); + } + + refreshOverloaded(); + refreshExpertisePenalty(); + broadcastUserInfo(); + + // Clear resurrect xp calculation + setExpBeforeDeath(0); + + _shortCuts.restore(); + sendPacket(new ShortCutInit(this)); + + broadcastPacket(new SocialAction(this, SocialAction.LEVEL_UP)); + sendPacket(new SkillCoolTime(this)); + sendPacket(new ExStorageMaxCount(this)); + + return true; + } + finally + { + _subclassLock.unlock(); + } + } + + public boolean isLocked() + { + return _subclassLock.isLocked(); + } + + public void stopWarnUserTakeBreak() + { + if (_taskWarnUserTakeBreak != null) + { + _taskWarnUserTakeBreak.cancel(true); + // ThreadPoolManager.getInstance().removeGeneral((Runnable)_taskWarnUserTakeBreak); + _taskWarnUserTakeBreak = null; + } + } + + public void startWarnUserTakeBreak() + { + if (_taskWarnUserTakeBreak == null) + { + _taskWarnUserTakeBreak = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new WarnUserTakeBreak(), 7200000, 7200000); + } + } + + public void stopRentPet() + { + if (_taskRentPet != null) + { + // if the rent of a wyvern expires while over a flying zone, tp to down before unmounting + if (checkLandingState() && (getMountType() == 2)) + { + teleToLocation(MapRegionTable.TeleportWhereType.Town); + } + + if (dismount()) // this should always be true now, since we teleported already + { + _taskRentPet.cancel(true); + _taskRentPet = null; + } + } + } + + public void startRentPet(int seconds) + { + if (_taskRentPet == null) + { + _taskRentPet = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new RentPetTask(), seconds * 1000L, seconds * 1000L); + } + } + + public boolean isRentedPet() + { + if (_taskRentPet != null) + { + return true; + } + + return false; + } + + public void stopWaterTask() + { + if (_taskWater != null) + { + _taskWater.cancel(false); + + _taskWater = null; + sendPacket(new SetupGauge(2, 0)); + } + } + + public void startWaterTask() + { + if (!isDead() && (_taskWater == null)) + { + int timeinwater = (int) calcStat(Stats.BREATH, 60000, this, null); + + sendPacket(new SetupGauge(2, timeinwater)); + _taskWater = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new WaterTask(), timeinwater, 1000); + } + } + + public boolean isInWater() + { + if (_taskWater != null) + { + return true; + } + + return false; + } + + public void checkWaterState() + { + if (isInsideZone(ZONE_WATER)) + { + startWaterTask(); + } + else + { + stopWaterTask(); + } + } + + public void onPlayerEnter() + { + startWarnUserTakeBreak(); + + if (SevenSigns.getInstance().isSealValidationPeriod() || SevenSigns.getInstance().isCompResultsPeriod()) + { + if (!isGM() && isIn7sDungeon() && (SevenSigns.getInstance().getPlayerCabal(getObjectId()) != SevenSigns.getInstance().getCabalHighestScore())) + { + teleToLocation(MapRegionTable.TeleportWhereType.Town); + setIsIn7sDungeon(false); + sendMessage("You have been teleported to the nearest town due to the beginning of the Seal Validation period."); + } + } + else + { + if (!isGM() && isIn7sDungeon() && (SevenSigns.getInstance().getPlayerCabal(getObjectId()) == SevenSigns.CABAL_NULL)) + { + teleToLocation(MapRegionTable.TeleportWhereType.Town); + setIsIn7sDungeon(false); + sendMessage("You have been teleported to the nearest town because you have not signed for any cabal."); + } + } + + // jail task + updatePunishState(); + + if (isGM()) + { + if (isInvul()) + { + sendMessage("Entering world in Invulnerable mode."); + } + if (getAppearance().getInvisible()) + { + sendMessage("Entering world in Invisible mode."); + } + if (isSilenceMode()) + { + sendMessage("Entering world in Silence mode."); + } + } + + revalidateZone(true); + + notifyFriends(); + if (!isGM() && Config.DECREASE_SKILL_LEVEL) + { + checkPlayerSkills(); + } + + if (Config.CACHE_CHAR_NAMES) + { + CharNameTable.getInstance().addName(this); + } + } + + public long getLastAccess() + { + return _lastAccess; + } + + @Override + public void doRevive() + { + super.doRevive(); + stopEffects(L2EffectType.CHARMOFCOURAGE); + updateEffectIcons(); + sendPacket(new EtcStatusUpdate(this)); + _reviveRequested = 0; + _revivePower = 0; + + if (isMounted()) + { + startFeed(_mountNpcId); + } + if (isInParty() && getParty().isInDimensionalRift()) + { + if (!DimensionalRiftManager.getInstance().checkIfInPeaceZone(getX(), getY(), getZ())) + { + getParty().getDimensionalRift().memberRessurected(this); + } + } + } + + @Override + public void doRevive(double revivePower) + { + // Restore the player's lost experience, + // depending on the % return of the skill used (based on its power). + restoreExp(revivePower); + doRevive(); + } + + public void reviveRequest(L2PcInstance Reviver, L2Skill skill, boolean Pet) + { + if (isResurrectionBlocked()) + { + return; + } + + if (_reviveRequested == 1) + { + if (_revivePet == Pet) + { + Reviver.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.RES_HAS_ALREADY_BEEN_PROPOSED)); // Resurrection is already been proposed. + } + else + { + if (Pet) + { + Reviver.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_RES_PET2)); // A pet cannot be resurrected while it's owner is in the process of resurrecting. + } + else + { + Reviver.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.MASTER_CANNOT_RES)); // While a pet is attempting to resurrect, it cannot help in resurrecting its master. + } + } + return; + } + if ((Pet && (getPet() != null) && getPet().isDead()) || (!Pet && isDead())) + { + _reviveRequested = 1; + int restoreExp = 0; + if (isPhoenixBlessed()) + { + _revivePower = 100; + } + else if (isAffected(CharEffectList.EFFECT_FLAG_CHARM_OF_COURAGE)) + { + _revivePower = 0; + } + else + { + _revivePower = Formulas.calculateSkillResurrectRestorePercent(skill.getPower(), Reviver); + } + + restoreExp = (int) Math.round(((getExpBeforeDeath() - getExp()) * _revivePower) / 100); + + _revivePet = Pet; + + if (isAffected(CharEffectList.EFFECT_FLAG_CHARM_OF_COURAGE)) + { + ConfirmDlg dlg = new ConfirmDlg(SystemMessageId.RESURRECT_USING_CHARM_OF_COURAGE.getId()); + dlg.addTime(60000); + sendPacket(dlg); + return; + } + ConfirmDlg dlg = new ConfirmDlg(SystemMessageId.RESSURECTION_REQUEST_BY_C1_FOR_S2_XP.getId()); + dlg.addPcName(Reviver); + dlg.addString(String.valueOf(restoreExp)); + sendPacket(dlg); + } + } + + public void reviveAnswer(int answer) + { + if ((_reviveRequested != 1) || (!isDead() && !_revivePet) || (_revivePet && (getPet() != null) && !getPet().isDead())) + { + return; + } + // If character refuses a PhoenixBless autoress, cancel all buffs he had + if ((answer == 0) && isPhoenixBlessed()) + { + stopPhoenixBlessing(null); + stopAllEffectsExceptThoseThatLastThroughDeath(); + } + if (answer == 1) + { + if (!_revivePet) + { + if (_revivePower != 0) + { + doRevive(_revivePower); + } + else + { + doRevive(); + } + } + else if (getPet() != null) + { + if (_revivePower != 0) + { + getPet().doRevive(_revivePower); + } + else + { + getPet().doRevive(); + } + } + } + _reviveRequested = 0; + _revivePower = 0; + } + + public boolean isReviveRequested() + { + return (_reviveRequested == 1); + } + + public boolean isRevivingPet() + { + return _revivePet; + } + + public void removeReviving() + { + _reviveRequested = 0; + _revivePower = 0; + } + + public void onActionRequest() + { + if (isSpawnProtected()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_NO_LONGER_PROTECTED_FROM_AGGRESSIVE_MONSTERS)); + } + if (isTeleportProtected()) + { + sendMessage("Teleport spawn protection ended."); + } + setProtection(false); + setTeleportProtection(false); + } + + /** + * Expertise of the L2PcInstance (None=0, D=1, C=2, B=3, A=4, S=5, S80=6, S84=7) + * @return int Expertise skill level. + */ + public int getExpertiseLevel() + { + int level = getSkillLevel(239); + if (level < 0) + { + level = 0; + } + return level; + } + + @Override + public void teleToLocation(int x, int y, int z, int heading, boolean allowRandomOffset) + { + if ((getVehicle() != null) && !getVehicle().isTeleporting()) + { + setVehicle(null); + } + + if (isFlyingMounted() && (z < -1005)) + { + z = -1005; + } + + super.teleToLocation(x, y, z, heading, allowRandomOffset); + } + + @Override + public final void onTeleported() + { + super.onTeleported(); + + if (isInAirShip()) + { + getAirShip().sendInfo(this); + } + + // Force a revalidation + revalidateZone(true); + + checkItemRestriction(); + + if ((Config.PLAYER_TELEPORT_PROTECTION > 0) && !isInOlympiadMode()) + { + setTeleportProtection(true); + } + + // Trained beast is after teleport lost + if (getTrainedBeasts() != null) + { + for (L2TamedBeastInstance tamedBeast : getTrainedBeasts()) + { + tamedBeast.deleteMe(); + } + getTrainedBeasts().clear(); + } + + // Modify the position of the pet if necessary + L2Summon pet = getPet(); + if (pet != null) + { + pet.setFollowStatus(false); + pet.teleToLocation(getPosition().getX(), getPosition().getY(), getPosition().getZ(), false); + ((L2SummonAI) pet.getAI()).setStartFollowController(true); + pet.setFollowStatus(true); + pet.updateAndBroadcastStatus(0); + } + + TvTEvent.onTeleported(this); + } + + @Override + public void setIsTeleporting(boolean teleport) + { + setIsTeleporting(teleport, true); + } + + public void setIsTeleporting(boolean teleport, boolean useWatchDog) + { + super.setIsTeleporting(teleport); + if (!useWatchDog) + { + return; + } + if (teleport) + { + if ((_teleportWatchdog == null) && (Config.TELEPORT_WATCHDOG_TIMEOUT > 0)) + { + synchronized (this) + { + if (_teleportWatchdog == null) + { + _teleportWatchdog = ThreadPoolManager.getInstance().scheduleGeneral(new TeleportWatchdog(), Config.TELEPORT_WATCHDOG_TIMEOUT * 1000); + } + } + } + } + else + { + if (_teleportWatchdog != null) + { + _teleportWatchdog.cancel(false); + // ThreadPoolManager.getInstance().removeGeneral((Runnable)_teleportWatchdog); + _teleportWatchdog = null; + } + } + } + + private class TeleportWatchdog implements Runnable + { + private final L2PcInstance _player; + + TeleportWatchdog() + { + _player = L2PcInstance.this; + } + + @Override + public void run() + { + if ((_player == null) || !_player.isTeleporting()) + { + return; + } + + if (Config.DEBUG) + { + _log.warning("Player " + _player.getName() + " teleport timeout expired"); + } + _player.onTeleported(); + } + } + + public void setLastServerPosition(int x, int y, int z) + { + _lastServerPosition.setXYZ(x, y, z); + } + + public Point3D getLastServerPosition() + { + return _lastServerPosition; + } + + public boolean checkLastServerPosition(int x, int y, int z) + { + return _lastServerPosition.equals(x, y, z); + } + + public int getLastServerDistance(int x, int y, int z) + { + double dx = (x - _lastServerPosition.getX()); + double dy = (y - _lastServerPosition.getY()); + double dz = (z - _lastServerPosition.getZ()); + + return (int) Math.sqrt((dx * dx) + (dy * dy) + (dz * dz)); + } + + @Override + public void addExpAndSp(long addToExp, int addToSp) + { + getStat().addExpAndSp(addToExp, addToSp, false); + } + + public void addExpAndSp(long addToExp, int addToSp, boolean useVitality) + { + getStat().addExpAndSp(addToExp, addToSp, useVitality); + } + + public void removeExpAndSp(long removeExp, int removeSp) + { + getStat().removeExpAndSp(removeExp, removeSp, true); + } + + public void removeExpAndSp(long removeExp, int removeSp, boolean sendMessage) + { + getStat().removeExpAndSp(removeExp, removeSp, sendMessage); + } + + @Override + public void reduceCurrentHp(double value, L2Character attacker, boolean awake, boolean isDOT, L2Skill skill) + { + if (skill != null) + { + getStatus().reduceHp(value, attacker, awake, isDOT, skill.isToggle(), skill.getDmgDirectlyToHP()); + } + else + { + getStatus().reduceHp(value, attacker, awake, isDOT, false, false); + } + + // notify the tamed beast of attacks + if (getTrainedBeasts() != null) + { + for (L2TamedBeastInstance tamedBeast : getTrainedBeasts()) + { + tamedBeast.onOwnerGotAttacked(attacker); + } + } + } + + public void broadcastSnoop(int type, String name, String _text) + { + if (!_snoopListener.isEmpty()) + { + Snoop sn = new Snoop(getObjectId(), getName(), type, name, _text); + + for (L2PcInstance pci : _snoopListener) + { + if (pci != null) + { + pci.sendPacket(sn); + } + } + } + } + + public void addSnooper(L2PcInstance pci) + { + if (!_snoopListener.contains(pci)) + { + _snoopListener.add(pci); + } + } + + public void removeSnooper(L2PcInstance pci) + { + _snoopListener.remove(pci); + } + + public void addSnooped(L2PcInstance pci) + { + if (!_snoopedPlayer.contains(pci)) + { + _snoopedPlayer.add(pci); + } + } + + public void removeSnooped(L2PcInstance pci) + { + _snoopedPlayer.remove(pci); + } + + public void addBypass(String bypass) + { + if (bypass == null) + { + return; + } + synchronized (_validBypass) + { + _validBypass.add(bypass); + } + // _log.warning("[BypassAdd]"+getName()+" '"+bypass+"'"); + } + + public void addBypass2(String bypass) + { + if (bypass == null) + { + return; + } + synchronized (_validBypass2) + { + _validBypass2.add(bypass); + } + // _log.warning("[BypassAdd]"+getName()+" '"+bypass+"'"); + } + + public boolean validateBypass(String cmd) + { + if (!Config.BYPASS_VALIDATION) + { + return true; + } + + synchronized (_validBypass) + { + for (String bp : _validBypass) + { + if (bp == null) + { + continue; + } + + // _log.warning("[BypassValidation]"+getName()+" '"+bp+"'"); + if (bp.equals(cmd)) + { + return true; + } + } + } + + synchronized (_validBypass2) + { + for (String bp : _validBypass2) + { + if (bp == null) + { + continue; + } + + // _log.warning("[BypassValidation]"+getName()+" '"+bp+"'"); + if (cmd.startsWith(bp)) + { + return true; + } + } + } + _log.warning("[L2PcInstance] player [" + getName() + "] sent invalid bypass '" + cmd + "'."); + return false; + } + + /** + * Performs following tests:
    + *
  • Inventory contains item
  • Item owner id == this.owner id
  • It isnt pet control item while mounting pet or pet summoned
  • It isnt active enchant item
  • It isnt cursed weapon/item
  • It isnt wear item
    + * @param objectId: item object id + * @param action: just for login porpouse + * @return + */ + public boolean validateItemManipulation(int objectId, String action) + { + L2ItemInstance item = getInventory().getItemByObjectId(objectId); + + if ((item == null) || (item.getOwnerId() != getObjectId())) + { + _log.finest(getObjectId() + ": player tried to " + action + " item he is not owner of"); + return false; + } + + // Pet is summoned and not the item that summoned the pet AND not the buggle from strider you're mounting + if (((getPet() != null) && (getPet().getControlObjectId() == objectId)) || (getMountObjectID() == objectId)) + { + if (Config.DEBUG) + { + _log.finest(getObjectId() + ": player tried to " + action + " item controling pet"); + } + + return false; + } + + if ((getActiveEnchantItem() != null) && (getActiveEnchantItem().getObjectId() == objectId)) + { + if (Config.DEBUG) + { + _log.finest(getObjectId() + ":player tried to " + action + " an enchant scroll he was using"); + } + + return false; + } + + if (CursedWeaponsManager.getInstance().isCursed(item.getItemId())) + { + // can not trade a cursed weapon + return false; + } + + return true; + } + + public void clearBypass() + { + synchronized (_validBypass) + { + _validBypass.clear(); + } + synchronized (_validBypass2) + { + _validBypass2.clear(); + } + } + + /** + * @return Returns the inBoat. + */ + public boolean isInBoat() + { + return (_vehicle != null) && _vehicle.isBoat(); + } + + /** + * @return + */ + public L2BoatInstance getBoat() + { + return (L2BoatInstance) _vehicle; + } + + /** + * @return Returns the inAirShip. + */ + public boolean isInAirShip() + { + return (_vehicle != null) && _vehicle.isAirShip(); + } + + /** + * @return + */ + public L2AirShipInstance getAirShip() + { + return (L2AirShipInstance) _vehicle; + } + + public L2Vehicle getVehicle() + { + return _vehicle; + } + + public void setVehicle(L2Vehicle v) + { + if ((v == null) && (_vehicle != null)) + { + _vehicle.removePassenger(this); + } + + _vehicle = v; + } + + public void setInCrystallize(boolean inCrystallize) + { + _inCrystallize = inCrystallize; + } + + public boolean isInCrystallize() + { + return _inCrystallize; + } + + /** + * @return + */ + public Point3D getInVehiclePosition() + { + return _inVehiclePosition; + } + + public void setInVehiclePosition(Point3D pt) + { + _inVehiclePosition = pt; + } + + /** + * Manage the delete task of a L2PcInstance (Leave Party, Unsummon pet, Save its inventory in the database, Remove it from the world...).
    + *
    + * Actions :
    + *
    + *
  • If the L2PcInstance is in observer mode, set its position to its position before entering in observer mode
  • Set the online Flag to True or False and update the characters table of the database with online status and lastAccess
  • Stop the HP/MP/CP Regeneration task
  • + * Cancel Crafting, Attak or Cast
  • Remove the L2PcInstance from the world
  • Stop Party and Unsummon Pet
  • Update database with items in its inventory and remove them from the world
  • Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then + * cancel Attak or Cast and notify AI
  • Close the connection with the client

  • + *
    + */ + @Override + public void deleteMe() + { + cleanup(); + store(); + super.deleteMe(); + } + + private synchronized void cleanup() + { + // Set the online Flag to True or False and update the characters table of the database with online status and lastAccess (called when login and logout) + try + { + if (!isOnline()) + { + _log.log(Level.SEVERE, "deleteMe() called on offline character " + this, new RuntimeException()); + } + setOnlineStatus(false, true); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + try + { + if (Config.ENABLE_BLOCK_CHECKER_EVENT && (getBlockCheckerArena() != -1)) + { + HandysBlockCheckerManager.getInstance().onDisconnect(this); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + try + { + _isOnline = false; + abortAttack(); + abortCast(); + stopMove(null); + setDebug(null); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + // remove combat flag + try + { + if (getInventory().getItemByItemId(9819) != null) + { + Fort fort = FortManager.getInstance().getFort(this); + if (fort != null) + { + FortSiegeManager.getInstance().dropCombatFlag(this, fort.getFortId()); + } + else + { + int slot = getInventory().getSlotFromItem(getInventory().getItemByItemId(9819)); + getInventory().unEquipItemInBodySlot(slot); + destroyItem("CombatFlag", getInventory().getItemByItemId(9819), null, true); + } + } + else if (isCombatFlagEquipped()) + { + TerritoryWarManager.getInstance().dropCombatFlag(this, false, false); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + try + { + PartyMatchWaitingList.getInstance().removePlayer(this); + if (_partyroom != 0) + { + PartyMatchRoom room = PartyMatchRoomList.getInstance().getRoom(_partyroom); + if (room != null) + { + room.deleteMember(this); + } + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + try + { + if (isFlying()) + { + removeSkill(SkillTable.getInstance().getInfo(4289, 1)); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + try + { + CommunityServerThread.getInstance().sendPacket(new WorldInfo(this, null, WorldInfo.TYPE_UPDATE_PLAYER_STATUS)); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + // Recommendations must be saved before task (timer) is canceled + try + { + storeRecommendations(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + // Stop the HP/MP/CP Regeneration task (scheduled tasks) + try + { + stopAllTimers(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + try + { + setIsTeleporting(false); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + // Stop crafting, if in progress + try + { + RecipeController.getInstance().requestMakeItemAbort(this); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + // Cancel Attak or Cast + try + { + setTarget(null); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + try + { + if (_fusionSkill != null) + { + abortCast(); + } + + for (L2Character character : getKnownList().getKnownCharacters()) + { + if ((character.getFusionSkill() != null) && (character.getFusionSkill().getTarget() == this)) + { + character.abortCast(); + } + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + try + { + for (L2Effect effect : getAllEffects()) + { + if (effect.getSkill().isToggle()) + { + effect.exit(); + continue; + } + + switch (effect.getEffectType()) + { + case SIGNET_GROUND: + case SIGNET_EFFECT: + effect.exit(); + break; + } + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + // Remove from world regions zones + final L2WorldRegion oldRegion = getWorldRegion(); + + if (oldRegion != null) + { + oldRegion.removeFromZones(this); + } + + // Remove the L2PcInstance from the world + try + { + decayMe(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + // If a Party is in progress, leave it (and festival party) + if (isInParty()) + { + try + { + leaveParty(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + } + + if (OlympiadManager.getInstance().isRegistered(this) || (getOlympiadGameId() != -1)) + { + OlympiadManager.getInstance().removeDisconnectedCompetitor(this); + } + + // If the L2PcInstance has Pet, unsummon it + if (getPet() != null) + { + try + { + getPet().unSummon(this); + // dead pet wasnt unsummoned, broadcast npcinfo changes (pet will be without owner name - means owner offline) + if (getPet() != null) + { + getPet().broadcastNpcInfo(0); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + }// returns pet to control item + } + + if (getClan() != null) + { + // set the status for pledge member list to OFFLINE + try + { + L2ClanMember clanMember = getClan().getClanMember(getObjectId()); + if (clanMember != null) + { + clanMember.setPlayerInstance(null); + } + + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + } + + if (getActiveRequester() != null) + { + // deals with sudden exit in the middle of transaction + setActiveRequester(null); + cancelActiveTrade(); + } + + // If the L2PcInstance is a GM, remove it from the GM List + if (isGM()) + { + try + { + GmListTable.getInstance().deleteGm(this); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + } + + try + { + // Check if the L2PcInstance is in observer mode to set its position to its position + // before entering in observer mode + if (inObserverMode()) + { + setXYZInvisible(_lastX, _lastY, _lastZ); + } + + if (getVehicle() != null) + { + getVehicle().oustPlayer(this); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + // remove player from instance and set spawn location if any + try + { + final int instanceId = getInstanceId(); + if ((instanceId != 0) && !Config.RESTORE_PLAYER_INSTANCE) + { + final Instance inst = InstanceManager.getInstance().getInstance(instanceId); + if (inst != null) + { + inst.removePlayer(getObjectId()); + final int[] spawn = inst.getSpawnLoc(); + if ((spawn[0] != 0) && (spawn[1] != 0) && (spawn[2] != 0)) + { + final int x = spawn[0] + Rnd.get(-30, 30); + final int y = spawn[1] + Rnd.get(-30, 30); + setXYZInvisible(x, y, spawn[2]); + if (getPet() != null) // dead pet + { + getPet().teleToLocation(x, y, spawn[2]); + getPet().setInstanceId(0); + } + } + } + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + // TvT Event removal + try + { + TvTEvent.onLogout(this); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + // Update database with items in its inventory and remove them from the world + try + { + getInventory().deleteMe(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + // Update database with items in its warehouse and remove them from the world + try + { + clearWarehouse(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + if (Config.WAREHOUSE_CACHE) + { + WarehouseCacheManager.getInstance().remCacheTask(this); + } + + try + { + clearRefund(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + if (isCursedWeaponEquipped()) + { + try + { + CursedWeaponsManager.getInstance().getCursedWeapon(_cursedWeaponEquippedId).setPlayer(null); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + } + + // Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attak or Cast and notify AI + try + { + getKnownList().removeAllKnownObjects(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "deleteMe()", e); + } + + if (getClanId() > 0) + { + getClan().broadcastToOtherOnlineMembers(new PledgeShowMemberListUpdate(this), this); + // ClanTable.getInstance().getClan(getClanId()).broadcastToOnlineMembers(new PledgeShowMemberListAdd(this)); + } + + for (L2PcInstance player : _snoopedPlayer) + { + player.removeSnooper(this); + } + + for (L2PcInstance player : _snoopListener) + { + player.removeSnooped(this); + } + + // Remove L2Object object from _allObjects of L2World + L2World.getInstance().removeObject(this); + L2World.getInstance().removeFromAllPlayers(this); // force remove in case of crash during teleport + + // update bbs + try + { + RegionBBSManager.getInstance().changeCommunityBoard(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception on deleteMe() changeCommunityBoard: " + e.getMessage(), e); + } + + try + { + notifyFriends(); + getBlockList().playerLogout(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception on deleteMe() notifyFriends: " + e.getMessage(), e); + } + } + + private FishData _fish; + + /* + * startFishing() was stripped of any pre-fishing related checks, namely the fishing zone check. Also worthy of note is the fact the code to find the hook landing position was also striped. The stripped code was moved into fishing.java. In my opinion it makes more sense for it to be there since + * all other skill related checks were also there. Last but not least, moving the zone check there, fixed a bug where baits would always be consumed no matter if fishing actualy took place. startFishing() now takes up 3 arguments, wich are acurately described as being the hook landing + * coordinates. + */ + public void startFishing(int _x, int _y, int _z) + { + stopMove(null); + setIsImmobilized(true); + _fishing = true; + _fishx = _x; + _fishy = _y; + _fishz = _z; + // broadcastUserInfo(); + // Starts fishing + int lvl = GetRandomFishLvl(); + int group = GetRandomGroup(); + int type = GetRandomFishType(group); + List fishs = FishTable.getInstance().getfish(lvl, type, group); + if ((fishs == null) || fishs.isEmpty()) + { + sendMessage("Error - Fishes are not definied"); + endFishing(false); + return; + } + int check = Rnd.get(fishs.size()); + // Use a copy constructor else the fish data may be over-written below + _fish = new FishData(fishs.get(check)); + fishs.clear(); + fishs = null; + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CAST_LINE_AND_START_FISHING)); + if (!GameTimeController.getInstance().isNowNight() && _lure.isNightLure()) + { + _fish.setType(-1); + } + // sendMessage("Hook x,y: " + _x + "," + _y + " - Water Z, Player Z:" + _z + ", " + getZ()); //debug line, uncoment to show coordinates used in fishing. + broadcastPacket(new ExFishingStart(this, _fish.getType(), _x, _y, _z, _lure.isNightLure())); + sendPacket(new PlaySound(1, "SF_P_01", 0, 0, 0, 0, 0)); + startLookingForFishTask(); + } + + public void stopLookingForFishTask() + { + if (_taskforfish != null) + { + _taskforfish.cancel(false); + _taskforfish = null; + } + } + + public void startLookingForFishTask() + { + if (!isDead() && (_taskforfish == null)) + { + int checkDelay = 0; + boolean isNoob = false; + boolean isUpperGrade = false; + + if (_lure != null) + { + int lureid = _lure.getItemId(); + isNoob = _fish.getGroup() == 0; + isUpperGrade = _fish.getGroup() == 2; + if ((lureid == 6519) || (lureid == 6522) || (lureid == 6525) || (lureid == 8505) || (lureid == 8508) || (lureid == 8511)) + { + checkDelay = Math.round((float) (_fish.getGutsCheckTime() * (1.33))); + } + else if ((lureid == 6520) || (lureid == 6523) || (lureid == 6526) || ((lureid >= 8505) && (lureid <= 8513)) || ((lureid >= 7610) && (lureid <= 7613)) || ((lureid >= 7807) && (lureid <= 7809)) || ((lureid >= 8484) && (lureid <= 8486))) + { + checkDelay = Math.round((float) (_fish.getGutsCheckTime() * (1.00))); + } + else if ((lureid == 6521) || (lureid == 6524) || (lureid == 6527) || (lureid == 8507) || (lureid == 8510) || (lureid == 8513)) + { + checkDelay = Math.round((float) (_fish.getGutsCheckTime() * (0.66))); + } + } + _taskforfish = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new LookingForFishTask(_fish.getWaitTime(), _fish.getFishGuts(), _fish.getType(), isNoob, isUpperGrade), 10000, checkDelay); + } + } + + private int GetRandomGroup() + { + switch (_lure.getItemId()) + { + case 7807: // green for beginners + case 7808: // purple for beginners + case 7809: // yellow for beginners + case 8486: // prize-winning for beginners + return 0; + case 8485: // prize-winning luminous + case 8506: // green luminous + case 8509: // purple luminous + case 8512: // yellow luminous + return 2; + default: + return 1; + } + } + + private int GetRandomFishType(int group) + { + int check = Rnd.get(100); + int type = 1; + switch (group) + { + case 0: // fish for novices + switch (_lure.getItemId()) + { + case 7807: // green lure, preferred by fast-moving (nimble) fish (type 5) + if (check <= 54) + { + type = 5; + } + else if (check <= 77) + { + type = 4; + } + else + { + type = 6; + } + break; + case 7808: // purple lure, preferred by fat fish (type 4) + if (check <= 54) + { + type = 4; + } + else if (check <= 77) + { + type = 6; + } + else + { + type = 5; + } + break; + case 7809: // yellow lure, preferred by ugly fish (type 6) + if (check <= 54) + { + type = 6; + } + else if (check <= 77) + { + type = 5; + } + else + { + type = 4; + } + break; + case 8486: // prize-winning fishing lure for beginners + if (check <= 33) + { + type = 4; + } + else if (check <= 66) + { + type = 5; + } + else + { + type = 6; + } + break; + } + break; + case 1: // normal fish + switch (_lure.getItemId()) + { + case 7610: + case 7611: + case 7612: + case 7613: + type = 3; + break; + case 6519: // all theese lures (green) are prefered by fast-moving (nimble) fish (type 1) + case 8505: + case 6520: + case 6521: + case 8507: + if (check <= 54) + { + type = 1; + } + else if (check <= 74) + { + type = 0; + } + else if (check <= 94) + { + type = 2; + } + else + { + type = 3; + } + break; + case 6522: // all theese lures (purple) are prefered by fat fish (type 0) + case 8508: + case 6523: + case 6524: + case 8510: + if (check <= 54) + { + type = 0; + } + else if (check <= 74) + { + type = 1; + } + else if (check <= 94) + { + type = 2; + } + else + { + type = 3; + } + break; + case 6525: // all theese lures (yellow) are prefered by ugly fish (type 2) + case 8511: + case 6526: + case 6527: + case 8513: + if (check <= 55) + { + type = 2; + } + else if (check <= 74) + { + type = 1; + } + else if (check <= 94) + { + type = 0; + } + else + { + type = 3; + } + break; + case 8484: // prize-winning fishing lure + if (check <= 33) + { + type = 0; + } + else if (check <= 66) + { + type = 1; + } + else + { + type = 2; + } + break; + } + break; + case 2: // upper grade fish, luminous lure + switch (_lure.getItemId()) + { + case 8506: // green lure, preferred by fast-moving (nimble) fish (type 8) + if (check <= 54) + { + type = 8; + } + else if (check <= 77) + { + type = 7; + } + else + { + type = 9; + } + break; + case 8509: // purple lure, preferred by fat fish (type 7) + if (check <= 54) + { + type = 7; + } + else if (check <= 77) + { + type = 9; + } + else + { + type = 8; + } + break; + case 8512: // yellow lure, preferred by ugly fish (type 9) + if (check <= 54) + { + type = 9; + } + else if (check <= 77) + { + type = 8; + } + else + { + type = 7; + } + break; + case 8485: // prize-winning fishing lure + if (check <= 33) + { + type = 7; + } + else if (check <= 66) + { + type = 8; + } + else + { + type = 9; + } + break; + } + } + return type; + } + + private int GetRandomFishLvl() + { + int skilllvl = getSkillLevel(1315); + final L2Effect e = getFirstEffect(2274); + if (e != null) + { + skilllvl = (int) e.getSkill().getPower(); + } + if (skilllvl <= 0) + { + return 1; + } + int randomlvl; + int check = Rnd.get(100); + + if (check <= 50) + { + randomlvl = skilllvl; + } + else if (check <= 85) + { + randomlvl = skilllvl - 1; + if (randomlvl <= 0) + { + randomlvl = 1; + } + } + else + { + randomlvl = skilllvl + 1; + if (randomlvl > 27) + { + randomlvl = 27; + } + } + + return randomlvl; + } + + public void startFishCombat(boolean isNoob, boolean isUpperGrade) + { + _fishCombat = new L2Fishing(this, _fish, isNoob, isUpperGrade); + } + + public void endFishing(boolean win) + { + _fishing = false; + _fishx = 0; + _fishy = 0; + _fishz = 0; + // broadcastUserInfo(); + if (_fishCombat == null) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.BAIT_LOST_FISH_GOT_AWAY)); + } + _fishCombat = null; + _lure = null; + // Ends fishing + broadcastPacket(new ExFishingEnd(win, this)); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.REEL_LINE_AND_STOP_FISHING)); + setIsImmobilized(false); + stopLookingForFishTask(); + } + + public L2Fishing getFishCombat() + { + return _fishCombat; + } + + public int getFishx() + { + return _fishx; + } + + public int getFishy() + { + return _fishy; + } + + public int getFishz() + { + return _fishz; + } + + public void setLure(L2ItemInstance lure) + { + _lure = lure; + } + + public L2ItemInstance getLure() + { + return _lure; + } + + public int getInventoryLimit() + { + int ivlim; + if (isGM()) + { + ivlim = Config.INVENTORY_MAXIMUM_GM; + } + else if (getRace() == Race.Dwarf) + { + ivlim = Config.INVENTORY_MAXIMUM_DWARF; + } + else + { + ivlim = Config.INVENTORY_MAXIMUM_NO_DWARF; + } + ivlim += (int) getStat().calcStat(Stats.INV_LIM, 0, null, null); + + return ivlim; + } + + public int getWareHouseLimit() + { + int whlim; + if (getRace() == Race.Dwarf) + { + whlim = Config.WAREHOUSE_SLOTS_DWARF; + } + else + { + whlim = Config.WAREHOUSE_SLOTS_NO_DWARF; + } + + whlim += (int) getStat().calcStat(Stats.WH_LIM, 0, null, null); + + return whlim; + } + + public int getPrivateSellStoreLimit() + { + int pslim; + + if (getRace() == Race.Dwarf) + { + pslim = Config.MAX_PVTSTORESELL_SLOTS_DWARF; + } + else + { + pslim = Config.MAX_PVTSTORESELL_SLOTS_OTHER; + } + + pslim += (int) getStat().calcStat(Stats.P_SELL_LIM, 0, null, null); + + return pslim; + } + + public int getPrivateBuyStoreLimit() + { + int pblim; + + if (getRace() == Race.Dwarf) + { + pblim = Config.MAX_PVTSTOREBUY_SLOTS_DWARF; + } + else + { + pblim = Config.MAX_PVTSTOREBUY_SLOTS_OTHER; + } + pblim += (int) getStat().calcStat(Stats.P_BUY_LIM, 0, null, null); + + return pblim; + } + + public int getDwarfRecipeLimit() + { + int recdlim = Config.DWARF_RECIPE_LIMIT; + recdlim += (int) getStat().calcStat(Stats.REC_D_LIM, 0, null, null); + return recdlim; + } + + public int getCommonRecipeLimit() + { + int recclim = Config.COMMON_RECIPE_LIMIT; + recclim += (int) getStat().calcStat(Stats.REC_C_LIM, 0, null, null); + return recclim; + } + + /** + * @return Returns the mountNpcId. + */ + public int getMountNpcId() + { + return _mountNpcId; + } + + /** + * @return Returns the mountLevel. + */ + public int getMountLevel() + { + return _mountLevel; + } + + public void setMountObjectID(int newID) + { + _mountObjectID = newID; + } + + public int getMountObjectID() + { + return _mountObjectID; + } + + private L2ItemInstance _lure = null; + public int _shortBuffTaskSkillId = 0; + + /** + * Get the current skill in use or return null.
    + *
    + */ + public SkillDat getCurrentSkill() + { + return _currentSkill; + } + + /** + * Create a new SkillDat object and set the player _currentSkill.
    + *
    + */ + public void setCurrentSkill(L2Skill currentSkill, boolean ctrlPressed, boolean shiftPressed) + { + if (currentSkill == null) + { + if (Config.DEBUG) + { + _log.info("Setting current skill: NULL for " + getName() + "."); + } + + _currentSkill = null; + return; + } + + if (Config.DEBUG) + { + _log.info("Setting current skill: " + currentSkill.getName() + " (ID: " + currentSkill.getId() + ") for " + getName() + "."); + } + + _currentSkill = new SkillDat(currentSkill, ctrlPressed, shiftPressed); + } + + /** + * Get the current pet skill in use or return null.
    + *
    + */ + public SkillDat getCurrentPetSkill() + { + return _currentPetSkill; + } + + /** + * Create a new SkillDat object and set the player _currentPetSkill.
    + *
    + */ + public void setCurrentPetSkill(L2Skill currentSkill, boolean ctrlPressed, boolean shiftPressed) + { + if (currentSkill == null) + { + if (Config.DEBUG) + { + _log.info("Setting current pet skill: NULL for " + getName() + "."); + } + + _currentPetSkill = null; + return; + } + + if (Config.DEBUG) + { + _log.info("Setting current Pet skill: " + currentSkill.getName() + " (ID: " + currentSkill.getId() + ") for " + getName() + "."); + } + + _currentPetSkill = new SkillDat(currentSkill, ctrlPressed, shiftPressed); + } + + public SkillDat getQueuedSkill() + { + return _queuedSkill; + } + + /** + * Create a new SkillDat object and queue it in the player _queuedSkill.
    + *
    + */ + public void setQueuedSkill(L2Skill queuedSkill, boolean ctrlPressed, boolean shiftPressed) + { + if (queuedSkill == null) + { + if (Config.DEBUG) + { + _log.info("Setting queued skill: NULL for " + getName() + "."); + } + + _queuedSkill = null; + return; + } + + if (Config.DEBUG) + { + _log.info("Setting queued skill: " + queuedSkill.getName() + " (ID: " + queuedSkill.getId() + ") for " + getName() + "."); + } + + _queuedSkill = new SkillDat(queuedSkill, ctrlPressed, shiftPressed); + } + + /** + * returns punishment level of player + * @return + */ + public PunishLevel getPunishLevel() + { + return _punishLevel; + } + + /** + * @return True if player is jailed + */ + public boolean isInJail() + { + return _punishLevel == PunishLevel.JAIL; + } + + /** + * @return True if player is chat banned + */ + public boolean isChatBanned() + { + return _punishLevel == PunishLevel.CHAT; + } + + public void setPunishLevel(int state) + { + switch (state) + { + case 0: + { + _punishLevel = PunishLevel.NONE; + break; + } + case 1: + { + _punishLevel = PunishLevel.CHAT; + break; + } + case 2: + { + _punishLevel = PunishLevel.JAIL; + break; + } + case 3: + { + _punishLevel = PunishLevel.CHAR; + break; + } + case 4: + { + _punishLevel = PunishLevel.ACC; + break; + } + } + } + + /** + * Sets punish level for player based on delay + * @param state + * @param delayInMinutes 0 - Indefinite + */ + public void setPunishLevel(PunishLevel state, int delayInMinutes) + { + long delayInMilliseconds = delayInMinutes * 60000L; + switch (state) + { + case NONE: // Remove Punishments + { + switch (_punishLevel) + { + case CHAT: + { + _punishLevel = state; + stopPunishTask(true); + sendPacket(new EtcStatusUpdate(this)); + sendMessage("Your Chat ban has been lifted"); + break; + } + case JAIL: + { + _punishLevel = state; + // Open a Html message to inform the player + NpcHtmlMessage htmlMsg = new NpcHtmlMessage(0); + String jailInfos = HtmCache.getInstance().getHtm(getHtmlPrefix(), "data/html/jail_out.htm"); + if (jailInfos != null) + { + htmlMsg.setHtml(jailInfos); + } + else + { + htmlMsg.setHtml("You are free for now, respect server rules!"); + } + sendPacket(htmlMsg); + stopPunishTask(true); + teleToLocation(17836, 170178, -3507, true); // Floran + break; + } + } + break; + } + case CHAT: // Chat Ban + { + // not allow player to escape jail using chat ban + if (_punishLevel == PunishLevel.JAIL) + { + break; + } + _punishLevel = state; + _punishTimer = 0; + sendPacket(new EtcStatusUpdate(this)); + // Remove the task if any + stopPunishTask(false); + + if (delayInMinutes > 0) + { + _punishTimer = delayInMilliseconds; + + // start the countdown + _punishTask = ThreadPoolManager.getInstance().scheduleGeneral(new PunishTask(), _punishTimer); + sendMessage("You are chat banned for " + delayInMinutes + " minutes."); + } + else + { + sendMessage("You have been chat banned"); + } + break; + + } + case JAIL: // Jail Player + { + _punishLevel = state; + _punishTimer = 0; + // Remove the task if any + stopPunishTask(false); + + if (delayInMinutes > 0) + { + _punishTimer = delayInMilliseconds; + + // start the countdown + _punishTask = ThreadPoolManager.getInstance().scheduleGeneral(new PunishTask(), _punishTimer); + sendMessage("You are in jail for " + delayInMinutes + " minutes."); + } + + if (!TvTEvent.isInactive() && TvTEvent.isPlayerParticipant(getObjectId())) + { + TvTEvent.removeParticipant(getObjectId()); + } + if (OlympiadManager.getInstance().isRegisteredInComp(this)) + { + OlympiadManager.getInstance().removeDisconnectedCompetitor(this); + } + + // Open a Html message to inform the player + NpcHtmlMessage htmlMsg = new NpcHtmlMessage(0); + String jailInfos = HtmCache.getInstance().getHtm(getHtmlPrefix(), "data/html/jail_in.htm"); + if (jailInfos != null) + { + htmlMsg.setHtml(jailInfos); + } + else + { + htmlMsg.setHtml("You have been put in jail by an admin."); + } + sendPacket(htmlMsg); + setInstanceId(0); + setIsIn7sDungeon(false); + + teleToLocation(-114356, -249645, -2984, false); // Jail + break; + } + case CHAR: // Ban Character + { + setAccessLevel(-100); + logout(); + break; + } + case ACC: // Ban Account + { + setAccountAccesslevel(-100); + logout(); + break; + } + default: + { + _punishLevel = state; + break; + } + } + + // store in database + storeCharBase(); + } + + public long getPunishTimer() + { + return _punishTimer; + } + + public void setPunishTimer(long time) + { + _punishTimer = time; + } + + private void updatePunishState() + { + if (getPunishLevel() != PunishLevel.NONE) + { + // If punish timer exists, restart punishtask. + if (_punishTimer > 0) + { + _punishTask = ThreadPoolManager.getInstance().scheduleGeneral(new PunishTask(), _punishTimer); + sendMessage("You are still " + getPunishLevel().string() + " for " + Math.round(_punishTimer / 60000f) + " minutes."); + } + if (getPunishLevel() == PunishLevel.JAIL) + { + // If player escaped, put him back in jail + if (!isInsideZone(ZONE_JAIL)) + { + teleToLocation(-114356, -249645, -2984, true); + } + } + } + } + + public void stopPunishTask(boolean save) + { + if (_punishTask != null) + { + if (save) + { + long delay = _punishTask.getDelay(TimeUnit.MILLISECONDS); + if (delay < 0) + { + delay = 0; + } + setPunishTimer(delay); + } + _punishTask.cancel(false); + // ThreadPoolManager.getInstance().removeGeneral((Runnable)_punishTask); + _punishTask = null; + } + } + + protected class PunishTask implements Runnable + { + @Override + public void run() + { + L2PcInstance.this.setPunishLevel(PunishLevel.NONE, 0); + } + } + + public void startFameTask(long delay, int fameFixRate) + { + if ((getLevel() < 40) || (getClassId().level() < 2)) + { + return; + } + if (_fameTask == null) + { + _fameTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FameTask(fameFixRate), delay, delay); + } + } + + public void stopFameTask() + { + if (_fameTask != null) + { + _fameTask.cancel(false); + // ThreadPoolManager.getInstance().removeGeneral((Runnable)_fameTask); + _fameTask = null; + } + } + + private class FameTask implements Runnable + { + private final L2PcInstance _player; + private final int _value; + + protected FameTask(int value) + { + _player = L2PcInstance.this; + _value = value; + } + + @Override + public void run() + { + if ((_player == null) || (_player.isDead() && !Config.FAME_FOR_DEAD_PLAYERS)) + { + return; + } + if (((_player.getClient() == null) || _player.getClient().isDetached()) && !Config.OFFLINE_FAME) + { + return; + } + _player.setFame(_player.getFame() + _value); + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.ACQUIRED_S1_REPUTATION_SCORE); + sm.addNumber(_value); + _player.sendPacket(sm); + _player.sendPacket(new UserInfo(_player)); + } + } + + public void startVitalityTask() + { + if (Config.ENABLE_VITALITY && (_vitalityTask == null)) + { + _vitalityTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new VitalityTask(this), 1000, 60000); + } + } + + public void stopVitalityTask() + { + if (_vitalityTask != null) + { + _vitalityTask.cancel(false); + // ThreadPoolManager.getInstance().removeGeneral((Runnable)_vitalityTask); + _vitalityTask = null; + } + } + + private class VitalityTask implements Runnable + { + private final L2PcInstance _player; + + protected VitalityTask(L2PcInstance player) + { + _player = player; + } + + @Override + public void run() + { + if (!_player.isInsideZone(L2Character.ZONE_PEACE)) + { + return; + } + + if (_player.getVitalityPoints() >= PcStat.MAX_VITALITY_POINTS) + { + return; + } + + _player.updateVitalityPoints(Config.RATE_RECOVERY_VITALITY_PEACE_ZONE, false, false); + _player.sendPacket(new ExVitalityPointInfo(getVitalityPoints())); + } + } + + /** + * @return + */ + public int getPowerGrade() + { + return _powerGrade; + } + + /** + * @return + */ + public void setPowerGrade(int power) + { + _powerGrade = power; + } + + public boolean isCursedWeaponEquipped() + { + return _cursedWeaponEquippedId != 0; + } + + public void setCursedWeaponEquippedId(int value) + { + _cursedWeaponEquippedId = value; + } + + public int getCursedWeaponEquippedId() + { + return _cursedWeaponEquippedId; + } + + @Override + public boolean isAttackingDisabled() + { + return (super.isAttackingDisabled() || _combatFlagEquippedId); + } + + public boolean isCombatFlagEquipped() + { + return _combatFlagEquippedId; + } + + public void setCombatFlagEquipped(boolean value) + { + _combatFlagEquippedId = value; + } + + public final void setIsRidingStrider(boolean mode) + { + _isRidingStrider = mode; + } + + public final boolean isRidingStrider() + { + return _isRidingStrider; + } + + /** + * Returns the Number of Souls this L2PcInstance got. + * @return + */ + public int getSouls() + { + return _souls; + } + + /** + * Absorbs a Soul from a Npc. + * @param skill + * @param target + */ + public void absorbSoul(L2Skill skill, L2Npc npc) + { + if (_souls >= skill.getNumSouls()) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.SOUL_CANNOT_BE_INCREASED_ANYMORE); + sendPacket(sm); + return; + } + + increaseSouls(1); + + if (npc != null) + { + broadcastPacket(new ExSpawnEmitter(this, npc), 500); + } + } + + /** + * Increase Souls + * @param count + */ + public void increaseSouls(int count) + { + if ((count < 0) || (count > 45)) + { + return; + } + + _souls += count; + + if (getSouls() > 45) + { + _souls = 45; + } + + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOUR_SOUL_HAS_INCREASED_BY_S1_SO_IT_IS_NOW_AT_S2); + sm.addNumber(count); + sm.addNumber(_souls); + sendPacket(sm); + + restartSoulTask(); + + sendPacket(new EtcStatusUpdate(this)); + } + + /** + * Decreases existing Souls. + * @param count + */ + public boolean decreaseSouls(int count, L2Skill skill) + { + if ((getSouls() <= 0) && (skill.getSoulConsumeCount() > 0)) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.THERE_IS_NOT_ENOUGH_SOUL)); + return false; + } + + _souls -= count; + + if (getSouls() < 0) + { + _souls = 0; + } + + if (getSouls() == 0) + { + stopSoulTask(); + } + else + { + restartSoulTask(); + } + + sendPacket(new EtcStatusUpdate(this)); + return true; + } + + /** + * Clear out all Souls from this L2PcInstance + */ + public void clearSouls() + { + _souls = 0; + stopSoulTask(); + sendPacket(new EtcStatusUpdate(this)); + } + + /** + * Starts/Restarts the SoulTask to Clear Souls after 10 Mins. + */ + private void restartSoulTask() + { + synchronized (this) + { + if (_soulTask != null) + { + _soulTask.cancel(false); + _soulTask = null; + } + _soulTask = ThreadPoolManager.getInstance().scheduleGeneral(new SoulTask(), 600000); + } + } + + /** + * Stops the Clearing Task. + */ + public void stopSoulTask() + { + if (_soulTask != null) + { + _soulTask.cancel(false); + // ThreadPoolManager.getInstance().removeGeneral((Runnable)_soulTask); + _soulTask = null; + } + } + + protected class SoulTask implements Runnable + { + @Override + public void run() + { + clearSouls(); + } + } + + /** + * @param magicId + * @param level + * @param time + */ + public void shortBuffStatusUpdate(int magicId, int level, int time) + { + if (_shortBuffTask != null) + { + _shortBuffTask.cancel(false); + _shortBuffTask = null; + } + _shortBuffTask = ThreadPoolManager.getInstance().scheduleGeneral(new ShortBuffTask(), time * 1000); + setShortBuffTaskSkillId(magicId); + + sendPacket(new ShortBuffStatusUpdate(magicId, level, time)); + } + + public void setShortBuffTaskSkillId(int id) + { + _shortBuffTaskSkillId = id; + } + + public int getDeathPenaltyBuffLevel() + { + return _deathPenaltyBuffLevel; + } + + public void setDeathPenaltyBuffLevel(int level) + { + _deathPenaltyBuffLevel = level; + } + + public void calculateDeathPenaltyBuffLevel(L2Character killer) + { + if (((getKarma() > 0) || (Rnd.get(1, 100) <= Config.DEATH_PENALTY_CHANCE)) && !(killer instanceof L2PcInstance) && !(isGM()) && !(getCharmOfLuck() && killer.isRaid()) && !isPhoenixBlessed() && !isLucky() && !(TvTEvent.isStarted() && TvTEvent.isPlayerParticipant(getObjectId())) && !(isInsideZone(L2Character.ZONE_PVP) || isInsideZone(L2Character.ZONE_SIEGE))) + { + increaseDeathPenaltyBuffLevel(); + } + } + + public void increaseDeathPenaltyBuffLevel() + { + if (getDeathPenaltyBuffLevel() >= 15) + { + return; + } + + if (getDeathPenaltyBuffLevel() != 0) + { + L2Skill skill = SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel()); + + if (skill != null) + { + removeSkill(skill, true); + } + } + + _deathPenaltyBuffLevel++; + + addSkill(SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel()), false); + sendPacket(new EtcStatusUpdate(this)); + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DEATH_PENALTY_LEVEL_S1_ADDED); + sm.addNumber(getDeathPenaltyBuffLevel()); + sendPacket(sm); + } + + public void reduceDeathPenaltyBuffLevel() + { + if (getDeathPenaltyBuffLevel() <= 0) + { + return; + } + + L2Skill skill = SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel()); + + if (skill != null) + { + removeSkill(skill, true); + } + + _deathPenaltyBuffLevel--; + + if (getDeathPenaltyBuffLevel() > 0) + { + addSkill(SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel()), false); + sendPacket(new EtcStatusUpdate(this)); + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DEATH_PENALTY_LEVEL_S1_ADDED); + sm.addNumber(getDeathPenaltyBuffLevel()); + sendPacket(sm); + } + else + { + sendPacket(new EtcStatusUpdate(this)); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.DEATH_PENALTY_LIFTED)); + } + } + + public void restoreDeathPenaltyBuffLevel() + { + if (getDeathPenaltyBuffLevel() > 0) + { + addSkill(SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel()), false); + } + } + + private final FastMap _reuseTimeStamps = new FastMap().shared(); + private boolean _canFeed; + private int _eventEffectId = 0; + private boolean _isInSiege; + + public Collection getReuseTimeStamps() + { + return _reuseTimeStamps.values(); + } + + public FastMap getReuseTimeStamp() + { + return _reuseTimeStamps; + } + + /** + * Simple class containing all neccessary information to maintain valid timestamps and reuse for skills upon relog. Filter this carefully as it becomes redundant to store reuse for small delays. + * @author Yesod + */ + public static class TimeStamp + { + private final int _skillId; + private final int _skillLvl; + private final long _reuse; + private final long _stamp; + + public TimeStamp(L2Skill skill, long reuse) + { + _skillId = skill.getId(); + _skillLvl = skill.getLevel(); + _reuse = reuse; + _stamp = System.currentTimeMillis() + reuse; + } + + public TimeStamp(L2Skill skill, long reuse, long systime) + { + _skillId = skill.getId(); + _skillLvl = skill.getLevel(); + _reuse = reuse; + _stamp = systime; + } + + public long getStamp() + { + return _stamp; + } + + public int getSkillId() + { + return _skillId; + } + + public int getSkillLvl() + { + return _skillLvl; + } + + public long getReuse() + { + return _reuse; + } + + public long getRemaining() + { + return Math.max(_stamp - System.currentTimeMillis(), 0); + } + + /* + * Check if the reuse delay has passed and if it has not then update the stored reuse time according to what is currently remaining on the delay. + */ + public boolean hasNotPassed() + { + return System.currentTimeMillis() < _stamp; + } + } + + /** + * Index according to skill id the current timestamp of use. + * @param skillid + * @param reuse delay + */ + @Override + public void addTimeStamp(L2Skill skill, long reuse) + { + _reuseTimeStamps.put(skill.getReuseHashCode(), new TimeStamp(skill, reuse)); + } + + /** + * Index according to skill this TimeStamp instance for restoration purposes only. + * @param TimeStamp + */ + public void addTimeStamp(L2Skill skill, long reuse, long systime) + { + _reuseTimeStamps.put(skill.getReuseHashCode(), new TimeStamp(skill, reuse, systime)); + } + + @Override + public L2PcInstance getActingPlayer() + { + return this; + } + + @Override + public final void sendDamageMessage(L2Character target, int damage, boolean mcrit, boolean pcrit, boolean miss) + { + // Check if hit is missed + if (miss) + { + if (target instanceof L2PcInstance) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_EVADED_C2_ATTACK); + sm.addPcName((L2PcInstance) target); + sm.addCharName(this); + target.sendPacket(sm); + } + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.C1_ATTACK_WENT_ASTRAY).addPcName(this)); + return; + } + + // Check if hit is critical + if (pcrit) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.C1_HAD_CRITICAL_HIT).addPcName(this)); + if ((target instanceof L2Npc) && (getSkillLevel(467) > 0)) + { + L2Skill skill = SkillTable.getInstance().getInfo(467, getSkillLevel(467)); + if (Rnd.get(100) < skill.getCritChance()) + { + absorbSoul(skill, ((L2Npc) target)); + } + } + } + if (mcrit) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CRITICAL_HIT_MAGIC)); + } + + if (isInOlympiadMode() && (target instanceof L2PcInstance) && ((L2PcInstance) target).isInOlympiadMode() && (((L2PcInstance) target).getOlympiadGameId() == getOlympiadGameId())) + { + OlympiadGameManager.getInstance().notifyCompetitorDamage(this, damage); + } + + final SystemMessage sm; + + if (target.isInvul() && !(target instanceof L2Npc)) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.ATTACK_WAS_BLOCKED); + } + else if ((target instanceof L2DoorInstance) || (target instanceof L2ControlTowerInstance)) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_DID_S1_DMG); + sm.addNumber(damage); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.C1_GAVE_C2_DAMAGE_OF_S3); + sm.addPcName(this); + sm.addCharName(target); + sm.addNumber(damage); + } + + sendPacket(sm); + } + + /** + * @param npcId + */ + public void setAgathionId(int npcId) + { + _agathionId = npcId; + } + + /** + * @return + */ + public int getAgathionId() + { + return _agathionId; + } + + public int getVitalityPoints() + { + return getStat().getVitalityPoints(); + } + + /** + * @return Vitality Level + */ + public int getVitalityLevel() + { + return getStat().getVitalityLevel(); + } + + public void setVitalityPoints(int points, boolean quiet) + { + getStat().setVitalityPoints(points, quiet); + } + + public void updateVitalityPoints(float points, boolean useRates, boolean quiet) + { + getStat().updateVitalityPoints(points, useRates, quiet); + } + + /* + * Function for skill summon friend or Gate Chant. + */ + /** Request Teleport **/ + public boolean teleportRequest(L2PcInstance requester, L2Skill skill) + { + if ((_summonRequest.getTarget() != null) && (requester != null)) + { + return false; + } + _summonRequest.setTarget(requester, skill); + return true; + } + + /** Action teleport **/ + public void teleportAnswer(int answer, int requesterId) + { + if (_summonRequest.getTarget() == null) + { + return; + } + if ((answer == 1) && (_summonRequest.getTarget().getObjectId() == requesterId)) + { + teleToTarget(this, _summonRequest.getTarget(), _summonRequest.getSkill()); + } + _summonRequest.setTarget(null, null); + } + + public static void teleToTarget(L2PcInstance targetChar, L2PcInstance summonerChar, L2Skill summonSkill) + { + if ((targetChar == null) || (summonerChar == null) || (summonSkill == null)) + { + return; + } + + if (!checkSummonerStatus(summonerChar)) + { + return; + } + if (!checkSummonTargetStatus(targetChar, summonerChar)) + { + return; + } + + int itemConsumeId = summonSkill.getTargetConsumeId(); + int itemConsumeCount = summonSkill.getTargetConsume(); + if ((itemConsumeId != 0) && (itemConsumeCount != 0)) + { + // Delete by rocknow + if (targetChar.getInventory().getInventoryItemCount(itemConsumeId, 0) < itemConsumeCount) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_REQUIRED_FOR_SUMMONING); + sm.addItemName(summonSkill.getTargetConsumeId()); + targetChar.sendPacket(sm); + return; + } + targetChar.getInventory().destroyItemByItemId("Consume", itemConsumeId, itemConsumeCount, summonerChar, targetChar); + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); + sm.addItemName(summonSkill.getTargetConsumeId()); + targetChar.sendPacket(sm); + } + targetChar.teleToLocation(summonerChar.getX(), summonerChar.getY(), summonerChar.getZ(), true); + } + + public static boolean checkSummonerStatus(L2PcInstance summonerChar) + { + if (summonerChar == null) + { + return false; + } + + if (summonerChar.isInOlympiadMode()) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.THIS_ITEM_IS_NOT_AVAILABLE_FOR_THE_OLYMPIAD_EVENT)); + return false; + } + + if (summonerChar.inObserverMode()) + { + return false; + } + + if (!TvTEvent.onEscapeUse(summonerChar.getObjectId())) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_TARGET_IS_IN_AN_AREA_WHICH_BLOCKS_SUMMONING)); + return false; + } + + if (summonerChar.isInsideZone(L2Character.ZONE_NOSUMMONFRIEND) || summonerChar.isFlyingMounted()) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_TARGET_IS_IN_AN_AREA_WHICH_BLOCKS_SUMMONING)); + return false; + } + return true; + } + + public static boolean checkSummonTargetStatus(L2Object target, L2PcInstance summonerChar) + { + if ((target == null) || !(target instanceof L2PcInstance)) + { + return false; + } + + L2PcInstance targetChar = (L2PcInstance) target; + + if (targetChar.isAlikeDead()) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_IS_DEAD_AT_THE_MOMENT_AND_CANNOT_BE_SUMMONED); + sm.addPcName(targetChar); + summonerChar.sendPacket(sm); + return false; + } + + if (targetChar.isInStoreMode()) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CURRENTLY_TRADING_OR_OPERATING_PRIVATE_STORE_AND_CANNOT_BE_SUMMONED); + sm.addPcName(targetChar); + summonerChar.sendPacket(sm); + return false; + } + + if (targetChar.isRooted() || targetChar.isInCombat()) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_IS_ENGAGED_IN_COMBAT_AND_CANNOT_BE_SUMMONED); + sm.addPcName(targetChar); + summonerChar.sendPacket(sm); + return false; + } + + if (targetChar.isInOlympiadMode()) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_SUMMON_PLAYERS_WHO_ARE_IN_OLYMPIAD)); + return false; + } + + if (targetChar.isFestivalParticipant() || targetChar.isFlyingMounted()) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_TARGET_IS_IN_AN_AREA_WHICH_BLOCKS_SUMMONING)); + return false; + } + + if (targetChar.inObserverMode()) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.C1_STATE_FORBIDS_SUMMONING).addCharName(targetChar)); + return false; + } + + if (targetChar.isCombatFlagEquipped()) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_TARGET_IS_IN_AN_AREA_WHICH_BLOCKS_SUMMONING)); + return false; + } + + if (!TvTEvent.onEscapeUse(targetChar.getObjectId())) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_TARGET_IS_IN_AN_AREA_WHICH_BLOCKS_SUMMONING)); + return false; + } + + if (targetChar.isInsideZone(L2Character.ZONE_NOSUMMONFRIEND)) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_IN_SUMMON_BLOCKING_AREA); + sm.addString(targetChar.getName()); + summonerChar.sendPacket(sm); + return false; + } + + if (summonerChar.getInstanceId() > 0) + { + Instance summonerInstance = InstanceManager.getInstance().getInstance(summonerChar.getInstanceId()); + if (!Config.ALLOW_SUMMON_TO_INSTANCE || !summonerInstance.isSummonAllowed()) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_MAY_NOT_SUMMON_FROM_YOUR_CURRENT_LOCATION)); + return false; + } + } + + // on retail character can enter 7s dungeon with summon friend, + // but will be teleported away by mobs + // because currently this is not working in L2J we do not allowing summoning + if (summonerChar.isIn7sDungeon()) + { + int targetCabal = SevenSigns.getInstance().getPlayerCabal(targetChar.getObjectId()); + if (SevenSigns.getInstance().isSealValidationPeriod()) + { + if (targetCabal != SevenSigns.getInstance().getCabalHighestScore()) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_TARGET_IS_IN_AN_AREA_WHICH_BLOCKS_SUMMONING)); + return false; + } + } + else + { + if (targetCabal == SevenSigns.CABAL_NULL) + { + summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_TARGET_IS_IN_AN_AREA_WHICH_BLOCKS_SUMMONING)); + return false; + } + } + } + + return true; + } + + public void gatesRequest(L2DoorInstance door) + { + _gatesRequest.setTarget(door); + } + + public void gatesAnswer(int answer, int type) + { + if (_gatesRequest.getDoor() == null) + { + return; + } + + if ((answer == 1) && (getTarget() == _gatesRequest.getDoor()) && (type == 1)) + { + _gatesRequest.getDoor().openMe(); + } + else if ((answer == 1) && (getTarget() == _gatesRequest.getDoor()) && (type == 0)) + { + _gatesRequest.getDoor().closeMe(); + } + + _gatesRequest.setTarget(null); + } + + public void checkItemRestriction() + { + for (int i = 0; i < Inventory.PAPERDOLL_TOTALSLOTS; i++) + { + L2ItemInstance equippedItem = getInventory().getPaperdollItem(i); + if ((equippedItem != null) && (!equippedItem.getItem().checkCondition(this, this, false) || (isInOlympiadMode() && equippedItem.isOlyRestrictedItem()))) + { + getInventory().unEquipItemInSlot(i); + + InventoryUpdate iu = new InventoryUpdate(); + iu.addModifiedItem(equippedItem); + sendPacket(iu); + + SystemMessage sm = null; + if (equippedItem.getItem().getBodyPart() == L2Item.SLOT_BACK) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CLOAK_REMOVED_BECAUSE_ARMOR_SET_REMOVED)); + return; + } + + if (equippedItem.getEnchantLevel() > 0) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED); + sm.addNumber(equippedItem.getEnchantLevel()); + sm.addItemName(equippedItem); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISARMED); + sm.addItemName(equippedItem); + } + sendPacket(sm); + } + } + } + + public void setTransformAllowedSkills(int[] ids) + { + _transformAllowedSkills = ids; + } + + public boolean containsAllowedTransformSkill(int id) + { + for (int _transformAllowedSkill : _transformAllowedSkills) + { + if (_transformAllowedSkill == id) + { + return true; + } + } + return false; + } + + /** Section for mounted pets */ + protected class FeedTask implements Runnable + { + @Override + public void run() + { + try + { + if (!isMounted()) + { + stopFeed(); + return; + } + + if (getCurrentFeed() > getFeedConsume()) + { + // eat + setCurrentFeed(getCurrentFeed() - getFeedConsume()); + } + else + { + // go back to pet control item, or simply said, unsummon it + setCurrentFeed(0); + stopFeed(); + dismount(); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.OUT_OF_FEED_MOUNT_CANCELED)); + } + + int[] foodIds = getPetData(getMountNpcId()).getFood(); + if (foodIds.length == 0) + { + return; + } + L2ItemInstance food = null; + for (int id : foodIds) + { + // TODO: possibly pet inv? + food = getInventory().getItemByItemId(id); + if (food != null) + { + break; + } + } + + if ((food != null) && isHungry()) + { + IItemHandler handler = ItemHandler.getInstance().getItemHandler(food.getEtcItem()); + if (handler != null) + { + handler.useItem(L2PcInstance.this, food, false); + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.PET_TOOK_S1_BECAUSE_HE_WAS_HUNGRY); + sm.addItemName(food.getItemId()); + sendPacket(sm); + } + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Mounted Pet [NpcId: " + getMountNpcId() + "] a feed task error has occurred", e); + } + } + } + + protected synchronized void startFeed(int npcId) + { + _canFeed = npcId > 0; + if (!isMounted()) + { + return; + } + if (getPet() != null) + { + setCurrentFeed(((L2PetInstance) getPet()).getCurrentFed()); + _controlItemId = getPet().getControlObjectId(); + sendPacket(new SetupGauge(3, (getCurrentFeed() * 10000) / getFeedConsume(), (getMaxFeed() * 10000) / getFeedConsume())); + if (!isDead()) + { + _mountFeedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FeedTask(), 10000, 10000); + } + } + else if (_canFeed) + { + setCurrentFeed(getMaxFeed()); + SetupGauge sg = new SetupGauge(3, (getCurrentFeed() * 10000) / getFeedConsume(), (getMaxFeed() * 10000) / getFeedConsume()); + sendPacket(sg); + if (!isDead()) + { + _mountFeedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FeedTask(), 10000, 10000); + } + } + } + + protected synchronized void stopFeed() + { + if (_mountFeedTask != null) + { + _mountFeedTask.cancel(false); + // ThreadPoolManager.getInstance().removeGeneral((Runnable)_mountFeedTask); + _mountFeedTask = null; + if (Config.DEBUG) + { + _log.fine("Pet [#" + _mountNpcId + "] feed task stop"); + } + } + } + + private final void clearPetData() + { + _data = null; + } + + protected final L2PetData getPetData(int npcId) + { + if (_data == null) + { + _data = PetDataTable.getInstance().getPetData(npcId); + } + return _data; + } + + private final L2PetLevelData getPetLevelData(int npcId) + { + if (_leveldata == null) + { + _leveldata = PetDataTable.getInstance().getPetData(npcId).getPetLevelData(getMountLevel()); + } + return _leveldata; + } + + public int getCurrentFeed() + { + return _curFeed; + } + + protected int getFeedConsume() + { + // if pet is attacking + if (isAttackingNow()) + { + return getPetLevelData(_mountNpcId).getPetFeedBattle(); + } + return getPetLevelData(_mountNpcId).getPetFeedNormal(); + } + + public void setCurrentFeed(int num) + { + _curFeed = num > getMaxFeed() ? getMaxFeed() : num; + SetupGauge sg = new SetupGauge(3, (getCurrentFeed() * 10000) / getFeedConsume(), (getMaxFeed() * 10000) / getFeedConsume()); + sendPacket(sg); + } + + private int getMaxFeed() + { + return getPetLevelData(_mountNpcId).getPetMaxFeed(); + } + + protected boolean isHungry() + { + return _canFeed ? (getCurrentFeed() < ((getPetData(getMountNpcId()).getHungry_limit() / 100f) * getPetLevelData(getMountNpcId()).getPetMaxFeed())) : false; + } + + protected class Dismount implements Runnable + { + @Override + public void run() + { + try + { + dismount(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception on dismount(): " + e.getMessage(), e); + } + } + } + + public void enteredNoLanding(int delay) + { + _dismountTask = ThreadPoolManager.getInstance().scheduleGeneral(new Dismount(), delay * 1000); + } + + public void exitedNoLanding() + { + if (_dismountTask != null) + { + _dismountTask.cancel(true); + _dismountTask = null; + } + } + + public void storePetFood(int petId) + { + if ((_controlItemId != 0) && (petId != 0)) + { + String req; + req = "UPDATE pets SET fed=? WHERE item_obj_id = ?"; + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(req); + statement.setInt(1, getCurrentFeed()); + statement.setInt(2, _controlItemId); + statement.executeUpdate(); + statement.close(); + _controlItemId = 0; + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Failed to store Pet [NpcId: " + petId + "] data", e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + } + + /** End of section for mounted pets */ + + @Override + public int getAttackElementValue(byte attribute) + { + int value = super.getAttackElementValue(attribute); + + // 20% if summon exist + if ((getPet() != null) && getClassId().isSummoner() && (getPet() instanceof L2SummonInstance)) + { + return value / 5; + } + + return value; + } + + /** + * @return event effect id + */ + public int getEventEffectId() + { + return _eventEffectId; + } + + public void startEventEffect(AbnormalEffect mask) + { + _eventEffectId |= mask.getMask(); + broadcastUserInfo(); + } + + public void stopEventEffect(AbnormalEffect mask) + { + _eventEffectId &= ~mask.getMask(); + broadcastUserInfo(); + } + + public void setIsInSiege(boolean b) + { + _isInSiege = b; + } + + public boolean isInSiege() + { + return _isInSiege; + } + + public FloodProtectors getFloodProtectors() + { + return getClient().getFloodProtectors(); + } + + public boolean isFlyingMounted() + { + return _isFlyingMounted; + } + + public void setIsFlyingMounted(boolean val) + { + _isFlyingMounted = val; + setIsFlying(val); + } + + /** + * Returns the Number of Charges this L2PcInstance got. + * @return + */ + public int getCharges() + { + return _charges.get(); + } + + public synchronized void increaseCharges(int count, int max) + { + if (_charges.get() >= max) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FORCE_MAXLEVEL_REACHED)); + return; + } + + // if no charges - start clear task + if (_charges.get() == 0) + { + restartChargeTask(); + } + + if (_charges.addAndGet(count) >= max) + { + _charges.set(max); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FORCE_MAXLEVEL_REACHED)); + } + else + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.FORCE_INCREASED_TO_S1); + sm.addNumber(_charges.get()); + sendPacket(sm); + } + + sendPacket(new EtcStatusUpdate(this)); + } + + public synchronized boolean decreaseCharges(int count) + { + if (_charges.get() < count) + { + return false; + } + + if (_charges.addAndGet(-count) == 0) + { + stopChargeTask(); + } + + sendPacket(new EtcStatusUpdate(this)); + return true; + } + + public void clearCharges() + { + _charges.set(0); + sendPacket(new EtcStatusUpdate(this)); + } + + /** + * Starts/Restarts the ChargeTask to Clear Charges after 10 Mins. + */ + private void restartChargeTask() + { + if (_chargeTask != null) + { + _chargeTask.cancel(false); + _chargeTask = null; + } + _chargeTask = ThreadPoolManager.getInstance().scheduleGeneral(new ChargeTask(), 600000); + } + + /** + * Stops the Charges Clearing Task. + */ + public void stopChargeTask() + { + if (_chargeTask != null) + { + _chargeTask.cancel(false); + // ThreadPoolManager.getInstance().removeGeneral((Runnable)_chargeTask); + _chargeTask = null; + } + } + + protected class ChargeTask implements Runnable + { + + @Override + public void run() + { + clearCharges(); + } + } + + public static class TeleportBookmark + { + public int _id, _x, _y, _z, _icon; + public String _name, _tag; + + TeleportBookmark(int id, int x, int y, int z, int icon, String tag, String name) + { + _id = id; + _x = x; + _y = y; + _z = z; + _icon = icon; + _name = name; + _tag = tag; + } + + } + + public void teleportBookmarkModify(int Id, int icon, String tag, String name) + { + int count = 0; + int size = tpbookmark.size(); + while (size > count) + { + if (tpbookmark.get(count)._id == Id) + { + tpbookmark.get(count)._icon = icon; + tpbookmark.get(count)._tag = tag; + tpbookmark.get(count)._name = name; + + Connection con = null; + + try + { + + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(UPDATE_TP_BOOKMARK); + + statement.setInt(1, icon); + statement.setString(2, tag); + statement.setString(3, name); + statement.setInt(4, getObjectId()); + statement.setInt(5, Id); + + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not update character teleport bookmark data: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + + } + count++; + } + + sendPacket(new ExGetBookMarkInfoPacket(this)); + + } + + public void teleportBookmarkDelete(int Id) + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(DELETE_TP_BOOKMARK); + + statement.setInt(1, getObjectId()); + statement.setInt(2, Id); + + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not delete character teleport bookmark data: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + + int count = 0; + int size = tpbookmark.size(); + + while (size > count) + { + if (tpbookmark.get(count)._id == Id) + { + tpbookmark.remove(count); + break; + } + count++; + } + + sendPacket(new ExGetBookMarkInfoPacket(this)); + } + + public void teleportBookmarkGo(int Id) + { + if (!teleportBookmarkCondition(0)) + { + return; + } + if (getInventory().getInventoryItemCount(13016, 0) == 0) + { + sendPacket(SystemMessage.getSystemMessage(2359)); + return; + } + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); + sm.addItemName(13016); + sendPacket(sm); + int count = 0; + int size = tpbookmark.size(); + while (size > count) + { + if (tpbookmark.get(count)._id == Id) + { + destroyItem("Consume", getInventory().getItemByItemId(13016).getObjectId(), 1, null, false); + this.teleToLocation(tpbookmark.get(count)._x, tpbookmark.get(count)._y, tpbookmark.get(count)._z); + break; + } + count++; + } + sendPacket(new ExGetBookMarkInfoPacket(this)); + } + + public boolean teleportBookmarkCondition(int type) + { + if (isInCombat()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_DURING_A_BATTLE)); + return false; + } + else if (isInSiege() || (getSiegeState() != 0)) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_PARTICIPATING)); + return false; + } + else if (isInDuel()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_DURING_A_DUEL)); + return false; + } + else if (isFlying()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_FLYING)); + return false; + } + else if (isInOlympiadMode()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_PARTICIPATING_IN_AN_OLYMPIAD_MATCH)); + return false; + } + else if (isParalyzed()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_YOU_ARE_PARALYZED)); + return false; + } + else if (isDead()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_YOU_ARE_DEAD)); + return false; + } + else if ((type == 1) && (isIn7sDungeon() || (isInParty() && getParty().isInDimensionalRift()))) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_TO_REACH_THIS_AREA)); + return false; + } + else if (isInBoat() || isInAirShip() || isInJail() || isInsideZone(ZONE_NOSUMMONFRIEND)) + { + if (type == 0) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_IN_THIS_AREA)); + } + else if (type == 1) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_TO_REACH_THIS_AREA)); + } + return false; + } + else if (isInWater()) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_UNDERWATER)); + return false; + } + else if ((type == 1) && (isInsideZone(ZONE_SIEGE) || isInsideZone(ZONE_CLANHALL) || isInsideZone(ZONE_JAIL) || isInsideZone(ZONE_CASTLE) || isInsideZone(ZONE_NOSUMMONFRIEND) || isInsideZone(ZONE_FORT))) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_TO_REACH_THIS_AREA)); + return false; + } + else if (isInsideZone(ZONE_NOBOOKMARK)) + { + if (type == 0) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_IN_THIS_AREA)); + } + else if (type == 1) + { + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_TO_REACH_THIS_AREA)); + } + return false; + } + /* + * TODO: Instant Zone still not implement else if (this.isInsideZone(ZONE_INSTANT)) { sendPacket(SystemMessage.getSystemMessage(2357)); return; } + */ + else + { + return true; + } + } + + public void teleportBookmarkAdd(int x, int y, int z, int icon, String tag, String name) + { + if (!teleportBookmarkCondition(1)) + { + return; + } + + if (tpbookmark.size() >= _bookmarkslot) + { + sendPacket(SystemMessage.getSystemMessage(2358)); + return; + } + + if (getInventory().getInventoryItemCount(20033, 0) == 0) + { + sendPacket(SystemMessage.getSystemMessage(6501)); + return; + } + + int count = 0; + int id = 1; + FastList idlist = new FastList<>(); + + int size = tpbookmark.size(); + + while (size > count) + { + idlist.add(tpbookmark.get(count)._id); + count++; + } + + for (int i = 1; i < 10; i++) + { + if (!idlist.contains(i)) + { + id = i; + break; + } + } + + TeleportBookmark tpadd = new TeleportBookmark(id, x, y, z, icon, tag, name); + if (tpbookmark == null) + { + tpbookmark = new FastList<>(); + } + + tpbookmark.add(tpadd); + + destroyItem("Consume", getInventory().getItemByItemId(20033).getObjectId(), 1, null, false); + + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); + sm.addItemName(20033); + sendPacket(sm); + + Connection con = null; + + try + { + + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(INSERT_TP_BOOKMARK); + + statement.setInt(1, getObjectId()); + statement.setInt(2, id); + statement.setInt(3, x); + statement.setInt(4, y); + statement.setInt(5, z); + statement.setInt(6, icon); + statement.setString(7, tag); + statement.setString(8, name); + + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Could not insert character teleport bookmark data: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + + sendPacket(new ExGetBookMarkInfoPacket(this)); + } + + public void restoreTeleportBookmark() + { + if (tpbookmark == null) + { + tpbookmark = new FastList<>(); + } + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(RESTORE_TP_BOOKMARK); + statement.setInt(1, getObjectId()); + ResultSet rset = statement.executeQuery(); + + while (rset.next()) + { + tpbookmark.add(new TeleportBookmark(rset.getInt("Id"), rset.getInt("x"), rset.getInt("y"), rset.getInt("z"), rset.getInt("icon"), rset.getString("tag"), rset.getString("name"))); + } + + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Failed restoing character teleport bookmark.", e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + @Override + public void sendInfo(L2PcInstance activeChar) + { + if (isInBoat()) + { + getPosition().setWorldPosition(getBoat().getPosition().getWorldPosition()); + + activeChar.sendPacket(new CharInfo(this)); + activeChar.sendPacket(new ExBrExtraUserInfo(this)); + int relation1 = getRelation(activeChar); + int relation2 = activeChar.getRelation(this); + Integer oldrelation = getKnownList().getKnownRelations().get(activeChar.getObjectId()); + if ((oldrelation != null) && (oldrelation != relation1)) + { + activeChar.sendPacket(new RelationChanged(this, relation1, isAutoAttackable(activeChar))); + if (getPet() != null) + { + activeChar.sendPacket(new RelationChanged(getPet(), relation1, isAutoAttackable(activeChar))); + } + } + oldrelation = activeChar.getKnownList().getKnownRelations().get(getObjectId()); + if ((oldrelation != null) && (oldrelation != relation2)) + { + sendPacket(new RelationChanged(activeChar, relation2, activeChar.isAutoAttackable(this))); + if (activeChar.getPet() != null) + { + sendPacket(new RelationChanged(activeChar.getPet(), relation2, activeChar.isAutoAttackable(this))); + } + } + activeChar.sendPacket(new GetOnVehicle(getObjectId(), getBoat().getObjectId(), getInVehiclePosition())); + } + else if (isInAirShip()) + { + getPosition().setWorldPosition(getAirShip().getPosition().getWorldPosition()); + + activeChar.sendPacket(new CharInfo(this)); + activeChar.sendPacket(new ExBrExtraUserInfo(this)); + int relation1 = getRelation(activeChar); + int relation2 = activeChar.getRelation(this); + Integer oldrelation = getKnownList().getKnownRelations().get(activeChar.getObjectId()); + if ((oldrelation != null) && (oldrelation != relation1)) + { + activeChar.sendPacket(new RelationChanged(this, relation1, isAutoAttackable(activeChar))); + if (getPet() != null) + { + activeChar.sendPacket(new RelationChanged(getPet(), relation1, isAutoAttackable(activeChar))); + } + } + oldrelation = activeChar.getKnownList().getKnownRelations().get(getObjectId()); + if ((oldrelation != null) && (oldrelation != relation2)) + { + sendPacket(new RelationChanged(activeChar, relation2, activeChar.isAutoAttackable(this))); + if (activeChar.getPet() != null) + { + sendPacket(new RelationChanged(activeChar.getPet(), relation2, activeChar.isAutoAttackable(this))); + } + } + activeChar.sendPacket(new ExGetOnAirShip(this, getAirShip())); + } + else + { + activeChar.sendPacket(new CharInfo(this)); + activeChar.sendPacket(new ExBrExtraUserInfo(this)); + int relation1 = getRelation(activeChar); + int relation2 = activeChar.getRelation(this); + Integer oldrelation = getKnownList().getKnownRelations().get(activeChar.getObjectId()); + if ((oldrelation != null) && (oldrelation != relation1)) + { + activeChar.sendPacket(new RelationChanged(this, relation1, isAutoAttackable(activeChar))); + if (getPet() != null) + { + activeChar.sendPacket(new RelationChanged(getPet(), relation1, isAutoAttackable(activeChar))); + } + } + oldrelation = activeChar.getKnownList().getKnownRelations().get(getObjectId()); + if ((oldrelation != null) && (oldrelation != relation2)) + { + sendPacket(new RelationChanged(activeChar, relation2, activeChar.isAutoAttackable(this))); + if (activeChar.getPet() != null) + { + sendPacket(new RelationChanged(activeChar.getPet(), relation2, activeChar.isAutoAttackable(this))); + } + } + } + if (getMountType() == 4) + { + // TODO: Remove when horse mounts fixed + activeChar.sendPacket(new Ride(this, false, 0)); + activeChar.sendPacket(new Ride(this, true, getMountNpcId())); + } + + switch (getPrivateStoreType()) + { + case L2PcInstance.STORE_PRIVATE_SELL: + activeChar.sendPacket(new PrivateStoreMsgSell(this)); + break; + case L2PcInstance.STORE_PRIVATE_PACKAGE_SELL: + activeChar.sendPacket(new ExPrivateStoreSetWholeMsg(this)); + break; + case L2PcInstance.STORE_PRIVATE_BUY: + activeChar.sendPacket(new PrivateStoreMsgBuy(this)); + break; + case L2PcInstance.STORE_PRIVATE_MANUFACTURE: + activeChar.sendPacket(new RecipeShopMsg(this)); + break; + } + } + + public void showQuestMovie(int id) + { + if (_movieId > 0) + { + return; + } + abortAttack(); + abortCast(); + stopMove(null); + _movieId = id; + sendPacket(new ExStartScenePlayer(id)); + } + + public boolean isAllowedToEnchantSkills() + { + if (isLocked()) + { + return false; + } + if (isTransformed() || isInStance()) + { + return false; + } + if (AttackStanceTaskManager.getInstance().getAttackStanceTask(this)) + { + return false; + } + if (isCastingNow() || isCastingSimultaneouslyNow()) + { + return false; + } + if (isInBoat() || isInAirShip()) + { + return false; + } + return true; + } + + /** + * Set the _creationTime of the L2PcInstance.
    + *
    + */ + public void setCreateTime(long creationTime) + { + _creationTime = creationTime; + } + + /** + * Return the _creationTime of the L2PcInstance.
    + *
    + */ + public long getCreateTime() + { + return _creationTime; + } + + /** + * @return number of days to char birthday.
    + *
    + */ + public int checkBirthDay() + { + QuestState _state = getQuestState("CharacterBirthday"); + Calendar now = Calendar.getInstance(); + Calendar birth = Calendar.getInstance(); + now.setTimeInMillis(System.currentTimeMillis()); + birth.setTimeInMillis(_creationTime); + + if ((_state != null) && (_state.getInt("Birthday") > now.get(Calendar.YEAR))) + { + return -1; + } + + // "Characters with a February 29 creation date will receive a gift on February 28." + if ((birth.get(Calendar.DAY_OF_MONTH) == 29) && (birth.get(Calendar.MONTH) == 1)) + { + birth.add(Calendar.HOUR_OF_DAY, -24); + } + + if ((now.get(Calendar.MONTH) == birth.get(Calendar.MONTH)) && (now.get(Calendar.DAY_OF_MONTH) == birth.get(Calendar.DAY_OF_MONTH)) && (now.get(Calendar.YEAR) != birth.get(Calendar.YEAR))) + { + return 0; + } + + for (int i = 1; i < 6; i++) + { + now.add(Calendar.HOUR_OF_DAY, 24); + if ((now.get(Calendar.MONTH) == birth.get(Calendar.MONTH)) && (now.get(Calendar.DAY_OF_MONTH) == birth.get(Calendar.DAY_OF_MONTH)) && (now.get(Calendar.YEAR) != birth.get(Calendar.YEAR))) + { + return i; + } + } + return -1; + } + + /** + * list of character friends + */ + private final List _friendList = new FastList<>(); + + public List getFriendList() + { + return _friendList; + } + + public void restoreFriendList() + { + _friendList.clear(); + + Connection con = null; + + try + { + String sqlQuery = "SELECT friendId FROM character_friends WHERE charId=? AND relation=0"; + + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(sqlQuery); + statement.setInt(1, getObjectId()); + ResultSet rset = statement.executeQuery(); + + int friendId; + while (rset.next()) + { + friendId = rset.getInt("friendId"); + if (friendId == getObjectId()) + { + continue; + } + _friendList.add(friendId); + } + + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Error found in " + getName() + "'s FriendList: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * + */ + private void notifyFriends() + { + FriendStatusPacket pkt = new FriendStatusPacket(getObjectId()); + for (int id : _friendList) + { + L2PcInstance friend = L2World.getInstance().getPlayer(id); + if (friend != null) + { + friend.sendPacket(pkt); + } + } + } + + /** + * @return the _silenceMode + */ + public boolean isSilenceMode() + { + return _silenceMode; + } + + /** + * @param mode the _silenceMode to set + */ + public void setSilenceMode(boolean mode) + { + _silenceMode = mode; + sendPacket(new EtcStatusUpdate(this)); + } + + private void storeRecipeShopList() + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + L2ManufactureList list = getCreateList(); + + if ((list != null) && (list.size() > 0)) + { + int _position = 1; + statement = con.prepareStatement("DELETE FROM character_recipeshoplist WHERE charId=? "); + statement.setInt(1, getObjectId()); + statement.execute(); + statement.close(); + + PreparedStatement statement2 = con.prepareStatement("INSERT INTO character_recipeshoplist (charId, Recipeid, Price, Pos) VALUES (?, ?, ?, ?)"); + for (L2ManufactureItem item : list.getList()) + { + statement2.setInt(1, getObjectId()); + statement2.setInt(2, item.getRecipeId()); + statement2.setLong(3, item.getCost()); + statement2.setInt(4, _position); + statement2.execute(); + statement2.clearParameters(); + _position++; + } + statement2.close(); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not store recipe shop for playerID " + getObjectId() + ": ", e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void restoreRecipeShopList() + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT Recipeid,Price FROM character_recipeshoplist WHERE charId=? ORDER BY Pos ASC"); + statement.setInt(1, getObjectId()); + ResultSet rset = statement.executeQuery(); + + L2ManufactureList createList = new L2ManufactureList(); + while (rset.next()) + { + createList.add(new L2ManufactureItem(rset.getInt("Recipeid"), rset.getLong("Price"))); + } + setCreateList(createList); + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not restore recipe shop list data for playerId: " + getObjectId(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public double getCollisionRadius() + { + if (getAppearance().getSex()) + { + return getBaseTemplate().fCollisionRadius_female; + } + return getBaseTemplate().fCollisionRadius; + } + + public double getCollisionHeight() + { + if (getAppearance().getSex()) + { + return getBaseTemplate().fCollisionHeight_female; + } + return getBaseTemplate().fCollisionHeight; + } + + public final int getClientX() + { + return _clientX; + } + + public final int getClientY() + { + return _clientY; + } + + public final int getClientZ() + { + return _clientZ; + } + + public final int getClientHeading() + { + return _clientHeading; + } + + public final void setClientX(int val) + { + _clientX = val; + } + + public final void setClientY(int val) + { + _clientY = val; + } + + public final void setClientZ(int val) + { + _clientZ = val; + } + + public final void setClientHeading(int val) + { + _clientHeading = val; + } + + /** + * Return true if character falling now On the start of fall return false for correct coord sync ! + */ + public final boolean isFalling(int z) + { + if (isDead() || isFlying() || isFlyingMounted() || isInsideZone(ZONE_WATER)) + { + return false; + } + + if (System.currentTimeMillis() < _fallingTimestamp) + { + return true; + } + + final int deltaZ = getZ() - z; + if (deltaZ <= getBaseTemplate().getFallHeight()) + { + return false; + } + + final int damage = (int) Formulas.calcFallDam(this, deltaZ); + if (damage > 0) + { + reduceCurrentHp(Math.min(damage, getCurrentHp() - 1), null, false, true, null); + sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FALL_DAMAGE_S1).addNumber(damage)); + } + + setFalling(); + + return false; + } + + /** + * Set falling timestamp + */ + public final void setFalling() + { + _fallingTimestamp = System.currentTimeMillis() + FALLING_VALIDATION_DELAY; + } + + /** + * @return the _movieId + */ + public int getMovieId() + { + return _movieId; + } + + public void setMovieId(int id) + { + _movieId = id; + } + + /** + * Update last item auction request timestamp to current + */ + public void updateLastItemAuctionRequest() + { + _lastItemAuctionInfoRequest = System.currentTimeMillis(); + } + + /** + * Returns true if receiving item auction requests (last request was in 2 seconds before) + */ + public boolean isItemAuctionPolling() + { + return (System.currentTimeMillis() - _lastItemAuctionInfoRequest) < 2000; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.actor.L2Character#isMovementDisabled() + */ + @Override + public boolean isMovementDisabled() + { + return super.isMovementDisabled() || (_movieId > 0); + } + + private void restoreUISettings() + { + _uiKeySettings = new L2UIKeysSettings(this); + } + + private void storeUISettings() + { + if (_uiKeySettings == null) + { + return; + } + + if (!_uiKeySettings.isSaved()) + { + _uiKeySettings.saveInDB(); + } + } + + public L2UIKeysSettings getUISettings() + { + return _uiKeySettings; + } + + public String getHtmlPrefix() + { + if (!Config.L2JMOD_MULTILANG_ENABLE) + { + return null; + } + + return _htmlPrefix; + } + + public String getLang() + { + return _lang; + } + + public boolean setLang(String lang) + { + boolean result = false; + if (Config.L2JMOD_MULTILANG_ENABLE) + { + if (Config.L2JMOD_MULTILANG_ALLOWED.contains(lang)) + { + _lang = lang; + result = true; + } + else + { + _lang = Config.L2JMOD_MULTILANG_DEFAULT; + } + + _htmlPrefix = "data/lang/" + _lang + "/"; + } + else + { + _lang = null; + _htmlPrefix = null; + } + + return result; + } + + public long getOfflineStartTime() + { + return _offlineShopStart; + } + + public void setOfflineStartTime(long time) + { + _offlineShopStart = time; + } + + /** + * Remove player from BossZones (used on char logout/exit) + */ + public void removeFromBossZone() + { + try + { + for (L2BossZone _zone : GrandBossManager.getInstance().getZones()) + { + _zone.removePlayer(this); + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception on removeFromBossZone(): " + e.getMessage(), e); + } + } + + /** + * Check all player skills for skill level. If player level is lower than skill learn level - 9, skill level is decreased to next possible level. + */ + public void checkPlayerSkills() + { + for (int id : _skills.keySet()) + { + int level = getSkillLevel(id); + if (level >= 100) + { + level = SkillTable.getInstance().getMaxLevel(id); + } + final L2SkillLearn learn = SkillTreesData.getInstance().getClassSkill(id, level, getClassId()); + // not found - not a learn skill? + if (learn == null) + { + continue; + } + + // player level is too low for such skill level + if (getLevel() < (learn.getGetLevel() - 9)) + { + deacreaseSkillLevel(id); + } + } + } + + private void deacreaseSkillLevel(int id) + { + int nextLevel = -1; + final FastMap skillTree = SkillTreesData.getInstance().getCompleteClassSkillTree(getClassId()); + + for (L2SkillLearn sl : skillTree.values()) + { + if ((sl.getSkillId() == id) && (nextLevel < sl.getSkillLevel()) && (getLevel() >= (sl.getGetLevel() - 9))) + { + // next possible skill level + nextLevel = sl.getSkillLevel(); + } + } + + if (nextLevel == -1) // there is no lower skill + { + _log.info("Removing skill id " + id + " level " + getSkillLevel(id) + " from player " + this); + removeSkill(_skills.get(id), true); + } + else + // replace with lower one + { + _log.info("Decreasing skill id " + id + " from " + getSkillLevel(id) + " to " + nextLevel + " for " + this); + addSkill(SkillTable.getInstance().getInfo(id, nextLevel), true); + } + } + + public boolean canMakeSocialAction() + { + return (getPrivateStoreType() == 0) && (getActiveRequester() == null) && !isAlikeDead() && (!isAllSkillsDisabled() || isInDuel()) && !isCastingNow() && !isCastingSimultaneouslyNow() && (getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE) && !AttackStanceTaskManager.getInstance().getAttackStanceTask(this) && !isInOlympiadMode(); + } + + public void setMultiSocialAction(int id, int targetId) + { + _multiSociaAction = id; + _multiSocialTarget = targetId; + } + + public int getMultiSociaAction() + { + return _multiSociaAction; + } + + public int getMultiSocialTarget() + { + return _multiSocialTarget; + } + + public List getTpbookmark() + { + return tpbookmark; + } + + public int getBookmarkslot() + { + return _bookmarkslot; + } + + /** + * @return + */ + public int getQuestInventoryLimit() + { + return Config.INVENTORY_MAXIMUM_QUEST_ITEMS; + } + + public boolean canAttackCharacter(L2Character cha) + { + if (cha instanceof L2Attackable) + { + return true; + } + else if (cha instanceof L2Playable) + { + if (cha.isInsideZone(L2Character.ZONE_PVP) && !cha.isInsideZone(L2Character.ZONE_SIEGE)) + { + return true; + } + + L2PcInstance target; + if (cha instanceof L2Summon) + { + target = ((L2Summon) cha).getOwner(); + } + else + { + target = (L2PcInstance) cha; + } + + if (isInDuel() && target.isInDuel() && (target.getDuelId() == getDuelId())) + { + return true; + } + else if (isInParty() && target.isInParty()) + { + if (getParty() == target.getParty()) + { + return false; + } + if (((getParty().getCommandChannel() != null) || (target.getParty().getCommandChannel() != null)) && (getParty().getCommandChannel() == target.getParty().getCommandChannel())) + { + return false; + } + } + else if ((getClan() != null) && (target.getClan() != null)) + { + if (getClanId() == target.getClanId()) + { + return false; + } + if (((getAllyId() > 0) || (target.getAllyId() > 0)) && (getAllyId() == target.getAllyId())) + { + return false; + } + if (getClan().isAtWarWith(target.getClan().getClanId()) && target.getClan().isAtWarWith(getClan().getClanId())) + { + return true; + } + } + else if ((getClan() == null) || (target.getClan() == null)) + { + if ((target.getPvpFlag() == 0) && (target.getKarma() == 0)) + { + return false; + } + } + } + return true; + } + + /** + * Test if player inventory is under 80% capaity + * @param includeQuestInv check also quest inventory + * @return + */ + public boolean isInventoryUnder80(boolean includeQuestInv) + { + if (getInventory().getSize(false) <= (getInventoryLimit() * 0.8)) + { + if (includeQuestInv) + { + if (getInventory().getSize(true) <= (getQuestInventoryLimit() * 0.8)) + { + return true; + } + } + else + { + return true; + } + } + return false; + } + + public boolean havePetInvItems() + { + return _petItems; + } + + public void setPetInvItems(boolean haveit) + { + _petItems = haveit; + } + + private void checkPetInvItems() + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT object_id FROM `items` WHERE `owner_id`=? AND (`loc`='PET' OR `loc`='PET_EQUIP') LIMIT 1;"); + statement.setInt(1, getObjectId()); + ResultSet rset = statement.executeQuery(); + if (rset.next() && (rset.getInt("object_id") > 0)) + { + setPetInvItems(true); + } + else + { + setPetInvItems(false); + } + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not check Items in Pet Inventory for playerId: " + getObjectId(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public String getAdminConfirmCmd() + { + return _adminConfirmCmd; + } + + public void setAdminConfirmCmd(String adminConfirmCmd) + { + _adminConfirmCmd = adminConfirmCmd; + } + + public void setBlockCheckerArena(byte arena) + { + _handysBlockCheckerEventArena = arena; + } + + public int getBlockCheckerArena() + { + return _handysBlockCheckerEventArena; + } + + /** + * Load L2PcInstance Recommendations data.
    + *
    + */ + private long loadRecommendations() + { + long _time_left = 0; + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT rec_have,rec_left,time_left FROM character_reco_bonus WHERE charId=? LIMIT 1"); + statement.setInt(1, getObjectId()); + ResultSet rset = statement.executeQuery(); + + if (rset.next()) + { + setRecomHave(rset.getInt("rec_have")); + setRecomLeft(rset.getInt("rec_left")); + _time_left = rset.getLong("time_left"); + } + else + { + _time_left = 3600000; + } + + rset.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not restore Recommendations for player: " + getObjectId(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + return _time_left; + } + + /** + * Update L2PcInstance Recommendations data.
    + *
    + */ + public void storeRecommendations() + { + long _recoTaskEnd = 0; + if (_recoBonusTask != null) + { + _recoTaskEnd = Math.max(0, _recoBonusTask.getDelay(TimeUnit.MILLISECONDS)); + } + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("INSERT INTO character_reco_bonus (charId,rec_have,rec_left,time_left) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE rec_have=?, rec_left=?, time_left=?"); + statement.setInt(1, getObjectId()); + statement.setInt(2, getRecomHave()); + statement.setInt(3, getRecomLeft()); + statement.setLong(4, _recoTaskEnd); + // Update part + statement.setInt(5, getRecomHave()); + statement.setInt(6, getRecomLeft()); + statement.setLong(7, _recoTaskEnd); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not update Recommendations for player: " + getObjectId(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void checkRecoBonusTask() + { + // Load data + long _task_time = loadRecommendations(); + + if (_task_time > 0) + { + // Add 20 recos on first login + if (_task_time == 3600000) + { + setRecomLeft(getRecomLeft() + 20); + } + // If player have some timeleft, start bonus task + _recoBonusTask = ThreadPoolManager.getInstance().scheduleGeneral(new RecoBonusTaskEnd(), _task_time); + } + // Create task to give new recommendations + _recoGiveTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new RecoGiveTask(), 7200000, 3600000); + // Store new data + storeRecommendations(); + } + + public void stopRecoBonusTask() + { + if (_recoBonusTask != null) + { + _recoBonusTask.cancel(false); + _recoBonusTask = null; + } + } + + public void stopRecoGiveTask() + { + if (_recoGiveTask != null) + { + _recoGiveTask.cancel(false); + _recoGiveTask = null; + } + } + + protected class RecoGiveTask implements Runnable + { + @Override + public void run() + { + int reco_to_give; + // 10 recommendations to give out after 2 hours of being logged in + // 1 more recommendation to give out every hour after that. + if (_recoTwoHoursGiven) + { + reco_to_give = 1; + } + else + { + reco_to_give = 10; + } + + _recoTwoHoursGiven = true; + + setRecomLeft(getRecomLeft() + reco_to_give); + + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_OBTAINED_S1_RECOMMENDATIONS); + sm.addNumber(reco_to_give); + sendPacket(sm); + sendPacket(new UserInfo(L2PcInstance.this)); + } + } + + protected class RecoBonusTaskEnd implements Runnable + { + @Override + public void run() + { + sendPacket(new ExVoteSystemInfo(L2PcInstance.this)); + } + } + + public int getRecomBonusTime() + { + if (_recoBonusTask != null) + { + return (int) Math.max(0, _recoBonusTask.getDelay(TimeUnit.SECONDS)); + } + + return 0; + } + + public int getRecomBonusType() + { + // Maintain = 1 + return 0; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PenaltyMonsterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PenaltyMonsterInstance.java new file mode 100644 index 00000000..9d220ede --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PenaltyMonsterInstance.java @@ -0,0 +1,72 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.ai.CtrlEvent; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.network.clientpackets.Say2; +import com.l2jserver.gameserver.network.serverpackets.CreatureSay; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.Rnd; + +public class L2PenaltyMonsterInstance extends L2MonsterInstance +{ + private L2PcInstance _ptk; + + public L2PenaltyMonsterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2PenaltyMonsterInstance); + } + + @Override + public L2Character getMostHated() + { + if (_ptk != null) + { + return _ptk; // always attack only one person + } + return super.getMostHated(); + } + + public void setPlayerToKill(L2PcInstance ptk) + { + if (Rnd.get(100) <= 80) + { + CreatureSay cs = new CreatureSay(getObjectId(), Say2.ALL, getName(), "mmm your bait was delicious"); + this.broadcastPacket(cs); + } + _ptk = ptk; + addDamageHate(ptk, 0, 10); + getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, ptk); + addAttackerToAttackByList(ptk); + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + + if (Rnd.get(100) <= 75) + { + CreatureSay cs = new CreatureSay(getObjectId(), Say2.ALL, getName(), "I will tell fishes not to take your bait"); + this.broadcastPacket(cs); + } + return true; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PetInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PetInstance.java new file mode 100644 index 00000000..860a2c59 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PetInstance.java @@ -0,0 +1,1297 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.datatables.ItemTable; +import com.l2jserver.gameserver.datatables.PetDataTable; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.handler.IItemHandler; +import com.l2jserver.gameserver.handler.ItemHandler; +import com.l2jserver.gameserver.idfactory.IdFactory; +import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager; +import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2PetData; +import com.l2jserver.gameserver.model.L2PetLevelData; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.actor.stat.PetStat; +import com.l2jserver.gameserver.model.itemcontainer.Inventory; +import com.l2jserver.gameserver.model.itemcontainer.PetInventory; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate; +import com.l2jserver.gameserver.network.serverpackets.PetInventoryUpdate; +import com.l2jserver.gameserver.network.serverpackets.PetItemList; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.StopMove; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.skills.Stats; +import com.l2jserver.gameserver.taskmanager.DecayTaskManager; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.templates.item.L2EtcItemType; +import com.l2jserver.gameserver.templates.item.L2Item; +import com.l2jserver.gameserver.templates.item.L2Weapon; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.Rnd; + +/** + * This class ... + * @version $Revision: 1.15.2.10.2.16 $ $Date: 2005/04/06 16:13:40 $ + */ +public class L2PetInstance extends L2Summon +{ + protected static final Logger _logPet = Logger.getLogger(L2PetInstance.class.getName()); + + private int _curFed; + private final PetInventory _inventory; + private final int _controlObjectId; + private boolean _respawned; + private final boolean _mountable; + private Future _feedTask; + private L2PetData _data; + private L2PetLevelData _leveldata; + + /** The Experience before the last Death Penalty */ + private long _expBeforeDeath = 0; + private int _curWeightPenalty = 0; + + private static final int PET_DECAY_DELAY = 86400000; // 24 hours + + public final L2PetLevelData getPetLevelData() + { + if (_leveldata == null) + { + _leveldata = PetDataTable.getInstance().getPetLevelData(getTemplate().npcId, getStat().getLevel()); + } + + return _leveldata; + } + + public final L2PetData getPetData() + { + if (_data == null) + { + _data = PetDataTable.getInstance().getPetData(getTemplate().npcId); + } + + return _data; + } + + public final void setPetData(L2PetLevelData value) + { + _leveldata = value; + } + + /** + * Manage Feeding Task.
    + *
    + * Actions :
    + *
  • Feed or kill the pet depending on hunger level
  • If pet has food in inventory and feed level drops below 55% then consume food from inventory
  • Send a broadcastStatusUpdate packet for this L2PetInstance

  • + *
    + */ + + class FeedTask implements Runnable + { + @Override + public void run() + { + try + { + if ((getOwner() == null) || (getOwner().getPet() == null) || (getOwner().getPet().getObjectId() != getObjectId())) + { + stopFeed(); + return; + } + else if (getCurrentFed() > getFeedConsume()) + { + // eat + setCurrentFed(getCurrentFed() - getFeedConsume()); + } + else + { + setCurrentFed(0); + } + + broadcastStatusUpdate(); + + int[] foodIds = getPetData().getFood(); + if (foodIds.length == 0) + { + if (getCurrentFed() == 0) + { + // Owl Monk remove PK + if ((getTemplate().npcId == 16050) && (getOwner() != null)) + { + getOwner().setPkKills(Math.max(0, getOwner().getPkKills() - Rnd.get(1, 6))); + } + getOwner().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.THE_HELPER_PET_LEAVING)); + deleteMe(getOwner()); + } + else if (isHungry()) + { + getOwner().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.THERE_NOT_MUCH_TIME_REMAINING_UNTIL_HELPER_LEAVES)); + } + return; + } + L2ItemInstance food = null; + for (int id : foodIds) + { + food = getInventory().getItemByItemId(id); + if (food != null) + { + break; + } + } + if (isRunning() && isHungry()) + { + setWalking(); + } + else if (!isHungry() && !isRunning()) + { + setRunning(); + } + if ((food != null) && isHungry()) + { + IItemHandler handler = ItemHandler.getInstance().getItemHandler(food.getEtcItem()); + if (handler != null) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.PET_TOOK_S1_BECAUSE_HE_WAS_HUNGRY); + sm.addItemName(food.getItemId()); + getOwner().sendPacket(sm); + handler.useItem(L2PetInstance.this, food, false); + } + } + else + { + if (getCurrentFed() == 0) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOUR_PET_IS_VERY_HUNGRY); + getOwner().sendPacket(sm); + if (Rnd.get(100) < 30) + { + stopFeed(); + sm = SystemMessage.getSystemMessage(SystemMessageId.STARVING_GRUMPY_AND_FED_UP_YOUR_PET_HAS_LEFT); + getOwner().sendPacket(sm); + _log.info("Hungry pet [" + getTemplate().getName() + "][" + getLevel() + "] deleted for player: " + getOwner() + " Control Item Id :" + getControlObjectId()); + deleteMe(getOwner()); + } + } + else if (getCurrentFed() < (0.10 * getPetLevelData().getPetMaxFeed())) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.PET_CAN_RUN_AWAY_WHEN_HUNGER_BELOW_10_PERCENT); + getOwner().sendPacket(sm); + if (Rnd.get(100) < 3) + { + stopFeed(); + sm = SystemMessage.getSystemMessage(SystemMessageId.STARVING_GRUMPY_AND_FED_UP_YOUR_PET_HAS_LEFT); + getOwner().sendPacket(sm); + _log.info("Hungry pet [" + getTemplate().getName() + "][" + getLevel() + "] deleted for player: " + getOwner() + " Control Item Id :" + getControlObjectId()); + deleteMe(getOwner()); + } + } + } + } + catch (Exception e) + { + _logPet.log(Level.SEVERE, "Pet [ObjectId: " + getObjectId() + "] a feed task error has occurred", e); + } + } + + /** + * @return + */ + private int getFeedConsume() + { + // if pet is attacking + if (isAttackingNow()) + { + return getPetLevelData().getPetFeedBattle(); + } + return getPetLevelData().getPetFeedNormal(); + } + } + + public synchronized static L2PetInstance spawnPet(L2NpcTemplate template, L2PcInstance owner, L2ItemInstance control) + { + if (L2World.getInstance().getPet(owner.getObjectId()) != null) + { + return null; // owner has a pet listed in world + } + + L2PetInstance pet = restore(control, template, owner); + // add the pet instance to world + if (pet != null) + { + pet.setTitle(owner.getName()); + L2World.getInstance().addPet(owner.getObjectId(), pet); + } + + return pet; + } + + /** + * Constructor for new pet + * @param objectId + * @param template + * @param owner + * @param control + */ + public L2PetInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, L2ItemInstance control) + { + this(objectId, template, owner, control, (byte) (template.idTemplate == 12564 ? owner.getLevel() : template.level)); + } + + /** + * Constructor for restored pet + * @param objectId + * @param template + * @param owner + * @param control + * @param level + */ + public L2PetInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, L2ItemInstance control, byte level) + { + super(objectId, template, owner); + setInstanceType(InstanceType.L2PetInstance); + + _controlObjectId = control.getObjectId(); + + getStat().setLevel((byte) Math.max(level, PetDataTable.getInstance().getPetMinLevel(template.npcId))); + + _inventory = new PetInventory(this); + _inventory.restore(); + + int npcId = template.npcId; + _mountable = PetDataTable.isMountable(npcId); + getPetData(); + getPetLevelData(); + } + + @Override + public PetStat getStat() + { + return (PetStat) super.getStat(); + } + + @Override + public void initCharStat() + { + setStat(new PetStat(this)); + } + + @Override + public double getLevelMod() + { + return ((100.0 - 11) + getLevel()) / 100.0; + } + + public boolean isRespawned() + { + return _respawned; + } + + @Override + public int getSummonType() + { + return 2; + } + + @Override + public int getControlObjectId() + { + return _controlObjectId; + } + + public L2ItemInstance getControlItem() + { + return getOwner().getInventory().getItemByObjectId(_controlObjectId); + } + + public int getCurrentFed() + { + return _curFed; + } + + public void setCurrentFed(int num) + { + _curFed = num > getMaxFed() ? getMaxFed() : num; + } + + /** + * Returns the pet's currently equipped weapon instance (if any). + */ + @Override + public L2ItemInstance getActiveWeaponInstance() + { + for (L2ItemInstance item : getInventory().getItems()) + { + if ((item.getLocation() == L2ItemInstance.ItemLocation.PET_EQUIP) && (item.getItem().getBodyPart() == L2Item.SLOT_R_HAND)) + { + return item; + } + } + + return null; + } + + /** + * Returns the pet's currently equipped weapon (if any). + */ + @Override + public L2Weapon getActiveWeaponItem() + { + L2ItemInstance weapon = getActiveWeaponInstance(); + + if (weapon == null) + { + return null; + } + + return (L2Weapon) weapon.getItem(); + } + + @Override + public L2ItemInstance getSecondaryWeaponInstance() + { + // temporary? unavailable + return null; + } + + @Override + public L2Weapon getSecondaryWeaponItem() + { + // temporary? unavailable + return null; + } + + @Override + public PetInventory getInventory() + { + return _inventory; + } + + /** + * Destroys item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param objectId : int Item Instance identifier of the item to be destroyed + * @param count : int Quantity of items to be destroyed + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @return boolean informing if the action was successfull + */ + @Override + public boolean destroyItem(String process, int objectId, long count, L2Object reference, boolean sendMessage) + { + L2ItemInstance item = _inventory.destroyItem(process, objectId, count, getOwner(), reference); + + if (item == null) + { + if (sendMessage) + { + getOwner().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS)); + } + + return false; + } + + // Send Pet inventory update packet + PetInventoryUpdate petIU = new PetInventoryUpdate(); + petIU.addItem(item); + getOwner().sendPacket(petIU); + + if (sendMessage) + { + if (count > 1) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + sm.addItemName(item.getItemId()); + sm.addItemNumber(count); + getOwner().sendPacket(sm); + } + else + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); + sm.addItemName(item.getItemId()); + getOwner().sendPacket(sm); + } + } + return true; + } + + /** + * Destroy item from inventory by using its itemId and send a Server->Client InventoryUpdate packet to the L2PcInstance. + * @param process : String Identifier of process triggering this action + * @param itemId : int Item identifier of the item to be destroyed + * @param count : int Quantity of items to be destroyed + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @param sendMessage : boolean Specifies whether to send message to Client about this action + * @return boolean informing if the action was successfull + */ + @Override + public boolean destroyItemByItemId(String process, int itemId, long count, L2Object reference, boolean sendMessage) + { + L2ItemInstance item = _inventory.destroyItemByItemId(process, itemId, count, getOwner(), reference); + + if (item == null) + { + if (sendMessage) + { + getOwner().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_ITEMS)); + } + return false; + } + + // Send Pet inventory update packet + PetInventoryUpdate petIU = new PetInventoryUpdate(); + petIU.addItem(item); + getOwner().sendPacket(petIU); + + if (sendMessage) + { + if (count > 1) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + sm.addItemName(item.getItemId()); + sm.addItemNumber(count); + getOwner().sendPacket(sm); + } + else + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); + sm.addItemName(item.getItemId()); + getOwner().sendPacket(sm); + } + } + + return true; + } + + @Override + protected void doPickupItem(L2Object object) + { + boolean follow = getFollowStatus(); + if (isDead()) + { + return; + } + getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); + StopMove sm = new StopMove(getObjectId(), getX(), getY(), getZ(), getHeading()); + + if (Config.DEBUG) + { + _logPet.fine("Pet pickup pos: " + object.getX() + " " + object.getY() + " " + object.getZ()); + } + + broadcastPacket(sm); + + if (!(object instanceof L2ItemInstance)) + { + // dont try to pickup anything that is not an item :) + _logPet.warning(this + " trying to pickup wrong target." + object); + getOwner().sendPacket(ActionFailed.STATIC_PACKET); + return; + } + + L2ItemInstance target = (L2ItemInstance) object; + + // Cursed weapons + if (CursedWeaponsManager.getInstance().isCursed(target.getItemId())) + { + SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1); + smsg.addItemName(target.getItemId()); + getOwner().sendPacket(smsg); + return; + } + + synchronized (target) + { + if (!target.isVisible()) + { + getOwner().sendPacket(ActionFailed.STATIC_PACKET); + return; + } + if (!_inventory.validateCapacity(target)) + { + getOwner().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_PET_CANNOT_CARRY_ANY_MORE_ITEMS)); + return; + } + if (!_inventory.validateWeight(target, target.getCount())) + { + getOwner().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.UNABLE_TO_PLACE_ITEM_YOUR_PET_IS_TOO_ENCUMBERED)); + return; + } + if ((target.getOwnerId() != 0) && (target.getOwnerId() != getOwner().getObjectId()) && !getOwner().isInLooterParty(target.getOwnerId())) + { + getOwner().sendPacket(ActionFailed.STATIC_PACKET); + + if (target.getItemId() == 57) + { + SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1_ADENA); + smsg.addItemNumber(target.getCount()); + getOwner().sendPacket(smsg); + } + else if (target.getCount() > 1) + { + SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S2_S1_S); + smsg.addItemName(target.getItemId()); + smsg.addItemNumber(target.getCount()); + getOwner().sendPacket(smsg); + } + else + { + SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1); + smsg.addItemName(target.getItemId()); + getOwner().sendPacket(smsg); + } + + return; + } + if ((target.getItemLootShedule() != null) && ((target.getOwnerId() == getOwner().getObjectId()) || getOwner().isInLooterParty(target.getOwnerId()))) + { + target.resetOwnerTimer(); + } + + target.pickupMe(this); + + if (Config.SAVE_DROPPED_ITEM) + { + ItemsOnGroundManager.getInstance().removeObject(target); + } + } + + // Herbs + if (target.getItemType() == L2EtcItemType.HERB) + { + IItemHandler handler = ItemHandler.getInstance().getItemHandler(target.getEtcItem()); + if (handler == null) + { + _log.fine("No item handler registered for item ID " + target.getItemId() + "."); + } + else + { + handler.useItem(this, target, false); + } + + ItemTable.getInstance().destroyItem("Consume", target, getOwner(), null); + + broadcastStatusUpdate(); + } + else + { + if (target.getItemId() == 57) + { + SystemMessage sm2 = SystemMessage.getSystemMessage(SystemMessageId.PET_PICKED_S1_ADENA); + sm2.addItemNumber(target.getCount()); + getOwner().sendPacket(sm2); + } + else if (target.getEnchantLevel() > 0) + { + SystemMessage sm2 = SystemMessage.getSystemMessage(SystemMessageId.PET_PICKED_S1_S2); + sm2.addNumber(target.getEnchantLevel()); + sm2.addString(target.getName()); + getOwner().sendPacket(sm2); + } + else if (target.getCount() > 1) + { + SystemMessage sm2 = SystemMessage.getSystemMessage(SystemMessageId.PET_PICKED_S2_S1_S); + sm2.addItemNumber(target.getCount()); + sm2.addString(target.getName()); + getOwner().sendPacket(sm2); + } + else + { + SystemMessage sm2 = SystemMessage.getSystemMessage(SystemMessageId.PET_PICKED_S1); + sm2.addString(target.getName()); + getOwner().sendPacket(sm2); + } + getInventory().addItem("Pickup", target, getOwner(), this); + // FIXME Just send the updates if possible (old way wasn't working though) + PetItemList iu = new PetItemList(this); + getOwner().sendPacket(iu); + } + + getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); + + if (follow) + { + followOwner(); + } + } + + @Override + public void deleteMe(L2PcInstance owner) + { + getInventory().transferItemsToOwner(); + super.deleteMe(owner); + destroyControlItem(owner, false); // this should also delete the pet from the db + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer, true)) + { + return false; + } + stopFeed(); + getOwner().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.MAKE_SURE_YOU_RESSURECT_YOUR_PET_WITHIN_24_HOURS)); + DecayTaskManager.getInstance().addDecayTask(this, PET_DECAY_DELAY); + // do not decrease exp if is in duel, arena + L2PcInstance owner = getOwner(); + if ((owner != null) && !owner.isInDuel() && (!isInsideZone(ZONE_PVP) || isInsideZone(ZONE_SIEGE))) + { + deathPenalty(); + } + return true; + } + + @Override + public void doRevive() + { + getOwner().removeReviving(); + + super.doRevive(); + + // stopDecay + DecayTaskManager.getInstance().cancelDecayTask(this); + startFeed(); + if (!isHungry()) + { + setRunning(); + } + getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null); + } + + @Override + public void doRevive(double revivePower) + { + // Restore the pet's lost experience, + // depending on the % return of the skill used (based on its power). + restoreExp(revivePower); + doRevive(); + } + + /** + * Transfers item to another inventory + * @param process : String Identifier of process triggering this action + * @param itemId : int Item Identifier of the item to be transfered + * @param count : int Quantity of items to be transfered + * @param actor : L2PcInstance Player requesting the item transfer + * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation + * @return L2ItemInstance corresponding to the new item or the updated item in inventory + */ + public L2ItemInstance transferItem(String process, int objectId, long count, Inventory target, L2PcInstance actor, L2Object reference) + { + L2ItemInstance oldItem = getInventory().getItemByObjectId(objectId); + L2ItemInstance playerOldItem = target.getItemByItemId(oldItem.getItemId()); + L2ItemInstance newItem = getInventory().transferItem(process, objectId, count, target, actor, reference); + + if (newItem == null) + { + return null; + } + + // Send inventory update packet + PetInventoryUpdate petIU = new PetInventoryUpdate(); + if ((oldItem.getCount() > 0) && (oldItem != newItem)) + { + petIU.addModifiedItem(oldItem); + } + else + { + petIU.addRemovedItem(oldItem); + } + getOwner().sendPacket(petIU); + + // Send target update packet + if (!newItem.isStackable()) + { + InventoryUpdate iu = new InventoryUpdate(); + iu.addNewItem(newItem); + getOwner().sendPacket(iu); + } + else if ((playerOldItem != null) && newItem.isStackable()) + { + InventoryUpdate iu = new InventoryUpdate(); + iu.addModifiedItem(newItem); + getOwner().sendPacket(iu); + } + + return newItem; + } + + /** + * Remove the Pet from DB and its associated item from the player inventory + * @param owner The owner from whose invenory we should delete the item + */ + public void destroyControlItem(L2PcInstance owner, boolean evolve) + { + // remove the pet instance from world + L2World.getInstance().removePet(owner.getObjectId()); + + // delete from inventory + try + { + L2ItemInstance removedItem; + if (evolve) + { + removedItem = owner.getInventory().destroyItem("Evolve", getControlObjectId(), 1, getOwner(), this); + } + else + { + removedItem = owner.getInventory().destroyItem("PetDestroy", getControlObjectId(), 1, getOwner(), this); + if (removedItem != null) + { + owner.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED).addItemName(removedItem)); + } + } + + if (removedItem == null) + { + _log.warning("Couldn't destroy pet control item for " + owner + " pet: " + this + " evolve: " + evolve); + } + else + { + InventoryUpdate iu = new InventoryUpdate(); + iu.addRemovedItem(removedItem); + + owner.sendPacket(iu); + + StatusUpdate su = new StatusUpdate(owner); + su.addAttribute(StatusUpdate.CUR_LOAD, owner.getCurrentLoad()); + owner.sendPacket(su); + + owner.broadcastUserInfo(); + + L2World.getInstance().removeObject(removedItem); + } + } + catch (Exception e) + { + _logPet.log(Level.WARNING, "Error while destroying control item: " + e.getMessage(), e); + } + + // pet control item no longer exists, delete the pet from the db + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("DELETE FROM pets WHERE item_obj_id=?"); + statement.setInt(1, getControlObjectId()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _logPet.log(Level.SEVERE, "Failed to delete Pet [ObjectId: " + getObjectId() + "]", e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void dropAllItems() + { + try + { + for (L2ItemInstance item : getInventory().getItems()) + { + dropItemHere(item); + } + } + catch (Exception e) + { + _logPet.log(Level.WARNING, "Pet Drop Error: " + e.getMessage(), e); + } + } + + public void dropItemHere(L2ItemInstance dropit, boolean protect) + { + dropit = getInventory().dropItem("Drop", dropit.getObjectId(), dropit.getCount(), getOwner(), this); + + if (dropit != null) + { + if (protect) + { + dropit.getDropProtection().protect(getOwner()); + } + _logPet.finer("Item id to drop: " + dropit.getItemId() + " amount: " + dropit.getCount()); + dropit.dropMe(this, getX(), getY(), getZ() + 100); + } + } + + public void dropItemHere(L2ItemInstance dropit) + { + dropItemHere(dropit, false); + } + + /** @return Returns the mount able. */ + @Override + public boolean isMountable() + { + return _mountable; + } + + private static L2PetInstance restore(L2ItemInstance control, L2NpcTemplate template, L2PcInstance owner) + { + Connection con = null; + try + { + L2PetInstance pet; + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT item_obj_id, name, level, curHp, curMp, exp, sp, fed FROM pets WHERE item_obj_id=?"); + statement.setInt(1, control.getObjectId()); + ResultSet rset = statement.executeQuery(); + if (!rset.next()) + { + if (template.type.compareToIgnoreCase("L2BabyPet") == 0) + { + pet = new L2BabyPetInstance(IdFactory.getInstance().getNextId(), template, owner, control); + } + else + { + pet = new L2PetInstance(IdFactory.getInstance().getNextId(), template, owner, control); + } + + rset.close(); + statement.close(); + return pet; + } + + if (template.type.compareToIgnoreCase("L2BabyPet") == 0) + { + pet = new L2BabyPetInstance(IdFactory.getInstance().getNextId(), template, owner, control, rset.getByte("level")); + } + else + { + pet = new L2PetInstance(IdFactory.getInstance().getNextId(), template, owner, control, rset.getByte("level")); + } + + pet._respawned = true; + pet.setName(rset.getString("name")); + + long exp = rset.getLong("exp"); + L2PetLevelData info = PetDataTable.getInstance().getPetLevelData(pet.getNpcId(), pet.getLevel()); + // DS: update experience based by level + // Avoiding pet delevels due to exp per level values changed. + if ((info != null) && (exp < info.getPetMaxExp())) + { + exp = info.getPetMaxExp(); + } + + pet.getStat().setExp(exp); + pet.getStat().setSp(rset.getInt("sp")); + + pet.getStatus().setCurrentHp(rset.getDouble("curHp")); + pet.getStatus().setCurrentMp(rset.getDouble("curMp")); + pet.getStatus().setCurrentCp(pet.getMaxCp()); + if (rset.getDouble("curHp") < 0.5) + { + pet.setIsDead(true); + pet.stopHpMpRegeneration(); + } + + pet.setCurrentFed(rset.getInt("fed")); + + rset.close(); + statement.close(); + return pet; + } + catch (Exception e) + { + _logPet.log(Level.WARNING, "Could not restore pet data for owner: " + owner + " - " + e.getMessage(), e); + return null; + } + finally + { + L2DatabaseFactory.close(con); + } + } + + @Override + public void store() + { + if (getControlObjectId() == 0) + { + // this is a summon, not a pet, don't store anything + return; + } + + String req; + if (!isRespawned()) + { + req = "INSERT INTO pets (name,level,curHp,curMp,exp,sp,fed,item_obj_id) " + "VALUES (?,?,?,?,?,?,?,?)"; + } + else + { + req = "UPDATE pets SET name=?,level=?,curHp=?,curMp=?,exp=?,sp=?,fed=? " + "WHERE item_obj_id = ?"; + } + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(req); + statement.setString(1, getName()); + statement.setInt(2, getStat().getLevel()); + statement.setDouble(3, getStatus().getCurrentHp()); + statement.setDouble(4, getStatus().getCurrentMp()); + statement.setLong(5, getStat().getExp()); + statement.setInt(6, getStat().getSp()); + statement.setInt(7, getCurrentFed()); + statement.setInt(8, getControlObjectId()); + statement.executeUpdate(); + statement.close(); + _respawned = true; + } + catch (Exception e) + { + _logPet.log(Level.SEVERE, "Failed to store Pet [ObjectId: " + getObjectId() + "] data", e); + } + finally + { + L2DatabaseFactory.close(con); + } + + L2ItemInstance itemInst = getControlItem(); + if ((itemInst != null) && (itemInst.getEnchantLevel() != getStat().getLevel())) + { + itemInst.setEnchantLevel(getStat().getLevel()); + itemInst.updateDatabase(); + } + } + + public synchronized void stopFeed() + { + if (_feedTask != null) + { + _feedTask.cancel(false); + _feedTask = null; + if (Config.DEBUG) + { + _logPet.fine("Pet [#" + getObjectId() + "] feed task stop"); + } + } + } + + public synchronized void startFeed() + { + // stop feeding task if its active + + stopFeed(); + if (!isDead() && (getOwner().getPet() == this)) + { + _feedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FeedTask(), 10000, 10000); + } + } + + @Override + public synchronized void unSummon(L2PcInstance owner) + { + stopFeed(); + stopHpMpRegeneration(); + super.unSummon(owner); + + if (!isDead()) + { + if (getInventory() != null) + { + getInventory().deleteMe(); + } + L2World.getInstance().removePet(owner.getObjectId()); + } + } + + /** + * Restore the specified % of experience this L2PetInstance has lost.
    + *
    + */ + public void restoreExp(double restorePercent) + { + if (_expBeforeDeath > 0) + { + // Restore the specified % of lost experience. + getStat().addExp(Math.round(((_expBeforeDeath - getStat().getExp()) * restorePercent) / 100)); + _expBeforeDeath = 0; + } + } + + private void deathPenalty() + { + // TODO Need Correct Penalty + + int lvl = getStat().getLevel(); + double percentLost = (-0.07 * lvl) + 6.5; + + // Calculate the Experience loss + long lostExp = Math.round(((getStat().getExpForLevel(lvl + 1) - getStat().getExpForLevel(lvl)) * percentLost) / 100); + + // Get the Experience before applying penalty + _expBeforeDeath = getStat().getExp(); + + // Set the new Experience value of the L2PetInstance + getStat().addExp(-lostExp); + } + + @Override + public void addExpAndSp(long addToExp, int addToSp) + { + if (getNpcId() == 12564) + { + getStat().addExpAndSp(Math.round(addToExp * Config.SINEATER_XP_RATE), addToSp); + } + else + { + getStat().addExpAndSp(Math.round(addToExp * Config.PET_XP_RATE), addToSp); + } + } + + @Override + public long getExpForThisLevel() + { + return getStat().getExpForLevel(getLevel()); + } + + @Override + public long getExpForNextLevel() + { + return getStat().getExpForLevel(getLevel() + 1); + } + + @Override + public final int getLevel() + { + return getStat().getLevel(); + } + + public int getMaxFed() + { + return getStat().getMaxFeed(); + } + + @Override + public int getCriticalHit(L2Character target, L2Skill skill) + { + return getStat().getCriticalHit(target, skill); + } + + @Override + public int getMAtk(L2Character target, L2Skill skill) + { + return getStat().getMAtk(target, skill); + } + + @Override + public int getMDef(L2Character target, L2Skill skill) + { + return getStat().getMDef(target, skill); + } + + @Override + public final int getSkillLevel(int skillId) + { + if (getKnownSkill(skillId) == null) + { + return -1; + } + + final int lvl = getLevel(); + return lvl > 70 ? 7 + ((lvl - 70) / 5) : lvl / 10; + } + + public void updateRefOwner(L2PcInstance owner) + { + int oldOwnerId = getOwner().getObjectId(); + + setOwner(owner); + L2World.getInstance().removePet(oldOwnerId); + L2World.getInstance().addPet(oldOwnerId, this); + } + + public int getCurrentLoad() + { + return _inventory.getTotalWeight(); + } + + @Override + public final int getMaxLoad() + { + return getPetData().getLoad(); + } + + public int getInventoryLimit() + { + return Config.INVENTORY_MAXIMUM_PET; + } + + public void refreshOverloaded() + { + int maxLoad = getMaxLoad(); + if (maxLoad > 0) + { + int weightproc = (getCurrentLoad() * 1000) / maxLoad; + weightproc *= (int) calcStat(Stats.WEIGHT_LIMIT, 1, this, null); + int newWeightPenalty; + if ((weightproc < 500) || getOwner().getDietMode()) + { + newWeightPenalty = 0; + } + else if (weightproc < 666) + { + newWeightPenalty = 1; + } + else if (weightproc < 800) + { + newWeightPenalty = 2; + } + else if (weightproc < 1000) + { + newWeightPenalty = 3; + } + else + { + newWeightPenalty = 4; + } + + if (_curWeightPenalty != newWeightPenalty) + { + _curWeightPenalty = newWeightPenalty; + if (newWeightPenalty > 0) + { + addSkill(SkillTable.getInstance().getInfo(4270, newWeightPenalty)); + setIsOverloaded(getCurrentLoad() >= maxLoad); + } + else + { + super.removeSkill(getKnownSkill(4270)); + setIsOverloaded(false); + } + } + } + } + + @Override + public void updateAndBroadcastStatus(int val) + { + refreshOverloaded(); + super.updateAndBroadcastStatus(val); + } + + @Override + public final boolean isHungry() + { + return getCurrentFed() < ((getPetData().getHungry_limit() / 100f) * getPetLevelData().getPetMaxFeed()); + } + + @Override + public final int getWeapon() + { + L2ItemInstance weapon = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND); + if (weapon != null) + { + return weapon.getItemId(); + } + return 0; + } + + @Override + public final int getArmor() + { + L2ItemInstance weapon = getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST); + if (weapon != null) + { + return weapon.getItemId(); + } + return 0; + } + + public final int getJewel() + { + L2ItemInstance weapon = getInventory().getPaperdollItem(Inventory.PAPERDOLL_NECK); + if (weapon != null) + { + return weapon.getItemId(); + } + return 0; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.actor.L2Summon#getSoulShotsPerHit() + */ + @Override + public short getSoulShotsPerHit() + { + return getPetLevelData().getPetSoulShot(); + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.actor.L2Summon#getSpiritShotsPerHit() + */ + @Override + public short getSpiritShotsPerHit() + { + return getPetLevelData().getPetSpiritShot(); + } + + @Override + public void setName(String name) + { + L2ItemInstance controlItem = getControlItem(); + if (getControlItem().getCustomType2() == (name == null ? 1 : 0)) + { + // name not set yet + controlItem.setCustomType2(name != null ? 1 : 0); + controlItem.updateDatabase(); + InventoryUpdate iu = new InventoryUpdate(); + iu.addModifiedItem(controlItem); + getOwner().sendPacket(iu); + } + super.setName(name); + } + + @Override + protected void broadcastModifiedStats(FastList stats) + { + // check for initialization + if (getInstanceType() == InstanceType.L2PetInstance) + { + super.broadcastModifiedStats(stats); + } + } + + public boolean canEatFoodId(int itemId) + { + return Util.contains(_data.getFood(), itemId); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PetManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PetManagerInstance.java new file mode 100644 index 00000000..a2aef3a9 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2PetManagerInstance.java @@ -0,0 +1,181 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.util.Evolve; + +/** + * This class ... + * @version $Revision$ $Date$ + */ +public class L2PetManagerInstance extends L2MerchantInstance +{ + public L2PetManagerInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2PetManagerInstance); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/petmanager/" + pom + ".htm"; + } + + @Override + public void showChatWindow(L2PcInstance player) + { + String filename = "data/html/petmanager/" + getNpcId() + ".htm"; + + L2Summon summon = player.getPet(); + if ((getNpcId() == 36478) && (summon != null)) + { + filename = "data/html/petmanager/restore-unsummonpet.htm"; + } + + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), filename); + if (Config.ALLOW_RENTPET && Config.LIST_PET_RENT_NPC.contains(getNpcId())) + { + html.replace("_Quest", "_RentPet\">Rent Pet
    . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.List; + +import javolution.util.FastList; + +import com.l2jserver.gameserver.MonsterRace; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.idfactory.IdFactory; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.knownlist.RaceManagerKnownList; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.DeleteObject; +import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate; +import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket; +import com.l2jserver.gameserver.network.serverpackets.MonRaceInfo; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.PlaySound; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.util.Broadcast; + +public class L2RaceManagerInstance extends L2Npc +{ + public static final int LANES = 8; + public static final int WINDOW_START = 0; + + @SuppressWarnings("unused") + private static List _history; + private static List _managers; + protected static int _raceNumber = 4; + + // Time Constants + private final static long SECOND = 1000; + private final static long MINUTE = 60 * SECOND; + + private static int _minutes = 5; + + // States + private static final int ACCEPTING_BETS = 0; + private static final int WAITING = 1; + private static final int STARTING_RACE = 2; + private static final int RACE_END = 3; + private static int _state = RACE_END; + + protected static final int[][] _codes = + { + { + -1, + 0 + }, + { + 0, + 15322 + }, + { + 13765, + -1 + } + }; + private static boolean _notInitialized = true; + protected static MonRaceInfo _packet; + protected static final int _cost[] = + { + 100, + 500, + 1000, + 5000, + 10000, + 20000, + 50000, + 100000 + }; + + public L2RaceManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2RaceManagerInstance); + if (_notInitialized) + { + _notInitialized = false; + // * + _history = new FastList<>(); + _managers = new FastList<>(); + + ThreadPoolManager s = ThreadPoolManager.getInstance(); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_TICKETS_AVAILABLE_FOR_S1_RACE), 0, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_TICKETS_NOW_AVAILABLE_FOR_S1_RACE), 30 * SECOND, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_TICKETS_AVAILABLE_FOR_S1_RACE), MINUTE, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_TICKETS_NOW_AVAILABLE_FOR_S1_RACE), MINUTE + (30 * SECOND), 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_TICKETS_STOP_IN_S1_MINUTES), 2 * MINUTE, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_TICKETS_STOP_IN_S1_MINUTES), 3 * MINUTE, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_TICKETS_STOP_IN_S1_MINUTES), 4 * MINUTE, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_TICKETS_STOP_IN_S1_MINUTES), 5 * MINUTE, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_TICKETS_STOP_IN_S1_MINUTES), 6 * MINUTE, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_S1_TICKET_SALES_CLOSED), 7 * MINUTE, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_S2_BEGINS_IN_S1_MINUTES), 7 * MINUTE, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_S2_BEGINS_IN_S1_MINUTES), 8 * MINUTE, 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_S1_BEGINS_IN_30_SECONDS), (8 * MINUTE) + (30 * SECOND), 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_S1_COUNTDOWN_IN_FIVE_SECONDS), (8 * MINUTE) + (50 * SECOND), 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_BEGINS_IN_S1_SECONDS), (8 * MINUTE) + (55 * SECOND), 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_BEGINS_IN_S1_SECONDS), (8 * MINUTE) + (56 * SECOND), 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_BEGINS_IN_S1_SECONDS), (8 * MINUTE) + (57 * SECOND), 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_BEGINS_IN_S1_SECONDS), (8 * MINUTE) + (58 * SECOND), 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_BEGINS_IN_S1_SECONDS), (8 * MINUTE) + (59 * SECOND), 10 * MINUTE); + s.scheduleGeneralAtFixedRate(new Announcement(SystemMessageId.MONSRACE_RACE_START), 9 * MINUTE, 10 * MINUTE); + // */ + } + _managers.add(this); + } + + @Override + public final RaceManagerKnownList getKnownList() + { + return (RaceManagerKnownList) super.getKnownList(); + } + + @Override + public void initKnownList() + { + setKnownList(new RaceManagerKnownList(this)); + } + + class Announcement implements Runnable + { + private final SystemMessageId _type; + + public Announcement(SystemMessageId pType) + { + _type = pType; + } + + @Override + public void run() + { + makeAnnouncement(_type); + } + } + + public void makeAnnouncement(SystemMessageId type) + { + SystemMessage sm = SystemMessage.getSystemMessage(type); + switch (type.getId()) + { + case 816: // SystemMessageId.MONSRACE_TICKETS_AVAILABLE_FOR_S1_RACE + case 817: // SystemMessageId.MONSRACE_TICKETS_NOW_AVAILABLE_FOR_S1_RACE + if (_state != ACCEPTING_BETS) + {// _log.info("Race Initializing"); + _state = ACCEPTING_BETS; + startRace(); + }// else{_log.info("Race open");} + sm.addNumber(_raceNumber); + break; + case 818: // SystemMessageId.MONSRACE_TICKETS_STOP_IN_S1_MINUTES + case 820: // SystemMessageId.MONSRACE_S2_BEGINS_IN_S1_MINUTES + case 823: // SystemMessageId.MONSRACE_BEGINS_IN_S1_SECONDS + sm.addNumber(_minutes); + if (type.getId() == 820) + { + sm.addNumber(_raceNumber); + } + _minutes--; + break; + case 819: // SystemMessageId.MONSRACE_S1_TICKET_SALES_CLOSED + // _log.info("Sales closed"); + sm.addNumber(_raceNumber); + _state = WAITING; + _minutes = 2; + break; + case 821: // SystemMessageId.MONSRACE_S1_BEGINS_IN_30_SECONDS + case 822: // SystemMessageId.MONSRACE_S1_COUNTDOWN_IN_FIVE_SECONDS + case 825: // SystemMessageId.MONSRACE_S1_RACE_END + sm.addNumber(_raceNumber); + _minutes = 5; + break; + case 826: // SystemMessageId.MONSRACE_FIRST_PLACE_S1_SECOND_S2 + // _log.info("Placing"); + _state = RACE_END; + sm.addNumber(MonsterRace.getInstance().getFirstPlace()); + sm.addNumber(MonsterRace.getInstance().getSecondPlace()); + break; + } + // _logn.info("Counter: "+minutes); + // _log.info("State: "+state); + broadcast(sm); + // _log.info("Player's known: "+getKnownPlayers().size()); + + if (type == SystemMessageId.MONSRACE_RACE_START) + { + // _log.info("Starting race"); + _state = STARTING_RACE; + startRace(); + _minutes = 5; + } + } + + protected void broadcast(L2GameServerPacket pkt) + { + for (L2RaceManagerInstance manager : _managers) + { + if (!manager.isDead()) + { + Broadcast.toKnownPlayers(manager, pkt); + } + } + } + + public void sendMonsterInfo() + { + broadcast(_packet); + } + + private void startRace() + { + MonsterRace race = MonsterRace.getInstance(); + if (_state == STARTING_RACE) + { + // state++; + PlaySound SRace = new PlaySound(1, "S_Race", 0, 0, 0, 0, 0); + broadcast(SRace); + PlaySound SRace2 = new PlaySound(0, "ItemSound2.race_start", 1, 121209259, 12125, 182487, -3559); + broadcast(SRace2); + _packet = new MonRaceInfo(_codes[1][0], _codes[1][1], race.getMonsters(), race.getSpeeds()); + sendMonsterInfo(); + + ThreadPoolManager.getInstance().scheduleGeneral(new RunRace(), 5000); + } + else + { + // state++; + race.newRace(); + race.newSpeeds(); + _packet = new MonRaceInfo(_codes[0][0], _codes[0][1], race.getMonsters(), race.getSpeeds()); + sendMonsterInfo(); + } + + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if (command.startsWith("BuyTicket") && (_state != ACCEPTING_BETS)) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_TICKETS_NOT_AVAILABLE)); + command = "Chat 0"; + } + if (command.startsWith("ShowOdds") && (_state == ACCEPTING_BETS)) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.MONSRACE_NO_PAYOUT_INFO)); + command = "Chat 0"; + } + + if (command.startsWith("BuyTicket")) + { + int val = Integer.parseInt(command.substring(10)); + if (val == 0) + { + player.setRace(0, 0); + player.setRace(1, 0); + } + if (((val == 10) && (player.getRace(0) == 0)) || ((val == 20) && (player.getRace(0) == 0) && (player.getRace(1) == 0))) + { + val = 0; + } + showBuyTicket(player, val); + } + else if (command.equals("ShowOdds")) + { + showOdds(player); + } + else if (command.equals("ShowInfo")) + { + showMonsterInfo(player); + } + else if (command.equals("calculateWin")) + { + // displayCalculateWinnings(player); + } + else if (command.equals("viewHistory")) + { + // displayHistory(player); + } + else + { + // getKnownList().removeKnownObject(player); + super.onBypassFeedback(player, command); + } + } + + public void showOdds(L2PcInstance player) + { + if (_state == ACCEPTING_BETS) + { + return; + } + int npcId = getTemplate().npcId; + String filename, search; + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + filename = getHtmlPath(npcId, 5); + html.setFile(player.getHtmlPrefix(), filename); + for (int i = 0; i < 8; i++) + { + int n = i + 1; + search = "Mob" + n; + html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().name); + } + html.replace("1race", String.valueOf(_raceNumber)); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + public void showMonsterInfo(L2PcInstance player) + { + int npcId = getTemplate().npcId; + String filename, search; + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + filename = getHtmlPath(npcId, 6); + html.setFile(player.getHtmlPrefix(), filename); + for (int i = 0; i < 8; i++) + { + int n = i + 1; + search = "Mob" + n; + html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().name); + } + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + public void showBuyTicket(L2PcInstance player, int val) + { + if (_state != ACCEPTING_BETS) + { + return; + } + int npcId = getTemplate().npcId; + SystemMessage sm; + String filename, search, replace; + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + if (val < 10) + { + filename = getHtmlPath(npcId, 2); + html.setFile(player.getHtmlPrefix(), filename); + for (int i = 0; i < 8; i++) + { + int n = i + 1; + search = "Mob" + n; + html.replace(search, MonsterRace.getInstance().getMonsters()[i].getTemplate().name); + } + search = "No1"; + if (val == 0) + { + html.replace(search, ""); + } + else + { + html.replace(search, "" + val); + player.setRace(0, val); + } + } + else if (val < 20) + { + if (player.getRace(0) == 0) + { + return; + } + filename = getHtmlPath(npcId, 3); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("0place", "" + player.getRace(0)); + search = "Mob1"; + replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().name; + html.replace(search, replace); + search = "0adena"; + if (val == 10) + { + html.replace(search, ""); + } + else + { + html.replace(search, "" + _cost[val - 11]); + player.setRace(1, val - 10); + } + } + else if (val == 20) + { + if ((player.getRace(0) == 0) || (player.getRace(1) == 0)) + { + return; + } + filename = getHtmlPath(npcId, 4); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("0place", "" + player.getRace(0)); + search = "Mob1"; + replace = MonsterRace.getInstance().getMonsters()[player.getRace(0) - 1].getTemplate().name; + html.replace(search, replace); + search = "0adena"; + int price = _cost[player.getRace(1) - 1]; + html.replace(search, "" + price); + search = "0tax"; + int tax = 0; + html.replace(search, "" + tax); + search = "0total"; + int total = price + tax; + html.replace(search, "" + total); + } + else + { + if ((player.getRace(0) == 0) || (player.getRace(1) == 0)) + { + return; + } + int ticket = player.getRace(0); + int priceId = player.getRace(1); + if (!player.reduceAdena("Race", _cost[priceId - 1], this, true)) + { + return; + } + player.setRace(0, 0); + player.setRace(1, 0); + sm = SystemMessage.getSystemMessage(SystemMessageId.ACQUIRED_S1_S2); + sm.addNumber(_raceNumber); + sm.addItemName(4443); + player.sendPacket(sm); + L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), 4443); + item.setCount(1); + item.setEnchantLevel(_raceNumber); + item.setCustomType1(ticket); + item.setCustomType2(_cost[priceId - 1] / 100); + player.getInventory().addItem("Race", item, player, this); + InventoryUpdate iu = new InventoryUpdate(); + iu.addItem(item); + L2ItemInstance adenaupdate = player.getInventory().getItemByItemId(57); + iu.addModifiedItem(adenaupdate); + player.sendPacket(iu); + return; + } + html.replace("1race", String.valueOf(_raceNumber)); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + public static class Race + { + private final Info[] _info; + + public Race(Info[] pInfo) + { + _info = pInfo; + } + + public Info getLaneInfo(int lane) + { + return _info[lane]; + } + + public class Info + { + private final int _id; + private final int _place; + private final int _odds; + private final int _payout; + + public Info(int pId, int pPlace, int pOdds, int pPayout) + { + _id = pId; + _place = pPlace; + _odds = pOdds; + _payout = pPayout; + } + + public int getId() + { + return _id; + } + + public int getOdds() + { + return _odds; + } + + public int getPayout() + { + return _payout; + } + + public int getPlace() + { + return _place; + } + } + + } + + class RunRace implements Runnable + { + @Override + public void run() + { + _packet = new MonRaceInfo(_codes[2][0], _codes[2][1], MonsterRace.getInstance().getMonsters(), MonsterRace.getInstance().getSpeeds()); + sendMonsterInfo(); + ThreadPoolManager.getInstance().scheduleGeneral(new RunEnd(), 30000); + } + } + + class RunEnd implements Runnable + { + @Override + public void run() + { + makeAnnouncement(SystemMessageId.MONSRACE_FIRST_PLACE_S1_SECOND_S2); + makeAnnouncement(SystemMessageId.MONSRACE_S1_RACE_END); + _raceNumber++; + + DeleteObject obj = null; + for (int i = 0; i < 8; i++) + { + obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]); + broadcast(obj); + } + } + } + +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2RaidBossInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2RaidBossInstance.java new file mode 100644 index 00000000..f8c74339 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2RaidBossInstance.java @@ -0,0 +1,211 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.instancemanager.RaidBossPointsManager; +import com.l2jserver.gameserver.instancemanager.RaidBossSpawnManager; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2Spawn; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.entity.Hero; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.Rnd; + +/** + * This class manages all RaidBoss. In a group mob, there are one master called RaidBoss and several slaves called Minions. + * @version $Revision: 1.20.4.6 $ $Date: 2005/04/06 16:13:39 $ + */ +public class L2RaidBossInstance extends L2MonsterInstance +{ + private static final int RAIDBOSS_MAINTENANCE_INTERVAL = 30000; // 30 sec + + private RaidBossSpawnManager.StatusEnum _raidStatus; + private boolean _useRaidCurse = true; + + /** + * Constructor of L2RaidBossInstance (use L2Character and L2NpcInstance constructor).
    + *
    + * Actions :
    + *
    + *
  • Call the L2Character constructor to set the _template of the L2RaidBossInstance (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR)
  • Set the name of the L2RaidBossInstance
  • Create a RandomAnimation Task that will be launched after the + * calculated delay if the server allow it

  • + *
    + * @param objectId Identifier of the object to initialized + * @param L2NpcTemplate Template to apply to the NPC + */ + public L2RaidBossInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2RaidBossInstance); + setIsRaid(true); + } + + @Override + public void onSpawn() + { + setIsNoRndWalk(true); + super.onSpawn(); + } + + @Override + protected int getMaintenanceInterval() + { + return RAIDBOSS_MAINTENANCE_INTERVAL; + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + + L2PcInstance player = null; + if (killer instanceof L2PcInstance) + { + player = (L2PcInstance) killer; + } + else if (killer instanceof L2Summon) + { + player = ((L2Summon) killer).getOwner(); + } + + if (player != null) + { + broadcastPacket(SystemMessage.getSystemMessage(SystemMessageId.RAID_WAS_SUCCESSFUL)); + if (player.getParty() != null) + { + for (L2PcInstance member : player.getParty().getPartyMembers()) + { + RaidBossPointsManager.getInstance().addPoints(member, getNpcId(), (getLevel() / 2) + Rnd.get(-5, 5)); + if (member.isNoble()) + { + Hero.getInstance().setRBkilled(member.getObjectId(), getNpcId()); + } + } + } + else + { + RaidBossPointsManager.getInstance().addPoints(player, getNpcId(), (getLevel() / 2) + Rnd.get(-5, 5)); + if (player.isNoble()) + { + Hero.getInstance().setRBkilled(player.getObjectId(), getNpcId()); + } + } + } + + RaidBossSpawnManager.getInstance().updateStatus(this, true); + return true; + } + + /** + * Spawn all minions at a regular interval Also if boss is too far from home location at the time of this check, teleport it home + */ + @Override + protected void startMaintenanceTask() + { + if (getTemplate().getMinionData() != null) + { + getMinionList().spawnMinions(); + } + + _maintenanceTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(() -> checkAndReturnToSpawn(), 60000, getMaintenanceInterval() + Rnd.get(5000)); + } + + protected void checkAndReturnToSpawn() + { + if (isDead() || isMovementDisabled()) + { + return; + } + + // Gordon does not have permanent spawn + if (getNpcId() == 29095) + { + return; + } + + final L2Spawn spawn = getSpawn(); + if (spawn == null) + { + return; + } + + final int spawnX = spawn.getLocx(); + final int spawnY = spawn.getLocy(); + final int spawnZ = spawn.getLocz(); + + if (!isInCombat() && !isMovementDisabled()) + { + if (!isInsideRadius(spawnX, spawnY, spawnZ, Math.max(Config.MAX_DRIFT_RANGE, 200), true, false)) + { + teleToLocation(spawnX, spawnY, spawnZ, false); + } + } + } + + /** + * Reduce the current HP of the L2Attackable, update its _aggroList and launch the doDie Task if necessary.
    + *
    + */ + @Override + public void reduceCurrentHp(double damage, L2Character attacker, boolean awake, boolean isDOT, L2Skill skill) + { + super.reduceCurrentHp(damage, attacker, awake, isDOT, skill); + } + + public void setRaidStatus(RaidBossSpawnManager.StatusEnum status) + { + _raidStatus = status; + } + + public RaidBossSpawnManager.StatusEnum getRaidStatus() + { + return _raidStatus; + } + + @Override + public float getVitalityPoints(int damage) + { + return -super.getVitalityPoints(damage) / 100; + } + + @Override + public boolean useVitalityRate() + { + return false; + } + + public void setUseRaidCurse(boolean val) + { + _useRaidCurse = val; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.actor.L2Character#giveRaidCurse() + */ + @Override + public boolean giveRaidCurse() + { + return _useRaidCurse; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2RiftInvaderInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2RiftInvaderInstance.java new file mode 100644 index 00000000..ebb36062 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2RiftInvaderInstance.java @@ -0,0 +1,27 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2RiftInvaderInstance extends L2MonsterInstance +{ + // Not longer needed since rift monster targeting control now is handled by the room zones for any mob + public L2RiftInvaderInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2RiftInvaderInstance); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SepulcherMonsterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SepulcherMonsterInstance.java new file mode 100644 index 00000000..ad452a2f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SepulcherMonsterInstance.java @@ -0,0 +1,518 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.concurrent.Future; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.instancemanager.FourSepulchersManager; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.quest.QuestState; +import com.l2jserver.gameserver.network.serverpackets.NpcSay; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author sandman + */ +public class L2SepulcherMonsterInstance extends L2MonsterInstance +{ + public int mysteriousBoxId = 0; + + protected Future _victimSpawnKeyBoxTask = null; + protected Future _victimShout = null; + protected Future _changeImmortalTask = null; + protected Future _onDeadEventTask = null; + + public L2SepulcherMonsterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2SepulcherMonsterInstance); + setShowSummonAnimation(true); + switch (template.npcId) + { + case 25339: + case 25342: + case 25346: + case 25349: + setIsRaid(true); + } + } + + @Override + public void onSpawn() + { + setShowSummonAnimation(false); + switch (getNpcId()) + { + case 18150: + case 18151: + case 18152: + case 18153: + case 18154: + case 18155: + case 18156: + case 18157: + if (_victimSpawnKeyBoxTask != null) + { + _victimSpawnKeyBoxTask.cancel(true); + } + _victimSpawnKeyBoxTask = ThreadPoolManager.getInstance().scheduleEffect(new VictimSpawnKeyBox(this), 300000); + if (_victimShout != null) + { + _victimShout.cancel(true); + } + _victimShout = ThreadPoolManager.getInstance().scheduleEffect(new VictimShout(this), 5000); + break; + case 18196: + case 18197: + case 18198: + case 18199: + case 18200: + case 18201: + case 18202: + case 18203: + case 18204: + case 18205: + case 18206: + case 18207: + case 18208: + case 18209: + case 18210: + case 18211: + break; + + case 18231: + case 18232: + case 18233: + case 18234: + case 18235: + case 18236: + case 18237: + case 18238: + case 18239: + case 18240: + case 18241: + case 18242: + case 18243: + if (_changeImmortalTask != null) + { + _changeImmortalTask.cancel(true); + } + _changeImmortalTask = ThreadPoolManager.getInstance().scheduleEffect(new ChangeImmortal(this), 1600); + + break; + case 18256: + break; + case 25339: + case 25342: + case 25346: + case 25349: + setIsRaid(true); + break; + } + super.onSpawn(); + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + + switch (getNpcId()) + { + case 18120: + case 18121: + case 18122: + case 18123: + case 18124: + case 18125: + case 18126: + case 18127: + case 18128: + case 18129: + case 18130: + case 18131: + case 18149: + case 18158: + case 18159: + case 18160: + case 18161: + case 18162: + case 18163: + case 18164: + case 18165: + case 18183: + case 18184: + case 18212: + case 18213: + case 18214: + case 18215: + case 18216: + case 18217: + case 18218: + case 18219: + if (_onDeadEventTask != null) + { + _onDeadEventTask.cancel(true); + } + _onDeadEventTask = ThreadPoolManager.getInstance().scheduleEffect(new OnDeadEvent(this), 3500); + break; + + case 18150: + case 18151: + case 18152: + case 18153: + case 18154: + case 18155: + case 18156: + case 18157: + if (_victimSpawnKeyBoxTask != null) + { + _victimSpawnKeyBoxTask.cancel(true); + _victimSpawnKeyBoxTask = null; + } + if (_victimShout != null) + { + _victimShout.cancel(true); + _victimShout = null; + } + if (_onDeadEventTask != null) + { + _onDeadEventTask.cancel(true); + } + _onDeadEventTask = ThreadPoolManager.getInstance().scheduleEffect(new OnDeadEvent(this), 3500); + break; + + case 18141: + case 18142: + case 18143: + case 18144: + case 18145: + case 18146: + case 18147: + case 18148: + if (FourSepulchersManager.getInstance().isViscountMobsAnnihilated(mysteriousBoxId)) + { + if (_onDeadEventTask != null) + { + _onDeadEventTask.cancel(true); + } + _onDeadEventTask = ThreadPoolManager.getInstance().scheduleEffect(new OnDeadEvent(this), 3500); + } + break; + + case 18220: + case 18221: + case 18222: + case 18223: + case 18224: + case 18225: + case 18226: + case 18227: + case 18228: + case 18229: + case 18230: + case 18231: + case 18232: + case 18233: + case 18234: + case 18235: + case 18236: + case 18237: + case 18238: + case 18239: + case 18240: + if (FourSepulchersManager.getInstance().isDukeMobsAnnihilated(mysteriousBoxId)) + { + if (_onDeadEventTask != null) + { + _onDeadEventTask.cancel(true); + } + _onDeadEventTask = ThreadPoolManager.getInstance().scheduleEffect(new OnDeadEvent(this), 3500); + } + break; + + case 25339: + case 25342: + case 25346: + case 25349: + giveCup(killer); + if (_onDeadEventTask != null) + { + _onDeadEventTask.cancel(true); + } + _onDeadEventTask = ThreadPoolManager.getInstance().scheduleEffect(new OnDeadEvent(this), 8500); + break; + } + return true; + } + + @Override + public void deleteMe() + { + if (_victimSpawnKeyBoxTask != null) + { + _victimSpawnKeyBoxTask.cancel(true); + _victimSpawnKeyBoxTask = null; + } + if (_onDeadEventTask != null) + { + _onDeadEventTask.cancel(true); + _onDeadEventTask = null; + } + + super.deleteMe(); + } + + private void giveCup(L2Character killer) + { + String questId = "620_FourGoblets"; + int cupId = 0; + int oldBrooch = 7262; + + switch (getNpcId()) + { + case 25339: + cupId = 7256; + break; + case 25342: + cupId = 7257; + break; + case 25346: + cupId = 7258; + break; + case 25349: + cupId = 7259; + break; + } + + L2PcInstance player = killer.getActingPlayer(); + + if (player == null) + { + return; + } + + if (player.getParty() != null) + { + for (L2PcInstance mem : player.getParty().getPartyMembers()) + { + QuestState qs = mem.getQuestState(questId); + if ((qs != null) && (qs.isStarted() || qs.isCompleted()) && (mem.getInventory().getItemByItemId(oldBrooch) == null)) + { + mem.addItem("Quest", cupId, 1, mem, true); + } + } + } + else + { + QuestState qs = player.getQuestState(questId); + if ((qs != null) && (qs.isStarted() || qs.isCompleted()) && (player.getInventory().getItemByItemId(oldBrooch) == null)) + { + player.addItem("Quest", cupId, 1, player, true); + } + } + } + + private class VictimShout implements Runnable + { + private final L2SepulcherMonsterInstance _activeChar; + + public VictimShout(L2SepulcherMonsterInstance activeChar) + { + _activeChar = activeChar; + } + + @Override + public void run() + { + if (_activeChar.isDead()) + { + return; + } + + if (!_activeChar.isVisible()) + { + return; + } + + broadcastPacket(new NpcSay(getObjectId(), 0, getNpcId(), "forgive me!!")); + } + } + + private class VictimSpawnKeyBox implements Runnable + { + private final L2SepulcherMonsterInstance _activeChar; + + public VictimSpawnKeyBox(L2SepulcherMonsterInstance activeChar) + { + _activeChar = activeChar; + } + + @Override + public void run() + { + if (_activeChar.isDead()) + { + return; + } + + if (!_activeChar.isVisible()) + { + return; + } + + FourSepulchersManager.getInstance().spawnKeyBox(_activeChar); + broadcastPacket(new NpcSay(getObjectId(), 0, getNpcId(), "Many thanks for rescue me.")); + if (_victimShout != null) + { + _victimShout.cancel(true); + } + + } + } + + private static class OnDeadEvent implements Runnable + { + L2SepulcherMonsterInstance _activeChar; + + public OnDeadEvent(L2SepulcherMonsterInstance activeChar) + { + _activeChar = activeChar; + } + + @Override + public void run() + { + switch (_activeChar.getNpcId()) + { + case 18120: + case 18121: + case 18122: + case 18123: + case 18124: + case 18125: + case 18126: + case 18127: + case 18128: + case 18129: + case 18130: + case 18131: + case 18149: + case 18158: + case 18159: + case 18160: + case 18161: + case 18162: + case 18163: + case 18164: + case 18165: + case 18183: + case 18184: + case 18212: + case 18213: + case 18214: + case 18215: + case 18216: + case 18217: + case 18218: + case 18219: + FourSepulchersManager.getInstance().spawnKeyBox(_activeChar); + break; + + case 18150: + case 18151: + case 18152: + case 18153: + case 18154: + case 18155: + case 18156: + case 18157: + FourSepulchersManager.getInstance().spawnExecutionerOfHalisha(_activeChar); + break; + + case 18141: + case 18142: + case 18143: + case 18144: + case 18145: + case 18146: + case 18147: + case 18148: + FourSepulchersManager.getInstance().spawnMonster(_activeChar.mysteriousBoxId); + break; + + case 18220: + case 18221: + case 18222: + case 18223: + case 18224: + case 18225: + case 18226: + case 18227: + case 18228: + case 18229: + case 18230: + case 18231: + case 18232: + case 18233: + case 18234: + case 18235: + case 18236: + case 18237: + case 18238: + case 18239: + case 18240: + FourSepulchersManager.getInstance().spawnArchonOfHalisha(_activeChar.mysteriousBoxId); + break; + + case 25339: + case 25342: + case 25346: + case 25349: + FourSepulchersManager.getInstance().spawnEmperorsGraveNpc(_activeChar.mysteriousBoxId); + break; + } + } + } + + private static class ChangeImmortal implements Runnable + { + L2SepulcherMonsterInstance activeChar; + + public ChangeImmortal(L2SepulcherMonsterInstance mob) + { + activeChar = mob; + } + + @Override + public void run() + { + L2Skill fp = SkillTable.FrequentSkill.FAKE_PETRIFICATION.getSkill(); // Invulnerable by petrification + fp.getEffects(activeChar, activeChar); + } + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return true; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SepulcherNpcInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SepulcherNpcInstance.java new file mode 100644 index 00000000..21c72ef5 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SepulcherNpcInstance.java @@ -0,0 +1,487 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.Collection; +import java.util.concurrent.Future; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.datatables.DoorTable; +import com.l2jserver.gameserver.instancemanager.FourSepulchersManager; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.quest.Quest; +import com.l2jserver.gameserver.network.clientpackets.Say2; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.CreatureSay; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SocialAction; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.Rnd; + +/** + * @author sandman + */ +public class L2SepulcherNpcInstance extends L2Npc +{ + protected Future _closeTask = null; + protected Future _spawnNextMysteriousBoxTask = null; + protected Future _spawnMonsterTask = null; + + private final static String HTML_FILE_PATH = "data/html/SepulcherNpc/"; + private final static int HALLS_KEY = 7260; + + public L2SepulcherNpcInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2SepulcherNpcInstance); + setShowSummonAnimation(true); + + if (_closeTask != null) + { + _closeTask.cancel(true); + } + if (_spawnNextMysteriousBoxTask != null) + { + _spawnNextMysteriousBoxTask.cancel(true); + } + if (_spawnMonsterTask != null) + { + _spawnMonsterTask.cancel(true); + } + _closeTask = null; + _spawnNextMysteriousBoxTask = null; + _spawnMonsterTask = null; + } + + @Override + public void onSpawn() + { + super.onSpawn(); + setShowSummonAnimation(false); + } + + @Override + public void deleteMe() + { + if (_closeTask != null) + { + _closeTask.cancel(true); + _closeTask = null; + } + if (_spawnNextMysteriousBoxTask != null) + { + _spawnNextMysteriousBoxTask.cancel(true); + _spawnNextMysteriousBoxTask = null; + } + if (_spawnMonsterTask != null) + { + _spawnMonsterTask.cancel(true); + _spawnMonsterTask = null; + } + super.deleteMe(); + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if (!canTarget(player)) + { + return; + } + + // Check if the L2PcInstance already target the L2NpcInstance + if (this != player.getTarget()) + { + if (Config.DEBUG) + { + _log.info("new target selected:" + getObjectId()); + } + + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Check if the player is attackable (without a forced attack) + if (isAutoAttackable(player)) + { + // Send a Server->Client packet MyTargetSelected to the + // L2PcInstance player + // The player.getLevel() - getLevel() permit to display the + // correct color in the select window + MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()); + player.sendPacket(my); + + // Send a Server->Client packet StatusUpdate of the + // L2NpcInstance to the L2PcInstance to update its HP bar + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_HP, (int) getStatus().getCurrentHp()); + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); + player.sendPacket(su); + } + else + { + // Send a Server->Client packet MyTargetSelected to the + // L2PcInstance player + MyTargetSelected my = new MyTargetSelected(getObjectId(), 0); + player.sendPacket(my); + } + + // Send a Server->Client packet ValidateLocation to correct the + // L2NpcInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + // Check if the player is attackable (without a forced attack) and + // isn't dead + if (isAutoAttackable(player) && !isAlikeDead()) + { + // Check the height difference + if (Math.abs(player.getZ() - getZ()) < 400) // this max heigth + // difference might + // need some tweaking + { + // Set the L2PcInstance Intention to AI_INTENTION_ATTACK + player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); + } + else + { + // Send a Server->Client packet ActionFailed (target is out + // of attack range) to the L2PcInstance player + player.sendPacket(ActionFailed.STATIC_PACKET); + } + } + + if (!isAutoAttackable(player)) + { + // Calculate the distance between the L2PcInstance and the + // L2NpcInstance + if (!canInteract(player)) + { + // Notify the L2PcInstance AI with AI_INTENTION_INTERACT + player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this); + } + else + { + // Send a Server->Client packet SocialAction to the all + // L2PcInstance on the _knownPlayer of the L2NpcInstance + // to display a social action of the L2NpcInstance on their + // client + SocialAction sa = new SocialAction(this, Rnd.get(8)); + broadcastPacket(sa); + + doAction(player); + } + } + // Send a Server->Client ActionFailed to the L2PcInstance in order + // to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + } + + private void doAction(L2PcInstance player) + { + if (isDead()) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + return; + } + + switch (getNpcId()) + { + case 31468: + case 31469: + case 31470: + case 31471: + case 31472: + case 31473: + case 31474: + case 31475: + case 31476: + case 31477: + case 31478: + case 31479: + case 31480: + case 31481: + case 31482: + case 31483: + case 31484: + case 31485: + case 31486: + case 31487: + setIsInvul(false); + reduceCurrentHp(getMaxHp() + 1, player, null); + if (_spawnMonsterTask != null) + { + _spawnMonsterTask.cancel(true); + } + _spawnMonsterTask = ThreadPoolManager.getInstance().scheduleEffect(new SpawnMonster(getNpcId()), 3500); + break; + + case 31455: + case 31456: + case 31457: + case 31458: + case 31459: + case 31460: + case 31461: + case 31462: + case 31463: + case 31464: + case 31465: + case 31466: + case 31467: + setIsInvul(false); + reduceCurrentHp(getMaxHp() + 1, player, null); + if ((player.getParty() != null) && !player.getParty().isLeader(player)) + { + player = player.getParty().getLeader(); + } + player.addItem("Quest", HALLS_KEY, 1, player, true); + break; + + default: + { + Quest[] qlsa = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START); + if ((qlsa != null) && (qlsa.length > 0)) + { + player.setLastQuestNpcObject(getObjectId()); + } + Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.ON_FIRST_TALK); + if ((qlst != null) && (qlst.length == 1)) + { + qlst[0].notifyFirstTalk(this, player); + } + else + { + showChatWindow(player, 0); + } + } + } + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return HTML_FILE_PATH + pom + ".htm"; + } + + @Override + public void showChatWindow(L2PcInstance player, int val) + { + String filename = getHtmlPath(getNpcId(), val); + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if (isBusy()) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/npcbusy.htm"); + html.replace("%busymessage%", getBusyMessage()); + html.replace("%npcname%", getName()); + html.replace("%playername%", player.getName()); + player.sendPacket(html); + } + else if (command.startsWith("Chat")) + { + int val = 0; + try + { + val = Integer.parseInt(command.substring(5)); + } + catch (IndexOutOfBoundsException ioobe) + { + } + catch (NumberFormatException nfe) + { + } + showChatWindow(player, val); + } + else if (command.startsWith("open_gate")) + { + L2ItemInstance hallsKey = player.getInventory().getItemByItemId(HALLS_KEY); + if (hallsKey == null) + { + showHtmlFile(player, "Gatekeeper-no.htm"); + } + else if (FourSepulchersManager.getInstance().isAttackTime()) + { + switch (getNpcId()) + { + case 31929: + case 31934: + case 31939: + case 31944: + FourSepulchersManager.getInstance().spawnShadow(getNpcId()); + default: + { + openNextDoor(getNpcId()); + if (player.getParty() != null) + { + for (L2PcInstance mem : player.getParty().getPartyMembers()) + { + if ((mem != null) && (mem.getInventory().getItemByItemId(HALLS_KEY) != null)) + { + mem.destroyItemByItemId("Quest", HALLS_KEY, mem.getInventory().getItemByItemId(HALLS_KEY).getCount(), mem, true); + } + } + } + else + { + player.destroyItemByItemId("Quest", HALLS_KEY, hallsKey.getCount(), player, true); + } + } + } + } + } + else + { + super.onBypassFeedback(player, command); + } + } + + public void openNextDoor(int npcId) + { + int doorId = FourSepulchersManager.getInstance().getHallGateKeepers().get(npcId); + DoorTable _doorTable = DoorTable.getInstance(); + _doorTable.getDoor(doorId).openMe(); + + if (_closeTask != null) + { + _closeTask.cancel(true); + } + _closeTask = ThreadPoolManager.getInstance().scheduleEffect(new CloseNextDoor(doorId), 10000); + if (_spawnNextMysteriousBoxTask != null) + { + _spawnNextMysteriousBoxTask.cancel(true); + } + _spawnNextMysteriousBoxTask = ThreadPoolManager.getInstance().scheduleEffect(new SpawnNextMysteriousBox(npcId), 0); + } + + private static class CloseNextDoor implements Runnable + { + final DoorTable _DoorTable = DoorTable.getInstance(); + + private final int _DoorId; + + public CloseNextDoor(int doorId) + { + _DoorId = doorId; + } + + @Override + public void run() + { + try + { + _DoorTable.getDoor(_DoorId).closeMe(); + } + catch (Exception e) + { + _log.warning(e.getMessage()); + } + } + } + + private static class SpawnNextMysteriousBox implements Runnable + { + private final int _NpcId; + + public SpawnNextMysteriousBox(int npcId) + { + _NpcId = npcId; + } + + @Override + public void run() + { + FourSepulchersManager.getInstance().spawnMysteriousBox(_NpcId); + } + } + + private static class SpawnMonster implements Runnable + { + private final int _NpcId; + + public SpawnMonster(int npcId) + { + _NpcId = npcId; + } + + @Override + public void run() + { + FourSepulchersManager.getInstance().spawnMonster(_NpcId); + } + } + + public void sayInShout(String msg) + { + if ((msg == null) || msg.isEmpty()) + { + return;// wrong usage + } + Collection knownPlayers = L2World.getInstance().getAllPlayers().values(); + if ((knownPlayers == null) || knownPlayers.isEmpty()) + { + return; + } + CreatureSay sm = new CreatureSay(0, Say2.SHOUT, getName(), msg); + for (L2PcInstance player : knownPlayers) + { + if (player == null) + { + continue; + } + if (Util.checkIfInRange(15000, player, this, true)) + { + player.sendPacket(sm); + } + } + } + + public void showHtmlFile(L2PcInstance player, String file) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/SepulcherNpc/" + file); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SiegeFlagInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SiegeFlagInstance.java new file mode 100644 index 00000000..c84f3ec6 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SiegeFlagInstance.java @@ -0,0 +1,255 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.instancemanager.FortSiegeManager; +import com.l2jserver.gameserver.instancemanager.SiegeManager; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2SiegeClan; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.status.SiegeFlagStatus; +import com.l2jserver.gameserver.model.entity.Siegable; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2SiegeFlagInstance extends L2Npc +{ + private L2Clan _clan; + private L2PcInstance _player; + private Siegable _siege; + private final boolean _isAdvanced; + private boolean _canTalk; + + public L2SiegeFlagInstance(L2PcInstance player, int objectId, L2NpcTemplate template, boolean advanced, boolean outPost) + { + super(objectId, template); + setInstanceType(InstanceType.L2SiegeFlagInstance); + + if (TerritoryWarManager.getInstance().isTWInProgress()) + { + _clan = player.getClan(); + _player = player; + _canTalk = false; + if (_clan == null) + { + deleteMe(); + } + if (outPost) + { + _isAdvanced = false; + setIsInvul(true); + } + else + { + _isAdvanced = advanced; + setIsInvul(false); + } + getStatus(); + return; + } + _clan = player.getClan(); + _player = player; + _canTalk = true; + _siege = SiegeManager.getInstance().getSiege(_player.getX(), _player.getY(), _player.getZ()); + if (_siege == null) + { + _siege = FortSiegeManager.getInstance().getSiege(_player.getX(), _player.getY(), _player.getZ()); + } + if ((_clan == null) || (_siege == null)) + { + throw new NullPointerException(getClass().getSimpleName() + ": Initialization failed."); + } + L2SiegeClan sc = _siege.getAttackerClan(_clan); + if (sc == null) + { + throw new NullPointerException(getClass().getSimpleName() + ": Cannot find siege clan."); + } + sc.addFlag(this); + _isAdvanced = advanced; + getStatus(); + setIsInvul(false); + } + + /** + * Use L2SiegeFlagInstance(L2PcInstance, int, L2NpcTemplate, boolean) instead + */ + @Deprecated + public L2SiegeFlagInstance(L2PcInstance player, int objectId, L2NpcTemplate template) + { + super(objectId, template); + _isAdvanced = false; + } + + @Override + public boolean isAttackable() + { + return !isInvul(); + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return !isInvul(); + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + if ((_siege != null) && (_clan != null)) + { + L2SiegeClan sc = _siege.getAttackerClan(_clan); + if (sc != null) + { + sc.removeFlag(this); + } + } + else if (_clan != null) + { + TerritoryWarManager.getInstance().removeClanFlag(_clan); + } + return true; + } + + @Override + public void onForcedAttack(L2PcInstance player) + { + onAction(player); + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if ((player == null) || !canTarget(player)) + { + return; + } + + // Check if the L2PcInstance already target the L2NpcInstance + if (this != player.getTarget()) + { + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player + MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()); + player.sendPacket(my); + + // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_HP, (int) getStatus().getCurrentHp()); + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); + player.sendPacket(su); + + // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + if (isAutoAttackable(player) && (Math.abs(player.getZ() - getZ()) < 100)) + { + player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); + } + else + { + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + } + } + + public boolean isAdvancedHeadquarter() + { + return _isAdvanced; + } + + @Override + public SiegeFlagStatus getStatus() + { + return (SiegeFlagStatus) super.getStatus(); + } + + @Override + public void initCharStatus() + { + setStatus(new SiegeFlagStatus(this)); + } + + @Override + public void reduceCurrentHp(double damage, L2Character attacker, L2Skill skill) + { + super.reduceCurrentHp(damage, attacker, skill); + if (canTalk()) + { + if ((getCastle() != null) && getCastle().getSiege().getIsInProgress()) + { + if (_clan != null) + { + // send warning to owners of headquarters that theirs base is under attack + _clan.broadcastToOnlineMembers(SystemMessage.getSystemMessage(SystemMessageId.BASE_UNDER_ATTACK)); + setCanTalk(false); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleTalkTask(), 20000); + } + } + else if ((getFort() != null) && getFort().getSiege().getIsInProgress()) + { + if (_clan != null) + { + // send warning to owners of headquarters that theirs base is under attack + _clan.broadcastToOnlineMembers(SystemMessage.getSystemMessage(SystemMessageId.BASE_UNDER_ATTACK)); + setCanTalk(false); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleTalkTask(), 20000); + } + } + } + } + + private class ScheduleTalkTask implements Runnable + { + + public ScheduleTalkTask() + { + } + + @Override + public void run() + { + setCanTalk(true); + } + } + + void setCanTalk(boolean val) + { + _canTalk = val; + } + + private boolean canTalk() + { + return _canTalk; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SiegeNpcInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SiegeNpcInstance.java new file mode 100644 index 00000000..81d8b3a7 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SiegeNpcInstance.java @@ -0,0 +1,70 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * This class ... + * @version $Revision$ $Date$ + */ +public class L2SiegeNpcInstance extends L2NpcInstance +{ + public L2SiegeNpcInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2SiegeNpcInstance); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + showSiegeInfoWindow(player); + } + + /** + * If siege is in progress shows the Busy HTML
    + * else Shows the SiegeInfo window + * @param player + */ + public void showSiegeInfoWindow(L2PcInstance player) + { + if (validateCondition(player)) + { + getCastle().getSiege().listRegisterClan(player); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), "data/html/siege/" + getNpcId() + "-busy.htm"); + html.replace("%castlename%", getCastle().getName()); + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + player.sendPacket(ActionFailed.STATIC_PACKET); + } + } + + private boolean validateCondition(L2PcInstance player) + { + if (getCastle().getSiege().getIsInProgress()) + { + return false; // Busy because of siege + } + + return true; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SiegeSummonInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SiegeSummonInstance.java new file mode 100644 index 00000000..fd9472f5 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SiegeSummonInstance.java @@ -0,0 +1,43 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2SiegeSummonInstance extends L2SummonInstance +{ + public static final int SIEGE_GOLEM_ID = 14737; + public static final int HOG_CANNON_ID = 14768; + public static final int SWOOP_CANNON_ID = 14839; + + public L2SiegeSummonInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, L2Skill skill) + { + super(objectId, template, owner, skill); + setInstanceType(InstanceType.L2SiegeSummonInstance); + } + + @Override + public void onSpawn() + { + super.onSpawn(); + if (!getOwner().isGM() && !isInsideZone(L2Character.ZONE_SIEGE)) + { + unSummon(getOwner()); + getOwner().sendMessage("Summon was unsummoned because it exited siege zone"); + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SignsPriestInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SignsPriestInstance.java new file mode 100644 index 00000000..a145e5cf --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SignsPriestInstance.java @@ -0,0 +1,944 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.StringTokenizer; +import java.util.logging.Level; + +import javolution.text.TextBuilder; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.SevenSigns; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * Dawn/Dusk Seven Signs Priest Instance + * @author Tempy + */ +public class L2SignsPriestInstance extends L2Npc +{ + public L2SignsPriestInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2SignsPriestInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if ((player.getLastFolkNPC() == null) || (player.getLastFolkNPC().getObjectId() != getObjectId())) + { + return; + } + + if (command.startsWith("SevenSignsDesc")) + { + int val = Integer.parseInt(command.substring(15)); + showChatWindow(player, val, null, true); + } + else if (command.startsWith("SevenSigns")) + { + SystemMessage sm; + + String path; + + int cabal = SevenSigns.CABAL_NULL; + int stoneType = 0; + + L2ItemInstance ancientAdena = player.getInventory().getItemByItemId(SevenSigns.ANCIENT_ADENA_ID); + long ancientAdenaAmount = ancientAdena == null ? 0 : ancientAdena.getCount(); + + int val = Integer.parseInt(command.substring(11, 12).trim()); + + if (command.length() > 12) + { + val = Integer.parseInt(command.substring(11, 13).trim()); + } + + if (command.length() > 13) + { + try + { + cabal = Integer.parseInt(command.substring(14, 15).trim()); + } + catch (Exception e) + { + try + { + cabal = Integer.parseInt(command.substring(13, 14).trim()); + } + catch (Exception e2) + { + try + { + StringTokenizer st = new StringTokenizer(command.trim()); + st.nextToken(); + cabal = Integer.parseInt(st.nextToken()); + } + catch (Exception e3) + { + _log.warning("Failed to retrieve cabal from bypass command. NpcId: " + getNpcId() + "; Command: " + command); + } + } + } + } + + switch (val) + { + case 2: // Purchase Record of the Seven Signs + if (!player.getInventory().validateCapacity(1)) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SLOTS_FULL)); + break; + } + + if (!player.reduceAdena("SevenSigns", SevenSigns.RECORD_SEVEN_SIGNS_COST, this, true)) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA)); + break; + } + player.getInventory().addItem("SevenSigns", SevenSigns.RECORD_SEVEN_SIGNS_ID, 1, player, this); + + sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1); + sm.addItemName(SevenSigns.RECORD_SEVEN_SIGNS_ID); + player.sendPacket(sm); + + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, val, "dawn", false); + } + else + { + showChatWindow(player, val, "dusk", false); + } + break; + case 33: // "I want to participate" request + int oldCabal = SevenSigns.getInstance().getPlayerCabal(player.getObjectId()); + + if (oldCabal != SevenSigns.CABAL_NULL) + { + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, val, "dawn_member", false); + } + else + { + showChatWindow(player, val, "dusk_member", false); + } + return; + } + else if (player.getClassId().level() == 0) + { + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, val, "dawn_firstclass", false); + } + else + { + showChatWindow(player, val, "dusk_firstclass", false); + } + return; + } + else if ((cabal == SevenSigns.CABAL_DUSK) && Config.ALT_GAME_CASTLE_DUSK) // dusk + { + // castle owners cannot participate with dusk side + if ((player.getClan() != null) && (player.getClan().getHasCastle() > 0)) + { + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_33_dusk_no.htm"); + break; + } + } + else if ((cabal == SevenSigns.CABAL_DAWN) && Config.ALT_GAME_CASTLE_DAWN) // dawn + { + // clans without castle need to pay participation fee + if ((player.getClan() == null) || (player.getClan().getHasCastle() == 0)) + { + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_33_dawn_fee.htm"); + break; + } + } + + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, val, "dawn", false); + } + else + { + showChatWindow(player, val, "dusk", false); + } + break; + case 34: // Pay the participation fee request + L2ItemInstance adena = player.getInventory().getItemByItemId(57); // adena + L2ItemInstance certif = player.getInventory().getItemByItemId(6388); // Lord of the Manor's Certificate of Approval + boolean fee = true; + + if ((player.getClassId().level() < 2) || ((adena != null) && (adena.getCount() >= SevenSigns.ADENA_JOIN_DAWN_COST)) || ((certif != null) && (certif.getCount() >= 1))) + { + fee = false; + } + if (fee) + { + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_33_dawn_no.htm"); + break; + } + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_33_dawn.htm"); + break; + case 3: // Join Cabal Intro 1 + case 8: // Festival of Darkness Intro - SevenSigns x [0]1 + showChatWindow(player, val, SevenSigns.getCabalShortName(cabal), false); + break; + case 4: // Join a Cabal - SevenSigns 4 [0]1 x + int newSeal = Integer.parseInt(command.substring(15)); + + if (player.getClassId().level() >= 2) + { + if ((cabal == SevenSigns.CABAL_DUSK) && Config.ALT_GAME_CASTLE_DUSK) + { + if ((player.getClan() != null) && (player.getClan().getHasCastle() > 0)) // even if in htmls is said that ally can have castle too, but its not + { + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_33_dusk_no.htm"); + return; + } + } + /* + * If the player is trying to join the Lords of Dawn, check if they are carrying a Lord's certificate. If not then try to take the required amount of adena instead. + */ + if (Config.ALT_GAME_CASTLE_DAWN && (cabal == SevenSigns.CABAL_DAWN)) + { + boolean allowJoinDawn = false; + + if ((player.getClan() != null) && (player.getClan().getHasCastle() > 0)) + { + allowJoinDawn = true; + } + else if (player.destroyItemByItemId("SevenSigns", SevenSigns.CERTIFICATE_OF_APPROVAL_ID, 1, this, true)) + { + allowJoinDawn = true; + } + else if (player.reduceAdena("SevenSigns", SevenSigns.ADENA_JOIN_DAWN_COST, this, true)) + { + allowJoinDawn = true; + } + + if (!allowJoinDawn) + { + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_33_dawn_fee.htm"); + return; + } + } + } + SevenSigns.getInstance().setPlayerInfo(player.getObjectId(), cabal, newSeal); + + if (cabal == SevenSigns.CABAL_DAWN) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SEVENSIGNS_PARTECIPATION_DAWN)); // Joined Dawn + } + else + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SEVENSIGNS_PARTECIPATION_DUSK)); // Joined Dusk + } + + // Show a confirmation message to the user, indicating which seal they chose. + switch (newSeal) + { + case SevenSigns.SEAL_AVARICE: + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FIGHT_FOR_AVARICE)); + break; + case SevenSigns.SEAL_GNOSIS: + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FIGHT_FOR_GNOSIS)); + break; + case SevenSigns.SEAL_STRIFE: + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FIGHT_FOR_STRIFE)); + break; + } + + showChatWindow(player, 4, SevenSigns.getCabalShortName(cabal), false); + break; + case 5: + if (this instanceof L2DawnPriestInstance) + { + if (SevenSigns.getInstance().getPlayerCabal(player.getObjectId()) == SevenSigns.CABAL_NULL) + { + showChatWindow(player, val, "dawn_no", false); + } + else + { + showChatWindow(player, val, "dawn", false); + } + } + else + { + if (SevenSigns.getInstance().getPlayerCabal(player.getObjectId()) == SevenSigns.CABAL_NULL) + { + showChatWindow(player, val, "dusk_no", false); + } + else + { + showChatWindow(player, val, "dusk", false); + } + } + break; + case 21: + int contribStoneId = Integer.parseInt(command.substring(14, 18)); + + L2ItemInstance contribBlueStones = player.getInventory().getItemByItemId(SevenSigns.SEAL_STONE_BLUE_ID); + L2ItemInstance contribGreenStones = player.getInventory().getItemByItemId(SevenSigns.SEAL_STONE_GREEN_ID); + L2ItemInstance contribRedStones = player.getInventory().getItemByItemId(SevenSigns.SEAL_STONE_RED_ID); + + long contribBlueStoneCount = contribBlueStones == null ? 0 : contribBlueStones.getCount(); + long contribGreenStoneCount = contribGreenStones == null ? 0 : contribGreenStones.getCount(); + long contribRedStoneCount = contribRedStones == null ? 0 : contribRedStones.getCount(); + + long score = SevenSigns.getInstance().getPlayerContribScore(player.getObjectId()); + long contributionCount = 0; + + boolean contribStonesFound = false; + + long redContrib = 0; + long greenContrib = 0; + long blueContrib = 0; + + try + { + contributionCount = Long.parseLong(command.substring(19).trim()); + } + catch (Exception NumberFormatException) + { + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 6, "dawn_failure", false); + } + else + { + showChatWindow(player, 6, "dusk_failure", false); + } + break; + } + + switch (contribStoneId) + { + case SevenSigns.SEAL_STONE_BLUE_ID: + blueContrib = (Config.ALT_MAXIMUM_PLAYER_CONTRIB - score) / SevenSigns.BLUE_CONTRIB_POINTS; + if (blueContrib > contribBlueStoneCount) + { + blueContrib = contributionCount; + } + break; + case SevenSigns.SEAL_STONE_GREEN_ID: + greenContrib = (Config.ALT_MAXIMUM_PLAYER_CONTRIB - score) / SevenSigns.GREEN_CONTRIB_POINTS; + if (greenContrib > contribGreenStoneCount) + { + greenContrib = contributionCount; + } + break; + case SevenSigns.SEAL_STONE_RED_ID: + redContrib = (Config.ALT_MAXIMUM_PLAYER_CONTRIB - score) / SevenSigns.RED_CONTRIB_POINTS; + if (redContrib > contribRedStoneCount) + { + redContrib = contributionCount; + } + break; + } + + if (redContrib > 0) + { + if (player.destroyItemByItemId("SevenSigns", SevenSigns.SEAL_STONE_RED_ID, redContrib, this, false)) + { + contribStonesFound = true; + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + msg.addItemName(SevenSigns.SEAL_STONE_RED_ID); + msg.addItemNumber(redContrib); + player.sendPacket(msg); + } + } + if (greenContrib > 0) + { + if (player.destroyItemByItemId("SevenSigns", SevenSigns.SEAL_STONE_GREEN_ID, greenContrib, this, false)) + { + contribStonesFound = true; + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + msg.addItemName(SevenSigns.SEAL_STONE_GREEN_ID); + msg.addItemNumber(greenContrib); + player.sendPacket(msg); + } + } + if (blueContrib > 0) + { + if (player.destroyItemByItemId("SevenSigns", SevenSigns.SEAL_STONE_BLUE_ID, blueContrib, this, false)) + { + contribStonesFound = true; + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + msg.addItemName(SevenSigns.SEAL_STONE_BLUE_ID); + msg.addItemNumber(blueContrib); + player.sendPacket(msg); + } + } + + if (!contribStonesFound) + { + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 6, "dawn_low_stones", false); + } + else + { + showChatWindow(player, 6, "dusk_low_stones", false); + } + } + else + { + score = SevenSigns.getInstance().addPlayerStoneContrib(player.getObjectId(), blueContrib, greenContrib, redContrib); + sm = SystemMessage.getSystemMessage(SystemMessageId.CONTRIB_SCORE_INCREASED_S1); + sm.addItemNumber(score); + player.sendPacket(sm); + + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 6, "dawn", false); + } + else + { + showChatWindow(player, 6, "dusk", false); + } + } + break; + case 6: // Contribute Seal Stones - SevenSigns 6 x + stoneType = Integer.parseInt(command.substring(13)); + + L2ItemInstance blueStones = player.getInventory().getItemByItemId(SevenSigns.SEAL_STONE_BLUE_ID); + L2ItemInstance greenStones = player.getInventory().getItemByItemId(SevenSigns.SEAL_STONE_GREEN_ID); + L2ItemInstance redStones = player.getInventory().getItemByItemId(SevenSigns.SEAL_STONE_RED_ID); + + long blueStoneCount = blueStones == null ? 0 : blueStones.getCount(); + long greenStoneCount = greenStones == null ? 0 : greenStones.getCount(); + long redStoneCount = redStones == null ? 0 : redStones.getCount(); + + long contribScore = SevenSigns.getInstance().getPlayerContribScore(player.getObjectId()); + boolean stonesFound = false; + + if (contribScore == Config.ALT_MAXIMUM_PLAYER_CONTRIB) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CONTRIB_SCORE_EXCEEDED)); + break; + } + long redContribCount = 0; + long greenContribCount = 0; + long blueContribCount = 0; + + String contribStoneColor = null; + String stoneColorContr = null; + + long stoneCountContr = 0; + int stoneIdContr = 0; + + switch (stoneType) + { + case 1: + contribStoneColor = "Blue"; + stoneColorContr = "blue"; + stoneIdContr = SevenSigns.SEAL_STONE_BLUE_ID; + stoneCountContr = blueStoneCount; + break; + case 2: + contribStoneColor = "Green"; + stoneColorContr = "green"; + stoneIdContr = SevenSigns.SEAL_STONE_GREEN_ID; + stoneCountContr = greenStoneCount; + break; + case 3: + contribStoneColor = "Red"; + stoneColorContr = "red"; + stoneIdContr = SevenSigns.SEAL_STONE_RED_ID; + stoneCountContr = redStoneCount; + break; + case 4: + long tempContribScore = contribScore; + redContribCount = (Config.ALT_MAXIMUM_PLAYER_CONTRIB - tempContribScore) / SevenSigns.RED_CONTRIB_POINTS; + if (redContribCount > redStoneCount) + { + redContribCount = redStoneCount; + } + + tempContribScore += redContribCount * SevenSigns.RED_CONTRIB_POINTS; + greenContribCount = (Config.ALT_MAXIMUM_PLAYER_CONTRIB - tempContribScore) / SevenSigns.GREEN_CONTRIB_POINTS; + if (greenContribCount > greenStoneCount) + { + greenContribCount = greenStoneCount; + } + + tempContribScore += greenContribCount * SevenSigns.GREEN_CONTRIB_POINTS; + blueContribCount = (Config.ALT_MAXIMUM_PLAYER_CONTRIB - tempContribScore) / SevenSigns.BLUE_CONTRIB_POINTS; + if (blueContribCount > blueStoneCount) + { + blueContribCount = blueStoneCount; + } + + if (redContribCount > 0) + { + if (player.destroyItemByItemId("SevenSigns", SevenSigns.SEAL_STONE_RED_ID, redContribCount, this, false)) + { + stonesFound = true; + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + msg.addItemName(SevenSigns.SEAL_STONE_RED_ID); + msg.addItemNumber(redContribCount); + player.sendPacket(msg); + } + } + if (greenContribCount > 0) + { + if (player.destroyItemByItemId("SevenSigns", SevenSigns.SEAL_STONE_GREEN_ID, greenContribCount, this, false)) + { + stonesFound = true; + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + msg.addItemName(SevenSigns.SEAL_STONE_GREEN_ID); + msg.addItemNumber(greenContribCount); + player.sendPacket(msg); + } + } + if (blueContribCount > 0) + { + if (player.destroyItemByItemId("SevenSigns", SevenSigns.SEAL_STONE_BLUE_ID, blueContribCount, this, false)) + { + stonesFound = true; + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED); + msg.addItemName(SevenSigns.SEAL_STONE_BLUE_ID); + msg.addItemNumber(blueContribCount); + player.sendPacket(msg); + } + } + + if (!stonesFound) + { + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, val, "dawn_no_stones", false); + } + else + { + showChatWindow(player, val, "dusk_no_stones", false); + } + } + else + { + contribScore = SevenSigns.getInstance().addPlayerStoneContrib(player.getObjectId(), blueContribCount, greenContribCount, redContribCount); + sm = SystemMessage.getSystemMessage(SystemMessageId.CONTRIB_SCORE_INCREASED_S1); + sm.addItemNumber(contribScore); + player.sendPacket(sm); + + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 6, "dawn", false); + } + else + { + showChatWindow(player, 6, "dusk", false); + } + } + return; + } + + if (this instanceof L2DawnPriestInstance) + { + path = SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_6_dawn_contribute.htm"; + } + else + { + path = SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_6_dusk_contribute.htm"; + } + + String contentContr = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), path); + + if (contentContr != null) + { + contentContr = contentContr.replaceAll("%contribStoneColor%", contribStoneColor); + contentContr = contentContr.replaceAll("%stoneColor%", stoneColorContr); + contentContr = contentContr.replaceAll("%stoneCount%", String.valueOf(stoneCountContr)); + contentContr = contentContr.replaceAll("%stoneItemId%", String.valueOf(stoneIdContr)); + contentContr = contentContr.replaceAll("%objectId%", String.valueOf(getObjectId())); + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setHtml(contentContr); + player.sendPacket(html); + } + else + { + _log.warning("Problem with HTML text " + path); + } + break; + case 7: // Exchange Ancient Adena for Adena - SevenSigns 7 xxxxxxx + long ancientAdenaConvert = 0; + + try + { + ancientAdenaConvert = Long.parseLong(command.substring(13).trim()); + } + catch (NumberFormatException e) + { + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "blkmrkt_3.htm"); + break; + } + catch (StringIndexOutOfBoundsException e) + { + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "blkmrkt_3.htm"); + break; + } + + if (ancientAdenaConvert < 1) + { + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "blkmrkt_3.htm"); + break; + } + if (ancientAdenaAmount < ancientAdenaConvert) + { + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "blkmrkt_4.htm"); + break; + } + + player.reduceAncientAdena("SevenSigns", ancientAdenaConvert, this, true); + player.addAdena("SevenSigns", ancientAdenaConvert, this, true); + + showChatWindow(player, SevenSigns.SEVEN_SIGNS_HTML_PATH + "blkmrkt_5.htm"); + break; + case 9: // Receive Contribution Rewards + int playerCabal = SevenSigns.getInstance().getPlayerCabal(player.getObjectId()); + int winningCabal = SevenSigns.getInstance().getCabalHighestScore(); + + if (SevenSigns.getInstance().isSealValidationPeriod() && (playerCabal == winningCabal)) + { + int ancientAdenaReward = SevenSigns.getInstance().getAncientAdenaReward(player.getObjectId(), true); + + if (ancientAdenaReward < 3) + { + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 9, "dawn_b", false); + } + else + { + showChatWindow(player, 9, "dusk_b", false); + } + break; + } + + player.addAncientAdena("SevenSigns", ancientAdenaReward, this, true); + + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 9, "dawn_a", false); + } + else + { + showChatWindow(player, 9, "dusk_a", false); + } + } + break; + case 11: // Teleport to Hunting Grounds + try + { + String portInfo = command.substring(14).trim(); + StringTokenizer st = new StringTokenizer(portInfo); + + int x = Integer.parseInt(st.nextToken()); + int y = Integer.parseInt(st.nextToken()); + int z = Integer.parseInt(st.nextToken()); + + long ancientAdenaCost = Long.parseLong(st.nextToken()); + + if (ancientAdenaCost > 0) + { + if (!player.reduceAncientAdena("SevenSigns", ancientAdenaCost, this, true)) + { + break; + } + } + + player.teleToLocation(x, y, z); + } + catch (Exception e) + { + _log.log(Level.WARNING, "SevenSigns: Error occurred while teleporting player: " + e.getMessage(), e); + } + break; + case 16: + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, val, "dawn", false); + } + else + { + showChatWindow(player, val, "dusk", false); + } + break; + case 17: // Exchange Seal Stones for Ancient Adena (Type Choice) - SevenSigns 17 x + stoneType = Integer.parseInt(command.substring(14)); + + int stoneId = 0; + long stoneCount = 0; + int stoneValue = 0; + + String stoneColor = null; + + switch (stoneType) + { + case 1: + stoneColor = "blue"; + stoneId = SevenSigns.SEAL_STONE_BLUE_ID; + stoneValue = SevenSigns.SEAL_STONE_BLUE_VALUE; + break; + case 2: + stoneColor = "green"; + stoneId = SevenSigns.SEAL_STONE_GREEN_ID; + stoneValue = SevenSigns.SEAL_STONE_GREEN_VALUE; + break; + case 3: + stoneColor = "red"; + stoneId = SevenSigns.SEAL_STONE_RED_ID; + stoneValue = SevenSigns.SEAL_STONE_RED_VALUE; + break; + case 4: + L2ItemInstance blueStonesAll = player.getInventory().getItemByItemId(SevenSigns.SEAL_STONE_BLUE_ID); + L2ItemInstance greenStonesAll = player.getInventory().getItemByItemId(SevenSigns.SEAL_STONE_GREEN_ID); + L2ItemInstance redStonesAll = player.getInventory().getItemByItemId(SevenSigns.SEAL_STONE_RED_ID); + + long blueStoneCountAll = blueStonesAll == null ? 0 : blueStonesAll.getCount(); + long greenStoneCountAll = greenStonesAll == null ? 0 : greenStonesAll.getCount(); + long redStoneCountAll = redStonesAll == null ? 0 : redStonesAll.getCount(); + long ancientAdenaRewardAll = 0; + + ancientAdenaRewardAll = SevenSigns.calcAncientAdenaReward(blueStoneCountAll, greenStoneCountAll, redStoneCountAll); + + if (ancientAdenaRewardAll == 0) + { + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 18, "dawn_no_stones", false); + } + else + { + showChatWindow(player, 18, "dusk_no_stones", false); + } + return; + } + + if (blueStoneCountAll > 0) + { + player.destroyItemByItemId("SevenSigns", SevenSigns.SEAL_STONE_BLUE_ID, blueStoneCountAll, this, true); + } + if (greenStoneCountAll > 0) + { + player.destroyItemByItemId("SevenSigns", SevenSigns.SEAL_STONE_GREEN_ID, greenStoneCountAll, this, true); + } + if (redStoneCountAll > 0) + { + player.destroyItemByItemId("SevenSigns", SevenSigns.SEAL_STONE_RED_ID, redStoneCountAll, this, true); + } + + player.addAncientAdena("SevenSigns", ancientAdenaRewardAll, this, true); + + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 18, "dawn", false); + } + else + { + showChatWindow(player, 18, "dusk", false); + } + return; + } + + L2ItemInstance stoneInstance = player.getInventory().getItemByItemId(stoneId); + + if (stoneInstance != null) + { + stoneCount = stoneInstance.getCount(); + } + + if (this instanceof L2DawnPriestInstance) + { + path = SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_17_dawn.htm"; + } + else + { + path = SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_17_dusk.htm"; + } + + String content = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), path); + + if (content != null) + { + content = content.replaceAll("%stoneColor%", stoneColor); + content = content.replaceAll("%stoneValue%", String.valueOf(stoneValue)); + content = content.replaceAll("%stoneCount%", String.valueOf(stoneCount)); + content = content.replaceAll("%stoneItemId%", String.valueOf(stoneId)); + content = content.replaceAll("%objectId%", String.valueOf(getObjectId())); + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setHtml(content); + player.sendPacket(html); + } + else + { + _log.warning("Problem with HTML text " + SevenSigns.SEVEN_SIGNS_HTML_PATH + "signs_17.htm: " + path); + } + break; + case 18: // Exchange Seal Stones for Ancient Adena - SevenSigns 18 xxxx xxxxxx + int convertStoneId = Integer.parseInt(command.substring(14, 18)); + long convertCount = 0; + + try + { + convertCount = Long.parseLong(command.substring(19).trim()); + } + catch (Exception NumberFormatException) + { + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 18, "dawn_failed", false); + } + else + { + showChatWindow(player, 18, "dusk_failed", false); + } + break; + } + + L2ItemInstance convertItem = player.getInventory().getItemByItemId(convertStoneId); + + if (convertItem != null) + { + long ancientAdenaReward = 0; + long totalCount = convertItem.getCount(); + + if ((convertCount <= totalCount) && (convertCount > 0)) + { + switch (convertStoneId) + { + case SevenSigns.SEAL_STONE_BLUE_ID: + ancientAdenaReward = SevenSigns.calcAncientAdenaReward(convertCount, 0, 0); + break; + case SevenSigns.SEAL_STONE_GREEN_ID: + ancientAdenaReward = SevenSigns.calcAncientAdenaReward(0, convertCount, 0); + break; + case SevenSigns.SEAL_STONE_RED_ID: + ancientAdenaReward = SevenSigns.calcAncientAdenaReward(0, 0, convertCount); + break; + } + + if (player.destroyItemByItemId("SevenSigns", convertStoneId, convertCount, this, true)) + { + player.addAncientAdena("SevenSigns", ancientAdenaReward, this, true); + + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 18, "dawn", false); + } + else + { + showChatWindow(player, 18, "dusk", false); + } + } + } + else + { + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 18, "dawn_low_stones", false); + } + else + { + showChatWindow(player, 18, "dusk_low_stones", false); + } + break; + } + } + else + { + if (this instanceof L2DawnPriestInstance) + { + showChatWindow(player, 18, "dawn_no_stones", false); + } + else + { + showChatWindow(player, 18, "dusk_no_stones", false); + } + break; + } + break; + case 19: // Seal Information (for when joining a cabal) + int chosenSeal = Integer.parseInt(command.substring(16)); + + String fileSuffix = SevenSigns.getSealName(chosenSeal, true) + "_" + SevenSigns.getCabalShortName(cabal); + + showChatWindow(player, val, fileSuffix, false); + break; + case 20: // Seal Status (for when joining a cabal) + TextBuilder contentBuffer = new TextBuilder(); + + if (this instanceof L2DawnPriestInstance) + { + contentBuffer.append("Priest of Dawn:
    [ Seal Status ]
    "); + } + else + { + contentBuffer.append("Dusk Priestess:
    [ Status of the Seals ]
    "); + } + + for (int i = 1; i < 4; i++) + { + int sealOwner = SevenSigns.getInstance().getSealOwner(i); + + if (sealOwner != SevenSigns.CABAL_NULL) + { + contentBuffer.append("[" + SevenSigns.getSealName(i, false) + ": " + SevenSigns.getCabalName(sealOwner) + "]
    "); + } + else + { + contentBuffer.append("[" + SevenSigns.getSealName(i, false) + ": Nothingness]
    "); + } + } + + contentBuffer.append("
    Go back."); + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setHtml(contentBuffer.toString()); + player.sendPacket(html); + break; + default: + showChatWindow(player, val, null, false); + break; + } + } + else + { + super.onBypassFeedback(player, command); + } + } + + private void showChatWindow(L2PcInstance player, int val, String suffix, boolean isDescription) + { + String filename = SevenSigns.SEVEN_SIGNS_HTML_PATH; + + filename += (isDescription) ? "desc_" + val : "signs_" + val; + filename += (suffix != null) ? "_" + suffix + ".htm" : ".htm"; + + showChatWindow(player, filename); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2StaticObjectInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2StaticObjectInstance.java new file mode 100644 index 00000000..6bc5d7d5 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2StaticObjectInstance.java @@ -0,0 +1,250 @@ +/* + * $Header: /cvsroot/l2j/L2_Gameserver/java/net/sf/l2j/gameserver/model/L2StaticObjectInstance.java,v 1.3.2.2.2.2 2005/02/04 13:05:27 maximas Exp $ + * + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.logging.Logger; + +import com.l2jserver.gameserver.ai.L2CharacterAI; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.knownlist.StaticObjectKnownList; +import com.l2jserver.gameserver.model.actor.stat.StaticObjStat; +import com.l2jserver.gameserver.model.actor.status.StaticObjStatus; +import com.l2jserver.gameserver.network.serverpackets.ShowTownMap; +import com.l2jserver.gameserver.network.serverpackets.StaticObject; +import com.l2jserver.gameserver.templates.chars.L2CharTemplate; +import com.l2jserver.gameserver.templates.item.L2Weapon; + +/** + * GODSON ROX! + */ +public class L2StaticObjectInstance extends L2Character +{ + protected static final Logger log = Logger.getLogger(L2StaticObjectInstance.class.getName()); + + /** The interaction distance of the L2StaticObjectInstance */ + public static final int INTERACTION_DISTANCE = 150; + + private final int _staticObjectId; + private int _meshIndex = 0; // 0 - static objects, alternate static objects + private int _type = -1; // 0 - map signs, 1 - throne , 2 - arena signs + private ShowTownMap _map; + + /** This class may be created only by L2Character and only for AI */ + public class AIAccessor extends L2Character.AIAccessor + { + protected AIAccessor() + { + } + + @Override + public L2StaticObjectInstance getActor() + { + return L2StaticObjectInstance.this; + } + + @Override + public void moveTo(int x, int y, int z, int offset) + { + } + + @Override + public void moveTo(int x, int y, int z) + { + } + + @Override + public void stopMove(L2CharPosition pos) + { + } + + @Override + public void doAttack(L2Character target) + { + } + + @Override + public void doCast(L2Skill skill) + { + } + } + + @Override + public L2CharacterAI getAI() + { + return null; + } + + /** + * @return Returns the StaticObjectId. + */ + public int getStaticObjectId() + { + return _staticObjectId; + } + + /** + */ + public L2StaticObjectInstance(int objectId, L2CharTemplate template, int staticId) + { + super(objectId, template); + setInstanceType(InstanceType.L2StaticObjectInstance); + _staticObjectId = staticId; + } + + @Override + public final StaticObjectKnownList getKnownList() + { + return (StaticObjectKnownList) super.getKnownList(); + } + + @Override + public void initKnownList() + { + setKnownList(new StaticObjectKnownList(this)); + } + + @Override + public final StaticObjStat getStat() + { + return (StaticObjStat) super.getStat(); + } + + @Override + public void initCharStat() + { + setStat(new StaticObjStat(this)); + } + + @Override + public final StaticObjStatus getStatus() + { + return (StaticObjStatus) super.getStatus(); + } + + @Override + public void initCharStatus() + { + setStatus(new StaticObjStatus(this)); + } + + public int getType() + { + return _type; + } + + public void setType(int type) + { + _type = type; + } + + public void setMap(String texture, int x, int y) + { + _map = new ShowTownMap("town_map." + texture, x, y); + } + + public ShowTownMap getMap() + { + return _map; + } + + @Override + public final int getLevel() + { + return 1; + } + + /** + * Return null.
    + *
    + */ + @Override + public L2ItemInstance getActiveWeaponInstance() + { + return null; + } + + @Override + public L2Weapon getActiveWeaponItem() + { + return null; + } + + @Override + public L2ItemInstance getSecondaryWeaponInstance() + { + return null; + } + + @Override + public L2Weapon getSecondaryWeaponItem() + { + return null; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.L2Object#isAttackable() + */ + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return false; + } + + /** + * Set the meshIndex of the object
    + *
    + * Values :
    + *
    + *
  • default textures : 0
  • alternate textures : 1

  • + *
    + * @param meshIndex + */ + public void setMeshIndex(int meshIndex) + { + _meshIndex = meshIndex; + this.broadcastPacket(new StaticObject(this)); + } + + /** + * Return the meshIndex of the object.
    + *
    + * Values :
    + *
    + *
  • default textures : 0
  • alternate textures : 1

  • + *
    + */ + public int getMeshIndex() + { + return _meshIndex; + } + + @Override + public void updateAbnormalEffect() + { + } + + @Override + public void sendInfo(L2PcInstance activeChar) + { + activeChar.sendPacket(new StaticObject(this)); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SummonInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SummonInstance.java new file mode 100644 index 00000000..c0168e79 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SummonInstance.java @@ -0,0 +1,378 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.network.serverpackets.SetSummonRemainTime; +import com.l2jserver.gameserver.skills.l2skills.L2SkillSummon; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2SummonInstance extends L2Summon +{ + protected static final Logger log = Logger.getLogger(L2SummonInstance.class.getName()); + + private float _expPenalty = 0; // exp decrease multiplier (i.e. 0.3 (= 30%) for shadow) + private int _itemConsumeId; + private int _itemConsumeCount; + private int _itemConsumeSteps; + private final int _totalLifeTime; + private final int _timeLostIdle; + private final int _timeLostActive; + private int _timeRemaining; + private int _nextItemConsumeTime; + public int lastShowntimeRemaining; // Following FbiAgent's example to avoid sending useless packets + + private Future _summonLifeTask; + + public L2SummonInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, L2Skill skill) + { + super(objectId, template, owner); + setInstanceType(InstanceType.L2SummonInstance); + setShowSummonAnimation(true); + + if (skill != null) + { + final L2SkillSummon summonSkill = (L2SkillSummon) skill; + _itemConsumeId = summonSkill.getItemConsumeIdOT(); + _itemConsumeCount = summonSkill.getItemConsumeOT(); + _itemConsumeSteps = summonSkill.getItemConsumeSteps(); + _totalLifeTime = summonSkill.getTotalLifeTime(); + _timeLostIdle = summonSkill.getTimeLostIdle(); + _timeLostActive = summonSkill.getTimeLostActive(); + } + else + { + // defaults + _itemConsumeId = 0; + _itemConsumeCount = 0; + _itemConsumeSteps = 0; + _totalLifeTime = 1200000; // 20 minutes + _timeLostIdle = 1000; + _timeLostActive = 1000; + } + _timeRemaining = _totalLifeTime; + lastShowntimeRemaining = _totalLifeTime; + + if (_itemConsumeId == 0) + { + _nextItemConsumeTime = -1; // do not consume + } + else if (_itemConsumeSteps == 0) + { + _nextItemConsumeTime = -1; // do not consume + } + else + { + _nextItemConsumeTime = _totalLifeTime - (_totalLifeTime / (_itemConsumeSteps + 1)); + } + + // When no item consume is defined task only need to check when summon life time has ended. + // Otherwise have to destroy items from owner's inventory in order to let summon live. + int delay = 1000; + + if (Config.DEBUG && (_itemConsumeCount != 0)) + { + _log.warning("L2SummonInstance: Item Consume ID: " + _itemConsumeId + ", Count: " + _itemConsumeCount + ", Rate: " + _itemConsumeSteps + " times."); + } + if (Config.DEBUG) + { + _log.warning("L2SummonInstance: Task Delay " + (delay / 1000) + " seconds."); + } + + _summonLifeTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new SummonLifetime(getOwner(), this), delay, delay); + } + + @Override + public final int getLevel() + { + return (getTemplate() != null ? getTemplate().level : 0); + } + + @Override + public int getSummonType() + { + return 1; + } + + public void setExpPenalty(float expPenalty) + { + _expPenalty = expPenalty; + } + + public float getExpPenalty() + { + return _expPenalty; + } + + public int getItemConsumeCount() + { + return _itemConsumeCount; + } + + public int getItemConsumeId() + { + return _itemConsumeId; + } + + public int getItemConsumeSteps() + { + return _itemConsumeSteps; + } + + public int getNextItemConsumeTime() + { + return _nextItemConsumeTime; + } + + public int getTotalLifeTime() + { + return _totalLifeTime; + } + + public int getTimeLostIdle() + { + return _timeLostIdle; + } + + public int getTimeLostActive() + { + return _timeLostActive; + } + + public int getTimeRemaining() + { + return _timeRemaining; + } + + public void setNextItemConsumeTime(int value) + { + _nextItemConsumeTime = value; + } + + public void decNextItemConsumeTime(int value) + { + _nextItemConsumeTime -= value; + } + + public void decTimeRemaining(int value) + { + _timeRemaining -= value; + } + + public void addExpAndSp(int addToExp, int addToSp) + { + getOwner().addExpAndSp(addToExp, addToSp); + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + + if (Config.DEBUG) + { + _log.warning("L2SummonInstance: " + getTemplate().name + " (" + getOwner().getName() + ") has been killed."); + } + + if (_summonLifeTask != null) + { + _summonLifeTask.cancel(false); + _summonLifeTask = null; + } + return true; + + } + + /** + * Servitors' skills automatically change their level based on the servitor's level. Until level 70, the servitor gets 1 lv of skill per 10 levels. After that, it is 1 skill level per 5 servitor levels. If the resulting skill level doesn't exist use the max that does exist! + * @see com.l2jserver.gameserver.model.actor.L2Character#doCast(com.l2jserver.gameserver.model.L2Skill) + */ + @Override + public void doCast(L2Skill skill) + { + final int petLevel = getLevel(); + int skillLevel = petLevel / 10; + if (petLevel >= 70) + { + skillLevel += (petLevel - 65) / 10; + } + + // adjust the level for servitors less than lv 10 + if (skillLevel < 1) + { + skillLevel = 1; + } + + L2Skill skillToCast = SkillTable.getInstance().getInfo(skill.getId(), skillLevel); + + if (skillToCast != null) + { + super.doCast(skillToCast); + } + else + { + super.doCast(skill); + } + } + + static class SummonLifetime implements Runnable + { + private final L2PcInstance _activeChar; + private final L2SummonInstance _summon; + + SummonLifetime(L2PcInstance activeChar, L2SummonInstance newpet) + { + _activeChar = activeChar; + _summon = newpet; + } + + @Override + public void run() + { + if (Config.DEBUG) + { + log.warning("L2SummonInstance: " + _summon.getTemplate().name + " (" + _activeChar.getName() + ") run task."); + } + + try + { + double oldTimeRemaining = _summon.getTimeRemaining(); + int maxTime = _summon.getTotalLifeTime(); + double newTimeRemaining; + + // if pet is attacking + if (_summon.isAttackingNow()) + { + _summon.decTimeRemaining(_summon.getTimeLostActive()); + } + else + { + _summon.decTimeRemaining(_summon.getTimeLostIdle()); + } + newTimeRemaining = _summon.getTimeRemaining(); + // check if the summon's lifetime has ran out + if (newTimeRemaining < 0) + { + _summon.unSummon(_activeChar); + } + // check if it is time to consume another item + else if ((newTimeRemaining <= _summon.getNextItemConsumeTime()) && (oldTimeRemaining > _summon.getNextItemConsumeTime())) + { + _summon.decNextItemConsumeTime(maxTime / (_summon.getItemConsumeSteps() + 1)); + + // check if owner has enought itemConsume, if requested + if ((_summon.getItemConsumeCount() > 0) && (_summon.getItemConsumeId() != 0) && !_summon.isDead() && !_summon.destroyItemByItemId("Consume", _summon.getItemConsumeId(), _summon.getItemConsumeCount(), _activeChar, true)) + { + _summon.unSummon(_activeChar); + } + } + + // prevent useless packet-sending when the difference isn't visible. + if ((_summon.lastShowntimeRemaining - newTimeRemaining) > (maxTime / 352)) + { + _summon.getOwner().sendPacket(new SetSummonRemainTime(maxTime, (int) newTimeRemaining)); + _summon.lastShowntimeRemaining = (int) newTimeRemaining; + _summon.updateEffectIcons(); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "Error on player [" + _activeChar.getName() + "] summon item consume task.", e); + } + } + } + + @Override + public void unSummon(L2PcInstance owner) + { + if (Config.DEBUG) + { + _log.warning("L2SummonInstance: " + getTemplate().name + " (" + owner.getName() + ") unsummoned."); + } + + if (_summonLifeTask != null) + { + _summonLifeTask.cancel(false); + _summonLifeTask = null; + } + + super.unSummon(owner); + } + + @Override + public boolean destroyItem(String process, int objectId, long count, L2Object reference, boolean sendMessage) + { + return getOwner().destroyItem(process, objectId, count, reference, sendMessage); + } + + @Override + public boolean destroyItemByItemId(String process, int itemId, long count, L2Object reference, boolean sendMessage) + { + if (Config.DEBUG) + { + _log.warning("L2SummonInstance: " + getTemplate().name + " (" + getOwner().getName() + ") consume."); + } + + return getOwner().destroyItemByItemId(process, itemId, count, reference, sendMessage); + } + + @Override + public byte getAttackElement() + { + if ((getOwner() == null) || !getOwner().getClassId().isSummoner()) + { + return super.getAttackElement(); + } + + return getOwner().getAttackElement(); + } + + @Override + public int getAttackElementValue(byte attribute) + { + if ((getOwner() == null) || !getOwner().getClassId().isSummoner() || (getOwner().getExpertiseWeaponPenalty() > 0)) + { + return super.getAttackElementValue(attribute); + } + + // 80% of the owner (onwer already has only 20%) + return 4 * getOwner().getAttackElementValue(attribute); + } + + @Override + public int getDefenseElementValue(byte attribute) + { + if ((getOwner() == null) || !getOwner().getClassId().isSummoner()) + { + return super.getDefenseElementValue(attribute); + } + + // bonus from owner + return super.getDefenseElementValue(attribute) + getOwner().getDefenseElementValue(attribute); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SymbolMakerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SymbolMakerInstance.java new file mode 100644 index 00000000..af97a5f5 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2SymbolMakerInstance.java @@ -0,0 +1,44 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2SymbolMakerInstance extends L2Npc +{ + public L2SymbolMakerInstance(int objectID, L2NpcTemplate template) + { + super(objectID, template); + setInstanceType(InstanceType.L2SymbolMakerInstance); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + return "data/html/symbolmaker/SymbolMaker.htm"; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.L2Object#isAttackable() + */ + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return false; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TamedBeastInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TamedBeastInstance.java new file mode 100644 index 00000000..118a4e41 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TamedBeastInstance.java @@ -0,0 +1,605 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE; + +import java.util.List; +import java.util.concurrent.Future; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.network.serverpackets.AbstractNpcInfo; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.SocialAction; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.StopMove; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.templates.skills.L2SkillType; +import com.l2jserver.util.Point3D; +import com.l2jserver.util.Rnd; + +// While a tamed beast behaves a lot like a pet (ingame) and does have +// an owner, in all other aspects, it acts like a mob. +// In addition, it can be fed in order to increase its duration. +// This class handles the running tasks, AI, and feed of the mob. +// The (mostly optional) AI on feeding the spawn is handled by the datapack ai script +public final class L2TamedBeastInstance extends L2FeedableBeastInstance +{ + private int _foodSkillId; + private static final int MAX_DISTANCE_FROM_HOME = 30000; + private static final int MAX_DISTANCE_FROM_OWNER = 2000; + private static final int MAX_DURATION = 1200000; // 20 minutes + private static final int DURATION_CHECK_INTERVAL = 60000; // 1 minute + private static final int DURATION_INCREASE_INTERVAL = 20000; // 20 secs (gained upon feeding) + private static final int BUFF_INTERVAL = 5000; // 5 seconds + private int _remainingTime = MAX_DURATION; + private int _homeX, _homeY, _homeZ; + protected L2PcInstance _owner; + private Future _buffTask = null; + private Future _durationCheckTask = null; + protected static boolean _isFreyaBeast; + private List _beastSkills = null; + + public L2TamedBeastInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2TamedBeastInstance); + setHome(this); + } + + public L2TamedBeastInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, int foodSkillId, int x, int y, int z) + { + super(objectId, template); + _isFreyaBeast = false; + setInstanceType(InstanceType.L2TamedBeastInstance); + setCurrentHp(getMaxHp()); + setCurrentMp(getMaxMp()); + setOwner(owner); + setFoodType(foodSkillId); + setHome(x, y, z); + this.spawnMe(x, y, z); + } + + public L2TamedBeastInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, int food, int x, int y, int z, boolean isFreyaBeast) + { + super(objectId, template); + _isFreyaBeast = isFreyaBeast; + setInstanceType(InstanceType.L2TamedBeastInstance); + setCurrentHp(getMaxHp()); + setCurrentMp(getMaxMp()); + setFoodType(food); + setHome(x, y, z); + spawnMe(x, y, z); + setOwner(owner); + if (isFreyaBeast) + { + getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, _owner); + } + + } + + public void onReceiveFood() + { + // Eating food extends the duration by 20secs, to a max of 20minutes + _remainingTime = _remainingTime + DURATION_INCREASE_INTERVAL; + if (_remainingTime > MAX_DURATION) + { + _remainingTime = MAX_DURATION; + } + } + + public Point3D getHome() + { + return new Point3D(_homeX, _homeY, _homeZ); + } + + public void setHome(int x, int y, int z) + { + _homeX = x; + _homeY = y; + _homeZ = z; + } + + public void setHome(L2Character c) + { + setHome(c.getX(), c.getY(), c.getZ()); + } + + public int getRemainingTime() + { + return _remainingTime; + } + + public void setRemainingTime(int duration) + { + _remainingTime = duration; + } + + public int getFoodType() + { + return _foodSkillId; + } + + public void setFoodType(int foodItemId) + { + if (foodItemId > 0) + { + _foodSkillId = foodItemId; + + // start the duration checks + // start the buff tasks + if (_durationCheckTask != null) + { + _durationCheckTask.cancel(true); + } + _durationCheckTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new CheckDuration(this), DURATION_CHECK_INTERVAL, DURATION_CHECK_INTERVAL); + } + } + + @Override + public boolean doDie(L2Character killer) + { + if (!super.doDie(killer)) + { + return false; + } + + getAI().stopFollow(); + if (_buffTask != null) + { + _buffTask.cancel(true); + } + if (_durationCheckTask != null) + { + _durationCheckTask.cancel(true); + } + + // clean up variables + if ((_owner != null) && (_owner.getTrainedBeasts() != null)) + { + _owner.getTrainedBeasts().remove(this); + } + _buffTask = null; + _durationCheckTask = null; + _owner = null; + _foodSkillId = 0; + _remainingTime = 0; + return true; + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return !_isFreyaBeast; + } + + public boolean isFreyaBeast() + { + return _isFreyaBeast; + } + + public void addBeastSkill(L2Skill skill) + { + if (_beastSkills == null) + { + _beastSkills = new FastList<>(); + } + _beastSkills.add(skill); + } + + public void castBeastSkills() + { + if ((_owner == null) || (_beastSkills == null)) + { + return; + } + int delay = 100; + for (L2Skill skill : _beastSkills) + { + ThreadPoolManager.getInstance().scheduleGeneral(new buffCast(skill), delay); + delay += (100 + skill.getHitTime()); + } + ThreadPoolManager.getInstance().scheduleGeneral(new buffCast(null), delay); + } + + private class buffCast implements Runnable + { + private final L2Skill _skill; + + public buffCast(L2Skill skill) + { + _skill = skill; + } + + @Override + public void run() + { + if (_skill == null) + { + getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, _owner); + } + else + { + sitCastAndFollow(_skill, _owner); + } + } + } + + public L2PcInstance getOwner() + { + return _owner; + } + + public void setOwner(L2PcInstance owner) + { + if (owner != null) + { + _owner = owner; + setTitle(owner.getName()); + // broadcast the new title + setShowSummonAnimation(true); + broadcastPacket(new AbstractNpcInfo.NpcInfo(this, owner)); + + owner.addTrainedBeast(this); + + // always and automatically follow the owner. + getAI().startFollow(_owner, 100); + + if (!_isFreyaBeast) + { + // instead of calculating this value each time, let's get this now and pass it on + int totalBuffsAvailable = 0; + for (L2Skill skill : getTemplate().getSkills().values()) + { + // if the skill is a buff, check if the owner has it already [ owner.getEffect(L2Skill skill) ] + if (skill.getSkillType() == L2SkillType.BUFF) + { + totalBuffsAvailable++; + } + } + + // start the buff tasks + if (_buffTask != null) + { + _buffTask.cancel(true); + } + _buffTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new CheckOwnerBuffs(this, totalBuffsAvailable), BUFF_INTERVAL, BUFF_INTERVAL); + } + } + else + { + deleteMe(); // despawn if no owner + } + } + + public boolean isTooFarFromHome() + { + return !(this.isInsideRadius(_homeX, _homeY, _homeZ, MAX_DISTANCE_FROM_HOME, true, true)); + } + + @Override + public void deleteMe() + { + if (_buffTask != null) + { + _buffTask.cancel(true); + } + _durationCheckTask.cancel(true); + stopHpMpRegeneration(); + + // clean up variables + if ((_owner != null) && (_owner.getTrainedBeasts() != null)) + { + _owner.getTrainedBeasts().remove(this); + } + setTarget(null); + _buffTask = null; + _durationCheckTask = null; + _owner = null; + _foodSkillId = 0; + _remainingTime = 0; + + // remove the spawn + super.deleteMe(); + } + + // notification triggered by the owner when the owner is attacked. + // tamed mobs will heal/recharge or debuff the enemy according to their skills + public void onOwnerGotAttacked(L2Character attacker) + { + // check if the owner is no longer around...if so, despawn + if ((_owner == null) || !_owner.isOnline()) + { + deleteMe(); + return; + } + // if the owner is too far away, stop anything else and immediately run towards the owner. + if (!_owner.isInsideRadius(this, MAX_DISTANCE_FROM_OWNER, true, true)) + { + getAI().startFollow(_owner); + return; + } + // if the owner is dead, do nothing... + if (_owner.isDead() || _isFreyaBeast) + { + return; + } + + // if the tamed beast is currently in the middle of casting, let it complete its skill... + if (isCastingNow()) + { + return; + } + + float HPRatio = ((float) _owner.getCurrentHp()) / _owner.getMaxHp(); + + // if the owner has a lot of HP, then debuff the enemy with a random debuff among the available skills + // use of more than one debuff at this moment is acceptable + if (HPRatio >= 0.8) + { + FastMap skills = (FastMap) getTemplate().getSkills(); + + for (L2Skill skill : skills.values()) + { + // if the skill is a debuff, check if the attacker has it already [ attacker.getEffect(L2Skill skill) ] + if ((skill.getSkillType() == L2SkillType.DEBUFF) && (Rnd.get(3) < 1) && ((attacker != null) && (attacker.getFirstEffect(skill) != null))) + { + sitCastAndFollow(skill, attacker); + } + } + } + // for HP levels between 80% and 50%, do not react to attack events (so that MP can regenerate a bit) + // for lower HP ranges, heal or recharge the owner with 1 skill use per attack. + else if (HPRatio < 0.5) + { + int chance = 1; + if (HPRatio < 0.25) + { + chance = 2; + } + + // if the owner has a lot of HP, then debuff the enemy with a random debuff among the available skills + FastMap skills = (FastMap) getTemplate().getSkills(); + + for (L2Skill skill : skills.values()) + { + // if the skill is a buff, check if the owner has it already [ owner.getEffect(L2Skill skill) ] + if ((Rnd.get(5) < chance) && ((skill.getSkillType() == L2SkillType.HEAL) || (skill.getSkillType() == L2SkillType.HOT) || (skill.getSkillType() == L2SkillType.BALANCE_LIFE) || (skill.getSkillType() == L2SkillType.HEAL_PERCENT) || (skill.getSkillType() == L2SkillType.HEAL_STATIC) || (skill.getSkillType() == L2SkillType.COMBATPOINTHEAL) || (skill.getSkillType() == L2SkillType.CPHOT) || (skill.getSkillType() == L2SkillType.MANAHEAL) || (skill.getSkillType() == L2SkillType.MANA_BY_LEVEL) || (skill.getSkillType() == L2SkillType.MANAHEAL_PERCENT) || (skill.getSkillType() == L2SkillType.MANARECHARGE) || (skill.getSkillType() == L2SkillType.MPHOT))) + { + sitCastAndFollow(skill, _owner); + return; + } + } + } + } + + /** + * Prepare and cast a skill: First smoothly prepare the beast for casting, by abandoning other actions Next, call super.doCast(skill) in order to actually cast the spell Finally, return to auto-following the owner. + * @see com.l2jserver.gameserver.model.actor.L2Character#doCast(com.l2jserver.gameserver.model.L2Skill) + */ + protected void sitCastAndFollow(L2Skill skill, L2Character target) + { + stopMove(null); + broadcastPacket(new StopMove(this)); + getAI().setIntention(AI_INTENTION_IDLE); + + setTarget(target); + doCast(skill); + getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, _owner); + } + + private static class CheckDuration implements Runnable + { + private final L2TamedBeastInstance _tamedBeast; + + CheckDuration(L2TamedBeastInstance tamedBeast) + { + _tamedBeast = tamedBeast; + } + + @Override + public void run() + { + int foodTypeSkillId = _tamedBeast.getFoodType(); + L2PcInstance owner = _tamedBeast.getOwner(); + + L2ItemInstance item = null; + if (_isFreyaBeast) + { + item = owner.getInventory().getItemByItemId(foodTypeSkillId); + if ((item != null) && (item.getCount() >= 1)) + { + owner.destroyItem("BeastMob", item, 1, _tamedBeast, true); + _tamedBeast.broadcastPacket(new SocialAction(_tamedBeast, 3)); + } + else + { + _tamedBeast.deleteMe(); + } + } + else + { + _tamedBeast.setRemainingTime(_tamedBeast.getRemainingTime() - DURATION_CHECK_INTERVAL); + // I tried to avoid this as much as possible...but it seems I can't avoid hardcoding + // ids further, except by carrying an additional variable just for these two lines... + // Find which food item needs to be consumed. + if (foodTypeSkillId == 2188) + { + item = owner.getInventory().getItemByItemId(6643); + } + else if (foodTypeSkillId == 2189) + { + item = owner.getInventory().getItemByItemId(6644); + } + + // if the owner has enough food, call the item handler (use the food and triffer all necessary actions) + if ((item != null) && (item.getCount() >= 1)) + { + L2Object oldTarget = owner.getTarget(); + owner.setTarget(_tamedBeast); + L2Object[] targets = + { + _tamedBeast + }; + + // emulate a call to the owner using food, but bypass all checks for range, etc + // this also causes a call to the AI tasks handling feeding, which may call onReceiveFood as required. + owner.callSkill(SkillTable.getInstance().getInfo(foodTypeSkillId, 1), targets); + owner.setTarget(oldTarget); + } + else + { + // if the owner has no food, the beast immediately despawns, except when it was only + // newly spawned. Newly spawned beasts can last up to 5 minutes + if (_tamedBeast.getRemainingTime() < (MAX_DURATION - 300000)) + { + _tamedBeast.setRemainingTime(-1); + } + } + /* + * There are too many conflicting reports about whether distance from home should be taken into consideration. Disabled for now. if (_tamedBeast.isTooFarFromHome()) _tamedBeast.setRemainingTime(-1); + */ + + if (_tamedBeast.getRemainingTime() <= 0) + { + _tamedBeast.deleteMe(); + } + } + } + } + + private class CheckOwnerBuffs implements Runnable + { + private final L2TamedBeastInstance _tamedBeast; + private final int _numBuffs; + + CheckOwnerBuffs(L2TamedBeastInstance tamedBeast, int numBuffs) + { + _tamedBeast = tamedBeast; + _numBuffs = numBuffs; + } + + @Override + public void run() + { + L2PcInstance owner = _tamedBeast.getOwner(); + + // check if the owner is no longer around...if so, despawn + if ((owner == null) || !owner.isOnline()) + { + deleteMe(); + return; + } + // if the owner is too far away, stop anything else and immediately run towards the owner. + if (!isInsideRadius(owner, MAX_DISTANCE_FROM_OWNER, true, true)) + { + getAI().startFollow(owner); + return; + } + // if the owner is dead, do nothing... + if (owner.isDead()) + { + return; + } + // if the tamed beast is currently casting a spell, do not interfere (do not attempt to cast anything new yet). + if (isCastingNow()) + { + return; + } + + int totalBuffsOnOwner = 0; + int i = 0; + int rand = Rnd.get(_numBuffs); + L2Skill buffToGive = null; + + // get this npc's skills: getSkills() + FastMap skills = (FastMap) _tamedBeast.getTemplate().getSkills(); + + for (L2Skill skill : skills.values()) + { + // if the skill is a buff, check if the owner has it already [ owner.getEffect(L2Skill skill) ] + if (skill.getSkillType() == L2SkillType.BUFF) + { + if (i++ == rand) + { + buffToGive = skill; + } + if (owner.getFirstEffect(skill) != null) + { + totalBuffsOnOwner++; + } + } + } + // if the owner has less than 60% of this beast's available buff, cast a random buff + if (((_numBuffs * 2) / 3) > totalBuffsOnOwner) + { + _tamedBeast.sitCastAndFollow(buffToGive, owner); + } + getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, _tamedBeast.getOwner()); + } + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if ((player == null) || !canTarget(player)) + { + return; + } + + // Check if the L2PcInstance already target the L2NpcInstance + if (this != player.getTarget()) + { + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player + MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()); + player.sendPacket(my); + + // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_HP, (int) getStatus().getCurrentHp()); + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); + player.sendPacket(su); + + // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + if (isAutoAttackable(player) && (Math.abs(player.getZ() - getZ()) < 100)) + { + player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); + } + else + { + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TeleporterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TeleporterInstance.java new file mode 100644 index 00000000..78db48ea --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TeleporterInstance.java @@ -0,0 +1,336 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.Calendar; +import java.util.StringTokenizer; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.datatables.TeleportLocationTable; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.instancemanager.SiegeManager; +import com.l2jserver.gameserver.instancemanager.TownManager; +import com.l2jserver.gameserver.model.L2TeleportLocation; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author NightMarez + * @version $Revision: 1.3.2.2.2.5 $ $Date: 2005/03/27 15:29:32 $ + */ +public final class L2TeleporterInstance extends L2Npc +{ + private static final int COND_ALL_FALSE = 0; + private static final int COND_BUSY_BECAUSE_OF_SIEGE = 1; + private static final int COND_OWNER = 2; + private static final int COND_REGULAR = 3; + + /** + * @param template + */ + public L2TeleporterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2TeleporterInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + + int condition = validateCondition(player); + + StringTokenizer st = new StringTokenizer(command, " "); + String actualCommand = st.nextToken(); // Get actual command + + if ((player.getFirstEffect(6201) != null) || (player.getFirstEffect(6202) != null) || (player.getFirstEffect(6203) != null)) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + + String filename = "data/html/teleporter/epictransformed.htm"; + + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + return; + } + else if (actualCommand.equalsIgnoreCase("goto")) + { + int npcId = getNpcId(); + + switch (npcId) + { + case 32534: // Seed of Infinity + case 32539: + if (player.isFlyingMounted()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_ENTER_SEED_IN_FLYING_TRANSFORM)); + return; + } + break; + } + + if (st.countTokens() <= 0) + { + return; + } + + int whereTo = Integer.parseInt(st.nextToken()); + if (condition == COND_REGULAR) + { + doTeleport(player, whereTo); + return; + } + else if (condition == COND_OWNER) + { + int minPrivilegeLevel = 0; // NOTE: Replace 0 with highest level when privilege level is implemented + if (st.countTokens() >= 1) + { + minPrivilegeLevel = Integer.parseInt(st.nextToken()); + } + + if (10 >= minPrivilegeLevel) + { + doTeleport(player, whereTo); + } + else + { + player.sendMessage("You don't have the sufficient access level to teleport there."); + } + return; + } + } + else if (command.startsWith("Chat")) + { + Calendar cal = Calendar.getInstance(); + int val = 0; + try + { + val = Integer.parseInt(command.substring(5)); + } + catch (IndexOutOfBoundsException ioobe) + { + } + catch (NumberFormatException nfe) + { + } + + if ((val == 1) && (player.getLevel() < 41)) + { + showNewbieHtml(player); + return; + } + else if ((val == 1) && (cal.get(Calendar.HOUR_OF_DAY) >= 20) && (cal.get(Calendar.HOUR_OF_DAY) <= 23) && ((cal.get(Calendar.DAY_OF_WEEK) == 1) || (cal.get(Calendar.DAY_OF_WEEK) == 7))) + { + showHalfPriceHtml(player); + return; + } + showChatWindow(player, val); + } + + super.onBypassFeedback(player, command); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/teleporter/" + pom + ".htm"; + } + + private void showNewbieHtml(L2PcInstance player) + { + if (player == null) + { + return; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + + String filename = "data/html/teleporter/free/" + getTemplate().npcId + ".htm"; + if (!HtmCache.getInstance().isLoadable(filename)) + { + filename = "data/html/teleporter/" + getTemplate().npcId + "-1.htm"; + } + + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } + + private void showHalfPriceHtml(L2PcInstance player) + { + if (player == null) + { + return; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + + String filename = "data/html/teleporter/half/" + getNpcId() + ".htm"; + if (!HtmCache.getInstance().isLoadable(filename)) + { + filename = "data/html/teleporter/" + getNpcId() + "-1.htm"; + } + + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + String filename = "data/html/teleporter/castleteleporter-no.htm"; + + int condition = validateCondition(player); + if (condition == COND_REGULAR) + { + super.showChatWindow(player); + return; + } + else if (condition > COND_ALL_FALSE) + { + if (condition == COND_BUSY_BECAUSE_OF_SIEGE) + { + filename = "data/html/teleporter/castleteleporter-busy.htm"; // Busy because of siege + } + else if (condition == COND_OWNER) // Clan owns castle + { + filename = getHtmlPath(getNpcId(), 0); // Owner message window + } + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } + + private void doTeleport(L2PcInstance player, int val) + { + L2TeleportLocation list = TeleportLocationTable.getInstance().getTemplate(val); + if (list != null) + { + // you cannot teleport to village that is in siege + if (SiegeManager.getInstance().getSiege(list.getLocX(), list.getLocY(), list.getLocZ()) != null) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_PORT_THAT_IS_IN_SIGE)); + return; + } + else if (TownManager.townHasCastleInSiege(list.getLocX(), list.getLocY()) && isInsideZone(L2Character.ZONE_TOWN)) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NO_PORT_THAT_IS_IN_SIGE)); + return; + } + else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_USE_GK && (player.getKarma() > 0)) // karma + { + player.sendMessage("Go away, you're not welcome here."); + return; + } + else if (player.isCombatFlagEquipped()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_TELEPORT_WHILE_IN_POSSESSION_OF_A_WARD)); + return; + } + else if (list.getIsForNoble() && !player.isNoble()) + { + String filename = "data/html/teleporter/nobleteleporter-no.htm"; + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + return; + } + else if (player.isAlikeDead()) + { + return; + } + + Calendar cal = Calendar.getInstance(); + int price = list.getPrice(); + + if (player.getLevel() < 41) + { + price = 0; + } + else if (!list.getIsForNoble()) + { + if ((cal.get(Calendar.HOUR_OF_DAY) >= 20) && (cal.get(Calendar.HOUR_OF_DAY) <= 23) && ((cal.get(Calendar.DAY_OF_WEEK) == 1) || (cal.get(Calendar.DAY_OF_WEEK) == 7))) + { + price /= 2; + } + } + + if (Config.ALT_GAME_FREE_TELEPORT || player.destroyItemByItemId("Teleport " + (list.getIsForNoble() ? " nobless" : ""), list.getItemId(), price, this, true)) + { + if (Config.DEBUG) + { + _log.fine("Teleporting player " + player.getName() + " to new location: " + list.getLocX() + ":" + list.getLocY() + ":" + list.getLocZ()); + } + + player.teleToLocation(list.getLocX(), list.getLocY(), list.getLocZ(), true); + } + } + else + { + _log.warning("No teleport destination with id:" + val); + } + + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + private int validateCondition(L2PcInstance player) + { + if (CastleManager.getInstance().getCastleIndex(this) < 0) + { + return COND_REGULAR; // Regular access + } + else if (getCastle().getSiege().getIsInProgress()) + { + return COND_BUSY_BECAUSE_OF_SIEGE; // Busy because of siege + } + else if (player.getClan() != null) // Teleporter is on castle ground and player is in a clan + { + if (getCastle().getOwnerId() == player.getClanId()) + { + return COND_OWNER; // Owner + } + } + + return COND_ALL_FALSE; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TerrainObjectInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TerrainObjectInstance.java new file mode 100644 index 00000000..13ba4a35 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TerrainObjectInstance.java @@ -0,0 +1,48 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2TerrainObjectInstance extends L2Npc +{ + public L2TerrainObjectInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2TerrainObjectInstance); + _isHideName = true; + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public void onActionShift(L2PcInstance player) + { + if (player.isGM()) + { + super.onActionShift(player); + } + else + { + player.sendPacket(ActionFailed.STATIC_PACKET); + } + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TerritoryWardInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TerritoryWardInstance.java new file mode 100644 index 00000000..a98726b7 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TerritoryWardInstance.java @@ -0,0 +1,202 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2TerritoryWardInstance extends L2Attackable +{ + public L2TerritoryWardInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + + disableCoreAI(true); + } + + @Override + public boolean isAutoAttackable(L2Character attacker) + { + if (isInvul()) + { + return false; + } + if ((getCastle() == null) || !getCastle().getZone().isActive()) + { + return false; + } + + final L2PcInstance actingPlayer = attacker.getActingPlayer(); + if (actingPlayer == null) + { + return false; + } + if (actingPlayer.getSiegeSide() == 0) + { + return false; + } + if (TerritoryWarManager.getInstance().isAllyField(actingPlayer, getCastle().getCastleId())) + { + return false; + } + + return true; + } + + @Override + public boolean hasRandomAnimation() + { + return false; + } + + @Override + public void onSpawn() + { + super.onSpawn(); + + if (getCastle() == null) + { + _log.warning("L2TerritoryWardInstance(" + getName() + ") spawned outside Castle Zone!"); + } + } + + @Override + public void reduceCurrentHp(double damage, L2Character attacker, boolean awake, boolean isDOT, L2Skill skill) + { + if ((skill != null) || !TerritoryWarManager.getInstance().isTWInProgress()) + { + return; + } + + final L2PcInstance actingPlayer = attacker.getActingPlayer(); + if (actingPlayer == null) + { + return; + } + if (actingPlayer.isCombatFlagEquipped()) + { + return; + } + if (actingPlayer.getSiegeSide() == 0) + { + return; + } + if (getCastle() == null) + { + return; + } + if (TerritoryWarManager.getInstance().isAllyField(actingPlayer, getCastle().getCastleId())) + { + return; + } + + super.reduceCurrentHp(damage, attacker, awake, isDOT, skill); + } + + @Override + public void reduceCurrentHpByDOT(double i, L2Character attacker, L2Skill skill) + { + // wards can't be damaged by DOTs + } + + @Override + public boolean doDie(L2Character killer) + { + // Kill the L2NpcInstance (the corpse disappeared after 7 seconds) + if (!super.doDie(killer) || (getCastle() == null) || !TerritoryWarManager.getInstance().isTWInProgress()) + { + return false; + } + + if (killer instanceof L2PcInstance) + { + if ((((L2PcInstance) killer).getSiegeSide() > 0) && !((L2PcInstance) killer).isCombatFlagEquipped()) + { + ((L2PcInstance) killer).addItem("Pickup", getNpcId() - 23012, 1, null, false); + } + else + { + TerritoryWarManager.getInstance().getTerritoryWard(getNpcId() - 36491).spawnMe(); + } + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THE_S1_WARD_HAS_BEEN_DESTROYED_C2_HAS_THE_WARD); + sm.addString(getName().replaceAll(" Ward", "")); + sm.addPcName((L2PcInstance) killer); + TerritoryWarManager.getInstance().announceToParticipants(sm, 0, 0); + } + else + { + TerritoryWarManager.getInstance().getTerritoryWard(getNpcId() - 36491).spawnMe(); + } + decayMe(); + return true; + } + + @Override + public void onForcedAttack(L2PcInstance player) + { + onAction(player); + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if ((player == null) || !canTarget(player)) + { + return; + } + + // Check if the L2PcInstance already target the L2NpcInstance + if (this != player.getTarget()) + { + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player + MyTargetSelected my = new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()); + player.sendPacket(my); + + // Send a Server->Client packet StatusUpdate of the L2NpcInstance to the L2PcInstance to update its HP bar + StatusUpdate su = new StatusUpdate(this); + su.addAttribute(StatusUpdate.CUR_HP, (int) getStatus().getCurrentHp()); + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); + player.sendPacket(su); + + // Send a Server->Client packet ValidateLocation to correct the L2NpcInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + if (isAutoAttackable(player) && (Math.abs(player.getZ() - getZ()) < 100)) + { + player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this); + } + else + { + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TownPetInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TownPetInstance.java new file mode 100644 index 00000000..e626ab98 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TownPetInstance.java @@ -0,0 +1,107 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.model.L2CharPosition; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; +import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.Rnd; + +/** + * @author Kerberos + */ + +public class L2TownPetInstance extends L2Npc +{ + int randomX, randomY, spawnX, spawnY; + + public L2TownPetInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2TownPetInstance); + + if (Config.ALLOW_PET_WALKERS) + { + ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new RandomWalkTask(), 2000, 4000); + } + } + + @Override + public void onAction(L2PcInstance player, boolean interact) + { + if (!canTarget(player)) + { + return; + } + + if (this != player.getTarget()) + { + // Set the target of the L2PcInstance player + player.setTarget(this); + + // Send a Server->Client packet MyTargetSelected to the L2PcInstance player + // The color to display in the select window is White + MyTargetSelected my = new MyTargetSelected(getObjectId(), 0); + player.sendPacket(my); + + // Send a Server->Client packet ValidateLocation to correct the L2ArtefactInstance position and heading on the client + player.sendPacket(new ValidateLocation(this)); + } + else if (interact) + { + // Calculate the distance between the L2PcInstance and the L2NpcInstance + if (!canInteract(player)) + { + // Notify the L2PcInstance AI with AI_INTENTION_INTERACT + player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this); + } + } + // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet + player.sendPacket(ActionFailed.STATIC_PACKET); + } + + @Override + public void onSpawn() + { + super.onSpawn(); + spawnX = getX(); + spawnY = getY(); + } + + public class RandomWalkTask implements Runnable + { + @Override + public void run() + { + if (!isInActiveRegion()) + { + return; // but rather the AI should be turned off completely.. + } + randomX = (spawnX + Rnd.get(2 * 50)) - 50; + randomY = (spawnY + Rnd.get(2 * 50)) - 50; + setRunning(); + if ((randomX != getX()) && (randomY != getY())) + { + getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(randomX, randomY, getZ(), 0)); + } + } + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TrainerHealersInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TrainerHealersInstance.java new file mode 100644 index 00000000..e3e8a96a --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TrainerHealersInstance.java @@ -0,0 +1,187 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.Collection; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2SkillLearn; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author Zoey76 + */ +public final class L2TrainerHealersInstance extends L2TrainerInstance +{ + public L2TrainerHealersInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2TrainerHealersInstance); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/trainer/skilltransfer/" + pom + ".htm"; + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + if (command.equals("SkillTransfer_Learn")) + { + if (!getTemplate().canTeach(player.getClassId())) + { + showNoTeachHtml(player); + return; + } + if ((player.getLevel() < 76) || (player.getClassId().level() < 3)) + { + html.setFile(player.getHtmlPrefix(), "data/html/trainer/skilltransfer/learn-lowlevel.htm"); + player.sendPacket(html); + return; + } + showTransferSkillList(player); + } + else if (command.equals("SkillTransfer_Cleanse")) + { + if (!getTemplate().canTeach(player.getClassId())) + { + showNoTeachHtml(player); + return; + } + if ((player.getLevel() < 76) || (player.getClassId().level() < 3)) + { + html.setFile(player.getHtmlPrefix(), "data/html/trainer/skilltransfer/cleanse-no.htm"); + player.sendPacket(html); + return; + } + if (player.getAdena() < Config.FEE_DELETE_TRANSFER_SKILLS) + { + player.sendPacket(SystemMessageId.CANNOT_RESET_SKILL_LINK_BECAUSE_NOT_ENOUGH_ADENA); + return; + } + + boolean hasSkills = false; + if (!hasTransferSkillItems(player)) + { + final Collection skills = SkillTreesData.getInstance().getTransferSkillTree(player.getClassId()).values(); + + for (L2SkillLearn s : skills) + { + final L2Skill sk = player.getKnownSkill(s.getSkillId()); + if (sk != null) + { + player.removeSkill(sk); + if (s.getItemsIdCount() != null) + { + player.addItem("Cleanse", s.getItemsIdCount()[0][0], s.getItemsIdCount()[0][1], this, true); + } + else + { + _log.warning(getClass().getSimpleName() + ": Transfer skill Id: " + s.getSkillId() + " doesn't have required items defined!"); + } + hasSkills = true; + } + } + + // Adena gets reduced once. + if (hasSkills) + { + player.reduceAdena("Cleanse", Config.FEE_DELETE_TRANSFER_SKILLS, this, true); + } + } + else + { + // Come back when you have used all transfer skill items for this class. + html.setFile(player.getHtmlPrefix(), "data/html/trainer/skilltransfer/cleanse-no_skills.htm"); + player.sendPacket(html); + } + } + else + { + super.onBypassFeedback(player, command); + } + } + + /** + * This displays Transfer Skill List to the player. + * @param player the active character. + */ + public static void showTransferSkillList(L2PcInstance player) + { + final FastList skills = SkillTreesData.getInstance().getAvailableTransferSkills(player); + final AcquireSkillList asl = new AcquireSkillList(SkillType.Transfer); + int count = 0; + + for (L2SkillLearn s : skills) + { + if (SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()) != null) + { + count++; + asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), 0); + } + } + + if (count > 0) + { + player.sendPacket(asl); + } + else + { + player.sendPacket(SystemMessageId.NO_MORE_SKILLS_TO_LEARN); + } + } + + private boolean hasTransferSkillItems(L2PcInstance player) + { + int itemId; + switch (player.getClassId()) + { + case cardinal: + itemId = 15307; + break; + case evaSaint: + itemId = 15308; + break; + case shillienSaint: + itemId = 15309; + break; + default: + itemId = -1; + } + return (player.getInventory().getInventoryItemCount(itemId, -1) > 0); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TrainerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TrainerInstance.java new file mode 100644 index 00000000..0da24868 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TrainerInstance.java @@ -0,0 +1,42 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2TrainerInstance extends L2NpcInstance +{ + public L2TrainerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2TrainerInstance); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/trainer/" + pom + ".htm"; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TransformManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TransformManagerInstance.java new file mode 100644 index 00000000..9099b1b1 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TransformManagerInstance.java @@ -0,0 +1,356 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.datatables.MultiSell; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.instancemanager.QuestManager; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2SkillLearn; +import com.l2jserver.gameserver.model.quest.QuestState; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.util.Util; + +/** + * @author Zoey76 + */ +public final class L2TransformManagerInstance extends L2MerchantInstance +{ + + private static final String htmlFolder = "data/html/masterTransformation/"; + + public static final String[] _questVarNames = + { + "EmergentAbility65-", + "EmergentAbility70-", + "ClassAbility75-", + "ClassAbility80-" + }; + + private static final int[] _itemsIds = + { + 10280, + 10281, + 10282, + 10283, + 10284, + 10285, + 10286, + 10287, + 10288, + 10289, + 10290, + 10291, + 10292, + 10293, + 10294, + 10612 + }; + + public L2TransformManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2TransformManagerInstance); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + return htmlFolder + "master_transformation001.htm"; + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + if (command.startsWith("LearnTransformationSkill")) + { + if (canTransform(player)) + { + L2TransformManagerInstance.showTransformSkillList(player); + } + else + { + final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation003.htm"); + player.sendPacket(html); + } + return; + } + else if (command.startsWith("BuyTransformationItems")) + { + if (canTransform(player)) + { + MultiSell.getInstance().separateAndSend(32323001, player, this, false); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation004.htm"); + player.sendPacket(html); + } + return; + } + else if (command.startsWith("LearnSubClassSkill")) + { + if (player.isSubClassActive()) + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation008.htm"); + player.sendPacket(html); + } + else + { + boolean hasItems = false; + for (int i : _itemsIds) + { + if (player.getInventory().getItemByItemId(i) != null) + { + hasItems = true; + break; + } + } + if (hasItems) + { + showSubClassSkillList(player); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation008.htm"); + player.sendPacket(html); + } + } + return; + } + else if (command.startsWith("CancelCertification")) + { + final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + if (player.getSubClasses().size() == 0) + { + html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation007.htm"); + } + else if (player.isSubClassActive()) + { + html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation008.htm"); + } + else if (player.getAdena() < Config.FEE_DELETE_SUBCLASS_SKILLS) + { + html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation008no.htm"); + } + else + { + QuestState st = player.getQuestState("SubClassSkills"); + if (st == null) + { + st = QuestManager.getInstance().getQuest("SubClassSkills").newQuestState(player); + } + + int activeCertifications = 0; + for (String varName : _questVarNames) + { + for (int i = 1; i <= Config.MAX_SUBCLASS; i++) + { + String qvar = st.getGlobalQuestVar(varName + i); + if (!qvar.isEmpty() && (qvar.endsWith(";") || !qvar.equals("0"))) + { + activeCertifications++; + } + } + } + if (activeCertifications == 0) + { + html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation010no.htm"); + } + else + { + for (String varName : _questVarNames) + { + for (int i = 1; i <= Config.MAX_SUBCLASS; i++) + { + final String qvarName = varName + i; + final String qvar = st.getGlobalQuestVar(qvarName); + if (qvar.endsWith(";")) + { + final String skillIdVar = qvar.replace(";", ""); + if (Util.isDigit(skillIdVar)) + { + int skillId = Integer.parseInt(skillIdVar); + final L2Skill sk = SkillTable.getInstance().getInfo(skillId, 1); + if (sk != null) + { + player.removeSkill(sk); + st.saveGlobalQuestVar(qvarName, "0"); + } + } + else + { + _log.warning("Invalid Sub-Class Skill Id: " + skillIdVar + " for player " + player.getName() + "!"); + } + } + else if (!qvar.isEmpty() && !qvar.equals("0")) + { + if (Util.isDigit(qvar)) + { + final int itemObjId = Integer.parseInt(qvar); + L2ItemInstance itemInstance = player.getInventory().getItemByObjectId(itemObjId); + if (itemInstance != null) + { + player.destroyItem("CancelCertification", itemObjId, 1, player, false); + } + else + { + itemInstance = player.getWarehouse().getItemByObjectId(itemObjId); + if (itemInstance != null) + { + _log.warning("Somehow " + player.getName() + " put a certification book into warehouse!"); + player.getWarehouse().destroyItem("CancelCertification", itemInstance, 1, player, false); + } + else + { + _log.warning("Somehow " + player.getName() + " deleted a certification book!"); + } + } + st.saveGlobalQuestVar(qvarName, "0"); + } + else + { + _log.warning("Invalid item object Id: " + qvar + " for player " + player.getName() + "!"); + } + } + } + } + + player.reduceAdena("Cleanse", Config.FEE_DELETE_SUBCLASS_SKILLS, this, true); + html.setFile(player.getHtmlPrefix(), htmlFolder + "master_transformation009no.htm"); + player.sendSkillList(); + } + + // Let's consume all certification books, even those not present in datbase. + L2ItemInstance itemInstance = null; + for (int itemId : _itemsIds) + { + itemInstance = player.getInventory().getItemByItemId(itemId); + if (itemInstance != null) + { + _log.warning(getClass().getName() + ": player " + player + " had 'extra' certification skill books while cancelling sub-class certifications!"); + player.destroyItem("CancelCertificationExtraBooks", itemInstance, this, false); + } + } + } + player.sendPacket(html); + return; + } + super.onBypassFeedback(player, command); + } + + // Transformations: + /** + * Returns true if the player meets the required conditions to learn a transformation. + * @param player + * @return boolean + */ + public static boolean canTransform(L2PcInstance player) + { + if (Config.ALLOW_TRANSFORM_WITHOUT_QUEST) + { + return true; + } + final QuestState st = player.getQuestState("136_MoreThanMeetsTheEye"); + if ((st != null) && st.isCompleted()) + { + return true; + } + return false; + } + + /** + * This displays Transformation Skill List to the player. + * @param player the active character. + */ + public static void showTransformSkillList(L2PcInstance player) + { + final FastList skills = SkillTreesData.getInstance().getAvailableTransformSkills(player); + final AcquireSkillList asl = new AcquireSkillList(SkillType.ClassTransform); + int counts = 0; + + for (L2SkillLearn s : skills) + { + if (SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()) != null) + { + counts++; + asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), 0); + } + } + + if (counts == 0) + { + final int minlevel = SkillTreesData.getInstance().getMinLevelForNewSkill(player, SkillTreesData.getInstance().getTransformSkillTree()); + if (minlevel > 0) + { + // No more skills to learn, come back when you level. + final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DO_NOT_HAVE_FURTHER_SKILLS_TO_LEARN_S1); + sm.addNumber(minlevel); + player.sendPacket(sm); + } + else + { + player.sendPacket(SystemMessageId.NO_MORE_SKILLS_TO_LEARN); + } + } + else + { + player.sendPacket(asl); + } + } + + // SubClass: + /** + * This displays Sub-Class Skill List to the player. + * @param player the active character. + */ + public static void showSubClassSkillList(L2PcInstance player) + { + final FastList subClassSkills = SkillTreesData.getInstance().getAvailableSubClassSkills(player); + final AcquireSkillList asl = new AcquireSkillList(AcquireSkillList.SkillType.SubClass); + int count = 0; + + for (L2SkillLearn s : subClassSkills) + { + if (SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()) != null) + { + count++; + asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), 0, 0); + } + } + if (count > 0) + { + player.sendPacket(asl); + } + else + { + player.sendPacket(SystemMessageId.NO_MORE_SKILLS_TO_LEARN); + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TrapInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TrapInstance.java new file mode 100644 index 00000000..debaed94 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TrapInstance.java @@ -0,0 +1,277 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.List; + +import javolution.util.FastList; + +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Trap; +import com.l2jserver.gameserver.model.olympiad.OlympiadGameManager; +import com.l2jserver.gameserver.model.quest.Quest; +import com.l2jserver.gameserver.model.quest.Quest.TrapAction; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2TrapInstance extends L2Trap +{ + private L2PcInstance _owner; + private int _level; + private boolean _isInArena = false; + private final List _playersWhoDetectedMe = new FastList<>(); + + /** + * @param objectId + * @param template + * @param owner + */ + public L2TrapInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, int lifeTime, L2Skill skill) + { + super(objectId, template, lifeTime, skill); + setInstanceType(InstanceType.L2TrapInstance); + + setInstanceId(owner.getInstanceId()); + + _owner = owner; + _level = owner.getLevel(); + } + + public L2TrapInstance(int objectId, L2NpcTemplate template, int instanceId, int lifeTime, L2Skill skill) + { + super(objectId, template, lifeTime, skill); + setInstanceType(InstanceType.L2TrapInstance); + + setInstanceId(instanceId); + + _owner = null; + if (skill != null) + { + _level = skill.getLevel(); + } + else + { + _level = 1; + } + } + + @Override + public int getLevel() + { + return _level; + } + + @Override + public L2PcInstance getOwner() + { + return _owner; + } + + @Override + public L2PcInstance getActingPlayer() + { + return _owner; + } + + @Override + public void onSpawn() + { + super.onSpawn(); + _isInArena = isInsideZone(ZONE_PVP) && !isInsideZone(ZONE_SIEGE); + _playersWhoDetectedMe.clear(); + } + + @Override + public void deleteMe() + { + if (_owner != null) + { + _owner.setTrap(null); + _owner = null; + } + super.deleteMe(); + } + + @Override + public void unSummon() + { + if (_owner != null) + { + _owner.setTrap(null); + _owner = null; + } + super.unSummon(); + } + + @Override + public int getKarma() + { + return _owner != null ? _owner.getKarma() : 0; + } + + @Override + public byte getPvpFlag() + { + return _owner != null ? _owner.getPvpFlag() : 0; + } + + @Override + public void sendDamageMessage(L2Character target, int damage, boolean mcrit, boolean pcrit, boolean miss) + { + if (miss || (_owner == null)) + { + return; + } + + if (_owner.isInOlympiadMode() && (target instanceof L2PcInstance) && ((L2PcInstance) target).isInOlympiadMode() && (((L2PcInstance) target).getOlympiadGameId() == _owner.getOlympiadGameId())) + { + OlympiadGameManager.getInstance().notifyCompetitorDamage(getOwner(), damage); + } + + final SystemMessage sm; + + if (target.isInvul() && !(target instanceof L2NpcInstance)) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.ATTACK_WAS_BLOCKED); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.C1_GAVE_C2_DAMAGE_OF_S3); + sm.addCharName(this); + sm.addCharName(target); + sm.addNumber(damage); + } + + _owner.sendPacket(sm); + } + + @Override + public boolean canSee(L2Character cha) + { + if ((cha != null) && _playersWhoDetectedMe.contains(cha.getObjectId())) + { + return true; + } + + if ((_owner == null) || (cha == null)) + { + return false; + } + if (cha == _owner) + { + return true; + } + + if (cha instanceof L2PcInstance) + { + // observers can't see trap + if (((L2PcInstance) cha).inObserverMode()) + { + return false; + } + + // olympiad competitors can't see trap + if (_owner.isInOlympiadMode() && ((L2PcInstance) cha).isInOlympiadMode() && (((L2PcInstance) cha).getOlympiadSide() != _owner.getOlympiadSide())) + { + return false; + } + } + + if (_isInArena) + { + return true; + } + + if (_owner.isInParty() && cha.isInParty() && (_owner.getParty().getPartyLeaderOID() == cha.getParty().getPartyLeaderOID())) + { + return true; + } + + return false; + } + + @Override + public void setDetected(L2Character detector) + { + if (_isInArena) + { + super.setDetected(detector); + return; + } + if ((_owner != null) && (_owner.getPvpFlag() == 0) && (_owner.getKarma() == 0)) + { + return; + } + + _playersWhoDetectedMe.add(detector.getObjectId()); + if (getTemplate().getEventQuests(Quest.QuestEventType.ON_TRAP_ACTION) != null) + { + for (Quest quest : getTemplate().getEventQuests(Quest.QuestEventType.ON_TRAP_ACTION)) + { + quest.notifyTrapAction(this, detector, TrapAction.TRAP_DETECTED); + } + } + super.setDetected(detector); + } + + @Override + protected boolean checkTarget(L2Character target) + { + if (!L2Skill.checkForAreaOffensiveSkills(this, target, getSkill(), _isInArena)) + { + return false; + } + + // observers + if ((target instanceof L2PcInstance) && ((L2PcInstance) target).inObserverMode()) + { + return false; + } + + // olympiad own team and their summons not attacked + if ((_owner != null) && _owner.isInOlympiadMode()) + { + final L2PcInstance player = target.getActingPlayer(); + if ((player != null) && player.isInOlympiadMode() && (player.getOlympiadSide() == _owner.getOlympiadSide())) + { + return false; + } + } + + if (_isInArena) + { + return true; + } + + // trap owned by players not attack non-flagged players + if (_owner != null) + { + final L2PcInstance player = target.getActingPlayer(); + if (target instanceof L2Attackable) + { + return true; + } + else if ((player == null) || ((player.getPvpFlag() == 0) && (player.getKarma() == 0))) + { + return false; + } + } + + return true; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TvTEventNpcInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TvTEventNpcInstance.java new file mode 100644 index 00000000..467103ad --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2TvTEventNpcInstance.java @@ -0,0 +1,107 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.entity.TvTEvent; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2TvTEventNpcInstance extends L2Npc +{ + private static final String htmlPath = "data/html/mods/TvTEvent/"; + + public L2TvTEventNpcInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2TvTEventNpcInstance); + } + + @Override + public void onBypassFeedback(L2PcInstance playerInstance, String command) + { + TvTEvent.onBypass(command, playerInstance); + } + + @Override + public void showChatWindow(L2PcInstance playerInstance, int val) + { + if (playerInstance == null) + { + return; + } + + if (TvTEvent.isParticipating()) + { + final boolean isParticipant = TvTEvent.isPlayerParticipant(playerInstance.getObjectId()); + final String htmContent; + + if (!isParticipant) + { + htmContent = HtmCache.getInstance().getHtm(playerInstance.getHtmlPrefix(), htmlPath + "Participation.htm"); + } + else + { + htmContent = HtmCache.getInstance().getHtm(playerInstance.getHtmlPrefix(), htmlPath + "RemoveParticipation.htm"); + } + + if (htmContent != null) + { + int[] teamsPlayerCounts = TvTEvent.getTeamsPlayerCounts(); + NpcHtmlMessage npcHtmlMessage = new NpcHtmlMessage(getObjectId()); + + npcHtmlMessage.setHtml(htmContent); + npcHtmlMessage.replace("%objectId%", String.valueOf(getObjectId())); + npcHtmlMessage.replace("%team1name%", Config.TVT_EVENT_TEAM_1_NAME); + npcHtmlMessage.replace("%team1playercount%", String.valueOf(teamsPlayerCounts[0])); + npcHtmlMessage.replace("%team2name%", Config.TVT_EVENT_TEAM_2_NAME); + npcHtmlMessage.replace("%team2playercount%", String.valueOf(teamsPlayerCounts[1])); + npcHtmlMessage.replace("%playercount%", String.valueOf(teamsPlayerCounts[0] + teamsPlayerCounts[1])); + if (!isParticipant) + { + npcHtmlMessage.replace("%fee%", TvTEvent.getParticipationFee()); + } + + playerInstance.sendPacket(npcHtmlMessage); + } + } + else if (TvTEvent.isStarting() || TvTEvent.isStarted()) + { + final String htmContent = HtmCache.getInstance().getHtm(playerInstance.getHtmlPrefix(), htmlPath + "Status.htm"); + + if (htmContent != null) + { + int[] teamsPlayerCounts = TvTEvent.getTeamsPlayerCounts(); + int[] teamsPointsCounts = TvTEvent.getTeamsPoints(); + NpcHtmlMessage npcHtmlMessage = new NpcHtmlMessage(getObjectId()); + + npcHtmlMessage.setHtml(htmContent); + // npcHtmlMessage.replace("%objectId%", String.valueOf(getObjectId())); + npcHtmlMessage.replace("%team1name%", Config.TVT_EVENT_TEAM_1_NAME); + npcHtmlMessage.replace("%team1playercount%", String.valueOf(teamsPlayerCounts[0])); + npcHtmlMessage.replace("%team1points%", String.valueOf(teamsPointsCounts[0])); + npcHtmlMessage.replace("%team2name%", Config.TVT_EVENT_TEAM_2_NAME); + npcHtmlMessage.replace("%team2playercount%", String.valueOf(teamsPlayerCounts[1])); + npcHtmlMessage.replace("%team2points%", String.valueOf(teamsPointsCounts[1])); // <---- array index from 0 to 1 thx DaRkRaGe + playerInstance.sendPacket(npcHtmlMessage); + } + } + + playerInstance.sendPacket(ActionFailed.STATIC_PACKET); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterDElfInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterDElfInstance.java new file mode 100644 index 00000000..75d62abd --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterDElfInstance.java @@ -0,0 +1,38 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.base.PlayerClass; +import com.l2jserver.gameserver.model.base.Race; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2VillageMasterDElfInstance extends L2VillageMasterInstance +{ + public L2VillageMasterDElfInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + } + + @Override + protected final boolean checkVillageMasterRace(PlayerClass pclass) + { + if (pclass == null) + { + return false; + } + + return pclass.isOfRace(Race.DarkElf); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterDwarfInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterDwarfInstance.java new file mode 100644 index 00000000..f13a9f09 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterDwarfInstance.java @@ -0,0 +1,38 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.base.PlayerClass; +import com.l2jserver.gameserver.model.base.Race; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2VillageMasterDwarfInstance extends L2VillageMasterInstance +{ + public L2VillageMasterDwarfInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + } + + @Override + protected final boolean checkVillageMasterRace(PlayerClass pclass) + { + if (pclass == null) + { + return false; + } + + return pclass.isOfRace(Race.Dwarf); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterFighterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterFighterInstance.java new file mode 100644 index 00000000..f179977c --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterFighterInstance.java @@ -0,0 +1,50 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.base.ClassType; +import com.l2jserver.gameserver.model.base.PlayerClass; +import com.l2jserver.gameserver.model.base.Race; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2VillageMasterFighterInstance extends L2VillageMasterInstance +{ + public L2VillageMasterFighterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + } + + @Override + protected final boolean checkVillageMasterRace(PlayerClass pclass) + { + if (pclass == null) + { + return false; + } + + return pclass.isOfRace(Race.Human) || pclass.isOfRace(Race.Elf); + } + + @Override + protected final boolean checkVillageMasterTeachType(PlayerClass pclass) + { + if (pclass == null) + { + return false; + } + + return pclass.isOfType(ClassType.Fighter); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterInstance.java new file mode 100644 index 00000000..9f0474ea --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterInstance.java @@ -0,0 +1,1152 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.Iterator; +import java.util.Set; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.datatables.CharTemplateTable; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.instancemanager.FortManager; +import com.l2jserver.gameserver.instancemanager.FortSiegeManager; +import com.l2jserver.gameserver.instancemanager.SiegeManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2Clan.SubPledge; +import com.l2jserver.gameserver.model.L2ClanMember; +import com.l2jserver.gameserver.model.L2SkillLearn; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.base.ClassId; +import com.l2jserver.gameserver.model.base.PlayerClass; +import com.l2jserver.gameserver.model.base.Race; +import com.l2jserver.gameserver.model.base.SubClass; +import com.l2jserver.gameserver.model.entity.Castle; +import com.l2jserver.gameserver.model.entity.Fort; +import com.l2jserver.gameserver.model.quest.QuestState; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList; +import com.l2jserver.gameserver.network.serverpackets.AcquireSkillList.SkillType; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo; +import com.l2jserver.gameserver.network.serverpackets.MagicSkillLaunched; +import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.network.serverpackets.UserInfo; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.StringUtil; + +/** + * This class ... + * @version $Revision: 1.4.2.3.2.8 $ $Date: 2005/03/29 23:15:15 $ + */ +public class L2VillageMasterInstance extends L2NpcInstance +{ + // private static Logger _log = Logger.getLogger(L2VillageMasterInstance.class.getName()); + + /** + * @param template + */ + public L2VillageMasterInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2VillageMasterInstance); + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/villagemaster/" + pom + ".htm"; + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + String[] commandStr = command.split(" "); + String actualCommand = commandStr[0]; // Get actual command + + String cmdParams = ""; + String cmdParams2 = ""; + + if (commandStr.length >= 2) + { + cmdParams = commandStr[1]; + } + if (commandStr.length >= 3) + { + cmdParams2 = commandStr[2]; + } + + if (actualCommand.equalsIgnoreCase("create_clan")) + { + if (cmdParams.isEmpty()) + { + return; + } + + ClanTable.getInstance().createClan(player, cmdParams); + } + else if (actualCommand.equalsIgnoreCase("create_academy")) + { + if (cmdParams.isEmpty()) + { + return; + } + + createSubPledge(player, cmdParams, null, L2Clan.SUBUNIT_ACADEMY, 5); + } + else if (actualCommand.equalsIgnoreCase("rename_pledge")) + { + if (cmdParams.isEmpty() || cmdParams2.isEmpty()) + { + return; + } + + renameSubPledge(player, Integer.valueOf(cmdParams), cmdParams2); + } + else if (actualCommand.equalsIgnoreCase("create_royal")) + { + if (cmdParams.isEmpty()) + { + return; + } + + createSubPledge(player, cmdParams, cmdParams2, L2Clan.SUBUNIT_ROYAL1, 6); + } + else if (actualCommand.equalsIgnoreCase("create_knight")) + { + if (cmdParams.isEmpty()) + { + return; + } + + createSubPledge(player, cmdParams, cmdParams2, L2Clan.SUBUNIT_KNIGHT1, 7); + } + else if (actualCommand.equalsIgnoreCase("assign_subpl_leader")) + { + if (cmdParams.isEmpty()) + { + return; + } + + assignSubPledgeLeader(player, cmdParams, cmdParams2); + } + else if (actualCommand.equalsIgnoreCase("create_ally")) + { + if (cmdParams.isEmpty()) + { + return; + } + + if (player.getClan() == null) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ONLY_CLAN_LEADER_CREATE_ALLIANCE)); + } + else + { + player.getClan().createAlly(player, cmdParams); + } + } + else if (actualCommand.equalsIgnoreCase("dissolve_ally")) + { + player.getClan().dissolveAlly(player); + } + else if (actualCommand.equalsIgnoreCase("dissolve_clan")) + { + dissolveClan(player, player.getClanId()); + } + else if (actualCommand.equalsIgnoreCase("change_clan_leader")) + { + if (cmdParams.isEmpty()) + { + return; + } + + changeClanLeader(player, cmdParams); + } + else if (actualCommand.equalsIgnoreCase("recover_clan")) + { + recoverClan(player, player.getClanId()); + } + else if (actualCommand.equalsIgnoreCase("increase_clan_level")) + { + if (player.getClan().levelUpClan(player)) + { + player.broadcastPacket(new MagicSkillUse(player, 5103, 1, 0, 0)); + player.broadcastPacket(new MagicSkillLaunched(player, 5103, 1)); + } + } + else if (actualCommand.equalsIgnoreCase("learn_clan_skills")) + { + showPledgeSkillList(player); + } + else if (command.startsWith("Subclass")) + { + // Subclasses may not be changed while a skill is in use. + if (player.isCastingNow() || player.isAllSkillsDisabled()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SUBCLASS_NO_CHANGE_OR_CREATE_WHILE_SKILL_IN_USE)); + return; + } + + NpcHtmlMessage html = new NpcHtmlMessage(getObjectId()); + + if (player.getTransformation() != null) + { + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_NoTransformed.htm"); + player.sendPacket(html); + return; + } + + int cmdChoice = 0; + int paramOne = 0; + int paramTwo = 0; + + try + { + cmdChoice = Integer.parseInt(command.substring(9, 10).trim()); + + int endIndex = command.indexOf(' ', 11); + if (endIndex == -1) + { + endIndex = command.length(); + } + + paramOne = Integer.parseInt(command.substring(11, endIndex).trim()); + if (command.length() > endIndex) + { + paramTwo = Integer.parseInt(command.substring(endIndex).trim()); + } + } + catch (Exception NumberFormatException) + { + } + + switch (cmdChoice) + { + case 0: // Subclass change menu + html.setFile(player.getHtmlPrefix(), getSubClassMenu(player.getRace())); + break; + case 1: // Add Subclass - Initial + // Avoid giving player an option to add a new sub class, if they have three already. + if (player.getTotalSubClasses() >= Config.MAX_SUBCLASS) + { + html.setFile(player.getHtmlPrefix(), getSubClassFail()); + break; + } + + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_Add.htm"); + final StringBuilder content1 = StringUtil.startAppend(200); + Set subsAvailable = getAvailableSubClasses(player); + + if ((subsAvailable != null) && !subsAvailable.isEmpty()) + { + for (PlayerClass subClass : subsAvailable) + { + StringUtil.append(content1, "", formatClassForDisplay(subClass), "
    "); + } + } + else + { + // TODO: Retail message + player.sendMessage("There are no sub classes available at this time."); + return; + } + html.replace("%list%", content1.toString()); + break; + case 2: // Change Class - Initial + if (player.getSubClasses().isEmpty()) + { + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ChangeNo.htm"); + } + else + { + final StringBuilder content2 = StringUtil.startAppend(200); + + if (checkVillageMaster(player.getBaseClass())) + { + StringUtil.append(content2, "", CharTemplateTable.getInstance().getClassNameById(player.getBaseClass()), "
    "); + } + + for (Iterator subList = iterSubClasses(player); subList.hasNext();) + { + SubClass subClass = subList.next(); + if (checkVillageMaster(subClass.getClassDefinition())) + { + StringUtil.append(content2, "", formatClassForDisplay(subClass.getClassDefinition()), "
    "); + } + } + + if (content2.length() > 0) + { + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_Change.htm"); + html.replace("%list%", content2.toString()); + } + else + { + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ChangeNotFound.htm"); + } + } + break; + case 3: // Change/Cancel Subclass - Initial + if ((player.getSubClasses() == null) || player.getSubClasses().isEmpty()) + { + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyEmpty.htm"); + break; + } + + // custom value + if (player.getTotalSubClasses() > 3) + { + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyCustom.htm"); + final StringBuilder content3 = StringUtil.startAppend(200); + int classIndex = 1; + + for (Iterator subList = iterSubClasses(player); subList.hasNext();) + { + SubClass subClass = subList.next(); + + StringUtil.append(content3, "Sub-class ", String.valueOf(classIndex++), "
    ", "", CharTemplateTable.getInstance().getClassNameById(subClass.getClassId()), "
    "); + } + html.replace("%list%", content3.toString()); + } + else + { + // retail html contain only 3 subclasses + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_Modify.htm"); + if (player.getSubClasses().containsKey(1)) + { + html.replace("%sub1%", CharTemplateTable.getInstance().getClassNameById(player.getSubClasses().get(1).getClassId())); + } + else + { + html.replace("%sub1%
    ", ""); + } + + if (player.getSubClasses().containsKey(2)) + { + html.replace("%sub2%", CharTemplateTable.getInstance().getClassNameById(player.getSubClasses().get(2).getClassId())); + } + else + { + html.replace("%sub2%
    ", ""); + } + + if (player.getSubClasses().containsKey(3)) + { + html.replace("%sub3%", CharTemplateTable.getInstance().getClassNameById(player.getSubClasses().get(3).getClassId())); + } + else + { + html.replace("%sub3%
    ", ""); + } + } + break; + case 4: // Add Subclass - Action (Subclass 4 x[x]) + /* + * If the character is less than level 75 on any of their previously chosen classes then disallow them to change to their most recently added sub-class choice. + */ + + if (!player.getFloodProtectors().getSubclass().tryPerformAction("add subclass")) + { + _log.warning("Player " + player.getName() + " has performed a subclass change too fast"); + return; + } + + boolean allowAddition = true; + + if (player.getTotalSubClasses() >= Config.MAX_SUBCLASS) + { + allowAddition = false; + } + + if (player.getLevel() < 75) + { + allowAddition = false; + } + + if (allowAddition) + { + if (!player.getSubClasses().isEmpty()) + { + for (Iterator subList = iterSubClasses(player); subList.hasNext();) + { + SubClass subClass = subList.next(); + + if (subClass.getLevel() < 75) + { + allowAddition = false; + break; + } + } + } + } + + /* + * If quest checking is enabled, verify if the character has completed the Mimir's Elixir (Path to Subclass) and Fate's Whisper (A Grade Weapon) quests by checking for instances of their unique reward items. If they both exist, remove both unique items and continue with adding + * the sub-class. + */ + if (allowAddition && !Config.ALT_GAME_SUBCLASS_WITHOUT_QUESTS) + { + allowAddition = checkQuests(player); + } + + if (allowAddition && isValidNewSubClass(player, paramOne)) + { + if (!player.addSubClass(paramOne, player.getTotalSubClasses() + 1)) + { + return; + } + + player.setActiveClass(player.getTotalSubClasses()); + + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_AddOk.htm"); + + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ADD_NEW_SUBCLASS)); // Subclass added. + } + else + { + html.setFile(player.getHtmlPrefix(), getSubClassFail()); + } + break; + case 5: // Change Class - Action + /* + * If the character is less than level 75 on any of their previously chosen classes then disallow them to change to their most recently added sub-class choice. Note: paramOne = classIndex + */ + + if (!player.getFloodProtectors().getSubclass().tryPerformAction("change class")) + { + _log.warning("Player " + player.getName() + " has performed a subclass change too fast"); + return; + } + + if (player.getClassIndex() == paramOne) + { + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_Current.htm"); + break; + } + + if (paramOne == 0) + { + if (!checkVillageMaster(player.getBaseClass())) + { + return; + } + } + else + { + try + { + if (!checkVillageMaster(player.getSubClasses().get(paramOne).getClassDefinition())) + { + return; + } + } + catch (NullPointerException e) + { + return; + } + } + + player.setActiveClass(paramOne); + + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SUBCLASS_TRANSFER_COMPLETED)); // Transfer completed. + return; + case 6: // Change/Cancel Subclass - Choice + // validity check + if ((paramOne < 1) || (paramOne > Config.MAX_SUBCLASS)) + { + return; + } + + subsAvailable = getAvailableSubClasses(player); + + // another validity check + if ((subsAvailable == null) || subsAvailable.isEmpty()) + { + // TODO: Retail message + player.sendMessage("There are no sub classes available at this time."); + return; + } + + final StringBuilder content6 = StringUtil.startAppend(200); + + for (PlayerClass subClass : subsAvailable) + { + StringUtil.append(content6, "", formatClassForDisplay(subClass), "
    "); + } + + switch (paramOne) + { + case 1: + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyChoice1.htm"); + break; + case 2: + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyChoice2.htm"); + break; + case 3: + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyChoice3.htm"); + break; + default: + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyChoice.htm"); + } + html.replace("%list%", content6.toString()); + break; + case 7: // Change Subclass - Action + /* + * Warning: the information about this subclass will be removed from the subclass list even if false! + */ + + if (!player.getFloodProtectors().getSubclass().tryPerformAction("change class")) + { + _log.warning("Player " + player.getName() + " has performed a subclass change too fast"); + return; + } + + if (!isValidNewSubClass(player, paramTwo)) + { + return; + } + + if (player.modifySubClass(paramOne, paramTwo)) + { + player.abortCast(); + player.stopAllEffectsExceptThoseThatLastThroughDeath(); // all effects from old subclass stopped! + player.stopCubics(); + player.setActiveClass(paramOne); + + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/SubClass_ModifyOk.htm"); + html.replace("%name%", CharTemplateTable.getInstance().getClassNameById(paramTwo)); + + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ADD_NEW_SUBCLASS)); // Subclass added. + } + else + { + /* + * This isn't good! modifySubClass() removed subclass from memory we must update _classIndex! Else IndexOutOfBoundsException can turn up some place down the line along with other seemingly unrelated problems. + */ + player.setActiveClass(0); // Also updates _classIndex plus switching _classid to baseclass. + + player.sendMessage("The sub class could not be added, you have been reverted to your base class."); + return; + } + break; + } + + html.replace("%objectId%", String.valueOf(getObjectId())); + player.sendPacket(html); + } + else + { + super.onBypassFeedback(player, command); + } + } + + protected String getSubClassMenu(Race pRace) + { + if (Config.ALT_GAME_SUBCLASS_EVERYWHERE || (pRace != Race.Kamael)) + { + return "data/html/villagemaster/SubClass.htm"; + } + + return "data/html/villagemaster/SubClass_NoOther.htm"; + } + + protected String getSubClassFail() + { + return "data/html/villagemaster/SubClass_Fail.htm"; + } + + protected boolean checkQuests(L2PcInstance player) + { + // Noble players can add Sub-Classes without quests + if (player.isNoble()) + { + return true; + } + + QuestState qs = player.getQuestState("234_FatesWhisper"); + if ((qs == null) || !qs.isCompleted()) + { + return false; + } + + qs = player.getQuestState("235_MimirsElixir"); + if ((qs == null) || !qs.isCompleted()) + { + return false; + } + + return true; + } + + /* + * Returns list of available subclasses Base class and already used subclasses removed + */ + private final Set getAvailableSubClasses(L2PcInstance player) + { + // get player base class + final int currentBaseId = player.getBaseClass(); + final ClassId baseCID = ClassId.values()[currentBaseId]; + + // we need 2nd occupation ID + final int baseClassId; + if (baseCID.level() > 2) + { + baseClassId = baseCID.getParent().ordinal(); + } + else + { + baseClassId = currentBaseId; + } + + /** + * If the race of your main class is Elf or Dark Elf, you may not select each class as a subclass to the other class. If the race of your main class is Kamael, you may not subclass any other race If the race of your main class is NOT Kamael, you may not subclass any Kamael class You may not + * select Overlord and Warsmith class as a subclass. You may not select a similar class as the subclass. The occupations classified as similar classes are as follows: Treasure Hunter, Plainswalker and Abyss Walker Hawkeye, Silver Ranger and Phantom Ranger Paladin, Dark Avenger, Temple Knight + * and Shillien Knight Warlocks, Elemental Summoner and Phantom Summoner Elder and Shillien Elder Swordsinger and Bladedancer Sorcerer, Spellsinger and Spellhowler Also, Kamael have a special, hidden 4 subclass, the inspector, which can only be taken if you have already completed the other + * two Kamael subclasses + */ + Set availSubs = PlayerClass.values()[baseClassId].getAvailableSubclasses(player); + + if ((availSubs != null) && !availSubs.isEmpty()) + { + for (Iterator availSub = availSubs.iterator(); availSub.hasNext();) + { + PlayerClass pclass = availSub.next(); + + // check for the village master + if (!checkVillageMaster(pclass)) + { + availSub.remove(); + continue; + } + + // scan for already used subclasses + int availClassId = pclass.ordinal(); + ClassId cid = ClassId.values()[availClassId]; + for (Iterator subList = iterSubClasses(player); subList.hasNext();) + { + SubClass prevSubClass = subList.next(); + ClassId subClassId = ClassId.values()[prevSubClass.getClassId()]; + + if (subClassId.equalsOrChildOf(cid)) + { + availSub.remove(); + break; + } + } + } + } + + return availSubs; + } + + /* + * Check new subclass classId for validity (villagemaster race/type, is not contains in previous subclasses, is contains in allowed subclasses) Base class not added into allowed subclasses. + */ + private final boolean isValidNewSubClass(L2PcInstance player, int classId) + { + if (!checkVillageMaster(classId)) + { + return false; + } + + final ClassId cid = ClassId.values()[classId]; + for (Iterator subList = iterSubClasses(player); subList.hasNext();) + { + SubClass sub = subList.next(); + ClassId subClassId = ClassId.values()[sub.getClassId()]; + + if (subClassId.equalsOrChildOf(cid)) + { + return false; + } + } + + // get player base class + final int currentBaseId = player.getBaseClass(); + final ClassId baseCID = ClassId.values()[currentBaseId]; + + // we need 2nd occupation ID + final int baseClassId; + if (baseCID.level() > 2) + { + baseClassId = baseCID.getParent().ordinal(); + } + else + { + baseClassId = currentBaseId; + } + + Set availSubs = PlayerClass.values()[baseClassId].getAvailableSubclasses(player); + if ((availSubs == null) || availSubs.isEmpty()) + { + return false; + } + + boolean found = false; + for (PlayerClass pclass : availSubs) + { + if (pclass.ordinal() == classId) + { + found = true; + break; + } + } + + return found; + } + + protected boolean checkVillageMasterRace(PlayerClass pclass) + { + return true; + } + + protected boolean checkVillageMasterTeachType(PlayerClass pclass) + { + return true; + } + + /* + * Returns true if this classId allowed for master + */ + public final boolean checkVillageMaster(int classId) + { + return checkVillageMaster(PlayerClass.values()[classId]); + } + + /* + * Returns true if this PlayerClass is allowed for master + */ + public final boolean checkVillageMaster(PlayerClass pclass) + { + if (Config.ALT_GAME_SUBCLASS_EVERYWHERE) + { + return true; + } + + return checkVillageMasterRace(pclass) && checkVillageMasterTeachType(pclass); + } + + private static final String formatClassForDisplay(PlayerClass className) + { + String classNameStr = className.toString(); + char[] charArray = classNameStr.toCharArray(); + + for (int i = 1; i < charArray.length; i++) + { + if (Character.isUpperCase(charArray[i])) + { + classNameStr = classNameStr.substring(0, i) + " " + classNameStr.substring(i); + } + } + + return classNameStr; + } + + private static final Iterator iterSubClasses(L2PcInstance player) + { + return player.getSubClasses().values().iterator(); + } + + private static final void dissolveClan(L2PcInstance player, int clanId) + { + if (!player.isClanLeader()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_NOT_AUTHORIZED_TO_DO_THAT)); + return; + } + + final L2Clan clan = player.getClan(); + if (clan.getAllyId() != 0) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_DISPERSE_THE_CLANS_IN_ALLY)); + return; + } + if (clan.isAtWar()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_DISSOLVE_WHILE_IN_WAR)); + return; + } + if ((clan.getHasCastle() != 0) || (clan.getHasHideout() != 0) || (clan.getHasFort() != 0)) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_DISSOLVE_WHILE_OWNING_CLAN_HALL_OR_CASTLE)); + return; + } + + for (Castle castle : CastleManager.getInstance().getCastles()) + { + if (SiegeManager.getInstance().checkIsRegistered(clan, castle.getCastleId())) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_DISSOLVE_WHILE_IN_SIEGE)); + return; + } + } + for (Fort fort : FortManager.getInstance().getForts()) + { + if (FortSiegeManager.getInstance().checkIsRegistered(clan, fort.getFortId())) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_DISSOLVE_WHILE_IN_SIEGE)); + return; + } + } + + if (player.isInsideZone(L2Character.ZONE_SIEGE)) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_DISSOLVE_WHILE_IN_SIEGE)); + return; + } + if (clan.getDissolvingExpiryTime() > System.currentTimeMillis()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.DISSOLUTION_IN_PROGRESS)); + return; + } + + clan.setDissolvingExpiryTime(System.currentTimeMillis() + (Config.ALT_CLAN_DISSOLVE_DAYS * 86400000L)); // 24*60*60*1000 = 86400000 + clan.updateClanInDB(); + + ClanTable.getInstance().scheduleRemoveClan(clan.getClanId()); + + // The clan leader should take the XP penalty of a full death. + player.deathPenalty(false, false, false); + } + + private static final void recoverClan(L2PcInstance player, int clanId) + { + if (!player.isClanLeader()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_NOT_AUTHORIZED_TO_DO_THAT)); + return; + } + + final L2Clan clan = player.getClan(); + clan.setDissolvingExpiryTime(0); + clan.updateClanInDB(); + } + + private static final void changeClanLeader(L2PcInstance player, String target) + { + if (!player.isClanLeader()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_NOT_AUTHORIZED_TO_DO_THAT)); + return; + } + if (player.getName().equalsIgnoreCase(target)) + { + return; + } + /* + * Until proper clan leader change support is done, this is a little exploit fix (leader, while fliying wyvern changes clan leader and the new leader can ride the wyvern too) DrHouse + */ + if (player.isFlying()) + { + player.sendMessage("Please, stop flying"); + return; + } + + final L2Clan clan = player.getClan(); + + final L2ClanMember member = clan.getClanMember(target); + if (member == null) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DOES_NOT_EXIST); + sm.addString(target); + player.sendPacket(sm); + sm = null; + return; + } + if (!member.isOnline()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.INVITED_USER_NOT_ONLINE)); + return; + } + clan.setNewLeader(member); + } + + private static final void createSubPledge(L2PcInstance player, String clanName, String leaderName, int pledgeType, int minClanLvl) + { + if (!player.isClanLeader()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_NOT_AUTHORIZED_TO_DO_THAT)); + return; + } + + final L2Clan clan = player.getClan(); + if (clan.getLevel() < minClanLvl) + { + if (pledgeType == L2Clan.SUBUNIT_ACADEMY) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_DO_NOT_MEET_CRITERIA_IN_ORDER_TO_CREATE_A_CLAN_ACADEMY)); + } + else + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_DO_NOT_MEET_CRITERIA_IN_ORDER_TO_CREATE_A_MILITARY_UNIT)); + } + + return; + } + if (!Util.isAlphaNumeric(clanName) || (2 > clanName.length())) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CLAN_NAME_INCORRECT)); + return; + } + if (clanName.length() > 16) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CLAN_NAME_TOO_LONG)); + return; + } + + for (L2Clan tempClan : ClanTable.getInstance().getClans()) + { + if (tempClan.getSubPledge(clanName) != null) + { + if (pledgeType == L2Clan.SUBUNIT_ACADEMY) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_ALREADY_EXISTS); + sm.addString(clanName); + player.sendPacket(sm); + sm = null; + } + else + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ANOTHER_MILITARY_UNIT_IS_ALREADY_USING_THAT_NAME)); + } + + return; + } + } + + if (pledgeType != L2Clan.SUBUNIT_ACADEMY) + { + if ((clan.getClanMember(leaderName) == null) || (clan.getClanMember(leaderName).getPledgeType() != 0)) + { + if (pledgeType >= L2Clan.SUBUNIT_KNIGHT1) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CAPTAIN_OF_ORDER_OF_KNIGHTS_CANNOT_BE_APPOINTED)); + } + else if (pledgeType >= L2Clan.SUBUNIT_ROYAL1) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CAPTAIN_OF_ROYAL_GUARD_CANNOT_BE_APPOINTED)); + } + + return; + } + } + + final int leaderId = pledgeType != L2Clan.SUBUNIT_ACADEMY ? clan.getClanMember(leaderName).getObjectId() : 0; + + if (clan.createSubPledge(player, pledgeType, leaderId, clanName) == null) + { + return; + } + + SystemMessage sm; + if (pledgeType == L2Clan.SUBUNIT_ACADEMY) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.THE_S1S_CLAN_ACADEMY_HAS_BEEN_CREATED); + sm.addString(player.getClan().getName()); + } + else if (pledgeType >= L2Clan.SUBUNIT_KNIGHT1) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.THE_KNIGHTS_OF_S1_HAVE_BEEN_CREATED); + sm.addString(player.getClan().getName()); + } + else if (pledgeType >= L2Clan.SUBUNIT_ROYAL1) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.THE_ROYAL_GUARD_OF_S1_HAVE_BEEN_CREATED); + sm.addString(player.getClan().getName()); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.CLAN_CREATED); + } + player.sendPacket(sm); + + if (pledgeType != L2Clan.SUBUNIT_ACADEMY) + { + final L2ClanMember leaderSubPledge = clan.getClanMember(leaderName); + final L2PcInstance leaderPlayer = leaderSubPledge.getPlayerInstance(); + if (leaderPlayer != null) + { + leaderPlayer.setPledgeClass(leaderSubPledge.calculatePledgeClass(leaderPlayer)); + leaderPlayer.sendPacket(new UserInfo(leaderPlayer)); + leaderPlayer.sendPacket(new ExBrExtraUserInfo(leaderPlayer)); + } + } + } + + private static final void renameSubPledge(L2PcInstance player, int pledgeType, String pledgeName) + { + if (!player.isClanLeader()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_NOT_AUTHORIZED_TO_DO_THAT)); + return; + } + + final L2Clan clan = player.getClan(); + final SubPledge subPledge = player.getClan().getSubPledge(pledgeType); + + if (subPledge == null) + { + player.sendMessage("Pledge don't exists."); + return; + } + if (!Util.isAlphaNumeric(pledgeName) || (2 > pledgeName.length())) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CLAN_NAME_INCORRECT)); + return; + } + if (pledgeName.length() > 16) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CLAN_NAME_TOO_LONG)); + return; + } + + subPledge.setName(pledgeName); + clan.updateSubPledgeInDB(subPledge.getId()); + clan.broadcastClanStatus(); + player.sendMessage("Pledge name changed."); + } + + private static final void assignSubPledgeLeader(L2PcInstance player, String clanName, String leaderName) + { + if (!player.isClanLeader()) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_NOT_AUTHORIZED_TO_DO_THAT)); + return; + } + if (leaderName.length() > 16) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NAMING_CHARNAME_UP_TO_16CHARS)); + return; + } + if (player.getName().equals(leaderName)) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CAPTAIN_OF_ROYAL_GUARD_CANNOT_BE_APPOINTED)); + return; + } + + final L2Clan clan = player.getClan(); + final SubPledge subPledge = player.getClan().getSubPledge(clanName); + + if ((null == subPledge) || (subPledge.getId() == L2Clan.SUBUNIT_ACADEMY)) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CLAN_NAME_INCORRECT)); + return; + } + if ((clan.getClanMember(leaderName) == null) || (clan.getClanMember(leaderName).getPledgeType() != 0)) + { + if (subPledge.getId() >= L2Clan.SUBUNIT_KNIGHT1) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CAPTAIN_OF_ORDER_OF_KNIGHTS_CANNOT_BE_APPOINTED)); + } + else if (subPledge.getId() >= L2Clan.SUBUNIT_ROYAL1) + { + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CAPTAIN_OF_ROYAL_GUARD_CANNOT_BE_APPOINTED)); + } + + return; + } + + subPledge.setLeaderId(clan.getClanMember(leaderName).getObjectId()); + clan.updateSubPledgeInDB(subPledge.getId()); + + final L2ClanMember leaderSubPledge = clan.getClanMember(leaderName); + final L2PcInstance leaderPlayer = leaderSubPledge.getPlayerInstance(); + if (leaderPlayer != null) + { + leaderPlayer.setPledgeClass(leaderSubPledge.calculatePledgeClass(leaderPlayer)); + leaderPlayer.sendPacket(new UserInfo(leaderPlayer)); + leaderPlayer.sendPacket(new ExBrExtraUserInfo(leaderPlayer)); + } + + clan.broadcastClanStatus(); + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_BEEN_SELECTED_AS_CAPTAIN_OF_S2); + sm.addString(leaderName); + sm.addString(clanName); + clan.broadcastToOnlineMembers(sm); + sm = null; + } + + /** + * this displays PledgeSkillList to the player. + * @param player + */ + public static final void showPledgeSkillList(L2PcInstance player) + { + if (!player.isClanLeader()) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/NotClanLeader.htm"); + player.sendPacket(html); + player.sendPacket(ActionFailed.STATIC_PACKET); + return; + } + + final FastList skills = SkillTreesData.getInstance().getAvailablePledgeSkills(player.getClan()); + final AcquireSkillList asl = new AcquireSkillList(SkillType.Pledge); + int counts = 0; + + for (L2SkillLearn s : skills) + { + asl.addSkill(s.getSkillId(), s.getSkillLevel(), s.getSkillLevel(), s.getLevelUpSp(), s.getSocialClass()); + counts++; + } + // TODO: Missing some system message? + if (counts == 0) + { + if (player.getClan().getLevel() < 8) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.DO_NOT_HAVE_FURTHER_SKILLS_TO_LEARN_S1); + if (player.getClan().getLevel() < 5) + { + sm.addNumber(5); + } + else + { + sm.addNumber(player.getClan().getLevel() + 1); + } + player.sendPacket(sm); + } + else + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), "data/html/villagemaster/NoMoreSkills.htm"); + player.sendPacket(html); + } + } + else + { + player.sendPacket(asl); + } + player.sendPacket(ActionFailed.STATIC_PACKET); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterKamaelInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterKamaelInstance.java new file mode 100644 index 00000000..737919d2 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterKamaelInstance.java @@ -0,0 +1,81 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.base.PlayerClass; +import com.l2jserver.gameserver.model.base.Race; +import com.l2jserver.gameserver.model.quest.QuestState; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2VillageMasterKamaelInstance extends L2VillageMasterInstance +{ + public L2VillageMasterKamaelInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + } + + @Override + protected final String getSubClassMenu(Race pRace) + { + if (Config.ALT_GAME_SUBCLASS_EVERYWHERE || (pRace == Race.Kamael)) + { + return "data/html/villagemaster/SubClass.htm"; + } + + return "data/html/villagemaster/SubClass_NoKamael.htm"; + } + + @Override + protected final String getSubClassFail() + { + return "data/html/villagemaster/SubClass_Fail_Kamael.htm"; + } + + @Override + protected final boolean checkQuests(L2PcInstance player) + { + // Noble players can add subbclasses without quests + if (player.isNoble()) + { + return true; + } + + QuestState qs = player.getQuestState("234_FatesWhisper"); + if ((qs == null) || !qs.isCompleted()) + { + return false; + } + + qs = player.getQuestState("236_SeedsOfChaos"); + if ((qs == null) || !qs.isCompleted()) + { + return false; + } + + return true; + } + + @Override + protected final boolean checkVillageMasterRace(PlayerClass pclass) + { + if (pclass == null) + { + return false; + } + + return pclass.isOfRace(Race.Kamael); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterMysticInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterMysticInstance.java new file mode 100644 index 00000000..718f0747 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterMysticInstance.java @@ -0,0 +1,50 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.base.ClassType; +import com.l2jserver.gameserver.model.base.PlayerClass; +import com.l2jserver.gameserver.model.base.Race; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2VillageMasterMysticInstance extends L2VillageMasterInstance +{ + public L2VillageMasterMysticInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + } + + @Override + protected final boolean checkVillageMasterRace(PlayerClass pclass) + { + if (pclass == null) + { + return false; + } + + return pclass.isOfRace(Race.Human) || pclass.isOfRace(Race.Elf); + } + + @Override + protected final boolean checkVillageMasterTeachType(PlayerClass pclass) + { + if (pclass == null) + { + return false; + } + + return pclass.isOfType(ClassType.Mystic); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterOrcInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterOrcInstance.java new file mode 100644 index 00000000..966a8242 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterOrcInstance.java @@ -0,0 +1,38 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.base.PlayerClass; +import com.l2jserver.gameserver.model.base.Race; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2VillageMasterOrcInstance extends L2VillageMasterInstance +{ + public L2VillageMasterOrcInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + } + + @Override + protected final boolean checkVillageMasterRace(PlayerClass pclass) + { + if (pclass == null) + { + return false; + } + + return pclass.isOfRace(Race.Orc); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterPriestInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterPriestInstance.java new file mode 100644 index 00000000..da64d013 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2VillageMasterPriestInstance.java @@ -0,0 +1,50 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.base.ClassType; +import com.l2jserver.gameserver.model.base.PlayerClass; +import com.l2jserver.gameserver.model.base.Race; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public final class L2VillageMasterPriestInstance extends L2VillageMasterInstance +{ + public L2VillageMasterPriestInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + } + + @Override + protected final boolean checkVillageMasterRace(PlayerClass pclass) + { + if (pclass == null) + { + return false; + } + + return pclass.isOfRace(Race.Human) || pclass.isOfRace(Race.Elf); + } + + @Override + protected final boolean checkVillageMasterTeachType(PlayerClass pclass) + { + if (pclass == null) + { + return false; + } + + return pclass.isOfType(ClassType.Priest); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2WarehouseInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2WarehouseInstance.java new file mode 100644 index 00000000..aad1b941 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2WarehouseInstance.java @@ -0,0 +1,52 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2WarehouseInstance extends L2NpcInstance +{ + /** + * @param template + */ + public L2WarehouseInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2WarehouseInstance); + } + + @Override + public boolean isWarehouse() + { + return true; + } + + @Override + public String getHtmlPath(int npcId, int val) + { + String pom = ""; + + if (val == 0) + { + pom = "" + npcId; + } + else + { + pom = npcId + "-" + val; + } + + return "data/html/warehouse/" + pom + ".htm"; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2WeddingManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2WeddingManagerInstance.java new file mode 100644 index 00000000..2bf72264 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2WeddingManagerInstance.java @@ -0,0 +1,250 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.Announcements; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.instancemanager.CoupleManager; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.entity.Couple; +import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2WeddingManagerInstance extends L2Npc +{ + /** + * @author evill33t & squeezed + */ + public L2WeddingManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2WeddingManagerInstance); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + String filename = "data/html/mods/Wedding_start.htm"; + String replace = String.valueOf(Config.L2JMOD_WEDDING_PRICE); + + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%replace%", replace); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } + + @Override + public void onBypassFeedback(L2PcInstance player, String command) + { + // Standard message + String filename = "data/html/mods/Wedding_start.htm"; + String replace = ""; + + // If player has no partner + if (player.getPartnerId() == 0) + { + filename = "data/html/mods/Wedding_nopartner.htm"; + sendHtmlMessage(player, filename, replace); + return; + } + L2PcInstance ptarget = L2World.getInstance().getPlayer(player.getPartnerId()); + + // Is partner online? + if ((ptarget == null) || !ptarget.isOnline()) + { + filename = "data/html/mods/Wedding_notfound.htm"; + sendHtmlMessage(player, filename, replace); + return; + } + + // Is already married? + if (player.isMarried()) + { + filename = "data/html/mods/Wedding_already.htm"; + sendHtmlMessage(player, filename, replace); + return; + } + else if (player.isMarryAccepted()) + { + filename = "data/html/mods/Wedding_waitforpartner.htm"; + sendHtmlMessage(player, filename, replace); + return; + } + else if (command.startsWith("AcceptWedding")) + { + // Check for Formal Wear + if (!wearsFormalWear(player, ptarget)) + { + filename = "data/html/mods/Wedding_noformal.htm"; + sendHtmlMessage(ptarget, filename, replace); + sendHtmlMessage(player, filename, replace); + return; + } + + // Check and reduce wedding price + if ((player.getAdena() < Config.L2JMOD_WEDDING_PRICE) || (ptarget.getAdena() < Config.L2JMOD_WEDDING_PRICE)) + { + filename = "data/html/mods/Wedding_adena.htm"; + replace = String.valueOf(Config.L2JMOD_WEDDING_PRICE); + sendHtmlMessage(ptarget, filename, replace); + sendHtmlMessage(player, filename, replace); + return; + } + player.reduceAdena("Wedding", Config.L2JMOD_WEDDING_PRICE, player.getLastFolkNPC(), true); + ptarget.reduceAdena("Wedding", Config.L2JMOD_WEDDING_PRICE, player.getLastFolkNPC(), true); + + // Accept the wedding request + player.setMarryAccepted(true); + Couple couple = CoupleManager.getInstance().getCouple(player.getCoupleId()); + couple.marry(); + + // Messages to the couple + player.sendMessage("Congratulations you are married!"); + player.setMarried(true); + player.setMarryRequest(false); + ptarget.sendMessage("Congratulations you are married!"); + ptarget.setMarried(true); + ptarget.setMarryRequest(false); + + // Wedding march + MagicSkillUse MSU = new MagicSkillUse(player, player, 2230, 1, 1, 0); + player.broadcastPacket(MSU); + MSU = new MagicSkillUse(ptarget, ptarget, 2230, 1, 1, 0); + ptarget.broadcastPacket(MSU); + + // Fireworks + L2Skill skill = SkillTable.FrequentSkill.LARGE_FIREWORK.getSkill(); + if (skill != null) + { + MSU = new MagicSkillUse(player, player, 2025, 1, 1, 0); + player.sendPacket(MSU); + player.broadcastPacket(MSU); + player.useMagic(skill, false, false); + + MSU = new MagicSkillUse(ptarget, ptarget, 2025, 1, 1, 0); + ptarget.sendPacket(MSU); + ptarget.broadcastPacket(MSU); + ptarget.useMagic(skill, false, false); + + } + + Announcements.getInstance().announceToAll("Congratulations to " + player.getName() + " and " + ptarget.getName() + "! They have been married."); + + filename = "data/html/mods/Wedding_accepted.htm"; + sendHtmlMessage(ptarget, filename, replace); + sendHtmlMessage(player, filename, replace); + + return; + } + else if (command.startsWith("DeclineWedding")) + { + player.setMarryRequest(false); + ptarget.setMarryRequest(false); + player.setMarryAccepted(false); + ptarget.setMarryAccepted(false); + + player.sendMessage("You declined your partner's marriage request."); + ptarget.sendMessage("Your partner declined your marriage request."); + + filename = "data/html/mods/Wedding_declined.htm"; + sendHtmlMessage(ptarget, filename, replace); + sendHtmlMessage(player, filename, replace); + + return; + } + else if (player.isMarryRequest()) + { + // Check for Formal Wear + if (!wearsFormalWear(player, ptarget)) + { + filename = "data/html/mods/Wedding_noformal.htm"; + sendHtmlMessage(ptarget, filename, replace); + sendHtmlMessage(player, filename, replace); + return; + } + filename = "data/html/mods/Wedding_ask.htm"; + player.setMarryRequest(false); + ptarget.setMarryRequest(false); + replace = ptarget.getName(); + sendHtmlMessage(player, filename, replace); + return; + } + else if (command.startsWith("AskWedding")) + { + // Check for Formal Wear + if (!wearsFormalWear(player, ptarget)) + { + filename = "data/html/mods/Wedding_noformal.htm"; + sendHtmlMessage(ptarget, filename, replace); + sendHtmlMessage(player, filename, replace); + return; + } + + player.setMarryAccepted(true); + ptarget.setMarryRequest(true); + + filename = "data/html/mods/Wedding_ask.htm"; + replace = player.getName(); + sendHtmlMessage(ptarget, filename, replace); + + filename = "data/html/mods/Wedding_requested.htm"; + replace = ptarget.getName(); + sendHtmlMessage(player, filename, replace); + + return; + } + sendHtmlMessage(player, filename, replace); + } + + /** + * Are both partners wearing formal wear? If Formal Wear check is disabled, returns True in any case.
    + * @param p1 L2PcInstance + * @param p2 L2PcInstance + * @return boolean + */ + private boolean wearsFormalWear(L2PcInstance p1, L2PcInstance p2) + { + if (Config.L2JMOD_WEDDING_FORMALWEAR) + { + L2ItemInstance fw1 = p1.getChestArmorInstance(); + L2ItemInstance fw2 = p2.getChestArmorInstance(); + + if ((fw1 == null) || (fw2 == null) || (fw1.getItemId() != 6408) || (fw2.getItemId() != 6408)) + { + return false; + } + return true; + } + + return true; + } + + private void sendHtmlMessage(L2PcInstance player, String filename, String replace) + { + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%replace%", replace); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2WyvernManagerInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2WyvernManagerInstance.java new file mode 100644 index 00000000..be5e6622 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2WyvernManagerInstance.java @@ -0,0 +1,57 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class L2WyvernManagerInstance extends L2Npc +{ + public L2WyvernManagerInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2WyvernManagerInstance); + } + + @Override + public void showChatWindow(L2PcInstance player) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + String filename = "data/html/wyvernmanager/wyvernmanager-no.htm"; + + if (isOwnerClan(player)) + { + filename = "data/html/wyvernmanager/wyvernmanager.htm"; // Owner message window + } + + NpcHtmlMessage html = new NpcHtmlMessage(1); + html.setFile(player.getHtmlPrefix(), filename); + html.replace("%objectId%", String.valueOf(getObjectId())); + html.replace("%npcname%", getName()); + player.sendPacket(html); + } + + public boolean isOwnerClan(L2PcInstance player) + { + return true; + } + + public boolean isInSiege() + { + return false; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2XmassTreeInstance.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2XmassTreeInstance.java new file mode 100644 index 00000000..cee6123a --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/instance/L2XmassTreeInstance.java @@ -0,0 +1,113 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.instance; + +import java.util.Collection; +import java.util.concurrent.ScheduledFuture; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +/** + * @author Drunkard Zabb0x Lets drink2code! + */ +public class L2XmassTreeInstance extends L2Npc +{ + public static final int SPECIAL_TREE_ID = 13007; + protected ScheduledFuture _aiTask; + + class XmassAI implements Runnable + { + private final L2XmassTreeInstance _caster; + private final L2Skill _skill; + + protected XmassAI(L2XmassTreeInstance caster, L2Skill skill) + { + _caster = caster; + _skill = skill; + } + + @Override + public void run() + { + if ((_skill == null) || _caster.isInsideZone(ZONE_PEACE)) + { + _caster._aiTask.cancel(false); + _caster._aiTask = null; + return; + } + Collection plrs = getKnownList().getKnownPlayersInRadius(200); + for (L2PcInstance player : plrs) + { + if (player.getFirstEffect(_skill.getId()) == null) + { + _skill.getEffects(player, player); + } + } + } + } + + public L2XmassTreeInstance(int objectId, L2NpcTemplate template) + { + super(objectId, template); + setInstanceType(InstanceType.L2XmassTreeInstance); + if (template.npcId == SPECIAL_TREE_ID) + { + _aiTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new XmassAI(this, SkillTable.getInstance().getInfo(2139, 1)), 3000, 3000); + } + } + + @Override + public void deleteMe() + { + if (_aiTask != null) + { + _aiTask.cancel(true); + } + + super.deleteMe(); + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + return 900; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.L2Object#isAttackable() + */ + @Override + public boolean isAutoAttackable(L2Character attacker) + { + return false; + } + + /** + * @see com.l2jserver.gameserver.model.actor.L2Npc#onAction(com.l2jserver.gameserver.model.actor.instance.L2PcInstance, boolean) + */ + @Override + public void onAction(L2PcInstance player, boolean interact) + { + player.sendPacket(ActionFailed.STATIC_PACKET); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/AttackableKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/AttackableKnownList.java new file mode 100644 index 00000000..d5da2162 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/AttackableKnownList.java @@ -0,0 +1,93 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import java.util.Collection; + +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +public class AttackableKnownList extends NpcKnownList +{ + public AttackableKnownList(L2Attackable activeChar) + { + super(activeChar); + } + + @Override + protected boolean removeKnownObject(L2Object object, boolean forget) + { + if (!super.removeKnownObject(object, forget)) + { + return false; + } + + // Remove the L2Object from the _aggrolist of the L2Attackable + if (object instanceof L2Character) + { + getActiveChar().getAggroList().remove(object); + } + // Set the L2Attackable Intention to AI_INTENTION_IDLE + final Collection known = getKnownPlayers().values(); + + // FIXME: This is a temporary solution + if (getActiveChar().hasAI() && ((known == null) || known.isEmpty())) + { + getActiveChar().getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null); + } + + return true; + } + + @Override + public L2Attackable getActiveChar() + { + return (L2Attackable) super.getActiveChar(); + } + + @Override + public int getDistanceToForgetObject(L2Object object) + { + if (getActiveChar().getAggroList().get(object) != null) + { + return 3000; + } + + return Math.min(2200, 2 * getDistanceToWatchObject(object)); + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + if ((object instanceof L2NpcInstance) || !(object instanceof L2Character)) + { + return 0; + } + + if (object instanceof L2Playable) + { + return object.getKnownList().getDistanceToWatchObject(getActiveObject()); + } + + int max = Math.max(300, Math.max(getActiveChar().getAggroRange(), Math.max(getActiveChar().getFactionRange(), getActiveChar().getEnemyRange()))); + + return max; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/CharKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/CharKnownList.java new file mode 100644 index 00000000..925551ec --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/CharKnownList.java @@ -0,0 +1,290 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.util.Util; + +public class CharKnownList extends ObjectKnownList +{ + private Map _knownPlayers; + private Map _knownSummons; + private Map _knownRelations; + + public CharKnownList(L2Character activeChar) + { + super(activeChar); + } + + @Override + public boolean addKnownObject(L2Object object) + { + if (!super.addKnownObject(object)) + { + return false; + } + if (object instanceof L2PcInstance) + { + getKnownPlayers().put(object.getObjectId(), (L2PcInstance) object); + getKnownRelations().put(object.getObjectId(), -1); + } + else if (object instanceof L2Summon) + { + getKnownSummons().put(object.getObjectId(), (L2Summon) object); + } + + return true; + } + + /** + * Return True if the L2PcInstance is in _knownPlayer of the L2Character.
    + *
    + * @param player The L2PcInstance to search in _knownPlayer + */ + public final boolean knowsThePlayer(L2PcInstance player) + { + return (getActiveChar() == player) || getKnownPlayers().containsKey(player.getObjectId()); + } + + /** Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attack or Cast and notify AI. */ + @Override + public final void removeAllKnownObjects() + { + super.removeAllKnownObjects(); + getKnownPlayers().clear(); + getKnownRelations().clear(); + getKnownSummons().clear(); + + // Set _target of the L2Character to null + // Cancel Attack or Cast + getActiveChar().setTarget(null); + + // Cancel AI Task + if (getActiveChar().hasAI()) + { + getActiveChar().setAI(null); + } + } + + @Override + protected boolean removeKnownObject(L2Object object, boolean forget) + { + if (!super.removeKnownObject(object, forget)) + { + return false; + } + + if (!forget) // on forget objects removed by iterator + { + if (object instanceof L2PcInstance) + { + getKnownPlayers().remove(object.getObjectId()); + getKnownRelations().remove(object.getObjectId()); + } + else if (object instanceof L2Summon) + { + getKnownSummons().remove(object.getObjectId()); + } + } + + // If object is targeted by the L2Character, cancel Attack or Cast + if (object == getActiveChar().getTarget()) + { + getActiveChar().setTarget(null); + } + + return true; + } + + @Override + public void forgetObjects(boolean fullCheck) + { + if (!fullCheck) + { + final Collection plrs = getKnownPlayers().values(); + final Iterator pIter = plrs.iterator(); + L2PcInstance player; + // synchronized (getKnownPlayers()) + { + while (pIter.hasNext()) + { + player = pIter.next(); + if (player == null) + { + pIter.remove(); + } + else if (!player.isVisible() || !Util.checkIfInShortRadius(getDistanceToForgetObject(player), getActiveObject(), player, true)) + { + pIter.remove(); + removeKnownObject(player, true); + getKnownRelations().remove(player.getObjectId()); + getKnownObjects().remove(player.getObjectId()); + } + } + } + + final Collection sums = getKnownSummons().values(); + final Iterator sIter = sums.iterator(); + L2Summon summon; + // synchronized (sums) + { + while (sIter.hasNext()) + { + summon = sIter.next(); + if (summon == null) + { + sIter.remove(); + } + else if (!summon.isVisible() || !Util.checkIfInShortRadius(getDistanceToForgetObject(summon), getActiveObject(), summon, true)) + { + sIter.remove(); + removeKnownObject(summon, true); + getKnownObjects().remove(summon.getObjectId()); + } + } + } + return; + } + // Go through knownObjects + final Collection objs = getKnownObjects().values(); + final Iterator oIter = objs.iterator(); + L2Object object; + // synchronized (getKnownObjects()) + { + while (oIter.hasNext()) + { + object = oIter.next(); + if (object == null) + { + oIter.remove(); + } + else if (!object.isVisible() || !Util.checkIfInShortRadius(getDistanceToForgetObject(object), getActiveObject(), object, true)) + { + oIter.remove(); + removeKnownObject(object, true); + + if (object instanceof L2PcInstance) + { + getKnownPlayers().remove(object.getObjectId()); + getKnownRelations().remove(object.getObjectId()); + } + else if (object instanceof L2Summon) + { + getKnownSummons().remove(object.getObjectId()); + } + } + } + } + } + + public L2Character getActiveChar() + { + return (L2Character) super.getActiveObject(); + } + + public Collection getKnownCharacters() + { + FastList result = new FastList<>(); + + final Collection objs = getKnownObjects().values(); + // synchronized (getKnownObjects()) + { + for (L2Object obj : objs) + { + if (obj instanceof L2Character) + { + result.add((L2Character) obj); + } + } + } + return result; + } + + public Collection getKnownCharactersInRadius(long radius) + { + FastList result = new FastList<>(); + + final Collection objs = getKnownObjects().values(); + // synchronized (getKnownObjects()) + { + for (L2Object obj : objs) + { + if (obj instanceof L2Character) + { + if (Util.checkIfInRange((int) radius, getActiveChar(), obj, true)) + { + result.add((L2Character) obj); + } + } + } + } + return result; + } + + public final Map getKnownPlayers() + { + if (_knownPlayers == null) + { + _knownPlayers = new FastMap().shared(); + } + return _knownPlayers; + } + + public final Map getKnownRelations() + { + if (_knownRelations == null) + { + _knownRelations = new FastMap().shared(); + } + return _knownRelations; + } + + public final Map getKnownSummons() + { + if (_knownSummons == null) + { + _knownSummons = new FastMap().shared(); + } + return _knownSummons; + } + + public final Collection getKnownPlayersInRadius(long radius) + { + FastList result = new FastList<>(); + + final Collection plrs = getKnownPlayers().values(); + // synchronized (getKnownPlayers()) + { + for (L2PcInstance player : plrs) + { + if (Util.checkIfInRange((int) radius, getActiveChar(), player, true)) + { + result.add(player); + } + } + } + return result; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/DecoyKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/DecoyKnownList.java new file mode 100644 index 00000000..5c8baa94 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/DecoyKnownList.java @@ -0,0 +1,48 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Decoy; + +public class DecoyKnownList extends CharKnownList +{ + public DecoyKnownList(L2Decoy activeChar) + { + super(activeChar); + } + + @Override + public final L2Decoy getActiveChar() + { + return (L2Decoy) super.getActiveChar(); + } + + @Override + public int getDistanceToForgetObject(L2Object object) + { + if ((object == getActiveChar().getOwner()) || (object == getActiveChar().getTarget())) + { + return 6000; + } + return 3000; + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + return 1500; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/DefenderKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/DefenderKnownList.java new file mode 100644 index 00000000..8a560bd5 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/DefenderKnownList.java @@ -0,0 +1,88 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance; +import com.l2jserver.gameserver.model.actor.instance.L2FortCommanderInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.entity.Castle; +import com.l2jserver.gameserver.model.entity.Fort; + +public class DefenderKnownList extends AttackableKnownList +{ + // ========================================================= + // Data Field + + // ========================================================= + // Constructor + public DefenderKnownList(L2DefenderInstance activeChar) + { + super(activeChar); + } + + // ========================================================= + // Method - Public + @Override + public boolean addKnownObject(L2Object object) + { + if (!super.addKnownObject(object)) + { + return false; + } + + Castle castle = getActiveChar().getCastle(); + Fort fortress = getActiveChar().getFort(); + // Check if siege is in progress + if (((fortress != null) && fortress.getZone().isActive()) || ((castle != null) && castle.getZone().isActive())) + { + L2PcInstance player = null; + if (object instanceof L2PcInstance) + { + player = (L2PcInstance) object; + } + else if (object instanceof L2Summon) + { + player = ((L2Summon) object).getOwner(); + } + int activeSiegeId = (fortress != null ? fortress.getFortId() : (castle != null ? castle.getCastleId() : 0)); + + // Check if player is an enemy of this defender npc + if ((player != null) && (((player.getSiegeState() == 2) && !player.isRegisteredOnThisSiegeField(activeSiegeId)) || ((player.getSiegeState() == 1) && !TerritoryWarManager.getInstance().isAllyField(player, activeSiegeId)) || (player.getSiegeState() == 0))) + { + if (getActiveChar().getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE) + { + getActiveChar().getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null); + } + } + } + return true; + } + + // ========================================================= + // Property - Public + @Override + public final L2DefenderInstance getActiveChar() + { + if (super.getActiveChar() instanceof L2FortCommanderInstance) + { + return (L2FortCommanderInstance) super.getActiveChar(); + } + return (L2DefenderInstance) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/DoorKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/DoorKnownList.java new file mode 100644 index 00000000..6a371e7d --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/DoorKnownList.java @@ -0,0 +1,76 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +public class DoorKnownList extends CharKnownList +{ + // ========================================================= + // Data Field + + // ========================================================= + // Constructor + public DoorKnownList(L2DoorInstance activeChar) + { + super(activeChar); + } + + // ========================================================= + // Method - Public + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + @Override + public final L2DoorInstance getActiveChar() + { + return (L2DoorInstance) super.getActiveChar(); + } + + @Override + public int getDistanceToForgetObject(L2Object object) + { + if (object instanceof L2DefenderInstance) + { + return 800; + } + if (!(object instanceof L2PcInstance)) + { + return 0; + } + + return 4000; + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + if (object instanceof L2DefenderInstance) + { + return 600; + } + if (!(object instanceof L2PcInstance)) + { + return 0; + } + return 3000; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/FriendlyMobKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/FriendlyMobKnownList.java new file mode 100644 index 00000000..8c342540 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/FriendlyMobKnownList.java @@ -0,0 +1,87 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.ai.CtrlEvent; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2FriendlyMobInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +public class FriendlyMobKnownList extends AttackableKnownList +{ + public FriendlyMobKnownList(L2FriendlyMobInstance activeChar) + { + super(activeChar); + } + + @Override + public boolean addKnownObject(L2Object object) + { + if (!super.addKnownObject(object)) + { + return false; + } + + if ((object instanceof L2PcInstance) && (getActiveChar().getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)) + { + getActiveChar().getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null); + } + + return true; + } + + @Override + protected boolean removeKnownObject(L2Object object, boolean forget) + { + if (!super.removeKnownObject(object, forget)) + { + return false; + } + + if (!(object instanceof L2Character)) + { + return true; + } + + if (getActiveChar().hasAI()) + { + getActiveChar().getAI().notifyEvent(CtrlEvent.EVT_FORGET_OBJECT, object); + if (getActiveChar().getTarget() == (L2Character) object) + { + getActiveChar().setTarget(null); + } + } + + if (getActiveChar().isVisible() && getKnownPlayers().isEmpty() && getKnownSummons().isEmpty()) + { + getActiveChar().clearAggroList(); + // removeAllKnownObjects(); + if (getActiveChar().hasAI()) + { + getActiveChar().getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null); + } + } + + return true; + } + + @Override + public final L2FriendlyMobInstance getActiveChar() + { + return (L2FriendlyMobInstance) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/GuardKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/GuardKnownList.java new file mode 100644 index 00000000..fdea7987 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/GuardKnownList.java @@ -0,0 +1,109 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import java.util.logging.Logger; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.instance.L2GuardInstance; +import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +public class GuardKnownList extends AttackableKnownList +{ + private static final Logger _log = Logger.getLogger(GuardKnownList.class.getName()); + + public GuardKnownList(L2GuardInstance activeChar) + { + super(activeChar); + } + + @Override + public boolean addKnownObject(L2Object object) + { + if (!super.addKnownObject(object)) + { + return false; + } + + if (object instanceof L2PcInstance) + { + // Check if the object added is a L2PcInstance that owns Karma + if (((L2PcInstance) object).getKarma() > 0) + { + if (Config.DEBUG) + { + _log.fine(getActiveChar().getObjectId() + ": PK " + object.getObjectId() + " entered scan range"); + } + + // Set the L2GuardInstance Intention to AI_INTENTION_ACTIVE + if (getActiveChar().getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE) + { + getActiveChar().getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null); + } + } + } + else if ((Config.GUARD_ATTACK_AGGRO_MOB && getActiveChar().isInActiveRegion()) && (object instanceof L2MonsterInstance)) + { + // Check if the object added is an aggressive L2MonsterInstance + if (((L2MonsterInstance) object).isAggressive()) + { + if (Config.DEBUG) + { + _log.fine(getActiveChar().getObjectId() + ": Aggressive mob " + object.getObjectId() + " entered scan range"); + } + + // Set the L2GuardInstance Intention to AI_INTENTION_ACTIVE + if (getActiveChar().getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE) + { + getActiveChar().getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null); + } + } + } + + return true; + } + + @Override + protected boolean removeKnownObject(L2Object object, boolean forget) + { + if (!super.removeKnownObject(object, forget)) + { + return false; + } + + // Check if the _aggroList of the L2GuardInstance is Empty + if (getActiveChar().noTarget()) + { + // removeAllKnownObjects(); + + // Set the L2GuardInstance to AI_INTENTION_IDLE + if (getActiveChar().hasAI()) + { + getActiveChar().getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null); + } + } + + return true; + } + + @Override + public final L2GuardInstance getActiveChar() + { + return (L2GuardInstance) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/MonsterKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/MonsterKnownList.java new file mode 100644 index 00000000..895086d1 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/MonsterKnownList.java @@ -0,0 +1,87 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.ai.CtrlEvent; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.ai.L2CharacterAI; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +public class MonsterKnownList extends AttackableKnownList +{ + public MonsterKnownList(L2MonsterInstance activeChar) + { + super(activeChar); + } + + @Override + public boolean addKnownObject(L2Object object) + { + if (!super.addKnownObject(object)) + { + return false; + } + + final L2CharacterAI ai = getActiveChar().getAI(); // force AI creation + + // Set the L2MonsterInstance Intention to AI_INTENTION_ACTIVE if the state was AI_INTENTION_IDLE + if ((object instanceof L2PcInstance) && (ai != null) && (ai.getIntention() == CtrlIntention.AI_INTENTION_IDLE)) + { + ai.setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null); + } + + return true; + } + + @Override + protected boolean removeKnownObject(L2Object object, boolean forget) + { + if (!super.removeKnownObject(object, forget)) + { + return false; + } + + if (!(object instanceof L2Character)) + { + return true; + } + + if (getActiveChar().hasAI()) + { + // Notify the L2MonsterInstance AI with EVT_FORGET_OBJECT + getActiveChar().getAI().notifyEvent(CtrlEvent.EVT_FORGET_OBJECT, object); + } + + if (getActiveChar().isVisible() && getKnownPlayers().isEmpty() && getKnownSummons().isEmpty()) + { + // Clear the _aggroList of the L2MonsterInstance + getActiveChar().clearAggroList(); + + // Remove all L2Object from _knownObjects and _knownPlayer of the L2MonsterInstance then cancel Attak or Cast and notify AI + // removeAllKnownObjects(); + } + + return true; + } + + @Override + public final L2MonsterInstance getActiveChar() + { + return (L2MonsterInstance) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/NpcKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/NpcKnownList.java new file mode 100644 index 00000000..48593972 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/NpcKnownList.java @@ -0,0 +1,82 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.instance.L2CabaleBufferInstance; +import com.l2jserver.gameserver.model.actor.instance.L2FestivalGuideInstance; +import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance; + +public class NpcKnownList extends CharKnownList +{ + // ========================================================= + // Data Field + + // ========================================================= + // Constructor + public NpcKnownList(L2Npc activeChar) + { + super(activeChar); + } + + // ========================================================= + // Method - Public + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + @Override + public L2Npc getActiveChar() + { + return (L2Npc) super.getActiveChar(); + } + + @Override + public int getDistanceToForgetObject(L2Object object) + { + return 2 * getDistanceToWatchObject(object); + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + if (object instanceof L2FestivalGuideInstance) + { + return 4000; + } + + if ((object instanceof L2NpcInstance) || !(object instanceof L2Character)) + { + return 0; + } + + if (object instanceof L2CabaleBufferInstance) + { + return 900; + } + + if (object instanceof L2Playable) + { + return 1500; + } + + return 500; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/NullKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/NullKnownList.java new file mode 100644 index 00000000..aeb0dd32 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/NullKnownList.java @@ -0,0 +1,60 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.model.L2Object; + +public class NullKnownList extends ObjectKnownList +{ + public NullKnownList(L2Object activeObject) + { + super(activeObject); + } + + @Override + public boolean addKnownObject(L2Object object) + { + return false; + } + + @Override + public L2Object getActiveObject() + { + return super.getActiveObject(); + } + + @Override + public int getDistanceToForgetObject(L2Object object) + { + return 0; + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + return 0; + } + + @Override + public void removeAllKnownObjects() + { + } + + @Override + protected boolean removeKnownObject(L2Object object, boolean forget) + { + return false; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/ObjectKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/ObjectKnownList.java new file mode 100644 index 00000000..7258cfb9 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/ObjectKnownList.java @@ -0,0 +1,229 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import javolution.util.FastMap; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2WorldRegion; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.util.Util; + +public class ObjectKnownList +{ + private final L2Object _activeObject; + private Map _knownObjects; + + public ObjectKnownList(L2Object activeObject) + { + _activeObject = activeObject; + } + + public boolean addKnownObject(L2Object object) + { + if (object == null) + { + return false; + } + + // Instance -1 is for GMs that can see everything on all instances + if ((getActiveObject().getInstanceId() != -1) && (object.getInstanceId() != getActiveObject().getInstanceId())) + { + return false; + } + + // Check if the object is an L2PcInstance in ghost mode + if ((object instanceof L2PcInstance) && ((L2PcInstance) object).getAppearance().isGhost()) + { + return false; + } + + // Check if already know object + if (knowsObject(object)) + { + return false; + } + + // Check if object is not inside distance to watch object + if (!Util.checkIfInShortRadius(getDistanceToWatchObject(object), getActiveObject(), object, true)) + { + return false; + } + + return (getKnownObjects().put(object.getObjectId(), object) == null); + } + + public final boolean knowsObject(L2Object object) + { + if (object == null) + { + return false; + } + + return (getActiveObject() == object) || getKnownObjects().containsKey(object.getObjectId()); + } + + /** Remove all L2Object from _knownObjects */ + public void removeAllKnownObjects() + { + getKnownObjects().clear(); + } + + public final boolean removeKnownObject(L2Object object) + { + return removeKnownObject(object, false); + } + + protected boolean removeKnownObject(L2Object object, boolean forget) + { + if (object == null) + { + return false; + } + + if (forget) + { + return true; + } + + return getKnownObjects().remove(object.getObjectId()) != null; + } + + // used only in Config.MOVE_BASED_KNOWNLIST and does not support guards seeing + // moving monsters + public final void findObjects() + { + final L2WorldRegion region = getActiveObject().getWorldRegion(); + if (region == null) + { + return; + } + + if (getActiveObject() instanceof L2Playable) + { + for (L2WorldRegion regi : region.getSurroundingRegions()) // offer members of this and surrounding regions + { + Collection vObj = regi.getVisibleObjects().values(); + // synchronized (KnownListUpdateTaskManager.getInstance().getSync()) + { + // synchronized (regi.getVisibleObjects()) + { + for (L2Object _object : vObj) + { + if (_object != getActiveObject()) + { + addKnownObject(_object); + if (_object instanceof L2Character) + { + _object.getKnownList().addKnownObject(getActiveObject()); + } + } + } + } + } + } + } + else if (getActiveObject() instanceof L2Character) + { + for (L2WorldRegion regi : region.getSurroundingRegions()) // offer members of this and surrounding regions + { + if (regi.isActive()) + { + Collection vPls = regi.getVisiblePlayable().values(); + // synchronized (KnownListUpdateTaskManager.getInstance().getSync()) + { + // synchronized (regi.getVisiblePlayable()) + { + for (L2Object _object : vPls) + { + if (_object != getActiveObject()) + { + addKnownObject(_object); + } + } + } + } + } + } + } + } + + // Remove invisible and too far L2Object from _knowObject and if necessary from _knownPlayers of the L2Character + public void forgetObjects(boolean fullCheck) + { + // synchronized (KnownListUpdateTaskManager.getInstance().getSync()) + { + // Go through knownObjects + final Collection objs = getKnownObjects().values(); + final Iterator oIter = objs.iterator(); + L2Object object; + // synchronized (getKnownObjects()) + { + while (oIter.hasNext()) + { + object = oIter.next(); + if (object == null) + { + oIter.remove(); + continue; + } + + if (!fullCheck && !(object instanceof L2Playable)) + { + continue; + } + + // Remove all objects invisible or too far + if (!object.isVisible() || !Util.checkIfInShortRadius(getDistanceToForgetObject(object), getActiveObject(), object, true)) + { + oIter.remove(); + removeKnownObject(object, true); + } + } + } + } + } + + public L2Object getActiveObject() + { + return _activeObject; + } + + public int getDistanceToForgetObject(L2Object object) + { + return 0; + } + + public int getDistanceToWatchObject(L2Object object) + { + return 0; + } + + /** Return the _knownObjects containing all L2Object known by the L2Character. */ + public final Map getKnownObjects() + { + if (_knownObjects == null) + { + _knownObjects = new FastMap().shared(); + } + return _knownObjects; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/PcKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/PcKnownList.java new file mode 100644 index 00000000..f8e4522e --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/PcKnownList.java @@ -0,0 +1,185 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.L2Vehicle; +import com.l2jserver.gameserver.model.actor.instance.L2AirShipInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.serverpackets.DeleteObject; +import com.l2jserver.gameserver.network.serverpackets.SpawnItem; + +public class PcKnownList extends PlayableKnownList +{ + public PcKnownList(L2PcInstance activeChar) + { + super(activeChar); + } + + /** + * Add a visible L2Object to L2PcInstance _knownObjects and _knownPlayer (if necessary) and send Server-Client Packets needed to inform the L2PcInstance of its state and actions in progress.
    + *
    + * object is a L2ItemInstance :
    + *
    + *
  • Send Server-Client Packet DropItem/SpawnItem to the L2PcInstance

  • + *
    + * object is a L2DoorInstance :
    + *
    + *
  • Send Server-Client Packets DoorInfo and DoorStatusUpdate to the L2PcInstance
  • Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance

  • + *
    + * object is a L2NpcInstance :
    + *
    + *
  • Send Server-Client Packet NpcInfo to the L2PcInstance
  • Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance

  • + *
    + * object is a L2Summon :
    + *
    + *
  • Send Server-Client Packet NpcInfo/PetItemList (if the L2PcInstance is the owner) to the L2PcInstance
  • Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance

  • + *
    + * object is a L2PcInstance :
    + *
    + *
  • Send Server-Client Packet CharInfo to the L2PcInstance
  • If the object has a private store, Send Server-Client Packet PrivateStoreMsgSell to the L2PcInstance
  • Send Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance

  • + *
    + * @param object The L2Object to add to _knownObjects and _knownPlayer + * @param dropper The L2Character who dropped the L2Object + */ + @Override + public boolean addKnownObject(L2Object object) + { + if (!super.addKnownObject(object)) + { + return false; + } + + if (object.getPoly().isMorphed() && object.getPoly().getPolyType().equals("item")) + { + // if (object.getPolytype().equals("item")) + getActiveChar().sendPacket(new SpawnItem(object)); + // else if (object.getPolytype().equals("npc")) + // sendPacket(new NpcInfoPoly(object, this)); + } + else + { + object.sendInfo(getActiveChar()); + + if (object instanceof L2Character) + { + // Update the state of the L2Character object client side by sending Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance + L2Character obj = (L2Character) object; + if (obj.hasAI()) + { + obj.getAI().describeStateToPlayer(getActiveChar()); + } + } + } + + return true; + } + + /** + * Remove a L2Object from L2PcInstance _knownObjects and _knownPlayer (if necessary) and send Server-Client Packet DeleteObject to the L2PcInstance.
    + *
    + * @param object The L2Object to remove from _knownObjects and _knownPlayer + */ + @Override + protected boolean removeKnownObject(L2Object object, boolean forget) + { + if (!super.removeKnownObject(object, forget)) + { + return false; + } + + if (object instanceof L2AirShipInstance) + { + if ((((L2AirShipInstance) object).getCaptainId() != 0) && (((L2AirShipInstance) object).getCaptainId() != getActiveChar().getObjectId())) + { + getActiveChar().sendPacket(new DeleteObject(((L2AirShipInstance) object).getCaptainId())); + } + if (((L2AirShipInstance) object).getHelmObjectId() != 0) + { + getActiveChar().sendPacket(new DeleteObject(((L2AirShipInstance) object).getHelmObjectId())); + } + } + + // Send Server-Client Packet DeleteObject to the L2PcInstance + getActiveChar().sendPacket(new DeleteObject(object)); + + if (Config.CHECK_KNOWN && (object instanceof L2Npc) && getActiveChar().isGM()) + { + getActiveChar().sendMessage("Removed NPC: " + ((L2Npc) object).getName()); + } + + return true; + } + + @Override + public final L2PcInstance getActiveChar() + { + return (L2PcInstance) super.getActiveChar(); + } + + @Override + public int getDistanceToForgetObject(L2Object object) + { + if (object instanceof L2Vehicle) + { + return 10000; + } + + // when knownlist grows, the distance to forget should be at least + // the same as the previous watch range, or it becomes possible that + // extra charinfo packets are being sent (watch-forget-watch-forget) + final int knownlistSize = getKnownObjects().size(); + if (knownlistSize <= 25) + { + return 4000; + } + if (knownlistSize <= 35) + { + return 3500; + } + if (knownlistSize <= 70) + { + return 2910; + } + return 2310; + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + if (object instanceof L2Vehicle) + { + return 8000; + } + + final int knownlistSize = getKnownObjects().size(); + if (knownlistSize <= 25) + { + return 3400; // empty field + } + if (knownlistSize <= 35) + { + return 2900; + } + if (knownlistSize <= 70) + { + return 2300; + } + return 1700; // Siege, TOI, city + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/PlayableKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/PlayableKnownList.java new file mode 100644 index 00000000..b3248b64 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/PlayableKnownList.java @@ -0,0 +1,44 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.model.actor.L2Playable; + +public class PlayableKnownList extends CharKnownList +{ + // ========================================================= + // Data Field + + // ========================================================= + // Constructor + public PlayableKnownList(L2Playable activeChar) + { + super(activeChar); + } + + // ========================================================= + // Method - Public + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + @Override + public L2Playable getActiveChar() + { + return (L2Playable) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/RaceManagerKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/RaceManagerKnownList.java new file mode 100644 index 00000000..b24bb2af --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/RaceManagerKnownList.java @@ -0,0 +1,88 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import java.util.logging.Logger; + +import com.l2jserver.gameserver.MonsterRace; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2RaceManagerInstance; +import com.l2jserver.gameserver.network.serverpackets.DeleteObject; + +public class RaceManagerKnownList extends NpcKnownList +{ + protected static final Logger _log = Logger.getLogger(RaceManagerKnownList.class.getName()); + + // ========================================================= + // Data Field + + // ========================================================= + // Constructor + public RaceManagerKnownList(L2RaceManagerInstance activeChar) + { + super(activeChar); + } + + // ========================================================= + // Method - Public + @Override + public boolean addKnownObject(L2Object object) + { + if (!super.addKnownObject(object)) + { + return false; + } + + /* + * DONT KNOW WHY WE NEED THIS WHEN RACE MANAGER HAS A METHOD THAT BROADCAST TO ITS KNOW PLAYERS if (object instanceof L2PcInstance) { if (packet != null) ((L2PcInstance) object).sendPacket(packet); } + */ + + return true; + } + + @Override + protected boolean removeKnownObject(L2Object object, boolean forget) + { + if (!super.removeKnownObject(object, forget)) + { + return false; + } + + if (object instanceof L2PcInstance) + { + // _log.info("Sending delete monsrac info."); + DeleteObject obj = null; + for (int i = 0; i < 8; i++) + { + obj = new DeleteObject(MonsterRace.getInstance().getMonsters()[i]); + ((L2PcInstance) object).sendPacket(obj); + } + } + + return true; + } + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + @Override + public L2RaceManagerInstance getActiveChar() + { + return (L2RaceManagerInstance) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/StaticObjectKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/StaticObjectKnownList.java new file mode 100644 index 00000000..e6d0ccfe --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/StaticObjectKnownList.java @@ -0,0 +1,76 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2StaticObjectInstance; + +public class StaticObjectKnownList extends CharKnownList +{ + // ========================================================= + // Data Field + + // ========================================================= + // Constructor + public StaticObjectKnownList(L2StaticObjectInstance activeChar) + { + super(activeChar); + } + + // ========================================================= + // Method - Public + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + @Override + public final L2StaticObjectInstance getActiveChar() + { + return (L2StaticObjectInstance) super.getActiveChar(); + } + + @Override + public int getDistanceToForgetObject(L2Object object) + { + if (object instanceof L2DefenderInstance) + { + return 800; + } + if (!(object instanceof L2PcInstance)) + { + return 0; + } + + return 4000; + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + if (object instanceof L2DefenderInstance) + { + return 600; + } + if (!(object instanceof L2PcInstance)) + { + return 0; + } + return 2000; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/SummonKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/SummonKnownList.java new file mode 100644 index 00000000..736b245f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/SummonKnownList.java @@ -0,0 +1,61 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Summon; + +public class SummonKnownList extends PlayableKnownList +{ + // ========================================================= + // Data Field + + // ========================================================= + // Constructor + public SummonKnownList(L2Summon activeChar) + { + super(activeChar); + } + + // ========================================================= + // Method - Public + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + @Override + public final L2Summon getActiveChar() + { + return (L2Summon) super.getActiveChar(); + } + + @Override + public int getDistanceToForgetObject(L2Object object) + { + if ((object == getActiveChar().getOwner()) || (object == getActiveChar().getTarget())) + { + return 6000; + } + return 3000; + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + return 1500; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/TrapKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/TrapKnownList.java new file mode 100644 index 00000000..b884e978 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/TrapKnownList.java @@ -0,0 +1,49 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Trap; + +public class TrapKnownList extends CharKnownList +{ + public TrapKnownList(L2Trap activeChar) + { + super(activeChar); + } + + @Override + public final L2Trap getActiveChar() + { + return (L2Trap) super.getActiveChar(); + } + + @Override + public int getDistanceToForgetObject(L2Object object) + { + if ((object == getActiveChar().getOwner()) || (object == getActiveChar().getTarget())) + { + return 6000; + } + + return 3000; + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + return 1500; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/VehicleKnownList.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/VehicleKnownList.java new file mode 100644 index 00000000..f53330cb --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/knownlist/VehicleKnownList.java @@ -0,0 +1,49 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.knownlist; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +public class VehicleKnownList extends CharKnownList +{ + public VehicleKnownList(L2Character activeChar) + { + super(activeChar); + } + + @Override + public int getDistanceToForgetObject(L2Object object) + { + if (!(object instanceof L2PcInstance)) + { + return 0; + } + + return object.getKnownList().getDistanceToForgetObject(getActiveObject()); + } + + @Override + public int getDistanceToWatchObject(L2Object object) + { + if (!(object instanceof L2PcInstance)) + { + return 0; + } + + return object.getKnownList().getDistanceToWatchObject(getActiveObject()); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/poly/ObjectPoly.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/poly/ObjectPoly.java new file mode 100644 index 00000000..55cacc59 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/poly/ObjectPoly.java @@ -0,0 +1,76 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.poly; + +import com.l2jserver.gameserver.model.L2Object; + +public class ObjectPoly +{ + // ========================================================= + // Data Field + private final L2Object _activeObject; + private int _polyId; + private String _polyType; + + // ========================================================= + // Constructor + public ObjectPoly(L2Object activeObject) + { + _activeObject = activeObject; + } + + // ========================================================= + // Method - Public + public void setPolyInfo(String polyType, String polyId) + { + setPolyId(Integer.parseInt(polyId)); + setPolyType(polyType); + } + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + public final L2Object getActiveObject() + { + return _activeObject; + } + + public final boolean isMorphed() + { + return getPolyType() != null; + } + + public final int getPolyId() + { + return _polyId; + } + + public final void setPolyId(int value) + { + _polyId = value; + } + + public final String getPolyType() + { + return _polyType; + } + + public final void setPolyType(String value) + { + _polyType = value; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/position/CharPosition.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/position/CharPosition.java new file mode 100644 index 00000000..40ff3efd --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/position/CharPosition.java @@ -0,0 +1,58 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * http://www.gnu.org/copyleft/gpl.html + */ +package com.l2jserver.gameserver.model.actor.position; + +import com.l2jserver.gameserver.model.L2WorldRegion; +import com.l2jserver.gameserver.model.actor.L2Character; + +/** + * @author Erb + */ +public class CharPosition extends ObjectPosition +{ + // ========================================================= + // Constructor + public CharPosition(L2Character activeObject) + { + super(activeObject); + } + + @Override + protected void badCoords() + { + getActiveObject().decayMe(); + } + + @Override + public final void setWorldRegion(L2WorldRegion value) + { + if ((getWorldRegion() != null) && (getActiveObject() instanceof L2Character)) // confirm revalidation of old region's zones + { + if (value != null) + { + getWorldRegion().revalidateZones((L2Character) getActiveObject()); // at world region change + } + else + { + getWorldRegion().removeFromZones((L2Character) getActiveObject()); // at world region change + } + } + + super.setWorldRegion(value); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/position/ObjectPosition.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/position/ObjectPosition.java new file mode 100644 index 00000000..499ff97b --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/position/ObjectPosition.java @@ -0,0 +1,243 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.position; + +import java.util.logging.Logger; + +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.L2WorldRegion; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.util.Point3D; + +public class ObjectPosition +{ + private static final Logger _log = Logger.getLogger(ObjectPosition.class.getName()); + + // ========================================================= + // Data Field + private final L2Object _activeObject; + private int _heading = 0; + private Point3D _worldPosition; + private L2WorldRegion _worldRegion; // Object localization : Used for items/chars that are seen in the world + + // ========================================================= + // Constructor + public ObjectPosition(L2Object activeObject) + { + _activeObject = activeObject; + setWorldRegion(L2World.getInstance().getRegion(getWorldPosition())); + } + + // ========================================================= + // Method - Public + /** + * Set the x,y,z position of the L2Object and if necessary modify its _worldRegion.
    + *
    + * Assert :
    + *
    + *
  • _worldRegion != null

  • + *
    + * Example of use :
    + *
    + *
  • Update position during and after movement, or after teleport

  • + */ + public final void setXYZ(int x, int y, int z) + { + assert getWorldRegion() != null; + + setWorldPosition(x, y, z); + + try + { + if (L2World.getInstance().getRegion(getWorldPosition()) != getWorldRegion()) + { + updateWorldRegion(); + } + } + catch (Exception e) + { + _log.warning("Object Id at bad coords: (x: " + getX() + ", y: " + getY() + ", z: " + getZ() + ")."); + badCoords(); + } + } + + /** + * Called on setXYZ exception.
    + *
    + * Overwritten in :
    + *
    + *
  • CharPosition
  • PcPosition

  • + */ + protected void badCoords() + { + + } + + /** + * Set the x,y,z position of the L2Object and make it invisible.
    + *
    + * Concept :
    + *
    + * A L2Object is invisble if _hidden=true or _worldregion==null
    + *
    + * Assert :
    + *
    + *
  • _worldregion==null (L2Object is invisible)

  • + *
    + * Example of use :
    + *
    + *
  • Create a Door
  • Restore L2PcInstance

  • + */ + public final void setXYZInvisible(int x, int y, int z) + { + assert getWorldRegion() == null; + if (x > L2World.MAP_MAX_X) + { + x = L2World.MAP_MAX_X - 5000; + } + if (x < L2World.MAP_MIN_X) + { + x = L2World.MAP_MIN_X + 5000; + } + if (y > L2World.MAP_MAX_Y) + { + y = L2World.MAP_MAX_Y - 5000; + } + if (y < L2World.MAP_MIN_Y) + { + y = L2World.MAP_MIN_Y + 5000; + } + + setWorldPosition(x, y, z); + getActiveObject().setIsVisible(false); + } + + /** + * checks if current object changed its region, if so, update referencies + */ + public void updateWorldRegion() + { + if (!getActiveObject().isVisible()) + { + return; + } + + L2WorldRegion newRegion = L2World.getInstance().getRegion(getWorldPosition()); + if (newRegion != getWorldRegion()) + { + getWorldRegion().removeVisibleObject(getActiveObject()); + + setWorldRegion(newRegion); + + // Add the L2Oject spawn to _visibleObjects and if necessary to _allplayers of its L2WorldRegion + getWorldRegion().addVisibleObject(getActiveObject()); + } + } + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + public L2Object getActiveObject() + { + return _activeObject; + } + + public final int getHeading() + { + return _heading; + } + + public final void setHeading(int value) + { + _heading = value; + } + + /** Return the x position of the L2Object. */ + public final int getX() + { + return getWorldPosition().getX(); + } + + public final void setX(int value) + { + getWorldPosition().setX(value); + } + + /** Return the y position of the L2Object. */ + public final int getY() + { + return getWorldPosition().getY(); + } + + public final void setY(int value) + { + getWorldPosition().setY(value); + } + + /** Return the z position of the L2Object. */ + public final int getZ() + { + return getWorldPosition().getZ(); + } + + public final void setZ(int value) + { + getWorldPosition().setZ(value); + } + + public final Point3D getWorldPosition() + { + if (_worldPosition == null) + { + _worldPosition = new Point3D(0, 0, 0); + } + return _worldPosition; + } + + public final void setWorldPosition(int x, int y, int z) + { + getWorldPosition().setXYZ(x, y, z); + } + + public final void setWorldPosition(Point3D newPosition) + { + setWorldPosition(newPosition.getX(), newPosition.getY(), newPosition.getZ()); + } + + public final L2WorldRegion getWorldRegion() + { + return _worldRegion; + } + + public void setWorldRegion(L2WorldRegion value) + { + if ((_worldRegion != null) && (getActiveObject() instanceof L2Character)) // confirm revalidation of old region's zones + { + if (value != null) + { + _worldRegion.revalidateZones((L2Character) getActiveObject()); // at world region change + } + else + { + _worldRegion.removeFromZones((L2Character) getActiveObject()); // at world region change + } + } + + _worldRegion = value; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/position/PcPosition.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/position/PcPosition.java new file mode 100644 index 00000000..0ca4654f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/position/PcPosition.java @@ -0,0 +1,46 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * http://www.gnu.org/copyleft/gpl.html + */ +package com.l2jserver.gameserver.model.actor.position; + +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * @author Erb + */ +public class PcPosition extends CharPosition +{ + // ========================================================= + // Constructor + public PcPosition(L2PcInstance activeObject) + { + super(activeObject); + } + + @Override + public L2PcInstance getActiveObject() + { + return ((L2PcInstance) super.getActiveObject()); + } + + @Override + protected void badCoords() + { + getActiveObject().teleToLocation(0, 0, 0, false); + getActiveObject().sendMessage("Error with your coords, Please ask a GM for help!"); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/CharStat.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/CharStat.java new file mode 100644 index 00000000..7f5b785b --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/CharStat.java @@ -0,0 +1,880 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.stat; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.Elementals; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.skills.Calculator; +import com.l2jserver.gameserver.skills.Env; +import com.l2jserver.gameserver.skills.Stats; +import com.l2jserver.gameserver.templates.item.L2Weapon; +import com.l2jserver.gameserver.templates.item.L2WeaponType; + +public class CharStat +{ + // ========================================================= + // Data Field + private final L2Character _activeChar; + private long _exp = 0; + private int _sp = 0; + private byte _level = 1; + + // ========================================================= + // Constructor + public CharStat(L2Character activeChar) + { + _activeChar = activeChar; + } + + // ========================================================= + // Method - Public + /** + * Calculate the new value of the state with modifiers that will be applied on the targeted L2Character.
    + *
    + * Concept :
    + *
    + * A L2Character owns a table of Calculators called _calculators. Each Calculator (a calculator per state) own a table of Func object. A Func object is a mathematic function that permit to calculate the modifier of a state (ex : REGENERATE_HP_RATE...) :
    + *
    + * FuncAtkAccuracy -> Math.sqrt(_player.getDEX())*6+_player.getLevel()
    + *
    + * When the calc method of a calculator is launched, each mathematic function is called according to its priority _order. Indeed, Func with lowest priority order is executed firsta and Funcs with the same order are executed in unspecified order. The result of the calculation is stored in + * the value property of an Env class instance.
    + *
    + * @param stat The stat to calculate the new value with modifiers + * @param init The initial value of the stat before applying modifiers + * @param target The L2Charcater whose properties will be used in the calculation (ex : CON, INT...) + * @param skill The L2Skill whose properties will be used in the calculation (ex : Level...) + */ + public final double calcStat(Stats stat, double init, L2Character target, L2Skill skill) + { + if ((_activeChar == null) || (stat == null)) + { + return init; + } + + int id = stat.ordinal(); + + Calculator c = _activeChar.getCalculators()[id]; + + // If no Func object found, no modifier is applied + if ((c == null) || (c.size() == 0)) + { + return init; + } + + // Create and init an Env object to pass parameters to the Calculator + Env env = new Env(); + env.player = _activeChar; + env.target = target; + env.skill = skill; + env.value = init; + // Launch the calculation + c.calc(env); + // avoid some troubles with negative stats (some stats should never be negative) + if (env.value <= 0) + { + switch (stat) + { + case MAX_HP: + case MAX_MP: + case MAX_CP: + case MAGIC_DEFENCE: + case POWER_DEFENCE: + case POWER_ATTACK: + case MAGIC_ATTACK: + case POWER_ATTACK_SPEED: + case MAGIC_ATTACK_SPEED: + case SHIELD_DEFENCE: + case STAT_CON: + case STAT_DEX: + case STAT_INT: + case STAT_MEN: + case STAT_STR: + case STAT_WIT: + env.value = 1; + } + } + + return env.value; + } + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + /** + * Return the Accuracy (base+modifier) of the L2Character in function of the Weapon Expertise Penalty. + */ + public int getAccuracy() + { + if (_activeChar == null) + { + return 0; + } + return (int) Math.round(calcStat(Stats.ACCURACY_COMBAT, 0, null, null)); + } + + public L2Character getActiveChar() + { + return _activeChar; + } + + /** + * Return the Attack Speed multiplier (base+modifier) of the L2Character to get proper animations. + */ + public final float getAttackSpeedMultiplier() + { + if (_activeChar == null) + { + return 1; + } + + return (float) (((1.1) * getPAtkSpd()) / _activeChar.getTemplate().basePAtkSpd); + } + + /** Return the CON of the L2Character (base+modifier). */ + public final int getCON() + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.STAT_CON, _activeChar.getTemplate().baseCON, null, null); + } + + /** Return the Critical Damage rate (base+modifier) of the L2Character. */ + public final double getCriticalDmg(L2Character target, double init) + { + return calcStat(Stats.CRITICAL_DAMAGE, init, target, null); + } + + /** Return the Critical Hit rate (base+modifier) of the L2Character. */ + public int getCriticalHit(L2Character target, L2Skill skill) + { + if (_activeChar == null) + { + return 1; + } + + int criticalHit = (int) Math.round((calcStat(Stats.CRITICAL_RATE, _activeChar.getTemplate().baseCritRate, target, skill) * 10.0) + 0.5); + criticalHit /= 10; + // Set a cap of Critical Hit at 500 + if (criticalHit > Config.MAX_PCRIT_RATE) + { + criticalHit = Config.MAX_PCRIT_RATE; + } + + return criticalHit; + } + + /** Return the DEX of the L2Character (base+modifier). */ + public final int getDEX() + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.STAT_DEX, _activeChar.getTemplate().baseDEX, null, null); + } + + /** Return the Attack Evasion rate (base+modifier) of the L2Character. */ + public int getEvasionRate(L2Character target) + { + if (_activeChar == null) + { + return 1; + } + + int val = (int) Math.round(calcStat(Stats.EVASION_RATE, 0, target, null)); + if ((val > Config.MAX_EVASION) && !_activeChar.isGM()) + { + val = Config.MAX_EVASION; + } + return val; + } + + public long getExp() + { + return _exp; + } + + public void setExp(long value) + { + _exp = value; + } + + /** Return the INT of the L2Character (base+modifier). */ + public int getINT() + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.STAT_INT, _activeChar.getTemplate().baseINT, null, null); + } + + public byte getLevel() + { + return _level; + } + + public void setLevel(byte value) + { + _level = value; + } + + /** Return the Magical Attack range (base+modifier) of the L2Character. */ + public final int getMagicalAttackRange(L2Skill skill) + { + if (_activeChar == null) + { + return 1; + } + + if (skill != null) + { + return (int) calcStat(Stats.MAGIC_ATTACK_RANGE, skill.getCastRange(), null, skill); + } + + return _activeChar.getTemplate().baseAtkRange; + } + + public int getMaxCp() + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.MAX_CP, _activeChar.getTemplate().baseCpMax, null, null); + } + + public int getMaxHp() + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.LIMIT_HP, getMaxVisibleHp(), null, null); + } + + public int getMaxVisibleHp() + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.MAX_HP, _activeChar.getTemplate().baseHpMax, null, null); + } + + public int getMaxMp() + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.MAX_MP, _activeChar.getTemplate().baseMpMax, null, null); + } + + /** + * Return the MAtk (base+modifier) of the L2Character for a skill used in function of abnormal effects in progress.
    + *
    + * Example of use :
    + *
    + *
  • Calculate Magic damage

  • + *
    + * @param target The L2Character targeted by the skill + * @param skill The L2Skill used against the target + */ + public int getMAtk(L2Character target, L2Skill skill) + { + if (_activeChar == null) + { + return 1; + } + float bonusAtk = 1; + if (Config.L2JMOD_CHAMPION_ENABLE && _activeChar.isChampion()) + { + bonusAtk = Config.L2JMOD_CHAMPION_ATK; + } + if (_activeChar.isRaid()) + { + bonusAtk *= Config.RAID_MATTACK_MULTIPLIER; + } + double attack = _activeChar.getTemplate().baseMAtk * bonusAtk; + // Get the skill type to calculate its effect in function of base stats + // of the L2Character target + Stats stat = skill == null ? null : skill.getStat(); + + if (stat != null) + { + switch (stat) + { + case AGGRESSION: + attack += _activeChar.getTemplate().baseAggression; + break; + case BLEED: + attack += _activeChar.getTemplate().baseBleed; + break; + case POISON: + attack += _activeChar.getTemplate().basePoison; + break; + case STUN: + attack += _activeChar.getTemplate().baseStun; + break; + case ROOT: + attack += _activeChar.getTemplate().baseRoot; + break; + case MOVEMENT: + attack += _activeChar.getTemplate().baseMovement; + break; + case CONFUSION: + attack += _activeChar.getTemplate().baseConfusion; + break; + case SLEEP: + attack += _activeChar.getTemplate().baseSleep; + break; + } + } + + // Add the power of the skill to the attack effect + if (skill != null) + { + attack += skill.getPower(); + } + + // Calculate modifiers Magic Attack + return (int) calcStat(Stats.MAGIC_ATTACK, attack, target, skill); + } + + /** + * Return the MAtk Speed (base+modifier) of the L2Character in function of the Armour Expertise Penalty. + */ + public int getMAtkSpd() + { + if (_activeChar == null) + { + return 1; + } + float bonusSpdAtk = 1; + if (Config.L2JMOD_CHAMPION_ENABLE && _activeChar.isChampion()) + { + bonusSpdAtk = Config.L2JMOD_CHAMPION_SPD_ATK; + } + double val = calcStat(Stats.MAGIC_ATTACK_SPEED, _activeChar.getTemplate().baseMAtkSpd * bonusSpdAtk, null, null); + if ((val > Config.MAX_MATK_SPEED) && !_activeChar.isGM()) + { + val = Config.MAX_MATK_SPEED; + } + return (int) val; + } + + /** Return the Magic Critical Hit rate (base+modifier) of the L2Character. */ + public final int getMCriticalHit(L2Character target, L2Skill skill) + { + if (_activeChar == null) + { + return 1; + } + + double mrate = calcStat(Stats.MCRITICAL_RATE, _activeChar.getTemplate().baseMCritRate, target, skill); + if (mrate > Config.MAX_MCRIT_RATE) + { + mrate = Config.MAX_MCRIT_RATE; + } + return (int) mrate; + } + + /** + * Return the MDef (base+modifier) of the L2Character against a skill in function of abnormal effects in progress.
    + *
    + * Example of use :
    + *
    + *
  • Calculate Magic damage

  • + * @param target The L2Character targeted by the skill + * @param skill The L2Skill used against the target + */ + public int getMDef(L2Character target, L2Skill skill) + { + if (_activeChar == null) + { + return 1; + } + + // Get the base MAtk of the L2Character + double defence = _activeChar.getTemplate().baseMDef; + + // Calculate modifier for Raid Bosses + if (_activeChar.isRaid()) + { + defence *= Config.RAID_MDEFENCE_MULTIPLIER; + } + + // Calculate modifiers Magic Attack + return (int) calcStat(Stats.MAGIC_DEFENCE, defence, target, skill); + } + + /** Return the MEN of the L2Character (base+modifier). */ + public final int getMEN() + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.STAT_MEN, _activeChar.getTemplate().baseMEN, null, null); + } + + public float getMovementSpeedMultiplier() + { + if (_activeChar == null) + { + return 1; + } + + return getRunSpeed() / (float) _activeChar.getTemplate().baseRunSpd; + } + + /** + * Return the RunSpeed (base+modifier) or WalkSpeed (base+modifier) of the L2Character in function of the movement type. + */ + public float getMoveSpeed() + { + if (_activeChar == null) + { + return 1; + } + + if (_activeChar.isRunning()) + { + return getRunSpeed(); + } + return getWalkSpeed(); + } + + /** Return the MReuse rate (base+modifier) of the L2Character. */ + public final double getMReuseRate(L2Skill skill) + { + if (_activeChar == null) + { + return 1; + } + + return calcStat(Stats.MAGIC_REUSE_RATE, _activeChar.getTemplate().baseMReuseRate, null, skill); + } + + /** Return the PReuse rate (base+modifier) of the L2Character. */ + public final double getPReuseRate(L2Skill skill) + { + if (_activeChar == null) + { + return 1; + } + + return calcStat(Stats.P_REUSE, _activeChar.getTemplate().baseMReuseRate, null, skill); + } + + /** Return the PAtk (base+modifier) of the L2Character. */ + public int getPAtk(L2Character target) + { + if (_activeChar == null) + { + return 1; + } + float bonusAtk = 1; + if (Config.L2JMOD_CHAMPION_ENABLE && _activeChar.isChampion()) + { + bonusAtk = Config.L2JMOD_CHAMPION_ATK; + } + if (_activeChar.isRaid()) + { + bonusAtk *= Config.RAID_PATTACK_MULTIPLIER; + } + return (int) calcStat(Stats.POWER_ATTACK, _activeChar.getTemplate().basePAtk * bonusAtk, target, null); + } + + /** Return the PAtk Modifier against animals. */ + public final double getPAtkAnimals(L2Character target) + { + return calcStat(Stats.PATK_ANIMALS, 1, target, null); + } + + /** Return the PAtk Modifier against dragons. */ + public final double getPAtkDragons(L2Character target) + { + return calcStat(Stats.PATK_DRAGONS, 1, target, null); + } + + /** Return the PAtk Modifier against insects. */ + public final double getPAtkInsects(L2Character target) + { + return calcStat(Stats.PATK_INSECTS, 1, target, null); + } + + /** Return the PAtk Modifier against monsters. */ + public final double getPAtkMonsters(L2Character target) + { + return calcStat(Stats.PATK_MONSTERS, 1, target, null); + } + + /** Return the PAtk Modifier against plants. */ + public final double getPAtkPlants(L2Character target) + { + return calcStat(Stats.PATK_PLANTS, 1, target, null); + } + + /** Return the PAtk Modifier against giants. */ + public final double getPAtkGiants(L2Character target) + { + return calcStat(Stats.PATK_GIANTS, 1, target, null); + } + + /** Return the PAtk Modifier against magic creatures */ + public final double getPAtkMagicCreatures(L2Character target) + { + return calcStat(Stats.PATK_MCREATURES, 1, target, null); + } + + /** + * Return the PAtk Speed (base+modifier) of the L2Character in function of the Armour Expertise Penalty. + */ + public int getPAtkSpd() + { + if (_activeChar == null) + { + return 1; + } + float bonusAtk = 1; + if (Config.L2JMOD_CHAMPION_ENABLE && _activeChar.isChampion()) + { + bonusAtk = Config.L2JMOD_CHAMPION_SPD_ATK; + } + int val = (int) Math.round(calcStat(Stats.POWER_ATTACK_SPEED, _activeChar.getTemplate().basePAtkSpd * bonusAtk, null, null)); + return val; + } + + /** Return the PDef Modifier against animals. */ + public final double getPDefAnimals(L2Character target) + { + return calcStat(Stats.PDEF_ANIMALS, 1, target, null); + } + + /** Return the PDef Modifier against dragons. */ + public final double getPDefDragons(L2Character target) + { + return calcStat(Stats.PDEF_DRAGONS, 1, target, null); + } + + /** Return the PDef Modifier against insects. */ + public final double getPDefInsects(L2Character target) + { + return calcStat(Stats.PDEF_INSECTS, 1, target, null); + } + + /** Return the PDef Modifier against monsters. */ + public final double getPDefMonsters(L2Character target) + { + return calcStat(Stats.PDEF_MONSTERS, 1, target, null); + } + + /** Return the PDef Modifier against plants. */ + public final double getPDefPlants(L2Character target) + { + return calcStat(Stats.PDEF_PLANTS, 1, target, null); + } + + /** Return the PDef Modifier against giants. */ + public final double getPDefGiants(L2Character target) + { + return calcStat(Stats.PDEF_GIANTS, 1, target, null); + } + + /** Return the PDef Modifier against giants. */ + public final double getPDefMagicCreatures(L2Character target) + { + return calcStat(Stats.PDEF_MCREATURES, 1, target, null); + } + + /** Return the PDef (base+modifier) of the L2Character. */ + public int getPDef(L2Character target) + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.POWER_DEFENCE, (_activeChar.isRaid()) ? _activeChar.getTemplate().basePDef * Config.RAID_PDEFENCE_MULTIPLIER : _activeChar.getTemplate().basePDef, target, null); + } + + /** Return the Physical Attack range (base+modifier) of the L2Character. */ + public final int getPhysicalAttackRange() + { + if (_activeChar == null) + { + return 1; + } + + if (_activeChar.isTransformed()) + { + return _activeChar.getTemplate().baseAtkRange; + } + // Polearm handled here for now. Basically L2PcInstance could have a function + // similar to FuncBowAtkRange and NPC are defined in DP. + L2Weapon weaponItem = _activeChar.getActiveWeaponItem(); + if ((weaponItem != null) && (weaponItem.getItemType() == L2WeaponType.POLE)) + { + return (int) calcStat(Stats.POWER_ATTACK_RANGE, 66, null, null); + } + + return (int) calcStat(Stats.POWER_ATTACK_RANGE, _activeChar.getTemplate().baseAtkRange, null, null); + } + + /** Return the weapon reuse modifier */ + public final double getWeaponReuseModifier(L2Character target) + { + return calcStat(Stats.ATK_REUSE, 1, target, null); + } + + /** + * Return the RunSpeed (base+modifier) of the L2Character in function of the Armour Expertise Penalty. + */ + public int getRunSpeed() + { + if (_activeChar == null) + { + return 1; + } + + // err we should be adding TO the persons run speed + // not making it a constant + double baseRunSpd = _activeChar.getTemplate().baseRunSpd; + + if (baseRunSpd == 0) + { + return 0; + } + + return (int) Math.round(calcStat(Stats.RUN_SPEED, baseRunSpd, null, null)); + } + + /** Return the ShieldDef rate (base+modifier) of the L2Character. */ + public final int getShldDef() + { + return (int) calcStat(Stats.SHIELD_DEFENCE, 0, null, null); + } + + public int getSp() + { + return _sp; + } + + public void setSp(int value) + { + _sp = value; + } + + /** Return the STR of the L2Character (base+modifier). */ + public final int getSTR() + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.STAT_STR, _activeChar.getTemplate().baseSTR, null, null); + } + + /** Return the WalkSpeed (base+modifier) of the L2Character. */ + public int getWalkSpeed() + { + if (_activeChar == null) + { + return 1; + } + + double baseWalkSpd = _activeChar.getTemplate().baseWalkSpd; + + if (baseWalkSpd == 0) + { + return 0; + } + + return (int) calcStat(Stats.WALK_SPEED, baseWalkSpd, null, null); + } + + /** Return the WIT of the L2Character (base+modifier). */ + public final int getWIT() + { + if (_activeChar == null) + { + return 1; + } + + return (int) calcStat(Stats.STAT_WIT, _activeChar.getTemplate().baseWIT, null, null); + } + + /** Return the mpConsume. */ + public final int getMpConsume(L2Skill skill) + { + if (skill == null) + { + return 1; + } + double mpConsume = skill.getMpConsume(); + if (skill.isDance()) + { + if (Config.DANCE_CONSUME_ADDITIONAL_MP && (_activeChar != null) && (_activeChar.getDanceCount() > 0)) + { + mpConsume += _activeChar.getDanceCount() * skill.getNextDanceMpCost(); + } + } + + mpConsume = calcStat(Stats.MP_CONSUME, mpConsume, null, skill); + + if (skill.isDance()) + { + return (int) calcStat(Stats.DANCE_MP_CONSUME_RATE, mpConsume, null, null); + } + else if (skill.isMagic()) + { + return (int) calcStat(Stats.MAGICAL_MP_CONSUME_RATE, mpConsume, null, null); + } + else + { + return (int) calcStat(Stats.PHYSICAL_MP_CONSUME_RATE, mpConsume, null, null); + } + } + + /** Return the mpInitialConsume. */ + public final int getMpInitialConsume(L2Skill skill) + { + if (skill == null) + { + return 1; + } + + double mpConsume = calcStat(Stats.MP_CONSUME, skill.getMpInitialConsume(), null, skill); + + if (skill.isDance()) + { + return (int) calcStat(Stats.DANCE_MP_CONSUME_RATE, mpConsume, null, null); + } + else if (skill.isMagic()) + { + return (int) calcStat(Stats.MAGICAL_MP_CONSUME_RATE, mpConsume, null, null); + } + else + { + return (int) calcStat(Stats.PHYSICAL_MP_CONSUME_RATE, mpConsume, null, null); + } + } + + public byte getAttackElement() + { + L2ItemInstance weaponInstance = _activeChar.getActiveWeaponInstance(); + // 1st order - weapon element + if ((weaponInstance != null) && (weaponInstance.getAttackElementType() >= 0)) + { + return weaponInstance.getAttackElementType(); + } + + // temp fix starts + int tempVal = 0, stats[] = + { + 0, + 0, + 0, + 0, + 0, + 0 + }; + + byte returnVal = -2; + stats[0] = (int) calcStat(Stats.FIRE_POWER, _activeChar.getTemplate().baseFire, null, null); + stats[1] = (int) calcStat(Stats.WATER_POWER, _activeChar.getTemplate().baseWater, null, null); + stats[2] = (int) calcStat(Stats.WIND_POWER, _activeChar.getTemplate().baseWind, null, null); + stats[3] = (int) calcStat(Stats.EARTH_POWER, _activeChar.getTemplate().baseEarth, null, null); + stats[4] = (int) calcStat(Stats.HOLY_POWER, _activeChar.getTemplate().baseHoly, null, null); + stats[5] = (int) calcStat(Stats.DARK_POWER, _activeChar.getTemplate().baseDark, null, null); + + for (byte x = 0; x < 6; x++) + { + if (stats[x] > tempVal) + { + returnVal = x; + tempVal = stats[x]; + } + } + + return returnVal; + // temp fix ends + + /* + * uncomment me once deadlocks in getAllEffects() fixed return _activeChar.getElementIdFromEffects(); + */ + } + + public int getAttackElementValue(byte attackAttribute) + { + switch (attackAttribute) + { + case Elementals.FIRE: + return (int) calcStat(Stats.FIRE_POWER, _activeChar.getTemplate().baseFire, null, null); + case Elementals.WATER: + return (int) calcStat(Stats.WATER_POWER, _activeChar.getTemplate().baseWater, null, null); + case Elementals.WIND: + return (int) calcStat(Stats.WIND_POWER, _activeChar.getTemplate().baseWind, null, null); + case Elementals.EARTH: + return (int) calcStat(Stats.EARTH_POWER, _activeChar.getTemplate().baseEarth, null, null); + case Elementals.HOLY: + return (int) calcStat(Stats.HOLY_POWER, _activeChar.getTemplate().baseHoly, null, null); + case Elementals.DARK: + return (int) calcStat(Stats.DARK_POWER, _activeChar.getTemplate().baseDark, null, null); + default: + return 0; + } + } + + public int getDefenseElementValue(byte defenseAttribute) + { + switch (defenseAttribute) + { + case Elementals.FIRE: + return (int) calcStat(Stats.FIRE_RES, _activeChar.getTemplate().baseFireRes, null, null); + case Elementals.WATER: + return (int) calcStat(Stats.WATER_RES, _activeChar.getTemplate().baseWaterRes, null, null); + case Elementals.WIND: + return (int) calcStat(Stats.WIND_RES, _activeChar.getTemplate().baseWindRes, null, null); + case Elementals.EARTH: + return (int) calcStat(Stats.EARTH_RES, _activeChar.getTemplate().baseEarthRes, null, null); + case Elementals.HOLY: + return (int) calcStat(Stats.HOLY_RES, _activeChar.getTemplate().baseHolyRes, null, null); + case Elementals.DARK: + return (int) calcStat(Stats.DARK_RES, _activeChar.getTemplate().baseDarkRes, null, null); + default: + return 0; + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/ControllableAirShipStat.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/ControllableAirShipStat.java new file mode 100644 index 00000000..c18c71a0 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/ControllableAirShipStat.java @@ -0,0 +1,41 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.stat; + +import com.l2jserver.gameserver.model.actor.instance.L2ControllableAirShipInstance; + +public class ControllableAirShipStat extends VehicleStat +{ + public ControllableAirShipStat(L2ControllableAirShipInstance activeChar) + { + super(activeChar); + } + + @Override + public L2ControllableAirShipInstance getActiveChar() + { + return (L2ControllableAirShipInstance) super.getActiveChar(); + } + + @Override + public float getMoveSpeed() + { + if (getActiveChar().isInDock() || (getActiveChar().getFuel() > 0)) + { + return super.getMoveSpeed(); + } + return super.getMoveSpeed() * 0.05f; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/DoorStat.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/DoorStat.java new file mode 100644 index 00000000..686ca275 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/DoorStat.java @@ -0,0 +1,52 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.stat; + +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; + +public class DoorStat extends CharStat +{ + // ========================================================= + // Data Field + + // ========================================================= + // Constructor + public DoorStat(L2DoorInstance activeChar) + { + super(activeChar); + + setLevel((byte) 1); + } + + // ========================================================= + // Method - Public + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + @Override + public L2DoorInstance getActiveChar() + { + return (L2DoorInstance) super.getActiveChar(); + } + + @Override + public final byte getLevel() + { + return 1; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/NpcStat.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/NpcStat.java new file mode 100644 index 00000000..17d0c9ca --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/NpcStat.java @@ -0,0 +1,58 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.stat; + +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.skills.Stats; + +public class NpcStat extends CharStat +{ + public NpcStat(L2Npc activeChar) + { + super(activeChar); + } + + @Override + public byte getLevel() + { + return getActiveChar().getTemplate().level; + } + + @Override + public L2Npc getActiveChar() + { + return (L2Npc) super.getActiveChar(); + } + + @Override + public int getWalkSpeed() + { + return (int) calcStat(Stats.WALK_SPEED, getActiveChar().getTemplate().baseWalkSpd, null, null); + } + + @Override + public float getMovementSpeedMultiplier() + { + if (getActiveChar() == null) + { + return 1; + } + if (getActiveChar().isRunning()) + { + return (getRunSpeed() * 1f) / getActiveChar().getTemplate().baseRunSpd; + } + return (getWalkSpeed() * 1f) / getActiveChar().getTemplate().baseWalkSpd; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/PcStat.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/PcStat.java new file mode 100644 index 00000000..c8810e7c --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/PcStat.java @@ -0,0 +1,704 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.stat; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2ClassMasterInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PetInstance; +import com.l2jserver.gameserver.model.base.Experience; +import com.l2jserver.gameserver.model.entity.RecoBonus; +import com.l2jserver.gameserver.model.quest.QuestState; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo; +import com.l2jserver.gameserver.network.serverpackets.ExVitalityPointInfo; +import com.l2jserver.gameserver.network.serverpackets.ExVoteSystemInfo; +import com.l2jserver.gameserver.network.serverpackets.PledgeShowMemberListUpdate; +import com.l2jserver.gameserver.network.serverpackets.SocialAction; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.network.serverpackets.UserInfo; +import com.l2jserver.gameserver.skills.Stats; + +public class PcStat extends PlayableStat +{ + // private static Logger _log = Logger.getLogger(PcStat.class.getName()); + + // ========================================================= + // Data Field + + private int _oldMaxHp; // stats watch + private int _oldMaxMp; // stats watch + private int _oldMaxCp; // stats watch + private float _vitalityPoints = 1; + private byte _vitalityLevel = 0; + + public static final int VITALITY_LEVELS[] = + { + 240, + 2000, + 13000, + 17000, + 20000 + }; + public static final int MAX_VITALITY_POINTS = VITALITY_LEVELS[4]; + public static final int MIN_VITALITY_POINTS = 1; + + // ========================================================= + // Constructor + public PcStat(L2PcInstance activeChar) + { + super(activeChar); + } + + // ========================================================= + // Method - Public + @Override + public boolean addExp(long value) + { + L2PcInstance activeChar = getActiveChar(); + + // Allowed to gain exp? + if (!getActiveChar().getAccessLevel().canGainExp()) + { + return false; + } + + if (!super.addExp(value)) + { + return false; + } + + // Set new karma + if (!activeChar.isCursedWeaponEquipped() && (activeChar.getKarma() > 0) && (activeChar.isGM() || !activeChar.isInsideZone(L2Character.ZONE_PVP))) + { + int karmaLost = activeChar.calculateKarmaLost(value); + if (karmaLost > 0) + { + activeChar.setKarma(activeChar.getKarma() - karmaLost); + } + } + + // EXP status update currently not used in retail + activeChar.sendPacket(new UserInfo(activeChar)); + activeChar.sendPacket(new ExBrExtraUserInfo(activeChar)); + return true; + } + + /** + * Add Experience and SP rewards to the L2PcInstance, remove its Karma (if necessary) and Launch increase level task.
    + *
    + * Actions :
    + *
    + *
  • Remove Karma when the player kills L2MonsterInstance
  • Send a Server->Client packet StatusUpdate to the L2PcInstance
  • Send a Server->Client System Message to the L2PcInstance
  • If the L2PcInstance increases it's level, send a Server->Client packet SocialAction + * (broadcast)
  • If the L2PcInstance increases it's level, manage the increase level task (Max MP, Max MP, Recommandation, Expertise and beginner skills...)
  • If the L2PcInstance increases it's level, send a Server->Client packet UserInfo to the L2PcInstance

  • + *
    + * @param addToExp The Experience value to add + * @param addToSp The SP value to add + */ + @Override + public boolean addExpAndSp(long addToExp, int addToSp) + { + float ratioTakenByPlayer = 0; + // Allowed to gain exp/sp? + L2PcInstance activeChar = getActiveChar(); + if (!activeChar.getAccessLevel().canGainExp()) + { + return false; + } + + // if this player has a pet that takes from the owner's Exp, give the pet Exp now + + if (activeChar.getPet() instanceof L2PetInstance) + { + L2PetInstance pet = (L2PetInstance) activeChar.getPet(); + ratioTakenByPlayer = pet.getPetLevelData().getOwnerExpTaken() / 100f; + + // only give exp/sp to the pet by taking from the owner if the pet has a non-zero, positive ratio + // allow possible customizations that would have the pet earning more than 100% of the owner's exp/sp + if (ratioTakenByPlayer > 1) + { + ratioTakenByPlayer = 1; + } + if (!pet.isDead()) + { + pet.addExpAndSp((long) (addToExp * (1 - ratioTakenByPlayer)), (int) (addToSp * (1 - ratioTakenByPlayer))); + } + // now adjust the max ratio to avoid the owner earning negative exp/sp + addToExp = (long) (addToExp * ratioTakenByPlayer); + addToSp = (int) (addToSp * ratioTakenByPlayer); + } + + if (!super.addExpAndSp(addToExp, addToSp)) + { + return false; + } + + // Send a Server->Client System Message to the L2PcInstance + if ((addToExp == 0) && (addToSp != 0)) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.ACQUIRED_S1_SP); + sm.addNumber(addToSp); + activeChar.sendPacket(sm); + } + else if ((addToSp == 0) && (addToExp != 0)) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S1_EXPERIENCE); + sm.addNumber((int) addToExp); + activeChar.sendPacket(sm); + } + else + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_EARNED_S1_EXP_AND_S2_SP); + sm.addNumber((int) addToExp); + sm.addNumber(addToSp); + activeChar.sendPacket(sm); + } + return true; + } + + public boolean addExpAndSp(long addToExp, int addToSp, boolean useBonuses) + { + if (useBonuses) + { + if (Config.ENABLE_VITALITY) + { + switch (_vitalityLevel) + { + case 1: + addToExp *= Config.RATE_VITALITY_LEVEL_1; + addToSp *= Config.RATE_VITALITY_LEVEL_1; + break; + case 2: + addToExp *= Config.RATE_VITALITY_LEVEL_2; + addToSp *= Config.RATE_VITALITY_LEVEL_2; + break; + case 3: + addToExp *= Config.RATE_VITALITY_LEVEL_3; + addToSp *= Config.RATE_VITALITY_LEVEL_3; + break; + case 4: + addToExp *= Config.RATE_VITALITY_LEVEL_4; + addToSp *= Config.RATE_VITALITY_LEVEL_4; + break; + } + } + // Apply recommendation bonus + addToExp *= RecoBonus.getRecoMultiplier(getActiveChar()); + addToSp *= RecoBonus.getRecoMultiplier(getActiveChar()); + } + return addExpAndSp(addToExp, addToSp); + } + + @Override + public boolean removeExpAndSp(long addToExp, int addToSp) + { + return removeExpAndSp(addToExp, addToSp, true); + } + + public boolean removeExpAndSp(long addToExp, int addToSp, boolean sendMessage) + { + int level = getLevel(); + if (!super.removeExpAndSp(addToExp, addToSp)) + { + return false; + } + + if (sendMessage) + { + // Send a Server->Client System Message to the L2PcInstance + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EXP_DECREASED_BY_S1); + sm.addNumber((int) addToExp); + getActiveChar().sendPacket(sm); + sm = SystemMessage.getSystemMessage(SystemMessageId.SP_DECREASED_S1); + sm.addNumber(addToSp); + getActiveChar().sendPacket(sm); + if (getLevel() < level) + { + getActiveChar().broadcastStatusUpdate(); + } + } + return true; + } + + @Override + public final boolean addLevel(byte value) + { + if ((getLevel() + value) > (Experience.MAX_LEVEL - 1)) + { + return false; + } + + boolean levelIncreased = super.addLevel(value); + + if (levelIncreased) + { + if (!Config.DISABLE_TUTORIAL) + { + QuestState qs = getActiveChar().getQuestState("255_Tutorial"); + if (qs != null) + { + qs.getQuest().notifyEvent("CE40", null, getActiveChar()); + } + } + + getActiveChar().setCurrentCp(getMaxCp()); + getActiveChar().broadcastPacket(new SocialAction(getActiveChar(), SocialAction.LEVEL_UP)); + getActiveChar().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_INCREASED_YOUR_LEVEL)); + + L2ClassMasterInstance.showQuestionMark(getActiveChar()); + } + + // Give AutoGet skills and all normal skills if Auto-Learn is activated. + getActiveChar().rewardSkills(); + + if (getActiveChar().getClan() != null) + { + getActiveChar().getClan().updateClanMember(getActiveChar()); + getActiveChar().getClan().broadcastToOnlineMembers(new PledgeShowMemberListUpdate(getActiveChar())); + } + if (getActiveChar().isInParty()) + { + getActiveChar().getParty().recalculatePartyLevel(); // Recalculate the party level + } + + if (getActiveChar().isTransformed() || getActiveChar().isInStance()) + { + getActiveChar().getTransformation().onLevelUp(); + } + + StatusUpdate su = new StatusUpdate(getActiveChar()); + su.addAttribute(StatusUpdate.LEVEL, getLevel()); + su.addAttribute(StatusUpdate.MAX_CP, getMaxCp()); + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); + su.addAttribute(StatusUpdate.MAX_MP, getMaxMp()); + getActiveChar().sendPacket(su); + + // Update the overloaded status of the L2PcInstance + getActiveChar().refreshOverloaded(); + // Update the expertise status of the L2PcInstance + getActiveChar().refreshExpertisePenalty(); + // Send a Server->Client packet UserInfo to the L2PcInstance + getActiveChar().sendPacket(new UserInfo(getActiveChar())); + getActiveChar().sendPacket(new ExBrExtraUserInfo(getActiveChar())); + getActiveChar().sendPacket(new ExVoteSystemInfo(getActiveChar())); + + return levelIncreased; + } + + @Override + public boolean addSp(int value) + { + if (!super.addSp(value)) + { + return false; + } + + StatusUpdate su = new StatusUpdate(getActiveChar()); + su.addAttribute(StatusUpdate.SP, getSp()); + getActiveChar().sendPacket(su); + + return true; + } + + @Override + public final long getExpForLevel(int level) + { + return Experience.LEVEL[level]; + } + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + @Override + public final L2PcInstance getActiveChar() + { + return (L2PcInstance) super.getActiveChar(); + } + + @Override + public final long getExp() + { + if (getActiveChar().isSubClassActive()) + { + return getActiveChar().getSubClasses().get(getActiveChar().getClassIndex()).getExp(); + } + + return super.getExp(); + } + + @Override + public final void setExp(long value) + { + if (getActiveChar().isSubClassActive()) + { + getActiveChar().getSubClasses().get(getActiveChar().getClassIndex()).setExp(value); + } + else + { + super.setExp(value); + } + } + + @Override + public final byte getLevel() + { + if (getActiveChar().isSubClassActive()) + { + return getActiveChar().getSubClasses().get(getActiveChar().getClassIndex()).getLevel(); + } + + return super.getLevel(); + } + + @Override + public final void setLevel(byte value) + { + if (value > (Experience.MAX_LEVEL - 1)) + { + value = Experience.MAX_LEVEL - 1; + } + + if (getActiveChar().isSubClassActive()) + { + getActiveChar().getSubClasses().get(getActiveChar().getClassIndex()).setLevel(value); + } + else + { + super.setLevel(value); + } + } + + @Override + public final int getMaxCp() + { + // Get the Max CP (base+modifier) of the L2PcInstance + int val = super.getMaxCp(); + if (val != _oldMaxCp) + { + _oldMaxCp = val; + + // Launch a regen task if the new Max CP is higher than the old one + if (getActiveChar().getStatus().getCurrentCp() != val) + { + getActiveChar().getStatus().setCurrentCp(getActiveChar().getStatus().getCurrentCp()); // trigger start of regeneration + } + } + return val; + } + + @Override + public final int getMaxHp() + { + // Get the Max HP (base+modifier) of the L2PcInstance + int val = super.getMaxHp(); + if (val != _oldMaxHp) + { + _oldMaxHp = val; + + // Launch a regen task if the new Max HP is higher than the old one + if (getActiveChar().getStatus().getCurrentHp() != val) + { + getActiveChar().getStatus().setCurrentHp(getActiveChar().getStatus().getCurrentHp()); // trigger start of regeneration + } + } + + return val; + } + + @Override + public final int getMaxMp() + { + // Get the Max MP (base+modifier) of the L2PcInstance + int val = super.getMaxMp(); + + if (val != _oldMaxMp) + { + _oldMaxMp = val; + + // Launch a regen task if the new Max MP is higher than the old one + if (getActiveChar().getStatus().getCurrentMp() != val) + { + getActiveChar().getStatus().setCurrentMp(getActiveChar().getStatus().getCurrentMp()); // trigger start of regeneration + } + } + + return val; + } + + @Override + public final int getSp() + { + if (getActiveChar().isSubClassActive()) + { + return getActiveChar().getSubClasses().get(getActiveChar().getClassIndex()).getSp(); + } + + return super.getSp(); + } + + @Override + public final void setSp(int value) + { + if (getActiveChar().isSubClassActive()) + { + getActiveChar().getSubClasses().get(getActiveChar().getClassIndex()).setSp(value); + } + else + { + super.setSp(value); + } + } + + @Override + public int getRunSpeed() + { + if (getActiveChar() == null) + { + return 1; + } + + int val; + + L2PcInstance player = getActiveChar(); + if (player.isMounted()) + { + int baseRunSpd = NpcTable.getInstance().getTemplate(getActiveChar().getMountNpcId()).baseRunSpd; + val = (int) Math.round(calcStat(Stats.RUN_SPEED, baseRunSpd, null, null)); + } + else + { + val = super.getRunSpeed(); + } + + val += Config.RUN_SPD_BOOST; + + // Apply max run speed cap. + if ((val > Config.MAX_RUN_SPEED) && !getActiveChar().isGM()) + { + return Config.MAX_RUN_SPEED; + } + + return val; + } + + @Override + public int getPAtkSpd() + { + int val = super.getPAtkSpd(); + + if ((val > Config.MAX_PATK_SPEED) && !getActiveChar().isGM()) + { + return Config.MAX_PATK_SPEED; + } + + return val; + } + + @Override + public int getEvasionRate(L2Character target) + { + int val = super.getEvasionRate(target); + + if ((val > Config.MAX_EVASION) && !getActiveChar().isGM()) + { + return Config.MAX_EVASION; + } + + return val; + } + + @Override + public int getMAtkSpd() + { + int val = super.getMAtkSpd(); + + if ((val > Config.MAX_MATK_SPEED) && !getActiveChar().isGM()) + { + return Config.MAX_MATK_SPEED; + } + + return val; + } + + @Override + public float getMovementSpeedMultiplier() + { + if (getActiveChar() == null) + { + return 1; + } + + if (getActiveChar().isMounted()) + { + return (getRunSpeed() * 1f) / NpcTable.getInstance().getTemplate(getActiveChar().getMountNpcId()).baseRunSpd; + } + + return super.getMovementSpeedMultiplier(); + } + + @Override + public int getWalkSpeed() + { + if (getActiveChar() == null) + { + return 1; + } + + return (getRunSpeed() * 70) / 100; + } + + private void updateVitalityLevel(boolean quiet) + { + final byte level; + + if (_vitalityPoints <= VITALITY_LEVELS[0]) + { + level = 0; + } + else if (_vitalityPoints <= VITALITY_LEVELS[1]) + { + level = 1; + } + else if (_vitalityPoints <= VITALITY_LEVELS[2]) + { + level = 2; + } + else if (_vitalityPoints <= VITALITY_LEVELS[3]) + { + level = 3; + } + else + { + level = 4; + } + + if (!quiet && (level != _vitalityLevel)) + { + if (level < _vitalityLevel) + { + getActiveChar().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.VITALITY_HAS_DECREASED)); + } + else + { + getActiveChar().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.VITALITY_HAS_INCREASED)); + } + if (level == 0) + { + getActiveChar().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.VITALITY_IS_EXHAUSTED)); + } + else if (level == 4) + { + getActiveChar().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.VITALITY_IS_AT_MAXIMUM)); + } + } + + _vitalityLevel = level; + } + + /* + * Return current vitality points in integer format + */ + public int getVitalityPoints() + { + return (int) _vitalityPoints; + } + + /* + * Set current vitality points to this value if quiet = true - does not send system messages + */ + public void setVitalityPoints(int points, boolean quiet) + { + points = Math.min(Math.max(points, MIN_VITALITY_POINTS), MAX_VITALITY_POINTS); + if (points == _vitalityPoints) + { + return; + } + + _vitalityPoints = points; + updateVitalityLevel(quiet); + getActiveChar().sendPacket(new ExVitalityPointInfo(getVitalityPoints())); + } + + public synchronized void updateVitalityPoints(float points, boolean useRates, boolean quiet) + { + if ((points == 0) || !Config.ENABLE_VITALITY) + { + return; + } + + if (useRates) + { + if (getActiveChar().isLucky()) + { + return; + } + + if (points < 0) // vitality consumed + { + int stat = (int) calcStat(Stats.VITALITY_CONSUME_RATE, 1, getActiveChar(), null); + + if (stat == 0) + { + return; + } + if (stat < 0) + { + points = -points; + } + } + + if (points > 0) + { + // vitality increased + points *= Config.RATE_VITALITY_GAIN; + } + else + { + // vitality decreased + points *= Config.RATE_VITALITY_LOST; + } + } + + if (points > 0) + { + points = Math.min(_vitalityPoints + points, MAX_VITALITY_POINTS); + } + else + { + points = Math.max(_vitalityPoints + points, MIN_VITALITY_POINTS); + } + + if (points == _vitalityPoints) + { + return; + } + + _vitalityPoints = points; + updateVitalityLevel(quiet); + } + + /** + * @return the _vitalityLevel + */ + public byte getVitalityLevel() + { + return _vitalityLevel; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/PetStat.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/PetStat.java new file mode 100644 index 00000000..b9e9b20f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/PetStat.java @@ -0,0 +1,258 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.stat; + +import com.l2jserver.gameserver.datatables.PetDataTable; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2PetInstance; +import com.l2jserver.gameserver.model.base.Experience; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.SocialAction; +import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.skills.Stats; + +public class PetStat extends SummonStat +{ + public PetStat(L2PetInstance activeChar) + { + super(activeChar); + } + + public boolean addExp(int value) + { + if (!super.addExp(value)) + { + return false; + } + + getActiveChar().updateAndBroadcastStatus(1); + // The PetInfo packet wipes the PartySpelled (list of active spells' icons). Re-add them + getActiveChar().updateEffectIcons(true); + + return true; + } + + @Override + public boolean addExpAndSp(long addToExp, int addToSp) + { + if (!super.addExpAndSp(addToExp, addToSp)) + { + return false; + } + + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.PET_EARNED_S1_EXP); + sm.addNumber((int) addToExp); + getActiveChar().updateAndBroadcastStatus(1); + getActiveChar().getOwner().sendPacket(sm); + + return true; + } + + @Override + public final boolean addLevel(byte value) + { + if ((getLevel() + value) > (getMaxLevel() - 1)) + { + return false; + } + + boolean levelIncreased = super.addLevel(value); + + // Sync up exp with current level + // if (getExp() > getExpForLevel(getLevel() + 1) || getExp() < getExpForLevel(getLevel())) setExp(Experience.LEVEL[getLevel()]); + + // TODO : proper system msg if is any + // if (levelIncreased) getActiveChar().getOwner().sendMessage("Your pet has increased it's level."); + + StatusUpdate su = new StatusUpdate(getActiveChar()); + su.addAttribute(StatusUpdate.LEVEL, getLevel()); + su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); + su.addAttribute(StatusUpdate.MAX_MP, getMaxMp()); + getActiveChar().broadcastPacket(su); + if (levelIncreased) + { + getActiveChar().broadcastPacket(new SocialAction(getActiveChar(), SocialAction.LEVEL_UP)); + } + // Send a Server->Client packet PetInfo to the L2PcInstance + getActiveChar().updateAndBroadcastStatus(1); + + if (getActiveChar().getControlItem() != null) + { + getActiveChar().getControlItem().setEnchantLevel(getLevel()); + } + + return levelIncreased; + } + + @Override + public final long getExpForLevel(int level) + { + try + { + return PetDataTable.getInstance().getPetLevelData(getActiveChar().getNpcId(), level).getPetMaxExp(); + } + catch (NullPointerException e) + { + if (getActiveChar() != null) + { + _log.warning("Pet objectId:" + getActiveChar().getObjectId() + ", NpcId:" + getActiveChar().getNpcId() + ", level:" + level + " is missing data from pets_stats table!"); + } + throw e; + } + } + + @Override + public L2PetInstance getActiveChar() + { + return (L2PetInstance) super.getActiveChar(); + } + + public final int getFeedBattle() + { + return getActiveChar().getPetLevelData().getPetFeedBattle(); + } + + public final int getFeedNormal() + { + return getActiveChar().getPetLevelData().getPetFeedNormal(); + } + + @Override + public void setLevel(byte value) + { + getActiveChar().setPetData(PetDataTable.getInstance().getPetLevelData(getActiveChar().getTemplate().npcId, value)); + if (getActiveChar().getPetLevelData() == null) + { + throw new IllegalArgumentException("No pet data for npc: " + getActiveChar().getTemplate().npcId + " level: " + value); + } + getActiveChar().stopFeed(); + super.setLevel(value); + + getActiveChar().startFeed(); + + if (getActiveChar().getControlItem() != null) + { + getActiveChar().getControlItem().setEnchantLevel(getLevel()); + } + } + + public final int getMaxFeed() + { + return getActiveChar().getPetLevelData().getPetMaxFeed(); + } + + @Override + public int getMaxVisibleHp() + { + return (int) calcStat(Stats.MAX_HP, getActiveChar().getPetLevelData().getPetMaxHP(), null, null); + } + + @Override + public int getMaxMp() + { + return (int) calcStat(Stats.MAX_MP, getActiveChar().getPetLevelData().getPetMaxMP(), null, null); + } + + @Override + public int getMAtk(L2Character target, L2Skill skill) + { + double attack = getActiveChar().getPetLevelData().getPetMAtk(); + Stats stat = skill == null ? null : skill.getStat(); + if (stat != null) + { + switch (stat) + { + case AGGRESSION: + attack += getActiveChar().getTemplate().baseAggression; + break; + case BLEED: + attack += getActiveChar().getTemplate().baseBleed; + break; + case POISON: + attack += getActiveChar().getTemplate().basePoison; + break; + case STUN: + attack += getActiveChar().getTemplate().baseStun; + break; + case ROOT: + attack += getActiveChar().getTemplate().baseRoot; + break; + case MOVEMENT: + attack += getActiveChar().getTemplate().baseMovement; + break; + case CONFUSION: + attack += getActiveChar().getTemplate().baseConfusion; + break; + case SLEEP: + attack += getActiveChar().getTemplate().baseSleep; + break; + } + } + if (skill != null) + { + attack += skill.getPower(); + } + return (int) calcStat(Stats.MAGIC_ATTACK, attack, target, skill); + } + + @Override + public int getMDef(L2Character target, L2Skill skill) + { + double defence = getActiveChar().getPetLevelData().getPetMDef(); + return (int) calcStat(Stats.MAGIC_DEFENCE, defence, target, skill); + } + + @Override + public int getPAtk(L2Character target) + { + return (int) calcStat(Stats.POWER_ATTACK, getActiveChar().getPetLevelData().getPetPAtk(), target, null); + } + + @Override + public int getPDef(L2Character target) + { + return (int) calcStat(Stats.POWER_DEFENCE, getActiveChar().getPetLevelData().getPetPDef(), target, null); + } + + @Override + public int getPAtkSpd() + { + int val = super.getPAtkSpd(); + if (getActiveChar().isHungry()) + { + val = val / 2; + } + return val; + } + + @Override + public int getMAtkSpd() + { + int val = super.getMAtkSpd(); + if (getActiveChar().isHungry()) + { + val = val / 2; + } + return val; + } + + @Override + public int getMaxLevel() + { + return Experience.PET_MAX_LEVEL; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/PlayableStat.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/PlayableStat.java new file mode 100644 index 00000000..5f98af7a --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/PlayableStat.java @@ -0,0 +1,257 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.stat; + +import java.util.logging.Logger; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.datatables.PetDataTable; +import com.l2jserver.gameserver.instancemanager.ZoneManager; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PetInstance; +import com.l2jserver.gameserver.model.base.Experience; +import com.l2jserver.gameserver.model.zone.type.L2SwampZone; +import com.l2jserver.gameserver.network.communityserver.CommunityServerThread; +import com.l2jserver.gameserver.network.communityserver.writepackets.WorldInfo; + +public class PlayableStat extends CharStat +{ + protected static final Logger _log = Logger.getLogger(PlayableStat.class.getName()); + + public PlayableStat(L2Playable activeChar) + { + super(activeChar); + } + + public boolean addExp(long value) + { + if (((getExp() + value) < 0) || ((value > 0) && (getExp() == (getExpForLevel(getMaxLevel()) - 1)))) + { + return true; + } + + if ((getExp() + value) >= getExpForLevel(getMaxLevel())) + { + value = getExpForLevel(getMaxLevel()) - 1 - getExp(); + } + + setExp(getExp() + value); + + byte minimumLevel = 1; + if (getActiveChar() instanceof L2PetInstance) + { + // get minimum level from L2NpcTemplate + minimumLevel = (byte) PetDataTable.getInstance().getPetMinLevel(((L2PetInstance) getActiveChar()).getTemplate().npcId); + } + + byte level = minimumLevel; // minimum level + + for (byte tmp = level; tmp <= getMaxLevel(); tmp++) + { + if (getExp() >= getExpForLevel(tmp)) + { + continue; + } + level = --tmp; + break; + } + if ((level != getLevel()) && (level >= minimumLevel)) + { + addLevel((byte) (level - getLevel())); + } + + return true; + } + + public boolean removeExp(long value) + { + if ((getExp() - value) < 0) + { + value = getExp() - 1; + } + + setExp(getExp() - value); + + byte minimumLevel = 1; + if (getActiveChar() instanceof L2PetInstance) + { + // get minimum level from L2NpcTemplate + minimumLevel = (byte) PetDataTable.getInstance().getPetMinLevel(((L2PetInstance) getActiveChar()).getTemplate().npcId); + } + byte level = minimumLevel; + + for (byte tmp = level; tmp <= getMaxLevel(); tmp++) + { + if (getExp() >= getExpForLevel(tmp)) + { + continue; + } + level = --tmp; + break; + } + if ((level != getLevel()) && (level >= minimumLevel)) + { + addLevel((byte) (level - getLevel())); + } + return true; + } + + public boolean addExpAndSp(long addToExp, int addToSp) + { + boolean expAdded = false; + boolean spAdded = false; + if (addToExp >= 0) + { + expAdded = addExp(addToExp); + } + if (addToSp >= 0) + { + spAdded = addSp(addToSp); + } + + return expAdded || spAdded; + } + + public boolean removeExpAndSp(long removeExp, int removeSp) + { + boolean expRemoved = false; + boolean spRemoved = false; + if (removeExp > 0) + { + expRemoved = removeExp(removeExp); + } + if (removeSp > 0) + { + spRemoved = removeSp(removeSp); + } + + return expRemoved || spRemoved; + } + + public boolean addLevel(byte value) + { + if ((getLevel() + value) > (getMaxLevel() - 1)) + { + if (getLevel() < (getMaxLevel() - 1)) + { + value = (byte) (getMaxLevel() - 1 - getLevel()); + } + else + { + return false; + } + } + + boolean levelIncreased = ((getLevel() + value) > getLevel()); + value += getLevel(); + setLevel(value); + + // Sync up exp with current level + if ((getExp() >= getExpForLevel(getLevel() + 1)) || (getExpForLevel(getLevel()) > getExp())) + { + setExp(getExpForLevel(getLevel())); + } + + if (!levelIncreased && (getActiveChar() instanceof L2PcInstance) && !((L2PcInstance) (getActiveChar())).isGM() && Config.DECREASE_SKILL_LEVEL) + { + ((L2PcInstance) (getActiveChar())).checkPlayerSkills(); + } + + if (!levelIncreased) + { + return false; + } + + getActiveChar().getStatus().setCurrentHp(getActiveChar().getStat().getMaxHp()); + getActiveChar().getStatus().setCurrentMp(getActiveChar().getStat().getMaxMp()); + if (getActiveChar() instanceof L2PcInstance) + { + CommunityServerThread.getInstance().sendPacket(new WorldInfo((L2PcInstance) getActiveChar(), null, WorldInfo.TYPE_UPDATE_PLAYER_DATA)); + } + + return true; + } + + public boolean addSp(int value) + { + if (value < 0) + { + _log.warning("wrong usage"); + return false; + } + int currentSp = getSp(); + if (currentSp == Integer.MAX_VALUE) + { + return false; + } + + if (currentSp > (Integer.MAX_VALUE - value)) + { + value = Integer.MAX_VALUE - currentSp; + } + + setSp(currentSp + value); + return true; + } + + public boolean removeSp(int value) + { + int currentSp = getSp(); + if (currentSp < value) + { + value = currentSp; + } + setSp(getSp() - value); + return true; + } + + public long getExpForLevel(int level) + { + return level; + } + + @Override + public int getRunSpeed() + { + int val = super.getRunSpeed(); + if (getActiveChar().isInsideZone(L2Character.ZONE_WATER)) + { + val /= 2; + } + + if (getActiveChar().isInsideZone(L2Character.ZONE_SWAMP)) + { + L2SwampZone zone = ZoneManager.getInstance().getZone(getActiveChar(), L2SwampZone.class); + int bonus = zone == null ? 0 : zone.getMoveBonus(); + double dbonus = bonus / 100.0; // % + val += val * dbonus; + } + + return val; + } + + @Override + public L2Playable getActiveChar() + { + return (L2Playable) super.getActiveChar(); + } + + public int getMaxLevel() + { + return Experience.MAX_LEVEL; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/StaticObjStat.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/StaticObjStat.java new file mode 100644 index 00000000..6b5ad9dc --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/StaticObjStat.java @@ -0,0 +1,52 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.stat; + +import com.l2jserver.gameserver.model.actor.instance.L2StaticObjectInstance; + +public class StaticObjStat extends CharStat +{ + // ========================================================= + // Data Field + + // ========================================================= + // Constructor + public StaticObjStat(L2StaticObjectInstance activeChar) + { + super(activeChar); + + setLevel((byte) 1); + } + + // ========================================================= + // Method - Public + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + @Override + public L2StaticObjectInstance getActiveChar() + { + return (L2StaticObjectInstance) super.getActiveChar(); + } + + @Override + public final byte getLevel() + { + return 1; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/SummonStat.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/SummonStat.java new file mode 100644 index 00000000..636a10d8 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/SummonStat.java @@ -0,0 +1,44 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.stat; + +import com.l2jserver.gameserver.model.actor.L2Summon; + +public class SummonStat extends PlayableStat +{ + // ========================================================= + // Data Field + + // ========================================================= + // Constructor + public SummonStat(L2Summon activeChar) + { + super(activeChar); + } + + // ========================================================= + // Method - Public + + // ========================================================= + // Method - Private + + // ========================================================= + // Property - Public + @Override + public L2Summon getActiveChar() + { + return (L2Summon) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/VehicleStat.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/VehicleStat.java new file mode 100644 index 00000000..9ce3d3b8 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/stat/VehicleStat.java @@ -0,0 +1,49 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.stat; + +import com.l2jserver.gameserver.model.actor.L2Vehicle; + +public class VehicleStat extends CharStat +{ + private float _moveSpeed = 0; + private int _rotationSpeed = 0; + + public VehicleStat(L2Vehicle activeChar) + { + super(activeChar); + } + + @Override + public float getMoveSpeed() + { + return _moveSpeed; + } + + public final void setMoveSpeed(float speed) + { + _moveSpeed = speed; + } + + public final int getRotationSpeed() + { + return _rotationSpeed; + } + + public final void setRotationSpeed(int speed) + { + _rotationSpeed = speed; + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/AttackableStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/AttackableStatus.java new file mode 100644 index 00000000..d42c207f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/AttackableStatus.java @@ -0,0 +1,71 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.status; + +import com.l2jserver.gameserver.model.actor.L2Attackable; +import com.l2jserver.gameserver.model.actor.L2Character; + +public class AttackableStatus extends NpcStatus +{ + public AttackableStatus(L2Attackable activeChar) + { + super(activeChar); + } + + @Override + public final void reduceHp(double value, L2Character attacker) + { + reduceHp(value, attacker, true, false, false); + } + + @Override + public final void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHpConsumption) + { + if (getActiveChar().isDead()) + { + return; + } + + if (value > 0) + { + if (getActiveChar().isOverhit()) + { + getActiveChar().setOverhitValues(attacker, value); + } + else + { + getActiveChar().overhitEnabled(false); + } + } + else + { + getActiveChar().overhitEnabled(false); + } + + super.reduceHp(value, attacker, awake, isDOT, isHpConsumption); + + if (!getActiveChar().isDead()) + { + // And the attacker's hit didn't kill the mob, clear the over-hit flag + getActiveChar().overhitEnabled(false); + } + } + + @Override + public L2Attackable getActiveChar() + { + return (L2Attackable) super.getActiveChar(); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/CharStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/CharStatus.java new file mode 100644 index 00000000..613c1cda --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/CharStatus.java @@ -0,0 +1,435 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.status; + +import java.util.Set; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastSet; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.actor.stat.CharStat; +import com.l2jserver.gameserver.skills.Formulas; +import com.l2jserver.util.Rnd; + +public class CharStatus +{ + protected static final Logger _log = Logger.getLogger(CharStatus.class.getName()); + + private final L2Character _activeChar; + + private double _currentHp = 0; // Current HP of the L2Character + private double _currentMp = 0; // Current MP of the L2Character + + /** Array containing all clients that need to be notified about hp/mp updates of the L2Character */ + private Set _StatusListener; + + private Future _regTask; + + protected byte _flagsRegenActive = 0; + + protected static final byte REGEN_FLAG_CP = 4; + private static final byte REGEN_FLAG_HP = 1; + private static final byte REGEN_FLAG_MP = 2; + + public CharStatus(L2Character activeChar) + { + _activeChar = activeChar; + } + + /** + * Add the object to the list of L2Character that must be informed of HP/MP updates of this L2Character.
    + *
    + * Concept :
    + *
    + * Each L2Character owns a list called _statusListener that contains all L2PcInstance to inform of HP/MP updates. Players who must be informed are players that target this L2Character. When a RegenTask is in progress sever just need to go through this list to send Server->Client packet + * StatusUpdate.
    + *
    + * Example of use :
    + *
    + *
  • Target a PC or NPC

  • + *
    + * @param object L2Character to add to the listener + */ + public final void addStatusListener(L2Character object) + { + if (object == getActiveChar()) + { + return; + } + + getStatusListener().add(object); + } + + /** + * Remove the object from the list of L2Character that must be informed of HP/MP updates of this L2Character.
    + *
    + * Concept :
    + *
    + * Each L2Character owns a list called _statusListener that contains all L2PcInstance to inform of HP/MP updates. Players who must be informed are players that target this L2Character. When a RegenTask is in progress sever just need to go through this list to send Server->Client packet + * StatusUpdate.
    + *
    + * Example of use :
    + *
    + *
  • Untarget a PC or NPC

  • + *
    + * @param object L2Character to add to the listener + */ + public final void removeStatusListener(L2Character object) + { + getStatusListener().remove(object); + } + + /** + * Return the list of L2Character that must be informed of HP/MP updates of this L2Character.
    + *
    + * Concept :
    + *
    + * Each L2Character owns a list called _statusListener that contains all L2PcInstance to inform of HP/MP updates. Players who must be informed are players that target this L2Character. When a RegenTask is in progress sever just need to go through this list to send Server->Client packet + * StatusUpdate.
    + *
    + * @return The list of L2Character to inform or null if empty + */ + public final Set getStatusListener() + { + if (_StatusListener == null) + { + _StatusListener = new FastSet().shared(); + } + return _StatusListener; + } + + // place holder, only PcStatus has CP + public void reduceCp(int value) + { + } + + /** + * Reduce the current HP of the L2Character and launch the doDie Task if necessary.
    + *
    + * Overridden in :
    + *
    + *
  • L2Attackable : Set overhit values

  • + *
  • L2Npc : Update the attacker AggroInfo of the L2Attackable _aggroList and clear duel status of the attacking players

  • + *
    + * @param i The HP decrease value + * @param attacker The L2Character who attacks + * @param awake The awake state (If True : stop sleeping) + */ + public void reduceHp(double value, L2Character attacker) + { + reduceHp(value, attacker, true, false, false); + } + + public void reduceHp(double value, L2Character attacker, boolean isHpConsumption) + { + reduceHp(value, attacker, true, false, isHpConsumption); + } + + public void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHPConsumption) + { + if (getActiveChar().isDead()) + { + return; + } + + // invul handling + if (getActiveChar().isInvul()) + { + // other chars can't damage + if (attacker != getActiveChar()) + { + return; + } + + // only DOT and HP consumption allowed for damage self + if (!isDOT && !isHPConsumption) + { + return; + } + } + + if (attacker != null) + { + final L2PcInstance attackerPlayer = attacker.getActingPlayer(); + if ((attackerPlayer != null) && attackerPlayer.isGM() && !attackerPlayer.getAccessLevel().canGiveDamage()) + { + return; + } + } + + if (!isDOT && !isHPConsumption) + { + getActiveChar().stopEffectsOnDamage(awake); + if (getActiveChar().isStunned() && (Rnd.get(10) == 0)) + { + getActiveChar().stopStunning(true); + } + } + + if (value > 0) + { + setCurrentHp(Math.max(getCurrentHp() - value, 0)); + } + + if ((getActiveChar().getCurrentHp() < 0.5) && getActiveChar().isMortal()) // Die + { + getActiveChar().abortAttack(); + getActiveChar().abortCast(); + + if (Config.DEBUG) + { + _log.fine("char is dead."); + } + + getActiveChar().doDie(attacker); + } + } + + public void reduceMp(double value) + { + setCurrentMp(Math.max(getCurrentMp() - value, 0)); + } + + /** + * Start the HP/MP/CP Regeneration task.
    + *
    + * Actions :
    + *
    + *
  • Calculate the regen task period
  • Launch the HP/MP/CP Regeneration task with Medium priority

  • + *
    + */ + public final synchronized void startHpMpRegeneration() + { + if ((_regTask == null) && !getActiveChar().isDead()) + { + if (Config.DEBUG) + { + _log.fine("HP/MP regen started"); + } + + // Get the Regeneration periode + int period = Formulas.getRegeneratePeriod(getActiveChar()); + + // Create the HP/MP/CP Regeneration task + _regTask = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new RegenTask(), period, period); + } + } + + /** + * Stop the HP/MP/CP Regeneration task.
    + *
    + * Actions :
    + *
    + *
  • Set the RegenActive flag to False
  • Stop the HP/MP/CP Regeneration task

  • + *
    + */ + public final synchronized void stopHpMpRegeneration() + { + if (_regTask != null) + { + if (Config.DEBUG) + { + _log.fine("HP/MP regen stop"); + } + + // Stop the HP/MP/CP Regeneration task + _regTask.cancel(false); + _regTask = null; + + // Set the RegenActive flag to false + _flagsRegenActive = 0; + } + } + + // place holder, only PcStatus has CP + public double getCurrentCp() + { + return 0; + } + + // place holder, only PcStatus has CP + public void setCurrentCp(double newCp) + { + } + + public final double getCurrentHp() + { + return _currentHp; + } + + public final void setCurrentHp(double newHp) + { + setCurrentHp(newHp, true); + } + + public void setCurrentHp(double newHp, boolean broadcastPacket) + { + // Get the Max HP of the L2Character + final double maxHp = getActiveChar().getStat().getMaxHp(); + + synchronized (this) + { + if (getActiveChar().isDead()) + { + return; + } + + if (newHp >= maxHp) + { + // Set the RegenActive flag to false + _currentHp = maxHp; + _flagsRegenActive &= ~REGEN_FLAG_HP; + + // Stop the HP/MP/CP Regeneration task + if (_flagsRegenActive == 0) + { + stopHpMpRegeneration(); + } + } + else + { + // Set the RegenActive flag to true + _currentHp = newHp; + _flagsRegenActive |= REGEN_FLAG_HP; + + // Start the HP/MP/CP Regeneration task with Medium priority + startHpMpRegeneration(); + } + } + + // Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform + if (broadcastPacket) + { + getActiveChar().broadcastStatusUpdate(); + } + } + + public final void setCurrentHpMp(double newHp, double newMp) + { + setCurrentHp(newHp, false); + setCurrentMp(newMp, true); // send the StatusUpdate only once + } + + public final double getCurrentMp() + { + return _currentMp; + } + + public final void setCurrentMp(double newMp) + { + setCurrentMp(newMp, true); + } + + public final void setCurrentMp(double newMp, boolean broadcastPacket) + { + // Get the Max MP of the L2Character + final int maxMp = getActiveChar().getStat().getMaxMp(); + + synchronized (this) + { + if (getActiveChar().isDead()) + { + return; + } + + if (newMp >= maxMp) + { + // Set the RegenActive flag to false + _currentMp = maxMp; + _flagsRegenActive &= ~REGEN_FLAG_MP; + + // Stop the HP/MP/CP Regeneration task + if (_flagsRegenActive == 0) + { + stopHpMpRegeneration(); + } + } + else + { + // Set the RegenActive flag to true + _currentMp = newMp; + _flagsRegenActive |= REGEN_FLAG_MP; + + // Start the HP/MP/CP Regeneration task with Medium priority + startHpMpRegeneration(); + } + } + + // Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform + if (broadcastPacket) + { + getActiveChar().broadcastStatusUpdate(); + } + } + + protected void doRegeneration() + { + final CharStat charstat = getActiveChar().getStat(); + + // Modify the current HP of the L2Character and broadcast Server->Client packet StatusUpdate + if (getCurrentHp() < charstat.getMaxHp()) + { + setCurrentHp(getCurrentHp() + Formulas.calcHpRegen(getActiveChar()), false); + } + + // Modify the current MP of the L2Character and broadcast Server->Client packet StatusUpdate + if (getCurrentMp() < charstat.getMaxMp()) + { + setCurrentMp(getCurrentMp() + Formulas.calcMpRegen(getActiveChar()), false); + } + + if (!getActiveChar().isInActiveRegion()) + { + // no broadcast necessary for characters that are in inactive regions. + // stop regeneration for characters who are filled up and in an inactive region. + if ((getCurrentHp() == charstat.getMaxHp()) && (getCurrentMp() == charstat.getMaxMp())) + { + stopHpMpRegeneration(); + } + } + else + { + getActiveChar().broadcastStatusUpdate(); // send the StatusUpdate packet + } + } + + /** Task of HP/MP regeneration */ + class RegenTask implements Runnable + { + @Override + public void run() + { + try + { + doRegeneration(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } + + public L2Character getActiveChar() + { + return _activeChar; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/DoorStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/DoorStatus.java new file mode 100644 index 00000000..38a5aa4f --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/DoorStatus.java @@ -0,0 +1,31 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.status; + +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; + +public class DoorStatus extends CharStatus +{ + public DoorStatus(L2DoorInstance activeChar) + { + super(activeChar); + } + + @Override + public L2DoorInstance getActiveChar() + { + return (L2DoorInstance) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/FolkStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/FolkStatus.java new file mode 100644 index 00000000..f50ff586 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/FolkStatus.java @@ -0,0 +1,57 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.status; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.instance.L2ClanHallManagerInstance; +import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance; + +public class FolkStatus extends NpcStatus +{ + public FolkStatus(L2Npc activeChar) + { + super(activeChar); + } + + @Override + public final void reduceHp(double value, L2Character attacker) + { + reduceHp(value, attacker, true, false, false); + } + + @Override + public final void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHpConsumption) + { + } + + @Override + public final void reduceMp(double value) + { + // If Clan Hall buff are free and it's a Clan Hall Manager MP won't get reduced. + if (Config.CH_BUFF_FREE && (getActiveChar() instanceof L2ClanHallManagerInstance)) + { + return; + } + super.reduceMp(value); + } + + @Override + public L2NpcInstance getActiveChar() + { + return (L2NpcInstance) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/NpcStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/NpcStatus.java new file mode 100644 index 00000000..050c9673 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/NpcStatus.java @@ -0,0 +1,63 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.status; + +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.entity.Duel; + +public class NpcStatus extends CharStatus +{ + public NpcStatus(L2Npc activeChar) + { + super(activeChar); + } + + @Override + public void reduceHp(double value, L2Character attacker) + { + reduceHp(value, attacker, true, false, false); + } + + @Override + public void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHpConsumption) + { + if (getActiveChar().isDead()) + { + return; + } + + if (attacker != null) + { + final L2PcInstance attackerPlayer = attacker.getActingPlayer(); + if ((attackerPlayer != null) && attackerPlayer.isInDuel()) + { + attackerPlayer.setDuelState(Duel.DUELSTATE_INTERRUPTED); + } + + // Add attackers to npc's attacker list + getActiveChar().addAttackerToAttackByList(attacker); + } + + super.reduceHp(value, attacker, awake, isDOT, isHpConsumption); + } + + @Override + public L2Npc getActiveChar() + { + return (L2Npc) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/PcStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/PcStatus.java new file mode 100644 index 00000000..c8ba8c89 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/PcStatus.java @@ -0,0 +1,360 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.status; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.instancemanager.DuelManager; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Playable; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2SummonInstance; +import com.l2jserver.gameserver.model.actor.stat.PcStat; +import com.l2jserver.gameserver.model.entity.Duel; +import com.l2jserver.gameserver.model.quest.QuestState; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.skills.Formulas; +import com.l2jserver.gameserver.skills.Stats; +import com.l2jserver.gameserver.util.Util; +import com.l2jserver.util.Rnd; + +public class PcStatus extends PlayableStatus +{ + private double _currentCp = 0; // Current CP of the L2PcInstance + + public PcStatus(L2PcInstance activeChar) + { + super(activeChar); + } + + @Override + public final void reduceCp(int value) + { + if (getCurrentCp() > value) + { + setCurrentCp(getCurrentCp() - value); + } + else + { + setCurrentCp(0); + } + } + + @Override + public final void reduceHp(double value, L2Character attacker) + { + reduceHp(value, attacker, true, false, false, false); + } + + @Override + public final void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHPConsumption) + { + reduceHp(value, attacker, awake, isDOT, isHPConsumption, false); + } + + public final void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHPConsumption, boolean ignoreCP) + { + if (getActiveChar().isDead()) + { + return; + } + + if (getActiveChar().isInvul()) + { + if (attacker == getActiveChar()) + { + if (!isDOT && !isHPConsumption) + { + return; + } + } + else + { + return; + } + } + + if (!isHPConsumption) + { + getActiveChar().stopEffectsOnDamage(awake); + if (getActiveChar().isSitting()) + { + getActiveChar().standUp(); + } + + if (!isDOT) + { + if (getActiveChar().isStunned() && (Rnd.get(10) == 0)) + { + getActiveChar().stopStunning(true); + } + } + } + + int fullValue = (int) value; + int tDmg = 0; + + if ((attacker != null) && (attacker != getActiveChar())) + { + final L2PcInstance attackerPlayer = attacker.getActingPlayer(); + + if (attackerPlayer != null) + { + if (attackerPlayer.isGM() && !attackerPlayer.getAccessLevel().canGiveDamage()) + { + return; + } + + if (getActiveChar().isInDuel()) + { + if (getActiveChar().getDuelState() == Duel.DUELSTATE_DEAD) + { + return; + } + else if (getActiveChar().getDuelState() == Duel.DUELSTATE_WINNER) + { + return; + } + + // cancel duel if player got hit by another player, that is not part of the duel + if (attackerPlayer.getDuelId() != getActiveChar().getDuelId()) + { + getActiveChar().setDuelState(Duel.DUELSTATE_INTERRUPTED); + } + } + } + + // Check and calculate transfered damage + final L2Summon summon = getActiveChar().getPet(); + // TODO correct range + if ((summon != null) && (summon instanceof L2SummonInstance) && Util.checkIfInRange(900, getActiveChar(), summon, true)) + { + tDmg = ((int) value * (int) getActiveChar().getStat().calcStat(Stats.TRANSFER_DAMAGE_PERCENT, 0, null, null)) / 100; + + // Only transfer dmg up to current HP, it should not be killed + tDmg = Math.min((int) summon.getCurrentHp() - 1, tDmg); + if (tDmg > 0) + { + summon.reduceCurrentHp(tDmg, attacker, null); + value -= tDmg; + fullValue = (int) value; // reduce the announced value here as player will get a message about summon damage + } + } + + if (!ignoreCP && (attacker instanceof L2Playable)) + { + if (getCurrentCp() >= value) + { + setCurrentCp(getCurrentCp() - value); // Set Cp to diff of Cp vs value + value = 0; // No need to subtract anything from Hp + } + else + { + value -= getCurrentCp(); // Get diff from value vs Cp; will apply diff to Hp + setCurrentCp(0, false); // Set Cp to 0 + } + } + + if ((fullValue > 0) && !isDOT) + { + SystemMessage smsg; + // Send a System Message to the L2PcInstance + smsg = SystemMessage.getSystemMessage(SystemMessageId.C1_RECEIVED_DAMAGE_OF_S3_FROM_C2); + smsg.addString(getActiveChar().getName()); + smsg.addCharName(attacker); + smsg.addNumber(fullValue); + getActiveChar().sendPacket(smsg); + + if (tDmg > 0) + { + smsg = SystemMessage.getSystemMessage(SystemMessageId.C1_RECEIVED_DAMAGE_OF_S3_FROM_C2); + smsg.addString(getActiveChar().getPet().getName()); + smsg.addCharName(attacker); + smsg.addNumber(tDmg); + getActiveChar().sendPacket(smsg); + + if (attackerPlayer != null) + { + smsg = SystemMessage.getSystemMessage(SystemMessageId.GIVEN_S1_DAMAGE_TO_YOUR_TARGET_AND_S2_DAMAGE_TO_SERVITOR); + smsg.addNumber(fullValue); + smsg.addNumber(tDmg); + attackerPlayer.sendPacket(smsg); + } + } + } + + if (value > 0) + { + value = getCurrentHp() - value; + if (value <= 0) + { + if (getActiveChar().isInDuel()) + { + getActiveChar().disableAllSkills(); + stopHpMpRegeneration(); + attacker.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE); + attacker.sendPacket(ActionFailed.STATIC_PACKET); + + // let the DuelManager know of his defeat + DuelManager.getInstance().onPlayerDefeat(getActiveChar()); + value = 1; + } + else + { + value = 0; + } + } + setCurrentHp(value); + } + } + + if (getActiveChar().getCurrentHp() < 0.5) + { + getActiveChar().abortAttack(); + getActiveChar().abortCast(); + + if (getActiveChar().isInOlympiadMode()) + { + stopHpMpRegeneration(); + getActiveChar().setIsDead(true); + getActiveChar().setIsPendingRevive(true); + if (getActiveChar().getPet() != null) + { + getActiveChar().getPet().getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null); + } + return; + } + + getActiveChar().doDie(attacker); + if (!Config.DISABLE_TUTORIAL) + { + QuestState qs = getActiveChar().getQuestState("255_Tutorial"); + if (qs != null) + { + qs.getQuest().notifyEvent("CE30", null, getActiveChar()); + } + } + } + } + + @Override + public final void setCurrentHp(double newHp, boolean broadcastPacket) + { + super.setCurrentHp(newHp, broadcastPacket); + + if (!Config.DISABLE_TUTORIAL && (getCurrentHp() <= (getActiveChar().getStat().getMaxHp() * .3))) + { + QuestState qs = getActiveChar().getQuestState("255_Tutorial"); + if (qs != null) + { + qs.getQuest().notifyEvent("CE45", null, getActiveChar()); + } + } + } + + @Override + public final double getCurrentCp() + { + return _currentCp; + } + + @Override + public final void setCurrentCp(double newCp) + { + setCurrentCp(newCp, true); + } + + public final void setCurrentCp(double newCp, boolean broadcastPacket) + { + // Get the Max CP of the L2Character + int maxCp = getActiveChar().getStat().getMaxCp(); + + synchronized (this) + { + if (getActiveChar().isDead()) + { + return; + } + + if (newCp < 0) + { + newCp = 0; + } + + if (newCp >= maxCp) + { + // Set the RegenActive flag to false + _currentCp = maxCp; + _flagsRegenActive &= ~REGEN_FLAG_CP; + + // Stop the HP/MP/CP Regeneration task + if (_flagsRegenActive == 0) + { + stopHpMpRegeneration(); + } + } + else + { + // Set the RegenActive flag to true + _currentCp = newCp; + _flagsRegenActive |= REGEN_FLAG_CP; + + // Start the HP/MP/CP Regeneration task with Medium priority + startHpMpRegeneration(); + } + } + + // Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform + if (broadcastPacket) + { + getActiveChar().broadcastStatusUpdate(); + } + } + + @Override + protected void doRegeneration() + { + final PcStat charstat = getActiveChar().getStat(); + + // Modify the current CP of the L2Character and broadcast Server->Client packet StatusUpdate + if (getCurrentCp() < charstat.getMaxCp()) + { + setCurrentCp(getCurrentCp() + Formulas.calcCpRegen(getActiveChar()), false); + } + + // Modify the current HP of the L2Character and broadcast Server->Client packet StatusUpdate + if (getCurrentHp() < charstat.getMaxHp()) + { + setCurrentHp(getCurrentHp() + Formulas.calcHpRegen(getActiveChar()), false); + } + + // Modify the current MP of the L2Character and broadcast Server->Client packet StatusUpdate + if (getCurrentMp() < charstat.getMaxMp()) + { + setCurrentMp(getCurrentMp() + Formulas.calcMpRegen(getActiveChar()), false); + } + + getActiveChar().broadcastStatusUpdate(); // send the StatusUpdate packet + } + + @Override + public L2PcInstance getActiveChar() + { + return (L2PcInstance) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/PetStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/PetStatus.java new file mode 100644 index 00000000..919fe289 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/PetStatus.java @@ -0,0 +1,76 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.status; + +import com.l2jserver.gameserver.ai.CtrlEvent; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2PetInstance; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; + +public class PetStatus extends SummonStatus +{ + private int _currentFed = 0; // Current Fed of the L2PetInstance + + public PetStatus(L2PetInstance activeChar) + { + super(activeChar); + } + + @Override + public final void reduceHp(double value, L2Character attacker) + { + reduceHp(value, attacker, true, false, false); + } + + @Override + public final void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHpConsumption) + { + if (getActiveChar().isDead()) + { + return; + } + + super.reduceHp(value, attacker, awake, isDOT, isHpConsumption); + + if (attacker != null) + { + if (!isDOT && (getActiveChar().getOwner() != null)) + { + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.PET_RECEIVED_S2_DAMAGE_BY_C1); + sm.addCharName(attacker); + sm.addNumber((int) value); + getActiveChar().getOwner().sendPacket(sm); + } + getActiveChar().getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, attacker); + } + } + + public int getCurrentFed() + { + return _currentFed; + } + + public void setCurrentFed(int value) + { + _currentFed = value; + } + + @Override + public L2PetInstance getActiveChar() + { + return (L2PetInstance) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/PlayableStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/PlayableStatus.java new file mode 100644 index 00000000..bb2a56c5 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/PlayableStatus.java @@ -0,0 +1,31 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.status; + +import com.l2jserver.gameserver.model.actor.L2Playable; + +public class PlayableStatus extends CharStatus +{ + public PlayableStatus(L2Playable activeChar) + { + super(activeChar); + } + + @Override + public L2Playable getActiveChar() + { + return (L2Playable) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/SiegeFlagStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/SiegeFlagStatus.java new file mode 100644 index 00000000..c0b6b72d --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/SiegeFlagStatus.java @@ -0,0 +1,35 @@ +package com.l2jserver.gameserver.model.actor.status; + +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2SiegeFlagInstance; + +public class SiegeFlagStatus extends NpcStatus +{ + public SiegeFlagStatus(L2SiegeFlagInstance activeChar) + { + super(activeChar); + } + + @Override + public void reduceHp(double value, L2Character attacker) + { + reduceHp(value, attacker, true, false, false); + } + + @Override + public void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHpConsumption) + { + if (getActiveChar().isAdvancedHeadquarter()) + { + value /= 2.; + } + + super.reduceHp(value, attacker, awake, isDOT, isHpConsumption); + } + + @Override + public L2SiegeFlagInstance getActiveChar() + { + return (L2SiegeFlagInstance) super.getActiveChar(); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/StaticObjStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/StaticObjStatus.java new file mode 100644 index 00000000..b96ba3f9 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/StaticObjStatus.java @@ -0,0 +1,31 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.status; + +import com.l2jserver.gameserver.model.actor.instance.L2StaticObjectInstance; + +public class StaticObjStatus extends CharStatus +{ + public StaticObjStatus(L2StaticObjectInstance activeChar) + { + super(activeChar); + } + + @Override + public L2StaticObjectInstance getActiveChar() + { + return (L2StaticObjectInstance) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/SummonStatus.java b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/SummonStatus.java new file mode 100644 index 00000000..51cfd696 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/actor/status/SummonStatus.java @@ -0,0 +1,59 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.actor.status; + +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.L2Summon; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.entity.Duel; + +public class SummonStatus extends PlayableStatus +{ + public SummonStatus(L2Summon activeChar) + { + super(activeChar); + } + + @Override + public void reduceHp(double value, L2Character attacker) + { + reduceHp(value, attacker, true, false, false); + } + + @Override + public void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHPConsumption) + { + if (getActiveChar().isDead()) + { + return; + } + + if (attacker != null) + { + final L2PcInstance attackerPlayer = attacker.getActingPlayer(); + if ((attackerPlayer != null) && ((getActiveChar().getOwner() == null) || (getActiveChar().getOwner().getDuelId() != attackerPlayer.getDuelId()))) + { + attackerPlayer.setDuelState(Duel.DUELSTATE_INTERRUPTED); + } + } + super.reduceHp(value, attacker, awake, isDOT, isHPConsumption); + } + + @Override + public L2Summon getActiveChar() + { + return (L2Summon) super.getActiveChar(); + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/base/ClassId.java b/L2J_Server/java/com/l2jserver/gameserver/model/base/ClassId.java new file mode 100644 index 00000000..28d2e92c --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/base/ClassId.java @@ -0,0 +1,324 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.base; + +/** + * This class defines all classes (ex : human fighter, darkFighter...) that a player can chose.
    + *
    + * Data :
    + *
    + *
  • id : The Identifier of the class
  • isMage : True if the class is a mage class
  • race : The race of this class
  • parent : The parent ClassId or null if this class is the root

  • + *
    + * @version $Revision: 1.4.4.4 $ $Date: 2005/03/27 15:29:33 $ + */ +public enum ClassId +{ + fighter(0x00, false, Race.Human, null), + + warrior(0x01, false, Race.Human, fighter), + gladiator(0x02, false, Race.Human, warrior), + warlord(0x03, false, Race.Human, warrior), + knight(0x04, false, Race.Human, fighter), + paladin(0x05, false, Race.Human, knight), + darkAvenger(0x06, false, Race.Human, knight), + rogue(0x07, false, Race.Human, fighter), + treasureHunter(0x08, false, Race.Human, rogue), + hawkeye(0x09, false, Race.Human, rogue), + + mage(0x0a, true, Race.Human, null), + wizard(0x0b, true, Race.Human, mage), + sorceror(0x0c, true, Race.Human, wizard), + necromancer(0x0d, true, Race.Human, wizard), + warlock(0x0e, true, true, Race.Human, wizard), + cleric(0x0f, true, Race.Human, mage), + bishop(0x10, true, Race.Human, cleric), + prophet(0x11, true, Race.Human, cleric), + + elvenFighter(0x12, false, Race.Elf, null), + elvenKnight(0x13, false, Race.Elf, elvenFighter), + templeKnight(0x14, false, Race.Elf, elvenKnight), + swordSinger(0x15, false, Race.Elf, elvenKnight), + elvenScout(0x16, false, Race.Elf, elvenFighter), + plainsWalker(0x17, false, Race.Elf, elvenScout), + silverRanger(0x18, false, Race.Elf, elvenScout), + + elvenMage(0x19, true, Race.Elf, null), + elvenWizard(0x1a, true, Race.Elf, elvenMage), + spellsinger(0x1b, true, Race.Elf, elvenWizard), + elementalSummoner(0x1c, true, true, Race.Elf, elvenWizard), + oracle(0x1d, true, Race.Elf, elvenMage), + elder(0x1e, true, Race.Elf, oracle), + + darkFighter(0x1f, false, Race.DarkElf, null), + palusKnight(0x20, false, Race.DarkElf, darkFighter), + shillienKnight(0x21, false, Race.DarkElf, palusKnight), + bladedancer(0x22, false, Race.DarkElf, palusKnight), + assassin(0x23, false, Race.DarkElf, darkFighter), + abyssWalker(0x24, false, Race.DarkElf, assassin), + phantomRanger(0x25, false, Race.DarkElf, assassin), + + darkMage(0x26, true, Race.DarkElf, null), + darkWizard(0x27, true, Race.DarkElf, darkMage), + spellhowler(0x28, true, Race.DarkElf, darkWizard), + phantomSummoner(0x29, true, true, Race.DarkElf, darkWizard), + shillienOracle(0x2a, true, Race.DarkElf, darkMage), + shillenElder(0x2b, true, Race.DarkElf, shillienOracle), + + orcFighter(0x2c, false, Race.Orc, null), + orcRaider(0x2d, false, Race.Orc, orcFighter), + destroyer(0x2e, false, Race.Orc, orcRaider), + orcMonk(0x2f, false, Race.Orc, orcFighter), + tyrant(0x30, false, Race.Orc, orcMonk), + + orcMage(0x31, false, Race.Orc, null), + orcShaman(0x32, true, Race.Orc, orcMage), + overlord(0x33, true, Race.Orc, orcShaman), + warcryer(0x34, true, Race.Orc, orcShaman), + + dwarvenFighter(0x35, false, Race.Dwarf, null), + scavenger(0x36, false, Race.Dwarf, dwarvenFighter), + bountyHunter(0x37, false, Race.Dwarf, scavenger), + artisan(0x38, false, Race.Dwarf, dwarvenFighter), + warsmith(0x39, false, Race.Dwarf, artisan), + + /* + * Dummy Entries (id's already in decimal format) btw FU NCSoft for the amount of work you put me through to do this!! + */ + dummyEntry1(58, false, null, null), + dummyEntry2(59, false, null, null), + dummyEntry3(60, false, null, null), + dummyEntry4(61, false, null, null), + dummyEntry5(62, false, null, null), + dummyEntry6(63, false, null, null), + dummyEntry7(64, false, null, null), + dummyEntry8(65, false, null, null), + dummyEntry9(66, false, null, null), + dummyEntry10(67, false, null, null), + dummyEntry11(68, false, null, null), + dummyEntry12(69, false, null, null), + dummyEntry13(70, false, null, null), + dummyEntry14(71, false, null, null), + dummyEntry15(72, false, null, null), + dummyEntry16(73, false, null, null), + dummyEntry17(74, false, null, null), + dummyEntry18(75, false, null, null), + dummyEntry19(76, false, null, null), + dummyEntry20(77, false, null, null), + dummyEntry21(78, false, null, null), + dummyEntry22(79, false, null, null), + dummyEntry23(80, false, null, null), + dummyEntry24(81, false, null, null), + dummyEntry25(82, false, null, null), + dummyEntry26(83, false, null, null), + dummyEntry27(84, false, null, null), + dummyEntry28(85, false, null, null), + dummyEntry29(86, false, null, null), + dummyEntry30(87, false, null, null), + /* + * Of Dummy entries + */ + + /* + * Now the bad boys! new class ids :)) (3rd classes) + */ + duelist(0x58, false, Race.Human, gladiator), + dreadnought(0x59, false, Race.Human, warlord), + phoenixKnight(0x5a, false, Race.Human, paladin), + hellKnight(0x5b, false, Race.Human, darkAvenger), + sagittarius(0x5c, false, Race.Human, hawkeye), + adventurer(0x5d, false, Race.Human, treasureHunter), + archmage(0x5e, true, Race.Human, sorceror), + soultaker(0x5f, true, Race.Human, necromancer), + arcanaLord(0x60, true, true, Race.Human, warlock), + cardinal(0x61, true, Race.Human, bishop), + hierophant(0x62, true, Race.Human, prophet), + + evaTemplar(0x63, false, Race.Elf, templeKnight), + swordMuse(0x64, false, Race.Elf, swordSinger), + windRider(0x65, false, Race.Elf, plainsWalker), + moonlightSentinel(0x66, false, Race.Elf, silverRanger), + mysticMuse(0x67, true, Race.Elf, spellsinger), + elementalMaster(0x68, true, true, Race.Elf, elementalSummoner), + evaSaint(0x69, true, Race.Elf, elder), + + shillienTemplar(0x6a, false, Race.DarkElf, shillienKnight), + spectralDancer(0x6b, false, Race.DarkElf, bladedancer), + ghostHunter(0x6c, false, Race.DarkElf, abyssWalker), + ghostSentinel(0x6d, false, Race.DarkElf, phantomRanger), + stormScreamer(0x6e, true, Race.DarkElf, spellhowler), + spectralMaster(0x6f, true, true, Race.DarkElf, phantomSummoner), + shillienSaint(0x70, true, Race.DarkElf, shillenElder), + + titan(0x71, false, Race.Orc, destroyer), + grandKhauatari(0x72, false, Race.Orc, tyrant), + dominator(0x73, true, Race.Orc, overlord), + doomcryer(0x74, true, Race.Orc, warcryer), + + fortuneSeeker(0x75, false, Race.Dwarf, bountyHunter), + maestro(0x76, false, Race.Dwarf, warsmith), + + dummyEntry31(0x77, false, null, null), + dummyEntry32(0x78, false, null, null), + dummyEntry33(0x79, false, null, null), + dummyEntry34(0x7a, false, null, null), + + maleSoldier(0x7b, false, Race.Kamael, null), + femaleSoldier(0x7C, false, Race.Kamael, null), + trooper(0x7D, false, Race.Kamael, maleSoldier), + warder(0x7E, false, Race.Kamael, femaleSoldier), + berserker(0x7F, false, Race.Kamael, trooper), + maleSoulbreaker(0x80, false, Race.Kamael, trooper), + femaleSoulbreaker(0x81, false, Race.Kamael, warder), + arbalester(0x82, false, Race.Kamael, warder), + doombringer(0x83, false, Race.Kamael, berserker), + maleSoulhound(0x84, false, Race.Kamael, maleSoulbreaker), + femaleSoulhound(0x85, false, Race.Kamael, femaleSoulbreaker), + trickster(0x86, false, Race.Kamael, arbalester), + inspector(0x87, false, Race.Kamael, warder), // DS: yes, both male/female inspectors use skills from warder + judicator(0x88, false, Race.Kamael, inspector); + + /** The Identifier of the Class */ + private final int _id; + + /** True if the class is a mage class */ + private final boolean _isMage; + + /** True if the class is a summoner class */ + private final boolean _isSummoner; + + /** The Race object of the class */ + private final Race _race; + + /** The parent ClassId or null if this class is a root */ + private final ClassId _parent; + + /** + * Constructor of ClassId.
    + *
    + */ + private ClassId(int pId, boolean pIsMage, Race pRace, ClassId pParent) + { + _id = pId; + _isMage = pIsMage; + _isSummoner = false; + _race = pRace; + _parent = pParent; + } + + /** + * Constructor of ClassId.
    + *
    + */ + private ClassId(int pId, boolean pIsMage, boolean pIsSummoner, Race pRace, ClassId pParent) + { + _id = pId; + _isMage = pIsMage; + _isSummoner = pIsSummoner; + _race = pRace; + _parent = pParent; + } + + /** + * Return the Identifier of the Class.
    + *
    + */ + public final int getId() + { + return _id; + } + + /** + * Return True if the class is a mage class.
    + *
    + */ + public final boolean isMage() + { + return _isMage; + } + + /** + * Return True if the class is a summoner class.
    + *
    + */ + public final boolean isSummoner() + { + return _isSummoner; + } + + /** + * Return the Race object of the class.
    + *
    + */ + public final Race getRace() + { + return _race; + } + + /** + * Return True if this Class is a child of the selected ClassId.
    + *
    + * @param cid The parent ClassId to check + */ + public final boolean childOf(ClassId cid) + { + if (_parent == null) + { + return false; + } + + if (_parent == cid) + { + return true; + } + + return _parent.childOf(cid); + + } + + /** + * Return True if this Class is equal to the selected ClassId or a child of the selected ClassId.
    + *
    + * @param cid The parent ClassId to check + */ + public final boolean equalsOrChildOf(ClassId cid) + { + return (this == cid) || childOf(cid); + } + + /** + * Return the child level of this Class (0=root, 1=child leve 1...).
    + *
    + * @param cid The parent ClassId to check + */ + public final int level() + { + if (_parent == null) + { + return 0; + } + + return 1 + _parent.level(); + } + + /** + * Return its parent ClassId
    + *
    + */ + public final ClassId getParent() + { + return _parent; + } + +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/base/ClassLevel.java b/L2J_Server/java/com/l2jserver/gameserver/model/base/ClassLevel.java new file mode 100644 index 00000000..9e7e8d27 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/base/ClassLevel.java @@ -0,0 +1,38 @@ +/* + * $Header: ClassLevel.java, 24/11/2005 15:56:45 luisantonioa Exp $ + * + * $Author: luisantonioa $ + * $Date: 24/11/2005 15:56:45 $ + * $Revision: 1 $ + * $Log: ClassLevel.java,v $ + * Revision 1 24/11/2005 15:56:45 luisantonioa + * Added copyright notice + * + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.base; + +/** + * This class ... + * @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $ + */ + +public enum ClassLevel +{ + First, + Second, + Third, + Fourth +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/base/ClassType.java b/L2J_Server/java/com/l2jserver/gameserver/model/base/ClassType.java new file mode 100644 index 00000000..30b7eeae --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/base/ClassType.java @@ -0,0 +1,27 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.base; + +/** + * ClassType Enum + * @author Tempy + */ + +public enum ClassType +{ + Fighter, + Mystic, + Priest +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/base/Experience.java b/L2J_Server/java/com/l2jserver/gameserver/model/base/Experience.java new file mode 100644 index 00000000..3e78bb20 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/base/Experience.java @@ -0,0 +1,126 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.base; + +/** + + * + */ +public class Experience +{ + public final static long LEVEL[] = + { + -1L, // level 0 (unreachable) + 0L, + 68L, + 363L, + 1168L, + 2884L, + 6038L, + 11287L, + 19423L, + 31378L, + 48229L, // level 10 + 71201L, + 101676L, + 141192L, + 191452L, + 254327L, + 331864L, + 426284L, + 539995L, + 675590L, + 835854L, // level 20 + 1023775L, + 1242536L, + 1495531L, + 1786365L, + 2118860L, + 2497059L, + 2925229L, + 3407873L, + 3949727L, + 4555766L, // level 30 + 5231213L, + 5981539L, + 6812472L, + 7729999L, + 8740372L, + 9850111L, + 11066012L, + 12395149L, + 13844879L, + 15422851L, // level 40 + 17137002L, + 18995573L, + 21007103L, + 23180442L, + 25524751L, + 28049509L, + 30764519L, + 33679907L, + 36806133L, + 40153995L, // level 50 + 45524865L, + 51262204L, + 57383682L, + 63907585L, + 70852742L, + 80700339L, + 91162131L, + 102265326L, + 114038008L, + 126509030L, // level 60 + 146307211L, + 167243291L, + 189363788L, + 212716741L, + 237351413L, + 271973532L, + 308441375L, + 346825235L, + 387197529L, + 429632402L, // level 70 + 474205751L, + 532692055L, + 606319094L, + 696376867L, + 804219972L, + 931275828L, + 1151275834L, + 1511275834L, + 2099275834L, + 4200000000L, // level 80 + 6300000000L, // level 81 + 8820000000L, // level 82 + 11844000000L, // level 83 + 15472800000L, // level 84 + 19827360000L, // level 85 + 25314105600L, // level 86 + 32211728640L + // level 87 + }; + + /** + * This is the first UNREACHABLE level.
    + * ex: If you want a max at 85 & 100.00%, you have to put 86.
    + *
    + */ + public final static byte MAX_LEVEL = 86; + public final static byte PET_MAX_LEVEL = 87; + + public final static byte MIN_NEWBIE_LEVEL = 6; + public final static byte MAX_NEWBIE_LEVEL = 39; +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/base/PlayerClass.java b/L2J_Server/java/com/l2jserver/gameserver/model/base/PlayerClass.java new file mode 100644 index 00000000..8b3e6c93 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/base/PlayerClass.java @@ -0,0 +1,353 @@ +/* + * $Header: PlayerClass.java, 24/11/2005 12:56:01 luisantonioa Exp $ + * + * $Author: luisantonioa $ + * $Date: 24/11/2005 12:56:01 $ + * $Revision: 1 $ + * $Log: PlayerClass.java,v $ + * Revision 1 24/11/2005 12:56:01 luisantonioa + * Added copyright notice + * + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.base; + +import static com.l2jserver.gameserver.model.base.ClassLevel.First; +import static com.l2jserver.gameserver.model.base.ClassLevel.Fourth; +import static com.l2jserver.gameserver.model.base.ClassLevel.Second; +import static com.l2jserver.gameserver.model.base.ClassLevel.Third; +import static com.l2jserver.gameserver.model.base.ClassType.Fighter; +import static com.l2jserver.gameserver.model.base.ClassType.Mystic; +import static com.l2jserver.gameserver.model.base.ClassType.Priest; +import static com.l2jserver.gameserver.model.base.Race.DarkElf; +import static com.l2jserver.gameserver.model.base.Race.Dwarf; +import static com.l2jserver.gameserver.model.base.Race.Elf; +import static com.l2jserver.gameserver.model.base.Race.Human; +import static com.l2jserver.gameserver.model.base.Race.Kamael; +import static com.l2jserver.gameserver.model.base.Race.Orc; + +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.Set; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * This class ... + * @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $ + */ +public enum PlayerClass +{ + HumanFighter(Human, Fighter, First), + Warrior(Human, Fighter, Second), + Gladiator(Human, Fighter, Third), + Warlord(Human, Fighter, Third), + HumanKnight(Human, Fighter, Second), + Paladin(Human, Fighter, Third), + DarkAvenger(Human, Fighter, Third), + Rogue(Human, Fighter, Second), + TreasureHunter(Human, Fighter, Third), + Hawkeye(Human, Fighter, Third), + HumanMystic(Human, Mystic, First), + HumanWizard(Human, Mystic, Second), + Sorceror(Human, Mystic, Third), + Necromancer(Human, Mystic, Third), + Warlock(Human, Mystic, Third), + Cleric(Human, Priest, Second), + Bishop(Human, Priest, Third), + Prophet(Human, Priest, Third), + + ElvenFighter(Elf, Fighter, First), + ElvenKnight(Elf, Fighter, Second), + TempleKnight(Elf, Fighter, Third), + Swordsinger(Elf, Fighter, Third), + ElvenScout(Elf, Fighter, Second), + Plainswalker(Elf, Fighter, Third), + SilverRanger(Elf, Fighter, Third), + ElvenMystic(Elf, Mystic, First), + ElvenWizard(Elf, Mystic, Second), + Spellsinger(Elf, Mystic, Third), + ElementalSummoner(Elf, Mystic, Third), + ElvenOracle(Elf, Priest, Second), + ElvenElder(Elf, Priest, Third), + + DarkElvenFighter(DarkElf, Fighter, First), + PalusKnight(DarkElf, Fighter, Second), + ShillienKnight(DarkElf, Fighter, Third), + Bladedancer(DarkElf, Fighter, Third), + Assassin(DarkElf, Fighter, Second), + AbyssWalker(DarkElf, Fighter, Third), + PhantomRanger(DarkElf, Fighter, Third), + DarkElvenMystic(DarkElf, Mystic, First), + DarkElvenWizard(DarkElf, Mystic, Second), + Spellhowler(DarkElf, Mystic, Third), + PhantomSummoner(DarkElf, Mystic, Third), + ShillienOracle(DarkElf, Priest, Second), + ShillienElder(DarkElf, Priest, Third), + + OrcFighter(Orc, Fighter, First), + OrcRaider(Orc, Fighter, Second), + Destroyer(Orc, Fighter, Third), + OrcMonk(Orc, Fighter, Second), + Tyrant(Orc, Fighter, Third), + OrcMystic(Orc, Mystic, First), + OrcShaman(Orc, Mystic, Second), + Overlord(Orc, Mystic, Third), + Warcryer(Orc, Mystic, Third), + + DwarvenFighter(Dwarf, Fighter, First), + DwarvenScavenger(Dwarf, Fighter, Second), + BountyHunter(Dwarf, Fighter, Third), + DwarvenArtisan(Dwarf, Fighter, Second), + Warsmith(Dwarf, Fighter, Third), + + dummyEntry1(null, null, null), + dummyEntry2(null, null, null), + dummyEntry3(null, null, null), + dummyEntry4(null, null, null), + dummyEntry5(null, null, null), + dummyEntry6(null, null, null), + dummyEntry7(null, null, null), + dummyEntry8(null, null, null), + dummyEntry9(null, null, null), + dummyEntry10(null, null, null), + dummyEntry11(null, null, null), + dummyEntry12(null, null, null), + dummyEntry13(null, null, null), + dummyEntry14(null, null, null), + dummyEntry15(null, null, null), + dummyEntry16(null, null, null), + dummyEntry17(null, null, null), + dummyEntry18(null, null, null), + dummyEntry19(null, null, null), + dummyEntry20(null, null, null), + dummyEntry21(null, null, null), + dummyEntry22(null, null, null), + dummyEntry23(null, null, null), + dummyEntry24(null, null, null), + dummyEntry25(null, null, null), + dummyEntry26(null, null, null), + dummyEntry27(null, null, null), + dummyEntry28(null, null, null), + dummyEntry29(null, null, null), + dummyEntry30(null, null, null), + /* + * (3rd classes) + */ + duelist(Human, Fighter, Fourth), + dreadnought(Human, Fighter, Fourth), + phoenixKnight(Human, Fighter, Fourth), + hellKnight(Human, Fighter, Fourth), + sagittarius(Human, Fighter, Fourth), + adventurer(Human, Fighter, Fourth), + archmage(Human, Mystic, Fourth), + soultaker(Human, Mystic, Fourth), + arcanaLord(Human, Mystic, Fourth), + cardinal(Human, Priest, Fourth), + hierophant(Human, Priest, Fourth), + + evaTemplar(Elf, Fighter, Fourth), + swordMuse(Elf, Fighter, Fourth), + windRider(Elf, Fighter, Fourth), + moonlightSentinel(Elf, Fighter, Fourth), + mysticMuse(Elf, Mystic, Fourth), + elementalMaster(Elf, Mystic, Fourth), + evaSaint(Elf, Priest, Fourth), + + shillienTemplar(DarkElf, Fighter, Fourth), + spectralDancer(DarkElf, Fighter, Fourth), + ghostHunter(DarkElf, Fighter, Fourth), + ghostSentinel(DarkElf, Fighter, Fourth), + stormScreamer(DarkElf, Mystic, Fourth), + spectralMaster(DarkElf, Mystic, Fourth), + shillienSaint(DarkElf, Priest, Fourth), + + titan(Orc, Fighter, Fourth), + grandKhauatari(Orc, Fighter, Fourth), + dominator(Orc, Mystic, Fourth), + doomcryer(Orc, Mystic, Fourth), + + fortuneSeeker(Dwarf, Fighter, Fourth), + maestro(Dwarf, Fighter, Fourth), + + dummyEntry31(null, null, null), + dummyEntry32(null, null, null), + dummyEntry33(null, null, null), + dummyEntry34(null, null, null), + + maleSoldier(Kamael, Fighter, First), + femaleSoldier(Kamael, Fighter, First), + trooper(Kamael, Fighter, Second), + warder(Kamael, Fighter, Second), + berserker(Kamael, Fighter, Third), + maleSoulbreaker(Kamael, Fighter, Third), + femaleSoulbreaker(Kamael, Fighter, Third), + arbalester(Kamael, Fighter, Third), + doombringer(Kamael, Fighter, Fourth), + maleSoulhound(Kamael, Fighter, Fourth), + femaleSoulhound(Kamael, Fighter, Fourth), + trickster(Kamael, Fighter, Fourth), + inspector(Kamael, Fighter, Third), + judicator(Kamael, Fighter, Fourth); + + private Race _race; + private ClassLevel _level; + private ClassType _type; + + private static final Set mainSubclassSet; + private static final Set neverSubclassed = EnumSet.of(Overlord, Warsmith); + + private static final Set subclasseSet1 = EnumSet.of(DarkAvenger, Paladin, TempleKnight, ShillienKnight); + private static final Set subclasseSet2 = EnumSet.of(TreasureHunter, AbyssWalker, Plainswalker); + private static final Set subclasseSet3 = EnumSet.of(Hawkeye, SilverRanger, PhantomRanger); + private static final Set subclasseSet4 = EnumSet.of(Warlock, ElementalSummoner, PhantomSummoner); + private static final Set subclasseSet5 = EnumSet.of(Sorceror, Spellsinger, Spellhowler); + + private static final EnumMap> subclassSetMap = new EnumMap<>(PlayerClass.class); + + static + { + Set subclasses = getSet(null, Third); + subclasses.removeAll(neverSubclassed); + + mainSubclassSet = subclasses; + + subclassSetMap.put(DarkAvenger, subclasseSet1); + subclassSetMap.put(Paladin, subclasseSet1); + subclassSetMap.put(TempleKnight, subclasseSet1); + subclassSetMap.put(ShillienKnight, subclasseSet1); + + subclassSetMap.put(TreasureHunter, subclasseSet2); + subclassSetMap.put(AbyssWalker, subclasseSet2); + subclassSetMap.put(Plainswalker, subclasseSet2); + + subclassSetMap.put(Hawkeye, subclasseSet3); + subclassSetMap.put(SilverRanger, subclasseSet3); + subclassSetMap.put(PhantomRanger, subclasseSet3); + + subclassSetMap.put(Warlock, subclasseSet4); + subclassSetMap.put(ElementalSummoner, subclasseSet4); + subclassSetMap.put(PhantomSummoner, subclasseSet4); + + subclassSetMap.put(Sorceror, subclasseSet5); + subclassSetMap.put(Spellsinger, subclasseSet5); + subclassSetMap.put(Spellhowler, subclasseSet5); + } + + PlayerClass(Race pRace, ClassType pType, ClassLevel pLevel) + { + _race = pRace; + _level = pLevel; + _type = pType; + } + + public final Set getAvailableSubclasses(L2PcInstance player) + { + Set subclasses = null; + + if (_level == Third) + { + if (player.getRace() != Kamael) + { + subclasses = EnumSet.copyOf(mainSubclassSet); + + subclasses.remove(this); + + switch (player.getRace()) + { + case Elf: + subclasses.removeAll(getSet(DarkElf, Third)); + break; + case DarkElf: + subclasses.removeAll(getSet(Elf, Third)); + break; + } + + subclasses.removeAll(getSet(Kamael, Third)); + + Set unavailableClasses = subclassSetMap.get(this); + + if (unavailableClasses != null) + { + subclasses.removeAll(unavailableClasses); + } + + } + else + { + subclasses = getSet(Kamael, Third); + subclasses.remove(this); + // Check sex, male subclasses female and vice versa + // If server owner set MaxSubclass > 3 some kamael's cannot take 4 sub + // So, in that situation we must skip sex check + if (Config.MAX_SUBCLASS <= 3) + { + if (player.getAppearance().getSex()) + { + subclasses.removeAll(EnumSet.of(femaleSoulbreaker)); + } + else + { + subclasses.removeAll(EnumSet.of(maleSoulbreaker)); + } + } + if (!player.getSubClasses().containsKey(2) || (player.getSubClasses().get(2).getLevel() < 75)) + { + subclasses.removeAll(EnumSet.of(inspector)); + } + } + } + return subclasses; + } + + public static final EnumSet getSet(Race race, ClassLevel level) + { + EnumSet allOf = EnumSet.noneOf(PlayerClass.class); + + for (PlayerClass playerClass : EnumSet.allOf(PlayerClass.class)) + { + if ((race == null) || playerClass.isOfRace(race)) + { + if ((level == null) || playerClass.isOfLevel(level)) + { + allOf.add(playerClass); + } + } + } + + return allOf; + } + + public final boolean isOfRace(Race pRace) + { + return _race == pRace; + } + + public final boolean isOfType(ClassType pType) + { + return _type == pType; + } + + public final boolean isOfLevel(ClassLevel pLevel) + { + return _level == pLevel; + } + + public final ClassLevel getLevel() + { + return _level; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/base/PlayerState.java b/L2J_Server/java/com/l2jserver/gameserver/model/base/PlayerState.java new file mode 100644 index 00000000..f2eac5cd --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/base/PlayerState.java @@ -0,0 +1,28 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.base; + +public enum PlayerState +{ + RESTING, + MOVING, + RUNNING, + STANDING, + FLYING, + BEHIND, + FRONT, + CHAOTIC, + OLYMPIAD +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/base/Race.java b/L2J_Server/java/com/l2jserver/gameserver/model/base/Race.java new file mode 100644 index 00000000..4f1e4e69 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/base/Race.java @@ -0,0 +1,31 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.base; + +/** + * This class defines all races (human, elf, darkelf, orc, dwarf) that a player can chose.
    + *
    + * @version $Revision: 1.2.4.2 $ $Date: 2005/03/27 15:29:32 $ + */ + +public enum Race +{ + Human, + Elf, + DarkElf, + Orc, + Dwarf, + Kamael; +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/base/Sex.java b/L2J_Server/java/com/l2jserver/gameserver/model/base/Sex.java new file mode 100644 index 00000000..68978875 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/base/Sex.java @@ -0,0 +1,25 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.base; + +/** + * This class ... + * @version $Revision: 1.2.4.1 $ $Date: 2005/03/27 15:29:32 $ + */ +public class Sex +{ + public static final int MALE = 0; + public static final int FEMALE = 1; +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/base/SubClass.java b/L2J_Server/java/com/l2jserver/gameserver/model/base/SubClass.java new file mode 100644 index 00000000..f3f6345d --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/base/SubClass.java @@ -0,0 +1,150 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.base; + +import com.l2jserver.Config; + +/** + * Character Sub-Class Definition
    + * Used to store key information about a character's sub-class. + * @author Tempy + */ +public final class SubClass +{ + private static final byte _maxLevel = Config.MAX_SUBCLASS_LEVEL < Experience.MAX_LEVEL ? Config.MAX_SUBCLASS_LEVEL : Experience.MAX_LEVEL - 1; + + private PlayerClass _class; + private long _exp = Experience.LEVEL[40]; + private int _sp = 0; + private byte _level = 40; + private int _classIndex = 1; + + public SubClass(int classId, long exp, int sp, byte level, int classIndex) + { + _class = PlayerClass.values()[classId]; + _exp = exp; + _sp = sp; + _level = level; + _classIndex = classIndex; + } + + public SubClass(int classId, int classIndex) + { + // Used for defining a sub class using default values for XP, SP and player level. + _class = PlayerClass.values()[classId]; + _classIndex = classIndex; + } + + public SubClass() + { + // Used for specifying ALL attributes of a sub class directly, + // using the preset default values. + } + + public PlayerClass getClassDefinition() + { + return _class; + } + + public int getClassId() + { + return _class.ordinal(); + } + + public long getExp() + { + return _exp; + } + + public int getSp() + { + return _sp; + } + + public byte getLevel() + { + return _level; + } + + /** + * First Sub-Class is index 1. + * @return int _classIndex + */ + public int getClassIndex() + { + return _classIndex; + } + + public void setClassId(int classId) + { + _class = PlayerClass.values()[classId]; + } + + public void setExp(long expValue) + { + if (expValue > (Experience.LEVEL[_maxLevel + 1] - 1)) + { + expValue = (Experience.LEVEL[_maxLevel + 1] - 1); + } + + _exp = expValue; + } + + public void setSp(int spValue) + { + _sp = spValue; + } + + public void setClassIndex(int classIndex) + { + _classIndex = classIndex; + } + + public void setLevel(byte levelValue) + { + if (levelValue > _maxLevel) + { + levelValue = _maxLevel; + } + else if (levelValue < 40) + { + levelValue = 40; + } + + _level = levelValue; + } + + public void incLevel() + { + if (getLevel() == _maxLevel) + { + return; + } + + _level++; + setExp(Experience.LEVEL[getLevel()]); + } + + public void decLevel() + { + if (getLevel() == 40) + { + return; + } + + _level--; + setExp(Experience.LEVEL[getLevel()]); + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/ActionKey.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/ActionKey.java new file mode 100644 index 00000000..25509e1c --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/ActionKey.java @@ -0,0 +1,82 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.entity; + +/** + * @author mrTJO + */ +public class ActionKey +{ + int _cat; + int _cmd; + int _key; + int _tgKey1; + int _tgKey2; + int _show; + + /** + * L2ActionKey Initialization + * @param cat: Category ID + * @param cmd: Command ID + * @param key: User Defined Primary Key + * @param tgKey1: 1st Toogled Key (eg. Alt, Ctrl or Shift) + * @param tgKey2: 2nd Toogled Key (eg. Alt, Ctrl or Shift) + * @param show: Show Action in UI + */ + public ActionKey(int cat, int cmd, int key, int tgKey1, int tgKey2, int show) + { + _cat = cat; + _cmd = cmd; + _key = key; + _tgKey1 = tgKey1; + _tgKey2 = tgKey2; + _show = show; + } + + public int getCategory() + { + return _cat; + } + + public int getCommandId() + { + return _cmd; + } + + public int getKeyId() + { + return _key; + } + + public int getToogleKey1() + { + return _tgKey1; + } + + public int getToogleKey2() + { + return _tgKey2; + } + + public int getShowStatus() + { + return _show; + } + + public String getSqlSaveString(int playerId, int order) + { + return "(" + playerId + ", " + _cat + ", " + order + ", " + _cmd + "," + _key + ", " + _tgKey1 + ", " + _tgKey2 + ", " + _show + ")"; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/Auction.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Auction.java new file mode 100644 index 00000000..1163552c --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Auction.java @@ -0,0 +1,660 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.entity; + +import static com.l2jserver.gameserver.model.itemcontainer.PcInventory.ADENA_ID; +import static com.l2jserver.gameserver.model.itemcontainer.PcInventory.MAX_ADENA; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Calendar; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastMap; + +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.idfactory.IdFactory; +import com.l2jserver.gameserver.instancemanager.AuctionManager; +import com.l2jserver.gameserver.instancemanager.ClanHallManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; + +public class Auction +{ + protected static final Logger _log = Logger.getLogger(Auction.class.getName()); + private int _id = 0; + private long _endDate; + private int _highestBidderId = 0; + private String _highestBidderName = ""; + private long _highestBidderMaxBid = 0; + private int _itemId = 0; + private String _itemName = ""; + private int _itemObjectId = 0; + private final long _itemQuantity = 0; + private String _itemType = ""; + private int _sellerId = 0; + private String _sellerClanName = ""; + private String _sellerName = ""; + private long _currentBid = 0; + private long _startingBid = 0; + + private final Map _bidders = new FastMap<>(); + + private static final String[] ItemTypeName = + { + "ClanHall" + }; + + public static enum ItemTypeEnum + { + ClanHall + } + + public static class Bidder + { + private final String _name; // TODO replace with objid + private final String _clanName; + private long _bid; + private final Calendar _timeBid; + + public Bidder(String name, String clanName, long bid, long timeBid) + { + _name = name; + _clanName = clanName; + _bid = bid; + _timeBid = Calendar.getInstance(); + _timeBid.setTimeInMillis(timeBid); + } + + public String getName() + { + return _name; + } + + public String getClanName() + { + return _clanName; + } + + public long getBid() + { + return _bid; + } + + public Calendar getTimeBid() + { + return _timeBid; + } + + public void setTimeBid(long timeBid) + { + _timeBid.setTimeInMillis(timeBid); + } + + public void setBid(long bid) + { + _bid = bid; + } + } + + /** Task Sheduler for endAuction */ + public class AutoEndTask implements Runnable + { + public AutoEndTask() + { + } + + @Override + public void run() + { + try + { + endAuction(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } + + /** Constructor */ + public Auction(int auctionId) + { + _id = auctionId; + load(); + startAutoTask(); + } + + public Auction(int itemId, L2Clan Clan, long delay, long bid, String name) + { + _id = itemId; + _endDate = System.currentTimeMillis() + delay; + _itemId = itemId; + _itemName = name; + _itemType = "ClanHall"; + _sellerId = Clan.getLeaderId(); + _sellerName = Clan.getLeaderName(); + _sellerClanName = Clan.getName(); + _startingBid = bid; + } + + /** Load auctions */ + private void load() + { + Connection con = null; + try + { + PreparedStatement statement; + ResultSet rs; + + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement("Select * from auction where id = ?"); + statement.setInt(1, getId()); + rs = statement.executeQuery(); + + while (rs.next()) + { + _currentBid = rs.getLong("currentBid"); + _endDate = rs.getLong("endDate"); + _itemId = rs.getInt("itemId"); + _itemName = rs.getString("itemName"); + _itemObjectId = rs.getInt("itemObjectId"); + _itemType = rs.getString("itemType"); + _sellerId = rs.getInt("sellerId"); + _sellerClanName = rs.getString("sellerClanName"); + _sellerName = rs.getString("sellerName"); + _startingBid = rs.getLong("startingBid"); + } + statement.close(); + loadBid(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: Auction.load(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Load bidders **/ + private void loadBid() + { + _highestBidderId = 0; + _highestBidderName = ""; + _highestBidderMaxBid = 0; + + Connection con = null; + try + { + PreparedStatement statement; + ResultSet rs; + + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement("SELECT bidderId, bidderName, maxBid, clan_name, time_bid FROM auction_bid WHERE auctionId = ? ORDER BY maxBid DESC"); + statement.setInt(1, getId()); + rs = statement.executeQuery(); + + while (rs.next()) + { + if (rs.isFirst()) + { + _highestBidderId = rs.getInt("bidderId"); + _highestBidderName = rs.getString("bidderName"); + _highestBidderMaxBid = rs.getLong("maxBid"); + } + _bidders.put(rs.getInt("bidderId"), new Bidder(rs.getString("bidderName"), rs.getString("clan_name"), rs.getLong("maxBid"), rs.getLong("time_bid"))); + } + + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: Auction.loadBid(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Task Manage */ + private void startAutoTask() + { + long currentTime = System.currentTimeMillis(); + long taskDelay = 0; + if (_endDate <= currentTime) + { + _endDate = currentTime + (7 * 24 * 60 * 60 * 1000); + saveAuctionDate(); + } + else + { + taskDelay = _endDate - currentTime; + } + ThreadPoolManager.getInstance().scheduleGeneral(new AutoEndTask(), taskDelay); + } + + public static String getItemTypeName(ItemTypeEnum value) + { + return ItemTypeName[value.ordinal()]; + } + + /** Save Auction Data End */ + private void saveAuctionDate() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("Update auction set endDate = ? where id = ?"); + statement.setLong(1, _endDate); + statement.setInt(2, _id); + statement.execute(); + + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: saveAuctionDate(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Set a bid */ + public synchronized void setBid(L2PcInstance bidder, long bid) + { + long requiredAdena = bid; + if (getHighestBidderName().equals(bidder.getClan().getLeaderName())) + { + requiredAdena = bid - getHighestBidderMaxBid(); + } + if (((getHighestBidderId() > 0) && (bid > getHighestBidderMaxBid())) || ((getHighestBidderId() == 0) && (bid >= getStartingBid()))) + { + if (takeItem(bidder, requiredAdena)) + { + updateInDB(bidder, bid); + bidder.getClan().setAuctionBiddedAt(_id, true); + return; + } + } + if ((bid < getStartingBid()) || (bid <= getHighestBidderMaxBid())) + { + bidder.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.BID_PRICE_MUST_BE_HIGHER)); + } + } + + /** Return Item in WHC */ + private void returnItem(String Clan, long quantity, boolean penalty) + { + if (penalty) + { + quantity *= 0.9; // take 10% tax fee if needed + } + + // avoid overflow on return + final long limit = MAX_ADENA - ClanTable.getInstance().getClanByName(Clan).getWarehouse().getAdena(); + quantity = Math.min(quantity, limit); + + ClanTable.getInstance().getClanByName(Clan).getWarehouse().addItem("Outbidded", ADENA_ID, quantity, null, null); + } + + /** Take Item in WHC */ + private boolean takeItem(L2PcInstance bidder, long quantity) + { + if ((bidder.getClan() != null) && (bidder.getClan().getWarehouse().getAdena() >= quantity)) + { + bidder.getClan().getWarehouse().destroyItemByItemId("Buy", ADENA_ID, quantity, bidder, bidder); + return true; + } + bidder.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_ADENA_IN_CWH)); + return false; + } + + /** Update auction in DB */ + private void updateInDB(L2PcInstance bidder, long bid) + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + if (getBidders().get(bidder.getClanId()) != null) + { + statement = con.prepareStatement("UPDATE auction_bid SET bidderId=?, bidderName=?, maxBid=?, time_bid=? WHERE auctionId=? AND bidderId=?"); + statement.setInt(1, bidder.getClanId()); + statement.setString(2, bidder.getClan().getLeaderName()); + statement.setLong(3, bid); + statement.setLong(4, System.currentTimeMillis()); + statement.setInt(5, getId()); + statement.setInt(6, bidder.getClanId()); + statement.execute(); + statement.close(); + } + else + { + statement = con.prepareStatement("INSERT INTO auction_bid (id, auctionId, bidderId, bidderName, maxBid, clan_name, time_bid) VALUES (?, ?, ?, ?, ?, ?, ?)"); + statement.setInt(1, IdFactory.getInstance().getNextId()); + statement.setInt(2, getId()); + statement.setInt(3, bidder.getClanId()); + statement.setString(4, bidder.getName()); + statement.setLong(5, bid); + statement.setString(6, bidder.getClan().getName()); + statement.setLong(7, System.currentTimeMillis()); + statement.execute(); + statement.close(); + if (L2World.getInstance().getPlayer(_highestBidderName) != null) + { + L2World.getInstance().getPlayer(_highestBidderName).sendMessage("You have been out bidded"); + } + } + _highestBidderId = bidder.getClanId(); + _highestBidderMaxBid = bid; + _highestBidderName = bidder.getClan().getLeaderName(); + if (_bidders.get(_highestBidderId) == null) + { + _bidders.put(_highestBidderId, new Bidder(_highestBidderName, bidder.getClan().getName(), bid, Calendar.getInstance().getTimeInMillis())); + } + else + { + _bidders.get(_highestBidderId).setBid(bid); + _bidders.get(_highestBidderId).setTimeBid(Calendar.getInstance().getTimeInMillis()); + } + bidder.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.BID_IN_CLANHALL_AUCTION)); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Auction.updateInDB(L2PcInstance bidder, int bid): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Remove bids */ + private void removeBids() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + statement = con.prepareStatement("DELETE FROM auction_bid WHERE auctionId=?"); + statement.setInt(1, getId()); + statement.execute(); + + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Auction.deleteFromDB(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + for (Bidder b : _bidders.values()) + { + if (ClanTable.getInstance().getClanByName(b.getClanName()).getHasHideout() == 0) + { + returnItem(b.getClanName(), b.getBid(), true); // 10 % tax + } + else + { + if (L2World.getInstance().getPlayer(b.getName()) != null) + { + L2World.getInstance().getPlayer(b.getName()).sendMessage("Congratulation you have won ClanHall!"); + } + } + ClanTable.getInstance().getClanByName(b.getClanName()).setAuctionBiddedAt(0, true); + } + _bidders.clear(); + } + + /** Remove auctions */ + public void deleteAuctionFromDB() + { + AuctionManager.getInstance().getAuctions().remove(this); + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + statement = con.prepareStatement("DELETE FROM auction WHERE itemId=?"); + statement.setInt(1, _itemId); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Auction.deleteFromDB(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** End of auction */ + public void endAuction() + { + if (ClanHallManager.getInstance().loaded()) + { + if ((_highestBidderId == 0) && (_sellerId == 0)) + { + startAutoTask(); + return; + } + if ((_highestBidderId == 0) && (_sellerId > 0)) + { + /** + * If seller haven't sell ClanHall, auction removed, THIS MUST BE CONFIRMED + */ + int aucId = AuctionManager.getInstance().getAuctionIndex(_id); + AuctionManager.getInstance().getAuctions().remove(aucId); + return; + } + if (_sellerId > 0) + { + returnItem(_sellerClanName, _highestBidderMaxBid, true); + returnItem(_sellerClanName, ClanHallManager.getInstance().getClanHallById(_itemId).getLease(), false); + } + deleteAuctionFromDB(); + L2Clan Clan = ClanTable.getInstance().getClanByName(_bidders.get(_highestBidderId).getClanName()); + _bidders.remove(_highestBidderId); + Clan.setAuctionBiddedAt(0, true); + removeBids(); + ClanHallManager.getInstance().setOwner(_itemId, Clan); + } + else + { + /** Task waiting ClanHallManager is loaded every 3s */ + ThreadPoolManager.getInstance().scheduleGeneral(new AutoEndTask(), 3000); + } + } + + /** Cancel bid */ + public synchronized void cancelBid(int bidder) + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + statement = con.prepareStatement("DELETE FROM auction_bid WHERE auctionId=? AND bidderId=?"); + statement.setInt(1, getId()); + statement.setInt(2, bidder); + statement.execute(); + + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Auction.cancelBid(String bidder): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + returnItem(_bidders.get(bidder).getClanName(), _bidders.get(bidder).getBid(), true); + ClanTable.getInstance().getClanByName(_bidders.get(bidder).getClanName()).setAuctionBiddedAt(0, true); + _bidders.clear(); + loadBid(); + } + + /** Cancel auction */ + public void cancelAuction() + { + deleteAuctionFromDB(); + removeBids(); + } + + /** Confirm an auction */ + public void confirmAuction() + { + AuctionManager.getInstance().getAuctions().add(this); + Connection con = null; + try + { + PreparedStatement statement; + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement("INSERT INTO auction (id, sellerId, sellerName, sellerClanName, itemType, itemId, itemObjectId, itemName, itemQuantity, startingBid, currentBid, endDate) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"); + statement.setInt(1, getId()); + statement.setInt(2, _sellerId); + statement.setString(3, _sellerName); + statement.setString(4, _sellerClanName); + statement.setString(5, _itemType); + statement.setInt(6, _itemId); + statement.setInt(7, _itemObjectId); + statement.setString(8, _itemName); + statement.setLong(9, _itemQuantity); + statement.setLong(10, _startingBid); + statement.setLong(11, _currentBid); + statement.setLong(12, _endDate); + statement.execute(); + statement.close(); + loadBid(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Auction.load(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Get var auction */ + public final int getId() + { + return _id; + } + + public final long getCurrentBid() + { + return _currentBid; + } + + public final long getEndDate() + { + return _endDate; + } + + public final int getHighestBidderId() + { + return _highestBidderId; + } + + public final String getHighestBidderName() + { + return _highestBidderName; + } + + public final long getHighestBidderMaxBid() + { + return _highestBidderMaxBid; + } + + public final int getItemId() + { + return _itemId; + } + + public final String getItemName() + { + return _itemName; + } + + public final int getItemObjectId() + { + return _itemObjectId; + } + + public final long getItemQuantity() + { + return _itemQuantity; + } + + public final String getItemType() + { + return _itemType; + } + + public final int getSellerId() + { + return _sellerId; + } + + public final String getSellerName() + { + return _sellerName; + } + + public final String getSellerClanName() + { + return _sellerClanName; + } + + public final long getStartingBid() + { + return _startingBid; + } + + public final Map getBidders() + { + return _bidders; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/BlockCheckerEngine.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/BlockCheckerEngine.java new file mode 100644 index 00000000..e51ec33d --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/BlockCheckerEngine.java @@ -0,0 +1,785 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.entity; + +import java.util.Map.Entry; +import java.util.concurrent.ScheduledFuture; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SpawnTable; +import com.l2jserver.gameserver.instancemanager.HandysBlockCheckerManager; +import com.l2jserver.gameserver.instancemanager.HandysBlockCheckerManager.ArenaParticipantsHolder; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2Spawn; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2BlockInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.itemcontainer.PcInventory; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.ExBasicActionList; +import com.l2jserver.gameserver.network.serverpackets.ExCubeGameChangePoints; +import com.l2jserver.gameserver.network.serverpackets.ExCubeGameCloseUI; +import com.l2jserver.gameserver.network.serverpackets.ExCubeGameEnd; +import com.l2jserver.gameserver.network.serverpackets.ExCubeGameExtendedChangePoints; +import com.l2jserver.gameserver.network.serverpackets.RelationChanged; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.Rnd; + +/** + * @author BiggBoss + */ +public final class BlockCheckerEngine +{ + protected static final Logger _log = Logger.getLogger(BlockCheckerEngine.class.getName()); + // The object which holds all basic members info + protected HandysBlockCheckerManager.ArenaParticipantsHolder _holder; + // Maps to hold player of each team and his points + protected final FastMap _redTeamPoints = new FastMap<>(); + protected final FastMap _blueTeamPoints = new FastMap<>(); + // The initial points of the event + protected int _redPoints = 15; + protected int _bluePoints = 15; + // Current used arena + protected int _arena = -1; + // All blocks + protected final FastList _spawns = new FastList<>(); + // Sets if the red team won the event at the end of this (used for packets) + protected boolean _isRedWinner; + // Time when the event starts. Used on packet sending + protected long _startedTime; + // The needed arena coordinates + // Arena X: team1X, team1Y, team2X, team2Y, ArenaCenterX, ArenaCenterY + protected static final int[][] _arenaCoordinates = + { + // Arena 0 - Team 1 XY, Team 2 XY - CENTER XY + { + -58368, + -62745, + -57751, + -62131, + -58053, + -62417 + }, + // Arena 1 - Team 1 XY, Team 2 XY - CENTER XY + { + -58350, + -63853, + -57756, + -63266, + -58053, + -63551 + }, + // Arena 2 - Team 1 XY, Team 2 XY - CENTER XY + { + -57194, + -63861, + -56580, + -63249, + -56886, + -63551 + }, + // Arena 3 - Team 1 XY, Team 2 XY - CENTER XY + { + -57200, + -62727, + -56584, + -62115, + -56850, + -62391 + } + }; + // Common z coordinate + private static final int _zCoord = -2405; + // List of dropped items in event (for later deletion) + protected final FastList _drops = new FastList<>(); + // Default arena + private static final byte DEFAULT_ARENA = -1; + // Event is started + protected boolean _isStarted = false; + // Event end + protected ScheduledFuture _task; + // Preserve from exploit reward by logging out + protected boolean _abnormalEnd = false; + + public BlockCheckerEngine(HandysBlockCheckerManager.ArenaParticipantsHolder holder, int arena) + { + _holder = holder; + if ((arena > -1) && (arena < 4)) + { + _arena = arena; + } + + for (L2PcInstance player : holder.getRedPlayers()) + { + _redTeamPoints.put(player, 0); + } + for (L2PcInstance player : holder.getBluePlayers()) + { + _blueTeamPoints.put(player, 0); + } + } + + /** + * Updates the player holder before the event starts to synchronize all info + * @param holder + */ + public void updatePlayersOnStart(ArenaParticipantsHolder holder) + { + _holder = holder; + } + + /** + * Returns the current holder object of this object engine + * @return HandysBlockCheckerManager.ArenaParticipantsHolder + */ + public ArenaParticipantsHolder getHolder() + { + return _holder; + } + + /** + * Will return the id of the arena used by this event + * @return false; + */ + public int getArena() + { + return _arena; + } + + /** + * Returns the time when the event started + * @return long + */ + public long getStarterTime() + { + return _startedTime; + } + + /** + * Returns the current red team points + * @return int + */ + public int getRedPoints() + { + synchronized (this) + { + return _redPoints; + } + } + + /** + * Returns the current blue team points + * @return int + */ + public int getBluePoints() + { + synchronized (this) + { + return _bluePoints; + } + } + + /** + * Returns the player points + * @param player + * @param isRed + * @return int + */ + public int getPlayerPoints(L2PcInstance player, boolean isRed) + { + if (!_redTeamPoints.containsKey(player) && !_blueTeamPoints.containsKey(player)) + { + return 0; + } + + if (isRed) + { + return _redTeamPoints.get(player); + } + return _blueTeamPoints.get(player); + } + + /** + * Increases player points for his teams + * @param player + * @param team + */ + public synchronized void increasePlayerPoints(L2PcInstance player, int team) + { + if (player == null) + { + return; + } + + if (team == 0) + { + int points = _redTeamPoints.get(player) + 1; + _redTeamPoints.put(player, points); + _redPoints++; + _bluePoints--; + } + else + { + int points = _blueTeamPoints.get(player) + 1; + _blueTeamPoints.put(player, points); + _bluePoints++; + _redPoints--; + } + } + + /** + * Will add a new drop into the list of dropped items + * @param item + */ + public void addNewDrop(L2ItemInstance item) + { + if (item != null) + { + _drops.add(item); + } + } + + /** + * Will return true if the event is alredy started + * @return boolean + */ + public boolean isStarted() + { + return _isStarted; + } + + /** + * Will send all packets for the event members with the relation info + */ + protected void broadcastRelationChanged(L2PcInstance plr) + { + for (L2PcInstance p : _holder.getAllPlayers()) + { + p.sendPacket(new RelationChanged(plr, plr.getRelation(p), plr.isAutoAttackable(p))); + } + } + + /** + * Called when a there is an empty team. The event will end. + */ + public void endEventAbnormally() + { + try + { + synchronized (this) + { + _isStarted = false; + + if (_task != null) + { + _task.cancel(true); + } + + _abnormalEnd = true; + + ThreadPoolManager.getInstance().executeTask(new EndEvent()); + + if (Config.DEBUG) + { + _log.config("Handys Block Checker Event at arena " + _arena + " ended due lack of players!"); + } + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Couldnt end Block Checker event at " + _arena, e); + } + } + + /** + * This inner class set ups all player and arena parameters to start the event + */ + public class StartEvent implements Runnable + { + // In event used skills + private final L2Skill _freeze, _transformationRed, _transformationBlue; + // Common and unparametizer packet + private final ExCubeGameCloseUI _closeUserInterface = new ExCubeGameCloseUI(); + + public StartEvent() + { + // Initialize all used skills + _freeze = SkillTable.getInstance().getInfo(6034, 1); + _transformationRed = SkillTable.getInstance().getInfo(6035, 1); + _transformationBlue = SkillTable.getInstance().getInfo(6036, 1); + } + + /** + * Will set up all player parameters and port them to their respective location based on their teams + */ + private void setUpPlayers() + { + // Set current arena as being used + HandysBlockCheckerManager.getInstance().setArenaBeingUsed(_arena); + + // Initialize packets avoiding create a new one per player + _redPoints = _spawns.size() / 2; + _bluePoints = _spawns.size() / 2; + final ExCubeGameChangePoints initialPoints = new ExCubeGameChangePoints(300, _bluePoints, _redPoints); + ExCubeGameExtendedChangePoints clientSetUp; + + for (L2PcInstance player : _holder.getAllPlayers()) + { + if (player == null) + { + continue; + } + + // Send the secret client packet set up + boolean isRed = _holder.getRedPlayers().contains(player); + + clientSetUp = new ExCubeGameExtendedChangePoints(300, _bluePoints, _redPoints, isRed, player, 0); + player.sendPacket(clientSetUp); + + player.sendPacket(ActionFailed.STATIC_PACKET); + + // Teleport Player - Array access + // Team 0 * 2 = 0; 0 = 0, 0 + 1 = 1. + // Team 1 * 2 = 2; 2 = 2, 2 + 1 = 3 + int tc = _holder.getPlayerTeam(player) * 2; + // Get x and y coordinates + int x = _arenaCoordinates[_arena][tc]; + int y = _arenaCoordinates[_arena][tc + 1]; + player.teleToLocation(x, y, _zCoord); + // Set the player team + if (isRed) + { + _redTeamPoints.put(player, 0); + player.setTeam(2); + } + else + { + _blueTeamPoints.put(player, 0); + player.setTeam(1); + } + player.stopAllEffects(); + + if (player.getPet() != null) + { + player.getPet().unSummon(player); + } + + // Give the player start up effects + // Freeze + _freeze.getEffects(player, player); + // Tranformation + if (_holder.getPlayerTeam(player) == 0) + { + _transformationRed.getEffects(player, player); + } + else + { + _transformationBlue.getEffects(player, player); + } + // Set the current player arena + player.setBlockCheckerArena((byte) _arena); + player.setInsideZone(L2Character.ZONE_PVP, true); + // Send needed packets + player.sendPacket(initialPoints); + player.sendPacket(_closeUserInterface); + // ExBasicActionList + final ExBasicActionList actionList = ExBasicActionList.getStaticPacket(player); + player.sendPacket(actionList); + broadcastRelationChanged(player); + } + } + + @Override + public void run() + { + // Wrong arena passed, stop event + if (_arena == -1) + { + _log.severe("Couldnt set up the arena Id for the Block Checker event, cancelling event..."); + return; + } + _isStarted = true; + // Spawn the blocks + ThreadPoolManager.getInstance().executeTask(new SpawnRound(16, 1)); + // Start up player parameters + setUpPlayers(); + // Set the started time + _startedTime = System.currentTimeMillis() + 300000; + } + } + + /** + * This class spawns the second round of boxes and schedules the event end + */ + private class SpawnRound implements Runnable + { + int _numOfBoxes; + int _round; + + SpawnRound(int numberOfBoxes, int round) + { + _numOfBoxes = numberOfBoxes; + _round = round; + } + + @Override + public void run() + { + if (!_isStarted) + { + return; + } + + switch (_round) + { + case 1: + // Schedule second spawn round + _task = ThreadPoolManager.getInstance().scheduleGeneral(new SpawnRound(20, 2), 60000); + break; + case 2: + // Schedule third spawn round + _task = ThreadPoolManager.getInstance().scheduleGeneral(new SpawnRound(14, 3), 60000); + break; + case 3: + // Schedule Event End Count Down + _task = ThreadPoolManager.getInstance().scheduleGeneral(new EndEvent(), 180000); + break; + } + // random % 2, if == 0 will spawn a red block + // if != 0, will spawn a blue block + byte random = 2; + // common template + final L2NpcTemplate template = NpcTable.getInstance().getTemplate(18672); + // Spawn blocks + try + { + // Creates 50 new blocks + for (int i = 0; i < _numOfBoxes; i++) + { + L2Spawn spawn = new L2Spawn(template); + spawn.setLocx(_arenaCoordinates[_arena][4] + Rnd.get(-400, 400)); + spawn.setLocy(_arenaCoordinates[_arena][5] + Rnd.get(-400, 400)); + spawn.setLocz(_zCoord); + spawn.setAmount(1); + spawn.setHeading(1); + spawn.setRespawnDelay(1); + SpawnTable.getInstance().addNewSpawn(spawn, false); + spawn.init(); + L2BlockInstance block = (L2BlockInstance) spawn.getLastSpawn(); + // switch color + if ((random % 2) == 0) + { + block.setRed(true); + } + else + { + block.setRed(false); + } + + block.disableCoreAI(true); + _spawns.add(spawn); + random++; + } + } + catch (Exception e) + { + e.printStackTrace(); + } + + // Spawn the block carrying girl + if ((_round == 1) || (_round == 2)) + { + L2NpcTemplate girl = NpcTable.getInstance().getTemplate(18676); + try + { + final L2Spawn girlSpawn = new L2Spawn(girl); + girlSpawn.setLocx(_arenaCoordinates[_arena][4] + Rnd.get(-400, 400)); + girlSpawn.setLocy(_arenaCoordinates[_arena][5] + Rnd.get(-400, 400)); + girlSpawn.setLocz(_zCoord); + girlSpawn.setAmount(1); + girlSpawn.setHeading(1); + girlSpawn.setRespawnDelay(1); + SpawnTable.getInstance().addNewSpawn(girlSpawn, false); + girlSpawn.init(); + // Schedule his deletion after 9 secs of spawn + ThreadPoolManager.getInstance().scheduleGeneral(new CarryingGirlUnspawn(girlSpawn), 9000); + } + catch (Exception e) + { + _log.warning("Couldnt Spawn Block Checker NPCs! Wrong instance type at npc table?"); + if (Config.DEBUG) + { + e.printStackTrace(); + } + } + } + + _redPoints += _numOfBoxes / 2; + _bluePoints += _numOfBoxes / 2; + + int timeLeft = (int) ((getStarterTime() - System.currentTimeMillis()) / 1000); + ExCubeGameChangePoints changePoints = new ExCubeGameChangePoints(timeLeft, getBluePoints(), getRedPoints()); + getHolder().broadCastPacketToTeam(changePoints); + } + } + + private class CarryingGirlUnspawn implements Runnable + { + private final L2Spawn _spawn; + + protected CarryingGirlUnspawn(L2Spawn spawn) + { + _spawn = spawn; + } + + @Override + public void run() + { + if (_spawn == null) + { + _log.warning("HBCE: Block Carrying Girl is null"); + return; + } + SpawnTable.getInstance().deleteSpawn(_spawn, false); + _spawn.stopRespawn(); + _spawn.getLastSpawn().deleteMe(); + } + } + + /* + * private class CountDown implements Runnable { + * @Override public void run() { _holder.broadCastPacketToTeam(SystemMessage.getSystemMessage(SystemMessageId.BLOCK_CHECKER_ENDS_5)); ThreadPoolManager.getInstance().scheduleGeneral(new EndEvent(), 5000); } } + */ + + /** + * This class erase all event parameters on player and port them back near Handy. Also, unspawn blocks, runs a garbage collector and set as free the used arena + */ + protected class EndEvent implements Runnable + { + // Garbage collector and arena free setter + private void clearMe() + { + HandysBlockCheckerManager.getInstance().clearPaticipantQueueByArenaId(_arena); + _holder.clearPlayers(); + _blueTeamPoints.clear(); + _redTeamPoints.clear(); + HandysBlockCheckerManager.getInstance().setArenaFree(_arena); + + for (L2Spawn spawn : _spawns) + { + spawn.stopRespawn(); + spawn.getLastSpawn().deleteMe(); + SpawnTable.getInstance().deleteSpawn(spawn, false); + spawn = null; + } + _spawns.clear(); + + for (L2ItemInstance item : _drops) + { + // npe + if (item == null) + { + continue; + } + + // a player has it, it will be deleted later + if (!item.isVisible() || (item.getOwnerId() != 0)) + { + continue; + } + + item.decayMe(); + L2World.getInstance().removeObject(item); + } + _drops.clear(); + } + + /** + * Reward players after event. Tie - No Reward + */ + private void rewardPlayers() + { + if (_redPoints == _bluePoints) + { + return; + } + + _isRedWinner = _redPoints > _bluePoints ? true : false; + + if (_isRedWinner) + { + rewardAsWinner(true); + rewardAsLooser(false); + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.TEAM_C1_WON); + msg.addString("Red Team"); + _holder.broadCastPacketToTeam(msg); + } + else if (_bluePoints > _redPoints) + { + rewardAsWinner(false); + rewardAsLooser(true); + SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.TEAM_C1_WON); + msg.addString("Blue Team"); + _holder.broadCastPacketToTeam(msg); + } + else + { + rewardAsLooser(true); + rewardAsLooser(false); + } + } + + /** + * Reward the speicifed team as a winner team 1) Higher score - 8 extra 2) Higher score - 5 extra + * @param isRed + */ + private void rewardAsWinner(boolean isRed) + { + FastMap tempPoints = isRed ? _redTeamPoints : _blueTeamPoints; + + // Main give + for (L2PcInstance pc : tempPoints.keySet()) + { + if (pc == null) + { + continue; + } + + if (tempPoints.get(pc) >= 10) + { + pc.addItem("Block Checker", 13067, 2, pc, true); + } + else + { + tempPoints.remove(pc); + } + } + + int first = 0, second = 0; + L2PcInstance winner1 = null, winner2 = null; + for (Entry entry : tempPoints.entrySet()) + { + L2PcInstance pc = entry.getKey(); + int pcPoints = entry.getValue(); + if (pcPoints > first) + { + // Move old data + second = first; + winner2 = winner1; + // Set new data + first = pcPoints; + winner1 = pc; + } + else if (pcPoints > second) + { + second = pcPoints; + winner2 = pc; + } + } + if (winner1 != null) + { + winner1.addItem("Block Checker", 13067, 8, winner1, true); + } + if (winner2 != null) + { + winner2.addItem("Block Checker", 13067, 5, winner2, true); + } + } + + /** + * Will reward the looser team with the predefined rewards Player got >= 10 points: 2 coins Player got < 10 points: 0 coins + * @param isRed + */ + private void rewardAsLooser(boolean isRed) + { + FastMap tempPoints = isRed ? _redTeamPoints : _blueTeamPoints; + + for (Entry entry : tempPoints.entrySet()) + { + L2PcInstance player = entry.getKey(); + if ((player != null) && (entry.getValue() >= 10)) + { + player.addItem("Block Checker", 13067, 2, player, true); + } + } + } + + /** + * Telport players back, give status back and send final packet + */ + private void setPlayersBack() + { + final ExCubeGameEnd end = new ExCubeGameEnd(_isRedWinner); + + for (L2PcInstance player : _holder.getAllPlayers()) + { + if (player == null) + { + continue; + } + + player.stopAllEffects(); + // Remove team aura + player.setTeam(0); + // Set default arena + player.setBlockCheckerArena(DEFAULT_ARENA); + // Remove the event items + PcInventory inv = player.getInventory(); + if (inv.getItemByItemId(13787) != null) + { + long count = inv.getInventoryItemCount(13787, 0); + inv.destroyItemByItemId("Handys Block Checker", 13787, count, player, player); + } + if (inv.getItemByItemId(13788) != null) + { + long count = inv.getInventoryItemCount(13788, 0); + inv.destroyItemByItemId("Handys Block Checker", 13788, count, player, player); + } + broadcastRelationChanged(player); + // Teleport Back + player.teleToLocation(-57478, -60367, -2370); + player.setInsideZone(L2Character.ZONE_PVP, false); + // Send end packet + player.sendPacket(end); + player.broadcastUserInfo(); + } + } + + @Override + public void run() + { + if (!_abnormalEnd) + { + rewardPlayers(); + } + setPlayersBack(); + clearMe(); + _isStarted = false; + _abnormalEnd = false; + } + } +} \ No newline at end of file diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/Castle.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Castle.java new file mode 100644 index 00000000..cea7b2a7 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Castle.java @@ -0,0 +1,1722 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.entity; + +import gnu.trove.TIntIntHashMap; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.CastleUpdater; +import com.l2jserver.gameserver.SevenSigns; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.datatables.DoorTable; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.instancemanager.CastleManorManager; +import com.l2jserver.gameserver.instancemanager.CastleManorManager.CropProcure; +import com.l2jserver.gameserver.instancemanager.CastleManorManager.SeedProduction; +import com.l2jserver.gameserver.instancemanager.FortManager; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; +import com.l2jserver.gameserver.instancemanager.TerritoryWarManager.Territory; +import com.l2jserver.gameserver.instancemanager.ZoneManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2Manor; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2SkillLearn; +import com.l2jserver.gameserver.model.actor.instance.L2ArtefactInstance; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.itemcontainer.PcInventory; +import com.l2jserver.gameserver.model.zone.type.L2CastleTeleportZone; +import com.l2jserver.gameserver.model.zone.type.L2CastleZone; +import com.l2jserver.gameserver.model.zone.type.L2SiegeZone; +import com.l2jserver.gameserver.network.serverpackets.PlaySound; +import com.l2jserver.gameserver.network.serverpackets.PledgeShowInfoUpdate; + +public class Castle +{ + protected static final Logger _log = Logger.getLogger(Castle.class.getName()); + + // ========================================================= + // Data Field + private List _procure = new ArrayList<>(); + private List _production = new ArrayList<>(); + private List _procureNext = new ArrayList<>(); + private List _productionNext = new ArrayList<>(); + private boolean _isNextPeriodApproved = false; + + private static final String CASTLE_MANOR_DELETE_PRODUCTION = "DELETE FROM castle_manor_production WHERE castle_id=?;"; + private static final String CASTLE_MANOR_DELETE_PRODUCTION_PERIOD = "DELETE FROM castle_manor_production WHERE castle_id=? AND period=?;"; + private static final String CASTLE_MANOR_DELETE_PROCURE = "DELETE FROM castle_manor_procure WHERE castle_id=?;"; + private static final String CASTLE_MANOR_DELETE_PROCURE_PERIOD = "DELETE FROM castle_manor_procure WHERE castle_id=? AND period=?;"; + private static final String CASTLE_UPDATE_CROP = "UPDATE castle_manor_procure SET can_buy=? WHERE crop_id=? AND castle_id=? AND period=?"; + private static final String CASTLE_UPDATE_SEED = "UPDATE castle_manor_production SET can_produce=? WHERE seed_id=? AND castle_id=? AND period=?"; + // ========================================================= + // Data Field + private int _castleId = 0; + private final List _doors = new FastList<>(); + private final List _doorDefault = new FastList<>(); + private String _name = ""; + private int _ownerId = 0; + private Siege _siege = null; + private Calendar _siegeDate; + private boolean _isTimeRegistrationOver = true; // true if Castle Lords set the time, or 24h is elapsed after the siege + private Calendar _siegeTimeRegistrationEndDate; // last siege end date + 1 day + private int _taxPercent = 0; + private double _taxRate = 0; + private long _treasury = 0; + private boolean _showNpcCrest = false; + private L2SiegeZone _zone = null; + private L2CastleZone _castleZone = null; + private L2CastleTeleportZone _teleZone; + private L2Clan _formerOwner = null; + private final List _artefacts = new ArrayList<>(1); + private final TIntIntHashMap _engrave = new TIntIntHashMap(1); + private final Map _function; + private final FastList _residentialSkills = new FastList<>(); + + /** Castle Functions */ + public static final int FUNC_TELEPORT = 1; + public static final int FUNC_RESTORE_HP = 2; + public static final int FUNC_RESTORE_MP = 3; + public static final int FUNC_RESTORE_EXP = 4; + public static final int FUNC_SUPPORT = 5; + + public class CastleFunction + { + private final int _type; + private int _lvl; + protected int _fee; + protected int _tempFee; + private final long _rate; + private long _endDate; + protected boolean _inDebt; + public boolean _cwh; + + public CastleFunction(int type, int lvl, int lease, int tempLease, long rate, long time, boolean cwh) + { + _type = type; + _lvl = lvl; + _fee = lease; + _tempFee = tempLease; + _rate = rate; + _endDate = time; + initializeTask(cwh); + } + + public int getType() + { + return _type; + } + + public int getLvl() + { + return _lvl; + } + + public int getLease() + { + return _fee; + } + + public long getRate() + { + return _rate; + } + + public long getEndTime() + { + return _endDate; + } + + public void setLvl(int lvl) + { + _lvl = lvl; + } + + public void setLease(int lease) + { + _fee = lease; + } + + public void setEndTime(long time) + { + _endDate = time; + } + + private void initializeTask(boolean cwh) + { + if (getOwnerId() <= 0) + { + return; + } + long currentTime = System.currentTimeMillis(); + if (_endDate > currentTime) + { + ThreadPoolManager.getInstance().scheduleGeneral(new FunctionTask(cwh), _endDate - currentTime); + } + else + { + ThreadPoolManager.getInstance().scheduleGeneral(new FunctionTask(cwh), 0); + } + } + + private class FunctionTask implements Runnable + { + public FunctionTask(boolean cwh) + { + _cwh = cwh; + } + + @Override + public void run() + { + try + { + if (getOwnerId() <= 0) + { + return; + } + if ((ClanTable.getInstance().getClan(getOwnerId()).getWarehouse().getAdena() >= _fee) || !_cwh) + { + int fee = _fee; + if (getEndTime() == -1) + { + fee = _tempFee; + } + + setEndTime(System.currentTimeMillis() + getRate()); + dbSave(); + if (_cwh) + { + ClanTable.getInstance().getClan(getOwnerId()).getWarehouse().destroyItemByItemId("CS_function_fee", 57, fee, null, null); + if (Config.DEBUG) + { + _log.warning("deducted " + fee + " adena from " + getName() + " owner's cwh for function id : " + getType()); + } + } + ThreadPoolManager.getInstance().scheduleGeneral(new FunctionTask(true), getRate()); + } + else + { + removeFunction(getType()); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } + + public void dbSave() + { + Connection con = null; + try + { + PreparedStatement statement; + + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("REPLACE INTO castle_functions (castle_id, type, lvl, lease, rate, endTime) VALUES (?,?,?,?,?,?)"); + statement.setInt(1, getCastleId()); + statement.setInt(2, getType()); + statement.setInt(3, getLvl()); + statement.setInt(4, getLease()); + statement.setLong(5, getRate()); + statement.setLong(6, getEndTime()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Castle.updateFunctions(int type, int lvl, int lease, long rate, long time, boolean addNew): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + } + + // ========================================================= + // Constructor + public Castle(int castleId) + { + _castleId = castleId; + /* + * if (_castleId == 7 || castleId == 9) // Goddard and Schuttgart _nbArtifact = 2; + */ + load(); + loadDoor(); + _function = new FastMap<>(); + final FastList residentialSkills = SkillTreesData.getInstance().getAvailableResidentialSkills(castleId); + for (L2SkillLearn s : residentialSkills) + { + final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()); + if (sk != null) + { + _residentialSkills.add(sk); + } + else + { + _log.warning("Castle Id: " + castleId + " has a null residential skill Id: " + s.getSkillId() + " level: " + s.getSkillLevel() + "!"); + } + } + if (getOwnerId() != 0) + { + loadFunctions(); + } + } + + // ========================================================= + // Method - Public + + /** Return function with id */ + public CastleFunction getFunction(int type) + { + if (_function.get(type) != null) + { + return _function.get(type); + } + return null; + } + + public synchronized void engrave(L2Clan clan, L2Object target) + { + if (!_artefacts.contains(target)) + { + return; + } + _engrave.put(target.getObjectId(), clan.getClanId()); + if (_engrave.size() == _artefacts.size()) + { + for (L2ArtefactInstance art : _artefacts) + { + if (_engrave.get(art.getObjectId()) != clan.getClanId()) + { + return; + } + } + _engrave.clear(); + setOwner(clan); + } + } + + // This method add to the treasury + /** Add amount to castle instance's treasury (warehouse). */ + public void addToTreasury(long amount) + { + // check if owned + if (getOwnerId() <= 0) + { + return; + } + + if (_name.equalsIgnoreCase("Schuttgart") || _name.equalsIgnoreCase("Goddard")) + { + Castle rune = CastleManager.getInstance().getCastle("rune"); + if (rune != null) + { + long runeTax = (long) (amount * rune.getTaxRate()); + if (rune.getOwnerId() > 0) + { + rune.addToTreasury(runeTax); + } + amount -= runeTax; + } + } + if (!_name.equalsIgnoreCase("aden") && !_name.equalsIgnoreCase("Rune") && !_name.equalsIgnoreCase("Schuttgart") && !_name.equalsIgnoreCase("Goddard")) // If current castle instance is not Aden, Rune, Goddard or Schuttgart. + { + Castle aden = CastleManager.getInstance().getCastle("aden"); + if (aden != null) + { + long adenTax = (long) (amount * aden.getTaxRate()); // Find out what Aden gets from the current castle instance's income + if (aden.getOwnerId() > 0) + { + aden.addToTreasury(adenTax); // Only bother to really add the tax to the treasury if not npc owned + } + + amount -= adenTax; // Subtract Aden's income from current castle instance's income + } + } + + addToTreasuryNoTax(amount); + } + + /** Add amount to castle instance's treasury (warehouse), no tax paying. */ + public boolean addToTreasuryNoTax(long amount) + { + if (getOwnerId() <= 0) + { + return false; + } + + if (amount < 0) + { + amount *= -1; + if (_treasury < amount) + { + return false; + } + _treasury -= amount; + } + else + { + if ((_treasury + amount) > PcInventory.MAX_ADENA) + { + _treasury = PcInventory.MAX_ADENA; + } + else + { + _treasury += amount; + } + } + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("Update castle set treasury = ? where id = ?"); + statement.setLong(1, getTreasury()); + statement.setInt(2, getCastleId()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + } + finally + { + L2DatabaseFactory.close(con); + } + return true; + } + + /** + * Move non clan members off castle area and to nearest town.
    + *
    + */ + public void banishForeigners() + { + getCastleZone().banishForeigners(getOwnerId()); + } + + /** + * Return true if object is inside the zone + */ + public boolean checkIfInZone(int x, int y, int z) + { + return getZone().isInsideZone(x, y, z); + } + + public L2SiegeZone getZone() + { + if (_zone == null) + { + for (L2SiegeZone zone : ZoneManager.getInstance().getAllZones(L2SiegeZone.class)) + { + if (zone.getSiegeObjectId() == getCastleId()) + { + _zone = zone; + break; + } + } + } + return _zone; + } + + public L2CastleZone getCastleZone() + { + if (_castleZone == null) + { + for (L2CastleZone zone : ZoneManager.getInstance().getAllZones(L2CastleZone.class)) + { + if (zone.getCastleId() == getCastleId()) + { + _castleZone = zone; + break; + } + } + } + return _castleZone; + } + + public L2CastleTeleportZone getTeleZone() + { + if (_teleZone == null) + { + for (L2CastleTeleportZone zone : ZoneManager.getInstance().getAllZones(L2CastleTeleportZone.class)) + { + if (zone.getCastleId() == getCastleId()) + { + _teleZone = zone; + break; + } + } + } + return _teleZone; + } + + public void oustAllPlayers() + { + getTeleZone().oustAllPlayers(); + } + + /** + * Get the objects distance to this castle + * @param obj + * @return + */ + public double getDistance(L2Object obj) + { + return getZone().getDistanceToZone(obj); + } + + public void closeDoor(L2PcInstance activeChar, int doorId) + { + openCloseDoor(activeChar, doorId, false); + } + + public void openDoor(L2PcInstance activeChar, int doorId) + { + openCloseDoor(activeChar, doorId, true); + } + + public void openCloseDoor(L2PcInstance activeChar, int doorId, boolean open) + { + if (activeChar.getClanId() != getOwnerId()) + { + return; + } + + L2DoorInstance door = getDoor(doorId); + if (door != null) + { + if (open) + { + door.openMe(); + } + else + { + door.closeMe(); + } + } + } + + // This method is used to begin removing all castle upgrades + public void removeUpgrade() + { + removeDoorUpgrade(); + for (Map.Entry fc : _function.entrySet()) + { + removeFunction(fc.getKey()); + } + _function.clear(); + } + + // This method updates the castle tax rate + public void setOwner(L2Clan clan) + { + // Remove old owner + if ((getOwnerId() > 0) && ((clan == null) || (clan.getClanId() != getOwnerId()))) + { + L2Clan oldOwner = ClanTable.getInstance().getClan(getOwnerId()); // Try to find clan instance + if (oldOwner != null) + { + if (_formerOwner == null) + { + _formerOwner = oldOwner; + if (Config.REMOVE_CASTLE_CIRCLETS) + { + CastleManager.getInstance().removeCirclet(_formerOwner, getCastleId()); + } + } + try + { + L2PcInstance oldleader = oldOwner.getLeader().getPlayerInstance(); + if (oldleader != null) + { + if (oldleader.getMountType() == 2) + { + oldleader.dismount(); + } + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception in setOwner: " + e.getMessage(), e); + } + oldOwner.setHasCastle(0); // Unset has castle flag for old owner + for (L2PcInstance member : oldOwner.getOnlineMembers(0)) + { + removeResidentialSkills(member); + member.sendSkillList(); + } + } + } + + updateOwnerInDB(clan); // Update in database + setShowNpcCrest(false); + + // if clan have fortress, remove it + if (clan.getHasFort() > 0) + { + FortManager.getInstance().getFortByOwner(clan).removeOwner(true); + } + + if (getSiege().getIsInProgress()) + { + getSiege().midVictory(); // Mid victory phase of siege + } + + TerritoryWarManager.getInstance().getTerritory(_castleId).setOwnerClan(clan); + + for (L2PcInstance member : clan.getOnlineMembers(0)) + { + giveResidentialSkills(member); + member.sendSkillList(); + } + } + + public void removeOwner(L2Clan clan) + { + if (clan != null) + { + _formerOwner = clan; + if (Config.REMOVE_CASTLE_CIRCLETS) + { + CastleManager.getInstance().removeCirclet(_formerOwner, getCastleId()); + } + for (L2PcInstance member : clan.getOnlineMembers(0)) + { + removeResidentialSkills(member); + member.sendSkillList(); + } + clan.setHasCastle(0); + clan.broadcastToOnlineMembers(new PledgeShowInfoUpdate(clan)); + } + + updateOwnerInDB(null); + if (getSiege().getIsInProgress()) + { + getSiege().midVictory(); + } + + for (Map.Entry fc : _function.entrySet()) + { + removeFunction(fc.getKey()); + } + _function.clear(); + } + + // This method updates the castle tax rate + public void setTaxPercent(L2PcInstance activeChar, int taxPercent) + { + int maxTax; + switch (SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_STRIFE)) + { + case SevenSigns.CABAL_DAWN: + maxTax = 25; + break; + case SevenSigns.CABAL_DUSK: + maxTax = 5; + break; + default: // no owner + maxTax = 15; + } + + if ((taxPercent < 0) || (taxPercent > maxTax)) + { + activeChar.sendMessage("Tax value must be between 0 and " + maxTax + "."); + return; + } + + setTaxPercent(taxPercent); + activeChar.sendMessage(getName() + " castle tax changed to " + taxPercent + "%."); + } + + public void setTaxPercent(int taxPercent) + { + _taxPercent = taxPercent; + _taxRate = _taxPercent / 100.0; + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("Update castle set taxPercent = ? where id = ?"); + statement.setInt(1, taxPercent); + statement.setInt(2, getCastleId()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Respawn all doors on castle grounds
    + *
    + */ + public void spawnDoor() + { + spawnDoor(false); + } + + /** + * Respawn all doors on castle grounds
    + *
    + */ + public void spawnDoor(boolean isDoorWeak) + { + for (int i = 0; i < getDoors().size(); i++) + { + L2DoorInstance door = getDoors().get(i); + if (door.getCurrentHp() <= 0) + { + door.decayMe(); // Kill current if not killed already + door = DoorTable.parseList(_doorDefault.get(i), false); + DoorTable.getInstance().putDoor(door); // Readd the new door to the DoorTable By Erb + if (isDoorWeak) + { + door.setCurrentHp(door.getMaxHp() / 2); + } + door.spawnMe(door.getX(), door.getY(), door.getZ()); + getDoors().set(i, door); + } + else if (door.getOpen()) + { + door.closeMe(); + } + } + loadDoorUpgrade(); // Check for any upgrade the doors may have + } + + // This method upgrade door + public void upgradeDoor(int doorId, int hp, int pDef, int mDef) + { + L2DoorInstance door = getDoor(doorId); + if (door == null) + { + return; + } + + door.setCurrentHp(door.getMaxHp() + hp); + + saveDoorUpgrade(doorId, hp, pDef, mDef); + } + + // ========================================================= + // Method - Private + // This method loads castle + private void load() + { + Connection con = null; + try + { + PreparedStatement statement; + ResultSet rs; + + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement("Select * from castle where id = ?"); + statement.setInt(1, getCastleId()); + rs = statement.executeQuery(); + + while (rs.next()) + { + _name = rs.getString("name"); + // _OwnerId = rs.getInt("ownerId"); + + _siegeDate = Calendar.getInstance(); + _siegeDate.setTimeInMillis(rs.getLong("siegeDate")); + _siegeTimeRegistrationEndDate = Calendar.getInstance(); + _siegeTimeRegistrationEndDate.setTimeInMillis(rs.getLong("regTimeEnd")); + _isTimeRegistrationOver = rs.getBoolean("regTimeOver"); + + _taxPercent = rs.getInt("taxPercent"); + _treasury = rs.getLong("treasury"); + + _showNpcCrest = rs.getBoolean("showNpcCrest"); + } + rs.close(); + statement.close(); + + _taxRate = _taxPercent / 100.0; + + statement = con.prepareStatement("Select clan_id from clan_data where hasCastle = ?"); + statement.setInt(1, getCastleId()); + rs = statement.executeQuery(); + + while (rs.next()) + { + _ownerId = rs.getInt("clan_id"); + } + + if (getOwnerId() > 0) + { + L2Clan clan = ClanTable.getInstance().getClan(getOwnerId()); // Try to find clan instance + ThreadPoolManager.getInstance().scheduleGeneral(new CastleUpdater(clan, 1), 3600000); // Schedule owner tasks to start running + } + rs.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: loadCastleData(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Load All Functions */ + private void loadFunctions() + { + Connection con = null; + try + { + PreparedStatement statement; + ResultSet rs; + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("Select * from castle_functions where castle_id = ?"); + statement.setInt(1, getCastleId()); + rs = statement.executeQuery(); + while (rs.next()) + { + _function.put(rs.getInt("type"), new CastleFunction(rs.getInt("type"), rs.getInt("lvl"), rs.getInt("lease"), 0, rs.getLong("rate"), rs.getLong("endTime"), true)); + } + rs.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Castle.loadFunctions(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Remove function In List and in DB */ + public void removeFunction(int functionType) + { + _function.remove(functionType); + Connection con = null; + try + { + PreparedStatement statement; + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("DELETE FROM castle_functions WHERE castle_id=? AND type=?"); + statement.setInt(1, getCastleId()); + statement.setInt(2, functionType); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Castle.removeFunctions(int functionType): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public boolean updateFunctions(L2PcInstance player, int type, int lvl, int lease, long rate, boolean addNew) + { + if (player == null) + { + return false; + } + if (Config.DEBUG) + { + _log.warning("Called Castle.updateFunctions(int type, int lvl, int lease, long rate, boolean addNew) Owner : " + getOwnerId()); + } + if (lease > 0) + { + if (!player.destroyItemByItemId("Consume", 57, lease, null, true)) + { + return false; + } + } + if (addNew) + { + _function.put(type, new CastleFunction(type, lvl, lease, 0, rate, 0, false)); + } + else + { + if ((lvl == 0) && (lease == 0)) + { + removeFunction(type); + } + else + { + int diffLease = lease - _function.get(type).getLease(); + if (Config.DEBUG) + { + _log.warning("Called Castle.updateFunctions diffLease : " + diffLease); + } + if (diffLease > 0) + { + _function.remove(type); + _function.put(type, new CastleFunction(type, lvl, lease, 0, rate, -1, false)); + } + else + { + _function.get(type).setLease(lease); + _function.get(type).setLvl(lvl); + _function.get(type).dbSave(); + } + } + } + return true; + } + + public void activateInstance() + { + for (final L2DoorInstance door : _doors) + { + door.spawnMe(door.getX(), door.getY(), door.getZ()); + } + } + + // This method loads castle door data from database + private void loadDoor() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("Select * from castle_door where castleId = ?"); + statement.setInt(1, getCastleId()); + ResultSet rs = statement.executeQuery(); + + while (rs.next()) + { + // Create list of the door default for use when respawning dead doors + _doorDefault.add(rs.getString("name") + ";" + rs.getInt("id") + ";" + rs.getInt("x") + ";" + rs.getInt("y") + ";" + rs.getInt("z") + ";" + rs.getInt("range_xmin") + ";" + rs.getInt("range_ymin") + ";" + rs.getInt("range_zmin") + ";" + rs.getInt("range_xmax") + ";" + rs.getInt("range_ymax") + ";" + rs.getInt("range_zmax") + ";" + rs.getInt("hp") + ";" + rs.getInt("pDef") + ";" + rs.getInt("mDef") + ";0"); + + L2DoorInstance door = DoorTable.parseList(_doorDefault.get(_doorDefault.size() - 1), false); + door.setIsWall(rs.getBoolean("isWall")); + _doors.add(door); + DoorTable.getInstance().putDoor(door); + } + rs.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: loadCastleDoor(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + // This method loads castle door upgrade data from database + private void loadDoorUpgrade() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("Select * from castle_doorupgrade where doorId in (Select Id from castle_door where castleId = ?)"); + statement.setInt(1, getCastleId()); + ResultSet rs = statement.executeQuery(); + + while (rs.next()) + { + upgradeDoor(rs.getInt("id"), rs.getInt("hp"), rs.getInt("pDef"), rs.getInt("mDef")); + } + rs.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: loadCastleDoorUpgrade(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void removeDoorUpgrade() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("delete from castle_doorupgrade where doorId in (select id from castle_door where castleId=?)"); + statement.setInt(1, getCastleId()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: removeDoorUpgrade(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void saveDoorUpgrade(int doorId, int hp, int pDef, int mDef) + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("INSERT INTO castle_doorupgrade (doorId, hp, pDef, mDef) values (?,?,?,?)"); + statement.setInt(1, doorId); + statement.setInt(2, hp); + statement.setInt(3, pDef); + statement.setInt(4, mDef); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: saveDoorUpgrade(int doorId, int hp, int pDef, int mDef): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void updateOwnerInDB(L2Clan clan) + { + if (clan != null) + { + _ownerId = clan.getClanId(); // Update owner id property + } + else + { + _ownerId = 0; // Remove owner + resetManor(); + } + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + // ============================================================================ + // NEED TO REMOVE HAS CASTLE FLAG FROM CLAN_DATA + // SHOULD BE CHECKED FROM CASTLE TABLE + statement = con.prepareStatement("UPDATE clan_data SET hasCastle=0 WHERE hasCastle=?"); + statement.setInt(1, getCastleId()); + statement.execute(); + statement.close(); + + statement = con.prepareStatement("UPDATE clan_data SET hasCastle=? WHERE clan_id=?"); + statement.setInt(1, getCastleId()); + statement.setInt(2, getOwnerId()); + statement.execute(); + statement.close(); + // ============================================================================ + + // Announce to clan memebers + if (clan != null) + { + clan.setHasCastle(getCastleId()); // Set has castle flag for new owner + clan.broadcastToOnlineMembers(new PledgeShowInfoUpdate(clan)); + clan.broadcastToOnlineMembers(new PlaySound(1, "Siege_Victory", 0, 0, 0, 0, 0)); + ThreadPoolManager.getInstance().scheduleGeneral(new CastleUpdater(clan, 1), 3600000); // Schedule owner tasks to start running + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: updateOwnerInDB(L2Clan clan): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + // ========================================================= + // Property + public final int getCastleId() + { + return _castleId; + } + + public final L2DoorInstance getDoor(int doorId) + { + if (doorId <= 0) + { + return null; + } + + for (L2DoorInstance door : getDoors()) + { + if (door.getDoorId() == doorId) + { + return door; + } + } + return null; + } + + public final List getDoors() + { + return _doors; + } + + public final String getName() + { + return _name; + } + + public final int getOwnerId() + { + return _ownerId; + } + + public final Siege getSiege() + { + if (_siege == null) + { + _siege = new Siege(new Castle[] + { + this + }); + } + return _siege; + } + + public final Calendar getSiegeDate() + { + return _siegeDate; + } + + public boolean getIsTimeRegistrationOver() + { + return _isTimeRegistrationOver; + } + + public void setIsTimeRegistrationOver(boolean val) + { + _isTimeRegistrationOver = val; + } + + public Calendar getTimeRegistrationOverDate() + { + if (_siegeTimeRegistrationEndDate == null) + { + _siegeTimeRegistrationEndDate = Calendar.getInstance(); + } + return _siegeTimeRegistrationEndDate; + } + + public final int getTaxPercent() + { + return _taxPercent; + } + + public final double getTaxRate() + { + return _taxRate; + } + + public final long getTreasury() + { + return _treasury; + } + + public final boolean getShowNpcCrest() + { + return _showNpcCrest; + } + + public final void setShowNpcCrest(boolean showNpcCrest) + { + if (_showNpcCrest != showNpcCrest) + { + _showNpcCrest = showNpcCrest; + updateShowNpcCrest(); + } + } + + public List getSeedProduction(int period) + { + return (period == CastleManorManager.PERIOD_CURRENT ? _production : _productionNext); + } + + public List getCropProcure(int period) + { + return (period == CastleManorManager.PERIOD_CURRENT ? _procure : _procureNext); + } + + public void setSeedProduction(List seed, int period) + { + if (period == CastleManorManager.PERIOD_CURRENT) + { + _production = seed; + } + else + { + _productionNext = seed; + } + } + + public void setCropProcure(List crop, int period) + { + if (period == CastleManorManager.PERIOD_CURRENT) + { + _procure = crop; + } + else + { + _procureNext = crop; + } + } + + public SeedProduction getSeed(int seedId, int period) + { + for (SeedProduction seed : getSeedProduction(period)) + { + if (seed.getId() == seedId) + { + return seed; + } + } + return null; + } + + public CropProcure getCrop(int cropId, int period) + { + for (CropProcure crop : getCropProcure(period)) + { + if (crop.getId() == cropId) + { + return crop; + } + } + return null; + } + + public long getManorCost(int period) + { + List procure; + List production; + + if (period == CastleManorManager.PERIOD_CURRENT) + { + procure = _procure; + production = _production; + } + else + { + procure = _procureNext; + production = _productionNext; + } + + long total = 0; + if (production != null) + { + for (SeedProduction seed : production) + { + total += L2Manor.getInstance().getSeedBuyPrice(seed.getId()) * seed.getStartProduce(); + } + } + if (procure != null) + { + for (CropProcure crop : procure) + { + total += crop.getPrice() * crop.getStartAmount(); + } + } + return total; + } + + // save manor production data + public void saveSeedData() + { + Connection con = null; + PreparedStatement statement; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement(CASTLE_MANOR_DELETE_PRODUCTION); + statement.setInt(1, getCastleId()); + + statement.execute(); + statement.close(); + + if (_production != null) + { + int count = 0; + String query = "INSERT INTO castle_manor_production VALUES "; + String values[] = new String[_production.size()]; + for (SeedProduction s : _production) + { + values[count++] = "(" + getCastleId() + "," + s.getId() + "," + s.getCanProduce() + "," + s.getStartProduce() + "," + s.getPrice() + "," + CastleManorManager.PERIOD_CURRENT + ")"; + } + if (values.length > 0) + { + query += values[0]; + for (int i = 1; i < values.length; i++) + { + query += "," + values[i]; + } + statement = con.prepareStatement(query); + statement.execute(); + statement.close(); + } + } + + if (_productionNext != null) + { + int count = 0; + String query = "INSERT INTO castle_manor_production VALUES "; + String values[] = new String[_productionNext.size()]; + for (SeedProduction s : _productionNext) + { + values[count++] = "(" + getCastleId() + "," + s.getId() + "," + s.getCanProduce() + "," + s.getStartProduce() + "," + s.getPrice() + "," + CastleManorManager.PERIOD_NEXT + ")"; + } + if (values.length > 0) + { + query += values[0]; + for (int i = 1; i < values.length; i++) + { + query += "," + values[i]; + } + statement = con.prepareStatement(query); + statement.execute(); + statement.close(); + } + } + } + catch (Exception e) + { + _log.info("Error adding seed production data for castle " + getName() + ": " + e.getMessage()); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + // save manor production data for specified period + public void saveSeedData(int period) + { + Connection con = null; + PreparedStatement statement; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement(CASTLE_MANOR_DELETE_PRODUCTION_PERIOD); + statement.setInt(1, getCastleId()); + statement.setInt(2, period); + statement.execute(); + statement.close(); + + List prod = null; + prod = getSeedProduction(period); + + if (prod != null) + { + int count = 0; + String query = "INSERT INTO castle_manor_production VALUES "; + String values[] = new String[prod.size()]; + for (SeedProduction s : prod) + { + values[count++] = "(" + getCastleId() + "," + s.getId() + "," + s.getCanProduce() + "," + s.getStartProduce() + "," + s.getPrice() + "," + period + ")"; + } + if (values.length > 0) + { + query += values[0]; + for (int i = 1; i < values.length; i++) + { + query += "," + values[i]; + } + statement = con.prepareStatement(query); + statement.execute(); + statement.close(); + } + } + } + catch (Exception e) + { + _log.info("Error adding seed production data for castle " + getName() + ": " + e.getMessage()); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + // save crop procure data + public void saveCropData() + { + Connection con = null; + PreparedStatement statement; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement(CASTLE_MANOR_DELETE_PROCURE); + statement.setInt(1, getCastleId()); + statement.execute(); + statement.close(); + if ((_procure != null) && (_procure.size() > 0)) + { + int count = 0; + String query = "INSERT INTO castle_manor_procure VALUES "; + String values[] = new String[_procure.size()]; + for (CropProcure cp : _procure) + { + values[count++] = "(" + getCastleId() + "," + cp.getId() + "," + cp.getAmount() + "," + cp.getStartAmount() + "," + cp.getPrice() + "," + cp.getReward() + "," + CastleManorManager.PERIOD_CURRENT + ")"; + } + if (values.length > 0) + { + query += values[0]; + for (int i = 1; i < values.length; i++) + { + query += "," + values[i]; + } + statement = con.prepareStatement(query); + statement.execute(); + statement.close(); + } + } + if ((_procureNext != null) && (_procureNext.size() > 0)) + { + int count = 0; + String query = "INSERT INTO castle_manor_procure VALUES "; + String values[] = new String[_procureNext.size()]; + for (CropProcure cp : _procureNext) + { + values[count++] = "(" + getCastleId() + "," + cp.getId() + "," + cp.getAmount() + "," + cp.getStartAmount() + "," + cp.getPrice() + "," + cp.getReward() + "," + CastleManorManager.PERIOD_NEXT + ")"; + } + if (values.length > 0) + { + query += values[0]; + for (int i = 1; i < values.length; i++) + { + query += "," + values[i]; + } + statement = con.prepareStatement(query); + statement.execute(); + statement.close(); + } + } + } + catch (Exception e) + { + _log.info("Error adding crop data for castle " + getName() + ": " + e.getMessage()); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + // save crop procure data for specified period + public void saveCropData(int period) + { + Connection con = null; + PreparedStatement statement; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement(CASTLE_MANOR_DELETE_PROCURE_PERIOD); + statement.setInt(1, getCastleId()); + statement.setInt(2, period); + statement.execute(); + statement.close(); + + List proc = null; + proc = getCropProcure(period); + + if ((proc != null) && (proc.size() > 0)) + { + int count = 0; + String query = "INSERT INTO castle_manor_procure VALUES "; + String values[] = new String[proc.size()]; + + for (CropProcure cp : proc) + { + values[count++] = "(" + getCastleId() + "," + cp.getId() + "," + cp.getAmount() + "," + cp.getStartAmount() + "," + cp.getPrice() + "," + cp.getReward() + "," + period + ")"; + } + if (values.length > 0) + { + query += values[0]; + for (int i = 1; i < values.length; i++) + { + query += "," + values[i]; + } + statement = con.prepareStatement(query); + statement.execute(); + statement.close(); + } + } + } + catch (Exception e) + { + _log.info("Error adding crop data for castle " + getName() + ": " + e.getMessage()); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void updateCrop(int cropId, long amount, int period) + { + Connection con = null; + PreparedStatement statement; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement(CASTLE_UPDATE_CROP); + statement.setLong(1, amount); + statement.setInt(2, cropId); + statement.setInt(3, getCastleId()); + statement.setInt(4, period); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.info("Error adding crop data for castle " + getName() + ": " + e.getMessage()); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void updateSeed(int seedId, long amount, int period) + { + Connection con = null; + PreparedStatement statement; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement(CASTLE_UPDATE_SEED); + statement.setLong(1, amount); + statement.setInt(2, seedId); + statement.setInt(3, getCastleId()); + statement.setInt(4, period); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.info("Error adding seed production data for castle " + getName() + ": " + e.getMessage()); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public boolean isNextPeriodApproved() + { + return _isNextPeriodApproved; + } + + public void setNextPeriodApproved(boolean val) + { + _isNextPeriodApproved = val; + } + + public void updateClansReputation() + { + if (_formerOwner != null) + { + if (_formerOwner != ClanTable.getInstance().getClan(getOwnerId())) + { + int maxreward = Math.max(0, _formerOwner.getReputationScore()); + _formerOwner.takeReputationScore(Config.LOOSE_CASTLE_POINTS, true); + L2Clan owner = ClanTable.getInstance().getClan(getOwnerId()); + if (owner != null) + { + owner.addReputationScore(Math.min(Config.TAKE_CASTLE_POINTS, maxreward), true); + } + } + else + { + _formerOwner.addReputationScore(Config.CASTLE_DEFENDED_POINTS, true); + } + } + else + { + L2Clan owner = ClanTable.getInstance().getClan(getOwnerId()); + if (owner != null) + { + owner.addReputationScore(Config.TAKE_CASTLE_POINTS, true); + } + } + } + + public void updateShowNpcCrest() + { + Connection con = null; + PreparedStatement statement; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement("UPDATE castle SET showNpcCrest = ? WHERE id = ?"); + statement.setString(1, String.valueOf(getShowNpcCrest())); + statement.setInt(2, getCastleId()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.info("Error saving showNpcCrest for castle " + getName() + ": " + e.getMessage()); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public FastList getResidentialSkills() + { + return _residentialSkills; + } + + public void giveResidentialSkills(L2PcInstance player) + { + if ((_residentialSkills != null) && !_residentialSkills.isEmpty()) + { + for (L2Skill sk : _residentialSkills) + { + player.addSkill(sk, false); + } + } + Territory territory = TerritoryWarManager.getInstance().getTerritory(getCastleId()); + if ((territory != null) && territory.getOwnedWardIds().contains(getCastleId() + 80)) + { + for (int wardId : territory.getOwnedWardIds()) + { + final FastList territorySkills = SkillTreesData.getInstance().getAvailableResidentialSkills(wardId); + for (L2SkillLearn s : territorySkills) + { + final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()); + if (sk != null) + { + player.addSkill(sk, false); + } + else + { + _log.warning("Trying to add a null skill for Territory Ward Id: " + wardId + ", skill Id: " + s.getSkillId() + " level: " + s.getSkillLevel() + "!"); + } + } + } + } + } + + public void removeResidentialSkills(L2PcInstance player) + { + if ((_residentialSkills != null) && !_residentialSkills.isEmpty()) + { + for (L2Skill sk : _residentialSkills) + { + player.removeSkill(sk, false, true); + } + } + if (TerritoryWarManager.getInstance().getTerritory(getCastleId()) != null) + { + for (int wardId : TerritoryWarManager.getInstance().getTerritory(getCastleId()).getOwnedWardIds()) + { + final FastList territorySkills = SkillTreesData.getInstance().getAvailableResidentialSkills(wardId); + for (L2SkillLearn s : territorySkills) + { + final L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()); + if (sk != null) + { + player.removeSkill(sk, false, true); + } + else + { + _log.warning("Trying to remove a null skill for Territory Ward Id: " + wardId + ", skill Id: " + s.getSkillId() + " level: " + s.getSkillLevel() + "!"); + } + } + } + } + } + + /** + * Register Artefact to castle + * @param artefact + */ + public void registerArtefact(L2ArtefactInstance artefact) + { + if (Config.DEBUG) + { + _log.info("ArtefactId: " + artefact.getObjectId() + " is registered to " + getName() + " castle."); + } + _artefacts.add(artefact); + } + + public List getArtefacts() + { + return _artefacts; + } + + public void resetManor() + { + setCropProcure(new FastList(), CastleManorManager.PERIOD_CURRENT); + setCropProcure(new FastList(), CastleManorManager.PERIOD_NEXT); + setSeedProduction(new FastList(), CastleManorManager.PERIOD_CURRENT); + setSeedProduction(new FastList(), CastleManorManager.PERIOD_NEXT); + if (Config.ALT_MANOR_SAVE_ALL_ACTIONS) + { + saveCropData(); + saveSeedData(); + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/ClanHall.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/ClanHall.java new file mode 100644 index 00000000..903f93e7 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/ClanHall.java @@ -0,0 +1,728 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.entity; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.datatables.DoorTable; +import com.l2jserver.gameserver.instancemanager.AuctionManager; +import com.l2jserver.gameserver.instancemanager.ClanHallManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.zone.type.L2ClanHallZone; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.PledgeShowInfoUpdate; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; + +public class ClanHall +{ + protected static final Logger _log = Logger.getLogger(ClanHall.class.getName()); + + private final int _clanHallId; + private List _doors; + private final List _doorDefault; + private final String _name; + private int _ownerId; + private final int _lease; + private final String _desc; + private final String _location; + protected long _paidUntil; + private L2ClanHallZone _zone; + private final int _grade; + protected final int _chRate = 604800000; + protected boolean _isFree = true; + private final Map _functions; + protected boolean _paid; + + /** Clan Hall Functions */ + public static final int FUNC_TELEPORT = 1; + public static final int FUNC_ITEM_CREATE = 2; + public static final int FUNC_RESTORE_HP = 3; + public static final int FUNC_RESTORE_MP = 4; + public static final int FUNC_RESTORE_EXP = 5; + public static final int FUNC_SUPPORT = 6; + public static final int FUNC_DECO_FRONTPLATEFORM = 7; + public static final int FUNC_DECO_CURTAINS = 8; + + public class ClanHallFunction + { + private final int _type; + private int _lvl; + protected int _fee; + protected int _tempFee; + private final long _rate; + private long _endDate; + protected boolean _inDebt; + public boolean _cwh; // first activating clanhall function is payed from player inventory, any others from clan warehouse + + public ClanHallFunction(int type, int lvl, int lease, int tempLease, long rate, long time, boolean cwh) + { + _type = type; + _lvl = lvl; + _fee = lease; + _tempFee = tempLease; + _rate = rate; + _endDate = time; + initializeTask(cwh); + } + + public int getType() + { + return _type; + } + + public int getLvl() + { + return _lvl; + } + + public int getLease() + { + return _fee; + } + + public long getRate() + { + return _rate; + } + + public long getEndTime() + { + return _endDate; + } + + public void setLvl(int lvl) + { + _lvl = lvl; + } + + public void setLease(int lease) + { + _fee = lease; + } + + public void setEndTime(long time) + { + _endDate = time; + } + + private void initializeTask(boolean cwh) + { + if (_isFree) + { + return; + } + long currentTime = System.currentTimeMillis(); + if (_endDate > currentTime) + { + ThreadPoolManager.getInstance().scheduleGeneral(new FunctionTask(cwh), _endDate - currentTime); + } + else + { + ThreadPoolManager.getInstance().scheduleGeneral(new FunctionTask(cwh), 0); + } + } + + private class FunctionTask implements Runnable + { + public FunctionTask(boolean cwh) + { + _cwh = cwh; + } + + @Override + public void run() + { + try + { + if (_isFree) + { + return; + } + if ((ClanTable.getInstance().getClan(getOwnerId()).getWarehouse().getAdena() >= _fee) || !_cwh) + { + int fee = _fee; + if (getEndTime() == -1) + { + fee = _tempFee; + } + + setEndTime(System.currentTimeMillis() + getRate()); + dbSave(); + if (_cwh) + { + ClanTable.getInstance().getClan(getOwnerId()).getWarehouse().destroyItemByItemId("CH_function_fee", 57, fee, null, null); + if (Config.DEBUG) + { + _log.warning("deducted " + fee + " adena from " + getName() + " owner's cwh for function id : " + getType()); + } + } + ThreadPoolManager.getInstance().scheduleGeneral(new FunctionTask(true), getRate()); + } + else + { + removeFunction(getType()); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } + + public void dbSave() + { + Connection con = null; + try + { + PreparedStatement statement; + + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("REPLACE INTO clanhall_functions (hall_id, type, lvl, lease, rate, endTime) VALUES (?,?,?,?,?,?)"); + statement.setInt(1, getId()); + statement.setInt(2, getType()); + statement.setInt(3, getLvl()); + statement.setInt(4, getLease()); + statement.setLong(5, getRate()); + statement.setLong(6, getEndTime()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: ClanHall.updateFunctions(int type, int lvl, int lease, long rate, long time, boolean addNew): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + } + + public ClanHall(int clanHallId, String name, int ownerId, int lease, String desc, String location, long paidUntil, int Grade, boolean paid) + { + _clanHallId = clanHallId; + _name = name; + _ownerId = ownerId; + if (Config.DEBUG) + { + _log.warning("Init Owner : " + _ownerId); + } + _lease = lease; + _desc = desc; + _location = location; + _paidUntil = paidUntil; + _grade = Grade; + _paid = paid; + _doorDefault = new FastList<>(); + _functions = new FastMap<>(); + + if (ownerId != 0) + { + _isFree = false; + initialyzeTask(false); + loadFunctions(); + } + } + + /** Return if clanHall is paid or not */ + public final boolean getPaid() + { + return _paid; + } + + /** Return Id Of Clan hall */ + public final int getId() + { + return _clanHallId; + } + + /** Return name */ + public final String getName() + { + return _name; + } + + /** Return OwnerId */ + public final int getOwnerId() + { + return _ownerId; + } + + /** Return lease */ + public final int getLease() + { + return _lease; + } + + /** Return Desc */ + public final String getDesc() + { + return _desc; + } + + /** Return Location */ + public final String getLocation() + { + return _location; + } + + /** Return PaidUntil */ + public final long getPaidUntil() + { + return _paidUntil; + } + + /** Return Grade */ + public final int getGrade() + { + return _grade; + } + + /** Return all DoorInstance */ + public final List getDoors() + { + if (_doors == null) + { + _doors = new FastList<>(); + } + return _doors; + } + + /** Return Door */ + public final L2DoorInstance getDoor(int doorId) + { + if (doorId <= 0) + { + return null; + } + for (L2DoorInstance door : getDoors()) + { + if (door.getDoorId() == doorId) + { + return door; + } + } + return null; + } + + /** Return function with id */ + public ClanHallFunction getFunction(int type) + { + if (_functions.get(type) != null) + { + return _functions.get(type); + } + return null; + } + + /** + * Sets this clan halls zone + * @param zone + */ + public void setZone(L2ClanHallZone zone) + { + _zone = zone; + } + + /** Returns the zone of this clan hall */ + public L2ClanHallZone getZone() + { + return _zone; + } + + /** Free this clan hall */ + public void free() + { + _ownerId = 0; + _isFree = true; + for (Map.Entry fc : _functions.entrySet()) + { + removeFunction(fc.getKey()); + } + _functions.clear(); + _paidUntil = 0; + _paid = false; + updateDb(); + } + + /** Set owner if clan hall is free */ + public void setOwner(L2Clan clan) + { + // Verify that this ClanHall is Free and Clan isn't null + if ((_ownerId > 0) || (clan == null)) + { + return; + } + _ownerId = clan.getClanId(); + _isFree = false; + _paidUntil = System.currentTimeMillis(); + initialyzeTask(true); + // Annonce to Online member new ClanHall + clan.broadcastToOnlineMembers(new PledgeShowInfoUpdate(clan)); + updateDb(); + } + + /** Respawn all doors */ + public void spawnDoor() + { + spawnDoor(false); + } + + /** Respawn all doors */ + public void spawnDoor(boolean isDoorWeak) + { + for (int i = 0; i < getDoors().size(); i++) + { + L2DoorInstance door = getDoors().get(i); + if (door.getCurrentHp() <= 0) + { + door.decayMe(); // Kill current if not killed already + door = DoorTable.parseList(_doorDefault.get(i), false); + DoorTable.getInstance().putDoor(door); // Readd the new door to the DoorTable By Erb + if (isDoorWeak) + { + door.setCurrentHp(door.getMaxHp() / 2); + } + door.spawnMe(door.getX(), door.getY(), door.getZ()); + getDoors().set(i, door); + } + else if (door.getOpen()) + { + door.closeMe(); + } + } + } + + /** Open or Close Door */ + public void openCloseDoor(L2PcInstance activeChar, int doorId, boolean open) + { + if ((activeChar != null) && (activeChar.getClanId() == getOwnerId())) + { + openCloseDoor(doorId, open); + } + } + + public void openCloseDoor(int doorId, boolean open) + { + openCloseDoor(getDoor(doorId), open); + } + + public void openCloseDoor(L2DoorInstance door, boolean open) + { + if (door != null) + { + if (open) + { + door.openMe(); + } + else + { + door.closeMe(); + } + } + } + + public void openCloseDoors(L2PcInstance activeChar, boolean open) + { + if ((activeChar != null) && (activeChar.getClanId() == getOwnerId())) + { + openCloseDoors(open); + } + } + + public void openCloseDoors(boolean open) + { + for (L2DoorInstance door : getDoors()) + { + if (door != null) + { + if (open) + { + door.openMe(); + } + else + { + door.closeMe(); + } + } + } + } + + /** Banish Foreigner */ + public void banishForeigners() + { + _zone.banishForeigners(getOwnerId()); + } + + /** Load All Functions */ + private void loadFunctions() + { + Connection con = null; + try + { + PreparedStatement statement; + ResultSet rs; + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("Select * from clanhall_functions where hall_id = ?"); + statement.setInt(1, getId()); + rs = statement.executeQuery(); + while (rs.next()) + { + _functions.put(rs.getInt("type"), new ClanHallFunction(rs.getInt("type"), rs.getInt("lvl"), rs.getInt("lease"), 0, rs.getLong("rate"), rs.getLong("endTime"), true)); + } + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: ClanHall.loadFunctions(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Remove function In List and in DB */ + public void removeFunction(int functionType) + { + _functions.remove(functionType); + Connection con = null; + try + { + PreparedStatement statement; + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("DELETE FROM clanhall_functions WHERE hall_id=? AND type=?"); + statement.setInt(1, getId()); + statement.setInt(2, functionType); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: ClanHall.removeFunctions(int functionType): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public boolean updateFunctions(L2PcInstance player, int type, int lvl, int lease, long rate, boolean addNew) + { + if (player == null) + { + return false; + } + if (Config.DEBUG) + { + _log.warning("Called ClanHall.updateFunctions(int type, int lvl, int lease, long rate, boolean addNew) Owner : " + getOwnerId()); + } + if (lease > 0) + { + if (!player.destroyItemByItemId("Consume", 57, lease, null, true)) + { + return false; + } + } + if (addNew) + { + _functions.put(type, new ClanHallFunction(type, lvl, lease, 0, rate, 0, false)); + } + else + { + if ((lvl == 0) && (lease == 0)) + { + removeFunction(type); + } + else + { + int diffLease = lease - _functions.get(type).getLease(); + if (Config.DEBUG) + { + _log.warning("Called ClanHall.updateFunctions diffLease : " + diffLease); + } + if (diffLease > 0) + { + _functions.remove(type); + _functions.put(type, new ClanHallFunction(type, lvl, lease, 0, rate, -1, false)); + } + else + { + _functions.get(type).setLease(lease); + _functions.get(type).setLvl(lvl); + _functions.get(type).dbSave(); + } + } + } + return true; + } + + /** Update DB */ + public void updateDb() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + statement = con.prepareStatement("UPDATE clanhall SET ownerId=?, paidUntil=?, paid=? WHERE id=?"); + statement.setInt(1, _ownerId); + statement.setLong(2, _paidUntil); + statement.setInt(3, (_paid) ? 1 : 0); + statement.setInt(4, _clanHallId); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: updateOwnerInDB(L2Clan clan): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Initialize Fee Task */ + private void initialyzeTask(boolean forced) + { + long currentTime = System.currentTimeMillis(); + if (_paidUntil > currentTime) + { + ThreadPoolManager.getInstance().scheduleGeneral(new FeeTask(), _paidUntil - currentTime); + } + else if (!_paid && !forced) + { + if ((System.currentTimeMillis() + (1000 * 60 * 60 * 24)) <= (_paidUntil + _chRate)) + { + ThreadPoolManager.getInstance().scheduleGeneral(new FeeTask(), System.currentTimeMillis() + (1000 * 60 * 60 * 24)); + } + else + { + ThreadPoolManager.getInstance().scheduleGeneral(new FeeTask(), (_paidUntil + _chRate) - System.currentTimeMillis()); + } + } + else + { + ThreadPoolManager.getInstance().scheduleGeneral(new FeeTask(), 0); + } + } + + /** Fee Task */ + private class FeeTask implements Runnable + { + public FeeTask() + { + } + + @Override + public void run() + { + try + { + long _time = System.currentTimeMillis(); + + if (_isFree) + { + return; + } + + if (_paidUntil > _time) + { + ThreadPoolManager.getInstance().scheduleGeneral(new FeeTask(), _paidUntil - _time); + return; + } + + L2Clan Clan = ClanTable.getInstance().getClan(getOwnerId()); + if (ClanTable.getInstance().getClan(getOwnerId()).getWarehouse().getAdena() >= getLease()) + { + if (_paidUntil != 0) + { + while (_paidUntil <= _time) + { + _paidUntil += _chRate; + } + } + else + { + _paidUntil = _time + _chRate; + } + ClanTable.getInstance().getClan(getOwnerId()).getWarehouse().destroyItemByItemId("CH_rental_fee", 57, getLease(), null, null); + if (Config.DEBUG) + { + _log.warning("deducted " + getLease() + " adena from " + getName() + " owner's cwh for ClanHall _paidUntil: " + _paidUntil); + } + ThreadPoolManager.getInstance().scheduleGeneral(new FeeTask(), _paidUntil - _time); + _paid = true; + updateDb(); + } + else + { + _paid = false; + if (_time > (_paidUntil + _chRate)) + { + if (ClanHallManager.getInstance().loaded()) + { + AuctionManager.getInstance().initNPC(getId()); + ClanHallManager.getInstance().setFree(getId()); + Clan.broadcastToOnlineMembers(SystemMessage.getSystemMessage(SystemMessageId.THE_CLAN_HALL_FEE_IS_ONE_WEEK_OVERDUE_THEREFORE_THE_CLAN_HALL_OWNERSHIP_HAS_BEEN_REVOKED)); + } + else + { + ThreadPoolManager.getInstance().scheduleGeneral(new FeeTask(), 3000); + } + } + else + { + updateDb(); + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.PAYMENT_FOR_YOUR_CLAN_HALL_HAS_NOT_BEEN_MADE_PLEASE_MAKE_PAYMENT_TO_YOUR_CLAN_WAREHOUSE_BY_S1_TOMORROW); + sm.addNumber(getLease()); + Clan.broadcastToOnlineMembers(sm); + if ((_time + (1000 * 60 * 60 * 24)) <= (_paidUntil + _chRate)) + { + ThreadPoolManager.getInstance().scheduleGeneral(new FeeTask(), _time + (1000 * 60 * 60 * 24)); + } + else + { + ThreadPoolManager.getInstance().scheduleGeneral(new FeeTask(), (_paidUntil + _chRate) - _time); + } + + } + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/Couple.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Couple.java new file mode 100644 index 00000000..7a7e4fa9 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Couple.java @@ -0,0 +1,205 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.entity; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Calendar; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.idfactory.IdFactory; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; + +/** + * @author evill33t + */ +public class Couple +{ + private static final Logger _log = Logger.getLogger(Couple.class.getName()); + + // ========================================================= + // Data Field + private int _Id = 0; + private int _player1Id = 0; + private int _player2Id = 0; + private boolean _maried = false; + private Calendar _affiancedDate; + private Calendar _weddingDate; + + // ========================================================= + // Constructor + public Couple(int coupleId) + { + _Id = coupleId; + + Connection con = null; + try + { + PreparedStatement statement; + ResultSet rs; + + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement("SELECT * FROM mods_wedding WHERE id = ?"); + statement.setInt(1, _Id); + rs = statement.executeQuery(); + + while (rs.next()) + { + _player1Id = rs.getInt("player1Id"); + _player2Id = rs.getInt("player2Id"); + _maried = rs.getBoolean("married"); + + _affiancedDate = Calendar.getInstance(); + _affiancedDate.setTimeInMillis(rs.getLong("affianceDate")); + + _weddingDate = Calendar.getInstance(); + _weddingDate.setTimeInMillis(rs.getLong("weddingDate")); + } + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Couple.load(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public Couple(L2PcInstance player1, L2PcInstance player2) + { + int _tempPlayer1Id = player1.getObjectId(); + int _tempPlayer2Id = player2.getObjectId(); + + _player1Id = _tempPlayer1Id; + _player2Id = _tempPlayer2Id; + + _affiancedDate = Calendar.getInstance(); + _affiancedDate.setTimeInMillis(Calendar.getInstance().getTimeInMillis()); + + _weddingDate = Calendar.getInstance(); + _weddingDate.setTimeInMillis(Calendar.getInstance().getTimeInMillis()); + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + _Id = IdFactory.getInstance().getNextId(); + statement = con.prepareStatement("INSERT INTO mods_wedding (id, player1Id, player2Id, married, affianceDate, weddingDate) VALUES (?, ?, ?, ?, ?, ?)"); + statement.setInt(1, _Id); + statement.setInt(2, _player1Id); + statement.setInt(3, _player2Id); + statement.setBoolean(4, false); + statement.setLong(5, _affiancedDate.getTimeInMillis()); + statement.setLong(6, _weddingDate.getTimeInMillis()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not create couple: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void marry() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + statement = con.prepareStatement("UPDATE mods_wedding set married = ?, weddingDate = ? where id = ?"); + statement.setBoolean(1, true); + _weddingDate = Calendar.getInstance(); + statement.setLong(2, _weddingDate.getTimeInMillis()); + statement.setInt(3, _Id); + statement.execute(); + statement.close(); + _maried = true; + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Could not marry: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void divorce() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + statement = con.prepareStatement("DELETE FROM mods_wedding WHERE id=?"); + statement.setInt(1, _Id); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Couple.divorce(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public final int getId() + { + return _Id; + } + + public final int getPlayer1Id() + { + return _player1Id; + } + + public final int getPlayer2Id() + { + return _player2Id; + } + + public final boolean getMaried() + { + return _maried; + } + + public final Calendar getAffiancedDate() + { + return _affiancedDate; + } + + public final Calendar getWeddingDate() + { + return _weddingDate; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/DimensionalRift.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/DimensionalRift.java new file mode 100644 index 00000000..78c01f25 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/DimensionalRift.java @@ -0,0 +1,483 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.entity; + +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.Future; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.instancemanager.DimensionalRiftManager; +import com.l2jserver.gameserver.instancemanager.QuestManager; +import com.l2jserver.gameserver.model.L2Party; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.quest.Quest; +import com.l2jserver.gameserver.model.quest.QuestState; +import com.l2jserver.gameserver.network.serverpackets.Earthquake; +import com.l2jserver.util.Rnd; + +/** + * Thanks to L2Fortress and balancer.ru - kombat + */ +public class DimensionalRift +{ + protected byte _type; + protected L2Party _party; + protected FastList _completedRooms = new FastList<>(); + private static final long seconds_5 = 5000L; + // private static final int MILLISECONDS_IN_MINUTE = 60000; + protected byte jumps_current = 0; + + private Timer teleporterTimer; + private TimerTask teleporterTimerTask; + private Timer spawnTimer; + private TimerTask spawnTimerTask; + + private Future earthQuakeTask; + + protected byte _choosenRoom = -1; + private boolean _hasJumped = false; + protected FastList deadPlayers = new FastList<>(); + protected FastList revivedInWaitingRoom = new FastList<>(); + private boolean isBossRoom = false; + + // private final static Log _log = LogFactory.getLog(DimensionalRift.class.getName()); + + public DimensionalRift(L2Party party, byte type, byte room) + { + DimensionalRiftManager.getInstance().getRoom(type, room).setPartyInside(true); + _type = type; + _party = party; + _choosenRoom = room; + int[] coords = getRoomCoord(room); + party.setDimensionalRift(this); + for (L2PcInstance p : party.getPartyMembers()) + { + Quest riftQuest = QuestManager.getInstance().getQuest(635); + if (riftQuest != null) + { + QuestState qs = p.getQuestState(riftQuest.getName()); + if (qs == null) + { + qs = riftQuest.newQuestState(p); + } + if (qs.getInt("cond") != 1) + { + qs.set("cond", "1"); + } + } + p.teleToLocation(coords[0], coords[1], coords[2]); + } + createSpawnTimer(_choosenRoom); + createTeleporterTimer(true); + } + + public byte getType() + { + return _type; + } + + public byte getCurrentRoom() + { + return _choosenRoom; + } + + protected void createTeleporterTimer(final boolean reasonTP) + { + if (teleporterTimerTask != null) + { + teleporterTimerTask.cancel(); + teleporterTimerTask = null; + } + + if (teleporterTimer != null) + { + teleporterTimer.cancel(); + teleporterTimer = null; + } + + if (earthQuakeTask != null) + { + earthQuakeTask.cancel(false); + earthQuakeTask = null; + } + + teleporterTimer = new Timer(); + teleporterTimerTask = new TimerTask() + { + @Override + public void run() + { + if (_choosenRoom > -1) + { + DimensionalRiftManager.getInstance().getRoom(_type, _choosenRoom).unspawn().setPartyInside(false); + } + + if (reasonTP && (jumps_current < getMaxJumps()) && (_party.getMemberCount() > deadPlayers.size())) + { + jumps_current++; + + _completedRooms.add(_choosenRoom); + _choosenRoom = -1; + + for (L2PcInstance p : _party.getPartyMembers()) + { + if (!revivedInWaitingRoom.contains(p)) + { + teleportToNextRoom(p); + } + } + createTeleporterTimer(true); + createSpawnTimer(_choosenRoom); + } + else + { + for (L2PcInstance p : _party.getPartyMembers()) + { + if (!revivedInWaitingRoom.contains(p)) + { + teleportToWaitingRoom(p); + } + } + killRift(); + cancel(); + } + } + }; + + if (reasonTP) + { + long jumpTime = calcTimeToNextJump(); + teleporterTimer.schedule(teleporterTimerTask, jumpTime); // Teleporter task, 8-10 minutes + + earthQuakeTask = ThreadPoolManager.getInstance().scheduleGeneral(() -> + { + for (L2PcInstance p : _party.getPartyMembers()) + { + if (!revivedInWaitingRoom.contains(p)) + { + p.sendPacket(new Earthquake(p.getX(), p.getY(), p.getZ(), 65, 9)); + } + } + }, jumpTime - 7000); + } + else + { + teleporterTimer.schedule(teleporterTimerTask, seconds_5); // incorrect party member invited. + } + } + + public void createSpawnTimer(final byte room) + { + if (spawnTimerTask != null) + { + spawnTimerTask.cancel(); + spawnTimerTask = null; + } + + if (spawnTimer != null) + { + spawnTimer.cancel(); + spawnTimer = null; + } + + spawnTimer = new Timer(); + spawnTimerTask = new TimerTask() + { + @Override + public void run() + { + DimensionalRiftManager.getInstance().getRoom(_type, room).spawn(); + } + }; + + spawnTimer.schedule(spawnTimerTask, Config.RIFT_SPAWN_DELAY); + } + + public void partyMemberInvited() + { + createTeleporterTimer(false); + } + + public void partyMemberExited(L2PcInstance player) + { + if (deadPlayers.contains(player)) + { + deadPlayers.remove(player); + } + + if (revivedInWaitingRoom.contains(player)) + { + revivedInWaitingRoom.remove(player); + } + + if ((_party.getMemberCount() < Config.RIFT_MIN_PARTY_SIZE) || (_party.getMemberCount() == 1)) + { + for (L2PcInstance p : _party.getPartyMembers()) + { + teleportToWaitingRoom(p); + } + killRift(); + } + } + + public void manualTeleport(L2PcInstance player, L2Npc npc) + { + if (!player.isInParty() || !player.getParty().isInDimensionalRift()) + { + return; + } + + if (player.getObjectId() != player.getParty().getPartyLeaderOID()) + { + DimensionalRiftManager.getInstance().showHtmlFile(player, "data/html/seven_signs/rift/NotPartyLeader.htm", npc); + return; + } + + if (_hasJumped) + { + DimensionalRiftManager.getInstance().showHtmlFile(player, "data/html/seven_signs/rift/AlreadyTeleported.htm", npc); + return; + } + + _hasJumped = true; + + DimensionalRiftManager.getInstance().getRoom(_type, _choosenRoom).unspawn().setPartyInside(false); + _completedRooms.add(_choosenRoom); + _choosenRoom = -1; + + for (L2PcInstance p : _party.getPartyMembers()) + { + teleportToNextRoom(p); + } + + DimensionalRiftManager.getInstance().getRoom(_type, _choosenRoom).setPartyInside(true); + + createSpawnTimer(_choosenRoom); + createTeleporterTimer(true); + } + + public void manualExitRift(L2PcInstance player, L2Npc npc) + { + if (!player.isInParty() || !player.getParty().isInDimensionalRift()) + { + return; + } + + if (player.getObjectId() != player.getParty().getPartyLeaderOID()) + { + DimensionalRiftManager.getInstance().showHtmlFile(player, "data/html/seven_signs/rift/NotPartyLeader.htm", npc); + return; + } + + for (L2PcInstance p : player.getParty().getPartyMembers()) + { + teleportToWaitingRoom(p); + } + killRift(); + } + + protected void teleportToNextRoom(L2PcInstance player) + { + + if (_choosenRoom == -1) + { + FastList emptyRooms; + do + { + emptyRooms = DimensionalRiftManager.getInstance().getFreeRooms(_type); + // Do not tp in the same room a second time + emptyRooms.removeAll(_completedRooms); + // If no room left, find any empty + if (emptyRooms.isEmpty()) + { + emptyRooms = DimensionalRiftManager.getInstance().getFreeRooms(_type); + } + _choosenRoom = emptyRooms.get(Rnd.get(1, emptyRooms.size()) - 1); + } + while (DimensionalRiftManager.getInstance().getRoom(_type, _choosenRoom).ispartyInside()); + } + + DimensionalRiftManager.getInstance().getRoom(_type, _choosenRoom).setPartyInside(true); + checkBossRoom(_choosenRoom); + int[] coords = getRoomCoord(_choosenRoom); + player.teleToLocation(coords[0], coords[1], coords[2]); + } + + protected void teleportToWaitingRoom(L2PcInstance player) + { + DimensionalRiftManager.getInstance().teleportToWaitingRoom(player); + Quest riftQuest = QuestManager.getInstance().getQuest(635); + if (riftQuest != null) + { + QuestState qs = player.getQuestState(riftQuest.getName()); + if ((qs != null) && (qs.getInt("cond") == 1)) + { + qs.set("cond", "0"); + } + } + } + + public void killRift() + { + _completedRooms = null; + + if (_party != null) + { + _party.setDimensionalRift(null); + } + + _party = null; + revivedInWaitingRoom = null; + deadPlayers = null; + + if (earthQuakeTask != null) + { + earthQuakeTask.cancel(false); + earthQuakeTask = null; + } + + DimensionalRiftManager.getInstance().getRoom(_type, _choosenRoom).unspawn().setPartyInside(false); + DimensionalRiftManager.getInstance().killRift(this); + } + + public Timer getTeleportTimer() + { + return teleporterTimer; + } + + public TimerTask getTeleportTimerTask() + { + return teleporterTimerTask; + } + + public Timer getSpawnTimer() + { + return spawnTimer; + } + + public TimerTask getSpawnTimerTask() + { + return spawnTimerTask; + } + + public void setTeleportTimer(Timer t) + { + teleporterTimer = t; + } + + public void setTeleportTimerTask(TimerTask tt) + { + teleporterTimerTask = tt; + } + + public void setSpawnTimer(Timer t) + { + spawnTimer = t; + } + + public void setSpawnTimerTask(TimerTask st) + { + spawnTimerTask = st; + } + + private long calcTimeToNextJump() + { + int time = Rnd.get(Config.RIFT_AUTO_JUMPS_TIME_MIN, Config.RIFT_AUTO_JUMPS_TIME_MAX) * 1000; + if (isBossRoom) + { + return (long) (time * Config.RIFT_BOSS_ROOM_TIME_MUTIPLY); + } + return time; + } + + public void memberDead(L2PcInstance player) + { + if (!deadPlayers.contains(player)) + { + deadPlayers.add(player); + } + } + + public void memberRessurected(L2PcInstance player) + { + if (deadPlayers.contains(player)) + { + deadPlayers.remove(player); + } + } + + public void usedTeleport(L2PcInstance player) + { + if (!revivedInWaitingRoom.contains(player)) + { + revivedInWaitingRoom.add(player); + } + + if (!deadPlayers.contains(player)) + { + deadPlayers.add(player); + } + + if ((_party.getMemberCount() - revivedInWaitingRoom.size()) < Config.RIFT_MIN_PARTY_SIZE) + { + // int pcm = _party.getMemberCount(); + // int rev = revivedInWaitingRoom.size(); + // int min = Config.RIFT_MIN_PARTY_SIZE; + + for (L2PcInstance p : _party.getPartyMembers()) + { + if ((p != null) && !revivedInWaitingRoom.contains(p)) + { + teleportToWaitingRoom(p); + } + } + killRift(); + } + } + + public FastList getDeadMemberList() + { + return deadPlayers; + } + + public FastList getRevivedAtWaitingRoom() + { + return revivedInWaitingRoom; + } + + public void checkBossRoom(byte room) + { + isBossRoom = DimensionalRiftManager.getInstance().getRoom(_type, room).isBossRoom(); + } + + public int[] getRoomCoord(byte room) + { + return DimensionalRiftManager.getInstance().getRoom(_type, room).getTeleportCoords(); + } + + public byte getMaxJumps() + { + if ((Config.RIFT_MAX_JUMPS <= 8) && (Config.RIFT_MAX_JUMPS >= 1)) + { + return (byte) Config.RIFT_MAX_JUMPS; + } + return 4; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/Duel.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Duel.java new file mode 100644 index 00000000..f27111d1 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Duel.java @@ -0,0 +1,1077 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.entity; + +import java.util.Calendar; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; + +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.ai.CtrlIntention; +import com.l2jserver.gameserver.instancemanager.DuelManager; +import com.l2jserver.gameserver.model.L2Effect; +import com.l2jserver.gameserver.model.actor.L2Character; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ActionFailed; +import com.l2jserver.gameserver.network.serverpackets.ExDuelEnd; +import com.l2jserver.gameserver.network.serverpackets.ExDuelReady; +import com.l2jserver.gameserver.network.serverpackets.ExDuelStart; +import com.l2jserver.gameserver.network.serverpackets.ExDuelUpdateUserInfo; +import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket; +import com.l2jserver.gameserver.network.serverpackets.PlaySound; +import com.l2jserver.gameserver.network.serverpackets.SocialAction; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; + +public class Duel +{ + protected static final Logger _log = Logger.getLogger(Duel.class.getName()); + + public static final int DUELSTATE_NODUEL = 0; + public static final int DUELSTATE_DUELLING = 1; + public static final int DUELSTATE_DEAD = 2; + public static final int DUELSTATE_WINNER = 3; + public static final int DUELSTATE_INTERRUPTED = 4; + + // ========================================================= + // Data Field + private final int _duelId; + private L2PcInstance _playerA; + private L2PcInstance _playerB; + private final boolean _partyDuel; + private final Calendar _duelEndTime; + private int _surrenderRequest = 0; + private int _countdown = 4; + private boolean _finished = false; + + private FastList _playerConditions; + + public static enum DuelResultEnum + { + Continue, + Team1Win, + Team2Win, + Team1Surrender, + Team2Surrender, + Canceled, + Timeout + } + + // ========================================================= + // Constructor + public Duel(L2PcInstance playerA, L2PcInstance playerB, int partyDuel, int duelId) + { + _duelId = duelId; + _playerA = playerA; + _playerB = playerB; + _partyDuel = partyDuel == 1 ? true : false; + + _duelEndTime = Calendar.getInstance(); + if (_partyDuel) + { + _duelEndTime.add(Calendar.SECOND, 300); + } + else + { + _duelEndTime.add(Calendar.SECOND, 120); + } + + _playerConditions = new FastList<>(); + + setFinished(false); + + if (_partyDuel) + { + // increase countdown so that start task can teleport players + _countdown++; + // inform players that they will be portet shortly + SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.IN_A_MOMENT_YOU_WILL_BE_TRANSPORTED_TO_THE_SITE_WHERE_THE_DUEL_WILL_TAKE_PLACE); + broadcastToTeam1(sm); + broadcastToTeam2(sm); + } + // Schedule duel start + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleStartDuelTask(this), 3000); + } + + // =============================================================== + // Nested Class + + public static class PlayerCondition + { + private L2PcInstance _player; + private double _hp; + private double _mp; + private double _cp; + private boolean _paDuel; + private int _x, _y, _z; + private FastList _debuffs; + + public PlayerCondition(L2PcInstance player, boolean partyDuel) + { + if (player == null) + { + return; + } + _player = player; + _hp = _player.getCurrentHp(); + _mp = _player.getCurrentMp(); + _cp = _player.getCurrentCp(); + _paDuel = partyDuel; + + if (_paDuel) + { + _x = _player.getX(); + _y = _player.getY(); + _z = _player.getZ(); + } + } + + public void restoreCondition() + { + if (_player == null) + { + return; + } + _player.setCurrentHp(_hp); + _player.setCurrentMp(_mp); + _player.setCurrentCp(_cp); + + if (_paDuel) + { + teleportBack(); + } + if (_debuffs != null) // Debuff removal + { + for (L2Effect temp : _debuffs) + { + if (temp != null) + { + temp.exit(); + } + } + } + } + + public void registerDebuff(L2Effect debuff) + { + if (_debuffs == null) + { + _debuffs = new FastList<>(); + } + + _debuffs.add(debuff); + } + + public void teleportBack() + { + if (_paDuel) + { + _player.teleToLocation(_x, _y, _z); + } + } + + public L2PcInstance getPlayer() + { + return _player; + } + } + + // =============================================================== + // Schedule task + public class ScheduleDuelTask implements Runnable + { + private final Duel _duel; + + public ScheduleDuelTask(Duel duel) + { + _duel = duel; + } + + @Override + public void run() + { + try + { + DuelResultEnum status = _duel.checkEndDuelCondition(); + + if (status == DuelResultEnum.Canceled) + { + // do not schedule duel end if it was interrupted + setFinished(true); + _duel.endDuel(status); + } + else if (status != DuelResultEnum.Continue) + { + setFinished(true); + playKneelAnimation(); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleEndDuelTask(_duel, status), 5000); + } + else + { + ThreadPoolManager.getInstance().scheduleGeneral(this, 1000); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } + + public static class ScheduleStartDuelTask implements Runnable + { + private final Duel _duel; + + public ScheduleStartDuelTask(Duel duel) + { + _duel = duel; + } + + @Override + public void run() + { + try + { + // start/continue countdown + int count = _duel.countdown(); + + if (count == 4) + { + // players need to be teleportet first + // TODO: stadia manager needs a function to return an unused stadium for duels + // currently only teleports to the same stadium + _duel.teleportPlayers(-83760, -238825, -3331); + + // give players 20 seconds to complete teleport and get ready (its ought to be 30 on offical..) + ThreadPoolManager.getInstance().scheduleGeneral(this, 20000); + } + else if (count > 0) // duel not started yet - continue countdown + { + ThreadPoolManager.getInstance().scheduleGeneral(this, 1000); + } + else + { + _duel.startDuel(); + } + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } + + public static class ScheduleEndDuelTask implements Runnable + { + private final Duel _duel; + private final DuelResultEnum _result; + + public ScheduleEndDuelTask(Duel duel, DuelResultEnum result) + { + _duel = duel; + _result = result; + } + + @Override + public void run() + { + try + { + _duel.endDuel(_result); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "", e); + } + } + } + + // ======================================================== + // Method - Private + + /** + * Stops all players from attacking. Used for duel timeout / interrupt. + */ + private void stopFighting() + { + ActionFailed af = ActionFailed.STATIC_PACKET; + if (_partyDuel) + { + for (L2PcInstance temp : _playerA.getParty().getPartyMembers()) + { + temp.abortCast(); + temp.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE); + temp.setTarget(null); + temp.sendPacket(af); + } + for (L2PcInstance temp : _playerB.getParty().getPartyMembers()) + { + temp.abortCast(); + temp.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE); + temp.setTarget(null); + temp.sendPacket(af); + } + } + else + { + _playerA.abortCast(); + _playerB.abortCast(); + _playerA.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE); + _playerA.setTarget(null); + _playerB.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE); + _playerB.setTarget(null); + _playerA.sendPacket(af); + _playerB.sendPacket(af); + } + } + + // ======================================================== + // Method - Public + + /** + * Check if a player engaged in pvp combat (only for 1on1 duels) + * @return returns true if a duelist is engaged in Pvp combat + */ + public boolean isDuelistInPvp(boolean sendMessage) + { + if (_partyDuel) + { + // Party duels take place in arenas - should be no other players there + return false; + } + else if ((_playerA.getPvpFlag() != 0) || (_playerB.getPvpFlag() != 0)) + { + if (sendMessage) + { + String engagedInPvP = "The duel was canceled because a duelist engaged in PvP combat."; + _playerA.sendMessage(engagedInPvP); + _playerB.sendMessage(engagedInPvP); + } + return true; + } + return false; + } + + /** + * Starts the duel + */ + public void startDuel() + { + // Save player Conditions + savePlayerConditions(); + + if ((_playerA == null) || (_playerB == null) || _playerA.isInDuel() || _playerB.isInDuel()) + { + // clean up + _playerConditions.clear(); + _playerConditions = null; + DuelManager.getInstance().removeDuel(this); + return; + } + + if (_partyDuel) + { + // set isInDuel() state + // cancel all active trades, just in case? xD + for (L2PcInstance temp : _playerA.getParty().getPartyMembers()) + { + temp.cancelActiveTrade(); + temp.setIsInDuel(_duelId); + temp.setTeam(1); + temp.broadcastUserInfo(); + broadcastToTeam2(new ExDuelUpdateUserInfo(temp)); + } + for (L2PcInstance temp : _playerB.getParty().getPartyMembers()) + { + temp.cancelActiveTrade(); + temp.setIsInDuel(_duelId); + temp.setTeam(2); + temp.broadcastUserInfo(); + broadcastToTeam1(new ExDuelUpdateUserInfo(temp)); + } + + // Send duel Start packets + ExDuelReady ready = new ExDuelReady(1); + ExDuelStart start = new ExDuelStart(1); + + broadcastToTeam1(ready); + broadcastToTeam2(ready); + broadcastToTeam1(start); + broadcastToTeam2(start); + } + else + { + // set isInDuel() state + _playerA.setIsInDuel(_duelId); + _playerA.setTeam(1); + _playerB.setIsInDuel(_duelId); + _playerB.setTeam(2); + + // Send duel Start packets + ExDuelReady ready = new ExDuelReady(0); + ExDuelStart start = new ExDuelStart(0); + + broadcastToTeam1(ready); + broadcastToTeam2(ready); + broadcastToTeam1(start); + broadcastToTeam2(start); + + broadcastToTeam1(new ExDuelUpdateUserInfo(_playerB)); + broadcastToTeam2(new ExDuelUpdateUserInfo(_playerA)); + + _playerA.broadcastUserInfo(); + _playerB.broadcastUserInfo(); + } + + // play sound + PlaySound ps = new PlaySound(1, "B04_S01", 0, 0, 0, 0, 0); + broadcastToTeam1(ps); + broadcastToTeam2(ps); + + // start duelling task + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleDuelTask(this), 1000); + } + + /** + * Save the current player condition: hp, mp, cp, location + */ + public void savePlayerConditions() + { + if (_partyDuel) + { + for (L2PcInstance temp : _playerA.getParty().getPartyMembers()) + { + _playerConditions.add(new PlayerCondition(temp, _partyDuel)); + } + for (L2PcInstance temp : _playerB.getParty().getPartyMembers()) + { + _playerConditions.add(new PlayerCondition(temp, _partyDuel)); + } + } + else + { + _playerConditions.add(new PlayerCondition(_playerA, _partyDuel)); + _playerConditions.add(new PlayerCondition(_playerB, _partyDuel)); + } + } + + /** + * Restore player conditions + * @param was the duel canceled? + */ + public void restorePlayerConditions(boolean abnormalDuelEnd) + { + // update isInDuel() state for all players + if (_partyDuel) + { + for (L2PcInstance temp : _playerA.getParty().getPartyMembers()) + { + temp.setIsInDuel(0); + temp.setTeam(0); + temp.broadcastUserInfo(); + } + for (L2PcInstance temp : _playerB.getParty().getPartyMembers()) + { + temp.setIsInDuel(0); + temp.setTeam(0); + temp.broadcastUserInfo(); + } + } + else + { + _playerA.setIsInDuel(0); + _playerA.setTeam(0); + _playerA.broadcastUserInfo(); + _playerB.setIsInDuel(0); + _playerB.setTeam(0); + _playerB.broadcastUserInfo(); + } + + // if it is an abnormal DuelEnd do not restore hp, mp, cp + if (abnormalDuelEnd) + { + return; + } + + // restore player conditions + for (FastList.Node e = _playerConditions.head(), end = _playerConditions.tail(); (e = e.getNext()) != end;) + { + e.getValue().restoreCondition(); + } + } + + /** + * Get the duel id + * @return id + */ + public int getId() + { + return _duelId; + } + + /** + * Returns the remaining time + * @return remaining time + */ + public int getRemainingTime() + { + return (int) (_duelEndTime.getTimeInMillis() - Calendar.getInstance().getTimeInMillis()); + } + + /** + * Get the player that requestet the duel + * @return duel requester + */ + public L2PcInstance getPlayerA() + { + return _playerA; + } + + /** + * Get the player that was challenged + * @return challenged player + */ + public L2PcInstance getPlayerB() + { + return _playerB; + } + + /** + * Returns whether this is a party duel or not + * @return is party duel + */ + public boolean isPartyDuel() + { + return _partyDuel; + } + + public void setFinished(boolean mode) + { + _finished = mode; + } + + public boolean getFinished() + { + return _finished; + } + + /** + * teleport all players to the given coordinates + * @param x + * @param y + * @param z + */ + public void teleportPlayers(int x, int y, int z) + { + // TODO: adjust the values if needed... or implement something better (especially using more then 1 arena) + if (!_partyDuel) + { + return; + } + int offset = 0; + + for (L2PcInstance temp : _playerA.getParty().getPartyMembers()) + { + temp.teleToLocation((x + offset) - 180, y - 150, z); + offset += 40; + } + offset = 0; + for (L2PcInstance temp : _playerB.getParty().getPartyMembers()) + { + temp.teleToLocation((x + offset) - 180, y + 150, z); + offset += 40; + } + } + + /** + * Broadcast a packet to the challanger team + */ + public void broadcastToTeam1(L2GameServerPacket packet) + { + if (_playerA == null) + { + return; + } + + if (_partyDuel && (_playerA.getParty() != null)) + { + for (L2PcInstance temp : _playerA.getParty().getPartyMembers()) + { + temp.sendPacket(packet); + } + } + else + { + _playerA.sendPacket(packet); + } + } + + /** + * Broadcast a packet to the challenged team + */ + public void broadcastToTeam2(L2GameServerPacket packet) + { + if (_playerB == null) + { + return; + } + + if (_partyDuel && (_playerB.getParty() != null)) + { + for (L2PcInstance temp : _playerB.getParty().getPartyMembers()) + { + temp.sendPacket(packet); + } + } + else + { + _playerB.sendPacket(packet); + } + } + + /** + * Get the duel winner + * @return winner + */ + public L2PcInstance getWinner() + { + if (!getFinished() || (_playerA == null) || (_playerB == null)) + { + return null; + } + if (_playerA.getDuelState() == DUELSTATE_WINNER) + { + return _playerA; + } + if (_playerB.getDuelState() == DUELSTATE_WINNER) + { + return _playerB; + } + return null; + } + + /** + * Get the duel looser + * @return looser + */ + public L2PcInstance getLooser() + { + if (!getFinished() || (_playerA == null) || (_playerB == null)) + { + return null; + } + if (_playerA.getDuelState() == DUELSTATE_WINNER) + { + return _playerB; + } + else if (_playerB.getDuelState() == DUELSTATE_WINNER) + { + return _playerA; + } + return null; + } + + /** + * Playback the bow animation for all loosers + */ + public void playKneelAnimation() + { + L2PcInstance looser = getLooser(); + + if (looser == null) + { + return; + } + + if (_partyDuel && (looser.getParty() != null)) + { + for (L2PcInstance temp : looser.getParty().getPartyMembers()) + { + temp.broadcastPacket(new SocialAction(temp, 7)); + } + } + else + { + looser.broadcastPacket(new SocialAction(looser, 7)); + } + } + + /** + * Do the countdown and send message to players if necessary + * @return current count + */ + public int countdown() + { + _countdown--; + + if (_countdown > 3) + { + return _countdown; + } + + // Broadcast countdown to duelists + SystemMessage sm = null; + if (_countdown > 0) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.THE_DUEL_WILL_BEGIN_IN_S1_SECONDS); + sm.addNumber(_countdown); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.LET_THE_DUEL_BEGIN); + } + + broadcastToTeam1(sm); + broadcastToTeam2(sm); + + return _countdown; + } + + /** + * The duel has reached a state in which it can no longer continue + * @param duel result + */ + public void endDuel(DuelResultEnum result) + { + if ((_playerA == null) || (_playerB == null)) + { + // clean up + _playerConditions.clear(); + _playerConditions = null; + DuelManager.getInstance().removeDuel(this); + return; + } + + // inform players of the result + SystemMessage sm = null; + switch (result) + { + case Team1Win: + case Team2Surrender: + restorePlayerConditions(false); + // send SystemMessage + if (_partyDuel) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.C1_PARTY_HAS_WON_THE_DUEL); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_WON_THE_DUEL); + } + sm.addString(_playerA.getName()); + + broadcastToTeam1(sm); + broadcastToTeam2(sm); + break; + case Team1Surrender: + case Team2Win: + restorePlayerConditions(false); + // send SystemMessage + if (_partyDuel) + { + sm = SystemMessage.getSystemMessage(SystemMessageId.C1_PARTY_HAS_WON_THE_DUEL); + } + else + { + sm = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_WON_THE_DUEL); + } + sm.addString(_playerB.getName()); + + broadcastToTeam1(sm); + broadcastToTeam2(sm); + break; + case Canceled: + stopFighting(); + // dont restore hp, mp, cp + restorePlayerConditions(true); + // TODO: is there no other message for a canceled duel? + // send SystemMessage + sm = SystemMessage.getSystemMessage(SystemMessageId.THE_DUEL_HAS_ENDED_IN_A_TIE); + + broadcastToTeam1(sm); + broadcastToTeam2(sm); + break; + case Timeout: + stopFighting(); + // hp,mp,cp seem to be restored in a timeout too... + restorePlayerConditions(false); + // send SystemMessage + sm = SystemMessage.getSystemMessage(SystemMessageId.THE_DUEL_HAS_ENDED_IN_A_TIE); + + broadcastToTeam1(sm); + broadcastToTeam2(sm); + break; + } + + // Send end duel packet + ExDuelEnd duelEnd = null; + if (_partyDuel) + { + duelEnd = new ExDuelEnd(1); + } + else + { + duelEnd = new ExDuelEnd(0); + } + + broadcastToTeam1(duelEnd); + broadcastToTeam2(duelEnd); + + // clean up + _playerConditions.clear(); + _playerConditions = null; + DuelManager.getInstance().removeDuel(this); + } + + /** + * Did a situation occur in which the duel has to be ended? + * @return DuelResultEnum duel status + */ + public DuelResultEnum checkEndDuelCondition() + { + // one of the players might leave during duel + if ((_playerA == null) || (_playerB == null)) + { + return DuelResultEnum.Canceled; + } + + // got a duel surrender request? + if (_surrenderRequest != 0) + { + if (_surrenderRequest == 1) + { + return DuelResultEnum.Team1Surrender; + } + return DuelResultEnum.Team2Surrender; + } + // duel timed out + else if (getRemainingTime() <= 0) + { + return DuelResultEnum.Timeout; + } + // Has a player been declared winner yet? + else if (_playerA.getDuelState() == DUELSTATE_WINNER) + { + // If there is a Winner already there should be no more fighting going on + stopFighting(); + return DuelResultEnum.Team1Win; + } + else if (_playerB.getDuelState() == DUELSTATE_WINNER) + { + // If there is a Winner already there should be no more fighting going on + stopFighting(); + return DuelResultEnum.Team2Win; + } + + // More end duel conditions for 1on1 duels + else if (!_partyDuel) + { + // Duel was interrupted e.g.: player was attacked by mobs / other players + if ((_playerA.getDuelState() == DUELSTATE_INTERRUPTED) || (_playerB.getDuelState() == DUELSTATE_INTERRUPTED)) + { + return DuelResultEnum.Canceled; + } + + // Are the players too far apart? + if (!_playerA.isInsideRadius(_playerB, 1600, false, false)) + { + return DuelResultEnum.Canceled; + } + + // Did one of the players engage in PvP combat? + if (isDuelistInPvp(true)) + { + return DuelResultEnum.Canceled; + } + + // is one of the players in a Siege, Peace or PvP zone? + if (_playerA.isInsideZone(L2Character.ZONE_PEACE) || _playerB.isInsideZone(L2Character.ZONE_PEACE) || _playerA.isInsideZone(L2Character.ZONE_SIEGE) || _playerB.isInsideZone(L2Character.ZONE_SIEGE) || _playerA.isInsideZone(L2Character.ZONE_PVP) || _playerB.isInsideZone(L2Character.ZONE_PVP)) + { + return DuelResultEnum.Canceled; + } + } + + return DuelResultEnum.Continue; + } + + /** + * Register a surrender request + * @param surrendering player + */ + public void doSurrender(L2PcInstance player) + { + // already recived a surrender request + if (_surrenderRequest != 0) + { + return; + } + + // stop the fight + stopFighting(); + + // TODO: Can every party member cancel a party duel? or only the party leaders? + if (_partyDuel) + { + if (_playerA.getParty().getPartyMembers().contains(player)) + { + _surrenderRequest = 1; + for (L2PcInstance temp : _playerA.getParty().getPartyMembers()) + { + temp.setDuelState(DUELSTATE_DEAD); + } + for (L2PcInstance temp : _playerB.getParty().getPartyMembers()) + { + temp.setDuelState(DUELSTATE_WINNER); + } + } + else if (_playerB.getParty().getPartyMembers().contains(player)) + { + _surrenderRequest = 2; + for (L2PcInstance temp : _playerB.getParty().getPartyMembers()) + { + temp.setDuelState(DUELSTATE_DEAD); + } + for (L2PcInstance temp : _playerA.getParty().getPartyMembers()) + { + temp.setDuelState(DUELSTATE_WINNER); + } + + } + } + else + { + if (player == _playerA) + { + _surrenderRequest = 1; + _playerA.setDuelState(DUELSTATE_DEAD); + _playerB.setDuelState(DUELSTATE_WINNER); + } + else if (player == _playerB) + { + _surrenderRequest = 2; + _playerB.setDuelState(DUELSTATE_DEAD); + _playerA.setDuelState(DUELSTATE_WINNER); + } + } + } + + /** + * This function is called whenever a player was defeated in a duel + * @param dieing player + */ + public void onPlayerDefeat(L2PcInstance player) + { + // Set player as defeated + player.setDuelState(DUELSTATE_DEAD); + + if (_partyDuel) + { + boolean teamdefeated = true; + for (L2PcInstance temp : player.getParty().getPartyMembers()) + { + if (temp.getDuelState() == DUELSTATE_DUELLING) + { + teamdefeated = false; + break; + } + } + + if (teamdefeated) + { + L2PcInstance winner = _playerA; + if (_playerA.getParty().getPartyMembers().contains(player)) + { + winner = _playerB; + } + + for (L2PcInstance temp : winner.getParty().getPartyMembers()) + { + temp.setDuelState(DUELSTATE_WINNER); + } + } + } + else + { + if ((player != _playerA) && (player != _playerB)) + { + _log.warning("Error in onPlayerDefeat(): player is not part of this 1vs1 duel"); + } + + if (_playerA == player) + { + _playerB.setDuelState(DUELSTATE_WINNER); + } + else + { + _playerA.setDuelState(DUELSTATE_WINNER); + } + } + } + + /** + * This function is called whenever a player leaves a party + * @param leaving player + */ + public void onRemoveFromParty(L2PcInstance player) + { + // if it isnt a party duel ignore this + if (!_partyDuel) + { + return; + } + + // this player is leaving his party during party duel + // if hes either playerA or playerB cancel the duel and port the players back + if ((player == _playerA) || (player == _playerB)) + { + for (FastList.Node e = _playerConditions.head(), end = _playerConditions.tail(); (e = e.getNext()) != end;) + { + e.getValue().teleportBack(); + e.getValue().getPlayer().setIsInDuel(0); + } + + _playerA = null; + _playerB = null; + } + else + // teleport the player back & delete his PlayerCondition record + { + for (FastList.Node e = _playerConditions.head(), end = _playerConditions.tail(); (e = e.getNext()) != end;) + { + if (e.getValue().getPlayer() == player) + { + e.getValue().teleportBack(); + _playerConditions.remove(e.getValue()); + break; + } + } + player.setIsInDuel(0); + } + } + + public void onBuff(L2PcInstance player, L2Effect debuff) + { + for (FastList.Node e = _playerConditions.head(), end = _playerConditions.tail(); (e = e.getNext()) != end;) + { + if (e.getValue().getPlayer() == player) + { + e.getValue().registerDebuff(debuff); + return; + } + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/Fort.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Fort.java new file mode 100644 index 00000000..f0d8a1f4 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Fort.java @@ -0,0 +1,1584 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.entity; + +import gnu.trove.TIntIntHashMap; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Calendar; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.FortUpdater; +import com.l2jserver.gameserver.FortUpdater.UpdaterType; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.datatables.DoorTable; +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.datatables.SkillTable; +import com.l2jserver.gameserver.datatables.SkillTreesData; +import com.l2jserver.gameserver.datatables.SpawnTable; +import com.l2jserver.gameserver.datatables.StaticObjects; +import com.l2jserver.gameserver.instancemanager.FortManager; +import com.l2jserver.gameserver.instancemanager.ZoneManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2Skill; +import com.l2jserver.gameserver.model.L2SkillLearn; +import com.l2jserver.gameserver.model.L2Spawn; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.actor.instance.L2StaticObjectInstance; +import com.l2jserver.gameserver.model.zone.type.L2FortZone; +import com.l2jserver.gameserver.model.zone.type.L2SiegeZone; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.PlaySound; +import com.l2jserver.gameserver.network.serverpackets.PledgeShowInfoUpdate; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class Fort +{ + protected static final Logger _log = Logger.getLogger(Fort.class.getName()); + + // ========================================================= + // Data Field + private int _fortId = 0; + private final List _doors = new FastList<>(); + private L2StaticObjectInstance _flagPole = null; + private final List _doorDefault = new FastList<>(); + private final List _flagPoleStats = new FastList<>(); + private String _name = ""; + private FortSiege _siege = null; + private Calendar _siegeDate; + private Calendar _lastOwnedTime; + private L2FortZone _fortZone; + private L2SiegeZone _zone; + private L2Clan _fortOwner = null; + private int _fortType = 0; + private int _state = 0; + private int _castleId = 0; + private int _blood = 0; + private int _supplyLvL = 0; + private final FastMap _function; + private final FastList _residentialSkills = new FastList<>(); + private final ScheduledFuture[] _FortUpdater = new ScheduledFuture[2]; + + // Spawn Data + private boolean _isSuspiciousMerchantSpawned = false; + private final FastList _siegeNpcs = new FastList<>(); + private final FastList _npcCommanders = new FastList<>(); + private final FastList _specialEnvoys = new FastList<>(); + + private final TIntIntHashMap _envoyCastles = new TIntIntHashMap(2); + + /** Fortress Functions */ + public static final int FUNC_TELEPORT = 1; + public static final int FUNC_RESTORE_HP = 2; + public static final int FUNC_RESTORE_MP = 3; + public static final int FUNC_RESTORE_EXP = 4; + public static final int FUNC_SUPPORT = 5; + + public class FortFunction + { + private final int _type; + private int _lvl; + protected int _fee; + protected int _tempFee; + private final long _rate; + private long _endDate; + protected boolean _inDebt; + public boolean _cwh; + + public FortFunction(int type, int lvl, int lease, int tempLease, long rate, long time, boolean cwh) + { + _type = type; + _lvl = lvl; + _fee = lease; + _tempFee = tempLease; + _rate = rate; + _endDate = time; + initializeTask(cwh); + } + + public int getType() + { + return _type; + } + + public int getLvl() + { + return _lvl; + } + + public int getLease() + { + return _fee; + } + + public long getRate() + { + return _rate; + } + + public long getEndTime() + { + return _endDate; + } + + public void setLvl(int lvl) + { + _lvl = lvl; + } + + public void setLease(int lease) + { + _fee = lease; + } + + public void setEndTime(long time) + { + _endDate = time; + } + + private void initializeTask(boolean cwh) + { + if (getOwnerClan() == null) + { + return; + } + long currentTime = System.currentTimeMillis(); + if (_endDate > currentTime) + { + ThreadPoolManager.getInstance().scheduleGeneral(new FunctionTask(cwh), _endDate - currentTime); + } + else + { + ThreadPoolManager.getInstance().scheduleGeneral(new FunctionTask(cwh), 0); + } + } + + private class FunctionTask implements Runnable + { + public FunctionTask(boolean cwh) + { + _cwh = cwh; + } + + @Override + public void run() + { + try + { + if (getOwnerClan() == null) + { + return; + } + if ((getOwnerClan().getWarehouse().getAdena() >= _fee) || !_cwh) + { + int fee = _fee; + if (getEndTime() == -1) + { + fee = _tempFee; + } + + setEndTime(System.currentTimeMillis() + getRate()); + dbSave(); + if (_cwh) + { + getOwnerClan().getWarehouse().destroyItemByItemId("CS_function_fee", 57, fee, null, null); + if (Config.DEBUG) + { + _log.warning("Deducted " + fee + " adena from " + getName() + " owner's cwh for function id : " + getType()); + } + } + ThreadPoolManager.getInstance().scheduleGeneral(new FunctionTask(true), getRate()); + } + else + { + removeFunction(getType()); + } + } + catch (Throwable t) + { + } + } + } + + public void dbSave() + { + Connection con = null; + try + { + PreparedStatement statement; + + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("REPLACE INTO fort_functions (fort_id, type, lvl, lease, rate, endTime) VALUES (?,?,?,?,?,?)"); + statement.setInt(1, getFortId()); + statement.setInt(2, getType()); + statement.setInt(3, getLvl()); + statement.setInt(4, getLease()); + statement.setLong(5, getRate()); + statement.setLong(6, getEndTime()); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Fort.updateFunctions(int type, int lvl, int lease, long rate, long time, boolean addNew): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + } + + // ========================================================= + // Constructor + public Fort(int fortId) + { + _fortId = fortId; + load(); + loadDoor(); + loadFlagPoles(); + _function = new FastMap<>(); + FastList residentialSkills = SkillTreesData.getInstance().getAvailableResidentialSkills(fortId); + for (L2SkillLearn s : residentialSkills) + { + L2Skill sk = SkillTable.getInstance().getInfo(s.getSkillId(), s.getSkillLevel()); + if (sk != null) + { + _residentialSkills.add(sk); + } + else + { + _log.warning("Fort Id: " + fortId + " has a null residential skill Id: " + s.getSkillId() + " level: " + s.getSkillLevel() + "!"); + } + } + if (getOwnerClan() != null) + { + setVisibleFlag(true); + loadFunctions(); + } + initNpcs(); // load and spawn npcs (Always spawned) + initSiegeNpcs(); // load suspicious merchants (Despawned 10mins before siege) + // spawnSuspiciousMerchant();// spawn suspicious merchants + initNpcCommanders(); // npc Commanders (not monsters) (Spawned during siege) + spawnNpcCommanders(); // spawn npc Commanders + initSpecialEnvoys(); // envoys from castles (Spawned after fort taken) + if ((getOwnerClan() != null) && (getFortState() == 0)) + { + spawnSpecialEnvoys(); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleSpecialEnvoysDeSpawn(this), 1 * 60 * 60 * 1000); // Prepare 1hr task for special envoys despawn + } + } + + /** Return function with id */ + public FortFunction getFunction(int type) + { + if (_function.get(type) != null) + { + return _function.get(type); + } + return null; + } + + public static class ScheduleSpecialEnvoysDeSpawn implements Runnable + { + private final Fort _fortInst; + + public ScheduleSpecialEnvoysDeSpawn(Fort pFort) + { + _fortInst = pFort; + } + + @Override + public void run() + { + try + { + // if state not decided, change state to indenpendent + if (_fortInst.getFortState() == 0) + { + _fortInst.setFortState(1, 0); + } + _fortInst.despawnSpecialEnvoys(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: ScheduleSpecialEnvoysSpawn() for Fort " + _fortInst.getName() + ": " + e.getMessage(), e); + } + } + } + + // ========================================================= + // Method - Public + + public void endOfSiege(L2Clan clan) + { + ThreadPoolManager.getInstance().scheduleGeneral(new endFortressSiege(this, clan), 1000); + } + + public void engrave(L2Clan clan) + { + setOwner(clan, true); + } + + /** + * Move non clan members off fort area and to nearest town.
    + *
    + */ + public void banishForeigners() + { + getFortZone().banishForeigners(getOwnerClan()); + } + + /** + * Return true if object is inside the zone + */ + public boolean checkIfInZone(int x, int y, int z) + { + return getZone().isInsideZone(x, y, z); + } + + public L2SiegeZone getZone() + { + if (_zone == null) + { + for (L2SiegeZone zone : ZoneManager.getInstance().getAllZones(L2SiegeZone.class)) + { + if (zone.getSiegeObjectId() == getFortId()) + { + _zone = zone; + break; + } + } + } + return _zone; + } + + public L2FortZone getFortZone() + { + if (_fortZone == null) + { + for (L2FortZone zone : ZoneManager.getInstance().getAllZones(L2FortZone.class)) + { + if (zone.getFortId() == getFortId()) + { + _fortZone = zone; + break; + } + } + } + return _fortZone; + } + + /** + * Get the objects distance to this fort + * @param object + * @return + */ + public double getDistance(L2Object obj) + { + return getZone().getDistanceToZone(obj); + } + + public void closeDoor(L2PcInstance activeChar, int doorId) + { + openCloseDoor(activeChar, doorId, false); + } + + public void openDoor(L2PcInstance activeChar, int doorId) + { + openCloseDoor(activeChar, doorId, true); + } + + public void openCloseDoor(L2PcInstance activeChar, int doorId, boolean open) + { + if (activeChar.getClan() != getOwnerClan()) + { + return; + } + + L2DoorInstance door = getDoor(doorId); + if (door != null) + { + if (open) + { + door.openMe(); + } + else + { + door.closeMe(); + } + } + } + + // This method is used to begin removing all fort upgrades + public void removeUpgrade() + { + removeDoorUpgrade(); + } + + /** + * This method will set owner for Fort + * @param clan + * @param updateClanPoints + */ + public boolean setOwner(L2Clan clan, boolean updateClansReputation) + { + L2Clan oldowner = getOwnerClan(); + + // Remove old owner + if ((oldowner != null) && (clan != null) && (clan != oldowner)) + { + // Remove points from old owner + updateClansReputation(oldowner, true); + try + { + L2PcInstance oldleader = oldowner.getLeader().getPlayerInstance(); + if (oldleader != null) + { + if (oldleader.getMountType() == 2) + { + oldleader.dismount(); + } + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception in setOwner: " + e.getMessage(), e); + } + removeOwner(true); + } + setFortState(0, 0); // initialize fort state + + // if clan already have castle, don't store him in fortress + if (clan.getHasCastle() > 0) + { + getSiege().announceToPlayer(SystemMessage.getSystemMessage(SystemMessageId.NPCS_RECAPTURED_FORTRESS)); + return false; + } + + // Give points to new owner + if (updateClansReputation) + { + updateClansReputation(clan, false); + } + + spawnSpecialEnvoys(); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleSpecialEnvoysDeSpawn(this), 1 * 60 * 60 * 1000); // Prepare 1hr task for special envoys despawn + // if clan have already fortress, remove it + if (clan.getHasFort() > 0) + { + FortManager.getInstance().getFortByOwner(clan).removeOwner(true); + } + + setBloodOathReward(0); + setSupplyLvL(0); + setOwnerClan(clan); + updateOwnerInDB(); // Update in database + saveFortVariables(); + + if (getSiege().getIsInProgress()) + { + getSiege().endSiege(); + } + + for (L2PcInstance member : clan.getOnlineMembers(0)) + { + giveResidentialSkills(member); + member.sendSkillList(); + } + return true; + } + + public void removeOwner(boolean updateDB) + { + L2Clan clan = getOwnerClan(); + if (clan != null) + { + for (L2PcInstance member : clan.getOnlineMembers(0)) + { + removeResidentialSkills(member); + member.sendSkillList(); + } + clan.setHasFort(0); + clan.broadcastToOnlineMembers(new PledgeShowInfoUpdate(clan)); + setOwnerClan(null); + setBloodOathReward(0); + setSupplyLvL(0); + saveFortVariables(); + removeAllFunctions(); + if (updateDB) + { + updateOwnerInDB(); + } + } + } + + public void setBloodOathReward(int val) + { + _blood = val; + } + + public int getBloodOathReward() + { + return _blood; + } + + public void raiseSupplyLvL() + { + _supplyLvL++; + if (_supplyLvL > Config.FS_MAX_SUPPLY_LEVEL) + { + _supplyLvL = Config.FS_MAX_SUPPLY_LEVEL; + } + } + + public void setSupplyLvL(int val) + { + if (val <= Config.FS_MAX_SUPPLY_LEVEL) + { + _supplyLvL = val; + } + } + + public int getSupplyLvL() + { + return _supplyLvL; + } + + public void saveFortVariables() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + statement = con.prepareStatement("UPDATE fort SET blood=?, supplyLvL=? WHERE id = ?"); + statement.setInt(1, _blood); + statement.setInt(2, _supplyLvL); + statement.setInt(3, getFortId()); + statement.execute(); + statement.close(); + + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: saveFortVariables(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Show or hide flag inside flagpole
    + *
    + */ + public void setVisibleFlag(boolean val) + { + L2StaticObjectInstance flagPole = getFlagPole(); + if (flagPole != null) + { + flagPole.setMeshIndex(val ? 1 : 0); + } + } + + /** + * Respawn all doors on fort grounds
    + *
    + */ + public void resetDoors() + { + for (int i = 0; i < getDoors().size(); i++) + { + L2DoorInstance door = getDoors().get(i); + if (door.getOpen()) + { + door.closeMe(); + } + if (door.getCurrentHp() <= 0) + { + door.doRevive(); + } + if (door.getCurrentHp() < door.getMaxHp()) + { + door.setCurrentHp(door.getMaxHp()); + } + } + loadDoorUpgrade(); // Check for any upgrade the doors may have + } + + // This method upgrade door + public void upgradeDoor(int doorId, int hp, int pDef, int mDef) + { + L2DoorInstance door = getDoor(doorId); + if ((door != null) && (door.getDoorId() == doorId)) + { + door.setCurrentHp(door.getMaxHp() + hp); + + saveDoorUpgrade(doorId, hp, pDef, mDef); + return; + } + } + + // ========================================================= + // Method - Private + // This method loads fort + private void load() + { + Connection con = null; + try + { + PreparedStatement statement; + ResultSet rs; + + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement("SELECT * FROM fort WHERE id = ?"); + statement.setInt(1, getFortId()); + rs = statement.executeQuery(); + int ownerId = 0; + + while (rs.next()) + { + _name = rs.getString("name"); + + _siegeDate = Calendar.getInstance(); + _lastOwnedTime = Calendar.getInstance(); + _siegeDate.setTimeInMillis(rs.getLong("siegeDate")); + _lastOwnedTime.setTimeInMillis(rs.getLong("lastOwnedTime")); + ownerId = rs.getInt("owner"); + _fortType = rs.getInt("fortType"); + _state = rs.getInt("state"); + _castleId = rs.getInt("castleId"); + _blood = rs.getInt("blood"); + _supplyLvL = rs.getInt("supplyLvL"); + } + + rs.close(); + statement.close(); + + if (ownerId > 0) + { + L2Clan clan = ClanTable.getInstance().getClan(ownerId); // Try to find clan instance + clan.setHasFort(getFortId()); + setOwnerClan(clan); + int runCount = getOwnedTime() / (Config.FS_UPDATE_FRQ * 60); + long initial = System.currentTimeMillis() - _lastOwnedTime.getTimeInMillis(); + while (initial > (Config.FS_UPDATE_FRQ * 60000l)) + { + initial -= (Config.FS_UPDATE_FRQ * 60000l); + } + initial = (Config.FS_UPDATE_FRQ * 60000l) - initial; + if ((Config.FS_MAX_OWN_TIME <= 0) || (getOwnedTime() < (Config.FS_MAX_OWN_TIME * 3600))) + { + _FortUpdater[0] = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FortUpdater(this, clan, runCount, UpdaterType.PERIODIC_UPDATE), initial, Config.FS_UPDATE_FRQ * 60000l); // Schedule owner tasks to start running + if (Config.FS_MAX_OWN_TIME > 0) + { + _FortUpdater[1] = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FortUpdater(this, clan, runCount, UpdaterType.MAX_OWN_TIME), 3600000, 3600000); // Schedule owner tasks to remove owener + } + } + else + { + _FortUpdater[1] = ThreadPoolManager.getInstance().scheduleGeneral(new FortUpdater(this, clan, 0, UpdaterType.MAX_OWN_TIME), 60000); // Schedule owner tasks to remove owner + } + } + else + { + setOwnerClan(null); + } + + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: loadFortData(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Load All Functions */ + private void loadFunctions() + { + Connection con = null; + try + { + PreparedStatement statement; + ResultSet rs; + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("SELECT * FROM fort_functions WHERE fort_id = ?"); + statement.setInt(1, getFortId()); + rs = statement.executeQuery(); + while (rs.next()) + { + _function.put(rs.getInt("type"), new FortFunction(rs.getInt("type"), rs.getInt("lvl"), rs.getInt("lease"), 0, rs.getLong("rate"), rs.getLong("endTime"), true)); + } + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Fort.loadFunctions(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Remove function In List and in DB */ + public void removeFunction(int functionType) + { + _function.remove(functionType); + Connection con = null; + try + { + PreparedStatement statement; + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("DELETE FROM fort_functions WHERE fort_id=? AND type=?"); + statement.setInt(1, getFortId()); + statement.setInt(2, functionType); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Exception: Fort.removeFunctions(int functionType): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Remove all fort functions. + */ + private void removeAllFunctions() + { + for (int id : _function.keySet()) + { + removeFunction(id); + } + } + + public boolean updateFunctions(L2PcInstance player, int type, int lvl, int lease, long rate, boolean addNew) + { + if (player == null) + { + return false; + } + if (Config.DEBUG) + { + _log.warning("Called Fort.updateFunctions(int type, int lvl, int lease, long rate, boolean addNew) Owner : " + getOwnerClan()); + } + if (lease > 0) + { + if (!player.destroyItemByItemId("Consume", 57, lease, null, true)) + { + return false; + } + } + if (addNew) + { + _function.put(type, new FortFunction(type, lvl, lease, 0, rate, 0, false)); + } + else + { + if ((lvl == 0) && (lease == 0)) + { + removeFunction(type); + } + else + { + int diffLease = lease - _function.get(type).getLease(); + if (Config.DEBUG) + { + _log.warning("Called Fort.updateFunctions diffLease : " + diffLease); + } + if (diffLease > 0) + { + _function.remove(type); + _function.put(type, new FortFunction(type, lvl, lease, 0, rate, -1, false)); + } + else + { + _function.get(type).setLease(lease); + _function.get(type).setLvl(lvl); + _function.get(type).dbSave(); + } + } + } + return true; + } + + public void activateInstance() + { + for (final L2DoorInstance door : _doors) + { + door.spawnMe(door.getX(), door.getY(), door.getZ()); + } + } + + // This method loads fort door data from database + private void loadDoor() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT * FROM fort_staticobjects WHERE fortId = ? AND objectType = ?"); + statement.setInt(1, getFortId()); + statement.setInt(2, 0); + ResultSet rs = statement.executeQuery(); + + while (rs.next()) + { + // Create list of the door default for use when respawning dead doors + _doorDefault.add(rs.getString("name") + ";" + rs.getInt("id") + ";" + rs.getInt("x") + ";" + rs.getInt("y") + ";" + rs.getInt("z") + ";" + rs.getInt("range_xmin") + ";" + rs.getInt("range_ymin") + ";" + rs.getInt("range_zmin") + ";" + rs.getInt("range_xmax") + ";" + rs.getInt("range_ymax") + ";" + rs.getInt("range_zmax") + ";" + rs.getInt("hp") + ";" + rs.getInt("pDef") + ";" + rs.getInt("mDef") + ";0;" + rs.getBoolean("openType") + ";" + rs.getBoolean("commanderDoor")); + L2DoorInstance door; + _doors.add(door = DoorTable.parseList(_doorDefault.get(_doorDefault.size() - 1), true)); + DoorTable.getInstance().putDoor(door); + } + + rs.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: loadFortDoor(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + // This method loads fort flagpoles data from database + private void loadFlagPoles() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT * FROM fort_staticobjects WHERE fortId = ? AND objectType = ?"); + statement.setInt(1, getFortId()); + statement.setInt(2, 1); + ResultSet rs = statement.executeQuery(); + while (rs.next()) + { + // Create list of the door default for use when respawning dead doors + _flagPoleStats.add(rs.getString("name") + ";" + rs.getInt("id") + ";" + rs.getInt("x") + ";" + rs.getInt("y") + ";" + rs.getInt("z") + ";3;none;0;0"); + _flagPole = StaticObjects.parse(_flagPoleStats.get(_flagPoleStats.size() - 1)); + StaticObjects.getInstance().putObject(_flagPole); + } + rs.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: loadFlagPoles(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + // This method loads fort door upgrade data from database + private void loadDoorUpgrade() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT * FROM fort_doorupgrade WHERE doorId IN (SELECT Id FROM fort_staticobjects WHERE fortId = ? AND objectType = ?)"); + statement.setInt(1, getFortId()); + statement.setInt(2, 0); + ResultSet rs = statement.executeQuery(); + + while (rs.next()) + { + upgradeDoor(rs.getInt("id"), rs.getInt("hp"), rs.getInt("pDef"), rs.getInt("mDef")); + } + rs.close(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: loadFortDoorUpgrade(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void removeDoorUpgrade() + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("DELETE FROM fort_doorupgrade WHERE doorId IN (SELECT id FROM fort_staticobjects WHERE fortId=? AND objectType = ?)"); + statement.setInt(1, getFortId()); + statement.setInt(2, 0); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: removeDoorUpgrade(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void saveDoorUpgrade(int doorId, int hp, int pDef, int mDef) + { + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("INSERT INTO fort_doorupgrade (doorId, hp, pDef, mDef) VALUES (?,?,?,?)"); + statement.setInt(1, doorId); + statement.setInt(2, hp); + statement.setInt(3, pDef); + statement.setInt(4, mDef); + statement.execute(); + statement.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: saveDoorUpgrade(int doorId, int hp, int pDef, int mDef): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void updateOwnerInDB() + { + L2Clan clan = getOwnerClan(); + int clanId = 0; + if (clan != null) + { + clanId = clan.getClanId(); + _lastOwnedTime.setTimeInMillis(System.currentTimeMillis()); + } + else + { + _lastOwnedTime.setTimeInMillis(0); + } + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + statement = con.prepareStatement("UPDATE fort SET owner=?,lastOwnedTime=?,state=?,castleId=?,blood=? WHERE id = ?"); + statement.setInt(1, clanId); + statement.setLong(2, _lastOwnedTime.getTimeInMillis()); + statement.setInt(3, 0); + statement.setInt(4, 0); + statement.setInt(5, getBloodOathReward()); + statement.setInt(6, getFortId()); + statement.execute(); + statement.close(); + + // ============================================================================ + // Announce to clan memebers + if (clan != null) + { + clan.setHasFort(getFortId()); // Set has fort flag for new owner + SystemMessage sm; + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CLAN_IS_VICTORIOUS_IN_THE_FORTRESS_BATTLE_OF_S2); + sm.addString(clan.getName()); + sm.addFortId(getFortId()); + Collection pls = L2World.getInstance().getAllPlayers().values(); + for (L2PcInstance player : pls) + { + player.sendPacket(sm); + } + clan.broadcastToOnlineMembers(new PledgeShowInfoUpdate(clan)); + clan.broadcastToOnlineMembers(new PlaySound(1, "Siege_Victory", 0, 0, 0, 0, 0)); + if (_FortUpdater[0] != null) + { + _FortUpdater[0].cancel(false); + } + if (_FortUpdater[1] != null) + { + _FortUpdater[1].cancel(false); + } + _FortUpdater[0] = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FortUpdater(this, clan, 0, UpdaterType.PERIODIC_UPDATE), Config.FS_UPDATE_FRQ * 60000l, Config.FS_UPDATE_FRQ * 60000l); // Schedule owner tasks to start running + if (Config.FS_MAX_OWN_TIME > 0) + { + _FortUpdater[1] = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FortUpdater(this, clan, 0, UpdaterType.MAX_OWN_TIME), 3600000, 3600000); // Schedule owner tasks to remove owener + } + } + else + { + if (_FortUpdater[0] != null) + { + _FortUpdater[0].cancel(false); + } + _FortUpdater[0] = null; + if (_FortUpdater[1] != null) + { + _FortUpdater[1].cancel(false); + } + _FortUpdater[1] = null; + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: updateOwnerInDB(L2Clan clan): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public final int getFortId() + { + return _fortId; + } + + public final L2Clan getOwnerClan() + { + return _fortOwner; + } + + public final void setOwnerClan(L2Clan clan) + { + setVisibleFlag(clan != null ? true : false); + _fortOwner = clan; + } + + public final L2DoorInstance getDoor(int doorId) + { + if (doorId <= 0) + { + return null; + } + + for (L2DoorInstance door : getDoors()) + { + if (door.getDoorId() == doorId) + { + return door; + } + } + return null; + } + + public final List getDoors() + { + return _doors; + } + + public final L2StaticObjectInstance getFlagPole() + { + return _flagPole; + } + + public final FortSiege getSiege() + { + if (_siege == null) + { + _siege = new FortSiege(this); + } + return _siege; + } + + public final Calendar getSiegeDate() + { + return _siegeDate; + } + + public final void setSiegeDate(Calendar siegeDate) + { + _siegeDate = siegeDate; + } + + public final int getOwnedTime() + { + if (_lastOwnedTime.getTimeInMillis() == 0) + { + return 0; + } + + return (int) ((System.currentTimeMillis() - _lastOwnedTime.getTimeInMillis()) / 1000); + } + + public final int getTimeTillRebelArmy() + { + if (_lastOwnedTime.getTimeInMillis() == 0) + { + return 0; + } + + return (int) (((_lastOwnedTime.getTimeInMillis() + (Config.FS_MAX_OWN_TIME * 3600000l)) - System.currentTimeMillis()) / 1000l); + } + + public final long getTimeTillNextFortUpdate() + { + if (_FortUpdater[0] == null) + { + return 0; + } + return _FortUpdater[0].getDelay(TimeUnit.SECONDS); + } + + public final String getName() + { + return _name; + } + + public void updateClansReputation(L2Clan owner, boolean removePoints) + { + if (owner != null) + { + if (removePoints) + { + owner.takeReputationScore(Config.LOOSE_FORT_POINTS, true); + } + else + { + owner.addReputationScore(Config.TAKE_FORT_POINTS, true); + } + } + } + + private static class endFortressSiege implements Runnable + { + private final Fort _f; + private final L2Clan _clan; + + public endFortressSiege(Fort f, L2Clan clan) + { + _f = f; + _clan = clan; + } + + @Override + public void run() + { + try + { + _f.engrave(_clan); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception in endFortressSiege " + e.getMessage(), e); + } + } + + } + + /** + * @return Returns state of fortress.
    + *
    + * 0 - not decided yet
    + * 1 - independent
    + * 2 - contracted with castle
    + */ + public final int getFortState() + { + return _state; + } + + /** + * @param State.
    + * 0 - not decided yet
    + * 1 - independent
    + * 2 - contracted with castle
    + *
    + * @param CastleId.
    + * set Castle Id for contracted fort + */ + public final void setFortState(int state, int castleId) + { + _state = state; + _castleId = castleId; + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement; + + statement = con.prepareStatement("UPDATE fort SET state=?,castleId=? WHERE id = ?"); + statement.setInt(1, getFortState()); + statement.setInt(2, getCastleId()); + statement.setInt(3, getFortId()); + statement.execute(); + statement.close(); + + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: setFortState(int state, int castleId): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * @return Returns Castle Id of fortress contracted with castle. + */ + public final int getCastleId() + { + return _castleId; + } + + /** + * @return Returns fortress type.
    + *
    + * 0 - small (3 commanders)
    + * 1 - big (4 commanders + control room) + */ + public final int getFortType() + { + return _fortType; + } + + public final int getCastleIdFromEnvoy(int npcId) + { + return _envoyCastles.get(npcId); + } + + /** + * @return Returns amount of barracks. + */ + public final int getFortSize() + { + return getFortType() == 0 ? 3 : 5; + } + + public void spawnSuspiciousMerchant() + { + if (_isSuspiciousMerchantSpawned) + { + return; + } + _isSuspiciousMerchantSpawned = true; + + for (L2Spawn spawnDat : _siegeNpcs) + { + spawnDat.doSpawn(); + spawnDat.startRespawn(); + } + } + + public void despawnSuspiciousMerchant() + { + if (!_isSuspiciousMerchantSpawned) + { + return; + } + _isSuspiciousMerchantSpawned = false; + + for (L2Spawn spawnDat : _siegeNpcs) + { + spawnDat.stopRespawn(); + spawnDat.getLastSpawn().deleteMe(); + } + } + + public void spawnNpcCommanders() + { + for (L2Spawn spawnDat : _npcCommanders) + { + spawnDat.doSpawn(); + spawnDat.startRespawn(); + } + } + + public void despawnNpcCommanders() + { + for (L2Spawn spawnDat : _npcCommanders) + { + spawnDat.stopRespawn(); + spawnDat.getLastSpawn().deleteMe(); + } + } + + public void spawnSpecialEnvoys() + { + for (L2Spawn spawnDat : _specialEnvoys) + { + spawnDat.doSpawn(); + spawnDat.startRespawn(); + } + } + + public void despawnSpecialEnvoys() + { + for (L2Spawn spawnDat : _specialEnvoys) + { + spawnDat.stopRespawn(); + spawnDat.getLastSpawn().deleteMe(); + } + } + + private void initNpcs() + { + Connection con = null; + + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT * FROM fort_spawnlist WHERE fortId = ? AND spawnType = ? "); + statement.setInt(1, getFortId()); + statement.setInt(2, 0); + ResultSet rset = statement.executeQuery(); + + L2Spawn spawnDat; + L2NpcTemplate template; + + while (rset.next()) + { + template = NpcTable.getInstance().getTemplate(rset.getInt("npcId")); + if (template != null) + { + spawnDat = new L2Spawn(template); + spawnDat.setAmount(1); + spawnDat.setLocx(rset.getInt("x")); + spawnDat.setLocy(rset.getInt("y")); + spawnDat.setLocz(rset.getInt("z")); + spawnDat.setHeading(rset.getInt("heading")); + spawnDat.setRespawnDelay(60); + SpawnTable.getInstance().addNewSpawn(spawnDat, false); + spawnDat.doSpawn(); + spawnDat.startRespawn(); + } + else + { + _log.warning("Fort " + getFortId() + " initNpcs: Data missing in NPC table for ID: " + rset.getInt("npcId") + "."); + } + } + + rset.close(); + statement.close(); + } + catch (Exception e) + { + // problem with initializing spawn, go to next one + _log.log(Level.WARNING, "Fort " + getFortId() + " initNpcs: Spawn could not be initialized: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void initSiegeNpcs() + { + Connection con = null; + _siegeNpcs.clear(); + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT id, npcId, x, y, z, heading FROM fort_spawnlist WHERE fortId = ? AND spawnType = ? ORDER BY id"); + statement.setInt(1, getFortId()); + statement.setInt(2, 2); + ResultSet rset = statement.executeQuery(); + + L2Spawn spawnDat; + L2NpcTemplate template; + while (rset.next()) + { + template = NpcTable.getInstance().getTemplate(rset.getInt("npcId")); + if (template != null) + { + spawnDat = new L2Spawn(template); + spawnDat.setAmount(1); + spawnDat.setLocx(rset.getInt("x")); + spawnDat.setLocy(rset.getInt("y")); + spawnDat.setLocz(rset.getInt("z")); + spawnDat.setHeading(rset.getInt("heading")); + spawnDat.setRespawnDelay(60); + _siegeNpcs.add(spawnDat); + } + else + { + _log.warning("Fort " + getFortId() + " initSiegeNpcs: Data missing in NPC table for ID: " + rset.getInt("npcId") + "."); + } + } + rset.close(); + statement.close(); + } + catch (Exception e) + { + // problem with initializing spawn, go to next one + _log.log(Level.WARNING, "Fort " + getFortId() + " initSiegeNpcs: Spawn could not be initialized: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void initNpcCommanders() + { + Connection con = null; + _npcCommanders.clear(); + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT id, npcId, x, y, z, heading FROM fort_spawnlist WHERE fortId = ? AND spawnType = ? ORDER BY id"); + statement.setInt(1, getFortId()); + statement.setInt(2, 1); + ResultSet rset = statement.executeQuery(); + + L2Spawn spawnDat; + L2NpcTemplate template; + while (rset.next()) + { + template = NpcTable.getInstance().getTemplate(rset.getInt("npcId")); + if (template != null) + { + spawnDat = new L2Spawn(template); + spawnDat.setAmount(1); + spawnDat.setLocx(rset.getInt("x")); + spawnDat.setLocy(rset.getInt("y")); + spawnDat.setLocz(rset.getInt("z")); + spawnDat.setHeading(rset.getInt("heading")); + spawnDat.setRespawnDelay(60); + _npcCommanders.add(spawnDat); + } + else + { + _log.warning("Fort " + getFortId() + " initNpcCommanders: Data missing in NPC table for ID: " + rset.getInt("npcId") + "."); + } + } + rset.close(); + statement.close(); + } + catch (Exception e) + { + // problem with initializing spawn, go to next one + _log.log(Level.WARNING, "Fort " + getFortId() + " initNpcCommanders: Spawn could not be initialized: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + private void initSpecialEnvoys() + { + Connection con = null; + _specialEnvoys.clear(); + _envoyCastles.clear(); + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT id, npcId, x, y, z, heading, castleId FROM fort_spawnlist WHERE fortId = ? AND spawnType = ? ORDER BY id"); + statement.setInt(1, getFortId()); + statement.setInt(2, 3); + ResultSet rset = statement.executeQuery(); + + L2Spawn spawnDat; + L2NpcTemplate template; + while (rset.next()) + { + int castleId = rset.getInt("castleId"); + int npcId = rset.getInt("npcId"); + template = NpcTable.getInstance().getTemplate(npcId); + if (template != null) + { + spawnDat = new L2Spawn(template); + spawnDat.setAmount(1); + spawnDat.setLocx(rset.getInt("x")); + spawnDat.setLocy(rset.getInt("y")); + spawnDat.setLocz(rset.getInt("z")); + spawnDat.setHeading(rset.getInt("heading")); + spawnDat.setRespawnDelay(60); + _specialEnvoys.add(spawnDat); + _envoyCastles.put(npcId, castleId); + } + else + { + _log.warning("Fort " + getFortId() + " initSpecialEnvoys: Data missing in NPC table for ID: " + rset.getInt("npcId") + "."); + } + } + rset.close(); + statement.close(); + } + catch (Exception e) + { + // problem with initializing spawn, go to next one + _log.log(Level.WARNING, "Fort " + getFortId() + " initSpecialEnvoys: Spawn could not be initialized: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public FastList getResidentialSkills() + { + return _residentialSkills; + } + + public void giveResidentialSkills(L2PcInstance player) + { + if ((_residentialSkills != null) && !_residentialSkills.isEmpty()) + { + for (L2Skill sk : _residentialSkills) + { + player.addSkill(sk, false); + } + } + } + + public void removeResidentialSkills(L2PcInstance player) + { + if ((_residentialSkills != null) && !_residentialSkills.isEmpty()) + { + for (L2Skill sk : _residentialSkills) + { + player.removeSkill(sk, false, true); + } + } + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/FortSiege.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/FortSiege.java new file mode 100644 index 00000000..4609f813 --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/FortSiege.java @@ -0,0 +1,1354 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.l2jserver.gameserver.model.entity; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Calendar; +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.ThreadPoolManager; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.datatables.MapRegionTable; +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.instancemanager.FortManager; +import com.l2jserver.gameserver.instancemanager.FortSiegeGuardManager; +import com.l2jserver.gameserver.instancemanager.FortSiegeManager; +import com.l2jserver.gameserver.instancemanager.FortSiegeManager.SiegeSpawn; +import com.l2jserver.gameserver.model.CombatFlag; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2Object; +import com.l2jserver.gameserver.model.L2SiegeClan; +import com.l2jserver.gameserver.model.L2SiegeClan.SiegeClanType; +import com.l2jserver.gameserver.model.L2Spawn; +import com.l2jserver.gameserver.model.actor.L2Npc; +import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; +import com.l2jserver.gameserver.model.actor.instance.L2FortCommanderInstance; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.NpcSay; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; + +public class FortSiege implements Siegable +{ + protected static final Logger _log = Logger.getLogger(FortSiege.class.getName()); + + public static enum TeleportWhoType + { + All, + Attacker, + Owner, + } + + public class ScheduleEndSiegeTask implements Runnable + { + @Override + public void run() + { + if (!getIsInProgress()) + { + return; + } + + try + { + _siegeEnd = null; + endSiege(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: ScheduleEndSiegeTask() for Fort: " + _fort.getName() + " " + e.getMessage(), e); + } + } + } + + public class ScheduleStartSiegeTask implements Runnable + { + private final Fort _fortInst; + private final int _time; + + public ScheduleStartSiegeTask(int time) + { + _fortInst = _fort; + _time = time; + } + + @Override + public void run() + { + if (getIsInProgress()) + { + return; + } + + try + { + final SystemMessage sm; + if (_time == 3600) // 1hr remains + { + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleStartSiegeTask(600), 3000000); // Prepare task for 10 minutes left. + } + else if (_time == 600) // 10min remains + { + getFort().despawnSuspiciousMerchant(); + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_MINUTES_UNTIL_THE_FORTRESS_BATTLE_STARTS); + sm.addNumber(10); + announceToPlayer(sm); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleStartSiegeTask(300), 300000); // Prepare task for 5 minutes left. + } + else if (_time == 300) // 5min remains + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_MINUTES_UNTIL_THE_FORTRESS_BATTLE_STARTS); + sm.addNumber(5); + announceToPlayer(sm); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleStartSiegeTask(60), 240000); // Prepare task for 1 minute left. + } + else if (_time == 60) // 1min remains + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_MINUTES_UNTIL_THE_FORTRESS_BATTLE_STARTS); + sm.addNumber(1); + announceToPlayer(sm); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleStartSiegeTask(30), 30000); // Prepare task for 30 seconds left. + } + else if (_time == 30) // 30seconds remains + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_SECONDS_UNTIL_THE_FORTRESS_BATTLE_STARTS); + sm.addNumber(30); + announceToPlayer(sm); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleStartSiegeTask(10), 20000); // Prepare task for 10 seconds left. + } + else if (_time == 10) // 10seconds remains + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_SECONDS_UNTIL_THE_FORTRESS_BATTLE_STARTS); + sm.addNumber(10); + announceToPlayer(sm); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleStartSiegeTask(5), 5000); // Prepare task for 5 seconds left. + } + else if (_time == 5) // 5seconds remains + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_SECONDS_UNTIL_THE_FORTRESS_BATTLE_STARTS); + sm.addNumber(5); + announceToPlayer(sm); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleStartSiegeTask(1), 4000); // Prepare task for 1 seconds left. + } + else if (_time == 1) // 1seconds remains + { + sm = SystemMessage.getSystemMessage(SystemMessageId.S1_SECONDS_UNTIL_THE_FORTRESS_BATTLE_STARTS); + sm.addNumber(1); + announceToPlayer(sm); + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleStartSiegeTask(0), 1000); // Prepare task start siege. + } + else if (_time == 0)// start siege + { + _fortInst.getSiege().startSiege(); + } + else + { + _log.warning("Exception: ScheduleStartSiegeTask(): unknown siege time: " + String.valueOf(_time)); + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: ScheduleStartSiegeTask() for Fort: " + _fortInst.getName() + " " + e.getMessage(), e); + } + } + } + + public class ScheduleSuspiciousMerchantSpawn implements Runnable + { + @Override + public void run() + { + if (getIsInProgress()) + { + return; + } + + try + { + _fort.spawnSuspiciousMerchant(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: ScheduleSuspicoiusMerchantSpawn() for Fort: " + _fort.getName() + " " + e.getMessage(), e); + } + } + } + + public class ScheduleSiegeRestore implements Runnable + { + @Override + public void run() + { + if (!getIsInProgress()) + { + return; + } + + try + { + _siegeRestore = null; + resetSiege(); + announceToPlayer(SystemMessage.getSystemMessage(SystemMessageId.BARRACKS_FUNCTION_RESTORED)); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: ScheduleSiegeRestore() for Fort: " + _fort.getName() + " " + e.getMessage(), e); + } + } + } + + private final List _attackerClans = new FastList<>(); + + // Fort setting + protected FastList _commanders = new FastList<>(); + protected final Fort _fort; + private boolean _isInProgress = false; + private FortSiegeGuardManager _siegeGuardManager; + ScheduledFuture _siegeEnd = null; + ScheduledFuture _siegeRestore = null; + ScheduledFuture _siegeStartTask = null; + + public FortSiege(Fort fort) + { + _fort = fort; + + checkAutoTask(); + FortSiegeManager.getInstance().addSiege(this); + } + + /** + * When siege ends
    + *
    + */ + @Override + public void endSiege() + { + if (getIsInProgress()) + { + _isInProgress = false; // Flag so that siege instance can be started + final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THE_FORTRESS_BATTLE_OF_S1_HAS_FINISHED); + sm.addFortId(getFort().getFortId()); + announceToPlayer(sm); + + removeFlags(); // Removes all flags. Note: Remove flag before teleporting players + unSpawnFlags(); + + updatePlayerSiegeStateFlags(true); + + getFort().getZone().banishForeigners(getFort().getOwnerClan()); + getFort().getZone().setIsActive(false); + getFort().getZone().updateZoneStatusForCharactersInside(); + getFort().getZone().setSiegeInstance(null); + + saveFortSiege(); // Save fort specific data + clearSiegeClan(); // Clear siege clan from db + removeCommanders(); // Remove commander from this fort + + getFort().spawnNpcCommanders(); // Spawn NPC commanders + getSiegeGuardManager().unspawnSiegeGuard(); // Remove all spawned siege guard from this fort + getFort().resetDoors(); // Respawn door to fort + + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleSuspiciousMerchantSpawn(), FortSiegeManager.getInstance().getSuspiciousMerchantRespawnDelay() * 60 * 1000l); // Prepare 3hr task for suspicious merchant respawn + setSiegeDateTime(true); // store suspicious merchant spawn in DB + + if (_siegeEnd != null) + { + _siegeEnd.cancel(true); + _siegeEnd = null; + } + if (_siegeRestore != null) + { + _siegeRestore.cancel(true); + _siegeRestore = null; + } + + if ((getFort().getOwnerClan() != null) && (getFort().getFlagPole().getMeshIndex() == 0)) + { + getFort().setVisibleFlag(true); + } + + _log.info("Siege of " + getFort().getName() + " fort finished."); + } + } + + /** + * When siege starts
    + *
    + */ + @Override + public void startSiege() + { + if (!getIsInProgress()) + { + if (_siegeStartTask != null) // used admin command "admin_startfortsiege" + { + _siegeStartTask.cancel(true); + getFort().despawnSuspiciousMerchant(); + } + _siegeStartTask = null; + + if (getAttackerClans().isEmpty()) + { + return; + } + + _isInProgress = true; // Flag so that same siege instance cannot be started again + + loadSiegeClan(); // Load siege clan from db + updatePlayerSiegeStateFlags(false); + teleportPlayer(FortSiege.TeleportWhoType.Attacker, MapRegionTable.TeleportWhereType.Town); // Teleport to the closest town + + getFort().despawnNpcCommanders(); // Despawn NPC commanders + spawnCommanders(); // Spawn commanders + getFort().resetDoors(); // Spawn door + spawnSiegeGuard(); // Spawn siege guard + getFort().setVisibleFlag(false); + getFort().getZone().setSiegeInstance(this); + getFort().getZone().setIsActive(true); + getFort().getZone().updateZoneStatusForCharactersInside(); + + // Schedule a task to prepare auto siege end + _siegeEnd = ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleEndSiegeTask(), FortSiegeManager.getInstance().getSiegeLength() * 60 * 1000l); // Prepare auto end task + + final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THE_FORTRESS_BATTLE_S1_HAS_BEGUN); + sm.addFortId(getFort().getFortId()); + announceToPlayer(sm); + saveFortSiege(); + + _log.info("Siege of " + getFort().getName() + " fort started."); + } + } + + /** + * Announce to player.
    + *
    + * @param message The String of the message to send to player + */ + public void announceToPlayer(SystemMessage sm) + { + // announce messages only for participants + L2Clan clan; + for (L2SiegeClan siegeclan : getAttackerClans()) + { + clan = ClanTable.getInstance().getClan(siegeclan.getClanId()); + for (L2PcInstance member : clan.getOnlineMembers(0)) + { + if (member != null) + { + member.sendPacket(sm); + } + } + } + if (getFort().getOwnerClan() != null) + { + clan = ClanTable.getInstance().getClan(getFort().getOwnerClan().getClanId()); + for (L2PcInstance member : clan.getOnlineMembers(0)) + { + if (member != null) + { + member.sendPacket(sm); + } + } + } + } + + public void announceToPlayer(SystemMessage sm, String s) + { + sm.addString(s); + announceToPlayer(sm); + } + + public void updatePlayerSiegeStateFlags(boolean clear) + { + L2Clan clan; + for (L2SiegeClan siegeclan : getAttackerClans()) + { + clan = ClanTable.getInstance().getClan(siegeclan.getClanId()); + for (L2PcInstance member : clan.getOnlineMembers(0)) + { + if (member == null) + { + continue; + } + + if (clear) + { + member.setSiegeState((byte) 0); + member.setSiegeSide(0); + member.setIsInSiege(false); + member.stopFameTask(); + } + else + { + member.setSiegeState((byte) 1); + member.setSiegeSide(getFort().getFortId()); + if (checkIfInZone(member)) + { + member.setIsInSiege(true); + member.startFameTask(Config.FORTRESS_ZONE_FAME_TASK_FREQUENCY * 1000, Config.FORTRESS_ZONE_FAME_AQUIRE_POINTS); + } + } + member.broadcastUserInfo(); + } + } + if (getFort().getOwnerClan() != null) + { + clan = ClanTable.getInstance().getClan(getFort().getOwnerClan().getClanId()); + for (L2PcInstance member : clan.getOnlineMembers(0)) + { + if (member == null) + { + continue; + } + + if (clear) + { + member.setSiegeState((byte) 0); + member.setSiegeSide(0); + member.setIsInSiege(false); + member.stopFameTask(); + } + else + { + member.setSiegeState((byte) 2); + member.setSiegeSide(getFort().getFortId()); + if (checkIfInZone(member)) + { + member.setIsInSiege(true); + member.startFameTask(Config.FORTRESS_ZONE_FAME_TASK_FREQUENCY * 1000, Config.FORTRESS_ZONE_FAME_AQUIRE_POINTS); + } + } + member.broadcastUserInfo(); + } + } + } + + /** Return true if object is inside the zone */ + public boolean checkIfInZone(L2Object object) + { + return checkIfInZone(object.getX(), object.getY(), object.getZ()); + } + + /** Return true if object is inside the zone */ + public boolean checkIfInZone(int x, int y, int z) + { + return (getIsInProgress() && (getFort().checkIfInZone(x, y, z))); // Fort zone during siege + } + + /** + * Return true if clan is attacker
    + *
    + * @param clan The L2Clan of the player + */ + @Override + public boolean checkIsAttacker(L2Clan clan) + { + return (getAttackerClan(clan) != null); + } + + /** + * Return true if clan is defender
    + *
    + * @param clan The L2Clan of the player + */ + @Override + public boolean checkIsDefender(L2Clan clan) + { + if ((clan != null) && (getFort().getOwnerClan() == clan)) + { + return true; + } + + return false; + } + + /** Clear all registered siege clans from database for fort */ + public void clearSiegeClan() + { + Connection con = null; + PreparedStatement statement = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("DELETE FROM fortsiege_clans WHERE fort_id=?"); + statement.setInt(1, getFort().getFortId()); + statement.execute(); + statement.close(); + + if (getFort().getOwnerClan() != null) + { + statement = con.prepareStatement("DELETE FROM fortsiege_clans WHERE clan_id=?"); + statement.setInt(1, getFort().getOwnerClan().getClanId()); + statement.execute(); + } + + getAttackerClans().clear(); + + // if siege is in progress, end siege + if (getIsInProgress()) + { + endSiege(); + } + + // if siege isnt in progress (1hr waiting time till siege starts), cancel waiting time + if (_siegeStartTask != null) + { + _siegeStartTask.cancel(true); + _siegeStartTask = null; + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: clearSiegeClan(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Set the date for the next siege. */ + private void clearSiegeDate() + { + getFort().getSiegeDate().setTimeInMillis(0); + } + + /** Return list of L2PcInstance registered as attacker in the zone. */ + @Override + public List getAttackersInZone() + { + List players = new FastList<>(); + L2Clan clan; + for (L2SiegeClan siegeclan : getAttackerClans()) + { + clan = ClanTable.getInstance().getClan(siegeclan.getClanId()); + for (L2PcInstance player : clan.getOnlineMembers(0)) + { + if (player == null) + { + continue; + } + + if (player.isInSiege()) + { + players.add(player); + } + } + } + return players; + } + + /** Return list of L2PcInstance in the zone. */ + public List getPlayersInZone() + { + return getFort().getZone().getAllPlayers(); + } + + /** Return list of L2PcInstance owning the fort in the zone. */ + public List getOwnersInZone() + { + List players = new FastList<>(); + L2Clan clan; + if (getFort().getOwnerClan() != null) + { + clan = ClanTable.getInstance().getClan(getFort().getOwnerClan().getClanId()); + if (clan != getFort().getOwnerClan()) + { + return null; + } + + for (L2PcInstance player : clan.getOnlineMembers(0)) + { + if (player == null) + { + continue; + } + + if (player.isInSiege()) + { + players.add(player); + } + } + } + return players; + } + + /** Commander was killed */ + public void killedCommander(L2FortCommanderInstance instance) + { + if ((_commanders != null) && (getFort() != null) && (_commanders.size() != 0)) + { + L2Spawn spawn = instance.getSpawn(); + if (spawn != null) + { + FastList commanders = FortSiegeManager.getInstance().getCommanderSpawnList(getFort().getFortId()); + for (SiegeSpawn spawn2 : commanders) + { + if (spawn2.getNpcId() == spawn.getNpcid()) + { + int message = 0; + switch (spawn2.getId()) + { + case 1: + message = 1300004; // You may have broken our arrows, but you will never break our will! Archers, retreat! + break; + case 2: + message = 1300006; // Aiieeee! Command Center! This is guard unit! We need backup right away! + break; + case 3: + message = 1300005; // At last! The Magic Field that protects the fortress has weakened! Volunteers, stand back! + break; + case 4: + message = 1300020; // I feel so much grief that I can't even take care of myself. There isn't any reason for me to stay here any longer. + break; + } + if (message != 0) + { + instance.broadcastPacket(new NpcSay(instance.getObjectId(), 1, instance.getNpcId(), message)); + } + } + } + _commanders.remove(spawn); + if (_commanders.isEmpty()) + { + // spawn fort flags + spawnFlag(getFort().getFortId()); + // cancel door/commanders respawn + if (_siegeRestore != null) + { + _siegeRestore.cancel(true); + } + // open doors in main building + for (L2DoorInstance door : getFort().getDoors()) + { + if (!door.getIsCommanderDoor()) + { + continue; + } + + door.openMe(); + } + getFort().getSiege().announceToPlayer(SystemMessage.getSystemMessage(SystemMessageId.ALL_BARRACKS_OCCUPIED)); + } + // schedule restoring doors/commanders respawn + else if (_siegeRestore == null) + { + getFort().getSiege().announceToPlayer(SystemMessage.getSystemMessage(SystemMessageId.SEIZED_BARRACKS)); + _siegeRestore = ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleSiegeRestore(), FortSiegeManager.getInstance().getCountDownLength() * 60 * 1000l); + } + else + { + getFort().getSiege().announceToPlayer(SystemMessage.getSystemMessage(SystemMessageId.SEIZED_BARRACKS)); + } + } + else + { + _log.warning("FortSiege.killedCommander(): killed commander, but commander not registered for fortress. NpcId: " + instance.getNpcId() + " FortId: " + getFort().getFortId()); + } + } + } + + /** Remove the flag that was killed */ + public void killedFlag(L2Npc flag) + { + if (flag == null) + { + return; + } + + for (L2SiegeClan clan : getAttackerClans()) + { + if (clan.removeFlag(flag)) + { + return; + } + } + } + + /** + * Register clan as attacker
    + *
    + * @param player The L2PcInstance of the player trying to register + */ + public boolean registerAttacker(L2PcInstance player, boolean force) + { + if (player.getClan() == null) + { + return false; + } + + if (force || checkIfCanRegister(player)) + { + saveSiegeClan(player.getClan()); // Save to database + // if the first registering we start the timer + if (getAttackerClans().size() == 1) + { + if (!force) + { + player.reduceAdena("siege", 250000, null, true); + } + startAutoTask(true); + } + return true; + } + return false; + } + + /** + * Remove clan from siege
    + *
    + * This function does not do any checks and should not be called from bypass ! + * @param clanId The int of player's clan id + */ + private void removeSiegeClan(int clanId) + { + Connection con = null; + PreparedStatement statement = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + if (clanId != 0) + { + statement = con.prepareStatement("DELETE FROM fortsiege_clans WHERE fort_id=? AND clan_id=?"); + } + else + { + statement = con.prepareStatement("DELETE FROM fortsiege_clans WHERE fort_id=?"); + } + + statement.setInt(1, getFort().getFortId()); + if (clanId != 0) + { + statement.setInt(2, clanId); + } + statement.execute(); + + loadSiegeClan(); + if (getAttackerClans().isEmpty()) + { + if (getIsInProgress()) + { + endSiege(); + } + else + { + saveFortSiege(); // Clear siege time in DB + } + + if (_siegeStartTask != null) + { + _siegeStartTask.cancel(true); + _siegeStartTask = null; + } + } + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception on removeSiegeClan: " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Remove clan from siege
    + *
    + * @paramclan The clan being removed + */ + public void removeSiegeClan(L2Clan clan) + { + if ((clan == null) || (clan.getHasFort() == getFort().getFortId()) || !FortSiegeManager.getInstance().checkIsRegistered(clan, getFort().getFortId())) + { + return; + } + + removeSiegeClan(clan.getClanId()); + } + + /** + * Start the auto tasks
    + *
    + */ + public void checkAutoTask() + { + if (_siegeStartTask != null) + { + return; + } + + final long delay = getFort().getSiegeDate().getTimeInMillis() - Calendar.getInstance().getTimeInMillis(); + + if (delay < 0) + { + // siege time in past + saveFortSiege(); + clearSiegeClan(); // remove all clans + // spawn suspicious merchant immediately + ThreadPoolManager.getInstance().executeTask(new ScheduleSuspiciousMerchantSpawn()); + return; + } + + loadSiegeClan(); + if (getAttackerClans().isEmpty()) + { + // no attackers - waiting for suspicious merchant spawn + ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleSuspiciousMerchantSpawn(), delay); + } + else + { + // preparing start siege task + if (delay > 3600000) // more than hour, how this can happens ? spawn suspicious merchant + { + ThreadPoolManager.getInstance().executeTask(new ScheduleSuspiciousMerchantSpawn()); + _siegeStartTask = ThreadPoolManager.getInstance().scheduleGeneral(new FortSiege.ScheduleStartSiegeTask(3600), delay - 3600000); + } + if (delay > 600000) // more than 10 min, spawn suspicious merchant + { + ThreadPoolManager.getInstance().executeTask(new ScheduleSuspiciousMerchantSpawn()); + _siegeStartTask = ThreadPoolManager.getInstance().scheduleGeneral(new FortSiege.ScheduleStartSiegeTask(600), delay - 600000); + } + else if (delay > 300000) + { + _siegeStartTask = ThreadPoolManager.getInstance().scheduleGeneral(new FortSiege.ScheduleStartSiegeTask(300), delay - 300000); + } + else if (delay > 60000) + { + _siegeStartTask = ThreadPoolManager.getInstance().scheduleGeneral(new FortSiege.ScheduleStartSiegeTask(60), delay - 60000); + } + else + { + // lower than 1 min, set to 1 min + _siegeStartTask = ThreadPoolManager.getInstance().scheduleGeneral(new FortSiege.ScheduleStartSiegeTask(60), 0); + } + + _log.info("Siege of " + getFort().getName() + " fort: " + getFort().getSiegeDate().getTime()); + } + } + + /** + * Start the auto tasks
    + *
    + */ + public void startAutoTask(boolean setTime) + { + if (_siegeStartTask != null) + { + return; + } + + if (setTime) + { + setSiegeDateTime(false); + } + + if (getFort().getOwnerClan() != null) + { + getFort().getOwnerClan().broadcastToOnlineMembers(SystemMessage.getSystemMessage(SystemMessageId.A_FORTRESS_IS_UNDER_ATTACK)); + } + + // Execute siege auto start + _siegeStartTask = ThreadPoolManager.getInstance().scheduleGeneral(new FortSiege.ScheduleStartSiegeTask(3600), 0); + } + + /** + * Teleport players + */ + public void teleportPlayer(TeleportWhoType teleportWho, MapRegionTable.TeleportWhereType teleportWhere) + { + List players; + switch (teleportWho) + { + case Owner: + players = getOwnersInZone(); + break; + case Attacker: + players = getAttackersInZone(); + break; + default: + players = getPlayersInZone(); + } + + for (L2PcInstance player : players) + { + if (player.isGM() || player.isInJail()) + { + continue; + } + + player.teleToLocation(teleportWhere); + } + } + + // ========================================================= + // Method - Private + /** + * Add clan as attacker
    + *
    + * @param clanId The int of clan's id + */ + private void addAttacker(int clanId) + { + getAttackerClans().add(new L2SiegeClan(clanId, SiegeClanType.ATTACKER)); // Add registered attacker to attacker list + } + + /** + * Return true if the player can register.
    + *
    + * @param player The L2PcInstance of the player trying to register + */ + public boolean checkIfCanRegister(L2PcInstance player) + { + boolean b = true; + if ((player.getClan() == null) || (player.getClan().getLevel() < FortSiegeManager.getInstance().getSiegeClanMinLevel())) + { + b = false; + player.sendMessage("Only clans with Level " + FortSiegeManager.getInstance().getSiegeClanMinLevel() + " and higher may register for a fortress siege."); + } + else if ((player.getClanPrivileges() & L2Clan.CP_CS_MANAGE_SIEGE) != L2Clan.CP_CS_MANAGE_SIEGE) + { + b = false; + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_NOT_AUTHORIZED_TO_DO_THAT)); + } + else if (player.getClan() == getFort().getOwnerClan()) + { + b = false; + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CLAN_THAT_OWNS_CASTLE_IS_AUTOMATICALLY_REGISTERED_DEFENDING)); + } + else if ((getFort().getOwnerClan() != null) && (player.getClan().getHasCastle() > 0) && (player.getClan().getHasCastle() == getFort().getCastleId())) + { + b = false; + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANT_REGISTER_TO_SIEGE_DUE_TO_CONTRACT)); + } + else if ((getFort().getTimeTillRebelArmy() > 0) && (getFort().getTimeTillRebelArmy() <= 7200)) + { + b = false; + player.sendMessage("You cannot register for the fortress siege 2 hours prior to rebel army attack."); + } + else if (getFort().getSiege().getAttackerClans().isEmpty() && (player.getInventory().getAdena() < 250000)) + { + b = false; + player.sendMessage("You need 250,000 adena to register"); // replace me with html + } + else + { + for (Fort fort : FortManager.getInstance().getForts()) + { + if (fort.getSiege().getAttackerClan(player.getClanId()) != null) + { + b = false; + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ALREADY_REQUESTED_SIEGE_BATTLE)); + break; + } + if ((fort.getOwnerClan() == player.getClan()) && (fort.getSiege().getIsInProgress() || (fort.getSiege()._siegeStartTask != null))) + { + b = false; + player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ALREADY_REQUESTED_SIEGE_BATTLE)); + break; + } + } + } + return b; + } + + /** + * Return true if the clan has already registered to a siege for the same day.
    + *
    + * @param clan The L2Clan of the player trying to register + */ + public boolean checkIfAlreadyRegisteredForSameDay(L2Clan clan) + { + for (FortSiege siege : FortSiegeManager.getInstance().getSieges()) + { + if (siege == this) + { + continue; + } + + if (siege.getSiegeDate().get(Calendar.DAY_OF_WEEK) == getSiegeDate().get(Calendar.DAY_OF_WEEK)) + { + if (siege.checkIsAttacker(clan)) + { + return true; + } + if (siege.checkIsDefender(clan)) + { + return true; + } + } + } + + return false; + } + + private void setSiegeDateTime(boolean merchant) + { + Calendar newDate = Calendar.getInstance(); + if (merchant) + { + newDate.add(Calendar.MINUTE, FortSiegeManager.getInstance().getSuspiciousMerchantRespawnDelay()); + } + else + { + newDate.add(Calendar.MINUTE, 60); + } + getFort().setSiegeDate(newDate); + saveSiegeDate(); + } + + /** Load siege clans. */ + private void loadSiegeClan() + { + Connection con = null; + PreparedStatement statement = null; + try + { + getAttackerClans().clear(); + + ResultSet rs = null; + + con = L2DatabaseFactory.getInstance().getConnection(); + + statement = con.prepareStatement("SELECT clan_id FROM fortsiege_clans WHERE fort_id=?"); + statement.setInt(1, getFort().getFortId()); + rs = statement.executeQuery(); + + while (rs.next()) + { + addAttacker(rs.getInt("clan_id")); + } + + rs.close(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: loadSiegeClan(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Remove commanders. */ + private void removeCommanders() + { + if ((_commanders != null) && !_commanders.isEmpty()) + { + // Remove all instance of commanders for this fort + for (L2Spawn spawn : _commanders) + { + if (spawn != null) + { + spawn.stopRespawn(); + if (spawn.getLastSpawn() != null) + { + spawn.getLastSpawn().deleteMe(); + } + } + } + _commanders.clear(); + } + } + + /** Remove all flags. */ + private void removeFlags() + { + for (L2SiegeClan sc : getAttackerClans()) + { + if (sc != null) + { + sc.removeFlags(); + } + } + } + + /** Save fort siege related to database. */ + private void saveFortSiege() + { + clearSiegeDate(); // clear siege date + saveSiegeDate(); // Save the new date + } + + /** Save siege date to database. */ + private void saveSiegeDate() + { + Connection con = null; + PreparedStatement statement = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("UPDATE fort SET siegeDate = ? WHERE id = ?"); + statement.setLong(1, getSiegeDate().getTimeInMillis()); + statement.setInt(2, getFort().getFortId()); + statement.execute(); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: saveSiegeDate(): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** + * Save registration to database.
    + *
    + * @param clan The L2Clan of player + * @param typeId -1 = owner 0 = defender, 1 = attacker, 2 = defender waiting + */ + private void saveSiegeClan(L2Clan clan) + { + if (getAttackerClans().size() >= FortSiegeManager.getInstance().getAttackerMaxClans()) + { + return; + } + + Connection con = null; + PreparedStatement statement = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + statement = con.prepareStatement("INSERT INTO fortsiege_clans (clan_id,fort_id) values (?,?)"); + statement.setInt(1, clan.getClanId()); + statement.setInt(2, getFort().getFortId()); + statement.execute(); + + addAttacker(clan.getClanId()); + } + catch (Exception e) + { + _log.log(Level.WARNING, "Exception: saveSiegeClan(L2Clan clan): " + e.getMessage(), e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + /** Spawn commanders. */ + private void spawnCommanders() + { + // Set commanders array size if one does not exist + try + { + _commanders.clear(); + L2Spawn spawnDat; + L2NpcTemplate template1; + for (SiegeSpawn _sp : FortSiegeManager.getInstance().getCommanderSpawnList(getFort().getFortId())) + { + template1 = NpcTable.getInstance().getTemplate(_sp.getNpcId()); + if (template1 != null) + { + spawnDat = new L2Spawn(template1); + spawnDat.setAmount(1); + spawnDat.setLocx(_sp.getLocation().getX()); + spawnDat.setLocy(_sp.getLocation().getY()); + spawnDat.setLocz(_sp.getLocation().getZ()); + spawnDat.setHeading(_sp.getLocation().getHeading()); + spawnDat.setRespawnDelay(60); + spawnDat.doSpawn(); + spawnDat.stopRespawn(); + _commanders.add(spawnDat); + } + else + { + _log.warning("FortSiege.spawnCommander: Data missing in NPC table for ID: " + _sp.getNpcId() + "."); + } + } + } + catch (Exception e) + { + // problem with initializing spawn, go to next one + _log.log(Level.WARNING, "FortSiege.spawnCommander: Spawn could not be initialized: " + e.getMessage(), e); + } + } + + private void spawnFlag(int Id) + { + for (CombatFlag cf : FortSiegeManager.getInstance().getFlagList(Id)) + { + cf.spawnMe(); + } + } + + private void unSpawnFlags() + { + if (FortSiegeManager.getInstance().getFlagList(getFort().getFortId()) == null) + { + return; + } + + for (CombatFlag cf : FortSiegeManager.getInstance().getFlagList(getFort().getFortId())) + { + cf.unSpawnMe(); + } + } + + /** + * Spawn siege guard.
    + *
    + */ + private void spawnSiegeGuard() + { + getSiegeGuardManager().spawnSiegeGuard(); + } + + @Override + public final L2SiegeClan getAttackerClan(L2Clan clan) + { + if (clan == null) + { + return null; + } + + return getAttackerClan(clan.getClanId()); + } + + @Override + public final L2SiegeClan getAttackerClan(int clanId) + { + for (L2SiegeClan sc : getAttackerClans()) + { + if ((sc != null) && (sc.getClanId() == clanId)) + { + return sc; + } + } + + return null; + } + + @Override + public final List getAttackerClans() + { + return _attackerClans; + } + + public final Fort getFort() + { + return _fort; + } + + public final boolean getIsInProgress() + { + return _isInProgress; + } + + @Override + public final Calendar getSiegeDate() + { + return getFort().getSiegeDate(); + } + + @Override + public List getFlag(L2Clan clan) + { + if (clan != null) + { + L2SiegeClan sc = getAttackerClan(clan); + if (sc != null) + { + return sc.getFlag(); + } + } + + return null; + } + + public final FortSiegeGuardManager getSiegeGuardManager() + { + if (_siegeGuardManager == null) + { + _siegeGuardManager = new FortSiegeGuardManager(getFort()); + } + + return _siegeGuardManager; + } + + public void resetSiege() + { + // reload commanders and repair doors + removeCommanders(); + spawnCommanders(); + getFort().resetDoors(); + } + + public List getCommanders() + { + return _commanders; + } + + @Override + public L2SiegeClan getDefenderClan(int clanId) + { + return null; + } + + @Override + public L2SiegeClan getDefenderClan(L2Clan clan) + { + return null; + } + + @Override + public List getDefenderClans() + { + return null; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.entity.Siegable#giveFame() + */ + @Override + public boolean giveFame() + { + return true; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.entity.Siegable#getFameFrequency() + */ + @Override + public int getFameFrequency() + { + return Config.FORTRESS_ZONE_FAME_TASK_FREQUENCY; + } + + /* + * (non-Javadoc) + * @see com.l2jserver.gameserver.model.entity.Siegable#getFameAmount() + */ + @Override + public int getFameAmount() + { + return Config.FORTRESS_ZONE_FAME_AQUIRE_POINTS; + } +} diff --git a/L2J_Server/java/com/l2jserver/gameserver/model/entity/Hero.java b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Hero.java new file mode 100644 index 00000000..dc370bab --- /dev/null +++ b/L2J_Server/java/com/l2jserver/gameserver/model/entity/Hero.java @@ -0,0 +1,1128 @@ +/* + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * @author godson + */ + +package com.l2jserver.gameserver.model.entity; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javolution.util.FastList; +import javolution.util.FastMap; + +import com.l2jserver.Config; +import com.l2jserver.L2DatabaseFactory; +import com.l2jserver.gameserver.cache.HtmCache; +import com.l2jserver.gameserver.datatables.CharNameTable; +import com.l2jserver.gameserver.datatables.CharTemplateTable; +import com.l2jserver.gameserver.datatables.ClanTable; +import com.l2jserver.gameserver.datatables.NpcTable; +import com.l2jserver.gameserver.instancemanager.CastleManager; +import com.l2jserver.gameserver.model.L2Clan; +import com.l2jserver.gameserver.model.L2ItemInstance; +import com.l2jserver.gameserver.model.L2World; +import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; +import com.l2jserver.gameserver.model.itemcontainer.Inventory; +import com.l2jserver.gameserver.model.olympiad.Olympiad; +import com.l2jserver.gameserver.network.SystemMessageId; +import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo; +import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate; +import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jserver.gameserver.network.serverpackets.SystemMessage; +import com.l2jserver.gameserver.network.serverpackets.UserInfo; +import com.l2jserver.gameserver.templates.StatsSet; +import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; +import com.l2jserver.util.StringUtil; + +public class Hero +{ + private static Logger _log = Logger.getLogger(Hero.class.getName()); + + private static final String GET_HEROES = "SELECT heroes.charId, " + "characters.char_name, heroes.class_id, heroes.count, heroes.played " + "FROM heroes, characters WHERE characters.charId = heroes.charId " + "AND heroes.played = 1"; + private static final String GET_ALL_HEROES = "SELECT heroes.charId, " + "characters.char_name, heroes.class_id, heroes.count, heroes.played " + "FROM heroes, characters WHERE characters.charId = heroes.charId"; + private static final String UPDATE_ALL = "UPDATE heroes SET played = 0"; + private static final String INSERT_HERO = "INSERT INTO heroes (charId, class_id, count, played) VALUES (?,?,?,?)"; + private static final String UPDATE_HERO = "UPDATE heroes SET count = ?, " + "played = ?" + " WHERE charId = ?"; + private static final String GET_CLAN_ALLY = "SELECT characters.clanid " + "AS clanid, coalesce(clan_data.ally_Id, 0) AS allyId FROM characters " + "LEFT JOIN clan_data ON clan_data.clan_id = characters.clanid " + "WHERE characters.charId = ?"; + private static final String GET_CLAN_NAME = "SELECT clan_name FROM clan_data " + "WHERE clan_id = (SELECT clanid FROM characters WHERE charId = ?)"; + // delete hero items + private static final String DELETE_ITEMS = "DELETE FROM items WHERE item_id IN " + "(6842, 6611, 6612, 6613, 6614, 6615, 6616, 6617, 6618, 6619, 6620, 6621, 9388, 9389, 9390) " + "AND owner_id NOT IN (SELECT charId FROM characters WHERE accesslevel > 0)"; + + private static Map _heroes; + private static Map _completeHeroes; + + private static Map _herocounts; + private static Map> _herofights; + private static List _fights; + + private static Map> _herodiary; + private static Map _heroMessage; + private static List _diary; + + public static final String COUNT = "count"; + public static final String PLAYED = "played"; + public static final String CLAN_NAME = "clan_name"; + public static final String CLAN_CREST = "clan_crest"; + public static final String ALLY_NAME = "ally_name"; + public static final String ALLY_CREST = "ally_crest"; + + public static final int ACTION_RAID_KILLED = 1; + public static final int ACTION_HERO_GAINED = 2; + public static final int ACTION_CASTLE_TAKEN = 3; + + public static Hero getInstance() + { + return SingletonHolder._instance; + } + + private Hero() + { + init(); + } + + private void init() + { + _heroes = new FastMap<>(); + _completeHeroes = new FastMap<>(); + + _herofights = new FastMap<>(); + _herocounts = new FastMap<>(); + _herodiary = new FastMap<>(); + _heroMessage = new FastMap<>(); + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement(GET_HEROES); + ResultSet rset = statement.executeQuery(); + PreparedStatement statement2 = con.prepareStatement(GET_CLAN_ALLY); + ResultSet rset2 = null; + + while (rset.next()) + { + StatsSet hero = new StatsSet(); + int charId = rset.getInt(Olympiad.CHAR_ID); + hero.set(Olympiad.CHAR_NAME, rset.getString(Olympiad.CHAR_NAME)); + hero.set(Olympiad.CLASS_ID, rset.getInt(Olympiad.CLASS_ID)); + hero.set(COUNT, rset.getInt(COUNT)); + hero.set(PLAYED, rset.getInt(PLAYED)); + + loadFights(charId); + loadDiary(charId); + loadMessage(charId); + + statement2.setInt(1, charId); + rset2 = statement2.executeQuery(); + + if (rset2.next()) + { + int clanId = rset2.getInt("clanid"); + int allyId = rset2.getInt("allyId"); + + String clanName = ""; + String allyName = ""; + int clanCrest = 0; + int allyCrest = 0; + + if (clanId > 0) + { + clanName = ClanTable.getInstance().getClan(clanId).getName(); + clanCrest = ClanTable.getInstance().getClan(clanId).getCrestId(); + + if (allyId > 0) + { + allyName = ClanTable.getInstance().getClan(clanId).getAllyName(); + allyCrest = ClanTable.getInstance().getClan(clanId).getAllyCrestId(); + } + } + + hero.set(CLAN_CREST, clanCrest); + hero.set(CLAN_NAME, clanName); + hero.set(ALLY_CREST, allyCrest); + hero.set(ALLY_NAME, allyName); + } + + rset2.close(); + statement2.clearParameters(); + + _heroes.put(charId, hero); + } + + rset.close(); + statement.close(); + + statement = con.prepareStatement(GET_ALL_HEROES); + rset = statement.executeQuery(); + + while (rset.next()) + { + StatsSet hero = new StatsSet(); + int charId = rset.getInt(Olympiad.CHAR_ID); + hero.set(Olympiad.CHAR_NAME, rset.getString(Olympiad.CHAR_NAME)); + hero.set(Olympiad.CLASS_ID, rset.getInt(Olympiad.CLASS_ID)); + hero.set(COUNT, rset.getInt(COUNT)); + hero.set(PLAYED, rset.getInt(PLAYED)); + + statement2.setInt(1, charId); + rset2 = statement2.executeQuery(); + + if (rset2.next()) + { + int clanId = rset2.getInt("clanid"); + int allyId = rset2.getInt("allyId"); + + String clanName = ""; + String allyName = ""; + int clanCrest = 0; + int allyCrest = 0; + + if (clanId > 0) + { + clanName = ClanTable.getInstance().getClan(clanId).getName(); + clanCrest = ClanTable.getInstance().getClan(clanId).getCrestId(); + + if (allyId > 0) + { + allyName = ClanTable.getInstance().getClan(clanId).getAllyName(); + allyCrest = ClanTable.getInstance().getClan(clanId).getAllyCrestId(); + } + } + + hero.set(CLAN_CREST, clanCrest); + hero.set(CLAN_NAME, clanName); + hero.set(ALLY_CREST, allyCrest); + hero.set(ALLY_NAME, allyName); + } + + rset2.close(); + statement2.clearParameters(); + + _completeHeroes.put(charId, hero); + } + + statement2.close(); + rset.close(); + statement.close(); + } + catch (SQLException e) + { + _log.warning("Hero System: Couldnt load Heroes"); + if (Config.DEBUG) + { + _log.log(Level.WARNING, "", e); + } + } + finally + { + L2DatabaseFactory.close(con); + } + + _log.info("Hero System: Loaded " + _heroes.size() + " Heroes."); + _log.info("Hero System: Loaded " + _completeHeroes.size() + " all time Heroes."); + } + + private String calcFightTime(long FightTime) + { + String format = String.format("%%0%dd", 2); + FightTime = FightTime / 1000; + String seconds = String.format(format, FightTime % 60); + String minutes = String.format(format, (FightTime % 3600) / 60); + String time = minutes + ":" + seconds; + return time; + } + + /** + * Restore hero message from Db. + * @param charId + */ + public void loadMessage(int charId) + { + Connection con = null; + try + { + String message = null; + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT message FROM heroes WHERE charId=?"); + statement.setInt(1, charId); + ResultSet rset = statement.executeQuery(); + rset.next(); + message = rset.getString("message"); + _heroMessage.put(charId, message); + rset.close(); + statement.close(); + } + catch (SQLException e) + { + _log.log(Level.WARNING, "Hero System: Couldnt load Hero Message for CharId: " + charId, e); + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void loadDiary(int charId) + { + _diary = new FastList<>(); + + int diaryentries = 0; + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT * FROM heroes_diary WHERE charId=? ORDER BY time ASC"); + statement.setInt(1, charId); + ResultSet rset = statement.executeQuery(); + + while (rset.next()) + { + StatsSet _diaryentry = new StatsSet(); + + long time = rset.getLong("time"); + int action = rset.getInt("action"); + int param = rset.getInt("param"); + + String date = (new SimpleDateFormat("yyyy-MM-dd HH")).format(new Date(time)); + _diaryentry.set("date", date); + + if (action == ACTION_RAID_KILLED) + { + L2NpcTemplate template = NpcTable.getInstance().getTemplate(param); + if (template != null) + { + _diaryentry.set("action", template.getName() + " was defeated"); + } + } + else if (action == ACTION_HERO_GAINED) + { + _diaryentry.set("action", "Gained Hero status"); + } + else if (action == ACTION_CASTLE_TAKEN) + { + Castle castle = CastleManager.getInstance().getCastleById(param); + if (castle != null) + { + _diaryentry.set("action", castle.getName() + " Castle was successfuly taken"); + } + } + _diary.add(_diaryentry); + diaryentries++; + } + rset.close(); + statement.close(); + + _herodiary.put(charId, _diary); + + _log.info("Hero System: Loaded " + diaryentries + " diary entries for Hero: " + CharNameTable.getInstance().getNameById(charId)); + } + catch (SQLException e) + { + _log.warning("Hero System: Couldnt load Hero Diary for CharId: " + charId); + if (Config.DEBUG) + { + _log.log(Level.WARNING, "", e); + } + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public void loadFights(int charId) + { + _fights = new FastList<>(); + + StatsSet _herocountdata = new StatsSet(); + + Calendar _data = Calendar.getInstance(); + _data.set(Calendar.DAY_OF_MONTH, 1); + _data.set(Calendar.HOUR_OF_DAY, 0); + _data.set(Calendar.MINUTE, 0); + _data.set(Calendar.MILLISECOND, 0); + + long from = _data.getTimeInMillis(); + int numberoffights = 0; + int _victorys = 0; + int _losses = 0; + int _draws = 0; + + Connection con = null; + try + { + con = L2DatabaseFactory.getInstance().getConnection(); + PreparedStatement statement = con.prepareStatement("SELECT * FROM olympiad_fights WHERE (charOneId=? OR charTwoId=?) AND startvictory"); + _victorys++; + } + else if (winner == 2) + { + fight.set("result", "loss"); + _losses++; + } + else if (winner == 0) + { + fight.set("result", "draw"); + _draws++; + } + + _fights.add(fight); + + numberoffights++; + } + } + else if (charId == charTwoId) + { + String name = CharNameTable.getInstance().getNameById(charOneId); + String cls = CharTemplateTable.getInstance().getClassNameById(charOneClass); + if ((name != null) && (cls != null)) + { + StatsSet fight = new StatsSet(); + fight.set("oponent", name); + fight.set("oponentclass", cls); + + fight.set("time", calcFightTime(time)); + String date = (new SimpleDateFormat("yyyy-MM-dd HH:mm")).format(new Date(start)); + fight.set("start", date); + + fight.set("classed", classed); + if (winner == 1) + { + fight.set("result", "loss"); + _losses++; + } + else if (winner == 2) + { + fight.set("result", "victory"); + _victorys++; + } + else if (winner == 0) + { + fight.set("result", "draw"); + _draws++; + } + + _fights.add(fight); + + numberoffights++; + } + } + } + rset.close(); + statement.close(); + + _herocountdata.set("victory", _victorys); + _herocountdata.set("draw", _draws); + _herocountdata.set("loss", _losses); + + _herocounts.put(charId, _herocountdata); + _herofights.put(charId, _fights); + + _log.info("Hero System: Loaded " + numberoffights + " fights for Hero: " + CharNameTable.getInstance().getNameById(charId)); + } + catch (SQLException e) + { + _log.warning("Hero System: Couldnt load Hero fights history for CharId: " + charId); + if (Config.DEBUG) + { + _log.log(Level.WARNING, "", e); + } + } + finally + { + L2DatabaseFactory.close(con); + } + } + + public Map getHeroes() + { + return _heroes; + } + + public int getHeroByClass(int classid) + { + if (!_heroes.isEmpty()) + { + for (Integer heroId : _heroes.keySet()) + { + StatsSet hero = _heroes.get(heroId); + if (hero.getInteger(Olympiad.CLASS_ID) == classid) + { + return heroId; + } + } + } + return 0; + } + + public void resetData() + { + _herodiary.clear(); + _herofights.clear(); + _herocounts.clear(); + _heroMessage.clear(); + } + + public void showHeroDiary(L2PcInstance activeChar, int heroclass, int charid, int page) + { + final int perpage = 10; + + if (_herodiary.containsKey(charid)) + { + List _mainlist = _herodiary.get(charid); + NpcHtmlMessage DiaryReply = new NpcHtmlMessage(5); + final String htmContent = HtmCache.getInstance().getHtm(activeChar.getHtmlPrefix(), "data/html/olympiad/herodiary.htm"); + if (htmContent != null) + { + DiaryReply.setHtml(htmContent); + DiaryReply.replace("%heroname%", CharNameTable.getInstance().getNameById(charid)); + DiaryReply.replace("%message%", _heroMessage.get(charid)); + DiaryReply.disableValidation(); + + if (!_mainlist.isEmpty()) + { + FastList _list = FastList.newInstance(); + _list.addAll(_mainlist); + Collections.reverse(_list); + + boolean color = true; + final StringBuilder fList = new StringBuilder(500); + int counter = 0; + int breakat = 0; + for (int i = ((page - 1) * perpage); i < _list.size(); i++) + { + breakat = i; + StatsSet _diaryentry = _list.get(i); + StringUtil.append(fList, ""); + if (color) + { + StringUtil.append(fList, ""); + } + else + { + StringUtil.append(fList, "
    "); + } + StringUtil.append(fList, ""); + StringUtil.append(fList, ""); + StringUtil.append(fList, "
    " + _diaryentry.getString("date") + ":xx
    " + _diaryentry.getString("action") + "
     
    "); + StringUtil.append(fList, ""); + color = !color; + counter++; + if (counter >= perpage) + { + break; + } + } + + if (breakat < (_list.size() - 1)) + { + DiaryReply.replace("%buttprev%", "