diff --git a/data/login.xml b/data/login.xml
index 27f2d6fa..c7f0371a 100644
--- a/data/login.xml
+++ b/data/login.xml
@@ -1,4 +1,3 @@
- org.apollo.io.player.impl.DummyPlayerLoader
- org.apollo.io.player.impl.DiscardPlayerSaver
+ org.apollo.io.player.DummyPlayerSerializer
\ No newline at end of file
diff --git a/data/plugins/areas/plugin.xml b/data/plugins/areas/plugin.xml
index 2c18bb32..0e9c4b1b 100644
--- a/data/plugins/areas/plugin.xml
+++ b/data/plugins/areas/plugin.xml
@@ -10,6 +10,7 @@
+
diff --git a/data/plugins/areas/wilderness.rb b/data/plugins/areas/wilderness.rb
new file mode 100644
index 00000000..27fcbd54
--- /dev/null
+++ b/data/plugins/areas/wilderness.rb
@@ -0,0 +1,52 @@
+require 'java'
+
+java_import 'org.apollo.game.model.entity.Player'
+java_import 'org.apollo.game.message.impl.OpenOverlayMessage'
+java_import 'org.apollo.game.message.impl.SetWidgetTextMessage'
+
+
+
+private
+
+MIN_X = 2945
+MIN_Y = 3522
+MAX_X = 3390
+MAX_Y = 3972
+
+OVERLAY_INTERFACE_ID = 197
+LEVEL_STRING_ID = 199
+
+declare_attribute(:wilderness_level, 0, :transient)
+
+# Determines the wilderness level for the specified player's position
+def wilderness_level(player)
+ return (player.position.y - (MIN_Y - 1) / 8).ceil
+end
+
+area_action :wilderness_level do
+
+ on_entry do |player|
+ player.wilderness_level = wilderness_level(player)
+ player.interface_set.open_overlay(OVERLAY_INTERFACE_ID)
+ player.send(SetWidgetTextMessage.new(LEVEL_STRING_ID, "Level: #{player.wilderness_level}"))
+ show_action(ATTACK_ACTION)
+ end
+
+ while_in do |player|
+ current = player.wilderness_level
+ updated = wilderness_level(player)
+ if (current != updated)
+ player.wilderness_level = updated
+ player.send(SetWidgetTextMessage.new(LEVEL_STRING_ID, "Level: #{player.wilderness_level}"))
+ end
+ end
+
+ on_exit do |player|
+ player.wilderness_level = 0
+ player.interface_set.close() # TODO: Will this cause issues with other potentially open interfaces?
+ hide_action(ATTACK_ACTION)
+ end
+
+end
+
+area :name => :wilderness, :coordinates => MIN_X, MIN_Y, MAX_X, MAX_Y, 0, => :actions => :wilderness_level
\ No newline at end of file
diff --git a/data/plugins/dialogue/dialogue.rb b/data/plugins/dialogue/dialogue.rb
index ffe51c9a..523424ad 100644
--- a/data/plugins/dialogue/dialogue.rb
+++ b/data/plugins/dialogue/dialogue.rb
@@ -6,6 +6,7 @@ java_import 'org.apollo.game.message.impl.SetWidgetItemModelMessage'
java_import 'org.apollo.game.message.impl.SetWidgetNpcModelMessage'
java_import 'org.apollo.game.message.impl.SetWidgetPlayerModelMessage'
java_import 'org.apollo.game.message.impl.SetWidgetTextMessage'
+java_import 'org.apollo.game.action.DistancedAction'
# The map of conversation names to Conversations.
CONVERSATIONS = {}
@@ -20,6 +21,30 @@ def conversation(name, &block)
CONVERSATIONS[name] = conversation
end
+# A distanced action which opens the dialogue when getting into interaction distance of the given npc
+class OpenDialogueAction < DistancedAction
+ attr_reader :player, :npc, :dialogue
+
+ def initialize(player, npc, dialogue)
+ super(0, true, player, npc.position, 1)
+
+ @player = player
+ @npc = npc
+ @dialogue = dialogue
+ end
+
+ def executeAction
+ @player.set_interacting_mob(@npc)
+ send_dialogue(@player, @dialogue)
+ stop
+ end
+
+ def equals(other)
+ return (@npc == other.npc && @dialogue == other.dialogue)
+ end
+
+end
+
# A conversation held between two entities.
class Conversation
@@ -42,15 +67,16 @@ class Conversation
@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?
+ npc_index = dialogue.npc
+ raise 'Npc cannot be null when opening a dialogue.' if npc_index.nil?
@starters << dialogue
on :message, :first_npc_action do |ctx, player, event|
- if npc == $world.npc_repository.get(event.index).id
+ npc = $world.npc_repository.get(event.index)
+ if npc_index == npc.id
@starters.each do |start|
if dialogue.precondition(player)
- send_dialogue(player, dialogue)
+ player.start_action(OpenDialogueAction.new(player, npc, dialogue))
ctx.break_handler_chain()
break
end
@@ -74,6 +100,12 @@ def declare_emote(name, id)
end
+# Sends the dialogue from the specified Conversation with the specified name.
+def get_dialogue(conversation, name)
+ CONVERSATIONS[conversation].part(name)
+end
+
+
# Sends the specified dialogue.
def send_dialogue(player, dialogue)
type = dialogue.type
@@ -144,17 +176,6 @@ class Dialogue
@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)
unless emote.nil?
@@ -186,16 +207,21 @@ class Dialogue
end
# Sets the id of the item displayed.
- def item(item=nil, scale=nil)
+ def item(item=nil, scale=100)
unless item.nil?
raise 'Can only display an item on :message_with_item dialogues.' unless @type == :message_with_item
- @item = item
+ @item = lookup_item(item)
@item_scale = scale
end
@item
end
+ # Gets the scale of the item.
+ def item_scale
+ @item_scale
+ end
+
# Sets the id of the model displayed.
def model(model=nil)
unless model.nil?
@@ -228,7 +254,7 @@ class Dialogue
def options
@options.dup
end
-
+
# Sets the precondition of this dialogue.
def precondition(player=nil, &block)
@precondition = block unless block.nil?
@@ -296,6 +322,19 @@ class Dialogue
@text = lines
end
+ protected
+
+ # 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.
@@ -350,6 +389,17 @@ OPTIONS_DIALOGUE_IDS = [ 2459, 2469, 2480, 2492 ]
## TODO separate this into different Dialogue types ##
+# Sends a dialogue displaying an item model and text.
+def send_item_dialogue(player, dialogue)
+ text = dialogue.text
+ dialogue_id = ITEM_DIALOGUE_IDS[text.size - 1]
+ player.send(SetWidgetItemModelMessage.new(dialogue_id + 1, dialogue.item, dialogue.item_scale))
+
+ indices = [ dialogue_id + 1 + 2, dialogue_id + 1 + 1, dialogue_id + 1 + 4, dialogue_id + 1 + 5 ]
+
+ text.each_with_index { |line, index| set_text(player, indices[index], line) }
+ player.interface_set.open_dialogue(ContinueDialogueAdapter.new(player, dialogue.options[0]), dialogue_id)
+end
# Sends a dialogue displaying only text, with no 'Click here to continue...' button.
def send_statement_dialogue(player, dialogue)
diff --git a/data/plugins/entity/spawning/npc-spawn.rb b/data/plugins/entity/spawning/npc-spawn.rb
index f5e86397..65aeeec3 100644
--- a/data/plugins/entity/spawning/npc-spawn.rb
+++ b/data/plugins/entity/spawning/npc-spawn.rb
@@ -16,7 +16,7 @@ java_import 'org.apollo.game.model.entity.Npc'
# :y - the y coordinate where the npc will spawn.
# Optional arguments are as follows:
# :face - the direction the npc should face when it spawns. Supported options are :north, :north_east, :east, :south_east, :south, :south_west, :west, and :north_west
-# :bounds - the rectangular bound that the npc can wander about in. Order is [top-left x-coordinate, top-left y-coordinate, bottom-right x-coordinate, bottom-right y-coordinate]
+# :bounds - the rectangular bound that the npc can wander about in. Order is [bottom-left x-coordinate, bottom-left y-coordinate, top-right x-coordinate, top-right y-coordinate]
# :delta_bounds - the rectangular bound that the npc can wander about in, as a difference from the spawn point. Order is [x-delta, y-delta]. Should not be used with :bounds.
# :spawn_animation - the animation that will be played when the npc spawns.
# :spawn_graphic - the graphic that will be played when the npc spawns.
@@ -33,11 +33,12 @@ end
# Spawns the specified npc and applies the properties in the hash.
def spawn(npc, hash)
- $world.register(npc)
unless hash.empty?
- hash = decode_hash(npc.position, hash) # Use npc.position here because sector registry events (called by World.register) can be hooked
- apply_decoded_hash(npc, hash) # into and someone might do something daft like move the npc immediately after it gets spawned.
+ hash = decode_hash(npc.position, hash)
+ apply_decoded_hash(npc, hash)
end
+
+ $world.register(npc)
end
# Returns an npc with the id and position specified by the hash.
@@ -54,7 +55,7 @@ def apply_decoded_hash(npc, hash)
hash.each do |key, value|
case key
when :face then npc.turn_to(value)
- when :boundary then npc.boundary = value
+ when :boundary then npc.boundaries = value
when :spawn_animation then npc.play_animation(Animation.new(value))
when :spawn_graphic then npc.play_graphic(Graphic.new(value))
else raise "Unrecognised key #{key} - value #{value}."
@@ -71,11 +72,16 @@ def decode_hash(position, hash)
when :face
decoded[:face] = direction_to_position(value, position)
when :delta_bounds
+ raise ':delta_bounds must have two values.' unless value.length == 2
dx, dy, x, y, z = value[0], value[1], position.x, position.y, position.height
raise 'Delta values cannot be less than 0.' if (dx < 0 || dy < 0)
- decoded[:boundary] = [ Position.new(x + dx, y, z), Position.new(x, y + dy, z), Position.new(x - dx, y, z), Position.new(x, y - dy, z) ]
- when :bounds then decoded[:boundary] = value
+ decoded[:boundary] = [ Position.new(x - dx, y - dy, z), Position.new(x + dx, y + dy, z) ]
+ when :bounds
+ raise ':bounds must have four values.' unless value.length == 4
+ min_x, min_y, max_x, max_y = value[0], value[1], value[2], value[3]
+
+ decoded[:boundary] = [ Position.new(min_x, min_y), Position.new(max_x, max_y) ]
when :spawn_animation then decoded[:spawn_animation] = Animation.new(value)
when :spawn_graphic then decoded[:spawn_graphic ] = Graphic.new(value)
else raise "Unrecognised key #{key} - value #{value}."
diff --git a/data/plugins/location/al-kharid/npcs.rb b/data/plugins/location/al-kharid/npcs.rb
index d61324ae..f5d48346 100644
--- a/data/plugins/location/al-kharid/npcs.rb
+++ b/data/plugins/location/al-kharid/npcs.rb
@@ -6,7 +6,7 @@
# :y - the y coordinate where the npc will spawn.
# Optional arguments are as follows:
# :face - the direction the npc should face when it spawns. Supported options are :north, :north_east, :east, :south_east, :south, :south_west, :west, and :north_west
-# :bounds - the rectangular bound that the npc can wander about in. Order is [top-left x-coordinate, top-left y-coordinate, bottom-right x-coordinate, bottom-right y-coordinate]
+# :bounds - the rectangular bound that the npc can wander about in. Order is [bottom-left x-coordinate, bottom-left y-coordinate, top-right x-coordinate, top-right y-coordinate]
# :delta_bounds - the rectangular bound that the npc can wander about in, as a difference from the spawn point. Order is [x-delta, y-delta]. Should not be used with :bounds.
# :spawn_animation - the animation that will be played when the npc spawns.
# :spawn_graphic - the graphic that will be played when the npc spawns.
diff --git a/data/plugins/location/edgeville/npcs.rb b/data/plugins/location/edgeville/npcs.rb
index 36cb4eb0..f8dbd14c 100644
--- a/data/plugins/location/edgeville/npcs.rb
+++ b/data/plugins/location/edgeville/npcs.rb
@@ -6,7 +6,7 @@
# :y - the y coordinate where the npc will spawn.
# Optional arguments are as follows:
# :face - the direction the npc should face when it spawns. Supported options are :north, :north_east, :east, :south_east, :south, :south_west, :west, and :north_west
-# :bounds - the rectangular bound that the npc can wander about in. Order is [top-left x-coordinate, top-left y-coordinate, bottom-right x-coordinate, bottom-right y-coordinate]
+# :bounds - the rectangular bound that the npc can wander about in. Order is [bottom-left x-coordinate, bottom-left y-coordinate, top-right x-coordinate, top-right y-coordinate]
# :delta_bounds - the rectangular bound that the npc can wander about in, as a difference from the spawn point. Order is [x-delta, y-delta]. Should not be used with :bounds.
# :spawn_animation - the animation that will be played when the npc spawns.
# :spawn_graphic - the graphic that will be played when the npc spawns.
diff --git a/data/plugins/location/lumbridge/npcs.rb b/data/plugins/location/lumbridge/npcs.rb
index 12dfc1d0..73f15689 100644
--- a/data/plugins/location/lumbridge/npcs.rb
+++ b/data/plugins/location/lumbridge/npcs.rb
@@ -6,7 +6,7 @@
# :y - the y coordinate where the npc will spawn.
# Optional arguments are as follows:
# :face - the direction the npc should face when it spawns. Supported options are :north, :north_east, :east, :south_east, :south, :south_west, :west, and :north_west
-# :bounds - the rectangular bound that the npc can wander about in. Order is [top-left x-coordinate, top-left y-coordinate, bottom-right x-coordinate, bottom-right y-coordinate]
+# :bounds - the rectangular bound that the npc can wander about in. Order is [bottom-left x-coordinate, bottom-left y-coordinate, top-right x-coordinate, top-right y-coordinate]
# :delta_bounds - the rectangular bound that the npc can wander about in, as a difference from the spawn point. Order is [x-delta, y-delta]. Should not be used with :bounds.
# :spawn_animation - the animation that will be played when the npc spawns.
# :spawn_graphic - the graphic that will be played when the npc spawns.
diff --git a/data/plugins/location/tutorial-island/guide.rb b/data/plugins/location/tutorial-island/guide.rb
new file mode 100644
index 00000000..16abb698
--- /dev/null
+++ b/data/plugins/location/tutorial-island/guide.rb
@@ -0,0 +1,119 @@
+require 'java'
+
+java_import 'org.apollo.game.message.impl.HintIconMessage'
+java_import 'org.apollo.game.message.impl.SwitchTabInterfaceMessage'
+
+
+private
+
+# The ids of tabs that are displayed when the player has yet to start the tutorial.
+INITIAL_TABS = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2449, 904, -1, -1 ]
+
+# The character design interface id.
+CHARACTER_DESIGN = 3559
+
+# The Runescape guide Npc
+@runescape_guide = spawn_npc :name => :runescape_guide, :x => 3093, :y => 3107
+
+# Sends the appropriate data to the client when the player logs in to the game.
+on :login do |event, player|
+ if player.in_tutorial_island
+ TutorialInstructions::show_instruction(player)
+ # INITIAL_TABS.each_with_index { |tab, index| player.send(SwitchTabInterfaceMessage.new(index, tab)) } # This is commented so the lame hack works
+
+ if (player.tutorial_island_progress == :not_started)
+ show_hint_icon(player)
+ player.interface_set.open_window(CHARACTER_DESIGN)
+ end
+ end
+end
+
+## TODO lame hack to get round the lack of door support - must remove
+
+
+on :message, :fifth_item_option do |ctx, player, message|
+ player.teleport(Position.new(3100, 3107)) if message.id == 1
+ player.tutorial_island_progress = :moving_around
+ player.interface_set.open_dialogue_overlay(-1)
+end
+
+on :login do |event, player|
+ player.inventory.add(1)
+end
+
+## END lame hack
+
+
+# The conversation with the Runescape Guide, when on tutorial island.
+conversation :tutorial_runescape_guide do
+
+ # The first dialogue of the Runescape Guide, greeting the Player.
+ dialogue :greetings do
+ type :npc_speech
+ npc :runescape_guide
+
+ precondition { |player| player.tutorial_island_progress == :not_started }
+
+ text "Greetings! I see you are a new arrival to this land. My job is to welcome all new visitors. So welcome!"
+
+ continue :dialogue => :go_through_door do |player|
+ player.tutorial_island_progress = :talk_to_people
+ end
+ end
+
+
+ # The Guide welcomes back the Player if they speak to him after they have already gone through the conversation once.
+ dialogue :welcome_back do
+ type :npc_speech
+ npc :runescape_guide
+
+ precondition { |player| player.tutorial_island_progress != :not_started }
+
+ text "Welcome back."
+
+ continue :dialogue => :talk_to_people
+ end
+
+
+ # The Guide tells Players to speak to people in order to succeed.
+ dialogue :talk_to_people do
+ type :npc_speech
+ npc :runescape_guide
+
+ text "You have already learned the first thing you need to succeed in this world: talking to people!",
+ "You will find many inhabitants of this world have useful things to say to you. By clicking on them with your mouse you can talk to them.",
+ "I would also suggest reading through some of the supporting information on the website. There you can find maps, a bestiary, and much more."
+
+ continue :dialogue => :go_through_door
+ end
+
+ # The Guide tells Players to go through the door, advancing the tutorial progress if this is the first time the Player has heard this.
+ dialogue :go_through_door do
+ type :npc_speech
+ npc :runescape_guide
+
+ text "To continue the tutorial go through that door over there, and speak to your first instructor."
+
+ continue :close => true do |player|
+ if (player.tutorial_island_progress < :runescape_guide_finished)
+ reset_hint_icon(player)
+ # TODO door hint icon
+ player.tutorial_island_progress = :runescape_guide_finished
+ end
+
+ TutorialInstructions.show_instruction(player)
+ end
+ end
+
+end
+
+
+# Enables the hint icon above the Runescape guide.
+def show_hint_icon(player)
+ player.send(HintIconMessage.for_npc(@runescape_guide.index))
+end
+
+# Resets the hint icon.
+def reset_hint_icon(player)
+ player.send(HintIconMessage.reset_npc())
+end
\ No newline at end of file
diff --git a/data/plugins/location/tutorial-island/instructions.rb b/data/plugins/location/tutorial-island/instructions.rb
new file mode 100644
index 00000000..26c223c5
--- /dev/null
+++ b/data/plugins/location/tutorial-island/instructions.rb
@@ -0,0 +1,121 @@
+
+# TODO update the status to :moving_around when the door is opened
+
+# Contains members related to the instructions issues during tutorial island.
+module TutorialInstructions
+
+ # Sends the appropriate instruction to the player.
+ def self.show_instruction(player)
+ instructions = CONVERSATIONS[:tutorial_island_instructions]
+ progress = player.tutorial_island_progress.name
+ name = case progress
+ # The Runescape Guide instructions.
+ when :not_started then :getting_started
+ when :runescape_guide_finished then :scenery
+ when :moving_around then :moving_around
+
+ # The Survival Guide instructions.
+ when :given_axe then :viewing_items
+ when :cut_tree then :cut_tree
+ when :cutting_tree then :please_wait
+ else raise "No dialogue for current stage #{progress} exists."
+ end
+
+ dialogue = instructions.part(name)
+ send_dialogue(player, dialogue)
+ end
+
+
+ # The one-sided 'conversation' of instruction instructions.
+ conversation :tutorial_island_instructions do
+
+ # The initial instruction displayed when the player logs in, before they have spoken to the guide.
+ dialogue :getting_started do
+ type :text
+
+ title "Getting started"
+ text "To start the tutorial, use your left mouse button to click on the Runescape Guide in this room. He is indicated by "\
+ "a flashing yellow arrow above his head. If you can't see him, use your keyboard's arrow keys to rotate the view."
+ end
+
+ # The instruction displayed after the player has spoken to the Runescape Guide.
+ dialogue :scenery do
+ type :text
+
+ title "Interacting with scenery"
+ text "You can interact with many items of the scenery by simply clicking on them. Right clicking will also give more options. "\
+ "Click on the door indicated with the yellow arrow to go through to the next area and speak with your next instructor."
+ end
+
+ ## TODO !! When we have door support, the player's tutorial island progress needs to be set to :moving_around when they walk through the door !!
+
+ # The instruction displayed after the player has left the initial building.
+ dialogue :moving_around do
+ type :text
+
+ title "Moving around"
+ text "Follow the path to find the next instructor. Clicking on the ground will walk you to that point. Talk to the survival expert "\
+ "by the pond to continue the tutorial. Remember you can rotate the view by pressing the arrow keys."
+ end
+
+ # The instruction displayed after the player has been given the tinderbox and bronze axe by the Survival Guide.
+ dialogue :viewing_items do
+ type :text
+
+ title "Viewing the items you were given"
+ text "Click on the flashing backpack icon to the right side of the main window to view your inventory. Your inventory is a list of "\
+ "everything you have in your backpack."
+ end
+
+ # The instruction displayed before the player has begun to cut the tree.
+ dialogue :cut_tree do
+ type :text
+
+ title "Cut down a tree"
+ text "You can click on the backpack icon at any time to view the items that you currently have in your inventory. You will see that you "\
+ "now have an axe in your inventory. Use this to get some logs by clicking on the indicated tree."
+ end
+
+ # The instruction displayed when the player begins to cut the tree.
+ dialogue :please_wait do
+ type :text
+
+ title "Please wait..."
+ text "Your character is now attempting to cut down the tree. Sit back for a moment whilst he does all the hard work." # TODO she instead of he if applicable
+ end
+
+ # The instruction displayed after the player has successfully cut logs from the tree.
+ dialogue :make_a_fire do
+ type :text
+
+ title "Making a fire"
+ text "Well done! You managed to cut some logs from the tree! Next, use the tinderbox in your inventory to light the logs. First click on the "\
+ "tinderbox to 'use' it. Then click on the logs in your inventory to light them."
+ end
+
+ # The instruction displayed when the player begins to light the fire.
+ dialogue :lighting_fire do
+ type :text
+
+ title "Please wait..."
+ text "Your character is now attempting to light the logs. Sit back for a moment whilst he does all the hard work." # TODO she instead of he if applicable
+ end
+
+ # The instruction displayed when the has lit the logs.
+ dialogue :gained_experience do
+ type :text
+
+ text "You gained some experience."\
+ "Click on the flashing bar graph icon near the inventory button to see your skill stats."
+ end
+
+ # The dialogue displayed when the Player has clicked the flashing skill tab icon.
+ dialogue :skill_stats do
+ type :text
+
+ title "Your skill stats."
+ text "" # TODO !!
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/data/plugins/location/tutorial-island/npcs.rb b/data/plugins/location/tutorial-island/npcs.rb
index 2ce4f67d..ee49851d 100644
--- a/data/plugins/location/tutorial-island/npcs.rb
+++ b/data/plugins/location/tutorial-island/npcs.rb
@@ -6,7 +6,7 @@
# :y - the y coordinate where the npc will spawn.
# Optional arguments are as follows:
# :face - the direction the npc should face when it spawns. Supported options are :north, :north_east, :east, :south_east, :south, :south_west, :west, and :north_west
-# :bounds - the rectangular bound that the npc can wander about in. Order is [top-left x-coordinate, top-left y-coordinate, bottom-right x-coordinate, bottom-right y-coordinate]
+# :bounds - the rectangular bound that the npc can wander about in. Order is [bottom-left x-coordinate, bottom-left y-coordinate, top-right x-coordinate, top-right y-coordinate]
# :delta_bounds - the rectangular bound that the npc can wander about in, as a difference from the spawn point. Order is [x-delta, y-delta]. Should not be used with :bounds.
# :spawn_animation - the animation that will be played when the npc spawns.
# :spawn_graphic - the graphic that will be played when the npc spawns.
@@ -16,8 +16,6 @@
# 'Above-ground' npcs
-spawn_npc :name => :runescape_guide, :x => 3093, :y => 3107
-spawn_npc :name => :survival_expert, :x => 3104, :y => 3095, :face => :north
spawn_npc :name => :master_chef, :x => 3076, :y => 3085
spawn_npc :name => :quest_guide, :x => 3086, :y => 3122, :face => :north
spawn_npc :name => :financial_advisor, :x => 3127, :y => 3124, :face => :west
diff --git a/data/plugins/location/tutorial-island/plugin.xml b/data/plugins/location/tutorial-island/plugin.xml
index bf9bf322..2306c8ec 100644
--- a/data/plugins/location/tutorial-island/plugin.xml
+++ b/data/plugins/location/tutorial-island/plugin.xml
@@ -3,14 +3,21 @@
location-tutorial-island0.1Tutorial Island
- Adds funtionality to Tutorial island.
+ Adds functionality to Tutorial island.Major
+
+
+
+
+
+ dialogueentity-spawning
+ quest
\ No newline at end of file
diff --git a/data/plugins/location/tutorial-island/stages.rb b/data/plugins/location/tutorial-island/stages.rb
new file mode 100644
index 00000000..453efb4b
--- /dev/null
+++ b/data/plugins/location/tutorial-island/stages.rb
@@ -0,0 +1,15 @@
+
+private
+
+# The array of stages in tutorial island.
+STAGES = []
+
+# The stages that are used when interacting with the Runescape Guide.
+RUNESCAPE_GUIDE = [ :not_started, :talk_to_people, :go_through_door, :runescape_guide_finished, :moving_around ]
+STAGES.concat(RUNESCAPE_GUIDE)
+
+# The stages that are used when interacting with the Survival Expert.
+SURVIVAL_EXPERT = [ :given_axe, :cut_tree, :cutting_tree, ]
+STAGES.concat(SURVIVAL_EXPERT)
+
+quest :tutorial_island, STAGES
\ No newline at end of file
diff --git a/data/plugins/location/tutorial-island/survival.rb b/data/plugins/location/tutorial-island/survival.rb
new file mode 100644
index 00000000..cbe12b30
--- /dev/null
+++ b/data/plugins/location/tutorial-island/survival.rb
@@ -0,0 +1,108 @@
+require 'java'
+
+java_import 'org.apollo.game.message.impl.FlashTabInterfaceMessage'
+java_import 'org.apollo.game.message.impl.FlashingTabClickedMessage'
+java_import 'org.apollo.game.message.impl.SwitchTabInterfaceMessage'
+
+private
+
+# The Survival Expert Npc.
+@survival_expert = spawn_npc :name => :survival_expert, :x => 3104, :y => 3095, :face => :north
+
+# The inventory tab index.
+INVENTORY_TAB_INDEX = 3
+
+# The inventory tab id.
+INVENTORY_TAB_ID = 3213
+
+# The id of the tree the Player will cut down.
+TREE_ID = 3033
+
+# TODO prevent axe equipping
+
+# The conversation with the Survival Expert, when on tutorial island.
+conversation :tutorial_surivival_expert do
+
+ dialogue :introduction do
+ type :npc_speech
+ npc :survival_expert
+
+ precondition { |player| player.tutorial_island_progress == :moving_around }
+
+ text "Hello there, newcomer. My name is Brynna. My job is to teach you a few survival tips and tricks. First off we're "\
+ "going to start with the most basic survival skill of all: making a fire."
+
+ continue :dialogue => :items
+ end
+
+
+ dialogue :hello_again do
+ type :npc_speech
+ npc :survival_expert
+
+ precondition { |player| player.tutorial_island_progress == :moving_around }
+
+ text "Hello again. I'm here to teach you a few survival tips and tricks. First off we're going to start with the most "\
+ "basic survival skill of all: making a fire."
+
+ continue :close => true, &:add_survival_items
+ end
+
+ # The dialogue displayed when the Survival Guide hands both a bronze axe and a tinderbox to the player.
+ dialogue :items do
+ type :message_with_item
+ item :bronze_axe, 200 # TODO the tinderbox is also displayed - find this dialogue id. Scale looks like the default http://i.imgur.com/i1abN5X.png
+
+ text "The Survival Guide gives you a tinderbox and a bronze axe!"
+
+ continue :close => true do |player|
+ if (player.tutorial_island_progress != :given_axe)
+ player.tutorial_island_progress = :given_axe
+ player.send(SwitchTabInterfaceMessage.new(INVENTORY_TAB_INDEX, INVENTORY_TAB_ID))
+ player.send(FlashTabInterfaceMessage.new(INVENTORY_TAB_INDEX))
+ end
+
+ add_survival_items(player)
+ end
+ end
+
+ # The dialogue displayed when the player has succesfully cut down a tree.
+ dialogue :get_logs do
+ type :message_with_item
+ item :logs
+
+ text "You get some logs."
+ continue :close => true do |player|
+ TutorialInstructions.show_instruction(player)
+ end
+ end
+
+end
+
+# The id of the bronze axe.
+BRONZE_AXE = lookup_item(:bronze_axe)
+
+# The id of the tinderbox.
+TINDERBOX = lookup_item(:tinderbox)
+
+# Add the survival items (bronze axe and tinderbox) to the inventory of the player, if they do not already have them.
+def add_survival_items(player)
+ inventory = player.inventory
+ [ BRONZE_AXE, TINDERBOX ].each { |item| inventory.add(item) unless inventory.contains(item) }
+ TutorialInstructions.show_instruction(player)
+end
+
+on :message, :first_object_action do |ctx, player, message|
+ # TODO display "You cannot cut down this tree; you must first follow the guide's instructions." if the player has yet to get the axe
+ if (player.in_tutorial_island && player.tutorial_island_progress == :cut_tree && message.id == TREE_ID)
+ player.tutorial_island_progress = :cutting_tree # Don't break the chain, so that the Woodcutting event actually happens.
+ end
+end
+
+# Intercept the FlashingTabClickedMessage to update the player's progress, if applicable.
+on :message, :flashing_tab_clicked do |ctx, player, message|
+ if (player.in_tutorial_island && message.tab == INVENTORY_TAB_INDEX)
+ player.tutorial_island_progress = :cut_tree
+ ctx.break_handler_chain()
+ end
+end
\ No newline at end of file
diff --git a/data/plugins/location/tutorial-island/utils.rb b/data/plugins/location/tutorial-island/utils.rb
new file mode 100644
index 00000000..00250f6c
--- /dev/null
+++ b/data/plugins/location/tutorial-island/utils.rb
@@ -0,0 +1,34 @@
+require 'java'
+
+java_import 'org.apollo.game.model.entity.Player'
+
+# Declare the tutorial island progress attribute.
+declare_attribute(:tutorial_island_progress, :not_started, :persistent)
+
+
+# The existing player class.
+class Player
+
+ # Returns whether or not this Player is currently on tutorial island.
+ def in_tutorial_island
+ x, y = self.position.x, self.position.y
+
+ return above_ground(x, y) || below_ground(x, y)
+ end
+
+end
+
+
+private
+
+
+# Returns whether or not the specified coordinate pair is above ground on tutorial island.
+def above_ground(x, y)
+ return (x >= 3053 && x <= 3156 && y >= 3056 && y <= 3136)
+end
+
+
+# Returns whether or not the specified coordinate pair is 'below' ground on tutorial island.
+def below_ground(x, y)
+ return (x >= 3072 && x <= 3118 && y >= 9492 && y <= 9535)
+end
\ No newline at end of file
diff --git a/data/plugins/quest/plugin.xml b/data/plugins/quest/plugin.xml
new file mode 100644
index 00000000..b92ef5b3
--- /dev/null
+++ b/data/plugins/quest/plugin.xml
@@ -0,0 +1,16 @@
+
+
+ quest
+ 0.9
+ Quest
+ Adds
+
+ Major
+
+
+
+
+
+ attributes
+
+
\ No newline at end of file
diff --git a/data/plugins/quest/repository.rb b/data/plugins/quest/repository.rb
new file mode 100644
index 00000000..ca827367
--- /dev/null
+++ b/data/plugins/quest/repository.rb
@@ -0,0 +1,133 @@
+
+# Defines a quest with the specified name.
+def quest(name, stage_names)
+ stages = { }
+ stage_names.each_with_index { |stage, index| stages[stage] = QuestStage.new(stage, index, name) }
+
+ QUESTS[name] = Quest.new(name, stages)
+end
+
+private
+
+# The repository of quests.
+QUESTS = {}
+
+# An ingame Quest.
+class Quest
+ attr_reader :name
+
+ # Creates the Quest.
+ def initialize(name, stages)
+ raise "Quest name must be a symbol, received '#{name}'." unless name.kind_of?(Symbol)
+ @name = name
+ @stages = stages
+ end
+
+ # Gets the finishing quest stage (i.e. the stage that indicates the Player has completed the quest).
+ def final_stage()
+ @stages.last
+ end
+
+ # Gets the starting quest stage.
+ def initial_stage()
+ @stages.first
+ end
+
+ # Gets the QuestStage with the specified name.
+ def stage(name)
+ stage = @stages[name]
+ raise "No stage named #{name} exists in #{@name}." if stage.nil?
+ stage
+ end
+
+end
+
+# A stage in a quest, indicating the progress of a Player.
+class QuestStage
+ attr_reader :name, :index
+
+ # Creates the QuestProgress.
+ def initialize(name, index, quest, log_text=nil)
+ @name = name
+ @index = index
+ @quest = quest
+ @log_text = log_text
+ end
+
+ # Returns whether or no this quest stage should be logged.
+ def logged
+ !@log_text.nil?
+ end
+
+ # Gets the log text for this stage.
+ def log_text
+ raise "Cannot get the log text from an unlogged quest stage." unless logged
+ @log_text
+ end
+
+ # Defines the equality operator.
+ def ==(name)
+ @index == index_of(name)
+ end
+
+ # Defines the not equal operator.
+ def !=(name)
+ @index != index_of(name)
+ end
+
+ # Defines the greater than or equal to operator.
+ def >=(name)
+ @index >= index_of(name)
+ end
+
+ # Defines the greater than operator.
+ def >(name)
+ @index > index_of(name)
+ end
+
+ # Defines the less than operator.
+ def <(name)
+ @index < index_of(name)
+ end
+
+ # Defines the less than or equal to operator.
+ def <=(name)
+ @index <= index_of(name)
+ end
+
+
+ private
+
+ # Gets the index of the QuestStage with the specified name.
+ def index_of(name)
+ QUESTS[@quest].stage(name).index
+ end
+
+end
+
+
+# Define method_missing for player
+class Player
+
+ # Override method_missing to return a QuestStage if the method name indicates quest.
+ def method_missing(symbol, *args)
+ unless args.nil?
+ arg = args[0]
+ args[0] = arg.name if arg.kind_of?(QuestStage)
+ end
+
+ result = super(symbol, *args)
+ string = symbol.to_s
+
+ if (string.end_with?('_progress'))
+ name = string[0..-10] # Cut the '_progress' from the end
+ quest = QUESTS[name.to_sym]
+ raise "No Quest with the name '#{name}' exists." if quest.nil?
+
+ result = quest.stage(result)
+ end
+
+ result
+ end
+
+end
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 65b141e2..a4b88906 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,12 +25,12 @@
org.apache.commonscommons-compress
- 1.8.1
+ 1.9org.jrubyjruby-complete
- 1.7.15
+ 1.7.19com.google.guava
@@ -40,7 +40,7 @@
io.nettynetty-all
- 4.0.23.Final
+ 4.0.25.Finalcompile
diff --git a/src/org/apollo/fs/FileSystemConstants.java b/src/org/apollo/fs/FileSystemConstants.java
index 4a1d2b5b..630b5fd4 100644
--- a/src/org/apollo/fs/FileSystemConstants.java
+++ b/src/org/apollo/fs/FileSystemConstants.java
@@ -27,11 +27,6 @@ public final class FileSystemConstants {
*/
public static final int BLOCK_SIZE = HEADER_SIZE + CHUNK_SIZE;
- /**
- * The number of caches.
- */
- public static final int CACHE_COUNT = 5;
-
/**
* The size of an index.
*/
diff --git a/src/org/apollo/fs/decoder/NpcDefinitionDecoder.java b/src/org/apollo/fs/decoder/NpcDefinitionDecoder.java
index 6e95b63f..743a2342 100644
--- a/src/org/apollo/fs/decoder/NpcDefinitionDecoder.java
+++ b/src/org/apollo/fs/decoder/NpcDefinitionDecoder.java
@@ -122,13 +122,13 @@ public final class NpcDefinitionDecoder {
buffer.getShort();
} else if (opcode == 106) {
@SuppressWarnings("unused")
- int morphVariableBitsIndex = wrapMorphism(buffer.getShort());
+ int morphVariableBitsIndex = wrap(buffer.getShort());
@SuppressWarnings("unused")
- int morphismCount = wrapMorphism(buffer.getShort());
+ int morphismCount = wrap(buffer.getShort());
int count = buffer.get() & 0xFF;
int[] morphisms = new int[count + 1];
- Arrays.setAll(morphisms, index -> wrapMorphism(buffer.getShort()));
+ Arrays.setAll(morphisms, index -> wrap(buffer.getShort()));
} else if (opcode == 107) {
@SuppressWarnings("unused")
boolean clickable = false;
@@ -137,12 +137,12 @@ public final class NpcDefinitionDecoder {
}
/**
- * Wraps a morphism value around, returning -1 if the specified value is 65,535. TODO name
+ * Wraps a morphism value around, returning -1 if the specified value is 65,535.
*
* @param value The value.
* @return -1 if {@code value} is 65,535, otherwise {@code value}.
*/
- private static int wrapMorphism(int value) {
+ private static int wrap(int value) {
return value == 65_535 ? -1 : value;
}
diff --git a/src/org/apollo/fs/package-info.java b/src/org/apollo/fs/package-info.java
index 1ddba019..6353ab6e 100644
--- a/src/org/apollo/fs/package-info.java
+++ b/src/org/apollo/fs/package-info.java
@@ -1,5 +1,4 @@
/**
- * Contains classes which deal with the file system that the client uses to
- * store game data files.
+ * Contains classes which deal with the file system that the client uses to store game data files.
*/
package org.apollo.fs;
\ No newline at end of file
diff --git a/src/org/apollo/game/GameService.java b/src/org/apollo/game/GameService.java
index efdbab2a..bb5d39b1 100644
--- a/src/org/apollo/game/GameService.java
+++ b/src/org/apollo/game/GameService.java
@@ -49,8 +49,7 @@ public final class GameService extends Service {
/**
* The scheduled executor service.
*/
- private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(
- "GameService"));
+ private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("GameService"));
/**
* The {@link ClientSynchronizer}.
@@ -170,8 +169,7 @@ public final class GameService extends Service {
*/
@Override
public void start() {
- scheduledExecutor.scheduleAtFixedRate(new GamePulseHandler(this), GameConstants.PULSE_DELAY, GameConstants.PULSE_DELAY,
- TimeUnit.MILLISECONDS);
+ scheduledExecutor.scheduleAtFixedRate(new GamePulseHandler(this), GameConstants.PULSE_DELAY, GameConstants.PULSE_DELAY, TimeUnit.MILLISECONDS);
}
/**
diff --git a/src/org/apollo/game/action/package-info.java b/src/org/apollo/game/action/package-info.java
index ab52281b..a89acf1d 100644
--- a/src/org/apollo/game/action/package-info.java
+++ b/src/org/apollo/game/action/package-info.java
@@ -1,5 +1,4 @@
/**
- * Contains classes related to actions, specialised scheduled tasks which
- * mobs perform.
+ * Contains classes related to actions, specialised scheduled tasks which mobs perform.
*/
package org.apollo.game.action;
\ No newline at end of file
diff --git a/src/org/apollo/game/message/handler/MessageHandlerChain.java b/src/org/apollo/game/message/handler/MessageHandlerChain.java
index 96e20db5..7954d7ef 100644
--- a/src/org/apollo/game/message/handler/MessageHandlerChain.java
+++ b/src/org/apollo/game/message/handler/MessageHandlerChain.java
@@ -41,8 +41,8 @@ public final class MessageHandlerChain {
}
/**
- * Handles the message, passing it down the chain until the chain is broken
- * or the message reaches the end of the chain.
+ * Handles the message, passing it down the chain until the chain is broken or the message reaches the end of the
+ * chain.
*
* @param player The player.
* @param message The message.
diff --git a/src/org/apollo/game/message/handler/MessageHandlerContext.java b/src/org/apollo/game/message/handler/MessageHandlerContext.java
index 6660d1e2..4fda61db 100644
--- a/src/org/apollo/game/message/handler/MessageHandlerContext.java
+++ b/src/org/apollo/game/message/handler/MessageHandlerContext.java
@@ -1,8 +1,7 @@
package org.apollo.game.message.handler;
/**
- * Provides operations specific to a {@link MessageHandler} in a
- * {@link MessageHandlerChain}.
+ * Provides operations specific to a {@link MessageHandler} in a {@link MessageHandlerChain}.
*
* @author Graham
* @author Ryley
@@ -24,8 +23,7 @@ public final class MessageHandlerContext {
/**
* Returns whether or not this handler chain is broken.
*
- * @return {@code true} if this handler chain is broken, otherwise
- * {@code false}.
+ * @return {@code true} if this handler chain is broken, otherwise {@code false}.
*/
public boolean isBroken() {
return broken;
diff --git a/src/org/apollo/game/message/handler/impl/BankMessageHandler.java b/src/org/apollo/game/message/handler/impl/BankMessageHandler.java
index 8e5c5925..02df5c4c 100644
--- a/src/org/apollo/game/message/handler/impl/BankMessageHandler.java
+++ b/src/org/apollo/game/message/handler/impl/BankMessageHandler.java
@@ -25,16 +25,16 @@ public final class BankMessageHandler extends MessageHandler
*/
private static final int optionToAmount(int option) {
switch (option) {
- case 1:
- return 1;
- case 2:
- return 5;
- case 3:
- return 10;
- case 4:
- return Integer.MAX_VALUE;
- case 5:
- return -1;
+ case 1:
+ return 1;
+ case 2:
+ return 5;
+ case 3:
+ return 10;
+ case 4:
+ return Integer.MAX_VALUE;
+ case 5:
+ return -1;
}
throw new IllegalArgumentException("Invalid option supplied.");
diff --git a/src/org/apollo/game/message/handler/impl/ItemOnItemVerificationHandler.java b/src/org/apollo/game/message/handler/impl/ItemOnItemVerificationHandler.java
index f01221a2..e40a3350 100644
--- a/src/org/apollo/game/message/handler/impl/ItemOnItemVerificationHandler.java
+++ b/src/org/apollo/game/message/handler/impl/ItemOnItemVerificationHandler.java
@@ -21,19 +21,19 @@ public final class ItemOnItemVerificationHandler extends MessageHandler= 0 && message.getNewSlot() >= 0 && message.getOldSlot() < inventory.capacity()
- && message.getNewSlot() < inventory.capacity()) {
+ if (message.getOldSlot() >= 0 && message.getNewSlot() >= 0 && message.getOldSlot() < inventory.capacity() && message.getNewSlot() < inventory.capacity()) {
// events must be fired for it to work if a sidebar inventory overlay is used
inventory.swap(insertPermitted && message.isInserting(), message.getOldSlot(), message.getNewSlot());
}
diff --git a/src/org/apollo/game/message/handler/impl/WalkMessageHandler.java b/src/org/apollo/game/message/handler/impl/WalkMessageHandler.java
index 003a9cee..d36728aa 100644
--- a/src/org/apollo/game/message/handler/impl/WalkMessageHandler.java
+++ b/src/org/apollo/game/message/handler/impl/WalkMessageHandler.java
@@ -36,6 +36,11 @@ public final class WalkMessageHandler extends MessageHandler {
player.stopAction();
}
player.getInterfaceSet().close();
+
+ if (player.getInteractingMob() != null) {
+ player.resetInteractingMob();
+ }
+
}
}
\ No newline at end of file
diff --git a/src/org/apollo/game/message/impl/PlayerSynchronizationMessage.java b/src/org/apollo/game/message/impl/PlayerSynchronizationMessage.java
index 8435f6b0..88f8bfde 100644
--- a/src/org/apollo/game/message/impl/PlayerSynchronizationMessage.java
+++ b/src/org/apollo/game/message/impl/PlayerSynchronizationMessage.java
@@ -53,8 +53,7 @@ public final class PlayerSynchronizationMessage extends Message {
* @param localPlayers The number of local players.
* @param segments A list of segments.
*/
- public PlayerSynchronizationMessage(Position lastKnownSector, Position position, boolean sectorChanged,
- SynchronizationSegment segment, int localPlayers, List segments) {
+ public PlayerSynchronizationMessage(Position lastKnownSector, Position position, boolean sectorChanged, SynchronizationSegment segment, int localPlayers, List segments) {
this.lastKnownSector = lastKnownSector;
this.position = position;
this.sectorChanged = sectorChanged;
diff --git a/src/org/apollo/game/model/Appearance.java b/src/org/apollo/game/model/Appearance.java
index 4ece0496..6bb6a961 100644
--- a/src/org/apollo/game/model/Appearance.java
+++ b/src/org/apollo/game/model/Appearance.java
@@ -14,8 +14,8 @@ public final class Appearance {
/**
* The default appearance.
*/
- public static final Appearance DEFAULT_APPEARANCE = new Appearance(Gender.MALE, new int[] { 0, 10, 18, 26, 33, 36, 42 },
- new int[5]);
+ public static final Appearance DEFAULT_APPEARANCE = new Appearance(Gender.MALE, new int[] { 0, 10, 18, 26, 33, 36,
+ 42 }, new int[5]);
/**
* The array of clothing/skin colors.
diff --git a/src/org/apollo/game/model/Direction.java b/src/org/apollo/game/model/Direction.java
index 41f3ae6c..61015ca3 100644
--- a/src/org/apollo/game/model/Direction.java
+++ b/src/org/apollo/game/model/Direction.java
@@ -1,9 +1,5 @@
package org.apollo.game.model;
-
-
-
-
/**
* Represents a single movement direction.
*
@@ -90,7 +86,7 @@ public enum Direction {
return Direction.WEST;
}
}
-
+
return Direction.NONE;
}
diff --git a/src/org/apollo/game/model/Position.java b/src/org/apollo/game/model/Position.java
index 24c1e3de..ac42c9b7 100644
--- a/src/org/apollo/game/model/Position.java
+++ b/src/org/apollo/game/model/Position.java
@@ -221,13 +221,12 @@ public final class Position {
public boolean isWithinDistance(Position other, int distance) {
int deltaX = Math.abs(getX() - other.getX());
int deltaY = Math.abs(getY() - other.getY());
- return deltaX <= distance && deltaY <= distance;
+ return deltaX <= distance && deltaY <= distance && getHeight() == other.getHeight();
}
@Override
public String toString() {
- return MoreObjects.toStringHelper(this).add("x", getX()).add("y", getY()).add("height", getHeight())
- .add("sector", getSectorCoordinates()).toString();
+ return MoreObjects.toStringHelper(this).add("x", getX()).add("y", getY()).add("height", getHeight()).add("sector", getSectorCoordinates()).toString();
}
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/World.java b/src/org/apollo/game/model/World.java
index 29bd4e36..98335a0b 100644
--- a/src/org/apollo/game/model/World.java
+++ b/src/org/apollo/game/model/World.java
@@ -30,6 +30,7 @@ import org.apollo.game.model.event.EventListener;
import org.apollo.game.model.event.EventListenerChainSet;
import org.apollo.game.scheduling.ScheduledTask;
import org.apollo.game.scheduling.Scheduler;
+import org.apollo.game.scheduling.impl.NpcMovementTask;
import org.apollo.io.EquipmentDefinitionParser;
import org.apollo.util.MobRepository;
import org.apollo.util.NameUtil;
@@ -97,6 +98,11 @@ public final class World {
*/
private final EventListenerChainSet events = new EventListenerChainSet();
+ /**
+ * The ScheduledTask that moves Npcs.
+ */
+ private NpcMovementTask npcMovement;
+
/**
* The {@link MobRepository} of {@link Npc}s.
*/
@@ -113,7 +119,7 @@ public final class World {
private final Map players = new HashMap<>();
/**
- * The {@link PluginManager}. TODO: better place than here!!
+ * The {@link PluginManager}.
*/
private PluginManager pluginManager;
@@ -178,7 +184,7 @@ public final class World {
}
/**
- * Gets the plugin manager. TODO should this be here?
+ * Gets the plugin manager.
*
* @return The plugin manager.
*/
@@ -242,8 +248,11 @@ public final class World {
placeEntities(objects);
logger.fine("Loaded " + objects.length + " static objects.");
+ npcMovement = new NpcMovementTask(); // Must be exactly here because of ordering issues.
+ scheduler.schedule(npcMovement);
+
manager.start();
- pluginManager = manager; // TODO move!!
+ pluginManager = manager;
}
/**
@@ -285,6 +294,10 @@ public final class World {
if (success) {
Sector sector = sectors.fromPosition(npc.getPosition());
sector.addEntity(npc);
+
+ if (npc.hasBoundaries()) {
+ npcMovement.addNpc(npc);
+ }
} else {
logger.warning("Failed to register npc, repository capacity reached: [count=" + npcRepository.size() + "]");
}
diff --git a/src/org/apollo/game/model/area/Sector.java b/src/org/apollo/game/model/area/Sector.java
index 3f9dfcd9..bc03933f 100644
--- a/src/org/apollo/game/model/area/Sector.java
+++ b/src/org/apollo/game/model/area/Sector.java
@@ -14,6 +14,7 @@ import org.apollo.game.model.area.collision.CollisionMatrix;
import org.apollo.game.model.entity.Entity;
import org.apollo.game.model.entity.Entity.EntityType;
+import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
@@ -174,13 +175,14 @@ public final class Sector {
Set local = entities.get(old);
- Preconditions.checkArgument(local != null && local.remove(entity), "Entity belongs in this sector but does not exist.");
+ if (local == null || !local.remove(entity)) {
+ throw new IllegalArgumentException("Entity belongs in this sector (" + this + ") but does not exist.");
+ }
local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE));
local.add(entity);
notifyListeners(entity, SectorOperation.MOVE);
-
}
/**
@@ -205,7 +207,9 @@ public final class Sector {
Set local = entities.get(position);
- Preconditions.checkArgument(local != null && local.remove(entity), "Entity belongs in this sector but does not exist.");
+ if (local == null || !local.remove(entity)) {
+ throw new IllegalArgumentException("Entity belongs in this sector (" + this + ") but does not exist.");
+ }
notifyListeners(entity, SectorOperation.REMOVE);
}
@@ -221,9 +225,14 @@ public final class Sector {
*/
public boolean traversable(Position position, EntityType entity, Direction direction) {
CollisionMatrix matrix = matrices[position.getHeight()];
- int x = position.getLocalX(), y = position.getLocalY();
+ int x = position.getX(), y = position.getY();
- return matrix.traversable(x, y, entity, direction);
+ return !matrix.untraversable(x % SECTOR_SIZE, y % SECTOR_SIZE, entity, direction);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("coordinates", coordinates).toString();
}
/**
@@ -233,8 +242,7 @@ public final class Sector {
* @throws IllegalArgumentException If the specified position is not included in this sector.
*/
private void checkPosition(Position position) {
- Preconditions.checkArgument(coordinates.equals(SectorCoordinates.fromPosition(position)),
- "Position is not included in this sector.");
+ Preconditions.checkArgument(coordinates.equals(SectorCoordinates.fromPosition(position)), "Position is not included in this sector.");
}
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/area/collision/CollisionFlag.java b/src/org/apollo/game/model/area/collision/CollisionFlag.java
index 20554176..f680013c 100644
--- a/src/org/apollo/game/model/area/collision/CollisionFlag.java
+++ b/src/org/apollo/game/model/area/collision/CollisionFlag.java
@@ -9,16 +9,16 @@ import org.apollo.game.model.entity.Entity.EntityType;
*/
public enum CollisionFlag {
- /**
- * The walk east flag.
- */
- MOB_EAST(1),
-
/**
* The walk north flag.
*/
MOB_NORTH(0),
+ /**
+ * The walk east flag.
+ */
+ MOB_EAST(1),
+
/**
* The walk south flag.
*/
@@ -29,16 +29,16 @@ public enum CollisionFlag {
*/
MOB_WEST(3),
- /**
- * The projectile east flag.
- */
- PROJECTILE_EAST(5),
-
/**
* The projectile north flag.
*/
PROJECTILE_NORTH(4),
+ /**
+ * The projectile east flag.
+ */
+ PROJECTILE_EAST(5),
+
/**
* The projectile south flag.
*/
diff --git a/src/org/apollo/game/model/area/collision/CollisionMatrix.java b/src/org/apollo/game/model/area/collision/CollisionMatrix.java
index 9630ad45..8a97aa3c 100644
--- a/src/org/apollo/game/model/area/collision/CollisionMatrix.java
+++ b/src/org/apollo/game/model/area/collision/CollisionMatrix.java
@@ -15,6 +15,16 @@ import com.google.common.base.Preconditions;
*/
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;
+
/**
* Creates an array of CollisionMatrix objects, all of the specified width and length.
*
@@ -29,16 +39,6 @@ public final class CollisionMatrix {
return matrices;
}
- /**
- * 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.
*/
@@ -76,13 +76,7 @@ public final class CollisionMatrix {
* @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;
+ return Arrays.stream(flags).allMatch(flag -> (get(x, y) & flag.asByte()) != 0);
}
/**
@@ -95,13 +89,7 @@ public final class CollisionMatrix {
* @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;
+ return Arrays.stream(flags).anyMatch(flag -> (get(x, y) & flag.asByte()) != 0);
}
/**
@@ -173,21 +161,20 @@ public final class CollisionMatrix {
@Override
public String toString() {
- return MoreObjects.toStringHelper(this).add("width", width).add("length", length).add("matrix", Arrays.toString(matrix))
- .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.
+ * Returns whether or not an Entity of the specified {@link EntityType type} cannot 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.
+ * @return {@code true} if the tile at the specified coordinate pair is not traversable, {@code false} if not.
*/
- public boolean traversable(int x, int y, EntityType entity, Direction direction) {
+ public boolean untraversable(int x, int y, EntityType entity, Direction direction) {
CollisionFlag[] flags = CollisionFlag.forType(entity);
int north = 0, east = 1, south = 2, west = 3;
diff --git a/src/org/apollo/game/model/area/package-info.java b/src/org/apollo/game/model/area/package-info.java
index a39c9f88..8a1f2a6b 100644
--- a/src/org/apollo/game/model/area/package-info.java
+++ b/src/org/apollo/game/model/area/package-info.java
@@ -1,5 +1,5 @@
/**
- * Contains classes which represent in-game sectors - blocks of 8x8 tiles used to store ground items, temporary objects, etc.
- * efficiently.
+ * Contains classes which represent in-game sectors - blocks of 8x8 tiles used to store ground items, temporary objects,
+ * etc. efficiently.
*/
package org.apollo.game.model.area;
\ No newline at end of file
diff --git a/src/org/apollo/game/model/def/ItemDefinition.java b/src/org/apollo/game/model/def/ItemDefinition.java
index 7e175a25..bbbad0c2 100644
--- a/src/org/apollo/game/model/def/ItemDefinition.java
+++ b/src/org/apollo/game/model/def/ItemDefinition.java
@@ -388,7 +388,7 @@ public final class ItemDefinition {
public void toNote() {
if (isNote()) {
if (description != null && description.startsWith("Swap this note at any bank for ")) {
- return; // already converted TODO better way of checking?
+ return; // already converted.
}
ItemDefinition infoDef = lookup(noteInfoId);
diff --git a/src/org/apollo/game/model/def/ObjectDefinition.java b/src/org/apollo/game/model/def/ObjectDefinition.java
index a890b4cc..de1af1c9 100644
--- a/src/org/apollo/game/model/def/ObjectDefinition.java
+++ b/src/org/apollo/game/model/def/ObjectDefinition.java
@@ -178,8 +178,7 @@ public final class ObjectDefinition {
/**
* Indicates the impenetrability of this object.
*
- * @return {@code true} if this object is impenetrable, otherwise
- * {@code false}.
+ * @return {@code true} if this object is impenetrable, otherwise {@code false}.
*/
public boolean isImpenetrable() {
return impenetrable;
@@ -188,8 +187,7 @@ public final class ObjectDefinition {
/**
* Indicates the interactivity of this object.
*
- * @return {@code true} if the object is interactive, otherwise
- * {@code false}.
+ * @return {@code true} if the object is interactive, otherwise {@code false}.
*/
public boolean isInteractive() {
return interactive;
@@ -198,8 +196,7 @@ public final class ObjectDefinition {
/**
* Indicates whether or not this object obstructs the ground.
*
- * @return {@code true} if the object obstructs the ground otherwise
- * {@code false}.
+ * @return {@code true} if the object obstructs the ground otherwise {@code false}.
*/
public boolean isObstructive() {
return obstructive;
diff --git a/src/org/apollo/game/model/def/package-info.java b/src/org/apollo/game/model/def/package-info.java
index c214a056..56c13bf9 100644
--- a/src/org/apollo/game/model/def/package-info.java
+++ b/src/org/apollo/game/model/def/package-info.java
@@ -1,5 +1,4 @@
/**
- * Contains definition classes which contain information about types of items,
- * NPCs, etc.
+ * Contains definition classes which contain information about types of items, NPCs, etc.
*/
package org.apollo.game.model.def;
\ No newline at end of file
diff --git a/src/org/apollo/game/model/entity/GameObject.java b/src/org/apollo/game/model/entity/GameObject.java
index 36b596a2..8592c7a1 100644
--- a/src/org/apollo/game/model/entity/GameObject.java
+++ b/src/org/apollo/game/model/entity/GameObject.java
@@ -89,8 +89,7 @@ public final class GameObject extends Entity {
@Override
public String toString() {
- return MoreObjects.toStringHelper(this).add("id", getId()).add("type", getType()).add("rotation", getRotation())
- .toString();
+ return MoreObjects.toStringHelper(this).add("id", getId()).add("type", getType()).add("rotation", getRotation()).toString();
}
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/entity/Mob.java b/src/org/apollo/game/model/entity/Mob.java
index 022e0692..e5a8cf33 100644
--- a/src/org/apollo/game/model/entity/Mob.java
+++ b/src/org/apollo/game/model/entity/Mob.java
@@ -194,8 +194,8 @@ public abstract class Mob extends Entity {
*/
public final Direction[] getDirections() {
if (firstDirection != Direction.NONE) {
- return secondDirection == Direction.NONE ? new Direction[] { firstDirection } : new Direction[] { firstDirection,
- secondDirection };
+ return secondDirection == Direction.NONE ? new Direction[] { firstDirection } : new Direction[] {
+ firstDirection, secondDirection };
}
return Direction.EMPTY_DIRECTION_ARRAY;
diff --git a/src/org/apollo/game/model/entity/Npc.java b/src/org/apollo/game/model/entity/Npc.java
index 66532a96..12ccc2a9 100644
--- a/src/org/apollo/game/model/entity/Npc.java
+++ b/src/org/apollo/game/model/entity/Npc.java
@@ -1,6 +1,5 @@
package org.apollo.game.model.entity;
-import java.util.Arrays;
import java.util.Optional;
import org.apollo.game.model.Position;
@@ -20,9 +19,9 @@ import com.google.common.base.Preconditions;
public final class Npc extends Mob {
/**
- * The positions representing the bounds (i.e. walking limits) of this Npc.
+ * The Positions representing the boundaries (i.e. walking limits) of this Npc.
*/
- private Position[] boundary;
+ private Optional boundaries;
/**
* Creates a new Npc with the specified id and {@link Position}.
@@ -31,18 +30,20 @@ public final class Npc extends Mob {
* @param position The position.
*/
public Npc(int id, Position position) {
- this(position, NpcDefinition.lookup(id));
+ this(position, NpcDefinition.lookup(id), null);
}
/**
* Creates a new Npc with the specified {@link NpcDefinition} and {@link Position}.
*
- * @param position The position.
- * @param definition The definition.
+ * @param position The Position.
+ * @param definition The NpcDefinition.
+ * @param boundaries The boundary Positions.
*/
- public Npc(Position position, NpcDefinition definition) {
+ public Npc(Position position, NpcDefinition definition, Position[] boundaries) {
super(position, definition);
+ this.boundaries = Optional.ofNullable(boundaries);
init();
}
@@ -50,19 +51,19 @@ public final class Npc extends Mob {
public boolean equals(Object obj) {
if (obj instanceof Npc) {
Npc other = (Npc) obj;
- return position.equals(other.position) && Arrays.equals(boundary, other.boundary) && getId() == other.getId();
+ return index == other.index && getId() == other.getId();
}
return false;
}
/**
- * Gets the boundary of this Npc.
+ * Gets the boundaries of this Npc.
*
- * @return The boundary.
+ * @return The boundaries.
*/
- public Position[] getBoundary() {
- return boundary.clone();
+ public Optional getBoundaries() {
+ return boundaries.isPresent() ? Optional.of(boundaries.get().clone()) : Optional.empty();
}
@Override
@@ -79,30 +80,29 @@ public final class Npc extends Mob {
return definition.get().getId();
}
+ /**
+ * Returns whether or not this Npc has boundaries.
+ *
+ * @return {@code true} if this Npc has boundaries, {@code false} if not.
+ */
+ public boolean hasBoundaries() {
+ return boundaries.isPresent();
+ }
+
@Override
public int hashCode() {
final int prime = 31;
- int result = prime * position.hashCode() + Arrays.hashCode(boundary);
- return prime * result + getId();
+ return prime * index + getId();
}
/**
- * Indicates whether or not this Npc is bound to a specific set of coordinates.
+ * Sets the boundaries of this Npc.
*
- * @return {@code true} if the Npc is bound, otherwise {@code false}.
+ * @param boundaries The boundaries.
*/
- public boolean isBound() {
- return boundary == null;
- }
-
- /**
- * Sets the boundary of this Npc.
- *
- * @param boundary The boundary.
- */
- public void setBoundary(Position[] boundary) {
- Preconditions.checkArgument(boundary.length == 4, "Boundary count must be 4.");
- this.boundary = boundary.clone();
+ public void setBoundaries(Position[] boundaries) {
+ Preconditions.checkArgument(boundaries.length == 2, "Boundary count must be 2.");
+ this.boundaries = Optional.of(boundaries.clone());
}
@Override
diff --git a/src/org/apollo/game/model/entity/Player.java b/src/org/apollo/game/model/entity/Player.java
index 79f9efc1..9c7e8552 100644
--- a/src/org/apollo/game/model/entity/Player.java
+++ b/src/org/apollo/game/model/entity/Player.java
@@ -686,7 +686,7 @@ public final class Player extends Mob {
*/
public void sendInitialMessages() {
blockSet.add(SynchronizationBlock.createAppearanceBlock(this));
- send(new IdAssignmentMessage(index, members)); // TODO should this be sent when we reconnect?
+ send(new IdAssignmentMessage(index, members));
sendMessage("Welcome to RuneScape.");
int[] tabs = InterfaceConstants.DEFAULT_INVENTORY_TABS;
@@ -943,8 +943,7 @@ public final class Player extends Mob {
@Override
public String toString() {
- return MoreObjects.toStringHelper(this).add("username", getUsername()).add("privilege", privilegeLevel)
- .add("client version", getClientVersion()).toString();
+ return MoreObjects.toStringHelper(this).add("username", getUsername()).add("privilege", privilegeLevel).add("client version", getClientVersion()).toString();
}
/**
@@ -968,11 +967,9 @@ public final class Player extends Mob {
InventoryListener fullBankListener = new FullInventoryListener(this, FullInventoryListener.FULL_BANK_MESSAGE);
InventoryListener appearanceListener = new AppearanceInventoryListener(this);
- InventoryListener syncInventoryListener = new SynchronizationInventoryListener(this,
- SynchronizationInventoryListener.INVENTORY_ID);
+ InventoryListener syncInventoryListener = new SynchronizationInventoryListener(this, SynchronizationInventoryListener.INVENTORY_ID);
InventoryListener syncBankListener = new SynchronizationInventoryListener(this, BankConstants.BANK_INVENTORY_ID);
- InventoryListener syncEquipmentListener = new SynchronizationInventoryListener(this,
- SynchronizationInventoryListener.EQUIPMENT_ID);
+ InventoryListener syncEquipmentListener = new SynchronizationInventoryListener(this, SynchronizationInventoryListener.EQUIPMENT_ID);
inventory.addListener(syncInventoryListener);
inventory.addListener(fullInventoryListener);
diff --git a/src/org/apollo/game/model/entity/Skill.java b/src/org/apollo/game/model/entity/Skill.java
index f9f9c54b..5db56e22 100644
--- a/src/org/apollo/game/model/entity/Skill.java
+++ b/src/org/apollo/game/model/entity/Skill.java
@@ -115,9 +115,9 @@ public final class Skill {
/**
* The skill names.
*/
- private static final String[] SKILL_NAMES = { "Attack", "Defence", "Strength", "Hitpoints", "Ranged", "Prayer", "Magic",
- "Cooking", "Woodcutting", "Fletching", "Fishing", "Firemaking", "Crafting", "Smithing", "Mining", "Herblore",
- "Agility", "Thieving", "Slayer", "Farming", "Runecraft" };
+ private static final String[] SKILL_NAMES = { "Attack", "Defence", "Strength", "Hitpoints", "Ranged", "Prayer",
+ "Magic", "Cooking", "Woodcutting", "Fletching", "Fishing", "Firemaking", "Crafting", "Smithing", "Mining",
+ "Herblore", "Agility", "Thieving", "Slayer", "Farming", "Runecraft" };
/**
* Gets the name of a skill.
diff --git a/src/org/apollo/game/model/entity/SkillSet.java b/src/org/apollo/game/model/entity/SkillSet.java
index 9cfbf2aa..c3b61223 100644
--- a/src/org/apollo/game/model/entity/SkillSet.java
+++ b/src/org/apollo/game/model/entity/SkillSet.java
@@ -57,8 +57,7 @@ public final class SkillSet {
* @return The minimum level.
*/
public static int getLevelForExperience(double experience) {
- Preconditions.checkArgument(experience >= 0 && experience <= MAXIMUM_EXP, "Experience must be between 0 and "
- + MAXIMUM_EXP + ", inclusive.");
+ Preconditions.checkArgument(experience >= 0 && experience <= MAXIMUM_EXP, "Experience must be between 0 and " + MAXIMUM_EXP + ", inclusive.");
for (int level = 1; level <= 98; level++) {
if (experience < EXPERIENCE_FOR_LEVEL[level + 1]) {
diff --git a/src/org/apollo/game/model/entity/attr/AttributeMap.java b/src/org/apollo/game/model/entity/attr/AttributeMap.java
index e1f0ce9b..a4410c8a 100644
--- a/src/org/apollo/game/model/entity/attr/AttributeMap.java
+++ b/src/org/apollo/game/model/entity/attr/AttributeMap.java
@@ -80,8 +80,7 @@ public final class AttributeMap {
AttributeDefinition definition = getDefinition(name);
Preconditions.checkNotNull(definition, "Attributes must be defined before their value can be retreived.");
- return (Attribute) attributes.computeIfAbsent(name,
- key -> createAttribute(definition.getDefault(), definition.getType()));
+ return (Attribute) attributes.computeIfAbsent(name, key -> createAttribute(definition.getDefault(), definition.getType()));
}
/**
diff --git a/src/org/apollo/game/model/entity/path/AStarPathfindingAlgorithm.java b/src/org/apollo/game/model/entity/path/AStarPathfindingAlgorithm.java
index eb06d263..6c10ed58 100644
--- a/src/org/apollo/game/model/entity/path/AStarPathfindingAlgorithm.java
+++ b/src/org/apollo/game/model/entity/path/AStarPathfindingAlgorithm.java
@@ -23,7 +23,7 @@ import org.apollo.game.model.Position;
*
* @author Major
*/
-final class AStarPathfindingAlgorithm extends PathfindingAlgorithm {
+public final class AStarPathfindingAlgorithm extends PathfindingAlgorithm {
/**
* The heuristic.
diff --git a/src/org/apollo/game/model/entity/path/PathfindingAlgorithm.java b/src/org/apollo/game/model/entity/path/PathfindingAlgorithm.java
index 104e67aa..eaea81db 100644
--- a/src/org/apollo/game/model/entity/path/PathfindingAlgorithm.java
+++ b/src/org/apollo/game/model/entity/path/PathfindingAlgorithm.java
@@ -1,7 +1,7 @@
package org.apollo.game.model.entity.path;
import java.util.Deque;
-import java.util.Set;
+import java.util.Optional;
import org.apollo.game.model.Direction;
import org.apollo.game.model.Position;
@@ -9,7 +9,8 @@ import org.apollo.game.model.World;
import org.apollo.game.model.area.Sector;
import org.apollo.game.model.area.SectorRepository;
import org.apollo.game.model.entity.Entity.EntityType;
-import org.apollo.game.model.entity.GameObject;
+
+import com.google.common.base.Preconditions;
/**
* An algorithm used to find a path between two {@link Position}s.
@@ -19,46 +20,48 @@ import org.apollo.game.model.entity.GameObject;
abstract class PathfindingAlgorithm {
/**
- * The repository of sectors.
+ * The repository of Sectors.
*/
- private static final SectorRepository repository = World.getWorld().getSectorRepository();
+ private static final SectorRepository REPOSITORY = World.getWorld().getSectorRepository();
/**
* Finds a valid path from the origin {@link Position} to the target one.
*
- * @param origin The origin position.
- * @param target The target position.
- * @return The {@link Deque} containing the positions to go through.
+ * @param origin The origin Position.
+ * @param target The target Position.
+ * @return The {@link Deque} containing the Positions to go through.
*/
public abstract Deque find(Position origin, Position target);
/**
- * Returns whether or not the tile at the specified position is walkable. FIXME do this properly w/tile collision
- * data!
- *
- * @param position The {@link Position}.
- * @return {@code true} if the tile is walkable, otherwise {@code false}.
+ * Returns whether or not a {@link Position} walking one step in any of the specified {@link Direction}s would lead
+ * to is traversable.
+ *
+ * @param current The current Position.
+ * @param directions The Directions that should be checked.
+ * @return {@code true} if any of the Directions lead to a traversable tile, otherwise {@code false}.
*/
- protected boolean traversable(Position position) {
- Sector sector = repository.get(position.getSectorCoordinates());
- Set objects = sector.getEntities(position, EntityType.GAME_OBJECT);
-
- return objects.stream().anyMatch(object -> object.getDefinition().isSolid());
+ protected boolean traversable(Position current, Direction... directions) {
+ return traversable(current, Optional.empty(), directions);
}
/**
- * Returns whether or not the {@link Position}s walking one step in a specified {@link Direction} would lead to is
- * traversable.
+ * Returns whether or not a {@link Position} walking one step in any of the specified {@link Direction}s would lead
+ * to is traversable.
*
- * @param position The starting position.
- * @param directions The directions that should be checked.
- * @return {@code true} if any of the directions lead to a traversable tile, otherwise {@code false}.
+ * @param current The current Position.
+ * @param boundaries The {@link Optional} containing the Position boundaries.
+ * @param directions The Directions that should be checked.
+ * @return {@code true} if any of the Directions lead to a traversable tile, otherwise {@code false}.
*/
- protected boolean traversable(Position position, Direction... directions) {
- int height = position.getHeight();
+ protected boolean traversable(Position current, Optional boundaries, Direction... directions) {
+ Preconditions.checkArgument(directions != null && directions.length > 0, "Directions array cannot be null.");
+ int height = current.getHeight();
+
+ Position[] positions = boundaries.isPresent() ? boundaries.get() : new Position[0];
for (Direction direction : directions) {
- int x = position.getX(), y = position.getY();
+ int x = current.getX(), y = current.getY();
int value = direction.toInteger();
if (value >= Direction.NORTH_WEST.toInteger() && value <= Direction.NORTH_EAST.toInteger()) {
@@ -73,7 +76,9 @@ abstract class PathfindingAlgorithm {
x--;
}
- if (traversable(new Position(x, y, height))) {
+ Position next = new Position(x, y, height);
+ Sector sector = REPOSITORY.get(next.getSectorCoordinates());
+ if (sector.traversable(next, EntityType.NPC, direction) && (positions.length == 0 || inside(next, positions))) {
return true;
}
}
@@ -81,4 +86,18 @@ abstract class PathfindingAlgorithm {
return false;
}
+ /**
+ * Returns whether or not the specified {@link Position} is inside the specified {@code boundary}.
+ *
+ * @param position The Position.
+ * @param boundary The boundary Positions.
+ * @return {@code true} if the specified Position is inside the boundary, {@code false} if not.
+ */
+ private boolean inside(Position position, Position[] boundary) {
+ int x = position.getX(), y = position.getY();
+ Position min = boundary[0], max = boundary[1];
+
+ return x >= min.getX() && y >= min.getY() && x <= max.getX() && y <= max.getY();
+ }
+
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/entity/path/SimplePathfindingAlgorithm.java b/src/org/apollo/game/model/entity/path/SimplePathfindingAlgorithm.java
index 0ef4b77d..8789eb38 100644
--- a/src/org/apollo/game/model/entity/path/SimplePathfindingAlgorithm.java
+++ b/src/org/apollo/game/model/entity/path/SimplePathfindingAlgorithm.java
@@ -2,6 +2,7 @@ package org.apollo.game.model.entity.path;
import java.util.ArrayDeque;
import java.util.Deque;
+import java.util.Optional;
import org.apollo.game.model.Direction;
import org.apollo.game.model.Position;
@@ -12,7 +13,12 @@ import org.apollo.game.model.Position;
*
* @author Major
*/
-final class SimplePathfindingAlgorithm extends PathfindingAlgorithm {
+public final class SimplePathfindingAlgorithm extends PathfindingAlgorithm {
+
+ /**
+ * The Optional containing the boundary Positions.
+ */
+ private Optional boundaries = Optional.empty();
@Override
public Deque find(Position origin, Position target) {
@@ -22,6 +28,19 @@ final class SimplePathfindingAlgorithm extends PathfindingAlgorithm {
return addHorizontal(origin, target, positions);
}
+ /**
+ * Finds a valid path from the origin {@link Position} to the target one.
+ *
+ * @param origin The origin Position.
+ * @param target The target Position.
+ * @param boundaries The boundary Positions, which are marking as untraversable.
+ * @return The {@link Deque} containing the Positions to go through.
+ */
+ public Deque find(Position origin, Position target, Position[] boundaries) {
+ this.boundaries = Optional.of(boundaries);
+ return find(origin, target);
+ }
+
/**
* Adds the necessary and possible horizontal {@link Position}s to the existing {@link Deque}.
*
@@ -33,33 +52,33 @@ final class SimplePathfindingAlgorithm extends PathfindingAlgorithm {
* if so, we traverse horizontally (see {@link #addHorizontal}); if not, return the current path.
*
*
- * @param current The current position.
+ * @param start The current position.
* @param target The target position.
* @param positions The deque of positions.
* @return The deque of positions containing the path.
*/
- private Deque addHorizontal(Position current, Position target, Deque positions) {
- int x = current.getX(), y = current.getY(), height = current.getHeight();
- int dx = x - target.getX();
+ private Deque addHorizontal(Position start, Position target, Deque positions) {
+ int x = start.getX(), y = start.getY(), height = start.getHeight();
+ int dx = x - target.getX(), dy = y - target.getY();
if (dx > 0) {
- Position west = new Position(x - 1, y, height);
+ Position current = start;
- while (traversable(west) && dx-- > 0) {
- west = new Position(--x, y, height);
- positions.addLast(west);
+ while (traversable(current, boundaries, Direction.WEST) && dx-- > 0) {
+ current = new Position(--x, y, height);
+ positions.addLast(current);
}
} else if (dx < 0) {
- Position east = new Position(x + 1, y, height);
+ Position current = start;
- while (traversable(east) && dx++ < 0) {
- east = new Position(++x, y, height);
- positions.addLast(east);
+ while (traversable(current, boundaries, Direction.EAST) && dx++ < 0) {
+ current = new Position(++x, y, height);
+ positions.addLast(current);
}
}
Position last = new Position(x, y, height);
- if (!current.equals(last) && traversable(last, Direction.NORTH, Direction.SOUTH)) {
+ if (!start.equals(last) && dy != 0 && traversable(last, boundaries, (dy > 0) ? Direction.SOUTH : Direction.NORTH)) {
return addVertical(last, target, positions);
}
@@ -77,33 +96,33 @@ final class SimplePathfindingAlgorithm extends PathfindingAlgorithm {
* if so, we traverse horizontally (see {@link #addHorizontal}); if not, return the current path.
*
*
- * @param current The current position.
+ * @param start The current position.
* @param target The target position.
* @param positions The deque of positions.
* @return The deque of positions containing the path.
*/
- private Deque addVertical(Position current, Position target, Deque positions) {
- int x = current.getX(), y = current.getY(), height = current.getHeight();
- int dy = y - target.getY();
+ private Deque addVertical(Position start, Position target, Deque positions) {
+ int x = start.getX(), y = start.getY(), height = start.getHeight();
+ int dy = y - target.getY(), dx = x - target.getX();
if (dy > 0) {
- Position south = new Position(x, y - 1, height);
+ Position current = start;
- while (traversable(south) && dy-- > 0) {
- south = new Position(x, --y, height);
- positions.addLast(south);
+ while (traversable(current, boundaries, Direction.SOUTH) && dy-- > 0) {
+ current = new Position(x, --y, height);
+ positions.addLast(current);
}
} else if (dy < 0) {
- Position north = new Position(x, y + 1, height);
+ Position current = start;
- while (traversable(north) && dy++ < 0) {
- north = new Position(x, ++y, height);
- positions.addLast(north);
+ while (traversable(current, boundaries, Direction.NORTH) && dy++ < 0) {
+ current = new Position(x, ++y, height);
+ positions.addLast(current);
}
}
Position last = new Position(x, y, height);
- if (!last.equals(target) && traversable(last, Direction.EAST, Direction.WEST)) {
+ if (!last.equals(target) && dx != 0 && traversable(last, boundaries, (dx > 0) ? Direction.WEST : Direction.EAST)) {
return addHorizontal(last, target, positions);
}
diff --git a/src/org/apollo/game/model/event/Event.java b/src/org/apollo/game/model/event/Event.java
index 8d71d84e..fbb225b0 100644
--- a/src/org/apollo/game/model/event/Event.java
+++ b/src/org/apollo/game/model/event/Event.java
@@ -6,7 +6,7 @@ package org.apollo.game.model.event;
* @author Major
*/
public abstract class Event {
-
+
/**
* Indicates whether or not the Event chain has been terminated.
*/
@@ -27,5 +27,5 @@ public abstract class Event {
public final boolean terminated() {
return terminated;
}
-
+
}
\ No newline at end of file
diff --git a/src/org/apollo/game/model/inter/InterfaceConstants.java b/src/org/apollo/game/model/inter/InterfaceConstants.java
index d0e44330..b356f590 100644
--- a/src/org/apollo/game/model/inter/InterfaceConstants.java
+++ b/src/org/apollo/game/model/inter/InterfaceConstants.java
@@ -10,8 +10,8 @@ public class InterfaceConstants {
/**
* The default inventory tab ids.
*/
- public static final int[] DEFAULT_INVENTORY_TABS = { 2423, 3917, 638, 3213, 1644, 5608, 1151, -1, 5065, 5715, 2449, 904, 147,
- 962, };
+ public static final int[] DEFAULT_INVENTORY_TABS = { 2423, 3917, 638, 3213, 1644, 5608, 1151, -1, 5065, 5715, 2449,
+ 904, 147, 962, };
// 6299 = music tab, music disabled
// 4445 = settings tab, music disabled
// 12855 = ancients magic
@@ -19,8 +19,8 @@ public class InterfaceConstants {
/**
* The level-up dialogue interface ids.
*/
- public static final int[] LEVEL_UP_INTERFACES = { 6247, 6253, 6206, 6216, 4443, 6242, 6211, 6226, 4272, 6231, 6258, 4282,
- 6263, 6221, 4416, 6237, 4277, 4261, 12122, 4887, 4267 };
+ public static final int[] LEVEL_UP_INTERFACES = { 6247, 6253, 6206, 6216, 4443, 6242, 6211, 6226, 4272, 6231, 6258,
+ 4282, 6263, 6221, 4416, 6237, 4277, 4261, 12122, 4887, 4267 };
/**
* The quest interface id.
@@ -35,12 +35,13 @@ public class InterfaceConstants {
/**
* The array of widgets that display the text.
*/
- public static final int[] QUEST_TEXT = { 8144, 8145, 8147, 8148, 8149, 8150, 8151, 8152, 8153, 8154, 8155, 8156, 8157, 8158,
- 8159, 8160, 8161, 8162, 8163, 8164, 8165, 8166, 8167, 8168, 8169, 8170, 8171, 8172, 8173, 8174, 8175, 8176, 8177,
- 8178, 8179, 8180, 8181, 8182, 8183, 8184, 8185, 8186, 8187, 8188, 8189, 8190, 8191, 8192, 8193, 8194, 8195, 12174,
- 12175, 12176, 12177, 12178, 12179, 12180, 12181, 12182, 12183, 12184, 12185, 12186, 12187, 12188, 12189, 12190,
- 12191, 12192, 12193, 12194, 12195, 12196, 12197, 12198, 12199, 12200, 12201, 12202, 12203, 12204, 12205, 12206,
- 12207, 12208, 12209, 12210, 12211, 12212, 12213, 12214, 12215, 12216, 12217, 12218, 12219, 12220, 12221, 12222, 12223 };
+ public static final int[] QUEST_TEXT = { 8144, 8145, 8147, 8148, 8149, 8150, 8151, 8152, 8153, 8154, 8155, 8156,
+ 8157, 8158, 8159, 8160, 8161, 8162, 8163, 8164, 8165, 8166, 8167, 8168, 8169, 8170, 8171, 8172, 8173, 8174,
+ 8175, 8176, 8177, 8178, 8179, 8180, 8181, 8182, 8183, 8184, 8185, 8186, 8187, 8188, 8189, 8190, 8191, 8192,
+ 8193, 8194, 8195, 12174, 12175, 12176, 12177, 12178, 12179, 12180, 12181, 12182, 12183, 12184, 12185,
+ 12186, 12187, 12188, 12189, 12190, 12191, 12192, 12193, 12194, 12195, 12196, 12197, 12198, 12199, 12200,
+ 12201, 12202, 12203, 12204, 12205, 12206, 12207, 12208, 12209, 12210, 12211, 12212, 12213, 12214, 12215,
+ 12216, 12217, 12218, 12219, 12220, 12221, 12222, 12223 };
/**
* The quest title widget id.
diff --git a/src/org/apollo/game/model/inv/Inventory.java b/src/org/apollo/game/model/inv/Inventory.java
index 5e9fd664..ac6abbe1 100644
--- a/src/org/apollo/game/model/inv/Inventory.java
+++ b/src/org/apollo/game/model/inv/Inventory.java
@@ -51,7 +51,7 @@ public final class Inventory {
/**
* A flag indicating if events are being fired.
*/
- private boolean firingEvents = true; // TODO: make this reentrant
+ private boolean firingEvents = true;
/**
* The items in this inventory.
diff --git a/src/org/apollo/game/model/package-info.java b/src/org/apollo/game/model/package-info.java
index 109e9321..d0bcf043 100644
--- a/src/org/apollo/game/model/package-info.java
+++ b/src/org/apollo/game/model/package-info.java
@@ -1,5 +1,4 @@
/**
- * Contains classes which represent things in the in-game world such as items,
- * players and NPCs.
+ * Contains classes which represent things in the in-game world such as items, players and NPCs.
*/
package org.apollo.game.model;
\ No newline at end of file
diff --git a/src/org/apollo/game/model/skill/LevelUpSkillListener.java b/src/org/apollo/game/model/skill/LevelUpSkillListener.java
index 953706f8..3539fb67 100644
--- a/src/org/apollo/game/model/skill/LevelUpSkillListener.java
+++ b/src/org/apollo/game/model/skill/LevelUpSkillListener.java
@@ -31,8 +31,7 @@ public final class LevelUpSkillListener extends SkillAdapter {
// TODO show the interface
String name = Skill.getName(id);
String article = LanguageUtil.getIndefiniteArticle(name);
- player.sendMessage("You've just advanced " + article + " " + name + " level! You have reached level "
- + skill.getMaximumLevel() + ".");
+ player.sendMessage("You've just advanced " + article + " " + name + " level! You have reached level " + skill.getMaximumLevel() + ".");
if (Skill.isCombatSkill(id)) {
player.getSkillSet().calculateCombatLevel();
diff --git a/src/org/apollo/game/scheduling/impl/NpcMovementTask.java b/src/org/apollo/game/scheduling/impl/NpcMovementTask.java
new file mode 100644
index 00000000..9a87a94a
--- /dev/null
+++ b/src/org/apollo/game/scheduling/impl/NpcMovementTask.java
@@ -0,0 +1,100 @@
+package org.apollo.game.scheduling.impl;
+
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.PriorityQueue;
+import java.util.Queue;
+import java.util.Random;
+
+import org.apollo.game.model.Position;
+import org.apollo.game.model.entity.Npc;
+import org.apollo.game.model.entity.WalkingQueue;
+import org.apollo.game.model.entity.path.SimplePathfindingAlgorithm;
+import org.apollo.game.scheduling.ScheduledTask;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * A {@link ScheduledTask} that causes {@link Npc}s to randomly walk around in their boundary.
+ *
+ * @author Major
+ */
+public final class NpcMovementTask extends ScheduledTask {
+
+ /**
+ * The delay between executions of this task, in pulses.
+ */
+ private static final int DELAY = 5;
+
+ /**
+ * The random number generator used to calculate how many Npcs should be moved per execution.
+ */
+ private static final Random RANDOM = new Random();
+
+ /**
+ * The comparator used to sort the Npcs in the PriorityQueue.
+ */
+ private static final Comparator RANDOM_COMPARATOR = (first, second) -> RANDOM.nextInt(2) - 1;
+
+ /**
+ * The PathfindingAlgorithm used by this Task.
+ */
+ private final SimplePathfindingAlgorithm algorithm = new SimplePathfindingAlgorithm();
+
+ /**
+ * The Queue of Npcs.
+ */
+ private final Queue npcs = new PriorityQueue<>(RANDOM_COMPARATOR);
+
+ /**
+ * Creates the NpcMovementTask.
+ */
+ public NpcMovementTask() {
+ super(DELAY, false);
+ }
+
+ /**
+ * Adds the {@link Npc} to this {@link ScheduledTask}.
+ *
+ * @param npc The Npc to add.
+ */
+ public void addNpc(Npc npc) {
+ Preconditions.checkArgument(npc.hasBoundaries(), "Cannot add an npc with no boundaries to the NpcMovementTask.");
+ npcs.offer(npc);
+ }
+
+ @Override
+ public void execute() {
+ int count = RANDOM.nextInt(npcs.size() / 50 + 5);
+ for (int iterations = 0; iterations < count; iterations++) {
+ Npc npc = npcs.poll();
+ if (npc == null) {
+ break;
+ }
+
+ Position[] boundary = npc.getBoundaries().get();
+ Position current = npc.getPosition();
+ Position min = boundary[0], max = boundary[1];
+ int currentX = current.getX(), currentY = current.getY();
+
+ boolean negativeX = RANDOM.nextBoolean(), negativeY = RANDOM.nextBoolean();
+ int x = RANDOM.nextInt(negativeX ? (currentX - min.getX()) : (max.getX() - currentX));
+ int y = RANDOM.nextInt(negativeY ? (currentY - min.getY()) : (max.getY() - currentY));
+
+ int dx = negativeX ? -x : x;
+ int dy = negativeY ? -y : y;
+ Position next = new Position(currentX + dx, currentY + dy);
+
+ Deque positions = algorithm.find(current, next, boundary);
+ WalkingQueue queue = npc.getWalkingQueue();
+
+ Position first = positions.pollFirst();
+ if (first != null && queue.addFirstStep(first)) {
+ positions.forEach(npc.getWalkingQueue()::addStep);
+ }
+
+ npcs.offer(npc);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/scheduling/package-info.java b/src/org/apollo/game/scheduling/package-info.java
index 6fe54d13..2362c0e3 100644
--- a/src/org/apollo/game/scheduling/package-info.java
+++ b/src/org/apollo/game/scheduling/package-info.java
@@ -1,5 +1,4 @@
/**
- * Contains classes related to scheduling which allow tasks to be executed in
- * future pulses periodically.
+ * Contains classes related to scheduling which allow tasks to be executed in future pulses periodically.
*/
package org.apollo.game.scheduling;
\ No newline at end of file
diff --git a/src/org/apollo/game/sync/block/AppearanceBlock.java b/src/org/apollo/game/sync/block/AppearanceBlock.java
index c37e8cb8..db91ea42 100644
--- a/src/org/apollo/game/sync/block/AppearanceBlock.java
+++ b/src/org/apollo/game/sync/block/AppearanceBlock.java
@@ -77,8 +77,7 @@ public final class AppearanceBlock extends SynchronizationBlock {
* @param isSkulled Whether or not the player is skulled.
* @param npcId The npc id of the player, if they are appearing as an npc, (otherwise {@code -1}).
*/
- AppearanceBlock(long name, Appearance appearance, int combat, int skill, Inventory equipment, int headIcon,
- boolean isSkulled, int npcId) {
+ AppearanceBlock(long name, Appearance appearance, int combat, int skill, Inventory equipment, int headIcon, boolean isSkulled, int npcId) {
this.name = name;
this.appearance = appearance;
this.combat = combat;
diff --git a/src/org/apollo/game/sync/block/ForceMovementBlock.java b/src/org/apollo/game/sync/block/ForceMovementBlock.java
index b213492a..4b22f050 100644
--- a/src/org/apollo/game/sync/block/ForceMovementBlock.java
+++ b/src/org/apollo/game/sync/block/ForceMovementBlock.java
@@ -48,8 +48,7 @@ public final class ForceMovementBlock extends SynchronizationBlock {
* @param travelDurationY The length of time (in game pulses) the player's movement along the Y-axis will last.
* @param direction The direction the player should move.
*/
- ForceMovementBlock(Position initialPosition, Position finalPosition, int travelDurationX, int travelDurationY,
- Direction direction) {
+ ForceMovementBlock(Position initialPosition, Position finalPosition, int travelDurationX, int travelDurationY, Direction direction) {
this.initialPosition = initialPosition;
this.finalPosition = finalPosition;
this.travelDurationX = travelDurationX;
diff --git a/src/org/apollo/game/sync/block/SynchronizationBlock.java b/src/org/apollo/game/sync/block/SynchronizationBlock.java
index e40d8a1e..b6817c8c 100644
--- a/src/org/apollo/game/sync/block/SynchronizationBlock.java
+++ b/src/org/apollo/game/sync/block/SynchronizationBlock.java
@@ -37,8 +37,7 @@ public abstract class SynchronizationBlock {
int combat = player.getSkillSet().getCombatLevel();
int id = player.hasNpcDefinition() ? player.getDefinition().getId() : -1;
- return new AppearanceBlock(player.getEncodedName(), player.getAppearance(), combat, 0, player.getEquipment(),
- player.getPrayerIcon(), player.isSkulled(), id);
+ return new AppearanceBlock(player.getEncodedName(), player.getAppearance(), combat, 0, player.getEquipment(), player.getPrayerIcon(), player.isSkulled(), id);
}
/**
@@ -72,8 +71,7 @@ public abstract class SynchronizationBlock {
* @param direction The {@link Direction} the player should move.
* @return The force movement block.
*/
- public static SynchronizationBlock createForceMovementBlock(Position initialPosition, Position finalPosition,
- int travelDurationX, int travelDurationY, Direction direction) {
+ public static SynchronizationBlock createForceMovementBlock(Position initialPosition, Position finalPosition, int travelDurationX, int travelDurationY, Direction direction) {
return new ForceMovementBlock(initialPosition, finalPosition, travelDurationX, travelDurationY, direction);
}
@@ -98,10 +96,8 @@ public abstract class SynchronizationBlock {
* @param secondary If the block is a secondary hit or not.
* @return The hit update block.
*/
- public static SynchronizationBlock createHitUpdateBlock(int damage, int type, int currentHealth, int maximumHealth,
- boolean secondary) {
- return secondary ? new SecondaryHitUpdateBlock(damage, type, currentHealth, maximumHealth) : new HitUpdateBlock(damage,
- type, currentHealth, maximumHealth);
+ public static SynchronizationBlock createHitUpdateBlock(int damage, int type, int currentHealth, int maximumHealth, boolean secondary) {
+ return secondary ? new SecondaryHitUpdateBlock(damage, type, currentHealth, maximumHealth) : new HitUpdateBlock(damage, type, currentHealth, maximumHealth);
}
/**
diff --git a/src/org/apollo/game/sync/package-info.java b/src/org/apollo/game/sync/package-info.java
index d2a5a620..6aaee3d8 100644
--- a/src/org/apollo/game/sync/package-info.java
+++ b/src/org/apollo/game/sync/package-info.java
@@ -1,5 +1,5 @@
/**
- * Contains classes related to client synchronization - the process where the
- * client's state is updated by the server so it matches the server's state.
+ * Contains classes related to client synchronization - the process where the client's state is updated by the server so
+ * it matches the server's state.
*/
package org.apollo.game.sync;
\ No newline at end of file
diff --git a/src/org/apollo/game/sync/seg/MovementSegment.java b/src/org/apollo/game/sync/seg/MovementSegment.java
index cbbaadf0..97376791 100644
--- a/src/org/apollo/game/sync/seg/MovementSegment.java
+++ b/src/org/apollo/game/sync/seg/MovementSegment.java
@@ -26,8 +26,7 @@ public final class MovementSegment extends SynchronizationSegment {
*/
public MovementSegment(SynchronizationBlockSet blockSet, Direction[] directions) {
super(blockSet);
- Preconditions.checkArgument(directions.length >= 0 && directions.length < 3,
- "Directions length must be between 0 and 2 inclusive.");
+ Preconditions.checkArgument(directions.length >= 0 && directions.length < 3, "Directions length must be between 0 and 2 inclusive.");
this.directions = directions;
}
@@ -43,14 +42,14 @@ public final class MovementSegment extends SynchronizationSegment {
@Override
public SegmentType getType() {
switch (directions.length) {
- case 0:
- return SegmentType.NO_MOVEMENT;
- case 1:
- return SegmentType.WALK;
- case 2:
- return SegmentType.RUN;
- default:
- throw new IllegalStateException("Direction type unsupported.");
+ case 0:
+ return SegmentType.NO_MOVEMENT;
+ case 1:
+ return SegmentType.WALK;
+ case 2:
+ return SegmentType.RUN;
+ default:
+ throw new IllegalStateException("Direction type unsupported.");
}
}
diff --git a/src/org/apollo/game/sync/seg/package-info.java b/src/org/apollo/game/sync/seg/package-info.java
index 41052407..eb33ad96 100644
--- a/src/org/apollo/game/sync/seg/package-info.java
+++ b/src/org/apollo/game/sync/seg/package-info.java
@@ -1,6 +1,5 @@
/**
- * Contains classes related to synchronization segments. Each segment contains
- * multiple blocks and can be used to add, remove, teleport or move a
- * mob.
+ * Contains classes related to synchronization segments. Each segment contains multiple blocks and can be used to add,
+ * remove, teleport or move a mob.
*/
package org.apollo.game.sync.seg;
\ No newline at end of file
diff --git a/src/org/apollo/game/sync/task/NpcSynchronizationTask.java b/src/org/apollo/game/sync/task/NpcSynchronizationTask.java
index 0f9255d3..b8d63221 100644
--- a/src/org/apollo/game/sync/task/NpcSynchronizationTask.java
+++ b/src/org/apollo/game/sync/task/NpcSynchronizationTask.java
@@ -50,8 +50,7 @@ public final class NpcSynchronizationTask extends SynchronizationTask {
for (Iterator it = localNpcs.iterator(); it.hasNext();) {
Npc npc = it.next();
- if (!npc.isActive() || npc.isTeleporting()
- || npc.getPosition().getLongestDelta(playerPosition) > player.getViewingDistance()) {
+ if (!npc.isActive() || npc.isTeleporting() || npc.getPosition().getLongestDelta(playerPosition) > player.getViewingDistance()) {
it.remove();
segments.add(new RemoveMobSegment());
} else {
@@ -70,8 +69,7 @@ public final class NpcSynchronizationTask extends SynchronizationTask {
}
Position npcPosition = npc.getPosition();
- if (npcPosition.isWithinDistance(playerPosition, player.getViewingDistance()) && !localNpcs.contains(npc)
- && npcPosition.getHeight() == playerPosition.getHeight()) {
+ if (npcPosition.isWithinDistance(playerPosition, player.getViewingDistance()) && !localNpcs.contains(npc) && npcPosition.getHeight() == playerPosition.getHeight()) {
localNpcs.add(npc);
added++;
npc.turnTo(npc.getFacingPosition());
diff --git a/src/org/apollo/game/sync/task/PhasedSynchronizationTask.java b/src/org/apollo/game/sync/task/PhasedSynchronizationTask.java
index 020ea7c8..c6b39da0 100644
--- a/src/org/apollo/game/sync/task/PhasedSynchronizationTask.java
+++ b/src/org/apollo/game/sync/task/PhasedSynchronizationTask.java
@@ -38,7 +38,7 @@ public final class PhasedSynchronizationTask extends SynchronizationTask {
public void run() {
try {
task.run();
- } catch (Exception e) { // TODO better solution...
+ } catch (Exception e) {
e.printStackTrace();
// The executor suppresses any exceptions thrown as part of the task, so we catch and print here as
// rethrowing them does nothing.
diff --git a/src/org/apollo/game/sync/task/PlayerSynchronizationTask.java b/src/org/apollo/game/sync/task/PlayerSynchronizationTask.java
index e3155924..5ffd93f2 100644
--- a/src/org/apollo/game/sync/task/PlayerSynchronizationTask.java
+++ b/src/org/apollo/game/sync/task/PlayerSynchronizationTask.java
@@ -70,8 +70,7 @@ public final class PlayerSynchronizationTask extends SynchronizationTask {
for (Iterator it = localPlayers.iterator(); it.hasNext();) {
Player other = it.next();
- if (!other.isActive() || other.isTeleporting()
- || other.getPosition().getLongestDelta(player.getPosition()) > player.getViewingDistance()) {
+ if (!other.isActive() || other.isTeleporting() || other.getPosition().getLongestDelta(player.getPosition()) > player.getViewingDistance()) {
it.remove();
segments.add(new RemoveMobSegment());
} else {
@@ -91,8 +90,7 @@ public final class PlayerSynchronizationTask extends SynchronizationTask {
break;
}
- if (other != player && other.getPosition().isWithinDistance(player.getPosition(), player.getViewingDistance())
- && !localPlayers.contains(other)) {
+ if (other != player && other.getPosition().isWithinDistance(player.getPosition(), player.getViewingDistance()) && !localPlayers.contains(other)) {
localPlayers.add(other);
added++;
@@ -107,8 +105,7 @@ public final class PlayerSynchronizationTask extends SynchronizationTask {
}
}
- PlayerSynchronizationMessage message = new PlayerSynchronizationMessage(lastKnownSector, player.getPosition(),
- sectorChanged, segment, oldLocalPlayers, segments);
+ PlayerSynchronizationMessage message = new PlayerSynchronizationMessage(lastKnownSector, player.getPosition(), sectorChanged, segment, oldLocalPlayers, segments);
player.send(message);
}
diff --git a/src/org/apollo/game/sync/task/package-info.java b/src/org/apollo/game/sync/task/package-info.java
index b45ff20d..bca00016 100644
--- a/src/org/apollo/game/sync/task/package-info.java
+++ b/src/org/apollo/game/sync/task/package-info.java
@@ -1,6 +1,5 @@
/**
- * Contains classes related to
- * {@link org.apollo.game.sync.task.SynchronizationTask}s, small chunks of work
- * executed during the client synchronization process.
+ * Contains classes related to {@link org.apollo.game.sync.task.SynchronizationTask}s, small chunks of work executed
+ * during the client synchronization process.
*/
package org.apollo.game.sync.task;
\ No newline at end of file
diff --git a/src/org/apollo/io/MessageHandlerChainParser.java b/src/org/apollo/io/MessageHandlerChainParser.java
index 96aa7b8c..27208725 100644
--- a/src/org/apollo/io/MessageHandlerChainParser.java
+++ b/src/org/apollo/io/MessageHandlerChainParser.java
@@ -53,8 +53,7 @@ public final class MessageHandlerChainParser {
* @return A {@link MessageHandlerChainGroup}.
*/
@SuppressWarnings("unchecked")
- public MessageHandlerChainGroup parse() throws IOException, SAXException, ClassNotFoundException, InstantiationException,
- IllegalAccessException {
+ public MessageHandlerChainGroup parse() throws IOException, SAXException, ClassNotFoundException, InstantiationException, IllegalAccessException {
XmlNode messages = parser.parse(is);
if (!messages.getName().equals("messages")) {
throw new IOException("Root node name is not 'messages'.");
@@ -94,8 +93,7 @@ public final class MessageHandlerChainParser {
throw new IOException("Handler node must have a value.");
}
- Class extends MessageHandler>> handlerClass = (Class extends MessageHandler>>) Class
- .forName(handlerClassName);
+ Class extends MessageHandler>> handlerClass = (Class extends MessageHandler>>) Class.forName(handlerClassName);
MessageHandler> handler = handlerClass.newInstance();
handlers.add(handler);
}
diff --git a/src/org/apollo/io/player/BinaryFileUtils.java b/src/org/apollo/io/player/BinaryFileUtils.java
new file mode 100644
index 00000000..00c747a4
--- /dev/null
+++ b/src/org/apollo/io/player/BinaryFileUtils.java
@@ -0,0 +1,56 @@
+package org.apollo.io.player;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apollo.util.NameUtil;
+
+/**
+ * A utility class with common functionality used by the binary player loader/ savers.
+ *
+ * @author Graham
+ * @author Major
+ */
+public final class BinaryFileUtils {
+
+ /**
+ * The Path to the saved games directory.
+ */
+ private static final Path SAVED_GAMES_DIRECTORY = Paths.get("data/savedGames");
+
+ /**
+ * Creates the saved games directory if it does not exist.
+ */
+ static {
+ try {
+ if (!Files.exists(SAVED_GAMES_DIRECTORY)) {
+ Files.createDirectory(SAVED_GAMES_DIRECTORY);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Error creating saved games directory.", e);
+ }
+ }
+
+ /**
+ * Gets the save {@link File} for the specified player.
+ *
+ * @param username The username of the player.
+ * @return The file.
+ */
+ public static Path getFile(String username) {
+ String filtered = NameUtil.decodeBase37(NameUtil.encodeBase37(username));
+ return SAVED_GAMES_DIRECTORY.resolve(filtered + ".dat");
+ }
+
+ /**
+ * Sole private constructor to prevent instantiation.
+ */
+ private BinaryFileUtils() {
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/impl/BinaryPlayerLoader.java b/src/org/apollo/io/player/BinaryPlayerSerializer.java
similarity index 52%
rename from src/org/apollo/io/player/impl/BinaryPlayerLoader.java
rename to src/org/apollo/io/player/BinaryPlayerSerializer.java
index 19d77500..6ae08b98 100644
--- a/src/org/apollo/io/player/impl/BinaryPlayerLoader.java
+++ b/src/org/apollo/io/player/BinaryPlayerSerializer.java
@@ -1,13 +1,20 @@
-package org.apollo.io.player.impl;
+package org.apollo.io.player;
+import java.io.BufferedInputStream;
import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import org.apollo.game.model.Appearance;
import org.apollo.game.model.Item;
@@ -16,6 +23,8 @@ import org.apollo.game.model.entity.Player;
import org.apollo.game.model.entity.Skill;
import org.apollo.game.model.entity.SkillSet;
import org.apollo.game.model.entity.attr.Attribute;
+import org.apollo.game.model.entity.attr.AttributeMap;
+import org.apollo.game.model.entity.attr.AttributePersistence;
import org.apollo.game.model.entity.attr.AttributeType;
import org.apollo.game.model.entity.attr.BooleanAttribute;
import org.apollo.game.model.entity.attr.NumericalAttribute;
@@ -26,8 +35,6 @@ 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.io.player.PlayerLoader;
-import org.apollo.io.player.PlayerLoaderResponse;
import org.apollo.net.codec.login.LoginConstants;
import org.apollo.security.PlayerCredentials;
import org.apollo.util.NameUtil;
@@ -36,28 +43,39 @@ import org.apollo.util.StreamUtil;
import com.lambdaworks.crypto.SCryptUtil;
/**
- * A {@link PlayerLoader} implementation that loads data from a binary file.
+ * A {@link PlayerSerializer} implementation that uses a binary file to store player data.
*
* @author Graham
+ * @author Major
*/
-public final class BinaryPlayerLoader implements PlayerLoader {
+public final class BinaryPlayerSerializer implements PlayerSerializer {
/**
- * The default spawn position.
+ * The Path to the saved games directory.
*/
- private static final Position SPAWN_POSITION = new Position(3093, 3104);
+ private static final Path SAVED_GAMES_DIRECTORY = Paths.get("data/savedGames");
+
+ static {
+ try {
+ if (!Files.exists(SAVED_GAMES_DIRECTORY)) {
+ Files.createDirectory(SAVED_GAMES_DIRECTORY);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException("Error creating saved games directory.", e);
+ }
+ }
@Override
public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) throws IOException {
- File file = BinaryPlayerUtil.getFile(credentials.getUsername());
- if (!file.exists()) {
- Player player = new Player(credentials, SPAWN_POSITION);
- player.getBank().add(995, 25); // 25 coins
+ Path path = getFile(credentials.getUsername());
+ if (!Files.exists(path)) {
+ Player player = new Player(credentials, TUTORIAL_ISLAND_SPAWN);
+
credentials.setPassword(SCryptUtil.scrypt(credentials.getPassword(), 16384, 8, 1));
return new PlayerLoaderResponse(LoginConstants.STATUS_OK, player);
}
- try (DataInputStream in = new DataInputStream(new FileInputStream(file))) {
+ try (DataInputStream in = new DataInputStream(new BufferedInputStream(Files.newInputStream(path)))) {
String name = StreamUtil.readString(in);
String password = StreamUtil.readString(in);
@@ -67,37 +85,35 @@ public final class BinaryPlayerLoader implements PlayerLoader {
credentials.setPassword(password); // Update password to the hashed one.
- PrivilegeLevel privilegeLevel = PrivilegeLevel.valueOf(in.readByte());
+ PrivilegeLevel privilege = PrivilegeLevel.valueOf(in.readByte());
MembershipStatus members = MembershipStatus.valueOf(in.readByte());
PrivacyState chatPrivacy = PrivacyState.valueOf(in.readByte(), true);
PrivacyState friendPrivacy = PrivacyState.valueOf(in.readByte(), false);
PrivacyState tradePrivacy = PrivacyState.valueOf(in.readByte(), false);
- int runEnergy = in.readByte();
ScreenBrightness brightness = ScreenBrightness.valueOf(in.readByte());
int x = in.readUnsignedShort();
int y = in.readUnsignedShort();
int height = in.readUnsignedByte();
- int genderIntValue = in.readUnsignedByte();
- Gender gender = genderIntValue == Gender.MALE.toInteger() ? Gender.MALE : Gender.FEMALE;
+ Gender gender = (in.readUnsignedByte() == Gender.MALE.toInteger()) ? Gender.MALE : Gender.FEMALE;
int[] style = new int[7];
- for (int i = 0; i < style.length; i++) {
- style[i] = in.readUnsignedByte();
+ for (int slot = 0; slot < style.length; slot++) {
+ style[slot] = in.readUnsignedByte();
}
+
int[] colors = new int[5];
- for (int i = 0; i < colors.length; i++) {
- colors[i] = in.readUnsignedByte();
+ for (int slot = 0; slot < colors.length; slot++) {
+ colors[slot] = in.readUnsignedByte();
}
Player player = new Player(credentials, new Position(x, y, height));
- player.setPrivilegeLevel(privilegeLevel);
+ player.setPrivilegeLevel(privilege);
player.setMembers(members);
player.setChatPrivacy(chatPrivacy);
player.setFriendPrivacy(friendPrivacy);
player.setTradePrivacy(tradePrivacy);
- player.setRunEnergy(runEnergy);
player.setScreenBrightness(brightness);
player.setAppearance(new Appearance(gender, style, colors));
@@ -141,6 +157,87 @@ public final class BinaryPlayerLoader implements PlayerLoader {
}
}
+ @Override
+ public void savePlayer(Player player) throws IOException {
+ Path file = getFile(player.getUsername());
+
+ try (DataOutputStream out = new DataOutputStream(Files.newOutputStream(file))) {
+ StreamUtil.writeString(out, player.getUsername());
+ StreamUtil.writeString(out, player.getCredentials().getPassword());
+ out.writeByte(player.getPrivilegeLevel().toInteger());
+ out.writeByte(player.getMembershipStatus().getValue());
+
+ out.writeByte(player.getChatPrivacy().toInteger(true));
+ out.writeByte(player.getFriendPrivacy().toInteger(false));
+ out.writeByte(player.getTradePrivacy().toInteger(false));
+ out.writeByte(player.getScreenBrightness().toInteger());
+
+ Position position = player.getPosition();
+ out.writeShort(position.getX());
+ out.writeShort(position.getY());
+ out.writeByte(position.getHeight());
+
+ Appearance appearance = player.getAppearance();
+ out.writeByte(appearance.getGender().toInteger());
+ int[] style = appearance.getStyle();
+ for (int element : style) {
+ out.writeByte(element);
+ }
+ int[] colors = appearance.getColors();
+ for (int color : colors) {
+ out.writeByte(color);
+ }
+
+ writeInventory(out, player.getInventory());
+ writeInventory(out, player.getEquipment());
+ writeInventory(out, player.getBank());
+
+ SkillSet skills = player.getSkillSet();
+ out.writeByte(skills.size());
+ for (int id = 0; id < skills.size(); id++) {
+ Skill skill = skills.getSkill(id);
+ out.writeByte(skill.getCurrentLevel());
+ out.writeDouble(skill.getExperience());
+ }
+
+ List usernames = player.getFriendUsernames();
+ out.writeByte(usernames.size());
+ for (String username : usernames) {
+ out.writeLong(NameUtil.encodeBase37(username));
+ }
+
+ usernames = player.getIgnoredUsernames();
+ out.writeByte(usernames.size());
+ for (String username : usernames) {
+ out.writeLong(NameUtil.encodeBase37(username));
+ }
+
+ Set>> attributes = player.getAttributes().entrySet();
+ attributes.removeIf(e -> AttributeMap.getDefinition(e.getKey()).getPersistence() != AttributePersistence.PERSISTENT);
+ out.writeInt(attributes.size());
+
+ for (Entry> entry : attributes) {
+ String name = entry.getKey();
+ StreamUtil.writeString(out, name);
+
+ Attribute> attribute = entry.getValue();
+ out.writeByte(attribute.getType().getValue());
+ out.write(attribute.encode());
+ }
+ }
+ }
+
+ /**
+ * Gets the save {@link File} for the specified player.
+ *
+ * @param username The username of the player.
+ * @return The file.
+ */
+ private Path getFile(String username) {
+ String filtered = NameUtil.decodeBase37(NameUtil.encodeBase37(username));
+ return SAVED_GAMES_DIRECTORY.resolve(filtered + ".dat");
+ }
+
/**
* Reads the player's {@link Attribute}s.
*
@@ -148,7 +245,7 @@ public final class BinaryPlayerLoader implements PlayerLoader {
* @return The {@link Map} of attribute names to attributes.
* @throws IOException If there is an error reading from the stream.
*/
- private static Map> readAttributes(DataInputStream in) throws IOException {
+ private Map> readAttributes(DataInputStream in) throws IOException {
int count = in.readInt();
Map> attributes = new HashMap<>(count);
@@ -187,7 +284,7 @@ public final class BinaryPlayerLoader implements PlayerLoader {
* @param inventory The inventory.
* @throws IOException If an I/O error occurs.
*/
- private static void readInventory(DataInputStream in, Inventory inventory) throws IOException {
+ private void readInventory(DataInputStream in, Inventory inventory) throws IOException {
int capacity = in.readUnsignedShort();
inventory.stopFiringEvents();
@@ -206,4 +303,27 @@ public final class BinaryPlayerLoader implements PlayerLoader {
}
}
+ /**
+ * Writes an inventory to the specified output stream.
+ *
+ * @param out The output stream.
+ * @param inventory The inventory.
+ * @throws IOException If an I/O error occurs.
+ */
+ private void writeInventory(DataOutputStream out, Inventory inventory) throws IOException {
+ int capacity = inventory.capacity();
+ out.writeShort(capacity);
+
+ for (int slot = 0; slot < capacity; slot++) {
+ Item item = inventory.get(slot);
+ if (item != null) {
+ out.writeShort(item.getId() + 1);
+ out.writeInt(item.getAmount());
+ } else {
+ out.writeShort(0);
+ out.writeInt(0);
+ }
+ }
+ }
+
}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/impl/DummyPlayerLoader.java b/src/org/apollo/io/player/DummyPlayerSerializer.java
similarity index 52%
rename from src/org/apollo/io/player/impl/DummyPlayerLoader.java
rename to src/org/apollo/io/player/DummyPlayerSerializer.java
index 83b0aedf..5466f385 100644
--- a/src/org/apollo/io/player/impl/DummyPlayerLoader.java
+++ b/src/org/apollo/io/player/DummyPlayerSerializer.java
@@ -1,35 +1,33 @@
-package org.apollo.io.player.impl;
+package org.apollo.io.player;
-import org.apollo.game.model.Position;
import org.apollo.game.model.entity.Player;
import org.apollo.game.model.entity.setting.MembershipStatus;
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;
import org.apollo.security.PlayerCredentials;
/**
- * A dummy {@link PlayerLoader} implementation used for testing purposes.
+ * A {@link PlayerSerializer} that saves no data and returns an administrator member account, ideal for debugging.
*
* @author Graham
+ * @author Major
*/
-public final class DummyPlayerLoader implements PlayerLoader {
-
- /**
- * The default spawn position for players loaded by this loader.
- */
- private static final Position DEFAULT_POSITION = new Position(3093, 3104);
+public final class DummyPlayerSerializer implements PlayerSerializer {
@Override
public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) {
int status = LoginConstants.STATUS_OK;
- Player player = new Player(credentials, DEFAULT_POSITION);
+ Player player = new Player(credentials, TUTORIAL_ISLAND_SPAWN);
player.setPrivilegeLevel(PrivilegeLevel.ADMINISTRATOR);
player.setMembers(MembershipStatus.PAID);
return new PlayerLoaderResponse(status, player);
}
+ @Override
+ public void savePlayer(Player player) {
+ /* discard player */
+ }
+
}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/JdbcPlayerSerializer.java b/src/org/apollo/io/player/JdbcPlayerSerializer.java
new file mode 100644
index 00000000..c8e48cf0
--- /dev/null
+++ b/src/org/apollo/io/player/JdbcPlayerSerializer.java
@@ -0,0 +1,23 @@
+package org.apollo.io.player;
+
+import org.apollo.game.model.entity.Player;
+import org.apollo.security.PlayerCredentials;
+
+/**
+ * A {@link PlayerSerializer} that utilises {@code JDBC} to communicate with an SQL database containing player data.
+ *
+ * @author Major
+ */
+public final class JdbcPlayerSerializer implements PlayerSerializer {
+
+ @Override
+ public void savePlayer(Player player) throws Exception {
+ throw new UnsupportedOperationException("JDBC saving is not supported at this time.");
+ }
+
+ @Override
+ public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) throws Exception {
+ throw new UnsupportedOperationException("JDBC loading is not supported at this time.");
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/PlayerLoader.java b/src/org/apollo/io/player/PlayerLoader.java
deleted file mode 100644
index ff14e622..00000000
--- a/src/org/apollo/io/player/PlayerLoader.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.apollo.io.player;
-
-import org.apollo.security.PlayerCredentials;
-
-/**
- * An interface which may be extended by others which are capable of loading players. For example, implementations might
- * include text-based, binary and SQL loaders.
- *
- * @author Graham
- */
-public interface PlayerLoader {
-
- /**
- * Loads a player.
- *
- * @param credentials The player's credentials.
- * @return The {@link PlayerLoaderResponse}.
- * @throws Exception If an error occurs.
- */
- public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) throws Exception;
-
-}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/PlayerLoaderResponse.java b/src/org/apollo/io/player/PlayerLoaderResponse.java
index 47527d11..6f6bc2b6 100644
--- a/src/org/apollo/io/player/PlayerLoaderResponse.java
+++ b/src/org/apollo/io/player/PlayerLoaderResponse.java
@@ -8,9 +8,10 @@ import org.apollo.net.codec.login.LoginConstants;
import com.google.common.base.Preconditions;
/**
- * A response for the {@link PlayerLoader#loadPlayer(org.apollo.security.PlayerCredentials)} call.
+ * A response for the {@link PlayerSerializer#loadPlayer} call.
*
* @author Graham
+ * @author Major
*/
public final class PlayerLoaderResponse {
diff --git a/src/org/apollo/io/player/PlayerSaver.java b/src/org/apollo/io/player/PlayerSaver.java
deleted file mode 100644
index 1a2def5d..00000000
--- a/src/org/apollo/io/player/PlayerSaver.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.apollo.io.player;
-
-import org.apollo.game.model.entity.Player;
-
-/**
- * An interface which may be implemented by others which are capable of saving players. For example, implementations
- * might include text-based, binary and SQL savers.
- *
- * @author Graham
- */
-public interface PlayerSaver {
-
- /**
- * Saves a player.
- *
- * @param player The player to save.
- * @throws Exception If an error occurs.
- */
- public void savePlayer(Player player) throws Exception;
-
-}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/PlayerSerializer.java b/src/org/apollo/io/player/PlayerSerializer.java
new file mode 100644
index 00000000..4d246a74
--- /dev/null
+++ b/src/org/apollo/io/player/PlayerSerializer.java
@@ -0,0 +1,38 @@
+package org.apollo.io.player;
+
+import org.apollo.game.model.Position;
+import org.apollo.game.model.entity.Player;
+import org.apollo.security.PlayerCredentials;
+
+/**
+ * An interface which may be implemented by others which are capable of serializing and deserializing players. For
+ * example, implementations might include text-based, binary and SQL serializers.
+ *
+ * @author Graham
+ * @author Major
+ */
+public interface PlayerSerializer {
+
+ /**
+ * The spawn point for Players, on Tutorial Island.
+ */
+ Position TUTORIAL_ISLAND_SPAWN = new Position(3093, 3104);
+
+ /**
+ * Loads a {@link Player}.
+ *
+ * @param credentials The {@link PlayerCredentials}.
+ * @return The {@link PlayerLoaderResponse}.
+ * @throws Exception If an error occurs.
+ */
+ public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) throws Exception;
+
+ /**
+ * Saves a {@link Player}.
+ *
+ * @param player The Player to save.
+ * @throws Exception If an error occurs.
+ */
+ public void savePlayer(Player player) throws Exception;
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/impl/BinaryPlayerSaver.java b/src/org/apollo/io/player/impl/BinaryPlayerSaver.java
deleted file mode 100644
index 5fca15a2..00000000
--- a/src/org/apollo/io/player/impl/BinaryPlayerSaver.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package org.apollo.io.player.impl;
-
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import org.apollo.game.model.Appearance;
-import org.apollo.game.model.Item;
-import org.apollo.game.model.Position;
-import org.apollo.game.model.entity.Player;
-import org.apollo.game.model.entity.Skill;
-import org.apollo.game.model.entity.SkillSet;
-import org.apollo.game.model.entity.attr.Attribute;
-import org.apollo.game.model.entity.attr.AttributeMap;
-import org.apollo.game.model.entity.attr.AttributePersistence;
-import org.apollo.game.model.inv.Inventory;
-import org.apollo.io.player.PlayerSaver;
-import org.apollo.util.NameUtil;
-import org.apollo.util.StreamUtil;
-
-/**
- * A {@link PlayerSaver} implementation that saves player data to a binary file.
- *
- * @author Graham
- */
-public final class BinaryPlayerSaver implements PlayerSaver {
-
- @Override
- public void savePlayer(Player player) throws IOException {
- File file = BinaryPlayerUtil.getFile(player.getUsername());
-
- try (DataOutputStream out = new DataOutputStream(new FileOutputStream(file))) {
- StreamUtil.writeString(out, player.getUsername());
- StreamUtil.writeString(out, player.getCredentials().getPassword());
- out.writeByte(player.getPrivilegeLevel().toInteger());
- out.writeByte(player.getMembershipStatus().getValue());
-
- out.writeByte(player.getChatPrivacy().toInteger(true));
- out.writeByte(player.getFriendPrivacy().toInteger(false));
- out.writeByte(player.getTradePrivacy().toInteger(false));
- out.writeByte(player.getRunEnergy());
- out.writeByte(player.getScreenBrightness().toInteger());
-
- Position position = player.getPosition();
- out.writeShort(position.getX());
- out.writeShort(position.getY());
- out.writeByte(position.getHeight());
-
- Appearance appearance = player.getAppearance();
- out.writeByte(appearance.getGender().toInteger());
- int[] style = appearance.getStyle();
- for (int element : style) {
- out.writeByte(element);
- }
- int[] colors = appearance.getColors();
- for (int color : colors) {
- out.writeByte(color);
- }
-
- writeInventory(out, player.getInventory());
- writeInventory(out, player.getEquipment());
- writeInventory(out, player.getBank());
-
- SkillSet skills = player.getSkillSet();
- out.writeByte(skills.size());
- for (int id = 0; id < skills.size(); id++) {
- Skill skill = skills.getSkill(id);
- out.writeByte(skill.getCurrentLevel());
- out.writeDouble(skill.getExperience());
- }
-
- List usernames = player.getFriendUsernames();
- out.writeByte(usernames.size());
- for (String username : usernames) {
- out.writeLong(NameUtil.encodeBase37(username));
- }
-
- usernames = player.getIgnoredUsernames();
- out.writeByte(usernames.size());
- for (String username : usernames) {
- out.writeLong(NameUtil.encodeBase37(username));
- }
-
- Set>> attributes = player.getAttributes().entrySet();
- attributes.removeIf(e -> AttributeMap.getDefinition(e.getKey()).getPersistence() != AttributePersistence.PERSISTENT);
- out.writeInt(attributes.size());
-
- for (Entry> entry : attributes) {
- String name = entry.getKey();
- StreamUtil.writeString(out, name);
-
- Attribute> attribute = entry.getValue();
- out.writeByte(attribute.getType().getValue());
- out.write(attribute.encode());
- }
- }
- }
-
- /**
- * Writes an inventory to the specified output stream.
- *
- * @param out The output stream.
- * @param inventory The inventory.
- * @throws IOException If an I/O error occurs.
- */
- private static void writeInventory(DataOutputStream out, Inventory inventory) throws IOException {
- int capacity = inventory.capacity();
- out.writeShort(capacity);
-
- for (int slot = 0; slot < capacity; slot++) {
- Item item = inventory.get(slot);
- if (item != null) {
- out.writeShort(item.getId() + 1);
- out.writeInt(item.getAmount());
- } else {
- out.writeShort(0);
- out.writeInt(0);
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/impl/BinaryPlayerUtil.java b/src/org/apollo/io/player/impl/BinaryPlayerUtil.java
deleted file mode 100644
index 9c4ca633..00000000
--- a/src/org/apollo/io/player/impl/BinaryPlayerUtil.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.apollo.io.player.impl;
-
-import java.io.File;
-
-import org.apollo.util.NameUtil;
-
-/**
- * A utility class with common functionality used by the binary player loader/ savers.
- *
- * @author Graham
- */
-public final class BinaryPlayerUtil {
-
- /**
- * The saved games directory.
- */
- private static final File SAVED_GAMES_DIRECTORY = new File("data/savedGames");
-
- /**
- * Creates the saved games directory if it does not exist.
- */
- static {
- if (!SAVED_GAMES_DIRECTORY.exists()) {
- SAVED_GAMES_DIRECTORY.mkdir();
- }
- }
-
- /**
- * Gets the save {@link File} for the specified player.
- *
- * @param username The username of the player.
- * @return The file.
- */
- public static File getFile(String username) {
- String filtered = NameUtil.decodeBase37(NameUtil.encodeBase37(username));
- return new File(SAVED_GAMES_DIRECTORY, filtered + ".dat");
- }
-
- /**
- * Default private constructor to prevent instantiation.
- */
- private BinaryPlayerUtil() {
-
- }
-
-}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/impl/DiscardPlayerSaver.java b/src/org/apollo/io/player/impl/DiscardPlayerSaver.java
deleted file mode 100644
index cc00e839..00000000
--- a/src/org/apollo/io/player/impl/DiscardPlayerSaver.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.apollo.io.player.impl;
-
-import org.apollo.game.model.entity.Player;
-import org.apollo.io.player.PlayerSaver;
-
-/**
- * A {@link PlayerSaver} implementation that discards player data.
- *
- * @author Graham
- */
-public final class DiscardPlayerSaver implements PlayerSaver {
-
- @Override
- public void savePlayer(Player player) {
- /* discard player */
- }
-
-}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/impl/JdbcPlayerLoader.java b/src/org/apollo/io/player/impl/JdbcPlayerLoader.java
deleted file mode 100644
index 9be78090..00000000
--- a/src/org/apollo/io/player/impl/JdbcPlayerLoader.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.apollo.io.player.impl;
-
-import org.apollo.io.player.PlayerLoader;
-import org.apollo.io.player.PlayerLoaderResponse;
-import org.apollo.security.PlayerCredentials;
-
-/**
- * A {@link PlayerLoader} that utilises {@code JDBC} to load player files.
- *
- * @author Major
- */
-public final class JdbcPlayerLoader implements PlayerLoader {
-
- @Override
- public PlayerLoaderResponse loadPlayer(PlayerCredentials credentials) throws Exception {
- throw new UnsupportedOperationException("JDBC loading is not supported at this time.");
- }
-
-}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/impl/JdbcPlayerSaver.java b/src/org/apollo/io/player/impl/JdbcPlayerSaver.java
deleted file mode 100644
index bbdc7d3d..00000000
--- a/src/org/apollo/io/player/impl/JdbcPlayerSaver.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.apollo.io.player.impl;
-
-import org.apollo.game.model.entity.Player;
-import org.apollo.io.player.PlayerSaver;
-
-/**
- * A {@link PlayerSaver} that utilises {@code JDBC} to save the player.
- *
- * @author Major
- */
-public final class JdbcPlayerSaver implements PlayerSaver {
-
- @Override
- public void savePlayer(Player player) throws Exception {
- throw new UnsupportedOperationException("JDBC saving is not supported at this time.");
- }
-
-}
\ No newline at end of file
diff --git a/src/org/apollo/io/player/impl/package-info.java b/src/org/apollo/io/player/impl/package-info.java
deleted file mode 100644
index a56ca9ca..00000000
--- a/src/org/apollo/io/player/impl/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Contains various player loader/saver implementations.
- */
-package org.apollo.io.player.impl;
\ No newline at end of file
diff --git a/src/org/apollo/login/LoginService.java b/src/org/apollo/login/LoginService.java
index fc45797f..d5664526 100644
--- a/src/org/apollo/login/LoginService.java
+++ b/src/org/apollo/login/LoginService.java
@@ -8,9 +8,8 @@ import java.util.concurrent.Executors;
import org.apollo.Service;
import org.apollo.game.model.entity.Player;
-import org.apollo.io.player.PlayerLoader;
import org.apollo.io.player.PlayerLoaderResponse;
-import org.apollo.io.player.PlayerSaver;
+import org.apollo.io.player.PlayerSerializer;
import org.apollo.net.codec.login.LoginConstants;
import org.apollo.net.codec.login.LoginRequest;
import org.apollo.net.release.Release;
@@ -25,6 +24,7 @@ import org.xml.sax.SAXException;
* The {@link LoginService} manages {@link LoginRequest}s.
*
* @author Graham
+ * @author Major
*/
public final class LoginService extends Service {
@@ -34,14 +34,9 @@ public final class LoginService extends Service {
private final ExecutorService executor = Executors.newCachedThreadPool(new NamedThreadFactory("LoginService"));
/**
- * The current {@link PlayerLoader}.
+ * The current {@link PlayerSerializer}.
*/
- private PlayerLoader loader;
-
- /**
- * The current {@link PlayerSaver}.
- */
- private PlayerSaver saver;
+ private PlayerSerializer serializer;
/**
* Creates the login service.
@@ -70,24 +65,16 @@ public final class LoginService extends Service {
}
if (!rootNode.getName().equals("login")) {
- throw new IOException("Unexpected root node name.");
+ throw new IOException("Unexpected root node name, expected 'login'.");
}
- XmlNode loaderNode = rootNode.getChild("loader");
- if (loaderNode == null || !loaderNode.hasValue()) {
- throw new IOException("No loader child node or value.");
+ XmlNode serializer = rootNode.getChild("serializer");
+ if (serializer == null || !serializer.hasValue()) {
+ throw new IOException("No serializer child node or value.");
}
- XmlNode saverNode = rootNode.getChild("saver");
- if (saverNode == null || !saverNode.hasValue()) {
- throw new IOException("No saver child node or value.");
- }
-
- Class> loaderClazz = Class.forName(loaderNode.getValue());
- Class> saverClazz = Class.forName(saverNode.getValue());
-
- loader = (PlayerLoader) loaderClazz.newInstance();
- saver = (PlayerSaver) saverClazz.newInstance();
+ Class> clazz = Class.forName(serializer.getValue());
+ this.serializer = (PlayerSerializer) clazz.newInstance();
}
/**
@@ -95,7 +82,7 @@ public final class LoginService extends Service {
*/
@Override
public void start() {
- /* empty - here for consistency with other services */
+
}
/**
@@ -110,7 +97,7 @@ public final class LoginService extends Service {
// TODO check archive 0 CRCs
session.handlePlayerLoaderResponse(request, new PlayerLoaderResponse(LoginConstants.STATUS_GAME_UPDATED));
} else {
- executor.submit(new PlayerLoaderWorker(loader, session, request));
+ executor.submit(new PlayerLoaderWorker(serializer, session, request));
}
}
@@ -121,7 +108,7 @@ public final class LoginService extends Service {
* @param player The player to save.
*/
public void submitSaveRequest(GameSession session, Player player) {
- executor.submit(new PlayerSaverWorker(saver, session, player));
+ executor.submit(new PlayerSaverWorker(serializer, session, player));
}
}
\ No newline at end of file
diff --git a/src/org/apollo/login/PlayerLoaderWorker.java b/src/org/apollo/login/PlayerLoaderWorker.java
index 9c9bfabe..0553fe77 100644
--- a/src/org/apollo/login/PlayerLoaderWorker.java
+++ b/src/org/apollo/login/PlayerLoaderWorker.java
@@ -3,8 +3,8 @@ package org.apollo.login;
import java.util.logging.Level;
import java.util.logging.Logger;
-import org.apollo.io.player.PlayerLoader;
import org.apollo.io.player.PlayerLoaderResponse;
+import org.apollo.io.player.PlayerSerializer;
import org.apollo.net.codec.login.LoginConstants;
import org.apollo.net.codec.login.LoginRequest;
import org.apollo.net.session.LoginSession;
@@ -22,9 +22,9 @@ public final class PlayerLoaderWorker implements Runnable {
private static final Logger logger = Logger.getLogger(PlayerLoaderWorker.class.getName());
/**
- * The player loader.
+ * The PlayerSerializer.
*/
- private final PlayerLoader loader;
+ private final PlayerSerializer loader;
/**
* The request.
@@ -39,11 +39,11 @@ public final class PlayerLoaderWorker implements Runnable {
/**
* Creates a {@link PlayerLoaderWorker} which will do the work for a single player load request.
*
- * @param loader The current player loader.
+ * @param loader The {@link PlayerSerializer}.
* @param session The {@link LoginSession} which initiated the request.
* @param request The {@link LoginRequest} object.
*/
- public PlayerLoaderWorker(PlayerLoader loader, LoginSession session, LoginRequest request) {
+ public PlayerLoaderWorker(PlayerSerializer loader, LoginSession session, LoginRequest request) {
this.loader = loader;
this.session = session;
this.request = request;
diff --git a/src/org/apollo/login/PlayerSaverWorker.java b/src/org/apollo/login/PlayerSaverWorker.java
index 2a0e369a..6ea2495c 100644
--- a/src/org/apollo/login/PlayerSaverWorker.java
+++ b/src/org/apollo/login/PlayerSaverWorker.java
@@ -4,7 +4,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.apollo.game.model.entity.Player;
-import org.apollo.io.player.PlayerSaver;
+import org.apollo.io.player.PlayerSerializer;
import org.apollo.net.session.GameSession;
/**
@@ -27,7 +27,7 @@ public final class PlayerSaverWorker implements Runnable {
/**
* The player saver.
*/
- private final PlayerSaver saver;
+ private final PlayerSerializer saver;
/**
* The game session.
@@ -41,7 +41,7 @@ public final class PlayerSaverWorker implements Runnable {
* @param session The game session.
* @param player The player to save.
*/
- public PlayerSaverWorker(PlayerSaver saver, GameSession session, Player player) {
+ public PlayerSaverWorker(PlayerSerializer saver, GameSession session, Player player) {
this.saver = saver;
this.session = session;
this.player = player;
diff --git a/src/org/apollo/net/ApolloHandler.java b/src/org/apollo/net/ApolloHandler.java
index ce0adfc9..3a3a8065 100644
--- a/src/org/apollo/net/ApolloHandler.java
+++ b/src/org/apollo/net/ApolloHandler.java
@@ -85,13 +85,13 @@ public final class ApolloHandler extends ChannelInboundHandlerAdapter {
HandshakeMessage handshakeMessage = (HandshakeMessage) message;
switch (handshakeMessage.getServiceId()) {
- case HandshakeConstants.SERVICE_GAME:
- attribute.set(new LoginSession(ctx, serverContext));
- break;
+ case HandshakeConstants.SERVICE_GAME:
+ attribute.set(new LoginSession(ctx, serverContext));
+ break;
- case HandshakeConstants.SERVICE_UPDATE:
- attribute.set(new UpdateSession(ctx.channel(), serverContext));
- break;
+ case HandshakeConstants.SERVICE_UPDATE:
+ attribute.set(new UpdateSession(ctx.channel(), serverContext));
+ break;
}
}
diff --git a/src/org/apollo/net/codec/game/GamePacketDecoder.java b/src/org/apollo/net/codec/game/GamePacketDecoder.java
index 947cd87f..6991d519 100644
--- a/src/org/apollo/net/codec/game/GamePacketDecoder.java
+++ b/src/org/apollo/net/codec/game/GamePacketDecoder.java
@@ -62,17 +62,17 @@ public final class GamePacketDecoder extends StatefulFrameDecoder out, GameDecoderState state) {
switch (state) {
- case GAME_OPCODE:
- decodeOpcode(in, out);
- break;
- case GAME_LENGTH:
- decodeLength(in);
- break;
- case GAME_PAYLOAD:
- decodePayload(in, out);
- break;
- default:
- throw new IllegalStateException("Invalid game decoder state.");
+ case GAME_OPCODE:
+ decodeOpcode(in, out);
+ break;
+ case GAME_LENGTH:
+ decodeLength(in);
+ break;
+ case GAME_PAYLOAD:
+ decodePayload(in, out);
+ break;
+ default:
+ throw new IllegalStateException("Invalid game decoder state.");
}
}
@@ -106,20 +106,20 @@ public final class GamePacketDecoder extends StatefulFrameDecoder
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List