Merge branch 'master' into resource.

This commit is contained in:
Ryley Kimmel
2015-03-01 11:07:26 -05:00
48 changed files with 1052 additions and 265 deletions
+1
View File
@@ -6,3 +6,4 @@
/data/fs
/data/savedGames
/bin/
/lib/
+12 -27
View File
@@ -3,8 +3,20 @@ require 'java'
java_import 'org.apollo.game.message.impl.DisplayCrossbonesMessage'
java_import 'org.apollo.game.model.entity.Player'
# Registers an area action.
def area_action(name, &block)
AREA_ACTIONS[name] = action = AreaAction.new
action.instance_eval(&block)
end
AREA_ACTIONS = {}
private
# An action that is called when a player enters or exits an area.
class AreaAction
@@ -38,31 +50,4 @@ class AreaAction
@on_exit.call(player) unless @on_exit.nil?
end
end
# Registers an area action.
def area_action(name, &block)
AREA_ACTIONS[name] = action = AreaAction.new
action.instance_eval(&block)
end
# Defines the pvp area action.
area_action :pvp do
on_entry { |player| player.in_pvp = true }
on_exit { |player| player.in_pvp = false }
end
# Defines the wilderness area action.
area_action :wilderness do
on_entry do |player|
player.send(DisplayCrossbonesMessage.new(true))
player.in_wilderness = true
end
on_exit do |player|
player.send(DisplayCrossbonesMessage.new(false))
player.in_wilderness = false
end
end
+70 -16
View File
@@ -1,11 +1,26 @@
require 'java'
java_import 'org.apollo.game.model.Position'
java_import 'org.apollo.game.model.entity.Entity$EntityType'
java_import 'org.apollo.game.model.entity.Player'
# Todo make 0 the default height
# Creates a new area and registers it with the supplied coordinates.
def area(hash)
raise 'Hash must contain a name, coordinates, and actions pair.' unless hash.has_keys?(:name, :coordinates, :actions)
name = hash[:name]; coordinates = hash[:coordinates]; actions = hash[:actions]
actions = [ actions ] if actions.is_a?(Symbol)
actions.map! { |action| AREA_ACTIONS[action]}
@areas << Area.new(name, coordinates, actions)
end
private
# A map of coordinates (as an array) to areas.
AREAS = []
@areas = []
# An area of the game world.
class Area
@@ -16,31 +31,70 @@ class Area
@actions = actions
end
# Called when the player has entered the area.
def entered(player)
actions.each { |action| action.entered(player) }
def min_x() # TODO better data structure and methods than this
@coordinates[0]
end
# Called whilst the player is inside the area.
def min_y()
@coordinates[1]
end
def max_x()
@coordinates[2]
end
def max_y()
@coordinates[3]
end
def height()
@coordinates[4]
end
# Called when the player has entered the area.
def entered(player)
@actions.each { |action| action.entered(player) }
end
# Called when the player has moved, but is still inside the area (and was in the area before).
def inside(player)
acttions.each { |action| action.inside(player) }
@actions.each { |action| action.inside(player) }
end
# Called when the player has exited the area.
def exited(player)
actions.each { |action| action.exited(player) }
@actions.each { |action| action.exited(player) }
end
end
# Creates a new area and registers it with the supplied coordinates.
def area(hash)
raise 'Hash must contain a name, coordinates, and actions pair.' unless hash.has_keys?(:name, :coordinates, :actions)
name = hash[:name]; coordinates = hash[:coordinates]; actions = hash[:actions]
# Listen for the MobPositionUpdateEvent and update the area listeners if appropriate.
on :mob_position_update do |event|
mob = event.mob
next unless mob.entity_type == EntityType::PLAYER
AREAS << Area.new(name, coordinates, actions.is_a?(Symbol) ? [actions] : actions)
old = mob.position
@areas.each do |area|
was_inside = old.inside(area)
next_inside = event.next.inside(area)
if was_inside
if next_inside then area.inside(mob) else area.exited(mob) end
else
area.entered(mob) if next_inside
end
end
end
# Coordinates refer to the bottom-left position (min_x, min_y) and the top-right position (max_x, max_y), followed by the height (optional).
area :name => :wilderness, :coordinates => [ 2944, 3520, 3392, 6400, 0 ], :actions => [ :pvp, :multicombat, :wilderness ]
area :name => :duel_arena, :coordinates => [ 3327, 3200, 3392, 3286 ], :actions => :pvp
# The existing Position class.
class Position
# Returns whether or not this Position is inside the specified Area.
def inside(area)
return false if (x < area.min_x() || x > area.max_x() || y < area.min_y() || y > area.max_y())
z = area.height()
return true if (z.nil? || z == height)
end
end
+1 -1
View File
@@ -20,7 +20,7 @@ java_import 'org.apollo.game.model.World'
java_import 'org.apollo.game.model.entity.Player'
java_import 'org.apollo.game.model.event.EventListener'
java_import 'org.apollo.game.model.event.PlayerEvent'
java_import 'org.apollo.game.model.setting.PrivilegeLevel'
java_import 'org.apollo.game.model.entity.setting.PrivilegeLevel'
java_import 'org.apollo.game.scheduling.ScheduledTask'
java_import 'org.apollo.util.plugin.PluginContext'
+1 -1
View File
@@ -1,6 +1,6 @@
require 'java'
java_import 'org.apollo.game.model.setting.PrivacyState'
java_import 'org.apollo.game.model.entity.setting.PrivacyState'
java_import 'org.apollo.game.message.impl.SendFriendMessage'
on :message, :privacy_option do |ctx, player, message|
@@ -4,8 +4,8 @@ java_import 'org.apollo.game.message.impl.FriendServerStatusMessage'
java_import 'org.apollo.game.message.impl.IgnoreListMessage'
java_import 'org.apollo.game.message.impl.SendFriendMessage'
java_import 'org.apollo.game.model.World'
java_import 'org.apollo.game.model.setting.ServerStatus'
java_import 'org.apollo.game.model.setting.PrivacyState'
java_import 'org.apollo.game.model.entity.setting.ServerStatus'
java_import 'org.apollo.game.model.entity.setting.PrivacyState'
java_import 'org.apollo.game.model.entity.Player'
@@ -2,7 +2,7 @@ require 'java'
java_import 'org.apollo.game.message.impl.ForwardPrivateChatMessage'
java_import 'org.apollo.game.model.World'
java_import 'org.apollo.game.model.setting.PrivacyState'
java_import 'org.apollo.game.model.entity.setting.PrivacyState'
on :message, :private_chat do |ctx, player, message|
friend = $world.get_player(message.username)
@@ -11,9 +11,9 @@ end
# Checks if the sender is permitted to interact with the friend they have added:
def interaction_permitted(sender, friend)
if friend == nil || friend.has_ignored(sender.username)
if friend.nil? || friend.has_ignored(sender.username)
return false
end
return friend.friends_with(sender.username) ? friend.friend_privacy != PrivacyState::OFF : friend.friend_privacy == PrivacyState::ON
return friend.friends_with(sender.username) ? (friend.friend_privacy != PrivacyState::OFF) : (friend.friend_privacy == PrivacyState::ON)
end
+183 -110
View File
@@ -7,26 +7,65 @@ java_import 'org.apollo.game.message.impl.SetWidgetNpcModelMessage'
java_import 'org.apollo.game.message.impl.SetWidgetPlayerModelMessage'
java_import 'org.apollo.game.message.impl.SetWidgetTextMessage'
# Defines a dialogue, with the specified name and block.
def dialogue(name, &block)
raise 'Dialogues must have a name and block.' if (name.nil? || block.nil?)
# The map of conversation names to Conversations.
CONVERSATIONS = {}
dialogue = Dialogue.new(name)
dialogue.instance_eval(&block)
dialogue.wrap
DIALOGUES[name] = dialogue
# Declares a conversation.
def conversation(name, &block)
conversation = Conversation.new(name)
conversation.instance_eval(&block)
raise "Conversation named #{name} already exists." if CONVERSATIONS.has_key?(name)
CONVERSATIONS[name] = conversation
end
# Defines an opening (i.e. conversation starter) dialogue, which hooks into the chain.
# Allows for a lambda prerequisite to be passed, which takes one argument the player; if the prerequisite evaluates to false, the dialogue will not be opened.
def opening_dialogue(name, prerequisite=nil, &block)
dialogue = dialogue(name, &block)
npc = dialogue.npc
raise 'Npc cannot be null when opening a dialogue.' if npc.nil?
# A conversation held between two entities.
class Conversation
on :message, :first_npc_action, npc do |ctx, player, event|
player.open_dialogue(name) if (prerequisite.nil? || prerequisite.call(player))
# Creates the Conversation.
def initialize(name)
@dialogues = {}
@starters = []
@name = name
end
# Defines a dialogue, with the specified name and block.
def dialogue(name, &block)
raise 'Dialogues must have a name and block.' if (name.nil? || block.nil?)
dialogue = Dialogue.new(name, self)
dialogue.instance_eval(&block)
dialogue.wrap
raise "Conversations #{@name} already has a dialogue named #{name}." if @dialogues.has_key?(name)
@dialogues[name] = dialogue
if ((@dialogues.empty? || dialogue.has_precondition?) && dialogue.type == :npc_speech)
npc = dialogue.npc
raise 'Npc cannot be null when opening a dialogue.' if npc.nil?
@starters << dialogue
on :message, :first_npc_action do |ctx, player, event|
if npc == $world.npc_repository.get(event.index).id
@starters.each do |start|
if dialogue.precondition(player)
send_dialogue(player, dialogue)
ctx.break_handler_chain()
break
end
end
end
end
end
end
# Gets part of a conversation (i.e. a dialogue).
def part(name)
raise "Conversation #{@name} does not contain a dialogue called #{name}." unless @dialogues.has_key?(name)
@dialogues[name]
end
end
# Declares an emote, with the specified name and id.
@@ -35,12 +74,26 @@ def declare_emote(name, id)
end
# Sends the specified dialogue.
def send_dialogue(player, dialogue)
type = dialogue.type
action = dialogue.action
action.call(player) unless action.nil?
case type
when :message_with_item then send_item_dialogue(player, dialogue)
when :message_with_model then send_model_dialogue(player, dialogue)
when :npc_speech then send_npc_dialogue(player, dialogue)
when :options then send_options_dialogue(player, dialogue)
when :player_speech then send_player_dialogue(player, dialogue)
when :text
if dialogue.has_continue? then send_text_dialogue(player, dialogue) else send_statement_dialogue(player, dialogue) end
else raise "Unrecognised dialogue type #{type}."
end
end
private
# The hash of dialogue names to dialogues.
DIALOGUES = {}
# The hash of emote names to ids.
EMOTES = {}
@@ -50,8 +103,11 @@ MAXIMUM_LINE_COUNT = 4
# The maximum amount of options that can be displayed on a dialogue.
MAXIMUM_OPTION_COUNT = 5
# The maximum width of a line, in characters.
MAXIMUM_LINE_WIDTH = 55
# The maximum width of a line, in pixels, for a dialogue with media.
MAXIMUM_MEDIA_LINE_WIDTH = 350
# The maximum width of a line, in pixels, for a dialogue with no media.
MAXIMUM_LINE_WIDTH = 430
# The possible types of a dialogue.
DIALOGUE_TYPES = [ :message_with_item, :message_with_model, :npc_speech, :options, :player_speech, :text ]
@@ -61,12 +117,19 @@ class Dialogue
attr_reader :emote, :name, :media, :options, :text, :title, :type
# Initializes the Dialogue.
def initialize(name)
def initialize(name, conversation)
@name = name.to_s
@conversation = conversation
@text = []
@options = []
end
# An action that is executed when the dialogue is displayed.
def action(&block)
@action = block unless block.nil?
@action
end
# Closes the dialogue interface when the player clicks the 'Click here to continue...' text.
def close
continue(:close => true)
@@ -77,16 +140,41 @@ class Dialogue
raise 'Cannot add a continue event on a dialogue with options.' unless @options.size.zero?
raise 'Must declare either a type or a block for a continue event.' if (type.nil? && block.nil?)
@options << (block.nil? ? get_next_dialogue(type) : block)
action = get_next_dialogue(type) unless type.nil?
@options << ->(player) { action.call(player) unless type.nil?; block.call(player) unless block.nil? }
end
# Copies the value of every variable from the specified Dialogue, optionally updating the text array.
def copy_from(dialogue, text=nil)
@emote = dialogue.emote
@item = dialogue.item
@model = dialogue.model
@npc = dialogue.npc
@options = dialogue.options
@text = if text.nil? then dialogue.text.dup else text.dup end
@type = dialogue.type
end
# Sets the emote performed by the dialogue head.
def emote(emote=nil)
raise 'Can only perform an emote on :player_speech or :npc_speech dialogues.' unless [ :npc_speech, :player_speech ].include?(@type)
@emote = EMOTES[emote] if emote.kind_of?(Symbol)
unless emote.nil?
raise 'Can only perform an emote on :player_speech or :npc_speech dialogues.' unless [ :npc_speech, :player_speech ].include?(@type)
@emote = emote.kind_of?(Symbol) ? EMOTES[emote] : emote
end
@emote
end
# Returns whether or not this Dialogue has a continue option.
def has_continue?
!@options.empty?
end
# Returns whether or not this dialogue has a precondition.
def has_precondition?
!@precondition.nil?
end
# Gets the media of this dialogue.
def media()
case @type
@@ -120,8 +208,10 @@ class Dialogue
# Sets the id of the npc displayed.
def npc(npc=nil)
raise 'Can only display an npc on :npc_speech dialogues.' unless @type == :npc_speech
@npc = lookup_npc(npc) unless npc.nil?
unless npc.nil?
raise 'Can only display an npc on :npc_speech dialogues.' unless @type == :npc_speech
@npc = lookup_npc(npc)
end
@npc
end
@@ -136,15 +226,18 @@ class Dialogue
# Gets the array of options.
def options
@options.dup
@options.dup
end
# Sets the precondition of this dialogue.
def precondition(player=nil, &block)
@precondition = block unless block.nil?
@precondition.call(player) unless player.nil?
end
# Appends a message to the text list.
def text(*message)
unless message.nil?
@text.concat(message)
end
@text.concat(message) unless message.nil?
@text
end
@@ -165,47 +258,44 @@ class Dialogue
end
# Wraps text in this Dialogue, inserting extra Dialogues in the chain if necessary.
def wrap
lines = []
def wrap # TODO redo this
next if @type == :options
lines = []
maximum_width = (@type == :text) ? MAXIMUM_LINE_WIDTH : MAXIMUM_MEDIA_LINE_WIDTH
maximum_lines = MAXIMUM_LINE_COUNT
text = @text[0]
segments = []# text.chars.each_slice(MAXIMUM_LINE_WIDTH).map(&:join) # Split text into array of strings with length <= 60.
previous = 0; index = MAXIMUM_LINE_WIDTH
text = @text.first
segments = []
index = 0; width = 0; space = 0
while index < text.length
index -= 1 until text[index] == ' '
segments << text[previous..index]
previous = index
index += MAXIMUM_LINE_WIDTH
end
segments << text[previous..text.length]
char = text[index]
space = index if char == ' '
width += get_width(char)
index += 1
if (segments.size <= MAXIMUM_LINE_COUNT)
if (width >= maximum_width)
segments << text[0..space]
text = text[(space + 1)..-1]
width = index = space = 0
end
end
segments << text
if (segments.size <= maximum_lines)
lines.concat(segments)
@text = @text.drop(1)
@text = @text[1..-1]
insert_copy(@text) if @text.size > 0
else
remaining = MAXIMUM_LINE_COUNT - segments.size
lines.concat(segments.first(remaining))
insert_copy(segments.drop(remaining).join().concat(@text.drop(1)))
lines.concat(segments.first(maximum_lines))
segments = [ segments.drop(maximum_lines).join() ]
insert_copy(segments << @text[1..-1].join())
end
@text = lines
end
# Copies the value of every variable from the specified Dialogue, optionally updating the text array.
def copy_from(dialogue, text=nil)
@emote = dialogue.emote
@item = dialogue.item
@model = dialogue.model
@npc = dialogue.npc
@options = dialogue.options
@text = if text.nil? then dialogue.text.dup else text.dup end
@type = dialogue.type
end
private
# Inserts a copy of this Dialogue into the chain, but with different text.
@@ -217,22 +307,20 @@ class Dialogue
index ||= -1
name = "#{name[0..index]}-auto-inserted-#{id}"
dialogue = Dialogue.new(name)
dialogue = Dialogue.new(name, @conversation)
dialogue.copy_from(self, text.dup)
dialogue.wrap()
DIALOGUES[name] = dialogue
@options[0] = ->(player) { send_dialogue(player, dialogue) }
end
# Decodes the next dialogue interface from the hash, returning a proc.
def get_next_dialogue(hash)
hash.keys.each do |key|
def get_next_dialogue(hash) # TODO rename
hash.each_pair do |key, value|
case key
when :close
return ->(player) { player.send(CloseInterfaceMessage.new) }
when :dialogue
return ->(player) { send_dialogue(player, lookup_dialogue(hash[key])) }
when :disabled then return ->(player) { }
when :close then return ->(player) { player.send(CloseInterfaceMessage.new) }
when :dialogue then return ->(player) { send_dialogue(player, @conversation.part(value)) }
else raise "Unrecognised dialogue continue type #{key}."
end
end
@@ -240,41 +328,11 @@ class Dialogue
end
# The existing Player class.
class Player
# The dialogue interface ids for dialogues that only display text, but with no 'Click here to continue...' message.
STATEMENT_DIALOGUE_IDS = [ 12788, 12790, 12793, 12797, 6179 ] # TODO
# Opens the dialogue with the specified name.
def open_dialogue(name)
dialogue = lookup_dialogue(name)
send_dialogue(self, dialogue)
end
end
# Gets a Dialogue using the name it was registered with.
def lookup_dialogue(name)
dialogue = DIALOGUES[name]
raise "No dialogue named #{name.to_s}." if dialogue.nil?
dialogue
end
# Sends the specified dialogue.
def send_dialogue(player, dialogue)
type = dialogue.type
case type
when :message_with_item then send_item_dialogue(player, dialogue)
when :message_with_model then send_model_dialogue(player, dialogue)
when :npc_speech then send_npc_dialogue(player, dialogue)
when :options then send_options_dialogue(player, dialogue)
when :player_speech then send_player_dialogue(player, dialogue)
when :text then send_text_dialogue(player, dialogue)
else raise "Unrecognised dialogue type #{type}."
end
end
# The dialogue interface ids for dialogues that display an item and text, ordered by line count.
ITEM_DIALOGUE_IDS = [ 306, 310, 315, 321 ]
# The dialogue interface ids for dialogues that only display text, ordered by line count.
TEXT_DIALOGUE_IDS = [ 356, 359, 363, 368, 374 ]
@@ -288,6 +346,21 @@ NPC_DIALOGUE_IDS = [ 4882, 4887, 4893, 4900 ]
# The dialogue interface ids for option dialogues, ordered by (option_count - 1)
OPTIONS_DIALOGUE_IDS = [ 2459, 2469, 2480, 2492 ]
## TODO separate this into different Dialogue types ##
# Sends a dialogue displaying only text, with no 'Click here to continue...' button.
def send_statement_dialogue(player, dialogue)
text = dialogue.text
dialogue_id = STATEMENT_DIALOGUE_IDS[text.size]
set_text(player, dialogue_id + 1, dialogue.title)
text.each_with_index { |line, index| set_text(player, dialogue_id + 2 + index, line) }
player.interface_set.open_dialogue_overlay(dialogue_id)
end
# Sends a dialogue displaying only text.
def send_text_dialogue(player, dialogue)
title = dialogue.title
@@ -296,7 +369,7 @@ end
# Sends a dialogue displaying the player's head.
def send_player_dialogue(player, dialogue)
send_generic_dialogue(player, dialogue, PLAYERS_DIALOGUE_IDS, ->(id) { SetWidgetPlayerModelMessage.new(id + 1) })
send_generic_dialogue(player, dialogue, player.username, PLAYER_DIALOGUE_IDS, ->(id) { SetWidgetPlayerModelMessage.new(id + 1) })
end
# Sends a dialogue displaying the head of an npc.
@@ -308,7 +381,6 @@ def send_npc_dialogue(player, dialogue)
send_generic_dialogue(player, dialogue, name, NPC_DIALOGUE_IDS, ->(id) { SetWidgetNpcModelMessage.new(id + 1, npc)})
end
# Sends a dialogue displaying an event.
def send_generic_dialogue(player, dialogue, title, ids, event=nil)
text = dialogue.text
@@ -317,8 +389,8 @@ def send_generic_dialogue(player, dialogue, title, ids, event=nil)
set_text(player, dialogue_title_id(dialogue_id), title)
text.each_index { |index| set_text(player, dialogue_text_id(dialogue_id, index), text[index]) }
player.interface_set.open_dialogue(ContinueDialogueAdapter.new(player, dialogue.options[0]), dialogue_id) # TODO listener!!!
text.each_with_index { |line, index| set_text(player, dialogue_text_id(dialogue_id, index), line) }
player.interface_set.open_dialogue(ContinueDialogueAdapter.new(player, dialogue.options[0]), dialogue_id)
end
@@ -334,8 +406,8 @@ def send_options_dialogue(player, dialogue)
question = dialogue.title
set_text(player, dialogue_question_id(dialogue_id), question)
text.each_index { |index| set_text(player, dialogue_option_id(dialogue_id, index), text[index]) }
player.interface_set.open_dialogue(OptionDialogueAdapter.new(player, options), dialogue_id) # TODO listener!!!
text.each_with_index { |line, index| set_text(player, dialogue_option_id(dialogue_id, index), line) }
player.interface_set.open_dialogue(OptionDialogueAdapter.new(player, options), dialogue_id)
end
@@ -344,7 +416,7 @@ class ContinueDialogueAdapter < DialogueAdapter
# Creates the ContinueDialogueAdadpter.
def initialize(player, continue)
super()
super()
@player = player
@continue = continue
end
@@ -362,7 +434,7 @@ class OptionDialogueAdapter < DialogueAdapter
# Creates the OptionDialogueAdadpter.
def initialize(player, options)
super()
super()
@player = player
@options = options.dup
end
@@ -421,6 +493,7 @@ GLYPH_SPACING = [ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
8, 8, 8, 8, 8, 8, 8, 8, 8, 13, 6, 8, 8, 8, 8, 4, 4, 5, 4, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 ]
# Gets the width of a single character.
def get_width(char)
return GLYPH_SPACING[char.ord]
end
+20 -14
View File
@@ -1,6 +1,8 @@
require 'java'
java_import 'org.apollo.game.model.entity.Entity'
java_import 'org.apollo.game.model.entity.Mob'
java_import 'org.apollo.game.model.entity.Npc'
java_import 'org.apollo.game.model.entity.Player'
java_import 'org.apollo.game.model.entity.attr.Attribute'
java_import 'org.apollo.game.model.entity.attr.AttributeDefinition'
java_import 'org.apollo.game.model.entity.attr.AttributeMap'
@@ -10,7 +12,18 @@ java_import 'org.apollo.game.model.entity.attr.BooleanAttribute'
java_import 'org.apollo.game.model.entity.attr.NumericalAttribute'
java_import 'org.apollo.game.model.entity.attr.StringAttribute'
class Entity
# Declares an attribute and adds its definition.
def declare_attribute(name, default, persistence=:transient)
raise "Attribute #{name} clashes with an existing variable." if (Player.method_defined?(name) || Mob.method_defined?(name) || Npc.method_defined?(name))
AttributeMap::add_definition(name.to_s, AttributeDefinition.new(default, get_persistence(persistence), get_type(default)))
end
private
# The existing Mob class.
class Mob
# Overrides method_missing
def method_missing(symbol, *args)
@@ -21,18 +34,17 @@ class Entity
name = name[0...-1].strip # Drop the equals
set_attribute(name, to_attribute(args[0]))
elsif AttributeMap::get_definition(name) == nil
elsif AttributeMap::get_definition(name).nil?
super(symbol, *args)
else
attribute = get_attribute(name); definition = AttributeMap::get_definition(name)
value = attribute == nil ? definition.default : attribute.value
value = attribute.nil? ? definition.default : attribute.value
return definition.type == AttributeType::SYMBOL ? value.to_sym : value
return (definition.type == AttributeType::SYMBOL) ? value.to_sym : value
end
end
# Gets the appropriate attribute for the specified value.
private
def to_attribute(value)
case value
when String, Symbol then return StringAttribute.new(value.to_s, value.is_a?(Symbol))
@@ -44,13 +56,7 @@ class Entity
end
# Declares an attribute and adds its definition.
def declare_attribute(name, default, persistence=:transient)
AttributeMap::add_definition(name.to_s, AttributeDefinition.new(default, get_persistence(persistence), get_type(default)))
end
# Gets the attribute type of the specified value.
private
def get_type(value)
case value
when String then return AttributeType::STRING
@@ -64,7 +70,7 @@ end
# Gets the Persistence type of the specified value.
def get_persistence(persistence)
raise "Undefined persistence type #{persistence}." unless persistence == :serialized || persistence == :transient
raise "Undefined persistence type #{persistence}." unless [ :persistent, :transient ].include?(persistence)
return persistence == :serialized ? AttributePersistence::SERIALIZED : AttributePersistence::TRANSIENT
return (persistence == :persistent) ? AttributePersistence::SERIALIZED : AttributePersistence::TRANSIENT
end
+5 -2
View File
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<plugin>
<id>skill-prayer</id>
<version>1</version>
<version>0.9</version>
<name>Prayer</name>
<description>Adds the Prayer skill.</description>
<authors>
@@ -10,6 +10,9 @@
</authors>
<scripts>
<script>bury.rb</script>
<script>prayers.rb</script>
</scripts>
<dependencies />
<dependencies>
<dependency>attributes</dependency>
</dependencies>
</plugin>
+89
View File
@@ -0,0 +1,89 @@
java_import 'org.apollo.game.message.impl.ConfigMessage'
# Declares the active prayer attribute.
declare_attribute(:active_prayer, -1, :persistent)
# The hash of button ids to prayers.
PRAYERS = {}
# Intercept the ButtonMessage to toggle a prayer.
on :message, :button do |ctx, player, message|
button = message.widget_id
prayer = PRAYERS[button]
unless prayer.nil?
if (prayer.level > player.skill_set.get_maximum_level(Skill::PRAYER))
update_setting(player, prayer, :off)
next
end
player.send_message("after level check")
previous = player.active_prayer
unless previous == -1
update_setting(player, PRAYERS[previous], :off)
end
if previous != button
player.send_message("Previous: #{previous}, new: #{button}.")
update_setting(player, prayer, :on)
player.active_prayer = button
end
end
end
private
# A Prayer that can be activated by a player.
class Prayer
attr_reader :name, :level, :button, :setting, :drain
def initialize(name, level, button, setting, drain)
@name = name
@level = level
@button = button
@setting = setting
@drain = drain
end
end
def update_setting(player, prayer, state)
value = (state == :on) ? 1 : 0
player.send_message("Toggling prayer #{prayer.name}, state: #{state}.")
player.send(ConfigMessage.new(prayer.setting, value))
end
# Appends a Prayer to the hash.
def append_prayer(properties)
raise 'Error: prayer properties hash must contain a name, level, button, setting, and drain.' unless properties.has_keys?(:name, :level, :button, :setting, :drain)
button = properties[:button]
PRAYERS[button] = Prayer.new(properties[:name], properties[:level], button, properties[:setting], properties[:drain])
end
# Don't deal with the actual effect here to avoid mess (TODO do it, but with attributes?).
append_prayer name: :thick_skin, level: 1, button: 5609, setting: 83, drain: 0.01
append_prayer name: :burst_of_strength, level: 4, button: 5610, setting: 84, drain: 0.01
append_prayer name: :clarity_of_thought, level: 7, button: 5611, setting: 85, drain: 0.01
append_prayer name: :rock_skin, level: 10, button: 5612, setting: 86, drain: 0.04
append_prayer name: :superhuman_strength, level: 13, button: 5613, setting: 87, drain: 0.04
append_prayer name: :improved_reflexes, level: 16, button: 5614, setting: 88, drain: 0.04
append_prayer name: :rapid_restore, level: 19, button: 5615, setting: 89, drain: 0.01
append_prayer name: :rapid_heal, level: 22, button: 5615, setting: 90, drain: 0.01
append_prayer name: :protect_item, level: 25, button: 5617, setting: 91, drain: 0.01
append_prayer name: :steel_skin, level: 28, button: 5618, setting: 92, drain: 0.1
append_prayer name: :ultimate_strength, level: 31, button: 5619, setting: 93, drain: 0.1
append_prayer name: :incredible_reflexes, level: 34, button: 5620, setting: 94, drain: 0.1
append_prayer name: :protect_from_magic, level: 37, button: 5621, setting: 95, drain: 0.15
append_prayer name: :protect_from_missiles, level: 40, button: 5622, setting: 96, drain: 0.15
append_prayer name: :protect_from_melee, level: 43, button: 5633, setting: 97, drain: 0.15
append_prayer name: :retribution, level: 46, button: 683, setting: 98, drain: 0.15
append_prayer name: :redemption, level: 49, button: 684, setting: 99, drain: 0.15
append_prayer name: :smite, level: 52, button: 685, setting: 100, drain: 0.2
+6
View File
@@ -1,3 +1,9 @@
require 'java'
java_import 'org.apollo.game.model.def.ItemDefinition'
java_import 'org.apollo.game.model.def.NpcDefinition'
java_import 'org.apollo.game.model.def.ObjectDefinition'
# Checks whether the amount of arguments provided is correct, sending the player the specified message if not.
def valid_arg_length(args, length, player, message)
@@ -1,7 +1,7 @@
package org.apollo.game.command;
import org.apollo.game.model.entity.Player;
import org.apollo.game.model.setting.PrivilegeLevel;
import org.apollo.game.model.entity.setting.PrivilegeLevel;
/**
* An interface which should be implemented to listen to {@link Command}s.
@@ -5,7 +5,7 @@ import org.apollo.game.message.handler.MessageHandlerContext;
import org.apollo.game.message.impl.PlayerDesignMessage;
import org.apollo.game.model.Appearance;
import org.apollo.game.model.entity.Player;
import org.apollo.game.model.setting.Gender;
import org.apollo.game.model.entity.setting.Gender;
/**
* A {@link MessageHandler} that verifies {@link PlayerDesignMessage}s.
@@ -1,7 +1,7 @@
package org.apollo.game.message.impl;
import org.apollo.game.message.Message;
import org.apollo.game.model.setting.PrivilegeLevel;
import org.apollo.game.model.entity.setting.PrivilegeLevel;
/**
* A {@link Message} sent to the client that forwards a private chat.
@@ -1,7 +1,7 @@
package org.apollo.game.message.impl;
import org.apollo.game.message.Message;
import org.apollo.game.model.setting.ServerStatus;
import org.apollo.game.model.entity.setting.ServerStatus;
/**
* A {@link Message} sent to the client to update the friend server status.
@@ -13,7 +13,7 @@ import org.apollo.game.model.Position;
*/
public final class HintIconMessage extends Message {
// TODO identify the other types and use an enum.
// TODO identify the other types.
/**
* The type of a HintIcon.
@@ -75,6 +75,24 @@ public final class HintIconMessage extends Message {
return new HintIconMessage(Type.PLAYER, Optional.of(index), Optional.empty());
}
/**
* Creates a HintIconMessage that removes the current Npc hint icon.
*
* @return The HintIconMessage.
*/
public static HintIconMessage resetNpc() {
return forNpc(-1);
}
/**
* Creates a HintIconMessage that removes the current Player hint icon.
*
* @return The HintIconMessage.
*/
public static HintIconMessage resetPlayer() {
return forPlayer(-1);
}
/**
* The index of the Mob, if applicable.
*/
@@ -0,0 +1,35 @@
package org.apollo.game.message.impl;
import org.apollo.game.message.Message;
/**
* A {@link Message} sent to the client that opens a dialogue interface (an interface that appears in the chat box).
*
* @author Chris Fletcher
*/
public final class OpenDialogueOverlayMessage extends Message {
/**
* The interface id.
*/
private final int interfaceId;
/**
* Creates a new message with the specified interface id.
*
* @param interfaceId The interface id.
*/
public OpenDialogueOverlayMessage(int interfaceId) {
this.interfaceId = interfaceId;
}
/**
* Gets the interface id.
*
* @return The interface id.
*/
public int getInterfaceId() {
return interfaceId;
}
}
@@ -1,7 +1,7 @@
package org.apollo.game.message.impl;
import org.apollo.game.message.Message;
import org.apollo.game.model.setting.PrivacyState;
import org.apollo.game.model.entity.setting.PrivacyState;
/**
* A {@link Message} sent both by and to the client to update the public chat, private (friend) chat, and trade chat
+1 -1
View File
@@ -1,6 +1,6 @@
package org.apollo.game.model;
import org.apollo.game.model.setting.Gender;
import org.apollo.game.model.entity.setting.Gender;
import com.google.common.base.Preconditions;
+47 -21
View File
@@ -8,7 +8,9 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apollo.game.model.Direction;
import org.apollo.game.model.Position;
import org.apollo.game.model.area.collision.CollisionMatrix;
import org.apollo.game.model.entity.Entity;
import org.apollo.game.model.entity.Entity.EntityType;
@@ -23,7 +25,7 @@ import com.google.common.collect.ImmutableSet;
public final class Sector {
/**
* The width and length of a sector, in tiles.
* The width and length of a Sector, in tiles.
*/
public static final int SECTOR_SIZE = 8;
@@ -33,20 +35,25 @@ public final class Sector {
private static final int DEFAULT_SET_SIZE = 2;
/**
* The sector coordinates of this sector.
* The SectorCoordinates of this Sector.
*/
private final SectorCoordinates coordinates;
/**
* A map of positions to entities in that position.
* The Map of Positions to Entities in that Position.
*/
private final Map<Position, Set<Entity>> entities = new HashMap<>();
/**
* A list of listeners registered to this sector.
* A List of SectorListeners registered to this Sector.
*/
private final List<SectorListener> listeners = new ArrayList<>();
/**
* The CollisionMatrix.
*/
private final CollisionMatrix matrix = new CollisionMatrix(SECTOR_SIZE, SECTOR_SIZE);
/**
* Creates a new sector.
*
@@ -67,28 +74,29 @@ public final class Sector {
}
/**
* Adds a {@link Entity} from to sector. Note that this does not spawn the entity, or do any other action other than
* Adds a {@link Entity} from to sector. Note that this does not spawn the Entity, or do any other action other than
* register it to this sector.
*
* @param entity The entity.
* @throws IllegalArgumentException If the entity does not belong in this sector.
* @param entity The Entity.
* @throws IllegalArgumentException If the Entity does not belong in this sector.
*/
public void addEntity(Entity entity) {
Position position = entity.getPosition();
checkPosition(position);
Set<Entity> local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE));
Set<Entity> local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE));
local.add(entity);
notifyListeners(entity, SectorOperation.ADD);
}
/**
* Checks if this sector contains the specified entity.
* Checks if this sector contains the specified Entity.
* <p>
* This method operates in constant time.
*
* @param entity The entity.
* @return {@code true} if this sector contains the entity, otherwise {@code false}.
* @param entity The Entity.
* @return {@code true} if this sector contains the Entity, otherwise {@code false}.
*/
public boolean contains(Entity entity) {
Position position = entity.getPosition();
@@ -114,12 +122,13 @@ public final class Sector {
* @return The list.
*/
public Set<Entity> getEntities(Position position) {
return ImmutableSet.copyOf(entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE)));
Set<Entity> set = entities.get(position);
return (set == null) ? ImmutableSet.of() : ImmutableSet.copyOf(set);
}
/**
* Gets a shallow copy of the {@link Set} of {@link Entity}s with the specified {@link EntityType}. The returned
* type will be immutable. Type will be inferred from the call, so ensure that the entity type and the reference
* type will be immutable. Type will be inferred from the call, so ensure that the Entity type and the reference
* correspond, or this method will fail at runtime.
*
* @param position The {@link Position} containing the entities.
@@ -127,21 +136,24 @@ public final class Sector {
* @return The set of entities.
*/
public <T extends Entity> Set<T> getEntities(Position position, EntityType type) {
Set<Entity> local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE));
Set<Entity> local = entities.get(position);
if (local == null) {
return ImmutableSet.of();
}
@SuppressWarnings("unchecked")
Set<T> filtered = (Set<T>) local.stream().filter(entity -> entity.getEntityType() == type).collect(Collectors.toSet());
Set<T> filtered = (Set<T>) local.stream().filter(Entity -> Entity.getEntityType() == type).collect(Collectors.toSet());
return ImmutableSet.copyOf(filtered);
}
/**
* Moves the {@link Entity} that was in the specified {@code old} {@link Position}, to the current position of the
* entity.
* Entity.
* <p>
* Both the {@code old} and current positions of the entity must belong to this sector.
* Both the {@code old} and current positions of the Entity must belong to this sector.
*
* @param old The old position of the entity.
* @param entity The entity to move.
* @param old The old position of the Entity.
* @param entity The Entity to move.
* @throws IllegalArgumentException If either of the positions do not belong to this sector.
*/
public void moveEntity(Position old, Entity entity) {
@@ -175,8 +187,8 @@ public final class Sector {
/**
* Removes a {@link Entity} from this sector.
*
* @param entity The entity.
* @throws IllegalArgumentException If the entity does not belong in this sector, or if it was never added.
* @param entity The Entity.
* @throws IllegalArgumentException If the Entity does not belong in this sector, or if it was never added.
*/
public void removeEntity(Entity entity) {
Position position = entity.getPosition();
@@ -191,6 +203,20 @@ public final class Sector {
notifyListeners(entity, SectorOperation.REMOVE);
}
/**
* Returns whether or not an Entity of the specified {@link EntityType type} can traverse the tile at the specified
* coordinate pair.
*
* @param x The x coordinate.
* @param y The y coordinate.
* @param entity The {@link EntityType}.
* @param direction The {@link Direction} the Entity is approaching from.
* @return {@code true} if the tile at the specified coordinate pair is traversable, {@code false} if not.
*/
public boolean traversable(int x, int y, EntityType entity, Direction direction) {
return matrix.traversable(x, y, entity, direction);
}
/**
* Checks that the specified {@link Position} is included in this sector.
*
@@ -0,0 +1,100 @@
package org.apollo.game.model.area.collision;
/**
* A type of flag in a {@link CollisionMatrix}.
*
* @author Major
*/
public enum CollisionFlag {
/**
* The walk north flag.
*/
MOB_NORTH(0),
/**
* The walk east flag.
*/
MOB_EAST(1),
/**
* The walk south flag.
*/
MOB_SOUTH(2),
/**
* The walk west flag.
*/
MOB_WEST(3),
/**
* The projectile north flag.
*/
PROJECTILE_NORTH(4),
/**
* The projectile east flag.
*/
PROJECTILE_EAST(5),
/**
* The projectile south flag.
*/
PROJECTILE_SOUTH(6),
/**
* The projectile west flag.
*/
PROJECTILE_WEST(7);
/**
* Returns an array of CollisionFlags that indicate if a Mob can traverse over a tile.
*
* @return The array of CollisionFlags.
*/
public static CollisionFlag[] mobs() {
return new CollisionFlag[] { MOB_NORTH, MOB_EAST, MOB_SOUTH, MOB_WEST };
}
/**
* Returns an array of CollisionFlags that indicate if a Projectile can traverse over a tile.
*
* @return The array of CollisionFlags.
*/
public static CollisionFlag[] projectiles() {
return new CollisionFlag[] { PROJECTILE_NORTH, PROJECTILE_EAST, PROJECTILE_SOUTH, PROJECTILE_WEST };
}
/**
* The index of the bit this flag is stored in.
*/
private final int bit;
/**
* Creates the CollisionFlag.
*
* @param bit The index of the bit this flag is stored in.
*/
private CollisionFlag(int bit) {
this.bit = bit;
}
/**
* Gets this CollisionFlag, as a {@code byte}.
*
* @return The value, as a {@code byte}.
*/
public byte asByte() {
return (byte) (1 << bit);
}
/**
* Gets the index of the bit this flag is stored in.
*
* @return The index of the bit.
*/
public int getBit() {
return bit;
}
}
@@ -0,0 +1,227 @@
package org.apollo.game.model.area.collision;
import java.util.Arrays;
import org.apollo.game.model.Direction;
import org.apollo.game.model.entity.Entity.EntityType;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
/**
* A 2-dimensional adjacency matrix containing tile collision data.
*
* @author Major
*/
public final class CollisionMatrix {
/**
* Indicates that all types of traversal are allowed.
*/
private static final byte ALL_ALLOWED = 0b0000_0000;
/**
* Indicates that no types of traversal are allowed.
*/
private static final byte ALL_BLOCKED = (byte) 0b1111_1111;
/**
* The length of the matrix.
*/
private final int length;
/**
* The collision matrix, as a {@code byte} array.
*/
private final byte[] matrix;
/**
* The width of the matrix.
*/
private final int width;
/**
* Creates the CollisionMatrix.
*
* @param width The width of the matrix.
* @param length The length of the matrix.
*/
public CollisionMatrix(int width, int length) {
this.width = width;
this.length = length;
matrix = new byte[width * length];
}
/**
* Returns whether or not <strong>all</strong> of the specified {@link CollisionFlag}s are set for the specified
* coordinate pair.
*
* @param x The x coordinate.
* @param y The y coordinate.
* @param flags The CollisionFlags.
* @return {@code true} if all of the CollisionFlags are set, otherwise {@code false}.
*/
public boolean all(int x, int y, CollisionFlag... flags) {
for (CollisionFlag flag : flags) {
if ((get(x, y) & flag.asByte()) == 0) {
return false;
}
}
return true;
}
/**
* Returns whether or not <strong>any</strong> of the specified {@link CollisionFlag}s are set for the specified
* coordinate pair.
*
* @param x The x coordinate.
* @param y The y coordinate.
* @param flags The CollisionFlags.
* @return {@code true} if any of the CollisionFlags are set, otherwise {@code false}.
*/
public boolean any(int x, int y, CollisionFlag... flags) {
for (CollisionFlag flag : flags) {
if ((get(x, y) & flag.asByte()) != 0) {
return true;
}
}
return false;
}
/**
* Completely blocks the tile at the specified coordinate pair.
*
* @param x The x coordinate.
* @param y The y coordinate.
*/
public void block(int x, int y) {
set(x, y, ALL_BLOCKED);
}
/**
* Clears (i.e. sets to {@code false}) the value of the specified {@link CollisionFlag} for the specified coordinate
* pair.
*
* @param x The x coordinate.
* @param y The y coordinate.
* @param flag The CollisionFlag.
*/
public void clear(int x, int y, CollisionFlag flag) {
set(x, y, (byte) ~flag.asByte());
}
/**
* Returns whether or not the specified {@link CollisionFlag} is set for the specified coordinate pair.
*
* @param x The x coordinate.
* @param y The y coordinate.
* @param flag The CollisionFlag.
* @return {@code true} if the CollisionFlag is set, {@code false} if not.
*/
public boolean flagged(int x, int y, CollisionFlag flag) {
return (get(x, y) & flag.asByte()) != 0;
}
/**
* Gets the value of the specified tile.
*
* @param x The x coordinate of the tile.
* @param y The y coordinate of the tile.
* @return The value.
*/
public int get(int x, int y) {
return matrix[indexOf(x, y)] & 0xFF;
}
/**
* Resets the cell of the specified coordinate pair.
*
* @param x The x coordinate.
* @param y The y coordinate.
*/
public void reset(int x, int y) {
set(x, y, ALL_ALLOWED);
}
/**
* Sets (i.e. sets to {@code true}) the value of the specified {@link CollisionFlag} for the specified coordinate
* pair.
*
* @param x The x coordinate.
* @param y The y coordinate.
* @param flag The CollisionFlag.
*/
public void set(int x, int y, CollisionFlag flag) {
set(x, y, flag.asByte());
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("width", width).add("length", length).add("matrix", Arrays.toString(matrix))
.toString();
}
/**
* Returns whether or not an Entity of the specified {@link EntityType type} can traverse the tile at the specified
* coordinate pair.
*
* @param x The x coordinate.
* @param y The y coordinate.
* @param entity The {@link EntityType}.
* @param direction The {@link Direction} the Entity is approaching from.
* @return {@code true} if the tile at the specified coordinate pair is traversable, {@code false} if not.
*/
public boolean traversable(int x, int y, EntityType entity, Direction direction) {
CollisionFlag[] flags = (entity == EntityType.PROJECTILE) ? CollisionFlag.projectiles() : CollisionFlag.mobs();
int north = 0, east = 1, south = 2, west = 3;
switch (direction) {
case NORTH_WEST:
return any(x, y, flags[south], flags[east]);
case NORTH:
return flagged(x, y, flags[south]);
case NORTH_EAST:
return any(x, y, flags[south], flags[west]);
case EAST:
return flagged(x, y, flags[west]);
case SOUTH_EAST:
return any(x, y, flags[north], flags[west]);
case SOUTH:
return flagged(x, y, flags[north]);
case SOUTH_WEST:
return any(x, y, flags[north], flags[east]);
case WEST:
return flagged(x, y, flags[east]);
}
throw new IllegalArgumentException("Unrecognised direction " + direction + ".");
}
/**
* Gets the index in the matrix for the specified coordinate pair.
*
* @param x The x coordinate.
* @param y The y coordinate.
* @return The index.
* @throws ArrayIndexOutOfBoundsException If the specified coordinate pair does not fit in this matrix.
*/
private int indexOf(int x, int y) {
int index = y * width + x;
Preconditions.checkElementIndex(index, matrix.length, "Index out of bounds.");
return index;
}
/**
* Sets the appropriate index for the specified coordinate pair to the specified value.
*
* @param x The x coordinate.
* @param y The y coordinate.
* @param value The value.
*/
private void set(int x, int y, byte value) {
matrix[indexOf(x, y)] = value;
}
}
@@ -0,0 +1,4 @@
/**
* Contains classes related to tile collision data.
*/
package org.apollo.game.model.area.collision;
+5 -1
View File
@@ -16,6 +16,7 @@ import org.apollo.game.model.area.SectorRepository;
import org.apollo.game.model.def.NpcDefinition;
import org.apollo.game.model.entity.attr.Attribute;
import org.apollo.game.model.entity.attr.AttributeMap;
import org.apollo.game.model.event.impl.MobPositionUpdateEvent;
import org.apollo.game.model.inv.Inventory;
import org.apollo.game.model.inv.Inventory.StackMode;
import org.apollo.game.model.inv.InventoryConstants;
@@ -402,7 +403,7 @@ public abstract class Mob extends Entity {
this.index = index;
}
}
/**
* Returns this mobs interacting index.
*
@@ -428,6 +429,9 @@ public abstract class Mob extends Entity {
* @param position The position.
*/
public final void setPosition(Position position) {
World.getWorld().submit(new MobPositionUpdateEvent(this, position));
// Intentionally ignore the Event result - accidentally terminating this method would break the entire server.
Position old = this.position;
SectorRepository repository = World.getWorld().getSectorRepository();
Sector current = repository.fromPosition(old);
+3 -3
View File
@@ -19,6 +19,9 @@ import org.apollo.game.model.Appearance;
import org.apollo.game.model.Position;
import org.apollo.game.model.World;
import org.apollo.game.model.area.Sector;
import org.apollo.game.model.entity.setting.PrivacyState;
import org.apollo.game.model.entity.setting.PrivilegeLevel;
import org.apollo.game.model.entity.setting.ScreenBrightness;
import org.apollo.game.model.event.impl.LoginEvent;
import org.apollo.game.model.event.impl.LogoutEvent;
import org.apollo.game.model.inter.InterfaceConstants;
@@ -33,9 +36,6 @@ import org.apollo.game.model.inv.Inventory.StackMode;
import org.apollo.game.model.inv.InventoryConstants;
import org.apollo.game.model.inv.InventoryListener;
import org.apollo.game.model.inv.SynchronizationInventoryListener;
import org.apollo.game.model.setting.PrivacyState;
import org.apollo.game.model.setting.PrivilegeLevel;
import org.apollo.game.model.setting.ScreenBrightness;
import org.apollo.game.model.skill.LevelUpSkillListener;
import org.apollo.game.model.skill.SynchronizationSkillListener;
import org.apollo.game.sync.block.SynchronizationBlock;
@@ -1,4 +1,4 @@
package org.apollo.game.model.setting;
package org.apollo.game.model.entity.setting;
/**
* An enumeration containing the two genders (male and female). This enumeration relies on the ordering of the elements
@@ -1,4 +1,4 @@
package org.apollo.game.model.setting;
package org.apollo.game.model.entity.setting;
import com.google.common.base.Preconditions;
@@ -1,4 +1,4 @@
package org.apollo.game.model.setting;
package org.apollo.game.model.entity.setting;
import com.google.common.base.Preconditions;
@@ -1,4 +1,4 @@
package org.apollo.game.model.setting;
package org.apollo.game.model.entity.setting;
import com.google.common.base.Preconditions;
@@ -1,4 +1,4 @@
package org.apollo.game.model.setting;
package org.apollo.game.model.entity.setting;
import com.google.common.base.Preconditions;
@@ -1,4 +1,4 @@
/**
* Contains player setting or customisation-related classes.
*/
package org.apollo.game.model.setting;
package org.apollo.game.model.entity.setting;
@@ -0,0 +1,56 @@
package org.apollo.game.model.event.impl;
import org.apollo.game.model.Position;
import org.apollo.game.model.entity.Mob;
import org.apollo.game.model.event.Event;
/**
* An {@link Event} created when a Mob's Position is being updated.
* <p>
* This Event intentionally ignores the result of execution - it should not be possible for a plugin to prevent this
* Event from happening, only to listen for it.
*
* @author Major
*/
public final class MobPositionUpdateEvent extends Event {
/**
* The Mob whose position is being updated.
*/
private final Mob mob;
/**
* The next Position of the Mob.
*/
private final Position next;
/**
* Creates the MobPositionUpdateEvent.
*
* @param mob The {@link Mob} whose Position is being updated.
* @param next The next {@link Position} of the Mob.
*/
public MobPositionUpdateEvent(Mob mob, Position next) {
this.mob = mob;
this.next = next;
}
/**
* Gets the {@link Mob} being moved.
*
* @return The Mob.
*/
public Mob getMob() {
return mob;
}
/**
* Gets the {@link Position} this {@link Mob} is being moved to.
*
* @return The Position.
*/
public Position getNext() {
return next;
}
}
@@ -7,6 +7,7 @@ import java.util.Optional;
import org.apollo.game.message.impl.CloseInterfaceMessage;
import org.apollo.game.message.impl.EnterAmountMessage;
import org.apollo.game.message.impl.OpenDialogueInterfaceMessage;
import org.apollo.game.message.impl.OpenDialogueOverlayMessage;
import org.apollo.game.message.impl.OpenInterfaceMessage;
import org.apollo.game.message.impl.OpenInterfaceSidebarMessage;
import org.apollo.game.message.impl.OpenOverlayMessage;
@@ -144,10 +145,10 @@ public final class InterfaceSet {
}
/**
* Opens a chat box dialogue.
* Opens a dialogue interface.
*
* @param listener The listener for the dialogue.
* @param dialogueId The dialogue's id.
* @param listener The {@link DialogueListener}.
* @param dialogueId The dialogue id.
*/
public void openDialogue(DialogueListener listener, int dialogueId) {
closeAndNotify();
@@ -160,14 +161,39 @@ public final class InterfaceSet {
}
/**
* Opens a chat box dialogue.
* Opens a dialogue.
*
* @param dialogueId The dialogue's id.
* @param dialogueId The dialogue id.
*/
public void openDialogue(int dialogueId) {
openDialogue(null, dialogueId);
}
/**
* Opens a dialogue overlay interface.
*
* @param listener The {@link DialogueListener}.
* @param dialogueId The dialogue id.
*/
public void openDialogueOverlay(DialogueListener listener, int dialogueId) {
closeAndNotify();
this.dialogueListener = Optional.ofNullable(listener);
this.listener = Optional.ofNullable(listener);
interfaces.put(InterfaceType.DIALOGUE, dialogueId);
player.send(new OpenDialogueOverlayMessage(dialogueId));
}
/**
* Opens a dialogue overlay.
*
* @param dialogueId The dialogue id.
*/
public void openDialogueOverlay(int dialogueId) {
openDialogueOverlay(null, dialogueId);
}
/**
* Opens the enter amount dialogue.
*
@@ -1,7 +1,7 @@
package org.apollo.game.sync.block;
import org.apollo.game.message.impl.ChatMessage;
import org.apollo.game.model.setting.PrivilegeLevel;
import org.apollo.game.model.entity.setting.PrivilegeLevel;
/**
* The chat {@link SynchronizationBlock}. Only players can utilise this block.
@@ -20,11 +20,11 @@ import org.apollo.game.model.entity.attr.AttributeType;
import org.apollo.game.model.entity.attr.BooleanAttribute;
import org.apollo.game.model.entity.attr.NumericalAttribute;
import org.apollo.game.model.entity.attr.StringAttribute;
import org.apollo.game.model.entity.setting.Gender;
import org.apollo.game.model.entity.setting.PrivacyState;
import org.apollo.game.model.entity.setting.PrivilegeLevel;
import org.apollo.game.model.entity.setting.ScreenBrightness;
import org.apollo.game.model.inv.Inventory;
import org.apollo.game.model.setting.Gender;
import org.apollo.game.model.setting.PrivacyState;
import org.apollo.game.model.setting.PrivilegeLevel;
import org.apollo.game.model.setting.ScreenBrightness;
import org.apollo.io.player.PlayerLoader;
import org.apollo.io.player.PlayerLoaderResponse;
import org.apollo.net.codec.login.LoginConstants;
@@ -2,7 +2,7 @@ package org.apollo.io.player.impl;
import org.apollo.game.model.Position;
import org.apollo.game.model.entity.Player;
import org.apollo.game.model.setting.PrivilegeLevel;
import org.apollo.game.model.entity.setting.PrivilegeLevel;
import org.apollo.io.player.PlayerLoader;
import org.apollo.io.player.PlayerLoaderResponse;
import org.apollo.net.codec.login.LoginConstants;
+22 -9
View File
@@ -5,6 +5,8 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.util.Attribute;
import io.netty.util.ReferenceCountUtil;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -65,25 +67,36 @@ public final class ApolloHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object message) {
if (ctx.attr(NetworkConstants.SESSION_KEY).get() == null) {
try {
Attribute<Session> attribute = ctx.attr(NetworkConstants.SESSION_KEY);
Session session = attribute.get();
if (message instanceof HttpRequest || message instanceof JagGrabRequest) {
new UpdateSession(ctx.channel(), serverContext).messageReceived(message);
} else {
session = new UpdateSession(ctx.channel(), serverContext);
}
if (session != null) {
session.messageReceived(message);
return;
}
// TODO: Perhaps let HandshakeMessage implement Message to remove this explicit check
if (message instanceof HandshakeMessage) {
HandshakeMessage handshakeMessage = (HandshakeMessage) message;
switch (handshakeMessage.getServiceId()) {
case HandshakeConstants.SERVICE_GAME:
ctx.attr(NetworkConstants.SESSION_KEY).set(new LoginSession(ctx, serverContext));
attribute.set(new LoginSession(ctx, serverContext));
break;
case HandshakeConstants.SERVICE_UPDATE:
ctx.attr(NetworkConstants.SESSION_KEY).set(new UpdateSession(ctx.channel(), serverContext));
attribute.set(new UpdateSession(ctx.channel(), serverContext));
break;
default:
throw new IllegalStateException("Invalid service id.");
}
}
} else {
ctx.attr(NetworkConstants.SESSION_KEY).get().messageReceived(message);
} finally {
ReferenceCountUtil.release(message);
}
}
@@ -5,6 +5,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
import java.util.logging.Logger;
import org.apollo.net.codec.login.LoginDecoder;
import org.apollo.net.codec.login.LoginEncoder;
@@ -19,35 +20,41 @@ import org.apollo.net.codec.update.UpdateEncoder;
*/
public final class HandshakeDecoder extends ByteToMessageDecoder {
/**
* The logger for this class.
*/
private static final Logger logger = Logger.getLogger(HandshakeDecoder.class.getName());
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) {
if (buffer.isReadable()) {
int id = buffer.readUnsignedByte();
switch (id) {
case HandshakeConstants.SERVICE_GAME:
ctx.pipeline().addFirst("loginEncoder", new LoginEncoder());
ctx.pipeline().addAfter("handshakeDecoder", "loginDecoder", new LoginDecoder());
break;
case HandshakeConstants.SERVICE_UPDATE:
ctx.pipeline().addFirst("updateEncoder", new UpdateEncoder());
ctx.pipeline().addBefore("handler", "updateDecoder", new UpdateDecoder());
ByteBuf buf = ctx.alloc().buffer(8);
buf.writeLong(0);
ctx.channel().writeAndFlush(buf);
break;
default:
throw new IllegalArgumentException("Invalid service id.");
}
ctx.pipeline().remove(this);
HandshakeMessage message = new HandshakeMessage(id);
out.add(message);
if (buffer.isReadable()) {
out.add(buffer.readBytes(buffer.readableBytes()));
}
if (!buffer.isReadable()) {
return;
}
int id = buffer.readUnsignedByte();
switch (id) {
case HandshakeConstants.SERVICE_GAME:
ctx.pipeline().addFirst("loginEncoder", new LoginEncoder());
ctx.pipeline().addAfter("handshakeDecoder", "loginDecoder", new LoginDecoder());
break;
case HandshakeConstants.SERVICE_UPDATE:
ctx.pipeline().addFirst("updateEncoder", new UpdateEncoder());
ctx.pipeline().addBefore("handler", "updateDecoder", new UpdateDecoder());
ByteBuf buf = ctx.alloc().buffer(8).writeLong(0);
ctx.channel().writeAndFlush(buf);
break;
default:
ByteBuf data = buffer.readBytes(buffer.readableBytes());
logger.info(String.format("Unexpected handshake request received: %d data: %s", id, data.toString()));
return;
}
ctx.pipeline().remove(this);
out.add(new HandshakeMessage(id));
}
}
@@ -0,0 +1,25 @@
package org.apollo.net.release.r317;
import org.apollo.game.message.impl.OpenDialogueOverlayMessage;
import org.apollo.net.codec.game.DataOrder;
import org.apollo.net.codec.game.DataTransformation;
import org.apollo.net.codec.game.DataType;
import org.apollo.net.codec.game.GamePacket;
import org.apollo.net.codec.game.GamePacketBuilder;
import org.apollo.net.release.MessageEncoder;
/**
* A {@link MessageEncoder} for the {@link OpenDialogueOverlayMessage}.
*
* @author Major
*/
public final class OpenDialogueOverlayMessageEncoder extends MessageEncoder<OpenDialogueOverlayMessage> {
@Override
public GamePacket encode(OpenDialogueOverlayMessage message) {
GamePacketBuilder builder = new GamePacketBuilder(218);
builder.put(DataType.SHORT, DataOrder.LITTLE, DataTransformation.ADD, message.getInterfaceId());
return builder.toGamePacket();
}
}
@@ -2,7 +2,7 @@ package org.apollo.net.release.r317;
import org.apollo.game.message.impl.PlayerDesignMessage;
import org.apollo.game.model.Appearance;
import org.apollo.game.model.setting.Gender;
import org.apollo.game.model.entity.setting.Gender;
import org.apollo.net.codec.game.DataType;
import org.apollo.net.codec.game.GamePacket;
import org.apollo.net.codec.game.GamePacketReader;
@@ -9,8 +9,8 @@ import org.apollo.game.model.Item;
import org.apollo.game.model.Position;
import org.apollo.game.model.def.EquipmentDefinition;
import org.apollo.game.model.entity.EquipmentConstants;
import org.apollo.game.model.entity.setting.Gender;
import org.apollo.game.model.inv.Inventory;
import org.apollo.game.model.setting.Gender;
import org.apollo.game.sync.block.AnimationBlock;
import org.apollo.game.sync.block.AppearanceBlock;
import org.apollo.game.sync.block.ChatBlock;
@@ -16,6 +16,7 @@ import org.apollo.game.message.impl.IgnoreListMessage;
import org.apollo.game.message.impl.LogoutMessage;
import org.apollo.game.message.impl.NpcSynchronizationMessage;
import org.apollo.game.message.impl.OpenDialogueInterfaceMessage;
import org.apollo.game.message.impl.OpenDialogueOverlayMessage;
import org.apollo.game.message.impl.OpenInterfaceMessage;
import org.apollo.game.message.impl.OpenInterfaceSidebarMessage;
import org.apollo.game.message.impl.OpenOverlayMessage;
@@ -214,5 +215,7 @@ public final class Release317 extends Release {
register(FlashTabInterfaceMessage.class, new FlashTabInterfaceMessageEncoder());
register(OpenSidebarMessage.class, new OpenSidebarMessageEncoder());
register(OpenOverlayMessage.class, new OpenOverlayMessageEncoder());
register(OpenDialogueOverlayMessage.class, new OpenDialogueOverlayMessageEncoder());
}
}
@@ -0,0 +1,24 @@
package org.apollo.net.release.r377;
import org.apollo.game.message.impl.OpenDialogueOverlayMessage;
import org.apollo.net.codec.game.DataOrder;
import org.apollo.net.codec.game.DataType;
import org.apollo.net.codec.game.GamePacket;
import org.apollo.net.codec.game.GamePacketBuilder;
import org.apollo.net.release.MessageEncoder;
/**
* A {@link MessageEncoder} for the {@link OpenDialogueOverlayMessage}.
*
* @author Major
*/
public final class OpenDialogueOverlayMessageEncoder extends MessageEncoder<OpenDialogueOverlayMessage> {
@Override
public GamePacket encode(OpenDialogueOverlayMessage message) {
GamePacketBuilder builder = new GamePacketBuilder(158);
builder.put(DataType.SHORT, DataOrder.LITTLE, message.getInterfaceId());
return builder.toGamePacket();
}
}
@@ -2,7 +2,7 @@ package org.apollo.net.release.r377;
import org.apollo.game.message.impl.PlayerDesignMessage;
import org.apollo.game.model.Appearance;
import org.apollo.game.model.setting.Gender;
import org.apollo.game.model.entity.setting.Gender;
import org.apollo.net.codec.game.DataType;
import org.apollo.net.codec.game.GamePacket;
import org.apollo.net.codec.game.GamePacketReader;
@@ -9,8 +9,8 @@ import org.apollo.game.model.Item;
import org.apollo.game.model.Position;
import org.apollo.game.model.def.EquipmentDefinition;
import org.apollo.game.model.entity.EquipmentConstants;
import org.apollo.game.model.entity.setting.Gender;
import org.apollo.game.model.inv.Inventory;
import org.apollo.game.model.setting.Gender;
import org.apollo.game.sync.block.AnimationBlock;
import org.apollo.game.sync.block.AppearanceBlock;
import org.apollo.game.sync.block.ChatBlock;
@@ -16,6 +16,7 @@ import org.apollo.game.message.impl.IgnoreListMessage;
import org.apollo.game.message.impl.LogoutMessage;
import org.apollo.game.message.impl.NpcSynchronizationMessage;
import org.apollo.game.message.impl.OpenDialogueInterfaceMessage;
import org.apollo.game.message.impl.OpenDialogueOverlayMessage;
import org.apollo.game.message.impl.OpenInterfaceMessage;
import org.apollo.game.message.impl.OpenInterfaceSidebarMessage;
import org.apollo.game.message.impl.OpenOverlayMessage;
@@ -210,6 +211,7 @@ public final class Release377 extends Release {
register(FlashTabInterfaceMessage.class, new FlashTabInterfaceMessageEncoder());
register(OpenSidebarMessage.class, new OpenSidebarMessageEncoder());
register(OpenOverlayMessage.class, new OpenOverlayMessageEncoder());
register(OpenDialogueOverlayMessage.class, new OpenDialogueOverlayMessageEncoder());
}
}
+1 -1
View File
@@ -131,7 +131,7 @@ public final class LoginSession extends Session {
future.addListener(ChannelFutureListener.CLOSE);
}
if (optional.isPresent()) {
if (optional.isPresent() && request.isReconnecting()) {
optional.get().sendInitialMessages();
}
}