From 7bac0d907d912247fb3a1119f9e06ea4b8967074 Mon Sep 17 00:00:00 2001 From: Felipe Muniz Date: Sun, 23 Jun 2024 15:37:56 -0300 Subject: [PATCH] feat: 0.3.7-pre --- Makefile.am | 71 +- Makefile.mingw | 69 + account.h | 23 +- actions.cpp | 323 +- actions.h | 25 +- admin.cpp | 416 +- admin.h | 83 +- allocator.cpp | 3 +- allocator.h | 2 + autogen.sh | 2 +- baseevents.cpp | 232 +- baseevents.h | 22 +- beds.cpp | 22 +- beds.h | 2 +- build.sh | 50 + cb/TheForgottenServer.cbp | 254 + .../TheForgottenServer.ico | Bin cb/TheForgottenServer.layout | 4 + cb/TheForgottenServer_private.rc | 29 + chat.cpp | 289 +- chat.h | 40 +- combat.cpp | 337 +- combat.h | 112 +- compile.sh | 177 + condition.cpp | 263 +- condition.h | 11 +- config.lua => config.lua.dist | 208 +- configmanager.cpp | 517 +- configmanager.h | 155 +- configure.ac | 201 +- connection.cpp | 92 +- connection.h | 4 +- const.h | 876 +- container.cpp | 229 +- container.h | 28 +- creature.cpp | 520 +- creature.h | 186 +- creatureevent.cpp | 1020 +- creatureevent.h | 55 +- cylinder.h | 67 +- data/XML/admin.xml | 7 - data/XML/channels.xml | 20 +- data/XML/groups.xml | 10 +- data/XML/mounts.xml | 31 + data/XML/outfits.xml | 56 +- data/XML/servers.xml | 2 +- data/XML/vocations.xml | 26 +- data/actions/actions.xml | 266 +- data/actions/lib/actions.lua | 205 + data/actions/scripts/default.lua | 8 + data/actions/scripts/foods/blessed_steak.lua | 12 + data/actions/scripts/foods/carrot_cake.lua | 18 + .../scripts/foods/coconut_shrimp_bake.lua | 26 + .../scripts/foods/demonic_candy_ball.lua | 51 + .../scripts/foods/filled_jalapeno_peppers.lua | 18 + data/actions/scripts/foods/food.lua | 56 + .../foods/fried_tropical_terrorbird.lua | 18 + .../scripts/foods/hydra_tongue_salad.lua | 22 + .../scripts/foods/northern_fishburger.lua | 18 + .../scripts/foods/pot_of_blackjack.lua | 39 + .../scripts/foods/roasted_dragon_wings.lua | 18 + data/actions/scripts/foods/rotworm_stew.lua | 12 + .../scripts/foods/sweet_mangonaise_elixir.lua | 38 + .../scripts/foods/veggie_casserole.lua | 18 + .../scripts/liquids/antidote_potion.lua | 72 +- .../scripts/liquids/berserk_potion.lua | 3 +- .../scripts/liquids/bullseye_potion.lua | 3 +- data/actions/scripts/liquids/containers.lua | 106 +- .../scripts/liquids/mastermind_potion.lua | 3 +- data/actions/scripts/liquids/potions.lua | 137 +- data/actions/scripts/other/blessings.lua | 13 + data/actions/scripts/other/blueberrybush.lua | 5 +- data/actions/scripts/other/changegold.lua | 33 - .../scripts/other/constructionkits.lua | 10 +- data/actions/scripts/other/createbread.lua | 8 +- data/actions/scripts/other/decayto.lua | 29 - data/actions/scripts/other/destroy.lua | 3 - data/actions/scripts/other/dice.lua | 7 +- data/actions/scripts/other/dolls.lua | 83 + data/actions/scripts/other/doors.lua | 153 +- data/actions/scripts/other/enchanting.lua | 18 +- .../actions/scripts/other/fireworksrocket.lua | 9 +- data/actions/scripts/other/food.lua | 93 - data/actions/scripts/other/furniturebeds.lua | 8 +- data/actions/scripts/other/keys.lua | 39 + data/actions/scripts/other/music.lua | 4 +- data/actions/scripts/other/present.lua | 7 + data/actions/scripts/other/pumpkinhead.lua | 15 + data/actions/scripts/other/spellwand.lua | 20 + data/actions/scripts/other/spideregg.lua | 17 + data/actions/scripts/other/stuffeddragon.lua | 23 - data/actions/scripts/other/taming.lua | 137 + data/actions/scripts/other/teleport.lua | 20 +- data/actions/scripts/other/trap.lua | 5 - data/actions/scripts/other/watch.lua | 26 +- data/actions/scripts/other/windows.lua | 19 +- data/actions/scripts/quests/annihilator.lua | 2 +- data/actions/scripts/quests/system.lua | 7 +- .../scripts/tools/blessed_wooden_stake.lua | 38 - data/actions/scripts/tools/fishing.lua | 97 +- data/actions/scripts/tools/machete.lua | 23 +- data/actions/scripts/tools/obsidian_knife.lua | 56 - data/actions/scripts/tools/pick.lua | 17 +- data/actions/scripts/tools/rope.lua | 37 +- data/actions/scripts/tools/scythe.lua | 8 +- data/actions/scripts/tools/shovel.lua | 29 +- data/actions/scripts/tools/skinning.lua | 80 + data/actions/scripts/tools/squeeze.lua | 52 + data/creaturescripts/creaturescripts.xml | 11 +- data/creaturescripts/lib/creaturescripts.lua | 1 - data/creaturescripts/scripts/advancesave.lua | 14 +- data/creaturescripts/scripts/ban/action.lua | 32 + data/creaturescripts/scripts/ban/finish.lua | 236 + data/creaturescripts/scripts/ban/type.lua | 85 + data/creaturescripts/scripts/guild.lua | 31 + data/creaturescripts/scripts/guildmotd.lua | 13 - data/creaturescripts/scripts/idle.lua | 2 +- data/creaturescripts/scripts/login.lua | 9 +- data/creaturescripts/scripts/mail.lua | 2 +- data/creaturescripts/scripts/thankyou.lua | 4 + data/globalevents/globalevents.xml | 11 +- data/globalevents/lib/globalevents.lua | 0 data/globalevents/scripts/clean.lua | 8 +- data/globalevents/scripts/init.lua | 92 + data/globalevents/scripts/save.lua | 20 +- data/globalevents/scripts/start.lua | 4 - data/items/items.otb | Bin 439012 -> 672518 bytes data/items/items.xml | 28750 ++++++++++------ data/items/randomization.xml | 2 +- data/lib/000-constant.lua | 491 +- data/lib/001-class.lua | 2 +- data/lib/002-wait.lua | 10 +- data/lib/004-database.lua | 6 + data/lib/011-string.lua | 18 +- data/lib/012-table.lua | 83 +- data/lib/013-math.lua | 3 + data/lib/032-position.lua | 34 +- data/lib/033-ip.lua | 4 +- data/lib/034-exhaustion.lua | 6 +- data/lib/050-function.lua | 444 +- data/lib/100-shortcut.lua | 351 + data/lib/{100-compat.lua => 101-compat.lua} | 117 +- data/monster/Amazons/amazon.xml | 22 +- data/monster/Amazons/valkyrie.xml | 34 +- data/monster/Annelids/carrion worm.xml | 32 +- data/monster/Annelids/rotworm.xml | 25 +- data/monster/Apes/kongra.xml | 24 +- data/monster/Apes/merlkin.xml | 24 +- data/monster/Apes/sibang.xml | 21 +- .../crystal spider.xml | 43 +- .../{Aracnids => Arachnids}/giant spider.xml | 37 +- .../{Aracnids => Arachnids}/poison spider.xml | 14 +- data/monster/Arachnids/sandstone scorpion.xml | 57 + .../{Aracnids => Arachnids}/scorpion.xml | 7 +- .../{Aracnids => Arachnids}/spider.xml | 7 +- .../{Aracnids => Arachnids}/tarantula.xml | 18 +- data/monster/Arachnids/wailing widow.xml | 66 + data/monster/Arena/greenhorn/achad.xml | 17 +- .../Arena/greenhorn/axeitus headbanger.xml | 16 +- data/monster/Arena/greenhorn/bloodpaw.xml | 18 +- data/monster/Arena/greenhorn/bovinus.xml | 18 +- .../greenhorn/colerian the barbarian.xml | 16 +- .../Arena/greenhorn/cursed gladiator.xml | 13 +- data/monster/Arena/greenhorn/frostfur.xml | 19 +- .../Arena/greenhorn/orcus the cruel.xml | 14 +- data/monster/Arena/greenhorn/rocky.xml | 18 +- .../monster/Arena/greenhorn/the hairy one.xml | 18 +- data/monster/Arena/scrapper/avalanche.xml | 14 +- data/monster/Arena/scrapper/drasilla.xml | 10 +- .../Arena/scrapper/grimgor guteater.xml | 11 +- .../Arena/scrapper/kreebosh the exile.xml | 14 +- data/monster/Arena/scrapper/slim.xml | 9 +- .../Arena/scrapper/spirit of earth.xml | 10 +- .../monster/Arena/scrapper/spirit of fire.xml | 10 +- .../Arena/scrapper/spirit of water.xml | 13 +- .../Arena/scrapper/the dark dancer.xml | 15 +- data/monster/Arena/scrapper/the hag.xml | 13 +- .../Arena/warlord/darakan the executioner.xml | 13 +- data/monster/Arena/warlord/deathbringer.xml | 10 +- .../warlord/fallen mooh'tah master ghar.xml | 14 +- .../monster/Arena/warlord/gnorre chyllson.xml | 11 +- .../Arena/warlord/norgle glacierbeard.xml | 15 +- data/monster/Arena/warlord/svoren the mad.xml | 13 +- .../Arena/warlord/the masked marauder.xml | 10 +- .../monster/Arena/warlord/the obliverator.xml | 11 +- data/monster/Arena/warlord/the pit lord.xml | 11 +- data/monster/Arena/warlord/webster.xml | 11 +- .../Barbarians/barbarian bloodwalker.xml | 31 +- .../Barbarians/barbarian brutetamer.xml | 28 +- .../Barbarians/barbarian headsplitter.xml | 29 +- .../Barbarians/barbarian skullhunter.xml | 23 +- data/monster/Bears/bear.xml | 12 +- data/monster/Bears/panda.xml | 10 +- data/monster/Bears/polar bear.xml | 10 +- data/monster/Bears/undead cavebear.xml | 39 + data/monster/Bio-Elementals/bog raider.xml | 24 +- data/monster/Bio-Elementals/carniphila.xml | 19 +- data/monster/Bio-Elementals/defiler.xml | 22 +- .../Bio-Elementals/haunted treeling.xml | 28 +- data/monster/Bio-Elementals/slime summon.xml | 6 +- data/monster/Bio-Elementals/slime.xml | 4 +- data/monster/Bio-Elementals/slug.xml | 37 + .../Bio-Elementals/son of verminor.xml | 2 +- data/monster/Bio-Elementals/spit nettle.xml | 9 +- data/monster/Birds/chicken.xml | 11 +- data/monster/Birds/flamingo.xml | 7 - data/monster/Birds/parrot.xml | 8 +- data/monster/Birds/penguin.xml | 12 +- data/monster/Birds/seagull.xml | 4 +- data/monster/Birds/terror bird.xml | 28 +- data/monster/Blobs/acid blob.xml | 4 +- data/monster/Blobs/death blob.xml | 4 +- data/monster/Blobs/mercury blob.xml | 4 +- .../beholder.xml => Bonelords/bonelord.xml} | 34 +- .../{Beholders => Bonelords}/braindeath.xml | 33 +- .../elder bonelord.xml} | 40 +- .../{Beholders => Bonelords}/gazer.xml | 22 +- data/monster/Bosses/annihilon.xml | 10 +- data/monster/Bosses/apprentice sheng.xml | 14 +- data/monster/Bosses/barbaria.xml | 64 + data/monster/Bosses/baron brute.xml | 38 + data/monster/Bosses/bones.xml | 21 +- .../Bosses/chizzoron the distorter.xml | 67 + data/monster/Bosses/countess sorrow.xml | 2 - data/monster/Bosses/demodras.xml | 20 +- data/monster/Bosses/dharalion.xml | 21 +- .../{Birds => Bosses}/dire penguin.xml | 10 +- data/monster/Bosses/dreadwing.xml | 32 + data/monster/Bosses/esmeralda.xml | 64 + data/monster/Bosses/fatality.xml | 33 + data/monster/Bosses/fernfang.xml | 12 +- data/monster/Bosses/ferumbras.xml | 18 +- data/monster/Bosses/fluffy.xml | 17 +- data/monster/Bosses/general murius.xml | 26 +- data/monster/Bosses/ghazbaran.xml | 11 +- data/monster/Bosses/glitterscale.xml | 41 + .../monster/Bosses/grand mother foulscale.xml | 77 + data/monster/Bosses/grorlam.xml | 14 +- data/monster/Bosses/hairman the huge.xml | 52 + data/monster/Bosses/handmaiden.xml | 1 - data/monster/Bosses/hellgorak.xml | 8 +- data/monster/Bosses/heoni.xml | 48 + data/monster/Bosses/hide.xml | 48 + data/monster/Bosses/koshei the deathless.xml | 17 +- data/monster/Bosses/latrivan.xml | 10 +- data/monster/Bosses/leviathan.xml | 7 +- data/monster/Bosses/madareth.xml | 8 +- data/monster/Bosses/man in the cave.xml | 18 +- data/monster/Bosses/massacre.xml | 1 - data/monster/Bosses/minishabaal.xml | 24 +- data/monster/Bosses/morgaroth.xml | 18 +- data/monster/Bosses/mr. punish.xml | 5 - data/monster/Bosses/munster.xml | 21 +- data/monster/Bosses/necropharus.xml | 34 +- data/monster/Bosses/pythius the rotten.xml | 69 + data/monster/Bosses/shardhead.xml | 56 + data/monster/Bosses/snake god essence.xml | 44 + data/monster/Bosses/snake thing.xml | 46 + data/monster/Bosses/stonecracker.xml | 62 + data/monster/Bosses/the abomination.xml | 5 - data/monster/Bosses/the blightfather.xml | 54 + data/monster/Bosses/the bloodtusk.xml | 43 + data/monster/Bosses/the evil eye.xml | 10 +- data/monster/Bosses/the handmaiden.xml | 1 - data/monster/Bosses/the horned fox.xml | 22 +- data/monster/Bosses/the imperor.xml | 30 +- data/monster/Bosses/the many.xml | 69 + data/monster/Bosses/the noxious spawn.xml | 70 + data/monster/Bosses/the old widow.xml | 19 +- data/monster/Bosses/the plasmother.xml | 62 + data/monster/Bosses/the snapper.xml | 45 + data/monster/Bosses/tibia bug.xml | 6 - data/monster/Bosses/undead minion.xml | 5 - data/monster/{Demons => Bosses}/ungreez.xml | 11 +- data/monster/Bosses/ushuriel.xml | 22 +- data/monster/Bosses/warlord ruzad.xml | 52 + data/monster/Bosses/xenia.xml | 25 +- data/monster/Bosses/yakchal.xml | 80 + data/monster/Bosses/zugurosh.xml | 18 +- data/monster/Bosses/zulazza the corruptor.xml | 80 + data/monster/Canines/crystal wolf.xml | 56 + data/monster/Canines/dog.xml | 14 - data/monster/Canines/gnarlhound.xml | 33 + data/monster/Canines/husky.xml | 5 +- data/monster/Canines/war wolf.xml | 9 +- data/monster/Canines/werewolf.xml | 37 +- data/monster/Canines/winter wolf.xml | 6 +- data/monster/Canines/wolf.xml | 8 +- data/monster/Chakoyas/chakoya toolshaper.xml | 18 +- data/monster/Chakoyas/chakoya tribewarden.xml | 21 +- data/monster/Chakoyas/chakoya windcaller.xml | 17 +- data/monster/Crustaceans/blood crab.xml | 13 +- data/monster/Crustaceans/crab.xml | 10 +- .../Crustaceans/crustacea gigantica.xml | 43 + data/monster/Cryo-Elementals/ice golem.xml | 30 +- data/monster/Cultists/acolyte of the cult.xml | 30 +- data/monster/Cultists/adept of the cult.xml | 29 +- .../Cultists/enlightened of the cult.xml | 39 +- data/monster/Cultists/novice of the cult.xml | 26 +- data/monster/Demons/dark torturer.xml | 40 +- data/monster/Demons/demon.xml | 68 +- data/monster/Demons/destroyer.xml | 42 +- data/monster/Demons/diabolic imp.xml | 42 +- data/monster/Demons/fire devil.xml | 34 +- data/monster/Demons/fury.xml | 43 +- data/monster/Demons/gozzler.xml | 28 +- data/monster/Demons/hand of cursed fate.xml | 33 +- .../monster/{Canines => Demons}/hellhound.xml | 38 +- data/monster/Demons/hellspawn.xml | 52 +- data/monster/Demons/juggernaut.xml | 55 +- data/monster/Demons/nightmare scion.xml | 32 +- data/monster/Demons/nightmare.xml | 44 +- data/monster/Demons/nightstalker.xml | 32 +- data/monster/Demons/plaguesmith.xml | 51 +- data/monster/Djinns/blue djinn.xml | 27 +- data/monster/Djinns/efreet.xml | 35 +- data/monster/Djinns/green djinn.xml | 25 +- data/monster/Djinns/marid.xml | 35 +- data/monster/Dragons/dragon hatchling.xml | 13 +- .../monster/Dragons/dragon lord hatchling.xml | 28 +- data/monster/Dragons/dragon lord.xml | 52 +- data/monster/Dragons/dragon.xml | 55 +- data/monster/Dragons/draptor.xml | 57 + .../Dragons/frost dragon hatchling.xml | 21 +- data/monster/Dragons/frost dragon.xml | 61 +- data/monster/Dragons/ghastly dragon.xml | 86 + data/monster/Dragons/hydra.xml | 78 - .../{Skeletons => Dragons}/undead dragon.xml | 45 +- data/monster/Dragons/wyrm.xml | 54 +- data/monster/Dwarves/dwarf geomancer.xml | 28 +- data/monster/Dwarves/dwarf guard.xml | 34 +- data/monster/Dwarves/dwarf soldier.xml | 34 +- data/monster/Dwarves/dwarf.xml | 28 +- data/monster/Dworcs/dworc fleshhunter.xml | 32 +- data/monster/Dworcs/dworc venomsniper.xml | 32 +- data/monster/Dworcs/dworc voodoomaster.xml | 33 +- data/monster/Elephants/elephant.xml | 16 +- data/monster/Elephants/mammoth.xml | 11 +- data/monster/Elves/elf arcanist.xml | 48 +- data/monster/Elves/elf scout.xml | 30 +- data/monster/Elves/elf.xml | 30 +- .../charged energy elemental.xml | 9 +- .../Energy-Elementals/energy elemental.xml | 21 +- .../Energy-Elementals/energy overlord.xml | 11 +- .../massive energy elemental.xml | 16 +- .../overcharged energy elemental.xml | 15 +- data/monster/Felines/cat.xml | 2 +- data/monster/Felines/lion.xml | 10 +- data/monster/Felines/midnight panther.xml | 44 + data/monster/Felines/tiger.xml | 9 +- data/monster/Frogs/azure frog.xml | 6 +- data/monster/Frogs/coral frog.xml | 6 +- data/monster/Frogs/crimson frog.xml | 6 +- data/monster/Frogs/green frog.xml | 4 +- data/monster/Frogs/orchid frog.xml | 8 +- data/monster/Frogs/toad.xml | 16 +- .../Geo-Elementals/damaged worker golem.xml | 14 +- .../Geo-Elementals/earth elemental.xml | 18 +- .../monster/Geo-Elementals/earth overlord.xml | 13 +- data/monster/Geo-Elementals/gargoyle.xml | 29 +- .../Geo-Elementals/jagged earth elemental.xml | 15 +- .../massive earth elemental.xml | 20 +- .../{Sorcerers => Geo-Elementals}/medusa.xml | 34 +- .../Geo-Elementals/muddy earth elemental.xml | 10 +- data/monster/Geo-Elementals/stone golem.xml | 24 +- data/monster/Geo-Elementals/war golem.xml | 38 +- data/monster/Geo-Elementals/worker golem.xml | 42 +- data/monster/Ghosts/ghost.xml | 29 +- data/monster/Ghosts/phantasm summon.xml | 7 +- data/monster/Ghosts/phantasm.xml | 26 +- data/monster/Ghosts/spectre.xml | 42 +- data/monster/Ghosts/wisp.xml | 19 +- data/monster/Giants/behemoth.xml | 55 +- data/monster/Giants/cyclops drone.xml | 19 +- data/monster/Giants/cyclops smith.xml | 22 +- data/monster/Giants/cyclops.xml | 33 +- data/monster/Giants/frost giant.xml | 23 +- data/monster/Giants/frost giantess.xml | 27 +- data/monster/Giants/yeti.xml | 22 +- data/monster/Goblins/goblin assassin.xml | 27 +- data/monster/Goblins/goblin leader.xml | 28 +- data/monster/Goblins/goblin scavenger.xml | 30 +- data/monster/Goblins/goblin.xml | 29 +- data/monster/Goblins/grynch clan goblin.xml | 101 +- data/monster/Grunts/boar.xml | 31 + data/monster/{Misc => Grunts}/pig.xml | 8 +- .../monster/Hydro-Elementals/ice overlord.xml | 15 +- .../massive water elemental.xml | 5 +- .../roaring water elemental.xml | 16 +- .../slick water elemental.xml | 14 +- .../Hydro-Elementals/water elemental.xml | 7 +- data/monster/Insects/ancient scarab.xml | 33 +- data/monster/Insects/blue butterfly.xml | 8 +- data/monster/Insects/brimstone bug.xml | 69 + data/monster/Insects/bug.xml | 9 +- data/monster/Insects/centipede.xml | 21 +- data/monster/Insects/cockroach.xml | 2 +- data/monster/Insects/insect swarm.xml | 35 + data/monster/Insects/insectoid scout.xml | 43 + data/monster/Insects/lancer beetle.xml | 54 + data/monster/Insects/larva.xml | 9 +- ...ink butterfly.xml => purple butterfly.xml} | 8 +- data/monster/Insects/red butterfly.xml | 8 +- data/monster/Insects/sandcrawler.xml | 34 + data/monster/Insects/scarab.xml | 25 +- data/monster/Insects/terramite.xml | 46 + data/monster/Insects/wasp.xml | 6 +- data/monster/Insects/yellow butterfly.xml | 13 +- .../Isle of Evil/berserker chicken.xml | 53 + data/monster/Isle of Evil/boogey.xml | 63 + data/monster/Isle of Evil/demon parrot.xml | 47 + data/monster/Isle of Evil/dirtbeard.xml | 47 + data/monster/Isle of Evil/docter perhaps.xml | 59 + data/monster/Isle of Evil/doom deer.xml | 43 + data/monster/Isle of Evil/evil mastermind.xml | 69 + data/monster/Isle of Evil/evil sheep lord.xml | 52 + data/monster/Isle of Evil/evil sheep.xml | 42 + data/monster/Isle of Evil/hot dog.xml | 48 + data/monster/Isle of Evil/infernal frog.xml | 43 + data/monster/Isle of Evil/killer rabbit.xml | 41 + data/monster/Isle of Evil/mephiles.xml | 54 + data/monster/Isle of Evil/monstor.xml | 51 + data/monster/Isle of Evil/vampire pig.xml | 47 + data/monster/Lizards/battlemaster zunzu.xml | 55 + data/monster/Lizards/draken abomination.xml | 76 + data/monster/Lizards/draken elite.xml | 77 + data/monster/Lizards/draken spellweaver.xml | 67 + data/monster/Lizards/draken warmaster.xml | 54 + data/monster/Lizards/eternal guardian.xml | 43 + data/monster/Lizards/lizard abomination.xml | 48 + data/monster/Lizards/lizard chosen.xml | 65 + data/monster/Lizards/lizard dragon priest.xml | 68 + data/monster/Lizards/lizard high guard.xml | 63 + data/monster/Lizards/lizard legionnaire.xml | 54 + data/monster/Lizards/lizard sentinel.xml | 35 +- data/monster/Lizards/lizard snakecharmer.xml | 35 +- data/monster/Lizards/lizard templar.xml | 31 +- data/monster/Lizards/lizard zaogun.xml | 58 + data/monster/Lizards/wyvern.xml | 28 +- data/monster/Minotaurs/minotaur archer.xml | 35 +- data/monster/Minotaurs/minotaur guard.xml | 35 +- data/monster/Minotaurs/minotaur mage.xml | 36 +- data/monster/Minotaurs/minotaur.xml | 34 +- data/monster/Misc/badger.xml | 7 +- data/monster/Misc/bat.xml | 10 +- data/monster/Misc/deer.xml | 16 +- data/monster/Misc/dromedary.xml | 32 + data/monster/Misc/hacker.xml | 17 +- data/monster/Misc/horse(brown).xml | 24 + data/monster/Misc/horse(fire).xml | 24 + data/monster/Misc/horse(grey).xml | 24 + data/monster/Misc/hyaena.xml | 8 +- data/monster/Misc/rabbit.xml | 4 +- data/monster/Misc/silver rabbit.xml | 7 +- data/monster/Misc/skunk.xml | 9 +- data/monster/Misc/squirrel.xml | 4 +- data/monster/Misc/white deer.xml | 35 + data/monster/Misc/yalahari.xml | 35 + data/monster/Monks/dark monk.xml | 36 +- data/monster/Monks/monk.xml | 26 +- data/monster/Mutated/mutated bat.xml | 32 +- data/monster/Mutated/mutated human.xml | 34 +- data/monster/Mutated/mutated rat.xml | 30 +- data/monster/Mutated/mutated tiger.xml | 21 +- data/monster/Necromancers/necromancer.xml | 34 +- data/monster/Necromancers/priestess.xml | 46 +- data/monster/Orcs/orc berserker.xml | 24 +- data/monster/Orcs/orc leader.xml | 54 +- data/monster/Orcs/orc marauder.xml | 51 + data/monster/Orcs/orc rider.xml | 29 +- data/monster/Orcs/orc shaman.xml | 24 +- data/monster/Orcs/orc spearman.xml | 22 +- data/monster/Orcs/orc warlord.xml | 48 +- data/monster/Orcs/orc warrior.xml | 27 +- data/monster/Orcs/orc.xml | 23 +- data/monster/Outlaws/assassin.xml | 34 +- data/monster/Outlaws/bandit.xml | 26 +- data/monster/Outlaws/black knight.xml | 48 +- data/monster/Outlaws/crazed beggar.xml | 30 +- data/monster/Outlaws/feverish citizen.xml | 52 + data/monster/Outlaws/gang member.xml | 22 +- data/monster/Outlaws/gladiator.xml | 24 +- data/monster/Outlaws/hero.xml | 53 +- data/monster/Outlaws/hunter.xml | 40 +- data/monster/Outlaws/nomad.xml | 34 +- data/monster/Outlaws/poacher.xml | 18 +- data/monster/Outlaws/primitive.xml | 21 +- data/monster/Outlaws/smuggler.xml | 35 +- data/monster/Outlaws/stalker.xml | 22 +- data/monster/Outlaws/wild warrior.xml | 27 +- data/monster/Pharaohs/ashmunrah.xml | 32 +- data/monster/Pharaohs/dipthrah.xml | 35 +- data/monster/Pharaohs/mahrdis.xml | 35 +- data/monster/Pharaohs/morguthis.xml | 36 +- data/monster/Pharaohs/omruc.xml | 39 +- data/monster/Pharaohs/rahemos.xml | 38 +- data/monster/Pharaohs/thalas.xml | 41 +- data/monster/Pharaohs/vashresamun.xml | 37 +- data/monster/Pirates/pirate buccaneer.xml | 37 +- data/monster/Pirates/pirate corsair.xml | 34 +- data/monster/Pirates/pirate cutthroat.xml | 34 +- .../{Ghosts => Pirates}/pirate ghost.xml | 19 +- data/monster/Pirates/pirate marauder.xml | 35 +- .../pirate skeleton.xml | 21 +- .../blazing fire elemental.xml | 10 +- .../blistering fire elemental.xml | 11 +- .../Pyro-Elementals/fire elemental.xml | 6 +- .../monster/Pyro-Elementals/fire overlord.xml | 10 +- .../Pyro-Elementals/hellfire fighter.xml | 36 +- .../massive fire elemental.xml | 2 +- .../Quaras/quara constrictor scout.xml | 18 +- data/monster/Quaras/quara constrictor.xml | 22 +- .../Quaras/quara hydromancer scout.xml | 29 +- data/monster/Quaras/quara hydromancer.xml | 33 +- data/monster/Quaras/quara mantassin scout.xml | 24 +- data/monster/Quaras/quara mantassin.xml | 30 +- data/monster/Quaras/quara pincher scout.xml | 28 +- data/monster/Quaras/quara pincher.xml | 25 +- data/monster/Quaras/quara predator scout.xml | 21 +- data/monster/Quaras/quara predator.xml | 28 +- data/monster/Rats/cave rat.xml | 11 +- data/monster/Rats/rat.xml | 11 +- data/monster/Reptiles/cobra.xml | 42 - data/monster/Reptiles/crocodile.xml | 16 +- data/monster/Reptiles/hydra.xml | 40 +- data/monster/Reptiles/killer caiman.xml | 47 + data/monster/Reptiles/sea serpent.xml | 68 - data/monster/Reptiles/serpent spawn.xml | 94 - data/monster/Reptiles/snake.xml | 33 - data/monster/Reptiles/wyvern.xml | 64 - data/monster/Rifts/rift scythe.xml | 6 +- data/monster/Rifts/rift worm.xml | 2 - data/monster/Serpents/cobra.xml | 8 +- data/monster/Serpents/deepling scout.xml | 56 + data/monster/Serpents/sea serpent.xml | 42 +- data/monster/Serpents/serpent spawn.xml | 54 +- data/monster/Serpents/snake.xml | 4 +- data/monster/Serpents/young sea serpent.xml | 76 +- data/monster/Shapeshifters/mimic.xml | 2 +- data/monster/Sheeps/black sheep.xml | 14 +- data/monster/Sheeps/sheep.xml | 7 +- data/monster/Skeletons/betrayed wraith.xml | 42 +- data/monster/Skeletons/bonebeast.xml | 28 +- data/monster/Skeletons/demon skeleton.xml | 28 +- data/monster/Skeletons/dreadbeast.xml | 3 - data/monster/Skeletons/lost soul.xml | 47 +- data/monster/Skeletons/skeleton warrior.xml | 42 +- data/monster/Skeletons/skeleton.xml | 25 +- data/monster/Skeletons/undead gladiator.xml | 47 +- data/monster/Sorcerers/dark apprentice.xml | 15 +- data/monster/Sorcerers/dark magician.xml | 15 +- data/monster/Sorcerers/ice witch.xml | 28 +- data/monster/Sorcerers/infernalist.xml | 43 +- data/monster/Sorcerers/mad mage.xml | 64 + data/monster/Sorcerers/mad scientist.xml | 35 +- data/monster/Sorcerers/warlock.xml | 50 +- data/monster/Sorcerers/witch.xml | 33 +- data/monster/Tortoises/thornback tortoise.xml | 22 +- data/monster/Tortoises/tortoise.xml | 21 +- data/monster/Traps/deathslicer.xml | 4 +- data/monster/Traps/eye of the seven.xml | 4 +- data/monster/Traps/flamethrower.xml | 8 +- data/monster/Traps/lavahole.xml | 42 + data/monster/Traps/magicthrower.xml | 5 +- data/monster/Traps/plaguethrower.xml | 7 +- data/monster/Traps/poisonthrower.xml | 47 - data/monster/Traps/shredderthrower.xml | 2 +- data/monster/Trolls/frost troll.xml | 24 +- data/monster/Trolls/island troll.xml | 32 +- data/monster/Trolls/swamp troll.xml | 19 +- data/monster/Trolls/troll champion.xml | 20 +- data/monster/Trolls/troll.xml | 28 +- data/monster/Undead Humanoids/banshee.xml | 38 +- .../monster/Undead Humanoids/blightwalker.xml | 40 +- .../Undead Humanoids/crypt shambler.xml | 25 +- data/monster/Undead Humanoids/ghoul.xml | 30 +- data/monster/Undead Humanoids/grim reaper.xml | 41 +- data/monster/Undead Humanoids/lich.xml | 36 +- data/monster/Undead Humanoids/mummy.xml | 33 +- data/monster/Undead Humanoids/souleater.xml | 74 + .../monster/Undead Humanoids/tomb servant.xml | 54 + .../Undead Humanoids/undead mine worker.xml | 38 + .../Undead Humanoids/undead prospector.xml | 47 + .../Undead Humanoids/vampire bride.xml | 29 +- data/monster/Undead Humanoids/vampire.xml | 37 +- data/monster/Undead Humanoids/zombie.xml | 27 +- data/monster/monsters.xml | 172 +- data/movements/lib/movements.lua | 1 - data/movements/movements.xml | 322 +- data/movements/scripts/closingdoor.lua | 38 +- data/movements/scripts/drown.lua | 34 +- data/movements/scripts/hotd.lua | 17 + data/movements/scripts/junglemaw.lua | 12 + data/movements/scripts/swimming.lua | 54 +- data/movements/scripts/tiles.lua | 47 +- data/movements/scripts/walkback.lua | 21 +- data/movements/scripts/yellowpillow.lua | 5 + data/npc/Riona.xml | 2 +- data/npc/Varkhal.xml | 9 +- data/npc/lib/AI/basic.lua | 416 + data/npc/lib/AI/containers.lua | 147 + data/npc/lib/AI/declarative_talk.lua | 312 + data/npc/lib/AI/dispatcher.lua | 81 + data/npc/lib/AI/focus.lua | 222 + data/npc/lib/AI/social.lua | 538 + data/npc/lib/AI/std_declarative.lua | 1 + data/npc/lib/AI/talk.lua | 396 + data/npc/lib/_AI.lua | 303 + .../npcsystem.lua => _npcsystem.lua} | 11 +- data/npc/lib/npc.lua | 145 +- data/npc/lib/npcsystem/keywordhandler.lua | 61 +- data/npc/lib/npcsystem/modules.lua | 267 +- data/npc/lib/npcsystem/npchandler.lua | 283 +- data/npc/lib/npcsystem/queue.lua | 10 +- data/npc/npcs.xml | 30 + data/npc/scripts/addons.lua | 45 - data/npc/scripts/aiexample.lua | 54 + data/npc/scripts/bless.lua | 7 +- data/npc/scripts/bootmaker.lua | 10 +- data/npc/scripts/default.lua | 10 +- data/npc/scripts/example82.lua | 7 +- data/npc/scripts/furniture.lua | 8 +- data/npc/scripts/loot.lua | 8 +- data/npc/scripts/oracle.lua | 114 +- data/npc/scripts/promotion.lua | 10 +- data/npc/scripts/runes.lua | 141 +- data/raids/lib/raids.lua | 0 data/spells/lib/spells.lua | 60 +- data/spells/scripts/attack/annihilation.lua | 17 + data/spells/scripts/attack/berserk.lua | 7 +- data/spells/scripts/attack/brutal strike.lua | 17 + data/spells/scripts/attack/curse.lua | 30 + data/spells/scripts/attack/electrify.lua | 13 + data/spells/scripts/attack/energy beam.lua | 2 +- data/spells/scripts/attack/envenom.lua | 17 +- data/spells/scripts/attack/ethereal spear.lua | 4 +- data/spells/scripts/attack/fierce berserk.lua | 7 +- data/spells/scripts/attack/front sweep.lua | 17 + .../scripts/attack/great energy beam.lua | 4 +- data/spells/scripts/attack/groundshaker.lua | 8 +- data/spells/scripts/attack/holy flash.lua | 12 + .../attack/{soul fire.lua => ignite.lua} | 8 +- data/spells/scripts/attack/inflict wound.lua | 17 + data/spells/scripts/attack/lightning.lua | 9 + .../spells/scripts/attack/physical strike.lua | 9 + .../scripts/attack/strong energy strike.lua | 9 + .../scripts/attack/strong ethereal spear.lua | 13 + .../scripts/attack/strong flame strike.lua | 9 + .../scripts/attack/strong ice strike.lua | 9 + .../spells/scripts/attack/strong ice wave.lua | 12 + .../scripts/attack/strong terra strike.lua | 9 + .../scripts/attack/ultimate energy strike.lua | 9 + .../scripts/attack/ultimate flame strike.lua | 9 + .../scripts/attack/ultimate ice strike.lua | 9 + .../scripts/attack/ultimate terra strike.lua | 9 + .../spells/scripts/attack/whirlwind throw.lua | 8 +- .../spells/scripts/attack/wrath of nature.lua | 2 +- data/spells/scripts/healing/cure bleeding.lua | 8 + data/spells/scripts/healing/cure burning.lua | 8 + data/spells/scripts/healing/cure curse.lua | 8 + .../scripts/healing/cure electrification.lua | 8 + .../healing/{antidote.lua => cure poison.lua} | 0 .../scripts/healing/intense recovery.lua | 15 + .../healing/intense wound cleansing.lua | 10 + data/spells/scripts/healing/recovery.lua | 15 + data/spells/scripts/healing/salvation.lua | 10 + ...wound cleasing.lua => wound cleansing.lua} | 0 data/spells/scripts/party/enchant.lua | 4 +- data/spells/scripts/party/heal.lua | 6 +- data/spells/scripts/party/protect.lua | 4 +- data/spells/scripts/party/train.lua | 4 +- .../scripts/summon/animate dead rune.lua | 29 - data/spells/scripts/summon/undead legion.lua | 25 - .../scripts/support/animate dead rune.lua | 26 + data/spells/scripts/support/challenge.lua | 4 +- data/spells/scripts/support/conjure food.lua | 25 + .../scripts/support/destroy field rune.lua | 8 +- data/spells/scripts/support/haste.lua | 2 +- data/spells/scripts/support/magic rope.lua | 22 +- .../scripts/support/magic wall rune.lua | 3 +- data/spells/scripts/support/paralyze rune.lua | 7 +- data/spells/scripts/support/protector.lua | 5 - data/spells/scripts/support/sharpshooter.lua | 5 - data/spells/scripts/support/strong haste.lua | 2 +- data/spells/scripts/support/swift foot.lua | 5 - .../scripts/support/wild growth rune.lua | 2 +- data/spells/spells.xml | 405 +- data/talkactions/lib/talkactions.lua | 1 - data/talkactions/scripts/animatedtext.lua | 21 - data/talkactions/scripts/balance.lua | 75 + data/talkactions/scripts/ban.lua | 16 + data/talkactions/scripts/broadcastclass.lua | 12 +- data/talkactions/scripts/clean.lua | 7 +- data/talkactions/scripts/commands.lua | 9 +- data/talkactions/scripts/configinfo.lua | 4 +- data/talkactions/scripts/createitem.lua | 5 +- data/talkactions/scripts/creature.lua | 13 +- data/talkactions/scripts/frags.lua | 12 +- data/talkactions/scripts/gamemaster.lua | 21 +- data/talkactions/scripts/gethouse.lua | 7 +- data/talkactions/scripts/masskick.lua | 26 +- data/talkactions/scripts/mode.lua | 24 +- data/talkactions/scripts/money.lua | 2 +- data/talkactions/scripts/multicheck.lua | 26 +- data/talkactions/scripts/mute.lua | 33 + data/talkactions/scripts/newtype.lua | 46 +- data/talkactions/scripts/notations.lua | 4 +- data/talkactions/scripts/online.lua | 19 +- data/talkactions/scripts/owner.lua | 2 +- data/talkactions/scripts/playerinfo.lua | 9 +- data/talkactions/scripts/promote.lua | 23 +- data/talkactions/scripts/pvp.lua | 6 +- data/talkactions/scripts/reload.lua | 6 +- data/talkactions/scripts/remove.lua | 2 +- data/talkactions/scripts/reports.lua | 57 +- data/talkactions/scripts/save.lua | 17 +- data/talkactions/scripts/serverinfo.lua | 5 +- data/talkactions/scripts/shutdown.lua | 47 +- data/talkactions/scripts/skill.lua | 43 + data/talkactions/scripts/storage.lua | 2 +- data/talkactions/scripts/teleporthere.lua | 2 +- data/talkactions/scripts/teleportmaster.lua | 37 - data/talkactions/scripts/teleportsend.lua | 2 +- data/talkactions/scripts/teleportto.lua | 4 +- data/talkactions/scripts/teleporttown.lua | 46 +- data/talkactions/scripts/unban.lua | 6 +- data/talkactions/scripts/war.lua | 178 + data/talkactions/scripts/waypoints.lua | 2 +- data/talkactions/talkactions.xml | 51 +- data/weapons/lib/weapons.lua | 1 - data/weapons/scripts/burst_arrow.lua | 9 +- data/weapons/scripts/poison_arrow.lua | 2 +- data/weapons/scripts/viper_star.lua | 5 +- data/weapons/weapons.xml | 268 +- data/world/tfs-house.xml | 2 + data/world/tfs-spawn.xml | 69 + data/world/tfs.otbm | Bin 0 -> 747636 bytes database.cpp | 64 +- database.h | 118 +- databasemanager.cpp | 707 +- databasemanager.h | 9 +- databasemysql.cpp | 356 +- databasemysql.h | 52 +- databasemysqlpp.cpp | 261 + databaseodbc.h => databasemysqlpp.h | 73 +- databaseodbc.cpp | 367 - databasepgsql.cpp | 125 +- databasepgsql.h | 20 +- databasesqlite.cpp | 111 +- databasesqlite.h | 32 +- debianfix.sh | 27 - definitions.h | 210 +- depot.cpp | 31 +- depot.h | 17 +- dev-cpp/TheForgottenServer-console-debug.dev | 1759 - dev-cpp/TheForgottenServer-console.dev | 1759 - dev-cpp/TheForgottenServer-debug.dev | 1759 - dev-cpp/TheForgottenServer.dev | 577 +- dev-cpp/TheForgottenServer_private.h | 16 +- dev-cpp/TheForgottenServer_private.rc | 16 +- tasks.cpp => dispatcher.cpp | 9 +- tasks.h => dispatcher.h | 2 + doc/ACTION_IDS | 2 +- doc/CHANGELOG | 92 +- doc/COMMANDS_HELP | 117 +- doc/CONFIG_HELP | 628 +- doc/GROUPS | 24 +- doc/LICENSE | 2 +- doc/LIQUIDS | 5 +- doc/LUA_FUNCTIONS | 259 +- doc/MYSQL_HELP | 15 +- doc/OUTFIT_STATS | 215 + doc/README | 121 +- doc/RESERVED_IDS | 2 +- doc/SCRIPTSYSTEM_HELP | 69 +- doc/SIGNALS | 4 +- doc/SQL_QUERIES | 10 +- doc/TODO | 12 +- doc/TODO_04 | 48 +- enums.h | 328 +- exception.cpp | 607 +- exception.h | 27 +- fileloader.cpp | 226 +- fileloader.h | 149 +- game.cpp | 3545 +- game.h | 234 +- gameservers.cpp | 103 +- gameservers.h | 23 +- globalevent.cpp | 233 +- globalevent.h | 40 +- group.cpp | 37 +- group.h | 14 +- gui.cpp | 47 - gui.h | 49 - gui_resources.rc | 65 - house.cpp | 318 +- house.h | 44 +- housetile.cpp | 45 +- housetile.h | 10 +- inputbox.cpp | 167 - inputbox.h | 50 - ioban.cpp | 157 +- ioban.h | 38 +- ioguild.cpp | 224 +- ioguild.h | 11 +- iologindata.cpp | 633 +- iologindata.h | 19 +- iomap.cpp | 240 +- iomap.h | 28 +- iomapserialize.cpp | 451 +- iomapserialize.h | 14 +- iomarket.cpp | 323 + iomarket.h | 66 + item.cpp | 831 +- item.h | 78 +- itemattributes.cpp | 65 +- itemattributes.h | 8 +- itemloader.h | 35 +- items.cpp | 2588 +- items.h | 93 +- luascript.cpp | 4969 ++- luascript.h | 186 +- mailbox.cpp | 70 +- mailbox.h | 32 +- manager.cpp | 636 + manager.h | 170 + map.cpp | 104 +- map.h | 5 + md5.cpp | 255 - md5.h | 91 - mods/change-gold.xml | 39 + mods/changender_command.xml | 18 +- mods/firstitems.xml | 32 +- monster.cpp | 302 +- monster.h | 36 +- monsters.cpp | 513 +- monsters.h | 12 +- mounts.cpp | 204 + mounts.h | 91 + movement.cpp | 379 +- movement.h | 82 +- networkmessage.cpp | 247 +- networkmessage.h | 160 +- npc.cpp | 1116 +- npc.h | 187 +- rsa.h => otpch.cpp | 29 +- otpch.h | 3 +- otserv.cpp | 1441 +- otsystem.h | 113 +- outfit.cpp | 71 +- outfit.h | 15 +- outputmessage.cpp | 22 +- outputmessage.h | 24 +- party.cpp | 104 +- party.h | 4 +- player.cpp | 2605 +- player.h | 342 +- playerbox.cpp | 194 - playerbox.h | 49 - position.h | 12 +- protocol.cpp | 139 +- protocol.h | 10 +- protocolgame.cpp | 2232 +- protocolgame.h | 91 +- protocolhttp.cpp | 40 +- protocolhttp.h | 15 +- protocollogin.cpp | 220 +- protocollogin.h | 9 +- protocolold.cpp | 48 +- protocolold.h | 5 - quests.cpp | 190 +- quests.h | 25 +- raids.cpp | 263 +- raids.h | 20 +- resources.h | 93 - rsa.cpp | 132 - run.sh | 51 + scheduler.cpp | 29 +- scheduler.h | 5 +- schemas/mysql.sql | 129 +- schemas/pgsql.sql | 2 +- schemas/sqlite.sql | 91 +- scriptmanager.cpp | 195 +- scriptmanager.h | 18 +- server.cpp | 147 +- server.h | 46 +- sha1.cpp | 587 - sha1.h | 99 - spawn.cpp | 147 +- spawn.h | 4 +- spells.cpp | 663 +- spells.h | 68 +- status.cpp | 149 +- status.h | 7 +- talkaction.cpp | 540 +- talkaction.h | 19 +- teleport.cpp | 35 +- teleport.h | 34 +- textlogger.cpp | 94 +- textlogger.h | 39 +- ...ttenserver.s3db => theforgottenserver.s3db | Bin 84992 -> 87040 bytes thing.cpp | 9 +- thing.h | 133 +- tile.cpp | 568 +- tile.h | 78 +- tools.cpp | 868 +- tools.h | 137 +- town.h | 5 +- trashholder.cpp | 29 +- trashholder.h | 34 +- vc10/TheForgottenServer.ico | Bin 0 -> 32038 bytes vc10/TheForgottenServer.rc | 1 + vc10/arch32.props | 12 + vc10/arch64.props | 12 + vc10/debug.props | 17 + vc10/release.props | 18 + vc10/settings.props | 87 + vc10/tfs.sln | 26 + vc10/tfs.vcxproj | 316 + vc10/tfs.vcxproj.filters | 524 + vocation.cpp | 50 +- vocation.h | 21 +- waitlist.cpp | 3 +- waypoints.h | 2 +- weapons.cpp | 284 +- weapons.h | 72 +- 926 files changed, 63035 insertions(+), 44374 deletions(-) create mode 100644 Makefile.mingw create mode 100644 build.sh create mode 100644 cb/TheForgottenServer.cbp rename TheForgottenServer.ico => cb/TheForgottenServer.ico (100%) create mode 100644 cb/TheForgottenServer.layout create mode 100644 cb/TheForgottenServer_private.rc create mode 100644 compile.sh rename config.lua => config.lua.dist (58%) delete mode 100644 data/XML/admin.xml create mode 100644 data/XML/mounts.xml create mode 100644 data/actions/scripts/default.lua create mode 100644 data/actions/scripts/foods/blessed_steak.lua create mode 100644 data/actions/scripts/foods/carrot_cake.lua create mode 100644 data/actions/scripts/foods/coconut_shrimp_bake.lua create mode 100644 data/actions/scripts/foods/demonic_candy_ball.lua create mode 100644 data/actions/scripts/foods/filled_jalapeno_peppers.lua create mode 100644 data/actions/scripts/foods/food.lua create mode 100644 data/actions/scripts/foods/fried_tropical_terrorbird.lua create mode 100644 data/actions/scripts/foods/hydra_tongue_salad.lua create mode 100644 data/actions/scripts/foods/northern_fishburger.lua create mode 100644 data/actions/scripts/foods/pot_of_blackjack.lua create mode 100644 data/actions/scripts/foods/roasted_dragon_wings.lua create mode 100644 data/actions/scripts/foods/rotworm_stew.lua create mode 100644 data/actions/scripts/foods/sweet_mangonaise_elixir.lua create mode 100644 data/actions/scripts/foods/veggie_casserole.lua create mode 100644 data/actions/scripts/other/blessings.lua delete mode 100644 data/actions/scripts/other/changegold.lua delete mode 100644 data/actions/scripts/other/decayto.lua delete mode 100644 data/actions/scripts/other/destroy.lua create mode 100644 data/actions/scripts/other/dolls.lua delete mode 100644 data/actions/scripts/other/food.lua create mode 100644 data/actions/scripts/other/keys.lua create mode 100644 data/actions/scripts/other/present.lua create mode 100644 data/actions/scripts/other/pumpkinhead.lua create mode 100644 data/actions/scripts/other/spellwand.lua create mode 100644 data/actions/scripts/other/spideregg.lua delete mode 100644 data/actions/scripts/other/stuffeddragon.lua create mode 100644 data/actions/scripts/other/taming.lua delete mode 100644 data/actions/scripts/other/trap.lua delete mode 100644 data/actions/scripts/tools/blessed_wooden_stake.lua delete mode 100644 data/actions/scripts/tools/obsidian_knife.lua create mode 100644 data/actions/scripts/tools/skinning.lua create mode 100644 data/actions/scripts/tools/squeeze.lua delete mode 100644 data/creaturescripts/lib/creaturescripts.lua create mode 100644 data/creaturescripts/scripts/ban/action.lua create mode 100644 data/creaturescripts/scripts/ban/finish.lua create mode 100644 data/creaturescripts/scripts/ban/type.lua create mode 100644 data/creaturescripts/scripts/guild.lua delete mode 100644 data/creaturescripts/scripts/guildmotd.lua create mode 100644 data/creaturescripts/scripts/thankyou.lua delete mode 100644 data/globalevents/lib/globalevents.lua create mode 100644 data/globalevents/scripts/init.lua delete mode 100644 data/globalevents/scripts/start.lua create mode 100644 data/lib/013-math.lua create mode 100644 data/lib/100-shortcut.lua rename data/lib/{100-compat.lua => 101-compat.lua} (69%) rename data/monster/{Aracnids => Arachnids}/crystal spider.xml (57%) rename data/monster/{Aracnids => Arachnids}/giant spider.xml (55%) rename data/monster/{Aracnids => Arachnids}/poison spider.xml (68%) create mode 100644 data/monster/Arachnids/sandstone scorpion.xml rename data/monster/{Aracnids => Arachnids}/scorpion.xml (80%) rename data/monster/{Aracnids => Arachnids}/spider.xml (76%) rename data/monster/{Aracnids => Arachnids}/tarantula.xml (71%) create mode 100644 data/monster/Arachnids/wailing widow.xml create mode 100644 data/monster/Bears/undead cavebear.xml create mode 100644 data/monster/Bio-Elementals/slug.xml rename data/monster/{Beholders/beholder.xml => Bonelords/bonelord.xml} (66%) rename data/monster/{Beholders => Bonelords}/braindeath.xml (69%) rename data/monster/{Beholders/elder beholder.xml => Bonelords/elder bonelord.xml} (65%) rename data/monster/{Beholders => Bonelords}/gazer.xml (62%) create mode 100644 data/monster/Bosses/barbaria.xml create mode 100644 data/monster/Bosses/baron brute.xml create mode 100644 data/monster/Bosses/chizzoron the distorter.xml rename data/monster/{Birds => Bosses}/dire penguin.xml (75%) create mode 100644 data/monster/Bosses/dreadwing.xml create mode 100644 data/monster/Bosses/esmeralda.xml create mode 100644 data/monster/Bosses/fatality.xml create mode 100644 data/monster/Bosses/glitterscale.xml create mode 100644 data/monster/Bosses/grand mother foulscale.xml create mode 100644 data/monster/Bosses/hairman the huge.xml create mode 100644 data/monster/Bosses/heoni.xml create mode 100644 data/monster/Bosses/hide.xml create mode 100644 data/monster/Bosses/pythius the rotten.xml create mode 100644 data/monster/Bosses/shardhead.xml create mode 100644 data/monster/Bosses/snake god essence.xml create mode 100644 data/monster/Bosses/snake thing.xml create mode 100644 data/monster/Bosses/stonecracker.xml create mode 100644 data/monster/Bosses/the blightfather.xml create mode 100644 data/monster/Bosses/the bloodtusk.xml create mode 100644 data/monster/Bosses/the many.xml create mode 100644 data/monster/Bosses/the noxious spawn.xml create mode 100644 data/monster/Bosses/the plasmother.xml create mode 100644 data/monster/Bosses/the snapper.xml rename data/monster/{Demons => Bosses}/ungreez.xml (86%) create mode 100644 data/monster/Bosses/warlord ruzad.xml create mode 100644 data/monster/Bosses/yakchal.xml create mode 100644 data/monster/Bosses/zulazza the corruptor.xml create mode 100644 data/monster/Canines/crystal wolf.xml create mode 100644 data/monster/Canines/gnarlhound.xml create mode 100644 data/monster/Crustaceans/crustacea gigantica.xml rename data/monster/{Canines => Demons}/hellhound.xml (62%) create mode 100644 data/monster/Dragons/draptor.xml create mode 100644 data/monster/Dragons/ghastly dragon.xml delete mode 100644 data/monster/Dragons/hydra.xml rename data/monster/{Skeletons => Dragons}/undead dragon.xml (62%) create mode 100644 data/monster/Felines/midnight panther.xml rename data/monster/{Sorcerers => Geo-Elementals}/medusa.xml (65%) create mode 100644 data/monster/Grunts/boar.xml rename data/monster/{Misc => Grunts}/pig.xml (77%) create mode 100644 data/monster/Insects/brimstone bug.xml create mode 100644 data/monster/Insects/insect swarm.xml create mode 100644 data/monster/Insects/insectoid scout.xml create mode 100644 data/monster/Insects/lancer beetle.xml rename data/monster/Insects/{pink butterfly.xml => purple butterfly.xml} (83%) create mode 100644 data/monster/Insects/sandcrawler.xml create mode 100644 data/monster/Insects/terramite.xml create mode 100644 data/monster/Isle of Evil/berserker chicken.xml create mode 100644 data/monster/Isle of Evil/boogey.xml create mode 100644 data/monster/Isle of Evil/demon parrot.xml create mode 100644 data/monster/Isle of Evil/dirtbeard.xml create mode 100644 data/monster/Isle of Evil/docter perhaps.xml create mode 100644 data/monster/Isle of Evil/doom deer.xml create mode 100644 data/monster/Isle of Evil/evil mastermind.xml create mode 100644 data/monster/Isle of Evil/evil sheep lord.xml create mode 100644 data/monster/Isle of Evil/evil sheep.xml create mode 100644 data/monster/Isle of Evil/hot dog.xml create mode 100644 data/monster/Isle of Evil/infernal frog.xml create mode 100644 data/monster/Isle of Evil/killer rabbit.xml create mode 100644 data/monster/Isle of Evil/mephiles.xml create mode 100644 data/monster/Isle of Evil/monstor.xml create mode 100644 data/monster/Isle of Evil/vampire pig.xml create mode 100644 data/monster/Lizards/battlemaster zunzu.xml create mode 100644 data/monster/Lizards/draken abomination.xml create mode 100644 data/monster/Lizards/draken elite.xml create mode 100644 data/monster/Lizards/draken spellweaver.xml create mode 100644 data/monster/Lizards/draken warmaster.xml create mode 100644 data/monster/Lizards/eternal guardian.xml create mode 100644 data/monster/Lizards/lizard abomination.xml create mode 100644 data/monster/Lizards/lizard chosen.xml create mode 100644 data/monster/Lizards/lizard dragon priest.xml create mode 100644 data/monster/Lizards/lizard high guard.xml create mode 100644 data/monster/Lizards/lizard legionnaire.xml create mode 100644 data/monster/Lizards/lizard zaogun.xml create mode 100644 data/monster/Misc/dromedary.xml create mode 100644 data/monster/Misc/horse(brown).xml create mode 100644 data/monster/Misc/horse(fire).xml create mode 100644 data/monster/Misc/horse(grey).xml create mode 100644 data/monster/Misc/white deer.xml create mode 100644 data/monster/Misc/yalahari.xml create mode 100644 data/monster/Orcs/orc marauder.xml create mode 100644 data/monster/Outlaws/feverish citizen.xml rename data/monster/{Ghosts => Pirates}/pirate ghost.xml (75%) rename data/monster/{Skeletons => Pirates}/pirate skeleton.xml (59%) delete mode 100644 data/monster/Reptiles/cobra.xml create mode 100644 data/monster/Reptiles/killer caiman.xml delete mode 100644 data/monster/Reptiles/sea serpent.xml delete mode 100644 data/monster/Reptiles/serpent spawn.xml delete mode 100644 data/monster/Reptiles/snake.xml delete mode 100644 data/monster/Reptiles/wyvern.xml create mode 100644 data/monster/Serpents/deepling scout.xml create mode 100644 data/monster/Sorcerers/mad mage.xml create mode 100644 data/monster/Traps/lavahole.xml delete mode 100644 data/monster/Traps/poisonthrower.xml create mode 100644 data/monster/Undead Humanoids/souleater.xml create mode 100644 data/monster/Undead Humanoids/tomb servant.xml create mode 100644 data/monster/Undead Humanoids/undead mine worker.xml create mode 100644 data/monster/Undead Humanoids/undead prospector.xml delete mode 100644 data/movements/lib/movements.lua create mode 100644 data/movements/scripts/hotd.lua create mode 100644 data/movements/scripts/junglemaw.lua create mode 100644 data/movements/scripts/yellowpillow.lua create mode 100644 data/npc/lib/AI/basic.lua create mode 100644 data/npc/lib/AI/containers.lua create mode 100644 data/npc/lib/AI/declarative_talk.lua create mode 100644 data/npc/lib/AI/dispatcher.lua create mode 100644 data/npc/lib/AI/focus.lua create mode 100644 data/npc/lib/AI/social.lua create mode 100644 data/npc/lib/AI/std_declarative.lua create mode 100644 data/npc/lib/AI/talk.lua create mode 100644 data/npc/lib/_AI.lua rename data/npc/lib/{npcsystem/npcsystem.lua => _npcsystem.lua} (93%) create mode 100644 data/npc/npcs.xml delete mode 100644 data/npc/scripts/addons.lua create mode 100644 data/npc/scripts/aiexample.lua delete mode 100644 data/raids/lib/raids.lua create mode 100644 data/spells/scripts/attack/annihilation.lua create mode 100644 data/spells/scripts/attack/brutal strike.lua create mode 100644 data/spells/scripts/attack/curse.lua create mode 100644 data/spells/scripts/attack/electrify.lua create mode 100644 data/spells/scripts/attack/front sweep.lua create mode 100644 data/spells/scripts/attack/holy flash.lua rename data/spells/scripts/attack/{soul fire.lua => ignite.lua} (62%) create mode 100644 data/spells/scripts/attack/inflict wound.lua create mode 100644 data/spells/scripts/attack/lightning.lua create mode 100644 data/spells/scripts/attack/physical strike.lua create mode 100644 data/spells/scripts/attack/strong energy strike.lua create mode 100644 data/spells/scripts/attack/strong ethereal spear.lua create mode 100644 data/spells/scripts/attack/strong flame strike.lua create mode 100644 data/spells/scripts/attack/strong ice strike.lua create mode 100644 data/spells/scripts/attack/strong ice wave.lua create mode 100644 data/spells/scripts/attack/strong terra strike.lua create mode 100644 data/spells/scripts/attack/ultimate energy strike.lua create mode 100644 data/spells/scripts/attack/ultimate flame strike.lua create mode 100644 data/spells/scripts/attack/ultimate ice strike.lua create mode 100644 data/spells/scripts/attack/ultimate terra strike.lua create mode 100644 data/spells/scripts/healing/cure bleeding.lua create mode 100644 data/spells/scripts/healing/cure burning.lua create mode 100644 data/spells/scripts/healing/cure curse.lua create mode 100644 data/spells/scripts/healing/cure electrification.lua rename data/spells/scripts/healing/{antidote.lua => cure poison.lua} (100%) create mode 100644 data/spells/scripts/healing/intense recovery.lua create mode 100644 data/spells/scripts/healing/intense wound cleansing.lua create mode 100644 data/spells/scripts/healing/recovery.lua create mode 100644 data/spells/scripts/healing/salvation.lua rename data/spells/scripts/healing/{wound cleasing.lua => wound cleansing.lua} (100%) delete mode 100644 data/spells/scripts/summon/animate dead rune.lua delete mode 100644 data/spells/scripts/summon/undead legion.lua create mode 100644 data/spells/scripts/support/animate dead rune.lua create mode 100644 data/spells/scripts/support/conjure food.lua delete mode 100644 data/talkactions/lib/talkactions.lua delete mode 100644 data/talkactions/scripts/animatedtext.lua create mode 100644 data/talkactions/scripts/balance.lua create mode 100644 data/talkactions/scripts/ban.lua create mode 100644 data/talkactions/scripts/mute.lua create mode 100644 data/talkactions/scripts/skill.lua delete mode 100644 data/talkactions/scripts/teleportmaster.lua create mode 100644 data/talkactions/scripts/war.lua delete mode 100644 data/weapons/lib/weapons.lua create mode 100644 data/world/tfs-house.xml create mode 100644 data/world/tfs-spawn.xml create mode 100644 data/world/tfs.otbm create mode 100644 databasemysqlpp.cpp rename databaseodbc.h => databasemysqlpp.h (57%) delete mode 100644 databaseodbc.cpp delete mode 100644 debianfix.sh delete mode 100644 dev-cpp/TheForgottenServer-console-debug.dev delete mode 100644 dev-cpp/TheForgottenServer-console.dev delete mode 100644 dev-cpp/TheForgottenServer-debug.dev rename tasks.cpp => dispatcher.cpp (94%) rename tasks.h => dispatcher.h (97%) create mode 100644 doc/OUTFIT_STATS delete mode 100644 gui.cpp delete mode 100644 gui.h delete mode 100644 gui_resources.rc delete mode 100644 inputbox.cpp delete mode 100644 inputbox.h create mode 100644 iomarket.cpp create mode 100644 iomarket.h create mode 100644 manager.cpp create mode 100644 manager.h delete mode 100644 md5.cpp delete mode 100644 md5.h create mode 100644 mods/change-gold.xml create mode 100644 mounts.cpp create mode 100644 mounts.h rename rsa.h => otpch.cpp (66%) delete mode 100644 playerbox.cpp delete mode 100644 playerbox.h delete mode 100644 resources.h delete mode 100644 rsa.cpp create mode 100644 run.sh delete mode 100644 sha1.cpp delete mode 100644 sha1.h rename forgottenserver.s3db => theforgottenserver.s3db (68%) create mode 100644 vc10/TheForgottenServer.ico create mode 100644 vc10/TheForgottenServer.rc create mode 100644 vc10/arch32.props create mode 100644 vc10/arch64.props create mode 100644 vc10/debug.props create mode 100644 vc10/release.props create mode 100644 vc10/settings.props create mode 100644 vc10/tfs.sln create mode 100644 vc10/tfs.vcxproj create mode 100644 vc10/tfs.vcxproj.filters diff --git a/Makefile.am b/Makefile.am index 19bbc99..1d9abbf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,58 +1,57 @@ noinst_PROGRAMS = theforgottenserver -CXXFLAGS = -g -O1 +CXXFLAGS = -pipe -g AM_CXXFLAGS = $(XML_CPPFLAGS) $(OTSERV_FLAGS) $(LUA_CFLAGS) $(DEBUG_FLAGS)\ -$(MYSQL_FLAGS) $(SQLITE_FLAGS) $(ODBC_FLAGS) $(PGSQL_FLAGS)\ -$(PROFILER_FLAGS) $(OPTIONAL_FLAGS) -D_THREAD_SAFE -D_REENTRANT -Wall +$(MYSQL_FLAGS) $(MYSQLPP_FLAGS) $(SQLITE_FLAGS) $(PGSQL_FLAGS) $(PROFILER_FLAGS)\ +$(OPTIONAL_FLAGS) -D_THREAD_SAFE -D_REENTRANT -Wall -Wextra -Werror -Wno-strict-aliasing theforgottenserver_LDADD = $(LUA_LIBS) if USE_MYSQL MAYBE_MYSQL = databasemysql.cpp databasemysql.h endif +if USE_MYSQLPP +MAYBE_MYSQLPP = databasemysqlpp.cpp databasemysqlpp.h +endif if USE_SQLITE MAYBE_SQLITE = databasesqlite.cpp databasesqlite.h endif if USE_PGSQL MAYBE_PGSQL = databasepgsql.cpp databasepgsql.h endif -if USE_ODBC -MAYBE_ODBC = databaseodbc.cpp databaseodbc.h -endif if LOGIN_SERVER MAYBE_LOGIN = gameservers.cpp gameservers.h endif -if REMOTE_CONTROL -MAYBE_REMOTE = admin.cpp admin.h +if OT_ADMIN +MAYBE_OTADMIN = admin.cpp admin.h endif -theforgottenserver_SOURCES = account.h actions.cpp actions.h $(MAYBE_REMOTE) \ +theforgottenserver_SOURCES = account.h actions.cpp actions.h $(MAYBE_OTADMIN) \ allocator.cpp allocator.h baseevents.cpp baseevents.h beds.cpp \ beds.h chat.cpp chat.h combat.cpp combat.h condition.cpp condition.h \ - config.h configmanager.cpp configmanager.h connection.cpp \ - connection.h const.h container.cpp container.h creature.cpp \ - creature.h creatureevent.cpp creatureevent.h cylinder.cpp cylinder.h \ - database.cpp database.h databasemanager.cpp databasemanager.h \ - $(MAYBE_MYSQL) $(MAYBE_SQLITE) $(MAYBE_PGSQL) $(MAYBE_ODBC) \ - depot.cpp depot.h exception.cpp exception.h fileloader.cpp \ - fileloader.h game.cpp game.h $(MAYBE_LOGIN) globalevent.cpp \ - globalevent.h group.cpp group.h gui.cpp gui.h house.cpp house.h \ - housetile.cpp housetile.h inputbox.cpp inputbox.h ioban.cpp ioban.h \ - ioguild.cpp ioguild.h iologindata.cpp iologindata.h iomap.cpp \ - iomapserialize.cpp iomapserialize.h item.cpp item.h itemattributes.cpp \ - itemattributes.h items.cpp items.h luascript.cpp luascript.h \ - mailbox.cpp mailbox.h map.cpp map.h md5.cpp md5.h monster.cpp \ - monster.h monsters.cpp monsters.h movement.cpp movement.h \ - networkmessage.cpp networkmessage.h npc.cpp npc.h otpch.h \ - otserv.cpp otsystem.h outfit.cpp outfit.h outputmessage.cpp \ - outputmessage.h party.cpp party.h playerbox.cpp playerbox.h \ - player.cpp player.h position.cpp position.h protocol.cpp protocol.h \ - protocolgame.cpp protocolgame.h protocollogin.cpp protocollogin.h \ - protocolold.cpp protocolold.h quests.cpp quests.h raids.cpp raids.h \ - resources.h rsa.cpp rsa.h scheduler.cpp scheduler.h scriptmanager.cpp \ - scriptmanager.h server.cpp server.h sha1.cpp sha1.h spawn.cpp spawn.h \ - spells.cpp spells.h status.cpp status.h talkaction.cpp talkaction.h \ - tasks.cpp tasks.h teleport.cpp teleport.h templates.h textlogger.cpp \ - textlogger.h thing.cpp thing.h tile.cpp tile.h tools.cpp tools.h \ - town.h trashholder.cpp trashholder.h waitlist.cpp waitlist.h \ - waypoints.h weapons.cpp weapons.h vocation.cpp vocation.h + config.h configmanager.cpp configmanager.h connection.cpp connection.h \ + const.h container.cpp container.h creature.cpp creature.h \ + creatureevent.cpp creatureevent.h cylinder.cpp cylinder.h database.cpp \ + database.h databasemanager.cpp databasemanager.h $(MAYBE_MYSQL) \ + $(MAYBE_MYSQLPP) $(MAYBE_SQLITE) $(MAYBE_PGSQL) depot.cpp depot.h \ + dispatcher.cpp dispatcher.h exception.cpp exception.h fileloader.cpp \ + fileloader.h game.cpp game.h $(MAYBE_LOGIN) globalevent.cpp globalevent.h \ + group.cpp group.h house.cpp house.h housetile.cpp housetile.h ioban.cpp \ + ioban.h ioguild.cpp ioguild.h iologindata.cpp iologindata.h iomap.cpp \ + iomap.h iomapserialize.cpp iomapserialize.h iomarket.cpp iomarket.h \ + item.cpp item.h itemattributes.cpp itemattributes.h items.cpp items.h \ + luascript.cpp luascript.h mailbox.cpp mailbox.h manager.cpp manager.h \ + map.cpp map.h monster.cpp monster.h monsters.cpp monsters.h mounts.cpp \ + mounts.h movement.cpp movement.h networkmessage.cpp networkmessage.h \ + npc.cpp npc.h otpch.h otserv.cpp otsystem.h outfit.cpp outfit.h \ + outputmessage.cpp outputmessage.h party.cpp party.h player.cpp player.h \ + position.cpp position.h protocol.cpp protocol.h protocolgame.cpp \ + protocolgame.h protocolhttp.cpp protocolhttp.h protocollogin.cpp \ + protocollogin.h protocolold.cpp protocolold.h quests.cpp quests.h \ + raids.cpp raids.h scheduler.cpp scheduler.h scriptmanager.cpp \ + scriptmanager.h server.cpp server.h spawn.cpp spawn.h spells.cpp \ + spells.h status.cpp status.h talkaction.cpp talkaction.h teleport.cpp \ + teleport.h templates.h textlogger.cpp textlogger.h thing.cpp thing.h \ + tile.cpp tile.h tools.cpp tools.h town.h trashholder.cpp trashholder.h \ + waitlist.cpp waitlist.h waypoints.h weapons.cpp weapons.h vocation.cpp \ + vocation.h diff --git a/Makefile.mingw b/Makefile.mingw new file mode 100644 index 0000000..b3b08d5 --- /dev/null +++ b/Makefile.mingw @@ -0,0 +1,69 @@ +# Makefile for Windows (MinGW) +# Copyright (C) 2011 Fallen +CXX = @echo Compiling $< && g++ + +CXXFLAGS = -O2 -Wall -Wno-strict-aliasing -D_THREAD_SAFE -D_REENTRANT -D__ENABLE_SERVER_DIAGNOSTIC__ -D__NO_CRYPTOPP__ + +LIBS = -llua5.1 -lboost_system -lboost_thread -lboost_regex -lboost_filesystem -leay32 -lxml2 -lws2_32 -lwsock32 + +ifdef USE_MYSQL + MAYBE_MYSQL = databasemysql.cpp + CXXFLAGS += -D__USE_MYSQL__ + LIBS += -lmysql +endif +ifdef USE_SQLITE + MAYBE_SQLITE = databasesqlite.cpp + CXXFLAGS += -D__USE_SQLITE__ + LIBS += -lsqlite3 +endif +ifdef USE_PGSQL + MAYBE_PGSQL = databasepgsql.cpp +endif +ifdef LOGIN_SERVER + MAYBE_LOGIN = gameservers.cpp +endif +ifdef OT_ADMIN + MAYBE_OTADMIN = admin.cpp +endif + +SRC = weapons.cpp waitlist.cpp vocation.cpp trashholder.cpp tools.cpp \ + tile.cpp thing.cpp textlogger.cpp teleport.cpp talkaction.cpp \ + status.cpp spells.cpp spawn.cpp server.cpp scriptmanager.cpp \ + scheduler.cpp raids.cpp quests.cpp protocolold.cpp protocollogin.cpp \ + protocolhttp.cpp protocolgame.cpp protocol.cpp position.cpp player.cpp \ + party.cpp outputmessage.cpp outfit.cpp otserv.cpp npc.cpp \ + networkmessage.cpp movement.cpp mounts.cpp monsters.cpp monster.cpp \ + map.cpp manager.cpp mailbox.cpp luascript.cpp items.cpp \ + itemattributes.cpp item.cpp iomarket.cpp iomapserialize.cpp iomap.cpp \ + iologindata.cpp ioguild.cpp ioban.cpp housetile.cpp house.cpp group.cpp \ + globalevent.cpp gameservers.cpp game.cpp fileloader.cpp exception.cpp \ + dispatcher.cpp depot.cpp databasemanager.cpp database.cpp cylinder.cpp \ + creatureevent.cpp creature.cpp container.cpp connection.cpp configmanager.cpp \ + condition.cpp combat.cpp chat.cpp beds.cpp baseevents.cpp allocator.cpp admin.cpp \ + actions.cpp $(MAYBE_MYSQL) $(MAYBE_SQLITE) $(MAYBE_PGSQL) \ + $(MAYBE_LOGIN) $(MAYBE_OTADMIN) + +INCPATH = -I"." + +LFLAGS = -enable-stdcall-fixup -enable-auto-import -enable-runtime-pseudo-reloc $(CXXFLAGS) + +DEL = del + +TARGET = theforgottenserver + +OBJDIR = obj + +OBJ = $(SRC:%.cpp=$(OBJDIR)/%.o) + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(OBJ) + $(CXX) $(LFLAGS) -o $@ $(OBJ) $(LIBS) + +$(OBJDIR)/%.o: %.cpp + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< + +clean: + -$(DEL) $(OBJ) $(TARGET) diff --git a/account.h b/account.h index 27b72e7..0fd3f9d 100644 --- a/account.h +++ b/account.h @@ -22,19 +22,30 @@ typedef std::list Characters; #else -#include "gameservers.h" -typedef std::map Characters; +class GameServer; +struct Character +{ + Character(): server(NULL), status(0) {} + Character(const std::string& _name, GameServer* _server, int8_t _status): + name(_name), server(_server), status(_status) {} + + std::string name; + GameServer* server; + int8_t status; +}; +typedef std::map Characters; #endif + class Account { public: - Account() {number = premiumDays = lastDay = warnings = 0;} + Account() {premiumDays = warnings = number = lastDay = 0;} virtual ~Account() {charList.clear();} - uint32_t number, premiumDays, lastDay; - int32_t warnings; - std::string name, password, recoveryKey; + uint16_t premiumDays, warnings; + uint32_t number, lastDay; + std::string name, password, recoveryKey, salt; Characters charList; }; #endif diff --git a/actions.cpp b/actions.cpp index aba2140..6fdf968 100644 --- a/actions.cpp +++ b/actions.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" + #include "const.h" #include "actions.h" @@ -48,6 +49,7 @@ Actions::Actions(): m_interface("Action Interface") { m_interface.initState(); + defaultAction = NULL; } Actions::~Actions() @@ -70,6 +72,8 @@ void Actions::clear() clearMap(actionItemMap); m_interface.reInitState(); + delete defaultAction; + defaultAction = NULL; } Event* Actions::getEvent(const std::string& nodeName) @@ -86,18 +90,31 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) if(!action) return false; - StringVec strVector; - IntegerVec intVector, endIntVector; + std::string strValue; + if(readXMLString(p, "default", strValue) && booleanString(strValue)) + { + if(!defaultAction) + defaultAction = action; + else if(override) + { + delete defaultAction; + defaultAction = action; + } + else + std::clog << "[Warning - Actions::registerEvent] You cannot define more than one default action, if you want to do so " + << "Please define \"override\"." << std::endl; - std::string strValue, endStrValue; - int32_t tmp = 0; + return true; + } bool success = true; + std::string endValue; if(readXMLString(p, "itemid", strValue)) { + IntegerVec intVector; if(!parseIntegerVec(strValue, intVector)) { - std::cout << "[Warning - Actions::registerEvent] Invalid itemid - '" << strValue << "'" << std::endl; + std::clog << "[Warning - Actions::registerEvent] Invalid itemid - '" << strValue << "'" << std::endl; return false; } @@ -105,7 +122,7 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) { if(!override) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item id: " << intVector[0] << std::endl; + std::clog << "[Warning - Actions::registerEvent] Duplicate registered item id: " << intVector[0] << std::endl; success = false; } else @@ -121,7 +138,7 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) { if(!override) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item id: " << intVector[i] << std::endl; + std::clog << "[Warning - Actions::registerEvent] Duplicate registered item id: " << intVector[i] << std::endl; continue; } else @@ -131,24 +148,23 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) useItemMap[intVector[i]] = new Action(action); } } - - if(readXMLString(p, "fromid", strValue) && readXMLString(p, "toid", endStrValue)) + else if(readXMLString(p, "fromid", strValue) && readXMLString(p, "toid", endValue)) { - intVector = vectorAtoi(explodeString(strValue, ";")); - endIntVector = vectorAtoi(explodeString(endStrValue, ";")); - if(intVector[0] && endIntVector[0] && intVector.size() == endIntVector.size()) + IntegerVec intVector = vectorAtoi(explodeString(strValue, ";")), endVector = vectorAtoi(explodeString(endValue, ";")); + if(intVector[0] && endVector[0] && intVector.size() == endVector.size()) { + int32_t tmp = 0; for(size_t i = 0, size = intVector.size(); i < size; ++i) { tmp = intVector[i]; - while(intVector[i] <= endIntVector[i]) + while(intVector[i] <= endVector[i]) { if(useItemMap.find(intVector[i]) != useItemMap.end()) { if(!override) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << intVector[i] << - ", in fromid: " << tmp << " and toid: " << endIntVector[i] << std::endl; + std::clog << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << intVector[i] << + ", in fromid: " << tmp << " and toid: " << endVector[i] << std::endl; intVector[i]++; continue; } @@ -161,15 +177,16 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) } } else - std::cout << "[Warning - Actions::registerEvent] Malformed entry (from item: \"" << strValue << - "\", to item: \"" << endStrValue << "\")" << std::endl; + std::clog << "[Warning - Actions::registerEvent] Malformed entry (from item: \"" << strValue << + "\", to item: \"" << endValue << "\")" << std::endl; } if(readXMLString(p, "uniqueid", strValue)) { + IntegerVec intVector; if(!parseIntegerVec(strValue, intVector)) { - std::cout << "[Warning - Actions::registerEvent] Invalid uniqueid - '" << strValue << "'" << std::endl; + std::clog << "[Warning - Actions::registerEvent] Invalid uniqueid - '" << strValue << "'" << std::endl; return false; } @@ -177,7 +194,7 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) { if(!override) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item uid: " << intVector[0] << std::endl; + std::clog << "[Warning - Actions::registerEvent] Duplicate registered item uid: " << intVector[0] << std::endl; success = false; } else @@ -193,7 +210,7 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) { if(!override) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item uid: " << intVector[i] << std::endl; + std::clog << "[Warning - Actions::registerEvent] Duplicate registered item uid: " << intVector[i] << std::endl; continue; } else @@ -203,24 +220,23 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) uniqueItemMap[intVector[i]] = new Action(action); } } - - if(readXMLString(p, "fromuid", strValue) && readXMLString(p, "touid", endStrValue)) + else if(readXMLString(p, "fromuid", strValue) && readXMLString(p, "touid", endValue)) { - intVector = vectorAtoi(explodeString(strValue, ";")); - endIntVector = vectorAtoi(explodeString(endStrValue, ";")); - if(intVector[0] && endIntVector[0] && intVector.size() == endIntVector.size()) + IntegerVec intVector = vectorAtoi(explodeString(strValue, ";")), endVector = vectorAtoi(explodeString(endValue, ";")); + if(intVector[0] && endVector[0] && intVector.size() == endVector.size()) { + int32_t tmp = 0; for(size_t i = 0, size = intVector.size(); i < size; ++i) { tmp = intVector[i]; - while(intVector[i] <= endIntVector[i]) + while(intVector[i] <= endVector[i]) { if(uniqueItemMap.find(intVector[i]) != uniqueItemMap.end()) { if(!override) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with uid: " << intVector[i] << - ", in fromuid: " << tmp << " and touid: " << endIntVector[i] << std::endl; + std::clog << "[Warning - Actions::registerEvent] Duplicate registered item with uid: " << intVector[i] << + ", in fromuid: " << tmp << " and touid: " << endVector[i] << std::endl; intVector[i]++; continue; } @@ -233,15 +249,16 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) } } else - std::cout << "[Warning - Actions::registerEvent] Malformed entry (from unique: \"" << strValue << - "\", to unique: \"" << endStrValue << "\")" << std::endl; + std::clog << "[Warning - Actions::registerEvent] Malformed entry (from unique: \"" << strValue << + "\", to unique: \"" << endValue << "\")" << std::endl; } - if(readXMLString(p, "actionid", strValue)) + if(readXMLString(p, "actionid", strValue) || readXMLString(p, "aid", strValue)) { + IntegerVec intVector; if(!parseIntegerVec(strValue, intVector)) { - std::cout << "[Warning - Actions::registerEvent] Invalid actionid - '" << strValue << "'" << std::endl; + std::clog << "[Warning - Actions::registerEvent] Invalid actionid - '" << strValue << "'" << std::endl; return false; } @@ -249,7 +266,7 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) { if(!override) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item aid: " << intVector[0] << std::endl; + std::clog << "[Warning - Actions::registerEvent] Duplicate registered item aid: " << intVector[0] << std::endl; success = false; } else @@ -265,7 +282,7 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) { if(!override) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item aid: " << intVector[i] << std::endl; + std::clog << "[Warning - Actions::registerEvent] Duplicate registered item aid: " << intVector[i] << std::endl; continue; } else @@ -275,24 +292,23 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) actionItemMap[intVector[i]] = new Action(action); } } - - if(readXMLString(p, "fromaid", strValue) && readXMLString(p, "toaid", endStrValue)) + else if(readXMLString(p, "fromaid", strValue) && readXMLString(p, "toaid", endValue)) { - intVector = vectorAtoi(explodeString(strValue, ";")); - endIntVector = vectorAtoi(explodeString(endStrValue, ";")); - if(intVector[0] && endIntVector[0] && intVector.size() == endIntVector.size()) + IntegerVec intVector = vectorAtoi(explodeString(strValue, ";")), endVector = vectorAtoi(explodeString(endValue, ";")); + if(intVector[0] && endVector[0] && intVector.size() == endVector.size()) { + int32_t tmp = 0; for(size_t i = 0, size = intVector.size(); i < size; ++i) { tmp = intVector[i]; - while(intVector[i] <= endIntVector[i]) + while(intVector[i] <= endVector[i]) { if(actionItemMap.find(intVector[i]) != actionItemMap.end()) { if(!override) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with aid: " << intVector[i] << - ", in fromaid: " << tmp << " and toaid: " << endIntVector[i] << std::endl; + std::clog << "[Warning - Actions::registerEvent] Duplicate registered item with aid: " << intVector[i] << + ", in fromaid: " << tmp << " and toaid: " << endVector[i] << std::endl; intVector[i]++; continue; } @@ -305,8 +321,8 @@ bool Actions::registerEvent(Event* event, xmlNodePtr p, bool override) } } else - std::cout << "[Warning - Actions::registerEvent] Malformed entry (from action: \"" << strValue << - "\", to action: \"" << endStrValue << "\")" << std::endl; + std::clog << "[Warning - Actions::registerEvent] Malformed entry (from action: \"" << strValue << + "\", to action: \"" << endValue << "\")" << std::endl; } return success; @@ -327,10 +343,17 @@ ReturnValue Actions::canUse(const Player* player, const Position& pos) if(!Position::areInRange<1,1,0>(playerPos, pos)) return RET_TOOFARAWAY; + Tile* tile = g_game.getTile(pos); + if(tile) + { + HouseTile* houseTile = tile->getHouseTile(); + if(houseTile && houseTile->getHouse() && !houseTile->getHouse()->isInvited(player)) + return RET_PLAYERISNOTINVITED; + } return RET_NOERROR; } -ReturnValue Actions::canUse(const Player* player, const Position& pos, const Item* item) +ReturnValue Actions::canUseEx(const Player* player, const Position& pos, const Item* item) { Action* action = NULL; if((action = getAction(item, ACTION_UNIQUEID))) @@ -345,6 +368,9 @@ ReturnValue Actions::canUse(const Player* player, const Position& pos, const Ite if((action = getAction(item, ACTION_RUNEID))) return action->canExecuteAction(player, pos); + if(defaultAction) + return defaultAction->canExecuteAction(player, pos); + return RET_NOERROR; } @@ -369,7 +395,7 @@ ReturnValue Actions::canUseFar(const Creature* creature, const Position& toPos, return RET_NOERROR; } -Action* Actions::getAction(const Item* item, ActionType_t type/* = ACTION_ANY*/) const +Action* Actions::getAction(const Item* item, ActionType_t type) const { if(item->getUniqueId() && (type == ACTION_ANY || type == ACTION_UNIQUEID)) { @@ -420,71 +446,35 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_ tmp = item->getParent()->__getIndexOfThing(item); PositionEx posEx(pos, tmp); - bool executed = false; - Action* action = NULL; if((action = getAction(item, ACTION_UNIQUEID))) { - if(action->isScripted()) - { - if(executeUse(action, player, item, posEx, creatureId)) - return RET_NOERROR; - } - else if(action->function) - { - if(action->function(player, item, posEx, posEx, false, creatureId)) - return RET_NOERROR; - } - - executed = true; + if(executeUse(action, player, item, posEx, creatureId)) + return RET_NOERROR; } if((action = getAction(item, ACTION_ACTIONID))) { - if(action->isScripted()) - { - if(executeUse(action, player, item, posEx, creatureId)) - return RET_NOERROR; - } - else if(action->function) - { - if(action->function(player, item, posEx, posEx, false, creatureId)) - return RET_NOERROR; - } - - executed = true; + if(executeUse(action, player, item, posEx, creatureId)) + return RET_NOERROR; } if((action = getAction(item, ACTION_ITEMID))) { - if(action->isScripted()) - { - if(executeUse(action, player, item, posEx, creatureId)) - return RET_NOERROR; - } - else if(action->function) - { - if(action->function(player, item, posEx, posEx, false, creatureId)) - return RET_NOERROR; - } - - executed = true; + if(executeUse(action, player, item, posEx, creatureId)) + return RET_NOERROR; } if((action = getAction(item, ACTION_RUNEID))) { - if(action->isScripted()) - { - if(executeUse(action, player, item, posEx, creatureId)) - return RET_NOERROR; - } - else if(action->function) - { - if(action->function(player, item, posEx, posEx, false, creatureId)) - return RET_NOERROR; - } + if(executeUse(action, player, item, posEx, creatureId)) + return RET_NOERROR; + } - executed = true; + if(defaultAction) + { + if(executeUse(defaultAction, player, item, posEx, creatureId)) + return RET_NOERROR; } if(BedItem* bed = item->getBed()) @@ -505,6 +495,9 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_ Container* tmpContainer = NULL; if(Depot* depot = container->getDepot()) { + if(player->hasFlag(PlayerFlag_CannotPickupItem)) + return RET_CANNOTUSETHISOBJECT; + if(Depot* playerDepot = player->getDepot(depot->getDepotId(), true)) { player->useDepot(depot->getDepotId(), true); @@ -547,10 +540,40 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_ return RET_NOERROR; } - if(!executed) - return RET_CANNOTUSETHISOBJECT; + if(item->getID() == ITEM_MARKET) + { + if(!g_config.getBool(ConfigManager::MARKET_ENABLED)) + { + player->sendTextMessage(MSG_INFO_DESCR, "The market is disabled."); + return RET_NOERROR; + } - return RET_NOERROR; + Depot* depot = NULL; + if(Cylinder* cylinder = item->getParent()) + { + if(Item* parentItem = cylinder->getItem()) + { + if(Container* parentContainer = parentItem->getContainer()) + depot = parentContainer->getDepot(); + } + } + + if(depot == NULL) + return RET_CANNOTUSETHISOBJECT; + + player->sendMarketEnter(depot->getDepotId()); + return RET_NOERROR; + } + + const ItemType& it = Item::items[item->getID()]; + if(it.transformUseTo) + { + g_game.transformItem(item, it.transformUseTo); + g_game.startDecay(item); + return RET_NOERROR; + } + + return RET_CANNOTUSETHISOBJECT; } bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* item) @@ -560,7 +583,7 @@ bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* player->setNextActionTask(NULL); player->stopWalk(); - player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL) - SCHEDULER_MINTICKS); + player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL) - 10); ReturnValue ret = internalUseItem(player, pos, index, item, 0); if(ret == RET_NOERROR) @@ -601,6 +624,7 @@ ReturnValue Actions::internalUseItemEx(Player* player, const PositionEx& fromPos //only continue with next action in the list if the previous returns false if(executeUseEx(action, player, item, fromPosEx, toPosEx, isHotkey, creatureId)) return RET_NOERROR; + } if((action = getAction(item, ACTION_ITEMID))) @@ -625,6 +649,17 @@ ReturnValue Actions::internalUseItemEx(Player* player, const PositionEx& fromPos return RET_NOERROR; } + if(defaultAction) + { + ReturnValue ret = defaultAction->canExecuteAction(player, toPosEx); + if(ret != RET_NOERROR) + return ret; + + //only continue with next action in the list if the previous returns false + if(executeUseEx(defaultAction, player, item, fromPosEx, toPosEx, isHotkey, creatureId)) + return RET_NOERROR; + } + return RET_CANNOTUSETHISOBJECT; } @@ -636,14 +671,7 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position& player->setNextActionTask(NULL); player->stopWalk(); - player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::EX_ACTIONS_DELAY_INTERVAL) - SCHEDULER_MINTICKS); - - Action* action = getAction(item); - if(!action) - { - player->sendCancelMessage(RET_CANNOTUSETHISOBJECT); - return false; - } + player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::EX_ACTIONS_DELAY_INTERVAL) - 10); int32_t fromStackPos = 0; if(item->getParent()) @@ -653,16 +681,14 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position& PositionEx toPosEx(toPos, toStackPos); ReturnValue ret = internalUseItemEx(player, fromPosEx, toPosEx, item, isHotkey, creatureId); - if(ret != RET_NOERROR) - { - player->sendCancelMessage(ret); - return false; - } + if(ret == RET_NOERROR) + return true; - return true; + player->sendCancelMessage(ret); + return false; } -Action::Action(LuaScriptInterface* _interface): +Action::Action(LuaInterface* _interface): Event(_interface) { allowFarUse = false; @@ -688,50 +714,18 @@ bool Action::configureEvent(xmlNodePtr p) return true; } -bool Action::loadFunction(const std::string& functionName) -{ - std::string tmpFunctionName = asLowerCaseString(functionName); - if(tmpFunctionName == "increaseitemid") - function = increaseItemId; - else if(tmpFunctionName == "decreaseitemid") - function = decreaseItemId; - else - { - std::cout << "[Warning - Action::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; - return false; - } - - m_scripted = EVENT_SCRIPT_FALSE; - return true; -} - -bool Action::increaseItemId(Player* player, Item* item, const PositionEx& posFrom, const PositionEx& posTo, bool extendedUse, uint32_t creatureId) +ReturnValue Action::canExecuteAction(const Player* player, const Position& pos) { - if(!player || !item) - return false; - - g_game.transformItem(item, item->getID() + 1); - return true; -} - -bool Action::decreaseItemId(Player* player, Item* item, const PositionEx& posFrom, const PositionEx& posTo, bool extendedUse, uint32_t creatureId) -{ - if(!player || !item) - return false; - - g_game.transformItem(item, item->getID() - 1); - return true; -} + if(player->hasCustomFlag(PlayerCustomFlag_CanUseFar)) + return RET_NOERROR; -ReturnValue Action::canExecuteAction(const Player* player, const Position& toPos) -{ if(!getAllowFarUse()) - return g_actions->canUse(player, toPos); + return g_actions->canUse(player, pos); - return g_actions->canUseFar(player, toPos, getCheckLineOfSight()); + return g_actions->canUseFar(player, pos, getCheckLineOfSight()); } -bool Action::executeUse(Player* player, Item* item, const PositionEx& fromPos, const PositionEx& toPos, bool extendedUse, uint32_t creatureId) +bool Action::executeUse(Player* player, Item* item, const PositionEx& fromPos, const PositionEx& toPos, bool extendedUse, uint32_t) { //onUse(cid, item, fromPosition, itemEx, toPosition) if(m_interface->reserveEnv()) @@ -752,8 +746,15 @@ bool Action::executeUse(Player* player, Item* item, const PositionEx& fromPos, c env->streamThing(scriptstream, "itemEx", thing, env->addThing(thing)); env->streamPosition(scriptstream, "toPosition", toPos, toPos.stackpos); } + else + { + env->streamThing(scriptstream, "itemEx", NULL, 0); + env->streamPosition(scriptstream, "toPosition", PositionEx()); + } + + if(m_scriptData) + scriptstream << *m_scriptData; - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -769,7 +770,7 @@ bool Action::executeUse(Player* player, Item* item, const PositionEx& fromPos, c #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << player->getName() << " - " << item->getID() << " " << fromPos << "|" << toPos; - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); @@ -779,19 +780,19 @@ bool Action::executeUse(Player* player, Item* item, const PositionEx& fromPos, c m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(player)); - LuaScriptInterface::pushThing(L, item, env->addThing(item)); - LuaScriptInterface::pushPosition(L, fromPos, fromPos.stackpos); + LuaInterface::pushThing(L, item, env->addThing(item)); + LuaInterface::pushPosition(L, fromPos, fromPos.stackpos); Thing* thing = g_game.internalGetThing(player, toPos, toPos.stackpos); if(thing && (thing != item || !extendedUse)) { - LuaScriptInterface::pushThing(L, thing, env->addThing(thing)); - LuaScriptInterface::pushPosition(L, toPos, toPos.stackpos); + LuaInterface::pushThing(L, thing, env->addThing(thing)); + LuaInterface::pushPosition(L, toPos, toPos.stackpos); } else { - LuaScriptInterface::pushThing(L, NULL, 0); - LuaScriptInterface::pushPosition(L, fromPos, fromPos.stackpos); + LuaInterface::pushThing(L, NULL, 0); + LuaInterface::pushPosition(L, PositionEx()); } bool result = m_interface->callFunction(5); @@ -801,7 +802,7 @@ bool Action::executeUse(Player* player, Item* item, const PositionEx& fromPos, c } else { - std::cout << "[Error - Action::executeUse]: Call stack overflow." << std::endl; + std::clog << "[Error - Action::executeUse]: Call stack overflow." << std::endl; return false; } } diff --git a/actions.h b/actions.h index 8beacf2..c7ed108 100644 --- a/actions.h +++ b/actions.h @@ -31,7 +31,7 @@ enum ActionType_t ACTION_UNIQUEID, ACTION_ACTIONID, ACTION_ITEMID, - ACTION_RUNEID, + ACTION_RUNEID }; class Actions : public BaseEvents @@ -45,19 +45,21 @@ class Actions : public BaseEvents uint8_t toStackPos, Item* item, bool isHotkey, uint32_t creatureId = 0); ReturnValue canUse(const Player* player, const Position& pos); - ReturnValue canUse(const Player* player, const Position& pos, const Item* item); + ReturnValue canUseEx(const Player* player, const Position& pos, const Item* item); ReturnValue canUseFar(const Creature* creature, const Position& toPos, bool checkLineOfSight); - bool hasAction(const Item* item) const {return getAction(item);} + bool hasAction(const Item* item) const {return getAction(item, ACTION_ANY) != NULL;} protected: + Action* defaultAction; + virtual std::string getScriptBaseName() const {return "actions";} virtual void clear(); virtual Event* getEvent(const std::string& nodeName); virtual bool registerEvent(Event* event, xmlNodePtr p, bool override); - virtual LuaScriptInterface& getInterface() {return m_interface;} - LuaScriptInterface m_interface; + virtual LuaInterface& getInterface() {return m_interface;} + LuaInterface m_interface; void registerItemID(int32_t itemId, Event* event); void registerActionID(int32_t actionId, Event* event); @@ -76,20 +78,18 @@ class Actions : public BaseEvents ReturnValue internalUseItemEx(Player* player, const PositionEx& fromPosEx, const PositionEx& toPosEx, Item* item, bool isHotkey, uint32_t creatureId); - Action* getAction(const Item* item, ActionType_t type = ACTION_ANY) const; + Action* getAction(const Item* item, ActionType_t type) const; void clearMap(ActionUseMap& map); }; -typedef bool (ActionFunction)(Player* player, Item* item, const PositionEx& posFrom, const PositionEx& posTo, bool extendedUse, uint32_t creatureId); class Action : public Event { public: Action(const Action* copy); - Action(LuaScriptInterface* _interface); + Action(LuaInterface* _interface); virtual ~Action() {} virtual bool configureEvent(xmlNodePtr p); - virtual bool loadFunction(const std::string& functionName); //scripting virtual bool executeUse(Player* player, Item* item, const PositionEx& posFrom, @@ -101,18 +101,13 @@ class Action : public Event bool getCheckLineOfSight() const {return checkLineOfSight;} void setCheckLineOfSight(bool v) {checkLineOfSight = v;} - virtual ReturnValue canExecuteAction(const Player* player, const Position& toPos); + virtual ReturnValue canExecuteAction(const Player* player, const Position& pos); virtual bool hasOwnErrorHandler() {return false;} - ActionFunction* function; - protected: virtual std::string getScriptEventName() const {return "onUse";} virtual std::string getScriptEventParams() const {return "cid, item, fromPosition, itemEx, toPosition";} - static ActionFunction increaseItemId; - static ActionFunction decreaseItemId; - bool allowFarUse; bool checkLineOfSight; }; diff --git a/admin.cpp b/admin.cpp index 897a274..04a235a 100644 --- a/admin.cpp +++ b/admin.cpp @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// -#ifdef __REMOTE_CONTROL__ #include "otpch.h" +#ifdef __OTADMIN__ #include #include "admin.h" @@ -33,9 +33,8 @@ #include "town.h" #include "iologindata.h" -extern Game g_game; extern ConfigManager g_config; -Admin* g_admin = NULL; +extern Game g_game; #ifdef __ENABLE_SERVER_DIAGNOSTIC__ uint32_t ProtocolAdmin::protocolAdminCount = 0; @@ -43,21 +42,22 @@ uint32_t ProtocolAdmin::protocolAdminCount = 0; void ProtocolAdmin::onRecvFirstMessage(NetworkMessage& msg) { - if(!g_admin->enabled()) + m_state = NO_CONNECTED; + if(g_config.getString(ConfigManager::ADMIN_PASSWORD).empty()) { + addLogLine(LOGTYPE_EVENT, "connection attempt on disabled protocol"); getConnection()->close(); return; } - m_state = NO_CONNECTED; - if(!g_admin->allowIP(getIP())) + if(!Admin::getInstance()->allow(getIP())) { addLogLine(LOGTYPE_EVENT, "ip not allowed"); getConnection()->close(); return; } - if(!g_admin->addConnection()) + if(!Admin::getInstance()->addConnection()) { addLogLine(LOGTYPE_EVENT, "cannot add new connection"); getConnection()->close(); @@ -68,11 +68,12 @@ void ProtocolAdmin::onRecvFirstMessage(NetworkMessage& msg) if(OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false)) { TRACK_MESSAGE(output); - output->AddByte(AP_MSG_HELLO); - output->AddU32(1); //version - output->AddString("OTADMIN"); - output->AddU16(g_admin->getProtocolPolicy()); //security policy - output->AddU32(g_admin->getProtocolOptions()); //protocol options(encryption, ...) + output->put(AP_MSG_HELLO); + output->put(1); //version + output->putString("OTADMIN"); + + output->put(Admin::getInstance()->getPolicy()); //security policy + output->put(Admin::getInstance()->getOptions()); //protocol options(encryption, ...) OutputMessagePool::getInstance()->send(output); } @@ -82,13 +83,13 @@ void ProtocolAdmin::onRecvFirstMessage(NetworkMessage& msg) void ProtocolAdmin::parsePacket(NetworkMessage& msg) { - if(g_game.getGameState() == GAME_STATE_SHUTDOWN) + if(g_game.getGameState() == GAMESTATE_SHUTDOWN) { getConnection()->close(); return; } - uint8_t recvbyte = msg.GetByte(); + uint8_t recvbyte = msg.get(); OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false); if(!output) return; @@ -98,7 +99,7 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) { case ENCRYPTION_NO_SET: { - if(g_admin->requireEncryption()) + if(Admin::getInstance()->isEncypted()) { if((time(NULL) - m_startTime) > 30000) { @@ -109,8 +110,8 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) if(recvbyte != AP_MSG_ENCRYPTION && recvbyte != AP_MSG_KEY_EXCHANGE) { - output->AddByte(AP_MSG_ERROR); - output->AddString("encryption needed"); + output->put(AP_MSG_ERROR); + output->putString("encryption needed"); OutputMessagePool::getInstance()->send(output); getConnection()->close(); @@ -126,7 +127,7 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) case NO_LOGGED_IN: { - if(g_admin->requireLogin()) + if(g_config.getBool(ConfigManager::ADMIN_REQUIRE_LOGIN)) { if((time(NULL) - m_startTime) > 30000) { @@ -138,8 +139,8 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) if(m_loginTries > 3) { - output->AddByte(AP_MSG_ERROR); - output->AddString("too many login tries"); + output->put(AP_MSG_ERROR); + output->putString("too many login tries"); OutputMessagePool::getInstance()->send(output); getConnection()->close(); @@ -149,8 +150,8 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) if(recvbyte != AP_MSG_LOGIN) { - output->AddByte(AP_MSG_ERROR); - output->AddString("you are not logged in"); + output->put(AP_MSG_ERROR); + output->putString("you are not logged in"); OutputMessagePool::getInstance()->send(output); getConnection()->close(); @@ -180,27 +181,28 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) { case AP_MSG_LOGIN: { - if(m_state == NO_LOGGED_IN && g_admin->requireLogin()) + if(m_state == NO_LOGGED_IN && g_config.getBool(ConfigManager::ADMIN_REQUIRE_LOGIN)) { - std::string password = msg.GetString(); - if(g_admin->passwordMatch(password)) + std::string pass = msg.getString(), word = g_config.getString(ConfigManager::ADMIN_PASSWORD); + _encrypt(word, false); + if(pass == word) { m_state = LOGGED_IN; - output->AddByte(AP_MSG_LOGIN_OK); + output->put(AP_MSG_LOGIN_OK); addLogLine(LOGTYPE_EVENT, "login ok"); } else { m_loginTries++; - output->AddByte(AP_MSG_LOGIN_FAILED); - output->AddString("wrong password"); - addLogLine(LOGTYPE_EVENT, "login failed.("+ password + ")"); + output->put(AP_MSG_LOGIN_FAILED); + output->putString("wrong password"); + addLogLine(LOGTYPE_EVENT, "login failed.("+ pass + ")"); } } else { - output->AddByte(AP_MSG_LOGIN_FAILED); - output->AddString("cannot login"); + output->put(AP_MSG_LOGIN_FAILED); + output->putString("cannot login"); addLogLine(LOGTYPE_EVENT, "wrong state at login"); } @@ -209,17 +211,17 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) case AP_MSG_ENCRYPTION: { - if(m_state == ENCRYPTION_NO_SET && g_admin->requireEncryption()) + if(m_state == ENCRYPTION_NO_SET && Admin::getInstance()->isEncypted()) { - uint8_t keyType = msg.GetByte(); + uint8_t keyType = msg.get(); switch(keyType) { case ENCRYPTION_RSA1024XTEA: { - RSA* rsa = g_admin->getRSAKey(ENCRYPTION_RSA1024XTEA); + RSA* rsa = Admin::getInstance()->getRSAKey(ENCRYPTION_RSA1024XTEA); if(!rsa) { - output->AddByte(AP_MSG_ENCRYPTION_FAILED); + output->put(AP_MSG_ENCRYPTION_FAILED); addLogLine(LOGTYPE_EVENT, "no valid server key type"); break; } @@ -227,19 +229,19 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) if(RSA_decrypt(rsa, msg)) { m_state = NO_LOGGED_IN; - uint32_t k[4]= {msg.GetU32(), msg.GetU32(), msg.GetU32(), msg.GetU32()}; + uint32_t k[4]= {msg.get(), msg.get(), msg.get(), msg.get()}; //use for in/out the new key we have enableXTEAEncryption(); setXTEAKey(k); - output->AddByte(AP_MSG_ENCRYPTION_OK); + output->put(AP_MSG_ENCRYPTION_OK); addLogLine(LOGTYPE_EVENT, "encryption ok"); } else { - output->AddByte(AP_MSG_ENCRYPTION_FAILED); - output->AddString("wrong encrypted packet"); + output->put(AP_MSG_ENCRYPTION_FAILED); + output->putString("wrong encrypted packet"); addLogLine(LOGTYPE_EVENT, "wrong encrypted packet"); } @@ -248,8 +250,8 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) default: { - output->AddByte(AP_MSG_ENCRYPTION_FAILED); - output->AddString("no valid key type"); + output->put(AP_MSG_ENCRYPTION_FAILED); + output->putString("no valid key type"); addLogLine(LOGTYPE_EVENT, "no valid client key type"); break; @@ -258,8 +260,8 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) } else { - output->AddByte(AP_MSG_ENCRYPTION_FAILED); - output->AddString("cannot set encryption"); + output->put(AP_MSG_ENCRYPTION_FAILED); + output->putString("cannot set encryption"); addLogLine(LOGTYPE_EVENT, "cannot set encryption"); } @@ -268,34 +270,34 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) case AP_MSG_KEY_EXCHANGE: { - if(m_state == ENCRYPTION_NO_SET && g_admin->requireEncryption()) + if(m_state == ENCRYPTION_NO_SET && Admin::getInstance()->isEncypted()) { - uint8_t keyType = msg.GetByte(); + uint8_t keyType = msg.get(); switch(keyType) { case ENCRYPTION_RSA1024XTEA: { - RSA* rsa = g_admin->getRSAKey(ENCRYPTION_RSA1024XTEA); + RSA* rsa = Admin::getInstance()->getRSAKey(ENCRYPTION_RSA1024XTEA); if(!rsa) { - output->AddByte(AP_MSG_KEY_EXCHANGE_FAILED); + output->put(AP_MSG_KEY_EXCHANGE_FAILED); addLogLine(LOGTYPE_EVENT, "no valid server key type"); break; } - output->AddByte(AP_MSG_KEY_EXCHANGE_OK); - output->AddByte(ENCRYPTION_RSA1024XTEA); + output->put(AP_MSG_KEY_EXCHANGE_OK); + output->put(ENCRYPTION_RSA1024XTEA); char RSAPublicKey[128]; rsa->getPublicKey(RSAPublicKey); - output->AddBytes(RSAPublicKey, 128); + output->puts(RSAPublicKey, 128); break; } default: { - output->AddByte(AP_MSG_KEY_EXCHANGE_FAILED); + output->put(AP_MSG_KEY_EXCHANGE_FAILED); addLogLine(LOGTYPE_EVENT, "no valid client key type"); break; } @@ -303,8 +305,8 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) } else { - output->AddByte(AP_MSG_KEY_EXCHANGE_FAILED); - output->AddString("cannot get public key"); + output->put(AP_MSG_KEY_EXCHANGE_FAILED); + output->putString("cannot get public key"); addLogLine(LOGTYPE_EVENT, "cannot get public key"); } @@ -319,17 +321,20 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) break; } - uint8_t command = msg.GetByte(); + uint8_t command = msg.get(); switch(command) { case CMD_SAVE_SERVER: case CMD_SHALLOW_SAVE_SERVER: { + uint8_t flags = (uint8_t)SAVE_PLAYERS | (uint8_t)SAVE_MAP | (uint8_t)SAVE_STATE; + if(command == CMD_SHALLOW_SAVE_SERVER) + flags |= SAVE_PLAYERS_SHALLOW; + addLogLine(LOGTYPE_EVENT, "saving server"); - Dispatcher::getInstance().addTask(createTask(boost::bind( - &Game::saveGameState, &g_game, (command == CMD_SHALLOW_SAVE_SERVER)))); + Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::saveGameState, &g_game, flags))); - output->AddByte(AP_MSG_COMMAND_OK); + output->put(AP_MSG_COMMAND_OK); break; } @@ -337,19 +342,18 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) { addLogLine(LOGTYPE_EVENT, "closing server"); Dispatcher::getInstance().addTask(createTask(boost::bind( - &Game::setGameState, &g_game, GAME_STATE_CLOSED))); + &Game::setGameState, &g_game, GAMESTATE_CLOSED))); - output->AddByte(AP_MSG_COMMAND_OK); + output->put(AP_MSG_COMMAND_OK); break; } case CMD_OPEN_SERVER: { addLogLine(LOGTYPE_EVENT, "opening server"); - Dispatcher::getInstance().addTask(createTask(boost::bind( - &Game::setGameState, &g_game, GAME_STATE_NORMAL))); + g_game.setGameState(GAMESTATE_NORMAL); - output->AddByte(AP_MSG_COMMAND_OK); + output->put(AP_MSG_COMMAND_OK); break; } @@ -357,9 +361,9 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) { addLogLine(LOGTYPE_EVENT, "shutting down server"); Dispatcher::getInstance().addTask(createTask(boost::bind( - &Game::setGameState, &g_game, GAME_STATE_SHUTDOWN))); + &Game::setGameState, &g_game, GAMESTATE_SHUTDOWN))); - output->AddByte(AP_MSG_COMMAND_OK); + output->put(AP_MSG_COMMAND_OK); break; } @@ -372,7 +376,7 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) case CMD_RELOAD_SCRIPTS: { - const int8_t reload = msg.GetByte(); + const int8_t reload = msg.get(); Dispatcher::getInstance().addTask(createTask(boost::bind( &ProtocolAdmin::adminCommandReload, this, reload))); break; @@ -380,23 +384,15 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) case CMD_KICK: { - const std::string param = msg.GetString(); + const std::string param = msg.getString(); Dispatcher::getInstance().addTask(createTask(boost::bind( &ProtocolAdmin::adminCommandKickPlayer, this, param))); break; } - case CMD_SETOWNER: - { - const std::string param = msg.GetString(); - Dispatcher::getInstance().addTask(createTask(boost::bind( - &ProtocolAdmin::adminCommandSetOwner, this, param))); - break; - } - case CMD_SEND_MAIL: { - const std::string xmlData = msg.GetString(); + const std::string xmlData = msg.getString(); Dispatcher::getInstance().addTask(createTask(boost::bind( &ProtocolAdmin::adminCommandSendMail, this, xmlData))); break; @@ -404,19 +400,19 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) case CMD_BROADCAST: { - const std::string param = msg.GetString(); + const std::string param = msg.getString(); addLogLine(LOGTYPE_EVENT, "broadcasting: " + param); Dispatcher::getInstance().addTask(createTask(boost::bind( &Game::broadcastMessage, &g_game, param, MSG_STATUS_WARNING))); - output->AddByte(AP_MSG_COMMAND_OK); + output->put(AP_MSG_COMMAND_OK); break; } default: { - output->AddByte(AP_MSG_COMMAND_FAILED); - output->AddString("not known server command"); + output->put(AP_MSG_COMMAND_FAILED); + output->putString("not known server command"); addLogLine(LOGTYPE_EVENT, "not known server command"); } } @@ -424,7 +420,7 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) } case AP_MSG_PING: - output->AddByte(AP_MSG_PING_OK); + output->put(AP_MSG_PING_OK); break; case AP_MSG_KEEP_ALIVE: @@ -432,36 +428,44 @@ void ProtocolAdmin::parsePacket(NetworkMessage& msg) default: { - output->AddByte(AP_MSG_ERROR); - output->AddString("not known command byte"); + output->put(AP_MSG_ERROR); + output->putString("not known command byte"); addLogLine(LOGTYPE_EVENT, "not known command byte"); break; } } - if(output->getMessageLength() > 0) + if(output->size() > 0) OutputMessagePool::getInstance()->send(output); } -void ProtocolAdmin::deleteProtocolTask() +void ProtocolAdmin::releaseProtocol() { addLogLine(LOGTYPE_EVENT, "end connection"); - g_admin->removeConnection(); + Admin::getInstance()->removeConnection(); + Protocol::releaseProtocol(); +} + +#ifdef __DEBUG_NET_DETAIL__ +void ProtocolAdmin::deleteProtocolTask() +{ + std::clog << "Deleting ProtocolAdmin" << std::endl; Protocol::deleteProtocolTask(); } +#endif void ProtocolAdmin::adminCommandPayHouses() { OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false); if(!output) return; - Houses::getInstance()->payHouses(); + Houses::getInstance()->check(); addLogLine(LOGTYPE_EVENT, "pay houses ok"); TRACK_MESSAGE(output); - output->AddByte(AP_MSG_COMMAND_OK); + output->put(AP_MSG_COMMAND_OK); OutputMessagePool::getInstance()->send(output); } @@ -475,7 +479,7 @@ void ProtocolAdmin::adminCommandReload(int8_t reload) addLogLine(LOGTYPE_EVENT, "reload ok"); TRACK_MESSAGE(output); - output->AddByte(AP_MSG_COMMAND_OK); + output->put(AP_MSG_COMMAND_OK); OutputMessagePool::getInstance()->send(output); } @@ -491,83 +495,13 @@ void ProtocolAdmin::adminCommandKickPlayer(const std::string& param) { Scheduler::getInstance().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, boost::bind(&Game::kickPlayer, &g_game, player->getID(), false))); addLogLine(LOGTYPE_EVENT, "kicking player " + player->getName()); - output->AddByte(AP_MSG_COMMAND_OK); + output->put(AP_MSG_COMMAND_OK); } else { addLogLine(LOGTYPE_EVENT, "failed setting kick for player " + param); - output->AddByte(AP_MSG_COMMAND_FAILED); - output->AddString("player is not online"); - } - - OutputMessagePool::getInstance()->send(output); -} - -void ProtocolAdmin::adminCommandSetOwner(const std::string& param) -{ - OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false); - if(!output) - return; - - StringVec params = explodeString(param, ","); - for(StringVec::iterator it = params.begin(); it != params.end(); ++it) - trimString(*it); - - TRACK_MESSAGE(output); - int32_t houseId = atoi(params[0].c_str()); - if(houseId > 0) - { - size_t size = params.size(); - if(size > 1) - { - std::string name = params[1]; - bool clean = true; - if(size > 2) - clean = booleanString(params[2]); - - if(House* house = Houses::getInstance()->getHouse(houseId)) - { - uint32_t guid; - if(IOLoginData::getInstance()->getGuidByName(guid, name)) - { - if(house->setOwnerEx(guid, clean)) - { - addLogLine(LOGTYPE_EVENT, "Set " + name + " as new owner of house with id " + house->getName()); - output->AddByte(AP_MSG_COMMAND_OK); - } - else - { - addLogLine(LOGTYPE_EVENT, "Failed setting " + name + " as new owner of house with id " + house->getName()); - output->AddByte(AP_MSG_COMMAND_FAILED); - output->AddString("failed while setting owner"); - } - } - else - { - addLogLine(LOGTYPE_EVENT, "Could not find player with name: " + name); - output->AddByte(AP_MSG_COMMAND_FAILED); - output->AddString("such player does not exists"); - } - } - else - { - addLogLine(LOGTYPE_EVENT, "Could not find house with id: " + houseId); - output->AddByte(AP_MSG_COMMAND_FAILED); - output->AddString("such house does not exists"); - } - } - else - { - addLogLine(LOGTYPE_EVENT, "Not enough params given, param data: " + param); - output->AddByte(AP_MSG_COMMAND_FAILED); - output->AddString("not enough params"); - } - } - else - { - addLogLine(LOGTYPE_EVENT, "Specified house id is not a valid one: " + params[0]); - output->AddByte(AP_MSG_COMMAND_FAILED); - output->AddString("invalid house id"); + output->put(AP_MSG_COMMAND_FAILED); + output->putString("player is not online"); } OutputMessagePool::getInstance()->send(output); @@ -588,154 +522,91 @@ void ProtocolAdmin::adminCommandSendMail(const std::string& xmlData) if(IOLoginData::getInstance()->playerMail(NULL, name, depotId, mailItem)) { addLogLine(LOGTYPE_EVENT, "sent mailbox to " + name); - output->AddByte(AP_MSG_COMMAND_OK); + output->put(AP_MSG_COMMAND_OK); } else { addLogLine(LOGTYPE_EVENT, "failed sending mailbox to " + name); - output->AddByte(AP_MSG_COMMAND_FAILED); - output->AddString("could not send the box"); + output->put(AP_MSG_COMMAND_FAILED); + output->putString("could not send the box"); } } else { addLogLine(LOGTYPE_EVENT, "failed parsing mailbox"); - output->AddByte(AP_MSG_COMMAND_FAILED); - output->AddString("could not parse the box"); + output->put(AP_MSG_COMMAND_FAILED); + output->putString("could not parse the box"); } OutputMessagePool::getInstance()->send(output); } -bool Admin::loadFromXml() +Admin::Admin(): m_currentConnections(0), m_encrypted(false), + m_key_RSA1024XTEA(NULL) { - xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "admin.xml").c_str()); - if(!doc) - { - std::cout << "[Warning - Admin::loadFromXml] Cannot load admin file." << std::endl; - std::cout << getLastXMLError() << std::endl; - return false; - } - - xmlNodePtr p, q, root = xmlDocGetRootElement(doc); - if(xmlStrcmp(root->name,(const xmlChar*)"otadmin")) - { - std::cout << "[Error - Admin::loadFromXml] Malformed admin file" << std::endl; - xmlFreeDoc(doc); - return false; - } - - std::string strValue; - if(readXMLString(root, "enabled", strValue)) - m_enabled = booleanString(strValue); - - int32_t intValue; - p = root->children; - while(p) + std::string strValue = g_config.getString(ConfigManager::ADMIN_ENCRYPTION); + if(!strValue.empty()) { - if(xmlStrEqual(p->name, (const xmlChar*)"security")) - { - if(readXMLString(p, "onlylocalhost", strValue)) - m_onlyLocalHost = booleanString(strValue); - if(readXMLInteger(p, "maxconnections", intValue) && intValue > 0) - m_maxConnections = intValue; - if(readXMLString(p, "loginrequired", strValue)) - m_requireLogin = booleanString(strValue); - if(readXMLString(p, "loginpassword", strValue)) - m_password = strValue; - else if(m_requireLogin) - std::cout << "[Warning - Admin::loadFromXml]: Login required, but no password specified - using default." << std::endl; - } - else if(xmlStrEqual(p->name, (const xmlChar*)"encryption")) + toLowerCaseString(strValue); + if(strValue == "rsa1024xtea") { - if(readXMLString(p, "required", strValue)) - m_requireEncryption = booleanString(strValue); - - q = p->children; - while(q) + m_key_RSA1024XTEA = new RSA(); + if(!m_key_RSA1024XTEA->initialize(getFilePath(FILE_TYPE_CONFIG, + g_config.getString(ConfigManager::ADMIN_ENCRYPTION_DATA)))) { - if(xmlStrEqual(q->name, (const xmlChar*)"key")) - { - if(readXMLString(q, "type", strValue)) - { - if(asLowerCaseString(strValue) == "rsa1024xtea") - { - if(readXMLString(q, "file", strValue)) - { - m_key_RSA1024XTEA = new RSA(); - if(!m_key_RSA1024XTEA->setKey(getFilePath(FILE_TYPE_XML, strValue))) - { - delete m_key_RSA1024XTEA; - m_key_RSA1024XTEA = NULL; - std::cout << "[Error - Admin::loadFromXml]: Could not load RSA key from file " << getFilePath(FILE_TYPE_XML, strValue) << std::endl; - } - } - else - std::cout << "[Error - Admin::loadFromXml]: Missing file for RSA1024XTEA key." << std::endl; - } - else - std::cout << "[Warning - Admin::loadFromXml]: " << strValue << " is not a valid key type." << std::endl; - } - } - - q = q->next; + std::clog << "[Warning - Admin::Admin] Unable to set RSA1024XTEA key!" << std::endl; + delete m_key_RSA1024XTEA; + m_key_RSA1024XTEA = NULL; } + else + m_encrypted = true; } - - p = p->next; } +} - xmlFreeDoc(doc); - return true; +Admin::~Admin() +{ + delete m_key_RSA1024XTEA; + m_key_RSA1024XTEA = NULL; } bool Admin::addConnection() { - if(m_currrentConnections >= m_maxConnections) + if(m_currentConnections >= g_config.getNumber(ConfigManager::ADMIN_CONNECTIONS_LIMIT)) return false; - m_currrentConnections++; + m_currentConnections++; return true; } void Admin::removeConnection() { - if(m_currrentConnections > 0) - m_currrentConnections--; + if(m_currentConnections > 0) + m_currentConnections--; } -uint16_t Admin::getProtocolPolicy() +uint16_t Admin::getPolicy() const { uint16_t policy = 0; - if(requireLogin()) + if(g_config.getBool(ConfigManager::ADMIN_REQUIRE_LOGIN)) policy |= REQUIRE_LOGIN; - if(requireEncryption()) + + if(m_encrypted) policy |= REQUIRE_ENCRYPTION; return policy; } -uint32_t Admin::getProtocolOptions() +uint32_t Admin::getOptions() const { uint32_t ret = 0; - if(requireEncryption() && m_key_RSA1024XTEA) - ret |= ENCRYPTION_RSA1024XTEA; - - return ret; -} - -RSA* Admin::getRSAKey(uint8_t type) -{ - switch(type) + if(m_encrypted) { - case ENCRYPTION_RSA1024XTEA: - return m_key_RSA1024XTEA; - - default: - break; + if(m_key_RSA1024XTEA) + ret |= ENCRYPTION_RSA1024XTEA; } - return NULL; + return ret; } Item* Admin::createMail(const std::string xmlData, std::string& name, uint32_t& depotId) @@ -795,32 +666,37 @@ Item* Admin::createMail(const std::string xmlData, std::string& name, uint32_t& return mailItem; } -bool Admin::allowIP(uint32_t ip) +bool Admin::allow(uint32_t ip) const { - if(!m_onlyLocalHost) + if(!g_config.getBool(ConfigManager::ADMIN_LOCALHOST_ONLY)) return !ConnectionManager::getInstance()->isDisabled(ip, 0xFE); if(ip == 0x0100007F) //127.0.0.1 return true; - if(g_config.getBool(ConfigManager::ADMIN_LOGS_ENABLED)) + if(g_config.getBool(ConfigManager::ADMIN_LOGS)) LOG_MESSAGE(LOGTYPE_EVENT, "forbidden connection try", "ADMIN " + convertIPAddress(ip)); return false; } -bool Admin::passwordMatch(const std::string& password) +RSA* Admin::getRSAKey(uint8_t type) { - //prevent empty password login - if(!m_password.length()) - return false; + switch(type) + { + case ENCRYPTION_RSA1024XTEA: + return m_key_RSA1024XTEA; + + default: + break; + } - return password == m_password; + return NULL; } void ProtocolAdmin::addLogLine(LogType_t type, std::string message) { - if(g_config.getBool(ConfigManager::ADMIN_LOGS_ENABLED)) + if(g_config.getBool(ConfigManager::ADMIN_LOGS)) LOG_MESSAGE(type, message, "ADMIN " + convertIPAddress(getIP())) } #endif diff --git a/admin.h b/admin.h index 21310b7..969c241 100644 --- a/admin.h +++ b/admin.h @@ -18,10 +18,10 @@ #ifndef __ADMIN__ #define __ADMIN__ #include "otsystem.h" -#ifdef __REMOTE_CONTROL__ +#ifdef __OTADMIN__ +#include "protocol.h" #include "textlogger.h" -#include "player.h" // -> server // command(1 byte) | size(2 bytes) | parameters(size bytes) @@ -109,79 +109,58 @@ enum CMD_OPEN_SERVER = 4, CMD_SHUTDOWN_SERVER = 5, CMD_RELOAD_SCRIPTS = 6, - //CMD_PLAYER_INFO = 7, - //CMD_GETONLINE = 8, CMD_KICK = 9, - //CMD_BAN_MANAGER = 10, - //CMD_SERVER_INFO = 11, - //CMD_GETHOUSE = 12, CMD_SAVE_SERVER = 13, CMD_SEND_MAIL = 14, - CMD_SHALLOW_SAVE_SERVER = 15, - CMD_SETOWNER = 16 + CMD_SHALLOW_SAVE_SERVER = 15 }; - enum { REQUIRE_LOGIN = 1, - REQUIRE_ENCRYPTION = 2, + REQUIRE_ENCRYPTION = 2 }; enum { - ENCRYPTION_RSA1024XTEA = 1, + ENCRYPTION_RSA1024XTEA = 1 }; class NetworkMessage; +class Player; class RSA; class Admin { public: - Admin() - { - m_enabled = m_onlyLocalHost = m_requireLogin = true; - m_requireEncryption = false; - m_currrentConnections = 0; - m_key_RSA1024XTEA = NULL; - m_maxConnections = 1; - m_password = ""; - } - - virtual ~Admin() + virtual ~Admin(); + static Admin* getInstance() { - delete m_key_RSA1024XTEA; + static Admin instance; + return &instance; } - bool loadFromXml(); - bool addConnection(); void removeConnection(); - uint16_t getProtocolPolicy(); - uint32_t getProtocolOptions(); - - RSA* getRSAKey(uint8_t type); + uint16_t getPolicy() const; + uint32_t getOptions() const; static Item* createMail(const std::string xmlData, std::string& name, uint32_t& depotId); - bool allowIP(uint32_t ip); - bool passwordMatch(const std::string& password); + bool allow(uint32_t ip) const; - bool enabled() const {return m_enabled;} - bool onlyLocalHost() const {return m_onlyLocalHost;} - bool requireLogin() const {return m_requireLogin;} - bool requireEncryption() const {return m_requireEncryption;} + bool isEncypted() const {return m_encrypted;} + RSA* getRSAKey(uint8_t type); protected: - int32_t m_maxConnections, m_currrentConnections; - bool m_enabled, m_onlyLocalHost, m_requireLogin, m_requireEncryption; + Admin(); + + int32_t m_currentConnections; + bool m_encrypted; - std::string m_password; RSA* m_key_RSA1024XTEA; }; - class ProtocolAdmin : public Protocol { public: @@ -212,24 +191,28 @@ class ProtocolAdmin : public Protocol static const char* protocolName() {return "admin protocol";} protected: - virtual void parsePacket(NetworkMessage& msg); - virtual void deleteProtocolTask(); - - void adminCommandPayHouses(); - void adminCommandReload(int8_t reload); - void adminCommandKickPlayer(const std::string& name); - void adminCommandSetOwner(const std::string& param); - void adminCommandSendMail(const std::string& xmlData); - enum ProtocolState_t { NO_CONNECTED, ENCRYPTION_NO_SET, ENCRYPTION_OK, NO_LOGGED_IN, - LOGGED_IN, + LOGGED_IN }; + virtual void parsePacket(NetworkMessage& msg); + virtual void releaseProtocol(); +#ifdef __DEBUG_NET_DETAIL__ + virtual void deleteProtocolTask(); +#endif + + // commands + void adminCommandPayHouses(); + void adminCommandReload(int8_t reload); + + void adminCommandKickPlayer(const std::string& name); + void adminCommandSendMail(const std::string& xmlData); + private: void addLogLine(LogType_t type, std::string message); diff --git a/allocator.cpp b/allocator.cpp index 2e484fc..f9ab328 100644 --- a/allocator.cpp +++ b/allocator.cpp @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// -#ifdef __OTSERV_ALLOCATOR__ #include "otpch.h" +#ifdef __OTSERV_ALLOCATOR__ #include "allocator.h" //normal new/delete @@ -45,6 +45,7 @@ void* operator new(size_t bytes, int32_t dummy) return malloc(bytes); } #ifdef _MSC_VER + void* operator new[](size_t bytes, int32_t dummy) { return malloc(bytes); diff --git a/allocator.h b/allocator.h index 9b35f94..988cdd1 100644 --- a/allocator.h +++ b/allocator.h @@ -84,9 +84,11 @@ void operator delete(void* p); void operator delete[](void* p); #ifdef _MSC_VER +void* operator new[](size_t bytes, int32_t dummy) void operator delete(void* p, int32_t dummy); void operator delete[](void* p, int32_t dummy); #endif + #ifdef __OTSERV_ALLOCATOR_STATS__ void allocatorStatsThread(void* a); #endif diff --git a/autogen.sh b/autogen.sh index a204979..389a305 100644 --- a/autogen.sh +++ b/autogen.sh @@ -1 +1 @@ -autoreconf -vfi \ No newline at end of file +autoreconf -vfi --warnings=none diff --git a/baseevents.cpp b/baseevents.cpp index a28b769..cfdf2ca 100644 --- a/baseevents.cpp +++ b/baseevents.cpp @@ -25,36 +25,34 @@ bool BaseEvents::loadFromXml() std::string scriptsName = getScriptBaseName(); if(m_loaded) { - std::cout << "[Error - BaseEvents::loadFromXml] " << scriptsName << " interface already loaded!" << std::endl; + std::clog << "[Error - BaseEvents::loadFromXml] " << scriptsName << " interface already loaded!" << std::endl; return false; } - if(!getInterface().loadDirectory(getFilePath(FILE_TYPE_OTHER, std::string(scriptsName + "/lib/")))) - std::cout << "[Warning - BaseEvents::loadFromXml] Cannot load " << scriptsName << "/lib/" << std::endl; + std::string path = getFilePath(FILE_TYPE_OTHER, std::string(scriptsName + "/lib/")); + if(!getInterface().loadDirectory(path, false, true)) + std::clog << "[Warning - BaseEvents::loadFromXml] Cannot load " << path << std::endl; - xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_OTHER, std::string(scriptsName + "/" + scriptsName + ".xml")).c_str()); + path = getFilePath(FILE_TYPE_OTHER, std::string(scriptsName + "/" + scriptsName + ".xml")); + xmlDocPtr doc = xmlParseFile(path.c_str()); if(!doc) { - std::cout << "[Warning - BaseEvents::loadFromXml] Cannot open " << scriptsName << ".xml file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - BaseEvents::loadFromXml] Cannot open " << path << " file." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr p, root = xmlDocGetRootElement(doc); - if(xmlStrcmp(root->name,(const xmlChar*)scriptsName.c_str())) + xmlNodePtr root = xmlDocGetRootElement(doc); + if(xmlStrcmp(root->name, (const xmlChar*)scriptsName.c_str())) { - std::cout << "[Error - BaseEvents::loadFromXml] Malformed " << scriptsName << ".xml file." << std::endl; + std::clog << "[Error - BaseEvents::loadFromXml] Malformed " << path << " file." << std::endl; xmlFreeDoc(doc); return false; } - std::string scriptsPath = getFilePath(FILE_TYPE_OTHER, std::string(scriptsName + "/scripts/")); - p = root->children; - while(p) - { - parseEventNode(p, scriptsPath, false); - p = p->next; - } + path = getFilePath(FILE_TYPE_OTHER, std::string(scriptsName + "/scripts/")); + for(xmlNodePtr p = root->children; p; p = p->next) + parseEventNode(p, path, false); xmlFreeDoc(doc); m_loaded = true; @@ -69,72 +67,93 @@ bool BaseEvents::parseEventNode(xmlNodePtr p, std::string scriptsPath, bool over if(!event->configureEvent(p)) { - std::cout << "[Warning - BaseEvents::loadFromXml] Cannot configure an event" << std::endl; + std::clog << "[Warning - BaseEvents::loadFromXml] Cannot configure an event" << std::endl; delete event; - event = NULL; return false; } - bool success = false; + bool success = true, skip = false; std::string strValue, tmpStrValue; if(readXMLString(p, "event", strValue)) { + skip = true; tmpStrValue = asLowerCaseString(strValue); if(tmpStrValue == "script") { bool file = readXMLString(p, "value", strValue); if(!file) - parseXMLContentString(p->children, strValue); + success = parseXMLContentString(p->children, strValue); else strValue = scriptsPath + strValue; - success = event->checkScript(strValue, file) && event->loadScript(strValue, file); + if(success) + success = event->checkScript(getScriptBaseName(), strValue, file) && event->loadScript(strValue, file); } else if(tmpStrValue == "buffer") { if(!readXMLString(p, "value", strValue)) - parseXMLContentString(p->children, strValue); + success = parseXMLContentString(p->children, strValue); - success = event->checkBuffer(strValue) && event->loadBuffer(strValue); + if(success) + success = event->checkBuffer(getScriptBaseName(), strValue) && event->loadBuffer(strValue); } else if(tmpStrValue == "function") { if(readXMLString(p, "value", strValue)) success = event->loadFunction(strValue); + else + success = false; } + else + skip = false; } - else if(readXMLString(p, "script", strValue)) + + if(!skip) { - bool file = asLowerCaseString(strValue) != "cdata"; - if(!file) - parseXMLContentString(p->children, strValue); - else - strValue = scriptsPath + strValue; + if(readXMLString(p, "script", strValue)) + { + bool file = asLowerCaseString(strValue) != "cdata"; + if(!file) + success = parseXMLContentString(p->children, strValue); + else + strValue = scriptsPath + strValue; - success = event->checkScript(strValue, file) && event->loadScript(strValue, file); + if(success) + success = event->checkScript(getScriptBaseName(), strValue, file) && event->loadScript(strValue, file); + } + else if(readXMLString(p, "buffer", strValue)) + { + if(asLowerCaseString(strValue) == "cdata") + success = parseXMLContentString(p->children, strValue); + + if(success) + success = event->checkBuffer(getScriptBaseName(), strValue) && event->loadBuffer(strValue); + } + else if((readXMLString(p, "function", strValue) && event->loadFunction(strValue)) + || (parseXMLContentString(p->children, strValue) && event->checkBuffer( + getScriptBaseName(), strValue) && event->loadBuffer(strValue))) + success = true; + else + success = false; } - else if(readXMLString(p, "buffer", strValue)) - { - if(asLowerCaseString(strValue) == "cdata") - parseXMLContentString(p->children, strValue); - success = event->checkBuffer(strValue) && event->loadBuffer(strValue); + if(!success) + { + delete event; + return false; } - else if(readXMLString(p, "function", strValue)) - success = event->loadFunction(strValue); - else if(parseXMLContentString(p->children, strValue) && event->checkBuffer(strValue)) - success = event->loadBuffer(strValue); if(!override && readXMLString(p, "override", strValue) && booleanString(strValue)) override = true; - if(success && !registerEvent(event, p, override) && event) - { - delete event; - event = NULL; - } + if(registerEvent(event, p, override)) + return true; - return success; + if(!event) + return false; + + delete event; + return false; } bool BaseEvents::reload() @@ -149,32 +168,83 @@ Event::Event(const Event* copy) m_interface = copy->m_interface; m_scripted = copy->m_scripted; m_scriptId = copy->m_scriptId; + + /*std::string* oldScript = m_scriptData; + This needs a reference system + to safely free the string + if its used in other events*/ m_scriptData = copy->m_scriptData; + /*if(oldScript) + delete oldScript;*/ +} + +Event::~Event() +{ + /*if(m_scriptData) + delete m_scriptData;*/ } bool Event::loadBuffer(const std::string& buffer) { - if(!m_interface || m_scriptData != "") + if(!m_interface) + { + std::clog << "[Error - Event::loadBuffer] m_interface = NULL" << std::endl; + return false; + } + + if(m_scriptData) { - std::cout << "[Error - Event::loadScriptFile] m_interface == NULL, scriptData != \"\"" << std::endl; + std::clog << "[Error - Event::loadBuffer] m_scriptData != NULL" << std::endl; return false; } m_scripted = EVENT_SCRIPT_BUFFER; - m_scriptData = buffer; + m_scriptData = new std::string(buffer); return true; } -bool Event::checkBuffer(const std::string& buffer) +bool Event::checkBuffer(const std::string& base, const std::string& script) const { - return true; //TODO + LuaInterface testInterface("Test Interface"); + testInterface.initState(); + + std::string path = getFilePath(FILE_TYPE_OTHER, std::string(base + "/lib/")); + if(!testInterface.loadDirectory(path, false, true)) + std::clog << "[Warning - Event::checkBuffer] Cannot load " << path << std::endl; + + if(m_scriptData) + { + std::clog << "[Error - Event::checkBuffer] m_scriptData != NULL" << std::endl; + return false; + } + + //TODO: is it really the way we should do it?... + std::string buffer = script; + trimString(buffer); + + std::stringstream scriptstream; + scriptstream << "function " << getScriptEventName() << "(" << getScriptEventParams() << ")" << std::endl << buffer << std::endl << "end"; + + buffer = scriptstream.str(); + if(testInterface.loadBuffer(buffer)) + return true; + + std::clog << "[Error - Event::checkBuffer] Cannot load buffer (" << script << ")" + << std::endl << testInterface.getLastError() << std::endl; + return false; } bool Event::loadScript(const std::string& script, bool file) { - if(!m_interface || m_scriptId != 0) + if(!m_interface) { - std::cout << "[Error - Event::loadScript] m_interface == NULL, scriptId = " << m_scriptId << std::endl; + std::clog << "[Error - Event::loadScript] m_interface = NULL" << std::endl; + return false; + } + + if(m_scriptId) + { + std::clog << "[Error - Event::loadScript] scriptId = " << m_scriptId << std::endl; return false; } @@ -197,15 +267,15 @@ bool Event::loadScript(const std::string& script, bool file) if(!result) { - std::cout << "[Warning - Event::loadScript] Cannot load script (" << script << ")" << std::endl; - std::cout << m_interface->getLastError() << std::endl; + std::clog << "[Warning - Event::loadScript] Cannot load script (" << script << ")" + << std::endl << m_interface->getLastError() << std::endl; return false; } int32_t id = m_interface->getEvent(getScriptEventName()); if(id == -1) { - std::cout << "[Warning - Event::loadScript] Event " << getScriptEventName() << " not found (" << script << ")" << std::endl; + std::clog << "[Warning - Event::loadScript] Event " << getScriptEventName() << " not found (" << script << ")" << std::endl; return false; } @@ -214,9 +284,50 @@ bool Event::loadScript(const std::string& script, bool file) return true; } -bool Event::checkScript(const std::string& script, bool file) +bool Event::checkScript(const std::string& base, const std::string& script, bool file) const { - return true; //TODO + LuaInterface testInterface("Test Interface"); + testInterface.initState(); + + std::string path = getFilePath(FILE_TYPE_OTHER, std::string(base + "/lib/")); + if(!testInterface.loadDirectory(path, false, true)) + std::clog << "[Warning - Event::checkScript] Cannot load " << path << std::endl; + + if(m_scriptId) + { + std::clog << "[Error - Event::checkScript] scriptId = " << m_scriptId << std::endl; + return false; + } + + bool result = false; + if(!file) + { + std::string buffer = script, function = "function " + getScriptEventName(); + trimString(buffer); + if(buffer.find(function) == std::string::npos) + { + std::stringstream scriptstream; + scriptstream << function << "(" << getScriptEventParams() << ")" << std::endl << buffer << std::endl << "end"; + buffer = scriptstream.str(); + } + + result = testInterface.loadBuffer(buffer); + } + else + result = testInterface.loadFile(script); + + if(!result) + { + std::clog << "[Error - Event::checkScript] Cannot load script (" << script << ")" + << std::endl << testInterface.getLastError() << std::endl; + return false; + } + + if(testInterface.getEvent(getScriptEventName()) != -1) + return true; + + std::clog << "[Error - Event::checkScript] Event " << getScriptEventName() << " not found (" << script << ")" << std::endl; + return false; } CallBack::CallBack() @@ -226,11 +337,11 @@ CallBack::CallBack() m_loaded = false; } -bool CallBack::loadCallBack(LuaScriptInterface* _interface, std::string name) +bool CallBack::loadCallBack(LuaInterface* _interface, std::string name) { if(!_interface) { - std::cout << "Failure: [CallBack::loadCallBack] m_interface == NULL" << std::endl; + std::clog << "[Error - CallBack::loadCallBack] m_interface = NULL" << std::endl; return false; } @@ -238,12 +349,13 @@ bool CallBack::loadCallBack(LuaScriptInterface* _interface, std::string name) int32_t id = m_interface->getEvent(name); if(id == -1) { - std::cout << "Warning: [CallBack::loadCallBack] Event " << name << " not found." << std::endl; + std::clog << "[Warning - CallBack::loadCallBack] Event " << name << " not found." << std::endl; return false; } m_callbackName = name; m_scriptId = id; + m_loaded = true; return true; } diff --git a/baseevents.h b/baseevents.h index 64f203e..3ebc86e 100644 --- a/baseevents.h +++ b/baseevents.h @@ -42,7 +42,7 @@ class BaseEvents virtual bool registerEvent(Event* event, xmlNodePtr p, bool override) = 0; virtual Event* getEvent(const std::string& nodeName) = 0; - virtual LuaScriptInterface& getInterface() = 0; + virtual LuaInterface& getInterface() = 0; bool m_loaded; }; @@ -57,31 +57,31 @@ enum EventScript_t class Event { public: - Event(LuaScriptInterface* _interface): m_interface(_interface), - m_scripted(EVENT_SCRIPT_FALSE), m_scriptId(0) {} + Event(LuaInterface* _interface): m_interface(_interface), + m_scripted(EVENT_SCRIPT_FALSE), m_scriptId(0), m_scriptData(NULL) {} Event(const Event* copy); - virtual ~Event() {} + virtual ~Event(); virtual bool configureEvent(xmlNodePtr p) = 0; virtual bool isScripted() const {return m_scripted != EVENT_SCRIPT_FALSE;} bool loadBuffer(const std::string& buffer); - bool checkBuffer(const std::string& buffer); + bool checkBuffer(const std::string& base, const std::string& buffer) const; bool loadScript(const std::string& script, bool file); - bool checkScript(const std::string& script, bool file); + bool checkScript(const std::string& base, const std::string& script, bool file) const; - virtual bool loadFunction(const std::string& functionName) {return false;} + virtual bool loadFunction(const std::string&) {return false;} protected: virtual std::string getScriptEventName() const = 0; virtual std::string getScriptEventParams() const = 0; - LuaScriptInterface* m_interface; + LuaInterface* m_interface; EventScript_t m_scripted; int32_t m_scriptId; - std::string m_scriptData; + std::string* m_scriptData; }; class CallBack @@ -90,11 +90,11 @@ class CallBack CallBack(); virtual ~CallBack() {} - bool loadCallBack(LuaScriptInterface* _interface, std::string name); + bool loadCallBack(LuaInterface* _interface, std::string name); protected: int32_t m_scriptId; - LuaScriptInterface* m_interface; + LuaInterface* m_interface; bool m_loaded; std::string m_callbackName; diff --git a/beds.cpp b/beds.cpp index 8f33d2c..85dd80d 100644 --- a/beds.cpp +++ b/beds.cpp @@ -35,7 +35,7 @@ Attr_ReadValue BedItem::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_SLEEPERGUID: { uint32_t _sleeper; - if(!propStream.GET_ULONG(_sleeper)) + if(!propStream.getLong(_sleeper)) return ATTR_READ_ERROR; if(_sleeper) @@ -55,7 +55,7 @@ Attr_ReadValue BedItem::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_SLEEPSTART: { uint32_t sleepStart; - if(!propStream.GET_ULONG(sleepStart)) + if(!propStream.getLong(sleepStart)) return ATTR_READ_ERROR; setAttribute("sleepstart", (int32_t)sleepStart); @@ -75,8 +75,8 @@ bool BedItem::serializeAttr(PropWriteStream& propWriteStream) const if(!sleeper) return ret; - propWriteStream.ADD_UCHAR(ATTR_SLEEPERGUID); - propWriteStream.ADD_ULONG(sleeper); + propWriteStream.addByte(ATTR_SLEEPERGUID); + propWriteStream.addLong(sleeper); return ret; } @@ -130,7 +130,7 @@ void BedItem::sleep(Player* player) g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_SLEEP); Scheduler::getInstance().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, boost::bind(&Game::kickPlayer, &g_game, player->getID(), false))); } - else if(Item::items[getID()].transformToFree) + else if(Item::items[getID()].transformUseTo) { wakeUp(); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); @@ -201,17 +201,17 @@ void BedItem::updateAppearance(const Player* player) if(it.type != ITEM_TYPE_BED) return; - if(player && it.transformUseTo[player->getSex(false)]) + if(player && it.transformBed[player->getSex(false)]) { - const ItemType& newType = Item::items[it.transformUseTo[player->getSex(false)]]; + const ItemType& newType = Item::items[it.transformBed[player->getSex(false)]]; if(newType.type == ITEM_TYPE_BED) - g_game.transformItem(this, it.transformUseTo[player->getSex(false)]); + g_game.transformItem(this, it.transformBed[player->getSex(false)]); } - else if(it.transformToFree) + else if(it.transformUseTo) { - const ItemType& newType = Item::items[it.transformToFree]; + const ItemType& newType = Item::items[it.transformUseTo]; if(newType.type == ITEM_TYPE_BED) - g_game.transformItem(this, it.transformToFree); + g_game.transformItem(this, it.transformUseTo); } } diff --git a/beds.h b/beds.h index 15a3821..09a0c74 100644 --- a/beds.h +++ b/beds.h @@ -34,7 +34,7 @@ class BedItem : public Item virtual Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream); virtual bool serializeAttr(PropWriteStream& propWriteStream) const; - virtual bool canRemove() const {return house;} + virtual bool canRemove() const {return house != NULL;} uint32_t getSleeper() const {return sleeper;} void setSleeper(uint32_t guid) {sleeper = guid;} diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..bd782c2 --- /dev/null +++ b/build.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# CCache + multicore compilation script +# For "The Forgotten Server" + +# Example: +# # make clean +# # time ./build.sh + +# Gives: +# real 3m27.070s +# user 6m4.066s +# sys 0m16.659s + +# CCACHE recompile (from scratch): +# # make clean +# # time ./build.sh + +# Gives: +# real 0m27.620s +# user 0m43.744s +# sys 0m4.766s + +# 1/7 of the original compile time! +# When more you do it, more ccache will cache, default is limited to use 1GB storage + +echo "The Forgotten Server build script - " + + +# Enable CCache +if test -x `which ccache`; then + echo "Using ccache" + if [ -f /usr/lib/ccache/bin ]; then + export PATH=/usr/lib/ccache/bin/:$PATH + echo "CCache binaries located in /usr/lib/ccache/bin" + else + export PATH=/usr/lib/ccache/:$PATH + echo "CCache binaries located in /usr/lib/ccache" + fi +else + echo "Warning: NOT using ccache, increase build time" +fi + +# Get number cores +CORES=`grep processor /proc/cpuinfo | wc -l` +# Set make processes - 1 + number of cores +MAKEOPT=$(($CORES + 1)) +echo "" +echo "Start building on $CORES cores, using $MAKEOPT processes" +echo "" +make V=0 -j $MAKEOPT diff --git a/cb/TheForgottenServer.cbp b/cb/TheForgottenServer.cbp new file mode 100644 index 0000000..e5f6580 --- /dev/null +++ b/cb/TheForgottenServer.cbp @@ -0,0 +1,254 @@ + + + + + + diff --git a/TheForgottenServer.ico b/cb/TheForgottenServer.ico similarity index 100% rename from TheForgottenServer.ico rename to cb/TheForgottenServer.ico diff --git a/cb/TheForgottenServer.layout b/cb/TheForgottenServer.layout new file mode 100644 index 0000000..2aead87 --- /dev/null +++ b/cb/TheForgottenServer.layout @@ -0,0 +1,4 @@ + + + + diff --git a/cb/TheForgottenServer_private.rc b/cb/TheForgottenServer_private.rc new file mode 100644 index 0000000..46be778 --- /dev/null +++ b/cb/TheForgottenServer_private.rc @@ -0,0 +1,29 @@ +#include + +A ICON MOVEABLE PURE LOADONCALL DISCARDABLE "TheForgottenServer.ico" + +1 VERSIONINFO +FILEVERSION 0,4,0,0 +PRODUCTVERSION 0,4,0,0 +FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E2" + { + VALUE "CompanyName", "OtLand.net" + VALUE "FileVersion", "0.4" + VALUE "FileDescription", "The Forgotten Server" + VALUE "InternalName", "" + VALUE "LegalCopyright", "" + VALUE "LegalTrademarks", "" + VALUE "OriginalFilename", "The Forgotten Server.exe" + VALUE "ProductName", "The Forgotten Server" + VALUE "ProductVersion", "0.4" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 1250 + } +} \ No newline at end of file diff --git a/chat.cpp b/chat.cpp index 36c7271..1f86d25 100644 --- a/chat.cpp +++ b/chat.cpp @@ -19,6 +19,7 @@ #include "player.h" #include "iologindata.h" +#include "manager.h" #include "configmanager.h" #include "game.h" @@ -34,7 +35,7 @@ PrivateChatChannel::PrivateChatChannel(uint16_t id, std::string name, uint16_t f bool PrivateChatChannel::isInvited(const Player* player) { - if(player->getGUID() == getOwner()) + if(player->getGUID() == m_owner) return true; return std::find(m_invites.begin(), m_invites.end(), player->getGUID()) != m_invites.end(); @@ -71,6 +72,13 @@ void PrivateChatChannel::invitePlayer(Player* player, Player* invitePlayer) msg.str(""); msg << invitePlayer->getName() << " has been invited."; player->sendTextMessage(MSG_INFO_DESCR, msg.str().c_str()); + + Player* tmpPlayer = NULL; + for(UsersMap::iterator cit = m_users.begin(); cit != m_users.end(); ++cit) + { + if((tmpPlayer = cit->second->getPlayer())) + tmpPlayer->sendChannelEvent(m_id, invitePlayer->getName(), CHANNELEVENT_INVITE); + } } void PrivateChatChannel::excludePlayer(Player* player, Player* excludePlayer) @@ -82,8 +90,15 @@ void PrivateChatChannel::excludePlayer(Player* player, Player* excludePlayer) msg += " has been excluded."; player->sendTextMessage(MSG_INFO_DESCR, msg.c_str()); - removeUser(excludePlayer); + removeUser(excludePlayer, true); excludePlayer->sendClosePrivate(getId()); + + Player* tmpPlayer = NULL; + for(UsersMap::iterator cit = m_users.begin(); cit != m_users.end(); ++cit) + { + if((tmpPlayer = cit->second->getPlayer())) + tmpPlayer->sendChannelEvent(m_id, excludePlayer->getName(), CHANNELEVENT_EXCLUDE); + } } void PrivateChatChannel::closeChannel() @@ -110,41 +125,69 @@ ChatChannel::ChatChannel(uint16_t id, const std::string& name, uint16_t flags, u bool ChatChannel::addUser(Player* player) { - if(m_users.find(player->getID()) != m_users.end()) + if(!player) return false; + if(m_users.find(player->getID()) != m_users.end()) + return true; + ChatChannel* channel = g_chat.getChannel(player, m_id); if(!channel) { #ifdef __DEBUG_CHAT__ - std::cout << "ChatChannel::addUser - failed retrieving channel." << std::endl; + std::clog << "ChatChannel::addUser - failed retrieving channel." << std::endl; #endif return false; } + if(m_id == CHANNEL_PARTY || m_id == CHANNEL_GUILD || m_id == CHANNEL_PRIVATE) + { + Player* tmpPlayer = NULL; + for(UsersMap::iterator cit = m_users.begin(); cit != m_users.end(); ++cit) + { + if((tmpPlayer = cit->second->getPlayer()) && (!tmpPlayer->isGhost() || player->getAccess() >= tmpPlayer->getAccess())) + tmpPlayer->sendChannelEvent(m_id, player->getName(), CHANNELEVENT_JOIN); + } + } + m_users[player->getID()] = player; CreatureEventList joinEvents = player->getCreatureEvents(CREATURE_EVENT_CHANNEL_JOIN); for(CreatureEventList::iterator it = joinEvents.begin(); it != joinEvents.end(); ++it) - (*it)->executeChannelJoin(player, m_id, m_users); + (*it)->executeChannel(player, m_id, m_users); + Manager::getInstance()->addUser(player->getID(), m_id); return true; } -bool ChatChannel::removeUser(Player* player) +bool ChatChannel::removeUser(Player* player, bool exclude/* = false*/) { + if(!player) + return false; + UsersMap::iterator it = m_users.find(player->getID()); if(it == m_users.end()) - return false; + return true; m_users.erase(it); CreatureEventList leaveEvents = player->getCreatureEvents(CREATURE_EVENT_CHANNEL_LEAVE); for(CreatureEventList::iterator it = leaveEvents.begin(); it != leaveEvents.end(); ++it) - (*it)->executeChannelLeave(player, m_id, m_users); + (*it)->executeChannel(player, m_id, m_users); + if((m_id == CHANNEL_PARTY || m_id == CHANNEL_GUILD || m_id == CHANNEL_PRIVATE) && !exclude) + { + Player* tmpPlayer = NULL; + for(UsersMap::iterator cit = m_users.begin(); cit != m_users.end(); ++cit) + { + if((tmpPlayer = cit->second->getPlayer()) && (!tmpPlayer->isGhost() || player->getAccess() >= tmpPlayer->getAccess())) + tmpPlayer->sendChannelEvent(m_id, player->getName(), CHANNELEVENT_LEAVE); + } + } + + Manager::getInstance()->removeUser(player->getID(), m_id); return true; } -bool ChatChannel::talk(Player* player, SpeakClasses type, const std::string& text, uint32_t _time/* = 0*/) +bool ChatChannel::talk(Player* player, MessageClasses type, const std::string& text, uint32_t statementId) { UsersMap::iterator it = m_users.find(player->getID()); if(it == m_users.end()) @@ -157,7 +200,7 @@ bool ChatChannel::talk(Player* player, SpeakClasses type, const std::string& tex } for(it = m_users.begin(); it != m_users.end(); ++it) - it->second->sendToChannel(player, type, text, m_id, _time); + it->second->sendCreatureChannelSay(player, type, text, m_id, statementId); if(hasFlag(CHANNELFLAG_LOGGED) && m_file->is_open()) *m_file << "[" << formatDate() << "] " << player->getName() << ": " << text << std::endl; @@ -165,6 +208,17 @@ bool ChatChannel::talk(Player* player, SpeakClasses type, const std::string& tex return true; } +bool ChatChannel::talk(std::string nick, MessageClasses type, const std::string& text) +{ + for(UsersMap::iterator it = m_users.begin(); it != m_users.end(); ++it) + it->second->sendChannelMessage(nick, text, type, m_id); + + if(hasFlag(CHANNELFLAG_LOGGED) && m_file->is_open()) + *m_file << "[" << formatDate() << "] " << nick << ": " << text << std::endl; + + return true; +} + Chat::~Chat() { for(GuildChannelMap::iterator it = m_guildChannels.begin(); it != m_guildChannels.end(); ++it) @@ -202,20 +256,20 @@ bool Chat::loadFromXml() xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "channels.xml").c_str()); if(!doc) { - std::cout << "[Warning - Chat::loadFromXml] Cannot load channels file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Chat::loadFromXml] Cannot load channels file." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr p, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"channels")) { - std::cout << "[Error - Chat::loadFromXml] Malformed channels file" << std::endl; + std::clog << "[Error - Chat::loadFromXml] Malformed channels file" << std::endl; xmlFreeDoc(doc); return false; } - for(p = root->children; p; p = p->next) + for(xmlNodePtr p = root->children; p; p = p->next) parseChannelNode(p); xmlFreeDoc(doc); @@ -230,7 +284,7 @@ bool Chat::parseChannelNode(xmlNodePtr p) if(!readXMLInteger(p, "id", intValue) || intValue <= CHANNEL_GUILD) { - std::cout << "[Warning - Chat::loadFromXml] Invalid or not specified channel id." << std::endl; + std::clog << "[Warning - Chat::loadFromXml] Invalid or not specified channel id." << std::endl; return false; } @@ -238,13 +292,13 @@ bool Chat::parseChannelNode(xmlNodePtr p) std::string strValue; if(m_normalChannels.find(id) != m_normalChannels.end() && (!readXMLString(p, "override", strValue) || !booleanString(strValue))) { - std::cout << "[Warning - Chat::loadFromXml] Duplicated channel with id: " << id << "." << std::endl; + std::clog << "[Warning - Chat::loadFromXml] Duplicated channel with id: " << id << "." << std::endl; return false; } if(!readXMLString(p, "name", strValue)) { - std::cout << "[Warning - Chat::loadFromXml] Missing name for channel with id: " << id << "." << std::endl; + std::clog << "[Warning - Chat::loadFromXml] Missing name for channel with id: " << id << "." << std::endl; return false; } @@ -273,21 +327,19 @@ bool Chat::parseChannelNode(xmlNodePtr p) Condition* condition = NULL; if(readXMLInteger(p, "muted", intValue)) { - conditionId = 2; + conditionId = 3; int32_t tmp = intValue * 1000; if(readXMLInteger(p, "conditionId", intValue)) { conditionId = intValue; - if(conditionId < 2) - std::cout << "[Warning - Chat::parseChannelNode] Using reserved muted condition sub id (" << conditionId << ")" << std::endl; + if(conditionId < 3) + std::clog << "[Warning - Chat::parseChannelNode] Using reserved muted condition sub id (" << conditionId << ")" << std::endl; } - if((condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, tmp, 0, false, conditionId))) - { - if(readXMLString(p, "conditionMessage", strValue)) - conditionMessage = strValue; - } - else + if(readXMLString(p, "conditionMessage", strValue)) + conditionMessage = strValue; + + if(tmp && !(condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, tmp, 0, false, conditionId))) conditionId = -1; } @@ -298,7 +350,7 @@ bool Chat::parseChannelNode(xmlNodePtr p) for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { if(!parseVocationNode(tmpNode, vocMap, vocStringVec, error)) - std::cout << "[Warning - Chat::loadFromXml] " << error << std::endl; + std::clog << "[Warning - Chat::loadFromXml] " << error << std::endl; } VocationMap* vocationMap = NULL; @@ -445,6 +497,36 @@ ChatChannel* Chat::addUserToChannel(Player* player, uint16_t channelId) return NULL; } +void Chat::reOpenChannels(Player* player) +{ + if(!player || player->isRemoved()) + return; + + for(NormalChannelMap::iterator it = m_normalChannels.begin(); it != m_normalChannels.end(); ++it) + { + if(it->second->hasUser(player)) + player->sendChannel(it->second->getId(), it->second->getName()); + } + + for(PartyChannelMap::iterator it = m_partyChannels.begin(); it != m_partyChannels.end(); ++it) + { + if(it->second->hasUser(player)) + player->sendChannel(it->second->getId(), it->second->getName()); + } + + for(GuildChannelMap::iterator it = m_guildChannels.begin(); it != m_guildChannels.end(); ++it) + { + if(it->second->hasUser(player)) + player->sendChannel(it->second->getId(), it->second->getName()); + } + + for(PrivateChannelMap::iterator it = m_privateChannels.begin(); it != m_privateChannels.end(); ++it) + { + if(it->second->hasUser(player)) + player->sendChannel(it->second->getId(), it->second->getName()); + } +} + bool Chat::removeUserFromChannel(Player* player, uint16_t channelId) { ChatChannel* channel = getChannel(player, channelId); @@ -457,9 +539,9 @@ bool Chat::removeUserFromChannel(Player* player, uint16_t channelId) return true; } -void Chat::removeUserFromAllChannels(Player* player) +void Chat::removeUserFromChannels(Player* player) { - if(!player) + if(!player || player->isRemoved()) return; for(NormalChannelMap::iterator it = m_normalChannels.begin(); it != m_normalChannels.end(); ++it) @@ -474,12 +556,13 @@ void Chat::removeUserFromAllChannels(Player* player) for(PrivateChannelMap::iterator it = m_privateChannels.begin(); it != m_privateChannels.end(); ++it) { it->second->removeUser(player); - if(player && it->second->getOwner() == player->getGUID()) + if(it->second->getOwner() == player->getGUID()) deleteChannel(player, it->second->getId()); } } -bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& text, uint16_t channelId) +bool Chat::talk(Player* player, MessageClasses type, const std::string& text, uint16_t channelId, + uint32_t statementId, bool anonymous/* = false*/) { if(text.empty()) return false; @@ -511,6 +594,9 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t } } + if(isPublicChannel(channelId)) + Manager::getInstance()->talk(player->getID(), channelId, type, text); + if(channelId != CHANNEL_GUILD || !g_config.getBool(ConfigManager::INGAME_GUILD_MANAGEMENT) || (text[0] != '!' && text[0] != '/')) { @@ -519,17 +605,21 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t switch(player->getGuildLevel()) { case GUILDLEVEL_VICE: - return channel->talk(player, SPEAK_CHANNEL_O, text); + return channel->talk(player, MSG_CHANNEL_HIGHLIGHT, text, statementId); case GUILDLEVEL_LEADER: - return channel->talk(player, SPEAK_CHANNEL_RN, text); + return channel->talk(player, MSG_GAMEMASTER_CHANNEL, text, statementId); default: break; } } - return channel->talk(player, type, text); + if(anonymous) + return channel->talk("", type, text); + + return channel->talk(player, type, text, statementId); } + /* TODO: move me to talkactions, please! */ if(!player->getGuildId()) { player->sendCancel("You are not in a guild."); @@ -547,9 +637,8 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { if(player->getGuildLevel() == GUILDLEVEL_LEADER) { - uint32_t guildId = player->getGuildId(); - channel->talk(player, SPEAK_CHANNEL_W, "The guild has been disbanded."); - IOGuild::getInstance()->disbandGuild(guildId); + IOGuild::getInstance()->disbandGuild(player->getGuildId()); + channel->talk("", MSG_GAMEMASTER_CHANNEL, "The guild has been disbanded."); } else player->sendCancel("You are not the leader of your guild."); @@ -562,6 +651,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { std::string param = text.substr(8); trimString(param); + Player* paramPlayer = NULL; if(g_game.getPlayerByNameWildcard(param, paramPlayer) == RET_NOERROR) { @@ -570,10 +660,11 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t if(!paramPlayer->isGuildInvited(player->getGuildId())) { sprintf(buffer, "%s has invited you to join the guild, %s. You may join this guild by writing: !joinguild %s", player->getName().c_str(), player->getGuildName().c_str(), player->getGuildName().c_str()); - paramPlayer->sendTextMessage(MSG_INFO_DESCR, buffer); + paramPlayer->sendTextMessage(MSG_EVENT_GUILD, buffer); + sprintf(buffer, "%s has invited %s to the guild.", player->getName().c_str(), paramPlayer->getName().c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); - paramPlayer->invitedToGuildsList.push_back(player->getGuildId()); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); + paramPlayer->invitationsList.push_back(player->getGuildId()); } else player->sendCancel("A player with that name has already been invited to your guild."); @@ -593,7 +684,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { IOGuild::getInstance()->invitePlayer(player->getGuildId(), guid); sprintf(buffer, "%s has invited %s to the guild.", player->getName().c_str(), param.c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); } else player->sendCancel("Your guild does not exist anymore."); @@ -617,9 +708,14 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { if(player->getGuildLevel() < GUILDLEVEL_LEADER) { - sprintf(buffer, "%s has left the guild.", player->getName().c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); - player->leaveGuild(); + if(!player->hasEnemy()) + { + sprintf(buffer, "%s has left the guild.", player->getName().c_str()); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); + player->leaveGuild(); + } + else + player->sendCancel("Your guild is currently at war, you cannot leave it right now."); } else player->sendCancel("You cannot leave your guild because you are the leader of it, you have to pass the leadership to another member of your guild or disband the guild."); @@ -632,19 +728,22 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { std::string param = text.substr(8); trimString(param); + Player* paramPlayer = NULL; if(g_game.getPlayerByNameWildcard(param, paramPlayer) == RET_NOERROR) { if(paramPlayer->getGuildId() == 0) { - InvitedToGuildsList::iterator it = std::find(paramPlayer->invitedToGuildsList.begin(),paramPlayer->invitedToGuildsList.end(), player->getGuildId()); - if(it != paramPlayer->invitedToGuildsList.end()) + InvitationsList::iterator it = std::find(paramPlayer->invitationsList.begin(), paramPlayer->invitationsList.end(), player->getGuildId()); + if(it != paramPlayer->invitationsList.end()) { sprintf(buffer, "%s has revoked your invite to %s guild.", player->getName().c_str(), (player->getSex(false) ? "his" : "her")); - paramPlayer->sendTextMessage(MSG_INFO_DESCR, buffer); + paramPlayer->sendTextMessage(MSG_EVENT_GUILD, buffer); + sprintf(buffer, "%s has revoked the guildinvite of %s.", player->getName().c_str(), paramPlayer->getName().c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); - paramPlayer->invitedToGuildsList.erase(it); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); + + paramPlayer->invitationsList.erase(it); return true; } else @@ -662,7 +761,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t if(IOGuild::getInstance()->guildExists(player->getGuildId())) { sprintf(buffer, "%s has revoked the guildinvite of %s.", player->getName().c_str(), param.c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); IOGuild::getInstance()->revokeInvite(player->getGuildId(), guid); } else @@ -703,6 +802,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t param = text.substr(length); trimString(param); + Player* paramPlayer = NULL; if(g_game.getPlayerByNameWildcard(param, paramPlayer) == RET_NOERROR) { @@ -720,7 +820,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { paramPlayer->setGuildLevel(GUILDLEVEL_VICE); sprintf(buffer, "%s has promoted %s to %s.", player->getName().c_str(), paramPlayer->getName().c_str(), paramPlayer->getRankName().c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); } else player->sendCancel("A player with that name does not have a premium account."); @@ -734,7 +834,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { paramPlayer->setGuildLevel(GUILDLEVEL_MEMBER); sprintf(buffer, "%s has demoted %s to %s.", player->getName().c_str(), paramPlayer->getName().c_str(), paramPlayer->getRankName().c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); } else player->sendCancel("You can only demote Vice-Leaders to Members."); @@ -748,9 +848,10 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { paramPlayer->setGuildLevel(GUILDLEVEL_LEADER); player->setGuildLevel(GUILDLEVEL_VICE); + IOGuild::getInstance()->updateOwnerId(paramPlayer->getGuildId(), paramPlayer->getGUID()); sprintf(buffer, "%s has passed the guild leadership to %s.", player->getName().c_str(), paramPlayer->getName().c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_GAMEMASTER_CHANNEL, buffer); } else { @@ -765,9 +866,14 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { if(player->getGuildLevel() > paramPlayer->getGuildLevel()) { - sprintf(buffer, "%s has been kicked from the guild by %s.", paramPlayer->getName().c_str(), player->getName().c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); - paramPlayer->leaveGuild(); + if(!player->hasEnemy()) + { + sprintf(buffer, "%s has been kicked from the guild by %s.", paramPlayer->getName().c_str(), player->getName().c_str()); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); + paramPlayer->leaveGuild(); + } + else + player->sendCancel("Your guild is currently at war, you cannot kick right now."); } else player->sendCancel("You may only kick players with a guild rank below your."); @@ -798,7 +904,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { IOGuild::getInstance()->setGuildLevel(guid, GUILDLEVEL_VICE); sprintf(buffer, "%s has promoted %s to %s.", player->getName().c_str(), param.c_str(), IOGuild::getInstance()->getRank(guid).c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); } else player->sendCancel("A player with that name does not have a premium account."); @@ -812,7 +918,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { IOGuild::getInstance()->setGuildLevel(guid, GUILDLEVEL_MEMBER); sprintf(buffer, "%s has demoted %s to %s.", player->getName().c_str(), param.c_str(), IOGuild::getInstance()->getRank(guid).c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); } else player->sendCancel("You can only demote Vice-Leaders to Members."); @@ -826,8 +932,9 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { IOGuild::getInstance()->setGuildLevel(guid, GUILDLEVEL_LEADER); player->setGuildLevel(GUILDLEVEL_VICE); + sprintf(buffer, "%s has passed the guild leadership to %s.", player->getName().c_str(), param.c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_GAMEMASTER_CHANNEL, buffer); } else { @@ -841,7 +948,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t else { sprintf(buffer, "%s has been kicked from the guild by %s.", param.c_str(), player->getName().c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); IOLoginData::getInstance()->resetGuildInformation(guid); } } @@ -863,6 +970,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t std::string param1 = params[0], param2 = params[1]; trimString(param1); trimString(param2); + Player* paramPlayer = NULL; if(g_game.getPlayerByNameWildcard(param1, paramPlayer) == RET_NOERROR) { @@ -885,7 +993,8 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t sprintf(buffer, "%s has set the guildnick of %s to \"%s\".", player->getName().c_str(), paramPlayer->getName().c_str(), param2.c_str()); else sprintf(buffer, "%s has set %s guildnick to \"%s\".", player->getName().c_str(), (player->getSex(false) ? "his" : "her"), param2.c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); } else player->sendCancel("You may only change the guild nick of players that have a lower rank than you."); @@ -928,7 +1037,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { IOGuild::getInstance()->setGuildNick(guid, param2); sprintf(buffer, "%s has set the guildnick of %s to \"%s\".", player->getName().c_str(), param1.c_str(), param2.c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); } else player->sendCancel("You may only change the guild nick of players that have a lower rank than you."); @@ -965,6 +1074,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t std::string param1 = params[0], param2 = params[1]; trimString(param1); trimString(param2); + if(player->getGuildLevel() == GUILDLEVEL_LEADER) { if(param2.length() > 2) @@ -979,7 +1089,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { IOGuild::getInstance()->changeRank(player->getGuildId(), param1, param2); sprintf(buffer, "%s has renamed the guildrank: \"%s\", to: \"%s\".", player->getName().c_str(), param1.c_str(), param2.c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); } else player->sendCancel("There is already a rank in your guild with that name."); @@ -1016,7 +1126,7 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { IOGuild::getInstance()->setMotd(player->getGuildId(), param); sprintf(buffer, "%s has set the Message of the Day to: %s", player->getName().c_str(), param.c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_GAMEMASTER_CHANNEL, buffer); } else player->sendCancel("That motd is too long."); @@ -1036,13 +1146,13 @@ bool Chat::talkToChannel(Player* player, SpeakClasses type, const std::string& t { IOGuild::getInstance()->setMotd(player->getGuildId(), ""); sprintf(buffer, "%s has cleaned the Message of the Day.", player->getName().c_str()); - channel->talk(player, SPEAK_CHANNEL_W, buffer); + channel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); } else player->sendCancel("Only the leader of your guild can clean the guild motd."); } else if(text.substr(1, 8) == "commands") - player->sendToChannel(player, SPEAK_CHANNEL_W, "Guild commands with parameters: disband, invite[name], leave, kick[name], revoke[name], demote[name], promote[name], passleadership[name], nick[name, nick], setrankname[oldName, newName], setmotd[text] and cleanmotd.", CHANNEL_GUILD); + player->sendChannelMessage("", "Guild commands with parameters: disband, invite[name], leave, kick[name], revoke[name], demote[name], promote[name], passleadership[name], nick[name, nick], setrankname[oldName, newName], setmotd[text] and cleanmotd.", MSG_CHANNEL_HIGHLIGHT, CHANNEL_GUILD); else return false; @@ -1057,42 +1167,42 @@ std::string Chat::getChannelName(Player* player, uint16_t channelId) return ""; } -ChannelList Chat::getChannelList(Player* player) +ChannelsList Chat::getChannelList(Player* player) { - ChannelList list; + ChannelsList list; if(!player || player->isRemoved()) return list; ChatChannel* channel = NULL; if(player->getParty() && ((channel = getChannel(player, CHANNEL_PARTY)) || (channel = createChannel(player, CHANNEL_PARTY)))) - list.push_back(channel); + list.push_back(std::make_pair(channel->getId(), channel->getName())); if(player->getGuildId() && player->getGuildName().length() && ((channel = getChannel( player, CHANNEL_GUILD)) || (channel = createChannel(player, CHANNEL_GUILD)))) - list.push_back(channel); + list.push_back(std::make_pair(channel->getId(), channel->getName())); for(NormalChannelMap::iterator it = m_normalChannels.begin(); it != m_normalChannels.end(); ++it) { if((channel = getChannel(player, it->first))) - list.push_back(it->second); + list.push_back(std::make_pair(channel->getId(), channel->getName())); } bool hasPrivate = false; - PrivateChatChannel* prvChannel = NULL; + PrivateChatChannel* privateChannel = NULL; for(PrivateChannelMap::iterator pit = m_privateChannels.begin(); pit != m_privateChannels.end(); ++pit) { - if(!(prvChannel = pit->second)) + if(!(privateChannel = pit->second)) continue; - if(prvChannel->isInvited(player)) - list.push_back(prvChannel); + if(privateChannel->isInvited(player)) + list.push_back(std::make_pair(privateChannel->getId(), privateChannel->getName())); - if(prvChannel->getOwner() == player->getGUID()) + if(privateChannel->getOwner() == player->getGUID()) hasPrivate = true; } if(!hasPrivate && player->isPremium()) - list.push_front(dummyPrivate); + list.push_front(std::make_pair(dummyPrivate->getId(), dummyPrivate->getName())); return list; } @@ -1100,7 +1210,7 @@ ChannelList Chat::getChannelList(Player* player) ChatChannel* Chat::getChannel(Player* player, uint16_t channelId) { #ifdef __DEBUG_CHAT__ - std::cout << "Chat::getChannel - getChannel id " << channelId << std::endl; + std::clog << "Chat::getChannel - getChannel id " << channelId << std::endl; #endif if(!player || player->isRemoved()) return false; @@ -1130,7 +1240,7 @@ ChatChannel* Chat::getChannel(Player* player, uint16_t channelId) if(nit != m_normalChannels.end()) { #ifdef __DEBUG_CHAT__ - std::cout << "Chat::getChannel - found normal channel" << std::endl; + std::clog << "Chat::getChannel - found normal channel" << std::endl; #endif ChatChannel* tmpChannel = nit->second; if(!tmpChannel || !tmpChannel->hasFlag(CHANNELFLAG_ENABLED) || player->getAccess() < tmpChannel->getAccess() @@ -1138,16 +1248,13 @@ ChatChannel* Chat::getChannel(Player* player, uint16_t channelId) player->getVocationId()))) { #ifdef __DEBUG_CHAT__ - std::cout << "Chat::getChannel - cannot access normal channel" << std::endl; + std::clog << "Chat::getChannel - cannot access normal channel" << std::endl; #endif return NULL; } - if(channelId == CHANNEL_RVR && !player->hasFlag(PlayerFlag_CanAnswerRuleViolations)) - return NULL; - #ifdef __DEBUG_CHAT__ - std::cout << "Chat::getChannel - endpoint return" << std::endl; + std::clog << "Chat::getChannel - endpoint return" << std::endl; #endif return tmpChannel; } @@ -1182,3 +1289,15 @@ PrivateChatChannel* Chat::getPrivateChannel(Player* player) return NULL; } + +ChannelList Chat::getPublicChannels() const +{ + ChannelList list; + for(NormalChannelMap::const_iterator it = m_normalChannels.begin(); it != m_normalChannels.end(); ++it) + { + if(isPublicChannel(it->first)) + list.push_back(it->second); + } + + return list; +} diff --git a/chat.h b/chat.h index faf4713..bad2fe2 100644 --- a/chat.h +++ b/chat.h @@ -43,11 +43,8 @@ class ChatChannel const std::string& conditionMessage = "", VocationMap* vocationMap = NULL); virtual ~ChatChannel() { - if(m_condition) - delete m_condition; - - if(m_vocationMap) - delete m_vocationMap; + delete m_condition; + delete m_vocationMap; } static uint16_t staticFlags; @@ -57,11 +54,11 @@ class ChatChannel int32_t getConditionId() const {return m_conditionId;} const std::string& getConditionMessage() const {return m_conditionMessage;} - const UsersMap& getUsers() {return m_users;} + const UsersMap& getUsers() const {return m_users;} uint32_t getLevel() const {return m_level;} uint32_t getAccess() const {return m_access;} - virtual const uint32_t getOwner() {return 0;} + virtual uint32_t getOwner() {return 0;} bool hasFlag(uint16_t value) const {return ((m_flags & (uint16_t)value) == (uint16_t)value);} bool checkVocation(uint32_t vocationId) const @@ -69,9 +66,11 @@ class ChatChannel vocationId) != m_vocationMap->end();} bool addUser(Player* player); - bool removeUser(Player* player); + bool removeUser(Player* player, bool exclude = false); + bool hasUser(Player* player) const {return player && m_users.find(player->getID()) != m_users.end();} - bool talk(Player* player, SpeakClasses type, const std::string& text, uint32_t _time = 0); + bool talk(Player* player, MessageClasses type, const std::string& text, uint32_t statementId); + bool talk(std::string nick, MessageClasses type, const std::string& text); protected: uint16_t m_id, m_flags; @@ -92,7 +91,7 @@ class PrivateChatChannel : public ChatChannel PrivateChatChannel(uint16_t id, std::string name, uint16_t flags); virtual ~PrivateChatChannel() {} - virtual const uint32_t getOwner() {return m_owner;} + virtual uint32_t getOwner() {return m_owner;} void setOwner(uint32_t id) {m_owner = id;} bool isInvited(const Player* player); @@ -105,18 +104,20 @@ class PrivateChatChannel : public ChatChannel void closeChannel(); + const InviteList& getInvitedUsers() {return m_invites;} + protected: InviteList m_invites; uint32_t m_owner; }; typedef std::list ChannelList; -typedef std::map StatementMap; +typedef std::list > ChannelsList; class Chat { public: - Chat(): statement(0), dummyPrivate(NULL), partyName("Party") {} + Chat(): dummyPrivate(NULL), partyName("Party") {} virtual ~Chat(); bool reload(); @@ -127,22 +128,25 @@ class Chat bool deleteChannel(Player* player, uint16_t channelId); ChatChannel* addUserToChannel(Player* player, uint16_t channelId); + void reOpenChannels(Player* player); bool removeUserFromChannel(Player* player, uint16_t channelId); - void removeUserFromAllChannels(Player* player); + void removeUserFromChannels(Player* player); - bool talkToChannel(Player* player, SpeakClasses type, const std::string& text, uint16_t channelId); + bool talk(Player* player, MessageClasses type, const std::string& text, + uint16_t channelId, uint32_t statementId, bool anonymous = false); ChatChannel* getChannel(Player* player, uint16_t channelId); ChatChannel* getChannelById(uint16_t channelId); std::string getChannelName(Player* player, uint16_t channelId); - ChannelList getChannelList(Player* player); + ChannelsList getChannelList(Player* player); PrivateChatChannel* getPrivateChannel(Player* player); - bool isPrivateChannel(uint16_t channelId) const {return m_privateChannels.find(channelId) != m_privateChannels.end();} + bool isPrivateChannel(uint16_t cid) const {return m_privateChannels.find(cid) != m_privateChannels.end();} - uint32_t statement; - StatementMap statementMap; + ChannelList getPublicChannels() const; + bool isPublicChannel(uint16_t cid) const {return cid != CHANNEL_GUILD && cid + != CHANNEL_PARTY && !isPrivateChannel(cid);} private: void clear(); diff --git a/combat.cpp b/combat.cpp index fd27623..3830ed8 100644 --- a/combat.cpp +++ b/combat.cpp @@ -55,7 +55,7 @@ Combat::~Combat() delete params.targetCallback; } -bool Combat::getMinMaxValues(Creature* creature, Creature* target, int32_t& min, int32_t& max) const +bool Combat::getMinMaxValues(Creature* creature, Creature* target, CombatParams& _params, int32_t& min, int32_t& max) const { if(creature) { @@ -66,7 +66,7 @@ bool Combat::getMinMaxValues(Creature* creature, Creature* target, int32_t& min, { if(params.valueCallback) { - params.valueCallback->getMinMaxValues(player, min, max, params.useCharges); + params.valueCallback->getMinMaxValues(player, _params, min, max); return true; } @@ -89,12 +89,19 @@ bool Combat::getMinMaxValues(Creature* creature, Creature* target, int32_t& min, case FORMULA_SKILL: { - Item* tool = player->getWeapon(); - if(const Weapon* weapon = g_weapons->getWeapon(tool)) + Item* item = player->getWeapon(false); + if(const Weapon* weapon = g_weapons->getWeapon(item)) { - max = (int32_t)(weapon->getWeaponDamage(player, target, tool, true) * maxa + maxb); - if(params.useCharges && tool->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES)) - g_game.transformItem(tool, tool->getID(), std::max((int32_t)0, ((int32_t)tool->getCharges()) - 1)); + _params.element.type = item->getElementType(); + if(_params.element.type != COMBAT_NONE) + { + _params.element.damage = weapon->getWeaponElementDamage(player, item, true); + _params.element.damage = random_range((int32_t)0, (int32_t)(_params.element.damage * maxa + maxb), DISTRO_NORMAL); + } + + max = (int32_t)(weapon->getWeaponDamage(player, target, item, true) * maxa + maxb); + if(params.useCharges && item->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES)) + g_game.transformItem(item, item->getID(), std::max((int32_t)0, ((int32_t)item->getCharges()) - 1)); } else max = (int32_t)maxb; @@ -171,7 +178,7 @@ CombatType_t Combat::ConditionToDamageType(ConditionType_t type) case CONDITION_DROWN: return COMBAT_DROWNDAMAGE; - case CONDITION_PHYSICAL: + case CONDITION_BLEEDING: return COMBAT_PHYSICALDAMAGE; default: @@ -204,7 +211,7 @@ ConditionType_t Combat::DamageToConditionType(CombatType_t type) return CONDITION_CURSED; case COMBAT_PHYSICALDAMAGE: - return CONDITION_PHYSICAL; + return CONDITION_BLEEDING; default: break; @@ -213,7 +220,7 @@ ConditionType_t Combat::DamageToConditionType(CombatType_t type) return CONDITION_NONE; } -ReturnValue Combat::canDoCombat(const Creature* caster, const Tile* tile, bool isAggressive) +ReturnValue Combat::canDoCombat(const Creature* caster, const Tile* tile, bool isAggressive, bool createItem) { if(tile->hasProperty(BLOCKPROJECTILE) || tile->floorChange() || tile->getTeleportItem()) return RET_NOTENOUGHROOM; @@ -237,6 +244,9 @@ ReturnValue Combat::canDoCombat(const Creature* caster, const Tile* tile, bool i if(caster->getPosition().z > tile->getPosition().z) return RET_FIRSTGOUPSTAIRS; + if(createItem && tile->isFull()) + return RET_TILEISFULL; + if(!isAggressive) return RET_NOERROR; @@ -249,7 +259,7 @@ ReturnValue Combat::canDoCombat(const Creature* caster, const Tile* tile, bool i RET_ACTIONNOTPERMITTEDINPROTECTIONZONE : RET_NOERROR; } -ReturnValue Combat::canDoCombat(const Creature* attacker, const Creature* target) +ReturnValue Combat::canDoCombat(const Creature* attacker, const Creature* target, bool isAggressive) { if(!attacker) return RET_NOERROR; @@ -258,7 +268,7 @@ ReturnValue Combat::canDoCombat(const Creature* attacker, const Creature* target CreatureEventList combatEvents = const_cast(attacker)->getCreatureEvents(CREATURE_EVENT_COMBAT); for(CreatureEventList::iterator it = combatEvents.begin(); it != combatEvents.end(); ++it) { - if(!(*it)->executeCombat(const_cast(attacker), const_cast(target)) && success) + if(!(*it)->executeCombat(const_cast(attacker), const_cast(target), isAggressive) && success) success = false; } @@ -272,14 +282,13 @@ ReturnValue Combat::canDoCombat(const Creature* attacker, const Creature* target return RET_YOUMAYNOTATTACKTHISPLAYER; const Player* attackerPlayer = NULL; - if((attackerPlayer = attacker->getPlayer()) || (attacker->getMaster() - && (attackerPlayer = attacker->getMaster()->getPlayer()))) + if((attackerPlayer = attacker->getPlayer()) || (attackerPlayer = attacker->getPlayerMaster())) { checkZones = true; - if((g_game.getWorldType() == WORLD_TYPE_NO_PVP && !Combat::isInPvpZone(attacker, target)) || - isProtected(const_cast(attackerPlayer), const_cast(targetPlayer)) - || (g_config.getBool(ConfigManager::CANNOT_ATTACK_SAME_LOOKFEET) && - attackerPlayer->getDefaultOutfit().lookFeet == targetPlayer->getDefaultOutfit().lookFeet) + if((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !Combat::isInPvpZone(attacker, target) + && !attackerPlayer->isEnemy(targetPlayer, true)) || isProtected(const_cast(attackerPlayer), + const_cast(targetPlayer)) || (g_config.getBool(ConfigManager::CANNOT_ATTACK_SAME_LOOKFEET) + && attackerPlayer->getDefaultOutfit().lookFeet == targetPlayer->getDefaultOutfit().lookFeet) || !attackerPlayer->canSeeCreature(targetPlayer)) return RET_YOUMAYNOTATTACKTHISPLAYER; } @@ -290,24 +299,26 @@ ReturnValue Combat::canDoCombat(const Creature* attacker, const Creature* target return RET_YOUMAYNOTATTACKTHISCREATURE; const Player* attackerPlayer = NULL; - if((attackerPlayer = attacker->getPlayer()) || (attacker->getMaster() - && (attackerPlayer = attacker->getMaster()->getPlayer()))) + if((attackerPlayer = attacker->getPlayer()) || (attackerPlayer = attacker->getPlayerMaster())) { if(attackerPlayer->hasFlag(PlayerFlag_CannotAttackMonster)) return RET_YOUMAYNOTATTACKTHISCREATURE; - if(target->getMaster() && target->getMaster()->getPlayer()) + if(target->isPlayerSummon()) { checkZones = true; - if(g_game.getWorldType() == WORLD_TYPE_NO_PVP && !Combat::isInPvpZone(attacker, target)) + if(g_game.getWorldType() == WORLDTYPE_OPTIONAL && !Combat::isInPvpZone(attacker, target) + && !attackerPlayer->isEnemy(target->getPlayerMaster(), true)) return RET_YOUMAYNOTATTACKTHISCREATURE; } } } + else if(target->getNpc() && !target->isAttackable()) + return RET_YOUMAYNOTATTACKTHISCREATURE; - return checkZones && (target->getTile()->hasFlag(TILESTATE_NOPVPZONE) || - (attacker->getTile()->hasFlag(TILESTATE_NOPVPZONE) - && !target->getTile()->hasFlag(TILESTATE_NOPVPZONE) && + return checkZones && (target->getTile()->hasFlag(TILESTATE_OPTIONALZONE) || + (attacker->getTile()->hasFlag(TILESTATE_OPTIONALZONE) + && !target->getTile()->hasFlag(TILESTATE_OPTIONALZONE) && !target->getTile()->hasFlag(TILESTATE_PROTECTIONZONE))) ? RET_ACTIONNOTPERMITTEDINANOPVPZONE : RET_NOERROR; } @@ -318,17 +329,17 @@ ReturnValue Combat::canTargetCreature(const Player* player, const Creature* targ return RET_YOUMAYNOTATTACKTHISPLAYER; Player* tmpPlayer = const_cast(player); - CreatureEventList targetEvents = tmpPlayer->getCreatureEvents(CREATURE_EVENT_TARGET); - bool deny = false; + + CreatureEventList targetEvents = tmpPlayer->getCreatureEvents(CREATURE_EVENT_TARGET); for(CreatureEventList::iterator it = targetEvents.begin(); it != targetEvents.end(); ++it) { - if(!(*it)->executeTarget(tmpPlayer, const_cast(target))) + if(!(*it)->executeAction(tmpPlayer, const_cast(target)) && !deny) deny = true; } if(deny) - return RET_DONTSHOWMESSAGE; + return RET_NEEDEXCHANGE; if(!player->hasFlag(PlayerFlag_IgnoreProtectionZone)) { @@ -338,20 +349,20 @@ ReturnValue Combat::canTargetCreature(const Player* player, const Creature* targ if(target->getZone() == ZONE_PROTECTION) return RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE; - if(target->getPlayer() || (target->getMaster() && target->getMaster()->getPlayer())) + if(target->getPlayer() || target->isPlayerSummon()) { - if(player->getZone() == ZONE_NOPVP) + if(player->getZone() == ZONE_OPTIONAL) return RET_ACTIONNOTPERMITTEDINANOPVPZONE; - if(target->getZone() == ZONE_NOPVP) + if(target->getZone() == ZONE_OPTIONAL) return RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE; } } - if(player->hasFlag(PlayerFlag_CannotUseCombat) || !target->isAttackable()) + if(player->hasFlag(PlayerFlag_CannotUseCombat)) return target->getPlayer() ? RET_YOUMAYNOTATTACKTHISPLAYER : RET_YOUMAYNOTATTACKTHISCREATURE; - if(target->getPlayer() && !Combat::isInPvpZone(player, target) && player->getSkullClient(target->getPlayer()) == SKULL_NONE) + if(target->getPlayer() && !Combat::isInPvpZone(player, target) && player->getSkullType(target->getPlayer()) == SKULL_NONE) { if(player->getSecureMode() == SECUREMODE_ON) return RET_TURNSECUREMODETOATTACKUNMARKEDPLAYERS; @@ -360,33 +371,26 @@ ReturnValue Combat::canTargetCreature(const Player* player, const Creature* targ return RET_YOUMAYNOTATTACKTHISPLAYER; } - return Combat::canDoCombat(player, target); + return Combat::canDoCombat(player, target, true); } bool Combat::isInPvpZone(const Creature* attacker, const Creature* target) { - return attacker->getZone() == ZONE_PVP && target->getZone() == ZONE_PVP; + return attacker->getZone() == ZONE_HARDCORE && target->getZone() == ZONE_HARDCORE; } bool Combat::isProtected(Player* attacker, Player* target) { - if(attacker->hasFlag(PlayerFlag_CannotAttackPlayer) || !target->isAttackable()) + if(attacker->hasFlag(PlayerFlag_CannotAttackPlayer)) return true; - if(attacker->getZone() == ZONE_PVP && target->getZone() == ZONE_PVP && g_config.getBool(ConfigManager::PVP_TILE_IGNORE_PROTECTION)) + if(attacker->hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges)) return false; - if(attacker->hasCustomFlag(PlayerCustomFlag_IsProtected) || target->hasCustomFlag(PlayerCustomFlag_IsProtected)) - return true; - - uint32_t protectionLevel = g_config.getNumber(ConfigManager::PROTECTION_LEVEL); - if(target->getLevel() < protectionLevel || attacker->getLevel() < protectionLevel) - return true; - - if(!attacker->getVocation()->isAttackable() || !target->getVocation()->isAttackable()) - return true; + if(attacker->getZone() == ZONE_HARDCORE && target->getZone() == ZONE_HARDCORE && g_config.getBool(ConfigManager::PVP_TILE_IGNORE_PROTECTION)) + return false; - return attacker->checkLoginDelay(target->getID()); + return target->isProtected() || attacker->isProtected() || attacker->checkLoginDelay(target->getID()); } void Combat::setPlayerCombatValues(formulaType_t _type, double _mina, double _minb, double _maxa, double _maxb, double _minl, double _maxl, double _minm, double _maxm, int32_t _minc, int32_t _maxc) @@ -452,9 +456,17 @@ bool Combat::setParam(CombatParam_t param, uint32_t value) return true; case COMBATPARAM_HITCOLOR: - params.effects.color = (TextColor_t)value; + params.effects.color = (Color_t)value; return true; + case COMBATPARAM_ELEMENTDAMAGE: + params.element.damage = value; + break; + + case COMBATPARAM_ELEMENTTYPE: + params.element.type = (CombatType_t)value; + break; + default: break; } @@ -495,7 +507,7 @@ bool Combat::setCallback(CallBackParam_t key) } default: - std::cout << "Combat::setCallback - Unknown callback type: " << (uint32_t)key << std::endl; + std::clog << "Combat::setCallback - Unknown callback type: " << (uint32_t)key << std::endl; break; } @@ -533,13 +545,21 @@ bool Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatPa change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL); } - if(g_game.combatBlockHit(params.combatType, caster, target, change, params.blockedByShield, params.blockedByArmor)) + if(g_game.combatBlockHit(params.combatType, caster, target, change, params.blockedByShield, params.blockedByArmor, params.itemId != 0)) return false; - if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK) - change = change / 2; + CombatParams _params = params; + if(_params.element.damage && _params.element.type != COMBAT_NONE) + g_game.combatBlockHit(_params.element.type, caster, target, _params.element.damage, params.blockedByShield, params.blockedByArmor, params.itemId != 0, true); + + if(caster && caster->getPlayer() && target->getPlayer() && target->getSkull() != SKULL_BLACK) + { + _params.element.damage /= 2; + if(change < 0) + change /= 2; + } - if(!g_game.combatChangeHealth(params.combatType, caster, target, change, params.effects.hit, params.effects.color)) + if(!g_game.combatChangeHealth(_params, caster, target, change, false)) return false; CombatConditionFunc(caster, target, params, NULL); @@ -557,8 +577,11 @@ bool Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatPara change = random_range(var->minChange, var->maxChange, DISTRO_NORMAL); } - if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getPlayer()->getSkull() != SKULL_BLACK) - change = change / 2; + if(g_game.combatBlockHit(COMBAT_MANADRAIN, caster, target, change, false, false, params.itemId != 0)) + return false; + + if(change < 0 && caster && caster->getPlayer() && target->getPlayer() && target->getSkull() != SKULL_BLACK) + change /= 2; if(!g_game.combatChangeMana(caster, target, change)) return false; @@ -568,7 +591,7 @@ bool Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatPara return true; } -bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, void* data) +bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, void*) { if(params.conditionList.empty()) return false; @@ -581,9 +604,15 @@ bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const Comba Condition* tmp = (*it)->clone(); if(caster) + { tmp->setParam(CONDITIONPARAM_OWNER, caster->getID()); + if(params.isAggressive) + caster->onTargetDrain(target, 0); + else + caster->onTargetGain(target, 0); + } - //TODO: infight condition until all aggressive conditions has ended + //TODO: infight condition until all aggressive conditions has ended [?] if(!target->addCombatCondition(tmp) && result) result = false; } @@ -591,16 +620,27 @@ bool Combat::CombatConditionFunc(Creature* caster, Creature* target, const Comba return result; } -bool Combat::CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, void* data) +bool Combat::CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, void*) { - if(!target->hasCondition(params.dispelType)) + if(!target->hasCondition(params.dispelType, -1, false)) return false; + if(params.dispelType == CONDITION_INVISIBLE) + { + if(Player* player = target->getPlayer()) + { + Item* item = player->getEquippedItem(SLOT_RING); + if(item && item->getID() == ITEM_STEALTH_RING && (g_game.getWorldType() == WORLDTYPE_HARDCORE + || player->getTile()->hasFlag(TILESTATE_HARDCOREZONE)) && random_range(1, 100) <= 10) + g_game.internalRemoveItem(NULL, item); + } + } + target->removeCondition(caster, params.dispelType); return true; } -bool Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, void* data) +bool Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, void*) { CombatConditionFunc(caster, target, params, NULL); CombatDispelFunc(caster, target, params, NULL); @@ -624,7 +664,8 @@ void Combat::combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* if(player) { bool pzLock = false; - if(g_game.getWorldType() == WORLD_TYPE_NO_PVP || tile->hasFlag(TILESTATE_NOPVPZONE)) + if((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !tile->hasFlag( + TILESTATE_HARDCOREZONE)) || tile->hasFlag(TILESTATE_OPTIONALZONE)) { switch(itemId) { @@ -744,7 +785,7 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const CombatArea* Tile* tile = NULL; for(std::list::iterator it = tileList.begin(); it != tileList.end(); ++it) { - if(!(tile = (*it)) || canDoCombat(caster, (*it), params.isAggressive) != RET_NOERROR) + if(!(tile = (*it)) || canDoCombat(caster, (*it), params.isAggressive, params.itemId != 0) != RET_NOERROR) continue; bool skip = true; @@ -769,7 +810,7 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const CombatArea* continue; } - if(!params.isAggressive || (caster != (*cit) && Combat::canDoCombat(caster, (*cit)) == RET_NOERROR)) + if(!params.isAggressive || (caster != (*cit) && Combat::canDoCombat(caster, (*cit), true) == RET_NOERROR)) { func(caster, (*cit), params, (void*)var); if(params.targetCallback) @@ -789,12 +830,17 @@ void Combat::doCombat(Creature* caster, Creature* target) const //target combat callback function if(params.combatType != COMBAT_NONE) { + if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target, true) != RET_NOERROR)) + return; + int32_t minChange = 0, maxChange = 0; - getMinMaxValues(caster, target, minChange, maxChange); + CombatParams _params = params; + + getMinMaxValues(caster, target, _params, minChange, maxChange); if(params.combatType != COMBAT_MANADRAIN) - doCombatHealth(caster, target, minChange, maxChange, params); + doCombatHealth(caster, target, minChange, maxChange, _params, false); else - doCombatMana(caster, target, minChange, maxChange, params); + doCombatMana(caster, target, minChange, maxChange, _params, false); } else doCombatDefault(caster, target, params); @@ -806,19 +852,21 @@ void Combat::doCombat(Creature* caster, const Position& pos) const if(params.combatType != COMBAT_NONE) { int32_t minChange = 0, maxChange = 0; - getMinMaxValues(caster, NULL, minChange, maxChange); + CombatParams _params = params; + + getMinMaxValues(caster, NULL, _params, minChange, maxChange); if(params.combatType != COMBAT_MANADRAIN) - doCombatHealth(caster, pos, area, minChange, maxChange, params); + doCombatHealth(caster, pos, area, minChange, maxChange, _params); else - doCombatMana(caster, pos, area, minChange, maxChange, params); + doCombatMana(caster, pos, area, minChange, maxChange, _params); } else CombatFunc(caster, pos, area, params, CombatNullFunc, NULL); } -void Combat::doCombatHealth(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params) +void Combat::doCombatHealth(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params, bool check/* = true*/) { - if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR)) + if(check && params.isAggressive && (caster == target || Combat::canDoCombat(caster, target, true) != RET_NOERROR)) return; Combat2Var var; @@ -846,9 +894,9 @@ void Combat::doCombatHealth(Creature* caster, const Position& pos, const CombatA CombatFunc(caster, pos, area, params, CombatHealthFunc, (void*)&var); } -void Combat::doCombatMana(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params) +void Combat::doCombatMana(Creature* caster, Creature* target, int32_t minChange, int32_t maxChange, const CombatParams& params, bool check/* = true*/) { - if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR)) + if(check && params.isAggressive && (caster == target || Combat::canDoCombat(caster, target, true) != RET_NOERROR)) return; Combat2Var var; @@ -882,9 +930,9 @@ void Combat::doCombatCondition(Creature* caster, const Position& pos, const Comb CombatFunc(caster, pos, area, params, CombatConditionFunc, NULL); } -void Combat::doCombatCondition(Creature* caster, Creature* target, const CombatParams& params) +void Combat::doCombatCondition(Creature* caster, Creature* target, const CombatParams& params, bool check/* = true*/) { - if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR)) + if(check && params.isAggressive && (caster == target || Combat::canDoCombat(caster, target, true) != RET_NOERROR)) return; CombatConditionFunc(caster, target, params, NULL); @@ -905,9 +953,9 @@ void Combat::doCombatDispel(Creature* caster, const Position& pos, const CombatA CombatFunc(caster, pos, area, params, CombatDispelFunc, NULL); } -void Combat::doCombatDispel(Creature* caster, Creature* target, const CombatParams& params) +void Combat::doCombatDispel(Creature* caster, Creature* target, const CombatParams& params, bool check/* = true*/) { - if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR)) + if(check && params.isAggressive && (caster == target || Combat::canDoCombat(caster, target, true) != RET_NOERROR)) return; CombatDispelFunc(caster, target, params, NULL); @@ -924,7 +972,7 @@ void Combat::doCombatDispel(Creature* caster, Creature* target, const CombatPara void Combat::doCombatDefault(Creature* caster, Creature* target, const CombatParams& params) { - if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target) != RET_NOERROR)) + if(params.isAggressive && (caster == target || Combat::canDoCombat(caster, target, true) != RET_NOERROR)) return; const SpectatorVec& list = g_game.getSpectators(target->getTile()->getPosition()); @@ -944,12 +992,12 @@ void Combat::doCombatDefault(Creature* caster, Creature* target, const CombatPar //********************************************************** -void ValueCallback::getMinMaxValues(Player* player, int32_t& min, int32_t& max, bool useCharges) const +void ValueCallback::getMinMaxValues(Player* player, CombatParams& params, int32_t& min, int32_t& max) const { //"onGetPlayerMinMaxValues"(cid, ...) if(!m_interface->reserveEnv()) { - std::cout << "[Error - ValueCallback::getMinMaxValues] Callstack overflow." << std::endl; + std::clog << "[Error - ValueCallback::getMinMaxValues] Callstack overflow." << std::endl; return; } @@ -976,45 +1024,67 @@ void ValueCallback::getMinMaxValues(Player* player, int32_t& min, int32_t& max, case FORMULA_SKILL: { - //"onGetPlayerMinMaxValues"(cid, level, skill, attack, factor) - Item* tool = player->getWeapon(); + //"onGetPlayerMinMaxValues"(cid, level, skill, attack, element, factor) lua_pushnumber(L, player->getLevel()); - lua_pushnumber(L, player->getWeaponSkill(tool)); + if(Item* weapon = player->getWeapon(false)) + { + lua_pushnumber(L, player->getWeaponSkill(weapon)); + if(params.useCharges && weapon->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES)) + g_game.transformItem(weapon, weapon->getID(), std::max(0, weapon->getCharges() - 1)); + + uint32_t attack = weapon->getAttack() + weapon->getExtraAttack(); + if(weapon->getWeaponType() == WEAPON_AMMO) + { + if(Item* bow = player->getWeapon(true)) + attack += bow->getAttack() + bow->getExtraAttack(); + } + + if(weapon->getElementType() != COMBAT_NONE) + { + attack -= weapon->getElementDamage(); + lua_pushnumber(L, attack); - int32_t attack = 7; - if(tool) + lua_pushnumber(L, weapon->getElementDamage()); + params.element.type = weapon->getElementType(); + } + else + { + lua_pushnumber(L, attack); + lua_pushnumber(L, 0); + } + } + else { - attack = tool->getAttack(); - if(useCharges && tool->hasCharges() && g_config.getBool(ConfigManager::REMOVE_WEAPON_CHARGES)) - g_game.transformItem(tool, tool->getID(), std::max(0, tool->getCharges() - 1)); + lua_pushnumber(L, player->getSkill(SKILL_FIST, SKILL_LEVEL)); + lua_pushnumber(L, g_config.getNumber(ConfigManager::FIST_BASE_ATTACK)); + lua_pushnumber(L, 0); } - lua_pushnumber(L, attack); lua_pushnumber(L, player->getAttackFactor()); - - parameters += 4; + parameters += 5; break; } default: { - std::cout << "[Warning - ValueCallback::getMinMaxValues] Unknown callback type" << std::endl; + std::clog << "[Warning - ValueCallback::getMinMaxValues] Unknown callback type" << std::endl; return; } } - int32_t params = lua_gettop(L); - if(!lua_pcall(L, parameters, 2, 0)) + int32_t args = lua_gettop(L); + if(!lua_pcall(L, parameters, 3, 0)) { - min = LuaScriptInterface::popNumber(L); - max = LuaScriptInterface::popNumber(L); - player->increaseCombatValues(min, max, useCharges, type != FORMULA_SKILL); + params.element.damage = LuaInterface::popNumber(L); + max = LuaInterface::popNumber(L); + min = LuaInterface::popNumber(L); + player->increaseCombatValues(min, max, params.useCharges, type != FORMULA_SKILL); } else - LuaScriptInterface::error(NULL, std::string(LuaScriptInterface::popString(L))); + LuaInterface::error(NULL, std::string(LuaInterface::popString(L))); - if((lua_gettop(L) + parameters + 1) != params) - LuaScriptInterface::error(__FUNCTION__, "Stack size changed!"); + if((lua_gettop(L) + parameters + 1) != args) + LuaInterface::error(__FUNCTION__, "Stack size changed!"); env->resetCallback(); m_interface->releaseEnv(); @@ -1042,7 +1112,7 @@ void TileCallback::onTileCombat(Creature* creature, Tile* tile) const m_interface->releaseEnv(); } else - std::cout << "[Error - TileCallback::onTileCombat] Call stack overflow." << std::endl; + std::clog << "[Error - TileCallback::onTileCombat] Call stack overflow." << std::endl; } //********************************************************** @@ -1068,17 +1138,17 @@ void TargetCallback::onTargetCombat(Creature* creature, Creature* target) const int32_t size = lua_gettop(L); if(lua_pcall(L, 2, 0 /*nReturnValues*/, 0) != 0) - LuaScriptInterface::error(NULL, std::string(LuaScriptInterface::popString(L))); + LuaInterface::error(NULL, std::string(LuaInterface::popString(L))); if((lua_gettop(L) + 2 /*nParams*/ + 1) != size) - LuaScriptInterface::error(__FUNCTION__, "Stack size changed!"); + LuaInterface::error(__FUNCTION__, "Stack size changed!"); env->resetCallback(); m_interface->releaseEnv(); } else { - std::cout << "[Error - TargetCallback::onTargetCombat] Call stack overflow." << std::endl; + std::clog << "[Error - TargetCallback::onTargetCombat] Call stack overflow." << std::endl; return; } } @@ -1202,18 +1272,16 @@ void CombatArea::copyArea(const MatrixArea* input, MatrixArea* output, MatrixOpe break; } - double angleRad = 3.1416 * angle / 180.0; - float a = std::cos(angleRad), b = -std::sin(angleRad); - float c = std::sin(angleRad), d = std::cos(angleRad); - - for(int32_t x = 0; x < (long)input->getCols(); ++x) + double _angle = 3.1416 * angle / 180.0; + float a = std::cos(_angle), b = std::sin(_angle); + for(int32_t x = 0; x < (int32_t)input->getCols(); ++x) { - for(int32_t y = 0; y < (long)input->getRows(); ++y) + for(int32_t y = 0; y < (int32_t)input->getRows(); ++y) { //calculate new coordinates using rotation center int32_t newX = x - centerX, newY = y - centerY, - rotatedX = round(newX * a + newY * b), - rotatedY = round(newX * c + newY * d); + rotatedX = round(newX * a + newY * -b), + rotatedY = round(newX * b + newY * a); //write in the output matrix using rotated coordinates (*output)[rotatedY + rotateCenterY][rotatedX + rotateCenterX] = (*input)[y][x]; } @@ -1253,7 +1321,7 @@ void CombatArea::setupArea(const std::list& list, uint32_t rows) //NORTH MatrixArea* area = createArea(list, rows); areas[NORTH] = area; - uint32_t maxOutput = std::max(area->getCols(), area->getRows()) * 2; + uint32_t maxOutput = std::max(area->getCols(), area->getRows()) << 1; //SOUTH MatrixArea* southArea = new MatrixArea(maxOutput, maxOutput); @@ -1278,7 +1346,7 @@ void CombatArea::setupArea(int32_t length, int32_t spread) int32_t cols = 1; if(spread != 0) - cols = ((length - length % spread) / spread) * 2 + 1; + cols = (((length - length % spread) / spread) << 1) + 1; int32_t colSpread = cols; for(uint32_t y = 1; y <= rows; ++y) @@ -1345,7 +1413,7 @@ void CombatArea::setupExtArea(const std::list& list, uint32_t rows) //NORTH-WEST MatrixArea* area = createArea(list, rows); areas[NORTHWEST] = area; - uint32_t maxOutput = std::max(area->getCols(), area->getRows()) * 2; + uint32_t maxOutput = std::max(area->getCols(), area->getRows()) << 1; //NORTH-EAST MatrixArea* neArea = new MatrixArea(maxOutput, maxOutput); @@ -1369,15 +1437,25 @@ void CombatArea::setupExtArea(const std::list& list, uint32_t rows) bool MagicField::isBlocking(const Creature* creature) const { - if(id != ITEM_MAGICWALL_SAFE && id != ITEM_WILDGROWTH_SAFE) + if(!isUnstepable()) return Item::isBlocking(creature); - return !creature || !creature->getPlayer(); + if(!creature || !creature->getPlayer()) + return true; + + uint32_t ownerId = getOwner(); + if(!ownerId) + return false; + + if(Creature* owner = g_game.getCreatureByID(ownerId)) + return creature->getPlayer()->getGuildEmblem(owner) != EMBLEM_NONE; + + return false; } void MagicField::onStepInField(Creature* creature, bool purposeful/* = true*/) { - if(id == ITEM_MAGICWALL_SAFE || id == ITEM_WILDGROWTH_SAFE || isBlocking(creature)) + if(isUnstepable() || isBlocking(creature)) { if(!creature->isGhost()) g_game.internalRemoveItem(creature, this, 1); @@ -1385,26 +1463,31 @@ void MagicField::onStepInField(Creature* creature, bool purposeful/* = true*/) return; } - if(!purposeful) + if(!purposeful || !creature->isAttackable()) return; - const ItemType& it = items[getID()]; + const ItemType& it = items[id]; if(!it.condition) return; - Condition* condition = it.condition->clone(); uint32_t ownerId = getOwner(); - if(ownerId && !getTile()->hasFlag(TILESTATE_PVPZONE)) + Tile* tile = getTile(); + + Condition* condition = it.condition->clone(); + if(ownerId && !tile->hasFlag(TILESTATE_HARDCOREZONE)) { if(Creature* owner = g_game.getCreatureByID(ownerId)) { + Player* ownerPlayer = owner->getPlayer(); + if(!ownerPlayer && owner->isPlayerSummon()) + ownerPlayer = owner->getPlayerMaster(); + bool harmful = true; - if((g_game.getWorldType() == WORLD_TYPE_NO_PVP || getTile()->hasFlag(TILESTATE_NOPVPZONE)) - && (owner->getPlayer() || owner->isPlayerSummon())) + if((g_game.getWorldType() == WORLDTYPE_OPTIONAL || tile->hasFlag(TILESTATE_OPTIONALZONE)) && ownerPlayer) harmful = false; - else if(Player* targetPlayer = creature->getPlayer()) + else if(Player* player = creature->getPlayer()) { - if(owner->getPlayer() && Combat::isProtected(owner->getPlayer(), targetPlayer)) + if(ownerPlayer && Combat::isProtected(ownerPlayer, player)) harmful = false; } diff --git a/combat.h b/combat.h index 067c2be..b80f88c 100644 --- a/combat.h +++ b/combat.h @@ -28,42 +28,11 @@ class Creature; class Position; class Item; -//for luascript callback -class ValueCallback : public CallBack -{ - public: - ValueCallback(formulaType_t _type) {type = _type;} - void getMinMaxValues(Player* player, int32_t& min, int32_t& max, bool useCharges) const; - - protected: - formulaType_t type; -}; - -class TileCallback : public CallBack -{ - public: - TileCallback() {} - void onTileCombat(Creature* creature, Tile* tile) const; - - protected: - formulaType_t type; -}; - -class TargetCallback : public CallBack -{ - public: - TargetCallback() {} - void onTargetCombat(Creature* creature, Creature* target) const; - - protected: - formulaType_t type; -}; - struct CombatEffects { CombatEffects(bool _show): show(_show) { - color = TEXTCOLOR_UNKNOWN; + color = COLOR_UNKNOWN; distance = SHOOT_EFFECT_NONE; impact = MAGIC_EFFECT_NONE; hit = MAGIC_EFFECT_UNKNOWN; @@ -71,7 +40,7 @@ struct CombatEffects CombatEffects() { - color = TEXTCOLOR_UNKNOWN; + color = COLOR_UNKNOWN; distance = SHOOT_EFFECT_NONE; impact = MAGIC_EFFECT_NONE; hit = MAGIC_EFFECT_UNKNOWN; @@ -80,10 +49,26 @@ struct CombatEffects MagicEffect_t impact, hit; ShootEffect_t distance; - TextColor_t color; + Color_t color; bool show; }; +struct CombatElement +{ + CombatElement() + { + type = COMBAT_NONE; + damage = 0; + } + + CombatType_t type; + int32_t damage; +}; + +class TargetCallback; +class ValueCallback; +class TileCallback; + struct CombatParams { CombatParams() @@ -101,13 +86,14 @@ struct CombatParams bool blockedByArmor, blockedByShield, targetCasterOrTopMost, targetPlayersOrSummons, differentAreaDamage, useCharges, isAggressive; ConditionType_t dispelType; - CombatType_t combatType; - uint32_t itemId; + CombatType_t combatType, elementType; + uint32_t itemId, elementDamage; TargetCallback* targetCallback; ValueCallback* valueCallback; TileCallback* tileCallback; CombatEffects effects; + CombatElement element; std::list conditionList; }; @@ -118,6 +104,37 @@ struct Combat2Var Combat2Var() {minChange = maxChange = change = 0;} }; +//for luascript callback +class ValueCallback : public CallBack +{ + public: + ValueCallback(formulaType_t _type) {type = _type;} + void getMinMaxValues(Player* player, CombatParams& params, int32_t& min, int32_t& max) const; + + protected: + formulaType_t type; +}; + +class TileCallback : public CallBack +{ + public: + TileCallback() {} + void onTileCombat(Creature* creature, Tile* tile) const; + + protected: + formulaType_t type; +}; + +class TargetCallback : public CallBack +{ + public: + TargetCallback() {} + void onTargetCombat(Creature* creature, Creature* target) const; + + protected: + formulaType_t type; +}; + typedef bool (*COMBATFUNC)(Creature*, Creature*, const CombatParams&, void*); class MatrixArea { @@ -255,22 +272,22 @@ class Combat virtual ~Combat(); static void doCombatHealth(Creature* caster, Creature* target, - int32_t minChange, int32_t maxChange, const CombatParams& params); + int32_t minChange, int32_t maxChange, const CombatParams& params, bool check = true); static void doCombatHealth(Creature* caster, const Position& pos, const CombatArea* area, int32_t minChange, int32_t maxChange, const CombatParams& params); static void doCombatMana(Creature* caster, Creature* target, - int32_t minChange, int32_t maxChange, const CombatParams& params); + int32_t minChange, int32_t maxChange, const CombatParams& params, bool check = true); static void doCombatMana(Creature* caster, const Position& pos, const CombatArea* area, int32_t minChange, int32_t maxChange, const CombatParams& params); static void doCombatCondition(Creature* caster, Creature* target, - const CombatParams& params); + const CombatParams& params, bool check = true); static void doCombatCondition(Creature* caster, const Position& pos, const CombatArea* area, const CombatParams& params); static void doCombatDispel(Creature* caster, Creature* target, - const CombatParams& params); + const CombatParams& params, bool check = true); static void doCombatDispel(Creature* caster, const Position& pos, const CombatArea* area, const CombatParams& params); @@ -284,8 +301,8 @@ class Combat static ConditionType_t DamageToConditionType(CombatType_t type); static ReturnValue canTargetCreature(const Player* attacker, const Creature* target); - static ReturnValue canDoCombat(const Creature* caster, const Tile* tile, bool isAggressive); - static ReturnValue canDoCombat(const Creature* attacker, const Creature* target); + static ReturnValue canDoCombat(const Creature* caster, const Tile* tile, bool isAggressive, bool createItem); + static ReturnValue canDoCombat(const Creature* attacker, const Creature* target, bool isAggressive); static void postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params); static void addDistanceEffect(Creature* caster, const Position& fromPos, const Position& toPos, ShootEffect_t effect); @@ -298,8 +315,8 @@ class Combat void setArea(CombatArea* _area) { - if(area) - delete area; + + delete area; area = _area; } @@ -326,7 +343,7 @@ class Combat static bool CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, void* data); static void combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params); - bool getMinMaxValues(Creature* creature, Creature* target, int32_t& min, int32_t& max) const; + bool getMinMaxValues(Creature* creature, Creature* target, CombatParams& params, int32_t& min, int32_t& max) const; //configureable CombatParams params; @@ -350,10 +367,11 @@ class MagicField : public Item virtual bool isBlocking(const Creature* creature) const; - bool isReplaceable() const {return Item::items[getID()].replaceable;} + bool isReplacable() const {return Item::items[id].replacable;} + bool isUnstepable() const {return id == ITEM_MAGICWALL_SAFE || id == ITEM_WILDGROWTH_SAFE;} CombatType_t getCombatType() const { - const ItemType& it = items[getID()]; + const ItemType& it = items[id]; return it.combatType; } diff --git a/compile.sh b/compile.sh new file mode 100644 index 0000000..0e5f70a --- /dev/null +++ b/compile.sh @@ -0,0 +1,177 @@ +###################### +#Update Configuration# +######################### +#Do Not Change this info# +######################### +LuajitVer=-2.0.0-beta8 +boostVer=.1.47.0 +boostVer_=_1_47_0 +####################### +#/Update Configuration# +####################### + +if [[ $EUID -ne 0 ]]; then + echo 'You are not root, type "su" to log in to root and run this script again. Press [ENTER] to end this script.' + read end + exit 1 +else + +################ +#Chose Your OS # +################ + + echo 'Chose your Operating System. {Supported OS: Debian, Ubuntu, Fedora, CentOS} ' + read ans1 + if [ $ans1 = "Fedora" ] || [ $ans1 = "CentOS" ]; then + +########################## +#TFS Dependencies & Tools# +################################################## +#This is all the dependencies needed to compile. # +################################################## + + echo -n "[$ans1]Do you want to install all Dependencies and Tools needed?[y/n] " + read ans1_1 + if [ $ans1_1 = 'y' ]; then + yum install mysql++-devel lua-devel boost-devel gmp-devel lua-sql-mysql lua-sql-sqlite libsqlite3x-devel zlib-devel libxml2-devel libxml++-devel kernel-devel mysql-connector-c++ mysql-connector-c++-devel && yum groupinstall "Development Tools" + fi + echo 'Continuing...' + + else if [ $ans1 = "Debian" ] || [ $ans1 = "Ubuntu" ]; then + echo -n "[$ans1]Do you want to install all Dependencies and Tools needed?[y/n] " + read ans1_1 + if [ $ans1_1 = 'y' ]; then + sudo apt-get install build-essential libxml2-dev libxml++2.6-dev liblua5.1-0-dev libboost-all-dev libmysql++-dev libgmp3-dev liblua5.1-sql-mysql-dev liblua5.1-sql-sqlite-dev libsqlite3-dev zlib1g-dev dh-autoreconf + echo 'Libraries and Build Tools... Installed' + else + echo 'Continuing...' + fi + else + echo ['Error: Can not Install Dependencies, this may be caused due to mistyping the OS name(OS name is case-sensitive) or that the OS is not supported.'] + fi + fi +################# +#Compiling Boost# +################################## +# Credits to: Fallen & Cykotitan # +################################## + + echo -n 'Do you want to compile the latest boost?[y/n] ' + read ans2 + if [ $ans2 = 'y' ]; then + wget http://voxel.dl.sourceforge.net/project/boost/boost/$boostVer/boost$boostVer_.tar.gz + + mv download boost.tar.gz + + tar xzf boost.tar.gz + + cd boost_1_47_0/ + + chmod +x bootstrap.sh + + ./bootstrap.sh + + ./bjam variant=release link=static threading=multi + + echo "Boost$boostVer_ has been installed." + + cd .. + else + echo 'Continuing...' + fi + +################# +#Building LuaJIT# +############################################################## +#This will compile LuaJIT(Lua Just-In-Time) for your server. # +############################################################## + + echo -n 'Do you want to compile and install LuaJIT?[y/n] ' + read ans3 + if [ $ans3 = 'y' ]; then + if [ -f "/usr/local/bin/luajit$LuajitVer" ]; then + echo 'LuaJIT has already been installed.' + else + wget http://luajit.org/download/LuaJIT$LuajitVer.tar.gz + + tar zxf LuaJIT-2.0.0-beta8.tar.gz + + cd LuaJIT-2.0.0-beta8 + + make && make install + + ln -sf luajit-2.0.0-beta8 /usr/local/bin/luajit + + cd .. + + fi + fi + echo 'Continuing...' + +############### +#Compiling TFS# +################################################# +#This will compile TFS, it has default settings.# +#If you wish to specify more options, go follow ################ +# the compiling instructions in DOC/README. # -D__LUAJIT-_ # +################################################################ + + if [ -f "autogen.sh" ]; then + ./autogen.sh + else + echo 'No autogen.sh found, moving on.' + fi + + echo -n 'Do you want to specify additional flags?[y/n] ' + read ans4 + if [ $ans4 = 'y' ]; then + if [ -f "configure.ac" ]; then + echo -n 'You are about to make changes to "configure.ac", when you are done editing press [CTRL]+X to exit and save, press [ENTER] to start editing. ' + read cntn1 + nano configure.ac + echo -n 'Do you want to enable additional features? ' + read cntn2 + if [ $cntn2 = 'yes' ]; then + echo "Enable any feature you want, Full list at:[ http://otland.net/content/compiling-forgotten-server-debian-gnu-linux-16/ ] simply write it and press [ENTER]: " + read addons + ./configure --enable-mysql $addons + else + ./configure --enable-mysql + fi + else + echo 'No configure.ac found, moving on.' + fi + else + if [ -f "configure.ac" ]; then + ./configure --enable-mysql + else + echo 'No configure.ac found, moving on.' + fi + fi + + if [ -f "build.sh" ]; then + ./build.sh + else + mkdir obj + make + fi + + echo -n 'Do you wish to clean up the server folder?[y/n] ' + read ans5 + if [ $ans5 = 'y' ]; then + mkdir src && mkdir objs + + mv *.cpp *.h src/ && mv *.o objs/ + #There are a few more files that are needed to be moved. + fi + echo -n 'Done, press [ENTER].' + read finish + + exit 0 +fi +#################################### +#Reference Used in this compilation# +####################################################################### +# Compiling The Forgotten Server in Debian GNU/Linux By: Don Daniello ############ +# URL: http://otland.net/content/compiling-forgotten-server-debian-gnu-linux-16/ # +################################################################################## diff --git a/condition.cpp b/condition.cpp index 7857f07..5d4cb9d 100644 --- a/condition.cpp +++ b/condition.cpp @@ -57,7 +57,7 @@ bool Condition::setParam(ConditionParam_t param, int32_t value) bool Condition::unserialize(PropStream& propStream) { uint8_t attrType; - while(propStream.GET_UCHAR(attrType) && attrType != CONDITIONATTR_END) + while(propStream.getByte(attrType) && attrType != CONDITIONATTR_END) { if(!unserializeProp((ConditionAttr_t)attrType, propStream)) return false; @@ -73,7 +73,7 @@ bool Condition::unserializeProp(ConditionAttr_t attr, PropStream& propStream) case CONDITIONATTR_TYPE: { int32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; conditionType = (ConditionType_t)value; @@ -83,7 +83,7 @@ bool Condition::unserializeProp(ConditionAttr_t attr, PropStream& propStream) case CONDITIONATTR_ID: { int32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; id = (ConditionId_t)value; @@ -93,7 +93,7 @@ bool Condition::unserializeProp(ConditionAttr_t attr, PropStream& propStream) case CONDITIONATTR_TICKS: { int32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; ticks = value; @@ -103,17 +103,17 @@ bool Condition::unserializeProp(ConditionAttr_t attr, PropStream& propStream) case CONDITIONATTR_BUFF: { int32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; - buff = value != 0; + buff = (value != 0); return true; } case CONDITIONATTR_SUBID: { int32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; subId = value; @@ -132,20 +132,20 @@ bool Condition::unserializeProp(ConditionAttr_t attr, PropStream& propStream) bool Condition::serialize(PropWriteStream& propWriteStream) { - propWriteStream.ADD_UCHAR(CONDITIONATTR_TYPE); - propWriteStream.ADD_VALUE((int32_t)conditionType); + propWriteStream.addByte(CONDITIONATTR_TYPE); + propWriteStream.addType((int32_t)conditionType); - propWriteStream.ADD_UCHAR(CONDITIONATTR_ID); - propWriteStream.ADD_VALUE((int32_t)id); + propWriteStream.addByte(CONDITIONATTR_ID); + propWriteStream.addType((int32_t)id); - propWriteStream.ADD_UCHAR(CONDITIONATTR_TICKS); - propWriteStream.ADD_VALUE((int32_t)ticks); + propWriteStream.addByte(CONDITIONATTR_TICKS); + propWriteStream.addType((int32_t)ticks); - propWriteStream.ADD_UCHAR(CONDITIONATTR_BUFF); - propWriteStream.ADD_VALUE((int32_t)buff ? 1 : 0); + propWriteStream.addByte(CONDITIONATTR_BUFF); + propWriteStream.addType((int32_t)buff ? 1 : 0); - propWriteStream.ADD_UCHAR(CONDITIONATTR_SUBID); - propWriteStream.ADD_VALUE((int32_t)subId); + propWriteStream.addByte(CONDITIONATTR_SUBID); + propWriteStream.addType((int32_t)subId); return true; } @@ -156,7 +156,7 @@ void Condition::setTicks(int32_t _ticks) endTime = OTSYS_TIME() + _ticks; } -bool Condition::startCondition(Creature* creature) +bool Condition::startCondition(Creature*) { if(ticks > 0) endTime = OTSYS_TIME() + ticks; @@ -190,7 +190,7 @@ Condition* Condition::createCondition(ConditionId_t _id, ConditionType_t _type, case CONDITION_DAZZLED: case CONDITION_CURSED: case CONDITION_DROWN: - case CONDITION_PHYSICAL: + case CONDITION_BLEEDING: return new ConditionDamage(_id, _type, _buff, _subId); case CONDITION_HASTE: @@ -223,6 +223,7 @@ Condition* Condition::createCondition(ConditionId_t _id, ConditionType_t _type, case CONDITION_DRUNK: case CONDITION_PACIFIED: case CONDITION_GAMEMASTER: + case CONDITION_SPELLCOOLDOWN: return new ConditionGeneric(_id, _type, _ticks, _buff, _subId); default: @@ -235,42 +236,42 @@ Condition* Condition::createCondition(ConditionId_t _id, ConditionType_t _type, Condition* Condition::createCondition(PropStream& propStream) { uint8_t attr = 0; - if(!propStream.GET_UCHAR(attr) || attr != CONDITIONATTR_TYPE) + if(!propStream.getByte(attr) || attr != CONDITIONATTR_TYPE) return NULL; uint32_t _type = 0; - if(!propStream.GET_ULONG(_type)) + if(!propStream.getLong(_type)) return NULL; - if(!propStream.GET_UCHAR(attr) || attr != CONDITIONATTR_ID) + if(!propStream.getByte(attr) || attr != CONDITIONATTR_ID) return NULL; uint32_t _id = 0; - if(!propStream.GET_ULONG(_id)) + if(!propStream.getLong(_id)) return NULL; - if(!propStream.GET_UCHAR(attr) || attr != CONDITIONATTR_TICKS) + if(!propStream.getByte(attr) || attr != CONDITIONATTR_TICKS) return NULL; uint32_t _ticks = 0; - if(!propStream.GET_ULONG(_ticks)) + if(!propStream.getLong(_ticks)) return NULL; - if(!propStream.GET_UCHAR(attr) || attr != CONDITIONATTR_BUFF) + if(!propStream.getByte(attr) || attr != CONDITIONATTR_BUFF) return NULL; uint32_t _buff = 0; - if(!propStream.GET_ULONG(_buff)) + if(!propStream.getLong(_buff)) return NULL; - if(!propStream.GET_UCHAR(attr) || attr != CONDITIONATTR_SUBID) + if(!propStream.getByte(attr) || attr != CONDITIONATTR_SUBID) return NULL; uint32_t _subId = 0; - if(!propStream.GET_ULONG(_subId)) + if(!propStream.getLong(_subId)) return NULL; - return createCondition((ConditionId_t)_id, (ConditionType_t)_type, _ticks, 0, _buff != 0, _subId); + return createCondition((ConditionId_t)_id, (ConditionType_t)_type, _ticks, 0, (_buff != 0), _subId); } bool Condition::updateCondition(const Condition* addCondition) @@ -290,10 +291,10 @@ Icons_t Condition::getIcons() const ConditionGeneric::ConditionGeneric(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, bool _buff, uint32_t _subId): Condition(_id, _type, _ticks, _buff, _subId) { - // + // TODO: get rid of this? } -void ConditionGeneric::addCondition(Creature* creature, const Condition* addCondition) +void ConditionGeneric::addCondition(Creature*, const Condition* addCondition) { if(updateCondition(addCondition)) setTicks(addCondition->getTicks()); @@ -375,7 +376,7 @@ bool ConditionAttributes::unserializeProp(ConditionAttr_t attr, PropStream& prop case CONDITIONATTR_SKILLS: { int32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; skills[currentSkill++] = value; @@ -385,7 +386,7 @@ bool ConditionAttributes::unserializeProp(ConditionAttr_t attr, PropStream& prop case CONDITIONATTR_STATS: { int32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; stats[currentStat++] = value; @@ -406,14 +407,14 @@ bool ConditionAttributes::serialize(PropWriteStream& propWriteStream) for(int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { - propWriteStream.ADD_UCHAR(CONDITIONATTR_SKILLS); - propWriteStream.ADD_VALUE(skills[i]); + propWriteStream.addByte(CONDITIONATTR_SKILLS); + propWriteStream.addType(skills[i]); } for(int32_t i = STAT_FIRST; i <= STAT_LAST; ++i) { - propWriteStream.ADD_UCHAR(CONDITIONATTR_STATS); - propWriteStream.ADD_VALUE(stats[i]); + propWriteStream.addByte(CONDITIONATTR_STATS); + propWriteStream.addType(stats[i]); } return true; @@ -489,7 +490,7 @@ bool ConditionAttributes::executeCondition(Creature* creature, int32_t interval) return ConditionGeneric::executeCondition(creature, interval); } -void ConditionAttributes::endCondition(Creature* creature, ConditionEnd_t reason) +void ConditionAttributes::endCondition(Creature* creature, ConditionEnd_t) { Player* player = creature->getPlayer(); if(!player) @@ -637,7 +638,7 @@ ConditionGeneric(_id, _type, _ticks, _buff, _subId) healthTicks = manaTicks = 1000; } -void ConditionRegeneration::addCondition(Creature* creature, const Condition* addCondition) +void ConditionRegeneration::addCondition(Creature*, const Condition* addCondition) { if(!updateCondition(addCondition)) return; @@ -659,7 +660,7 @@ bool ConditionRegeneration::unserializeProp(ConditionAttr_t attr, PropStream& pr case CONDITIONATTR_HEALTHTICKS: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; healthTicks = value; @@ -669,7 +670,7 @@ bool ConditionRegeneration::unserializeProp(ConditionAttr_t attr, PropStream& pr case CONDITIONATTR_HEALTHGAIN: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; healthGain = value; @@ -679,7 +680,7 @@ bool ConditionRegeneration::unserializeProp(ConditionAttr_t attr, PropStream& pr case CONDITIONATTR_MANATICKS: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; manaTicks = value; @@ -689,7 +690,7 @@ bool ConditionRegeneration::unserializeProp(ConditionAttr_t attr, PropStream& pr case CONDITIONATTR_MANAGAIN: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; manaGain = value; @@ -708,36 +709,48 @@ bool ConditionRegeneration::serialize(PropWriteStream& propWriteStream) if(!ConditionGeneric::serialize(propWriteStream)) return false; - propWriteStream.ADD_UCHAR(CONDITIONATTR_HEALTHTICKS); - propWriteStream.ADD_VALUE(healthTicks); + propWriteStream.addByte(CONDITIONATTR_HEALTHTICKS); + propWriteStream.addType(healthTicks); - propWriteStream.ADD_UCHAR(CONDITIONATTR_HEALTHGAIN); - propWriteStream.ADD_VALUE(healthGain); + propWriteStream.addByte(CONDITIONATTR_HEALTHGAIN); + propWriteStream.addType(healthGain); - propWriteStream.ADD_UCHAR(CONDITIONATTR_MANATICKS); - propWriteStream.ADD_VALUE(manaTicks); + propWriteStream.addByte(CONDITIONATTR_MANATICKS); + propWriteStream.addType(manaTicks); - propWriteStream.ADD_UCHAR(CONDITIONATTR_MANAGAIN); - propWriteStream.ADD_VALUE(manaGain); + propWriteStream.addByte(CONDITIONATTR_MANAGAIN); + propWriteStream.addType(manaGain); return true; } bool ConditionRegeneration::executeCondition(Creature* creature, int32_t interval) { - internalHealthTicks += interval; internalManaTicks += interval; + internalHealthTicks += interval; if(creature->getZone() != ZONE_PROTECTION) { if(internalHealthTicks >= healthTicks) { internalHealthTicks = 0; - creature->changeHealth(healthGain); + if(healthGain && creature->getHealth() < creature->getMaxHealth()) + { + if(getSubId() != 0) + g_game.combatChangeHealth(COMBAT_HEALING, creature, creature, healthGain); + else + creature->changeHealth(healthGain); + } } if(internalManaTicks >= manaTicks) { internalManaTicks = 0; - creature->changeMana(manaGain); + if(manaGain && creature->getMana() < creature->getMaxMana()) + { + if(getSubId() != 0) + g_game.combatChangeMana(creature, creature, manaGain); + else + creature->changeMana(manaGain); + } } } @@ -778,7 +791,7 @@ ConditionGeneric(_id, _type, _ticks, _buff, _subId) internalSoulTicks = soulTicks = soulGain = 0; } -void ConditionSoul::addCondition(Creature* creature, const Condition* addCondition) +void ConditionSoul::addCondition(Creature*, const Condition* addCondition) { if(!updateCondition(addCondition)) return; @@ -797,7 +810,7 @@ bool ConditionSoul::unserializeProp(ConditionAttr_t attr, PropStream& propStream case CONDITIONATTR_SOULGAIN: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; soulGain = value; @@ -807,7 +820,7 @@ bool ConditionSoul::unserializeProp(ConditionAttr_t attr, PropStream& propStream case CONDITIONATTR_SOULTICKS: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; soulTicks = value; @@ -826,11 +839,11 @@ bool ConditionSoul::serialize(PropWriteStream& propWriteStream) if(!ConditionGeneric::serialize(propWriteStream)) return false; - propWriteStream.ADD_UCHAR(CONDITIONATTR_SOULGAIN); - propWriteStream.ADD_VALUE(soulGain); + propWriteStream.addByte(CONDITIONATTR_SOULGAIN); + propWriteStream.addType(soulGain); - propWriteStream.ADD_UCHAR(CONDITIONATTR_SOULTICKS); - propWriteStream.ADD_VALUE(soulTicks); + propWriteStream.addByte(CONDITIONATTR_SOULTICKS); + propWriteStream.addType(soulTicks); return true; } @@ -888,11 +901,11 @@ bool ConditionDamage::setParam(ConditionParam_t param, int32_t value) return true; case CONDITIONPARAM_FORCEUPDATE: - forceUpdate = value; + forceUpdate = (value != 0); return true; case CONDITIONPARAM_DELAYED: - delayed = value; + delayed = (value != 0); return true; case CONDITIONPARAM_MAXVALUE: @@ -915,6 +928,10 @@ bool ConditionDamage::setParam(ConditionParam_t param, int32_t value) periodDamage = value; break; + case CONDITIONPARAM_FIELD: + field = (value != 0); + break; + default: break; } @@ -929,7 +946,7 @@ bool ConditionDamage::unserializeProp(ConditionAttr_t attr, PropStream& propStre case CONDITIONATTR_DELAYED: { bool value = false; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; delayed = value; @@ -939,7 +956,7 @@ bool ConditionDamage::unserializeProp(ConditionAttr_t attr, PropStream& propStre case CONDITIONATTR_PERIODDAMAGE: { int32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; periodDamage = value; @@ -949,7 +966,7 @@ bool ConditionDamage::unserializeProp(ConditionAttr_t attr, PropStream& propStre case CONDITIONATTR_OWNER: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; owner = value; @@ -959,7 +976,7 @@ bool ConditionDamage::unserializeProp(ConditionAttr_t attr, PropStream& propStre case CONDITIONATTR_INTERVALDATA: { IntervalInfo damageInfo; - if(!propStream.GET_VALUE(damageInfo)) + if(!propStream.getType(damageInfo)) return false; damageList.push_back(damageInfo); @@ -981,19 +998,19 @@ bool ConditionDamage::serialize(PropWriteStream& propWriteStream) if(!Condition::serialize(propWriteStream)) return false; - propWriteStream.ADD_UCHAR(CONDITIONATTR_DELAYED); - propWriteStream.ADD_VALUE(delayed); + propWriteStream.addByte(CONDITIONATTR_DELAYED); + propWriteStream.addType(delayed); - propWriteStream.ADD_UCHAR(CONDITIONATTR_PERIODDAMAGE); - propWriteStream.ADD_VALUE(periodDamage); + propWriteStream.addByte(CONDITIONATTR_PERIODDAMAGE); + propWriteStream.addType(periodDamage); - propWriteStream.ADD_UCHAR(CONDITIONATTR_OWNER); - propWriteStream.ADD_VALUE(owner); + propWriteStream.addByte(CONDITIONATTR_OWNER); + propWriteStream.addType(owner); for(DamageList::const_iterator it = damageList.begin(); it != damageList.end(); ++it) { - propWriteStream.ADD_UCHAR(CONDITIONATTR_INTERVALDATA); - propWriteStream.ADD_VALUE(*it); + propWriteStream.addByte(CONDITIONATTR_INTERVALDATA); + propWriteStream.addType(*it); } return true; @@ -1152,9 +1169,12 @@ bool ConditionDamage::doDamage(Creature* creature, int32_t damage) if(creature->isSuppress(getType())) return true; - CombatType_t combatType = Combat::ConditionToDamageType(conditionType); Creature* attacker = g_game.getCreatureByID(owner); - if(g_game.combatBlockHit(combatType, attacker, creature, damage, false, false)) + if(damage < 0 && attacker && attacker->getPlayer() && creature->getPlayer() && creature->getPlayer()->getSkull() != SKULL_BLACK) + damage = damage / 2; + + CombatType_t combatType = Combat::ConditionToDamageType(conditionType); + if(g_game.combatBlockHit(combatType, attacker, creature, damage, false, false, field)) return false; return g_game.combatChangeHealth(combatType, attacker, creature, damage); @@ -1246,6 +1266,9 @@ Icons_t ConditionDamage::getIcons() const case CONDITION_DROWN: return ICON_DROWNING; + case CONDITION_BLEEDING: + return ICON_BLEED; + default: break; } @@ -1255,14 +1278,23 @@ Icons_t ConditionDamage::getIcons() const void ConditionDamage::generateDamageList(int32_t amount, int32_t start, std::list& list) { + amount = std::abs(amount); + start = std::abs(start); + if(start >= amount) + { + list.push_back(start); + return; + } + int32_t sum = 0, med = 0; float x1, x2; - - amount = std::abs(amount); for(int32_t i = start; i > 0; --i) { med = ((start + 1 - i) * amount) / start; - do + x1 = std::fabs(1.0 - (((float)sum) + i) / med); + x2 = std::fabs(1.0 - (((float)sum) / med)); + + while(x1 < x2) { sum += i; list.push_back(i); @@ -1270,7 +1302,6 @@ void ConditionDamage::generateDamageList(int32_t amount, int32_t start, std::lis x1 = std::fabs(1.0 - (((float)sum) + i) / med); x2 = std::fabs(1.0 - (((float)sum) / med)); } - while(x1 < x2); } } @@ -1325,7 +1356,7 @@ bool ConditionSpeed::unserializeProp(ConditionAttr_t attr, PropStream& propStrea case CONDITIONATTR_SPEEDDELTA: { int32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; speedDelta = value; @@ -1335,7 +1366,7 @@ bool ConditionSpeed::unserializeProp(ConditionAttr_t attr, PropStream& propStrea case CONDITIONATTR_FORMULA_MINA: { float value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; mina = value; @@ -1345,7 +1376,7 @@ bool ConditionSpeed::unserializeProp(ConditionAttr_t attr, PropStream& propStrea case CONDITIONATTR_FORMULA_MINB: { float value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; minb = value; @@ -1355,7 +1386,7 @@ bool ConditionSpeed::unserializeProp(ConditionAttr_t attr, PropStream& propStrea case CONDITIONATTR_FORMULA_MAXA: { float value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; maxa = value; @@ -1365,7 +1396,7 @@ bool ConditionSpeed::unserializeProp(ConditionAttr_t attr, PropStream& propStrea case CONDITIONATTR_FORMULA_MAXB: { float value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; maxb = value; @@ -1384,20 +1415,20 @@ bool ConditionSpeed::serialize(PropWriteStream& propWriteStream) if(!ConditionOutfit::serialize(propWriteStream)) return false; - propWriteStream.ADD_UCHAR(CONDITIONATTR_SPEEDDELTA); - propWriteStream.ADD_VALUE(speedDelta); + propWriteStream.addByte(CONDITIONATTR_SPEEDDELTA); + propWriteStream.addType(speedDelta); - propWriteStream.ADD_UCHAR(CONDITIONATTR_FORMULA_MINA); - propWriteStream.ADD_VALUE(mina); + propWriteStream.addByte(CONDITIONATTR_FORMULA_MINA); + propWriteStream.addType(mina); - propWriteStream.ADD_UCHAR(CONDITIONATTR_FORMULA_MINB); - propWriteStream.ADD_VALUE(minb); + propWriteStream.addByte(CONDITIONATTR_FORMULA_MINB); + propWriteStream.addType(minb); - propWriteStream.ADD_UCHAR(CONDITIONATTR_FORMULA_MAXA); - propWriteStream.ADD_VALUE(maxa); + propWriteStream.addByte(CONDITIONATTR_FORMULA_MAXA); + propWriteStream.addType(maxa); - propWriteStream.ADD_UCHAR(CONDITIONATTR_FORMULA_MAXB); - propWriteStream.ADD_VALUE(maxb); + propWriteStream.addByte(CONDITIONATTR_FORMULA_MAXB); + propWriteStream.addType(maxb); return true; } @@ -1416,8 +1447,8 @@ bool ConditionSpeed::startCondition(Creature* creature) void ConditionSpeed::endCondition(Creature* creature, ConditionEnd_t reason) { - g_game.changeSpeed(creature, -speedDelta); ConditionOutfit::endCondition(creature, reason); + g_game.changeSpeed(creature, -speedDelta); } void ConditionSpeed::addCondition(Creature* creature, const Condition* addCondition) @@ -1484,7 +1515,7 @@ bool ConditionOutfit::unserializeProp(ConditionAttr_t attr, PropStream& propStre { case CONDITIONATTR_OUTFIT: { - if(!propStream.GET_VALUE(outfit)) + if(!propStream.getType(outfit)) return false; outfits.push_back(outfit); @@ -1505,8 +1536,8 @@ bool ConditionOutfit::serialize(PropWriteStream& propWriteStream) for(std::vector::const_iterator it = outfits.begin(); it != outfits.end(); ++it) { - propWriteStream.ADD_UCHAR(CONDITIONATTR_OUTFIT); - propWriteStream.ADD_VALUE(*it); + propWriteStream.addByte(CONDITIONATTR_OUTFIT); + propWriteStream.addType(*it); } return true; @@ -1529,7 +1560,7 @@ void ConditionOutfit::changeOutfit(Creature* creature, int32_t index/* = -1*/) g_game.internalCreatureChangeOutfit(creature, outfits[index], true); } -void ConditionOutfit::endCondition(Creature* creature, ConditionEnd_t reason) +void ConditionOutfit::endCondition(Creature* creature, ConditionEnd_t) { if(!outfits.empty()) g_game.internalCreatureChangeOutfit(creature, creature->getDefaultOutfit(), true); @@ -1585,9 +1616,9 @@ bool ConditionLight::executeCondition(Creature* creature, int32_t interval) return Condition::executeCondition(creature, interval); } -void ConditionLight::endCondition(Creature* creature, ConditionEnd_t reason) +void ConditionLight::endCondition(Creature* creature, ConditionEnd_t) { - creature->setNormalCreatureLight(); + creature->resetLight(); g_game.changeLight(creature); } @@ -1636,7 +1667,7 @@ bool ConditionLight::unserializeProp(ConditionAttr_t attr, PropStream& propStrea case CONDITIONATTR_LIGHTCOLOR: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; lightInfo.color = value; @@ -1646,7 +1677,7 @@ bool ConditionLight::unserializeProp(ConditionAttr_t attr, PropStream& propStrea case CONDITIONATTR_LIGHTLEVEL: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; lightInfo.level = value; @@ -1656,7 +1687,7 @@ bool ConditionLight::unserializeProp(ConditionAttr_t attr, PropStream& propStrea case CONDITIONATTR_LIGHTTICKS: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; internalLightTicks = value; @@ -1666,7 +1697,7 @@ bool ConditionLight::unserializeProp(ConditionAttr_t attr, PropStream& propStrea case CONDITIONATTR_LIGHTINTERVAL: { uint32_t value = 0; - if(!propStream.GET_VALUE(value)) + if(!propStream.getType(value)) return false; lightChangeInterval = value; @@ -1685,17 +1716,17 @@ bool ConditionLight::serialize(PropWriteStream& propWriteStream) if(!Condition::serialize(propWriteStream)) return false; - propWriteStream.ADD_UCHAR(CONDITIONATTR_LIGHTCOLOR); - propWriteStream.ADD_VALUE(lightInfo.color); + propWriteStream.addByte(CONDITIONATTR_LIGHTCOLOR); + propWriteStream.addType(lightInfo.color); - propWriteStream.ADD_UCHAR(CONDITIONATTR_LIGHTLEVEL); - propWriteStream.ADD_VALUE(lightInfo.level); + propWriteStream.addByte(CONDITIONATTR_LIGHTLEVEL); + propWriteStream.addType(lightInfo.level); - propWriteStream.ADD_UCHAR(CONDITIONATTR_LIGHTTICKS); - propWriteStream.ADD_VALUE(internalLightTicks); + propWriteStream.addByte(CONDITIONATTR_LIGHTTICKS); + propWriteStream.addType(internalLightTicks); - propWriteStream.ADD_UCHAR(CONDITIONATTR_LIGHTINTERVAL); - propWriteStream.ADD_VALUE(lightChangeInterval); + propWriteStream.addByte(CONDITIONATTR_LIGHTINTERVAL); + propWriteStream.addType(lightChangeInterval); return true; } diff --git a/condition.h b/condition.h index dffbde0..1511209 100644 --- a/condition.h +++ b/condition.h @@ -37,7 +37,7 @@ enum ConditionType_t CONDITION_POISON = 1 << 0, CONDITION_FIRE = 1 << 1, CONDITION_ENERGY = 1 << 2, - CONDITION_PHYSICAL = 1 << 3, + CONDITION_BLEEDING = 1 << 3, CONDITION_HASTE = 1 << 4, CONDITION_PARALYZE = 1 << 5, CONDITION_OUTFIT = 1 << 6, @@ -57,7 +57,8 @@ enum ConditionType_t CONDITION_CURSED = 1 << 20, CONDITION_PACIFIED = 1 << 21, CONDITION_GAMEMASTER = 1 << 22, - CONDITION_HUNTING = 1 << 23 + CONDITION_HUNTING = 1 << 23, + CONDITION_SPELLCOOLDOWN = 1 << 24 }; enum ConditionEnd_t @@ -115,8 +116,8 @@ class Condition virtual bool startCondition(Creature* creature); virtual bool executeCondition(Creature* creature, int32_t interval); - virtual void endCondition(Creature* creature, ConditionEnd_t reason) {} - virtual void addCondition(Creature* creature, const Condition* condition) {} + virtual void endCondition(Creature*, ConditionEnd_t) {} + virtual void addCondition(Creature*, const Condition*) {} virtual Icons_t getIcons() const; ConditionId_t getId() const {return id;} @@ -282,7 +283,7 @@ class ConditionDamage: public Condition bool getNextDamage(int32_t& damage); bool doDamage(Creature* creature, int32_t damage); - bool forceUpdate, delayed; + bool delayed, forceUpdate, field; int32_t maxDamage, minDamage, startDamage, periodDamage, periodDamageTick, tickInterval; uint32_t owner; diff --git a/config.lua b/config.lua.dist similarity index 58% rename from config.lua rename to config.lua.dist index aa0cf50..05112af 100644 --- a/config.lua +++ b/config.lua.dist @@ -4,13 +4,20 @@ accountManager = true namelockManager = true newPlayerChooseVoc = false - newPlayerSpawnPosX = 95 - newPlayerSpawnPosY = 117 + newPlayerSpawnPosX = 857 + newPlayerSpawnPosY = 964 newPlayerSpawnPosZ = 7 newPlayerTownId = 1 newPlayerLevel = 1 newPlayerMagicLevel = 0 generateAccountNumber = false + generateAccountSalt = false + + -- Rook system + useRookSystem = false + rookTownId = 1 + rookLevelToGetRooked = 5 + rookLevelToLeaveRook = 8 -- Unjustified kills -- NOTE: *Banishment and *BlackSkull variables are >summed up< @@ -19,21 +26,24 @@ -- Auto banishing works only if useBlackSkull set to negative. -- advancedFragList is not advised if you use huge frags -- requirements. + useFragHandler = true redSkullLength = 30 * 24 * 60 * 60 blackSkullLength = 45 * 24 * 60 * 60 - dailyFragsToRedSkull = 3 - weeklyFragsToRedSkull = 5 - monthlyFragsToRedSkull = 10 - dailyFragsToBlackSkull = dailyFragsToRedSkull - weeklyFragsToBlackSkull = weeklyFragsToRedSkull - monthlyFragsToBlackSkull = monthlyFragsToRedSkull - dailyFragsToBanishment = dailyFragsToRedSkull - weeklyFragsToBanishment = weeklyFragsToRedSkull - monthlyFragsToBanishment = monthlyFragsToRedSkull + fragsLimit = 24 * 60 * 60 + fragsSecondLimit = 7 * 24 * 60 * 60 + fragsThirdLimit = 30 * 24 * 60 * 60 + fragsToRedSkull = 3 + fragsSecondToRedSkull = 5 + fragsThirdToRedSkull = 10 + fragsToBlackSkull = fragsToRedSkull + fragsSecondToBlackSkull = fragsSecondToRedSkull + fragsThirdToBlackSkull = fragsThirdToRedSkull + fragsToBanishment = fragsToRedSkull + fragsSecondToBanishment = fragsSecondToRedSkull + fragsThirdToBanishment = fragsThirdToRedSkull blackSkulledDeathHealth = 40 blackSkulledDeathMana = 0 useBlackSkull = true - useFragHandler = true advancedFragList = false -- Banishments @@ -45,17 +55,14 @@ banLength = 7 * 24 * 60 * 60 killsBanLength = 7 * 24 * 60 * 60 finalBanLength = 30 * 24 * 60 * 60 - ipBanishmentLength = 1 * 24 * 60 * 60 - broadcastBanishments = true - maxViolationCommentSize = 200 - violationNameReportActionType = 2 - autoBanishUnknownBytes = false + ipBanLength = 1 * 24 * 60 * 60 + allowedMaxSizePackets = 20 -- Battle - -- NOTE: showHealingDamageForMonsters inheritates from showHealingDamage. + -- NOTE: showHealth/ManaChangeForMonsters inherites from showHealth/ManaChange. -- loginProtectionPeriod is the famous Tibia anti-magebomb system. -- deathLostPercent set to nil enables manual mode. - worldType = "pvp" + worldType = "open" protectionLevel = 1 pvpTileIgnoreLevelAndVocationProtection = true pzLocked = 60 * 1000 @@ -68,11 +75,12 @@ removeRuneCharges = true whiteSkullTime = 15 * 60 * 1000 noDamageToSameLookfeet = false - showHealingDamage = false - showHealingDamageForMonsters = false + showHealthChange = false + showManaChange = false + showHealthChangeForMonsters = false + showManaChangeForMonsters = false fieldOwnershipDuration = 5 * 1000 stopAttackingAtExit = false - oldConditionAccuracy = false loginProtectionPeriod = 10 * 1000 deathLostPercent = 10 stairhopDelay = 2 * 1000 @@ -80,25 +88,26 @@ deathContainerId = 1987 gainExperienceColor = 215 addManaSpentInPvPZone = true + recoverManaAfterDeathInPvPZone = false squareColor = 0 allowFightback = true + fistBaseAttack = 7 + useFairfightReduction = true -- Connection config worldId = 0 ip = "127.0.0.1" - bindOnlyConfiguredIpAddress = false + bindOnlyGlobalAddress = false loginPort = 7171 - gamePort = 7172 - adminPort = 7171 - statusPort = 7171 - loginTries = 10 + gamePort = "7172" + loginTries = 3 retryTimeout = 5 * 1000 loginTimeout = 60 * 1000 maxPlayers = 1000 motd = "Welcome to the Forgotten Server!" displayOnOrOffAtCharlist = false onePlayerOnlinePerAccount = true - allowClones = false + allowClones = 0 serverName = "Forgotten" loginMessage = "Welcome to the Forgotten Server!" statusTimeout = 5 * 60 * 1000 @@ -107,20 +116,32 @@ loginOnlyWithLoginServer = false premiumPlayerSkipWaitList = false + -- RSA + -- NOTE: These should not be changed unless you know what your doing! + -- Prime1 - known as p; Prime2 - known as q; Public - known as e; + -- Modulus - known as n; Private - known as d. + rsaPrime1 = "14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113" + rsaPrime2 = "7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101" + rsaPublic = "65537" + rsaModulus = "109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413" + rsaPrivate = "46730330223584118622160180015036832148732986808519344675210555262940258739805766860224610646919605860206328024326703361630109888417839241959507572247284807035235569619173792292786907845791904955103601652822519121908367187885509270025388641700821735345222087940578381210879116823013776808975766851829020659073" + -- Database -- NOTE: sqlFile is used only by sqlite database, and sqlKeepAlive by mysql database. -- To disable sqlKeepAlive such as mysqlReadTimeout use 0 value. + -- encryptionType can be plain, md5, sha1, sha256, sha512. sqlType = "sqlite" sqlHost = "localhost" sqlPort = 3306 sqlUser = "root" sqlPass = "" sqlDatabase = "theforgottenserver" - sqlFile = "forgottenserver.s3db" + sqlFile = "theforgottenserver.s3db" sqlKeepAlive = 0 mysqlReadTimeout = 10 mysqlWriteTimeout = 10 - encryptionType = "plain" + mysqlReconnectionAttempts = 3 + encryptionType = "sha1" -- Deathlist deathListEnabled = true @@ -148,36 +169,57 @@ houseRentAsPrice = false housePriceAsRent = false housePriceEachSquare = 1000 + houseSkipInitialRent = true houseRentPeriod = "never" houseCleanOld = 0 guildHalls = false + houseProtection = true -- Item usage timeBetweenActions = 200 timeBetweenExActions = 1000 + timeBetweenCustomActions = 500 hotkeyAimbotEnabled = true + tibiaClassicSlots = true + canOnlyRopePlayers = true -- Map -- NOTE: storeTrash costs more memory, but will perform alot faster cleaning. - mapName = "forgotten" - mapAuthor = "Komic" + mapName = "tfs.otbm" + mapAuthor = "Siramix" randomizeTiles = true storeTrash = true cleanProtectedZones = true - mailboxDisabledTowns = "-1" + + -- Mailbox + mailboxDisabledTowns = "" + mailMaxAttempts = 20 + mailBlockPeriod = 60 * 60 * 1000 + mailAttemptsFadeTime = 10 * 60 * 1000 + + -- Market + marketEnabled = true + marketOfferDuration = 30 * 24 * 60 * 60 + premiumToCreateMarketOffer = true + checkExpiredMarketOffersEachMinutes = 60 + maxMarketOffersAtATimePerPlayer = 100 -- Process - -- NOTE: defaultPriority works only on Windows and niceLevel on *nix + -- NOTE: daemonize works only on *nix, same as niceLevel, while + -- defaultPriority works only on Windows. -- coresUsed are seperated by comma cores ids used by server process, -- default is -1, so it stays untouched (automaticaly assigned by OS). + daemonize = false defaultPriority = "high" niceLevel = 5 + serviceThreads = 1 coresUsed = "-1" -- Startup - optimizeDatabaseAtStartup = true - removePremiumOnInit = true + startupDatabaseOptimization = true + updatePremiumStateAtStartup = true confirmOutdatedVersion = false + skipItemsVersionCheck = false -- Spells formulaLevel = 5.0 @@ -185,6 +227,8 @@ bufferMutedOnSpellFailure = false spellNameInsteadOfWords = false emoteSpells = false + unifiedSpells = true + enableCooldowns = true -- Outfits allowChangeOutfit = true @@ -197,24 +241,39 @@ -- NOTE: promptExceptionTracerErrorBox works only with precompiled support feature, -- called "exception tracer" (__EXCEPTION_TRACER__ flag). dataDirectory = "data/" + logsDirectory = "data/logs/" bankSystem = true - displaySkillLevelOnAdvance = false promptExceptionTracerErrorBox = true - separateViplistPerCharacter = false maximumDoorLevel = 500 maxMessageBuffer = 4 + tradeLimit = 100 + useCapacity = true + + -- Depot + defaultDepotSizePremium = 2000 + defaultDepotSize = 1000 + + -- Mounts + useMounts = true + unmountPlayerInPz = true + + -- VIP list + separateVipListPerCharacter = false + vipListDefaultLimit = 20 + vipListDefaultPremiumLimit = 100 -- Saving-related - -- useHouseDataStorage usage may be found at README. + -- houseDataStorage usage may be found at README. + houseDataStorage = "binary" saveGlobalStorage = true - useHouseDataStorage = false storePlayerDirection = false + savePlayerData = true -- Loot -- monsterLootMessage 0 to disable, 1 - only party, 2 - only player, 3 - party or player (like Tibia's) checkCorpseOwner = true monsterLootMessage = 3 - monsterLootMessageType = 25 + monsterLootMessageType = 29 -- Ghost mode ghostModeInvisibleEffect = false @@ -223,10 +282,11 @@ -- Limits idleWarningTime = 14 * 60 * 1000 idleKickTime = 15 * 60 * 1000 - expireReportsAfterReads = 1 - playerQueryDeepness = 2 - maxItemsPerPZTile = 0 - maxItemsPerHouseTile = 0 + reportsExpirationAfterReads = 1 + playerQueryDeepness = -1 + tileLimit = 0 + protectionTileLimit = 0 + houseTileLimit = 0 -- Premium-related freePremium = false @@ -235,21 +295,30 @@ -- Blessings -- NOTE: blessingReduction* regards items/containers loss. -- eachBlessReduction is how much each bless reduces the experience/magic/skills loss. + -- pvpBlessingThreshold is damage percent received from PvP that is required to + -- enable pvpBlessing. + -- fairFightTimeRange is last X seconds from which damage to player counts. + -- Applies to pvp blessing. + blessings = true blessingOnlyPremium = true blessingReductionBase = 30 - blessingReductionDecreament = 5 + blessingReductionDecrement = 5 eachBlessReduction = 8 + pvpBlessingThreshold = 40 + fairFightTimeRange = 60 -- Rates -- NOTE: experienceStages configuration is located in data/XML/stages.xml. -- rateExperienceFromPlayers 0 to disable. + -- For constant spawn count increment, use exact values in rateSpawn* variables. experienceStages = false rateExperience = 5.0 rateExperienceFromPlayers = 0 rateSkill = 3.0 rateMagic = 3.0 rateLoot = 2.0 - rateSpawn = 1 + rateSpawnMin = 1 + rateSpawnMax = 1 -- Monster rates rateMonsterHealth = 1.0 @@ -269,15 +338,18 @@ -- NOTE: Stamina is stored in miliseconds, so seconds are multiplied by 1000. -- rateStaminaHits multiplies every hit done a creature, which are later -- multiplied by player attack speed. - -- rateStaminaGain is divider of every logged out second, eg: - -- 60000 / 3 = 20000 milliseconds, what gives 20 stamina seconds for 1 minute being logged off. - -- rateStaminaThresholdGain is divider for the premium stamina. + -- rateStaminaGain is multiplying every second of logged out time, eg: + -- 60 * 1000 / 3 = 20 seconds, what gives 1 stamina minute for 3 being logged off. + -- rateStaminaThresholdGain is dividing in case the normal gain (that is + -- multiplied by rateStaminaGain, btw.) passed above threshold, eg: + -- 60 * 1000 / 3 = 20 / 4 = 5 seconds (3 * 4 = 12 minutes for 1 stamina minute). -- staminaRatingLimit* is in minutes. rateStaminaLoss = 1 rateStaminaGain = 3 rateStaminaThresholdGain = 12 - staminaRatingLimitTop = 41 * 60 + staminaRatingLimitTop = 40 * 60 staminaRatingLimitBottom = 14 * 60 + staminaLootLimit = 14 * 60 rateStaminaAboveNormal = 1.5 rateStaminaUnderNormal = 0.5 staminaThresholdOnlyPremium = true @@ -298,12 +370,14 @@ -- if you want such a system please check out data/globalevents/globalevents.xml. globalSaveEnabled = false globalSaveHour = 8 + globalSaveMinute = 0 shutdownAtGlobalSave = true cleanMapAtGlobalSave = false -- Spawns deSpawnRange = 2 deSpawnRadius = 50 + monsterSpawnWalkback = true -- Summons maxPlayerSummons = 2 @@ -311,6 +385,7 @@ teleportPlayerSummons = false -- Status + statusPort = 7171 ownerName = "" ownerEmail = "@otland.net" url = "http://otland.net/" @@ -318,12 +393,31 @@ displayGamemastersWithOnlineCommand = false -- Logs - -- NOTE: This kind of logging does not work in GUI version. - -- For such, please compile the software with __GUI_LOGS__ flag. - adminLogsEnabled = false + disableLuaErrors = false displayPlayersLogging = true prefixChannelLogs = "" runFile = "" - outLogName = "" - errorLogName = "" - truncateLogsOnStartup = false + outputLog = "" + truncateLogOnStartup = false + + -- Manager + -- NOTE: managerPassword left blank disables manager. + managerPort = 7171 + managerLogs = true + managerPassword = "" + managerLocalhostOnly = true + managerConnectionsLimit = 1 + + -- Admin + -- NOTE: adminPassword left blank disables manager. + -- Set to anything if you set adminRequireLogin to false. + -- adminEncryption available options: rsa1024xtea; + -- remember to set correct data! + adminPort = 7171 + adminLogs = true + adminPassword = "" + adminLocalhostOnly = true + adminConnectionsLimit = 1 + adminRequireLogin = true + adminEncryption = "" + adminEncryptionData = "" diff --git a/configmanager.cpp b/configmanager.cpp index 296810b..b69b028 100644 --- a/configmanager.cpp +++ b/configmanager.cpp @@ -18,6 +18,7 @@ #include #include "configmanager.h" +#include "house.h" #include "tools.h" ConfigManager::ConfigManager() @@ -26,11 +27,13 @@ ConfigManager::ConfigManager() m_loaded = false; m_startup = true; + m_confNumber[ENCRYPTION] = ENCRYPTION_SHA256; m_confString[CONFIG_FILE] = getFilePath(FILE_TYPE_CONFIG, "config.lua"); - m_confBool[LOGIN_ONLY_LOGINSERVER] = false; - m_confNumber[LOGIN_PORT] = m_confNumber[GAME_PORT] = m_confNumber[ADMIN_PORT] = m_confNumber[STATUS_PORT] = 0; - m_confString[DATA_DIRECTORY] = m_confString[IP] = m_confString[RUNFILE] = m_confString[ERROR_LOG] = m_confString[OUT_LOG] = ""; + m_confNumber[LOGIN_PORT] = m_confNumber[ADMIN_PORT] = m_confNumber[MANAGER_PORT] = m_confNumber[STATUS_PORT] = 0; + m_confString[DATA_DIRECTORY] = m_confString[LOGS_DIRECTORY] = m_confString[IP] = m_confString[RUNFILE] = m_confString[OUTPUT_LOG] = ""; + m_confBool[LOGIN_ONLY_LOGINSERVER] = m_confBool[START_CLOSED] = m_confBool[DAEMONIZE] = false; + m_confBool[SCRIPT_SYSTEM] = true; } bool ConfigManager::load() @@ -38,10 +41,11 @@ bool ConfigManager::load() if(L) lua_close(L); - L = lua_open(); + L = luaL_newstate(); if(!L) return false; + luaL_openlibs(L); if(luaL_dofile(L, m_confString[CONFIG_FILE].c_str())) { lua_close(L); @@ -49,38 +53,39 @@ bool ConfigManager::load() return false; } - //parse config - if(!m_loaded) //info that must be loaded one time (unless we reset the modules involved) + if(!m_loaded) //info that must be loaded one time- unless we reset the modules involved { if(m_confString[DATA_DIRECTORY] == "") m_confString[DATA_DIRECTORY] = getGlobalString("dataDirectory", "data/"); + if(m_confString[LOGS_DIRECTORY] == "") + m_confString[LOGS_DIRECTORY] = getGlobalString("logsDirectory", "logs/"); + if(m_confString[IP] == "") m_confString[IP] = getGlobalString("ip", "127.0.0.1"); if(m_confNumber[LOGIN_PORT] == 0) m_confNumber[LOGIN_PORT] = getGlobalNumber("loginPort", 7171); - if(m_confNumber[GAME_PORT] == 0) - m_confNumber[GAME_PORT] = getGlobalNumber("gamePort", 7172); + if(m_confString[GAME_PORT] == "") + m_confString[GAME_PORT] = getGlobalString("gamePort", "7172"); if(m_confNumber[ADMIN_PORT] == 0) m_confNumber[ADMIN_PORT] = getGlobalNumber("adminPort", 7171); + if(m_confNumber[MANAGER_PORT] == 0) + m_confNumber[MANAGER_PORT] = getGlobalNumber("managerPort", 7171); + if(m_confNumber[STATUS_PORT] == 0) m_confNumber[STATUS_PORT] = getGlobalNumber("statusPort", 7171); if(m_confString[RUNFILE] == "") m_confString[RUNFILE] = getGlobalString("runFile", ""); - if(m_confString[OUT_LOG] == "") - m_confString[OUT_LOG] = getGlobalString("outLogName", ""); - - if(m_confString[ERROR_LOG] == "") - m_confString[ERROR_LOG] = getGlobalString("errorLogName", ""); + if(m_confString[OUTPUT_LOG] == "") + m_confString[OUTPUT_LOG] = getGlobalString("outputLog", ""); - m_confBool[BIND_IP_ONLY] = getGlobalBool("bindOnlyConfiguredIpAddress", false); - m_confBool[TRUNCATE_LOGS] = getGlobalBool("truncateLogsOnStartup", true); + m_confBool[TRUNCATE_LOG] = getGlobalBool("truncateLogOnStartup", true); #ifdef MULTI_SQL_DRIVERS m_confString[SQL_TYPE] = getGlobalString("sqlType", "sqlite"); #endif @@ -93,202 +98,262 @@ bool ConfigManager::load() m_confNumber[SQL_KEEPALIVE] = getGlobalNumber("sqlKeepAlive", 0); m_confNumber[MYSQL_READ_TIMEOUT] = getGlobalNumber("mysqlReadTimeout", 10); m_confNumber[MYSQL_WRITE_TIMEOUT] = getGlobalNumber("mysqlWriteTimeout", 10); - m_confBool[OPTIMIZE_DB_AT_STARTUP] = getGlobalBool("optimizeDatabaseAtStartup", true); - m_confString[MAP_NAME] = getGlobalString("mapName", "forgotten"); + m_confBool[OPTIMIZE_DATABASE] = getGlobalBool("startupDatabaseOptimization", true); + m_confString[MAP_NAME] = getGlobalString("mapName", "forgotten.otbm"); m_confBool[GLOBALSAVE_ENABLED] = getGlobalBool("globalSaveEnabled", true); + m_confNumber[SERVICE_THREADS] = getGlobalNumber("serviceThreads", 1); m_confNumber[GLOBALSAVE_H] = getGlobalNumber("globalSaveHour", 8); + m_confNumber[GLOBALSAVE_M] = getGlobalNumber("globalSaveMinute", 0); m_confString[HOUSE_RENT_PERIOD] = getGlobalString("houseRentPeriod", "monthly"); m_confNumber[WORLD_ID] = getGlobalNumber("worldId", 0); m_confBool[RANDOMIZE_TILES] = getGlobalBool("randomizeTiles", true); m_confBool[STORE_TRASH] = getGlobalBool("storeTrash", true); - m_confBool[EXPERIENCE_STAGES] = getGlobalBool("experienceStages", false); m_confString[DEFAULT_PRIORITY] = getGlobalString("defaultPriority", "high"); + m_confBool[BIND_ONLY_GLOBAL_ADDRESS] = getGlobalBool("bindOnlyGlobalAddress", false); m_confBool[GUILD_HALLS] = getGlobalBool("guildHalls", false); #ifndef __LOGIN_SERVER__ m_confBool[LOGIN_ONLY_LOGINSERVER] = getGlobalBool("loginOnlyWithLoginServer", false); #endif - m_confString[ENCRYPTION_TYPE] = getGlobalString("encryptionType", "plain"); - m_confNumber[ENCRYPTION] = ENCRYPTION_PLAIN; + m_confString[ENCRYPTION_TYPE] = getGlobalString("encryptionType", "sha256"); + m_confString[RSA_PRIME1] = getGlobalString("rsaPrime1", "14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113"); + m_confString[RSA_PRIME2] = getGlobalString("rsaPrime2", "7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101"); + m_confString[RSA_PUBLIC] = getGlobalString("rsaPublic", "65537"); + m_confString[RSA_MODULUS] = getGlobalString("rsaModulus", "109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413"); + m_confString[RSA_PRIVATE] = getGlobalString("rsaPrivate", "46730330223584118622160180015036832148732986808519344675210555262940258739805766860224610646919605860206328024326703361630109888417839241959507572247284807035235569619173792292786907845791904955103601652822519121908367187885509270025388641700821735345222087940578381210879116823013776808975766851829020659073"); + m_confNumber[MARKET_OFFER_DURATION] = getGlobalNumber("marketOfferDuration", 30 * 24 * 60 * 60); } - m_confString[MAP_AUTHOR] = getGlobalString("mapAuthor", "Unknown"); - m_confNumber[LOGIN_TRIES] = getGlobalNumber("loginTries", 3); - m_confNumber[RETRY_TIMEOUT] = getGlobalNumber("retryTimeout", 30 * 1000); - m_confNumber[LOGIN_TIMEOUT] = getGlobalNumber("loginTimeout", 5 * 1000); - m_confNumber[MAX_MESSAGEBUFFER] = getGlobalNumber("maxMessageBuffer", 4); - m_confNumber[MAX_PLAYERS] = getGlobalNumber("maxPlayers"); - m_confNumber[DEFAULT_DESPAWNRANGE] = getGlobalNumber("deSpawnRange", 2); - m_confNumber[DEFAULT_DESPAWNRADIUS] = getGlobalNumber("deSpawnRadius", 50); - m_confNumber[PZ_LOCKED] = getGlobalNumber("pzLocked", 60 * 1000); - m_confNumber[HUNTING_DURATION] = getGlobalNumber("huntingDuration", 60 * 1000); - m_confString[SERVER_NAME] = getGlobalString("serverName"); - m_confString[OWNER_NAME] = getGlobalString("ownerName"); - m_confString[OWNER_EMAIL] = getGlobalString("ownerEmail"); - m_confString[URL] = getGlobalString("url"); - m_confString[LOCATION] = getGlobalString("location"); - m_confString[MOTD] = getGlobalString("motd"); - m_confNumber[ALLOW_CLONES] = getGlobalNumber("allowClones", 0); - m_confDouble[RATE_EXPERIENCE] = getGlobalDouble("rateExperience", 1); - m_confDouble[RATE_SKILL] = getGlobalDouble("rateSkill", 1); - m_confDouble[RATE_MAGIC] = getGlobalDouble("rateMagic", 1); - m_confDouble[RATE_LOOT] = getGlobalDouble("rateLoot", 1); - m_confNumber[RATE_SPAWN] = getGlobalNumber("rateSpawn", 1); - m_confNumber[PARTY_RADIUS_X] = getGlobalNumber("experienceShareRadiusX", 30); - m_confNumber[PARTY_RADIUS_Y] = getGlobalNumber("experienceShareRadiusY", 30); - m_confNumber[PARTY_RADIUS_Z] = getGlobalNumber("experienceShareRadiusZ", 1); - m_confDouble[PARTY_DIFFERENCE] = getGlobalDouble("experienceShareLevelDifference", (double)2 / 3); - m_confNumber[SPAWNPOS_X] = getGlobalNumber("newPlayerSpawnPosX", 100); - m_confNumber[SPAWNPOS_Y] = getGlobalNumber("newPlayerSpawnPosY", 100); - m_confNumber[SPAWNPOS_Z] = getGlobalNumber("newPlayerSpawnPosZ", 7); - m_confNumber[SPAWNTOWN_ID] = getGlobalNumber("newPlayerTownId", 1); - m_confString[WORLD_TYPE] = getGlobalString("worldType", "pvp"); - m_confBool[ACCOUNT_MANAGER] = getGlobalBool("accountManager", true); - m_confBool[NAMELOCK_MANAGER] = getGlobalBool("namelockManager", false); - m_confNumber[START_LEVEL] = getGlobalNumber("newPlayerLevel", 1); - m_confNumber[START_MAGICLEVEL] = getGlobalNumber("newPlayerMagicLevel", 0); - m_confBool[START_CHOOSEVOC] = getGlobalBool("newPlayerChooseVoc", false); - m_confNumber[HOUSE_PRICE] = getGlobalNumber("housePriceEachSquare", 1000); - m_confNumber[WHITE_SKULL_TIME] = getGlobalNumber("whiteSkullTime", 15 * 60 * 1000); - m_confNumber[HIGHSCORES_TOP] = getGlobalNumber("highscoreDisplayPlayers", 10); - m_confNumber[HIGHSCORES_UPDATETIME] = getGlobalNumber("updateHighscoresAfterMinutes", 60); - m_confBool[ON_OR_OFF_CHARLIST] = getGlobalBool("displayOnOrOffAtCharlist", false); - m_confBool[ALLOW_CHANGEOUTFIT] = getGlobalBool("allowChangeOutfit", true); - m_confBool[ONE_PLAYER_ON_ACCOUNT] = getGlobalBool("onePlayerOnlinePerAccount", true); - m_confBool[CANNOT_ATTACK_SAME_LOOKFEET] = getGlobalBool("noDamageToSameLookfeet", false); - m_confBool[AIMBOT_HOTKEY_ENABLED] = getGlobalBool("hotkeyAimbotEnabled", true); - m_confNumber[ACTIONS_DELAY_INTERVAL] = getGlobalNumber("timeBetweenActions", 200); - m_confNumber[EX_ACTIONS_DELAY_INTERVAL] = getGlobalNumber("timeBetweenExActions", 1000); - m_confNumber[CRITICAL_HIT_CHANCE] = getGlobalNumber("criticalHitChance", 5); - m_confBool[REMOVE_WEAPON_AMMO] = getGlobalBool("removeWeaponAmmunition", true); - m_confBool[REMOVE_WEAPON_CHARGES] = getGlobalBool("removeWeaponCharges", true); - m_confBool[REMOVE_RUNE_CHARGES] = getGlobalBool("removeRuneCharges", true); - m_confDouble[RATE_PVP_EXPERIENCE] = getGlobalDouble("rateExperienceFromPlayers", 0); - m_confDouble[EFP_MIN_THRESHOLD] = getGlobalDouble("minLevelThresholdForKilledPlayer", 0.9f); - m_confDouble[EFP_MAX_THRESHOLD] = getGlobalDouble("maxLevelThresholdForKilledPlayer", 1.1f); - m_confBool[SHUTDOWN_AT_GLOBALSAVE] = getGlobalBool("shutdownAtGlobalSave", false); - m_confBool[CLEAN_MAP_AT_GLOBALSAVE] = getGlobalBool("cleanMapAtGlobalSave", true); - m_confBool[FREE_PREMIUM] = getGlobalBool("freePremium", false); - m_confNumber[PROTECTION_LEVEL] = getGlobalNumber("protectionLevel", 1); - m_confBool[ADMIN_LOGS_ENABLED] = getGlobalBool("adminLogsEnabled", false); - m_confNumber[STATUSQUERY_TIMEOUT] = getGlobalNumber("statusTimeout", 5 * 60 * 1000); - m_confBool[BROADCAST_BANISHMENTS] = getGlobalBool("broadcastBanishments", true); - m_confBool[GENERATE_ACCOUNT_NUMBER] = getGlobalBool("generateAccountNumber", true); - m_confBool[INGAME_GUILD_MANAGEMENT] = getGlobalBool("ingameGuildManagement", true); - m_confNumber[LEVEL_TO_FORM_GUILD] = getGlobalNumber("levelToFormGuild", 8); - m_confNumber[MIN_GUILDNAME] = getGlobalNumber("guildNameMinLength", 4); - m_confNumber[MAX_GUILDNAME] = getGlobalNumber("guildNameMaxLength", 20); - m_confNumber[LEVEL_TO_BUY_HOUSE] = getGlobalNumber("levelToBuyHouse", 1); - m_confNumber[HOUSES_PER_ACCOUNT] = getGlobalNumber("housesPerAccount", 0); - m_confBool[HOUSE_BUY_AND_SELL] = getGlobalBool("buyableAndSellableHouses", true); - m_confBool[REPLACE_KICK_ON_LOGIN] = getGlobalBool("replaceKickOnLogin", true); - m_confBool[HOUSE_NEED_PREMIUM] = getGlobalBool("houseNeedPremium", true); - m_confBool[HOUSE_RENTASPRICE] = getGlobalBool("houseRentAsPrice", false); - m_confBool[HOUSE_PRICEASRENT] = getGlobalBool("housePriceAsRent", false); - m_confNumber[RED_SKULL_LENGTH] = getGlobalNumber("redSkullLength", 30 * 24 * 60 * 60); - m_confNumber[BLACK_SKULL_LENGTH] = getGlobalNumber("blackSkullLength", 45 * 24 * 60 * 60); - m_confNumber[MAX_VIOLATIONCOMMENT_SIZE] = getGlobalNumber("maxViolationCommentSize", 60); - m_confNumber[NOTATIONS_TO_BAN] = getGlobalNumber("notationsToBan", 3); - m_confNumber[WARNINGS_TO_FINALBAN] = getGlobalNumber("warningsToFinalBan", 4); - m_confNumber[WARNINGS_TO_DELETION] = getGlobalNumber("warningsToDeletion", 5); - m_confNumber[BAN_LENGTH] = getGlobalNumber("banLength", 7 * 24 * 60 * 60); - m_confNumber[KILLS_BAN_LENGTH] = getGlobalNumber("killsBanLength", 7 * 24 * 60 * 60); - m_confNumber[FINALBAN_LENGTH] = getGlobalNumber("finalBanLength", 30 * 24 * 60 * 60); - m_confNumber[IPBANISHMENT_LENGTH] = getGlobalNumber("ipBanishmentLength", 1 * 24 * 60 * 60); - m_confBool[BANK_SYSTEM] = getGlobalBool("bankSystem", true); - m_confBool[PREMIUM_FOR_PROMOTION] = getGlobalBool("premiumForPromotion", true); - m_confBool[REMOVE_PREMIUM_ON_INIT] = getGlobalBool("removePremiumOnInit", true); - m_confBool[SHOW_HEALING_DAMAGE] = getGlobalBool("showHealingDamage", false); - m_confBool[TELEPORT_SUMMONS] = getGlobalBool("teleportAllSummons", false); - m_confBool[TELEPORT_PLAYER_SUMMONS] = getGlobalBool("teleportPlayerSummons", false); - m_confBool[PVP_TILE_IGNORE_PROTECTION] = getGlobalBool("pvpTileIgnoreLevelAndVocationProtection", true); - m_confBool[DISPLAY_CRITICAL_HIT] = getGlobalBool("displayCriticalHitNotify", false); - m_confBool[ADVANCING_SKILL_LEVEL] = getGlobalBool("displaySkillLevelOnAdvance", false); - m_confBool[CLEAN_PROTECTED_ZONES] = getGlobalBool("cleanProtectedZones", true); - m_confBool[SPELL_NAME_INSTEAD_WORDS] = getGlobalBool("spellNameInsteadOfWords", false); - m_confBool[EMOTE_SPELLS] = getGlobalBool("emoteSpells", false); - m_confNumber[MAX_PLAYER_SUMMONS] = getGlobalNumber("maxPlayerSummons", 2); - m_confBool[SAVE_GLOBAL_STORAGE] = getGlobalBool("saveGlobalStorage", true); - m_confBool[FORCE_CLOSE_SLOW_CONNECTION] = getGlobalBool("forceSlowConnectionsToDisconnect", false); - m_confBool[BLESSING_ONLY_PREMIUM] = getGlobalBool("blessingOnlyPremium", true); - m_confBool[BED_REQUIRE_PREMIUM] = getGlobalBool("bedsRequirePremium", true); - m_confNumber[FIELD_OWNERSHIP] = getGlobalNumber("fieldOwnershipDuration", 5 * 1000); - m_confBool[ALLOW_CHANGECOLORS] = getGlobalBool("allowChangeColors", true); - m_confBool[STOP_ATTACK_AT_EXIT] = getGlobalBool("stopAttackingAtExit", false); - m_confNumber[EXTRA_PARTY_PERCENT] = getGlobalNumber("extraPartyExperiencePercent", 5); - m_confNumber[EXTRA_PARTY_LIMIT] = getGlobalNumber("extraPartyExperienceLimit", 20); - m_confBool[DISABLE_OUTFITS_PRIVILEGED] = getGlobalBool("disableOutfitsForPrivilegedPlayers", false); - m_confBool[OLD_CONDITION_ACCURACY] = getGlobalBool("oldConditionAccuracy", false); - m_confBool[HOUSE_STORAGE] = getGlobalBool("useHouseDataStorage", false); - m_confBool[TRACER_BOX] = getGlobalBool("promptExceptionTracerErrorBox", true); - m_confNumber[LOGIN_PROTECTION] = getGlobalNumber("loginProtectionPeriod", 10 * 1000); - m_confBool[STORE_DIRECTION] = getGlobalBool("storePlayerDirection", false); - m_confNumber[PLAYER_DEEPNESS] = getGlobalNumber("playerQueryDeepness", 1); - m_confDouble[CRITICAL_HIT_MUL] = getGlobalDouble("criticalHitMultiplier", 1); - m_confNumber[STAIRHOP_DELAY] = getGlobalNumber("stairhopDelay", 2 * 1000); - m_confNumber[RATE_STAMINA_LOSS] = getGlobalNumber("rateStaminaLoss", 1); - m_confDouble[RATE_STAMINA_GAIN] = getGlobalDouble("rateStaminaGain", 3); - m_confDouble[RATE_STAMINA_THRESHOLD] = getGlobalDouble("rateStaminaThresholdGain", 12); - m_confDouble[RATE_STAMINA_ABOVE] = getGlobalDouble("rateStaminaAboveNormal", 1.5f); - m_confDouble[RATE_STAMINA_UNDER] = getGlobalDouble("rateStaminaUnderNormal", 0.5f); - m_confNumber[STAMINA_LIMIT_TOP] = getGlobalNumber("staminaRatingLimitTop", 41 * 60); - m_confNumber[STAMINA_LIMIT_BOTTOM] = getGlobalNumber("staminaRatingLimitBottom", 14 * 60); - m_confBool[DISPLAY_LOGGING] = getGlobalBool("displayPlayersLogging", true); - m_confBool[STAMINA_BONUS_PREMIUM] = getGlobalBool("staminaThresholdOnlyPremium", true); - m_confBool[BAN_UNKNOWN_BYTES] = getGlobalBool("autoBanishUnknownBytes", false); - m_confNumber[BLESS_REDUCTION_BASE] = getGlobalNumber("blessingReductionBase", 30); - m_confNumber[BLESS_REDUCTION_DECREAMENT] = getGlobalNumber("blessingReductionDecreament", 5); - m_confBool[ALLOW_CHANGEADDONS] = getGlobalBool("allowChangeAddons", true); - m_confNumber[BLESS_REDUCTION] = getGlobalNumber("eachBlessReduction", 8); - m_confDouble[FORMULA_LEVEL] = getGlobalDouble("formulaLevel", 5.0); - m_confDouble[FORMULA_MAGIC] = getGlobalDouble("formulaMagic", 1.0); - m_confString[PREFIX_CHANNEL_LOGS] = getGlobalString("prefixChannelLogs", ""); - m_confBool[GHOST_INVISIBLE_EFFECT] = getGlobalBool("ghostModeInvisibleEffect", false); - m_confString[CORES_USED] = getGlobalString("coresUsed", "-1"); - m_confNumber[NICE_LEVEL] = getGlobalNumber("niceLevel", 5); - m_confNumber[EXPERIENCE_COLOR] = getGlobalNumber("gainExperienceColor", TEXTCOLOR_WHITE); - m_confBool[SHOW_HEALING_DAMAGE_MONSTER] = getGlobalBool("showHealingDamageForMonsters", false); - m_confBool[CHECK_CORPSE_OWNER] = getGlobalBool("checkCorpseOwner ", true); - m_confBool[BUFFER_SPELL_FAILURE] = getGlobalBool("bufferMutedOnSpellFailure", false); - m_confBool[CONFIM_OUTDATED_VERSION] = getGlobalBool("confirmOutdatedVersion", true); - m_confNumber[GUILD_PREMIUM_DAYS] = getGlobalNumber("premiumDaysToFormGuild", 0); - m_confNumber[PUSH_CREATURE_DELAY] = getGlobalNumber("pushCreatureDelay", 2 * 1000); - m_confNumber[DEATH_CONTAINER] = getGlobalNumber("deathContainerId", 1987); - m_confBool[PREMIUM_SKIP_WAIT] = getGlobalBool("premiumPlayerSkipWaitList", false); - m_confNumber[MAXIMUM_DOOR_LEVEL] = getGlobalNumber("maximumDoorLevel", 500); - m_confBool[DEATH_LIST] = getGlobalBool("deathListEnabled", true); - m_confNumber[DEATH_ASSISTS] = getGlobalNumber("deathAssistCount", 1); - m_confNumber[RED_DAILY_LIMIT] = getGlobalNumber("dailyFragsToRedSkull", 3); - m_confNumber[RED_WEEKLY_LIMIT] = getGlobalNumber("weeklyFragsToRedSkull", 5); - m_confNumber[RED_MONTHLY_LIMIT] = getGlobalNumber("monthlyFragsToRedSkull", 10); - m_confNumber[BLACK_DAILY_LIMIT] = getGlobalNumber("dailyFragsToBlackSkull", m_confNumber[RED_DAILY_LIMIT]); - m_confNumber[BLACK_WEEKLY_LIMIT] = getGlobalNumber("weeklyFragsToBlackSkull", m_confNumber[RED_WEEKLY_LIMIT]); - m_confNumber[BLACK_MONTHLY_LIMIT] = getGlobalNumber("monthlyFragsToBlackSkull", m_confNumber[RED_MONTHLY_LIMIT]); - m_confNumber[BAN_DAILY_LIMIT] = getGlobalNumber("dailyFragsToBanishment", m_confNumber[RED_DAILY_LIMIT]); - m_confNumber[BAN_WEEKLY_LIMIT] = getGlobalNumber("weeklyFragsToBanishment", m_confNumber[RED_WEEKLY_LIMIT]); - m_confNumber[BAN_MONTHLY_LIMIT] = getGlobalNumber("monthlyFragsToBanishment", m_confNumber[RED_MONTHLY_LIMIT]); - m_confNumber[BLACK_SKULL_DEATH_HEALTH] = getGlobalNumber("blackSkulledDeathHealth", 40); - m_confNumber[BLACK_SKULL_DEATH_MANA] = getGlobalNumber("blackSkulledDeathMana", 0); - m_confNumber[DEATHLIST_REQUIRED_TIME] = getGlobalNumber("deathListRequiredTime", 1 * 60 * 1000); - m_confNumber[EXPERIENCE_SHARE_ACTIVITY] = getGlobalNumber("experienceShareActivity", 2 * 60 * 1000); - m_confBool[GHOST_SPELL_EFFECTS] = getGlobalBool("ghostModeSpellEffects", true); - m_confBool[PVPZONE_ADDMANASPENT] = getGlobalBool("addManaSpentInPvPZone", true); - m_confNumber[ITEMLIMIT_PROTECTIONZONE] = getGlobalNumber("maxItemsPerPZTile", 0); - m_confNumber[ITEMLIMIT_HOUSETILE] = getGlobalNumber("maxItemsPerHouseTile", 0); - m_confString[MAILBOX_DISABLED_TOWNS] = getGlobalString("mailboxDisabledTowns", "-1"); - m_confNumber[SQUARE_COLOR] = getGlobalNumber("squareColor", 0); - m_confBool[USE_BLACK_SKULL] = getGlobalBool("useBlackSkull", false); - m_confBool[USE_FRAG_HANDLER] = getGlobalBool("useFragHandler", true); - m_confNumber[LOOT_MESSAGE] = getGlobalNumber("monsterLootMessage", 3); - m_confNumber[LOOT_MESSAGE_TYPE] = getGlobalNumber("monsterLootMessageType", 25); - m_confNumber[NAME_REPORT_TYPE] = getGlobalNumber("violationNameReportActionType", 2); - m_confBool[ALLOW_FIGHTBACK] = getGlobalBool("allowFightback", true); - m_confNumber[HOUSE_CLEAN_OLD] = getGlobalNumber("houseCleanOld", 0); - m_confBool[VIPLIST_PER_PLAYER] = getGlobalBool("separateViplistPerCharacter", false); - m_confDouble[RATE_MONSTER_HEALTH] = getGlobalDouble("rateMonsterHealth", 1); - m_confDouble[RATE_MONSTER_MANA] = getGlobalDouble("rateMonsterMana", 1); - m_confDouble[RATE_MONSTER_ATTACK] = getGlobalDouble("rateMonsterAttack", 1); - m_confDouble[RATE_MONSTER_DEFENSE] = getGlobalDouble("rateMonsterDefense", 1); - m_confBool[ADDONS_PREMIUM] = getGlobalBool("addonsOnlyPremium", true); + m_confString[MAP_AUTHOR] = getGlobalString("mapAuthor", "Unknown"); + m_confNumber[LOGIN_TRIES] = getGlobalNumber("loginTries", 3); + m_confNumber[RETRY_TIMEOUT] = getGlobalNumber("retryTimeout", 30000); + m_confNumber[LOGIN_TIMEOUT] = getGlobalNumber("loginTimeout", 5000); + m_confNumber[MAX_MESSAGEBUFFER] = getGlobalNumber("maxMessageBuffer", 4); + m_confNumber[MAX_PLAYERS] = getGlobalNumber("maxPlayers", 1000); + m_confNumber[DEFAULT_DESPAWNRANGE] = getGlobalNumber("deSpawnRange", 2); + m_confNumber[DEFAULT_DESPAWNRADIUS] = getGlobalNumber("deSpawnRadius", 50); + m_confNumber[PZ_LOCKED] = getGlobalNumber("pzLocked", 60000); + m_confNumber[HUNTING_DURATION] = getGlobalNumber("huntingDuration", 60000); + m_confString[SERVER_NAME] = getGlobalString("serverName"); + m_confString[OWNER_NAME] = getGlobalString("ownerName"); + m_confString[OWNER_EMAIL] = getGlobalString("ownerEmail"); + m_confString[URL] = getGlobalString("url"); + m_confString[LOCATION] = getGlobalString("location"); + m_confString[MOTD] = getGlobalString("motd"); + m_confNumber[ALLOW_CLONES] = getGlobalNumber("allowClones", 0); + m_confBool[EXPERIENCE_STAGES] = getGlobalBool("experienceStages", false); + m_confDouble[RATE_EXPERIENCE] = getGlobalDouble("rateExperience", 1); + m_confDouble[RATE_SKILL] = getGlobalDouble("rateSkill", 1); + m_confDouble[RATE_MAGIC] = getGlobalDouble("rateMagic", 1); + m_confDouble[RATE_LOOT] = getGlobalDouble("rateLoot", 1); + m_confNumber[RATE_SPAWN_MIN] = getGlobalNumber("rateSpawnMin", 1); + m_confNumber[RATE_SPAWN_MAX] = getGlobalNumber("rateSpawnMax", 1); + m_confNumber[PARTY_RADIUS_X] = getGlobalNumber("experienceShareRadiusX", 30); + m_confNumber[PARTY_RADIUS_Y] = getGlobalNumber("experienceShareRadiusY", 30); + m_confNumber[PARTY_RADIUS_Z] = getGlobalNumber("experienceShareRadiusZ", 1); + m_confDouble[PARTY_DIFFERENCE] = getGlobalDouble("experienceShareLevelDifference", (double)2 / 3); + m_confNumber[SPAWNPOS_X] = getGlobalNumber("newPlayerSpawnPosX", 100); + m_confNumber[SPAWNPOS_Y] = getGlobalNumber("newPlayerSpawnPosY", 100); + m_confNumber[SPAWNPOS_Z] = getGlobalNumber("newPlayerSpawnPosZ", 7); + m_confNumber[SPAWNTOWN_ID] = getGlobalNumber("newPlayerTownId", 1); + m_confString[WORLD_TYPE] = getGlobalString("worldType", "open"); + m_confBool[ACCOUNT_MANAGER] = getGlobalBool("accountManager", true); + m_confBool[NAMELOCK_MANAGER] = getGlobalBool("namelockManager", false); + m_confNumber[START_LEVEL] = getGlobalNumber("newPlayerLevel", 1); + m_confNumber[START_MAGICLEVEL] = getGlobalNumber("newPlayerMagicLevel", 0); + m_confBool[START_CHOOSEVOC] = getGlobalBool("newPlayerChooseVoc", false); + m_confNumber[HOUSE_PRICE] = getGlobalNumber("housePriceEachSquare", 1000); + m_confNumber[WHITE_SKULL_TIME] = getGlobalNumber("whiteSkullTime", 900000); + m_confNumber[HIGHSCORES_TOP] = getGlobalNumber("highscoreDisplayPlayers", 10); + m_confNumber[HIGHSCORES_UPDATETIME] = getGlobalNumber("updateHighscoresAfterMinutes", 60); + m_confBool[ON_OR_OFF_CHARLIST] = getGlobalBool("displayOnOrOffAtCharlist", false); + m_confBool[ALLOW_CHANGEOUTFIT] = getGlobalBool("allowChangeOutfit", true); + m_confBool[ONE_PLAYER_ON_ACCOUNT] = getGlobalBool("onePlayerOnlinePerAccount", true); + m_confBool[CANNOT_ATTACK_SAME_LOOKFEET] = getGlobalBool("noDamageToSameLookfeet", false); + m_confBool[AIMBOT_HOTKEY_ENABLED] = getGlobalBool("hotkeyAimbotEnabled", true); + m_confNumber[ACTIONS_DELAY_INTERVAL] = getGlobalNumber("timeBetweenActions", 200); + m_confNumber[EX_ACTIONS_DELAY_INTERVAL] = getGlobalNumber("timeBetweenExActions", 1000); + m_confNumber[CUSTOM_ACTIONS_DELAY_INTERVAL] = getGlobalNumber("timeBetweenCustomActions", 500); + m_confNumber[CRITICAL_HIT_CHANCE] = getGlobalNumber("criticalHitChance", 5); + m_confBool[REMOVE_WEAPON_AMMO] = getGlobalBool("removeWeaponAmmunition", true); + m_confBool[REMOVE_WEAPON_CHARGES] = getGlobalBool("removeWeaponCharges", true); + m_confBool[REMOVE_RUNE_CHARGES] = getGlobalBool("removeRuneCharges", true); + m_confDouble[RATE_PVP_EXPERIENCE] = getGlobalDouble("rateExperienceFromPlayers", 0); + m_confDouble[EFP_MIN_THRESHOLD] = getGlobalDouble("minLevelThresholdForKilledPlayer", 0.9f); + m_confDouble[EFP_MAX_THRESHOLD] = getGlobalDouble("maxLevelThresholdForKilledPlayer", 1.1f); + m_confBool[SHUTDOWN_AT_GLOBALSAVE] = getGlobalBool("shutdownAtGlobalSave", false); + m_confBool[CLEAN_MAP_AT_GLOBALSAVE] = getGlobalBool("cleanMapAtGlobalSave", true); + m_confBool[FREE_PREMIUM] = getGlobalBool("freePremium", false); + m_confNumber[PROTECTION_LEVEL] = getGlobalNumber("protectionLevel", 1); + m_confNumber[STATUSQUERY_TIMEOUT] = getGlobalNumber("statusTimeout", 300000); + m_confBool[GENERATE_ACCOUNT_NUMBER] = getGlobalBool("generateAccountNumber", false); + m_confBool[GENERATE_ACCOUNT_SALT] = getGlobalBool("generateAccountSalt", false); + m_confBool[INGAME_GUILD_MANAGEMENT] = getGlobalBool("ingameGuildManagement", true); + m_confNumber[LEVEL_TO_FORM_GUILD] = getGlobalNumber("levelToFormGuild", 8); + m_confNumber[MIN_GUILDNAME] = getGlobalNumber("guildNameMinLength", 4); + m_confNumber[MAX_GUILDNAME] = getGlobalNumber("guildNameMaxLength", 20); + m_confNumber[LEVEL_TO_BUY_HOUSE] = getGlobalNumber("levelToBuyHouse", 1); + m_confNumber[HOUSES_PER_ACCOUNT] = getGlobalNumber("housesPerAccount", 0); + m_confBool[HOUSE_BUY_AND_SELL] = getGlobalBool("buyableAndSellableHouses", true); + m_confBool[REPLACE_KICK_ON_LOGIN] = getGlobalBool("replaceKickOnLogin", true); + m_confBool[HOUSE_NEED_PREMIUM] = getGlobalBool("houseNeedPremium", true); + m_confBool[HOUSE_RENTASPRICE] = getGlobalBool("houseRentAsPrice", false); + m_confBool[HOUSE_PRICEASRENT] = getGlobalBool("housePriceAsRent", false); + m_confString[HOUSE_STORAGE] = getGlobalString("houseDataStorage", "binary"); + m_confNumber[RED_SKULL_LENGTH] = getGlobalNumber("redSkullLength", 2592000); + m_confNumber[BLACK_SKULL_LENGTH] = getGlobalNumber("blackSkullLength", 3888000); + m_confNumber[NOTATIONS_TO_BAN] = getGlobalNumber("notationsToBan", 3); + m_confNumber[WARNINGS_TO_FINALBAN] = getGlobalNumber("warningsToFinalBan", 4); + m_confNumber[WARNINGS_TO_DELETION] = getGlobalNumber("warningsToDeletion", 5); + m_confNumber[BAN_LENGTH] = getGlobalNumber("banLength", 604800); + m_confNumber[KILLS_BAN_LENGTH] = getGlobalNumber("killsBanLength", 604800); + m_confNumber[FINALBAN_LENGTH] = getGlobalNumber("finalBanLength", 2592000); + m_confNumber[IPBAN_LENGTH] = getGlobalNumber("ipBanLength", 86400); + m_confBool[BANK_SYSTEM] = getGlobalBool("bankSystem", true); + m_confBool[PREMIUM_FOR_PROMOTION] = getGlobalBool("premiumForPromotion", true); + m_confBool[INIT_PREMIUM_UPDATE] = getGlobalBool("updatePremiumStateAtStartup", true); + m_confBool[SHOW_HEALTH_CHANGE] = getGlobalBool("showHealthChange", false); + m_confBool[SHOW_MANA_CHANGE] = getGlobalBool("showManaChange", false); + m_confBool[TELEPORT_SUMMONS] = getGlobalBool("teleportAllSummons", false); + m_confBool[TELEPORT_PLAYER_SUMMONS] = getGlobalBool("teleportPlayerSummons", false); + m_confBool[PVP_TILE_IGNORE_PROTECTION] = getGlobalBool("pvpTileIgnoreLevelAndVocationProtection", true); + m_confBool[DISPLAY_CRITICAL_HIT] = getGlobalBool("displayCriticalHitNotify", false); + m_confBool[CLEAN_PROTECTED_ZONES] = getGlobalBool("cleanProtectedZones", true); + m_confBool[SPELL_NAME_INSTEAD_WORDS] = getGlobalBool("spellNameInsteadOfWords", false); + m_confBool[EMOTE_SPELLS] = getGlobalBool("emoteSpells", false); + m_confNumber[MAX_PLAYER_SUMMONS] = getGlobalNumber("maxPlayerSummons", 2); + m_confBool[SAVE_GLOBAL_STORAGE] = getGlobalBool("saveGlobalStorage", true); + m_confBool[SAVE_PLAYER_DATA] = getGlobalBool("savePlayerData", true); + m_confBool[FORCE_CLOSE_SLOW_CONNECTION] = getGlobalBool("forceSlowConnectionsToDisconnect", false); + m_confBool[BLESSINGS] = getGlobalBool("blessings", true); + m_confBool[BLESSING_ONLY_PREMIUM] = getGlobalBool("blessingOnlyPremium", true); + m_confBool[BED_REQUIRE_PREMIUM] = getGlobalBool("bedsRequirePremium", true); + m_confNumber[FIELD_OWNERSHIP] = getGlobalNumber("fieldOwnershipDuration", 5000); + m_confBool[ALLOW_CHANGECOLORS] = getGlobalBool("allowChangeColors", true); + m_confBool[STOP_ATTACK_AT_EXIT] = getGlobalBool("stopAttackingAtExit", false); + m_confNumber[EXTRA_PARTY_PERCENT] = getGlobalNumber("extraPartyExperiencePercent", 5); + m_confNumber[EXTRA_PARTY_LIMIT] = getGlobalNumber("extraPartyExperienceLimit", 20); + m_confBool[DISABLE_OUTFITS_PRIVILEGED] = getGlobalBool("disableOutfitsForPrivilegedPlayers", false); + m_confBool[TRACER_BOX] = getGlobalBool("promptExceptionTracerErrorBox", true); + m_confNumber[LOGIN_PROTECTION] = getGlobalNumber("loginProtectionPeriod", 10000); + m_confBool[STORE_DIRECTION] = getGlobalBool("storePlayerDirection", false); + m_confNumber[PLAYER_DEEPNESS] = getGlobalNumber("playerQueryDeepness", -1); + m_confDouble[CRITICAL_HIT_MUL] = getGlobalDouble("criticalHitMultiplier", 1); + m_confNumber[STAIRHOP_DELAY] = getGlobalNumber("stairhopDelay", 2 * 1000); + m_confNumber[RATE_STAMINA_LOSS] = getGlobalNumber("rateStaminaLoss", 1); + m_confDouble[RATE_STAMINA_GAIN] = getGlobalDouble("rateStaminaGain", 3); + m_confDouble[RATE_STAMINA_THRESHOLD] = getGlobalDouble("rateStaminaThresholdGain", 12); + m_confDouble[RATE_STAMINA_ABOVE] = getGlobalDouble("rateStaminaAboveNormal", 1.5f); + m_confDouble[RATE_STAMINA_UNDER] = getGlobalDouble("rateStaminaUnderNormal", 0.5f); + m_confNumber[STAMINA_LIMIT_TOP] = getGlobalNumber("staminaRatingLimitTop", 2460); + m_confNumber[STAMINA_LIMIT_BOTTOM] = getGlobalNumber("staminaRatingLimitBottom", 840); + m_confBool[DISPLAY_LOGGING] = getGlobalBool("displayPlayersLogging", true); + m_confBool[STAMINA_BONUS_PREMIUM] = getGlobalBool("staminaThresholdOnlyPremium", true); + m_confNumber[ALLOWED_MAX_PACKETS] = getGlobalNumber("allowedMaxSizePackets", 3); + m_confNumber[BLESS_REDUCTION_BASE] = getGlobalNumber("blessingReductionBase", 30); + m_confNumber[BLESS_REDUCTION_DECREMENT] = getGlobalNumber("blessingReductionDecrement", 5); + m_confBool[ALLOW_CHANGEADDONS] = getGlobalBool("allowChangeAddons", true); + m_confNumber[BLESS_REDUCTION] = getGlobalNumber("eachBlessReduction", 8); + m_confDouble[FORMULA_LEVEL] = getGlobalDouble("formulaLevel", 5.0); + m_confDouble[FORMULA_MAGIC] = getGlobalDouble("formulaMagic", 1.0); + m_confString[PREFIX_CHANNEL_LOGS] = getGlobalString("prefixChannelLogs", ""); + m_confBool[GHOST_INVISIBLE_EFFECT] = getGlobalBool("ghostModeInvisibleEffect", false); + m_confString[CORES_USED] = getGlobalString("coresUsed", "-1"); + m_confNumber[NICE_LEVEL] = getGlobalNumber("niceLevel", 5); + m_confNumber[EXPERIENCE_COLOR] = getGlobalNumber("gainExperienceColor", COLOR_WHITE); + m_confBool[SHOW_HEALTH_CHANGE_MONSTER] = getGlobalBool("showHealthChangeForMonsters", false); + m_confBool[SHOW_MANA_CHANGE_MONSTER] = getGlobalBool("showManaChangeForMonsters", false); + m_confBool[CHECK_CORPSE_OWNER] = getGlobalBool("checkCorpseOwner", true); + m_confBool[BUFFER_SPELL_FAILURE] = getGlobalBool("bufferMutedOnSpellFailure", false); + m_confBool[CONFIRM_OUTDATED_VERSION] = getGlobalBool("confirmOutdatedVersion", true); + m_confNumber[GUILD_PREMIUM_DAYS] = getGlobalNumber("premiumDaysToFormGuild", 0); + m_confNumber[PUSH_CREATURE_DELAY] = getGlobalNumber("pushCreatureDelay", 2000); + m_confNumber[DEATH_CONTAINER] = getGlobalNumber("deathContainerId", 1987); + m_confBool[PREMIUM_SKIP_WAIT] = getGlobalBool("premiumPlayerSkipWaitList", false); + m_confNumber[MAXIMUM_DOOR_LEVEL] = getGlobalNumber("maximumDoorLevel", 500); + m_confBool[DEATH_LIST] = getGlobalBool("deathListEnabled", true); + m_confNumber[DEATH_ASSISTS] = getGlobalNumber("deathAssistCount", 1); + m_confNumber[FRAG_LIMIT] = getGlobalNumber("fragsLimit", 86400); + m_confNumber[FRAG_SECOND_LIMIT] = getGlobalNumber("fragsSecondLimit", 604800); + m_confNumber[FRAG_THIRD_LIMIT] = getGlobalNumber("fragsThirdLimit", 2592000); + m_confNumber[RED_LIMIT] = getGlobalNumber("fragsToRedSkull", 3); + m_confNumber[RED_SECOND_LIMIT] = getGlobalNumber("fragsSecondToRedSkull", 5); + m_confNumber[RED_THIRD_LIMIT] = getGlobalNumber("fragsThirdToRedSkull", 10); + m_confNumber[BLACK_LIMIT] = getGlobalNumber("fragsToBlackSkull", m_confNumber[RED_LIMIT]); + m_confNumber[BLACK_SECOND_LIMIT] = getGlobalNumber("fragsSecondToBlackSkull", m_confNumber[RED_SECOND_LIMIT]); + m_confNumber[BLACK_THIRD_LIMIT] = getGlobalNumber("fragsThirdToBlackSkull", m_confNumber[RED_THIRD_LIMIT]); + m_confNumber[BAN_LIMIT] = getGlobalNumber("fragsToBanishment", m_confNumber[RED_LIMIT]); + m_confNumber[BAN_SECOND_LIMIT] = getGlobalNumber("fragsSecondToBanishment", m_confNumber[RED_SECOND_LIMIT]); + m_confNumber[BAN_THIRD_LIMIT] = getGlobalNumber("fragsThirdToBanishment", m_confNumber[RED_THIRD_LIMIT]); + m_confNumber[BLACK_SKULL_DEATH_HEALTH] = getGlobalNumber("blackSkulledDeathHealth", 40); + m_confNumber[BLACK_SKULL_DEATH_MANA] = getGlobalNumber("blackSkulledDeathMana", 0); + m_confNumber[DEATHLIST_REQUIRED_TIME] = getGlobalNumber("deathListRequiredTime", 60000); + m_confNumber[EXPERIENCE_SHARE_ACTIVITY] = getGlobalNumber("experienceShareActivity", 120000); + m_confBool[GHOST_SPELL_EFFECTS] = getGlobalBool("ghostModeSpellEffects", true); + m_confBool[PVPZONE_ADDMANASPENT] = getGlobalBool("addManaSpentInPvPZone", true); + m_confBool[PVPZONE_RECOVERMANA] = getGlobalBool("recoverManaAfterDeathInPvPZone", false); + m_confNumber[TILE_LIMIT] = getGlobalNumber("tileLimit", 0); + m_confNumber[PROTECTION_TILE_LIMIT] = getGlobalNumber("protectionTileLimit", 0); + m_confNumber[HOUSE_TILE_LIMIT] = getGlobalNumber("houseTileLimit", 0); + m_confNumber[TRADE_LIMIT] = getGlobalNumber("tradeLimit", 100); + m_confString[MAILBOX_DISABLED_TOWNS] = getGlobalString("mailboxDisabledTowns", ""); + m_confNumber[SQUARE_COLOR] = getGlobalNumber("squareColor", 0); + m_confBool[USE_BLACK_SKULL] = getGlobalBool("useBlackSkull", false); + m_confBool[USE_FRAG_HANDLER] = getGlobalBool("useFragHandler", true); + m_confNumber[LOOT_MESSAGE] = getGlobalNumber("monsterLootMessage", 3); + m_confNumber[LOOT_MESSAGE_TYPE] = getGlobalNumber("monsterLootMessageType", 19); + m_confBool[ALLOW_FIGHTBACK] = getGlobalBool("allowFightback", true); + m_confNumber[HOUSE_CLEAN_OLD] = getGlobalNumber("houseCleanOld", 0); + m_confBool[VIPLIST_PER_PLAYER] = getGlobalBool("separateVipListPerCharacter", false); + m_confDouble[RATE_MONSTER_HEALTH] = getGlobalDouble("rateMonsterHealth", 1); + m_confDouble[RATE_MONSTER_MANA] = getGlobalDouble("rateMonsterMana", 1); + m_confDouble[RATE_MONSTER_ATTACK] = getGlobalDouble("rateMonsterAttack", 1); + m_confDouble[RATE_MONSTER_DEFENSE] = getGlobalDouble("rateMonsterDefense", 1); + m_confBool[MANAGER_LOCALHOST_ONLY] = getGlobalBool("managerLocalhostOnly", true); + m_confNumber[MANAGER_CONNECTIONS_LIMIT] = getGlobalNumber("managerConnectionsLimit", 1); + m_confString[MANAGER_PASSWORD] = getGlobalString("managerPassword", ""); + m_confBool[MANAGER_LOGS] = getGlobalBool("managerLogs", false); + m_confBool[ADMIN_LOGS] = getGlobalBool("adminLogs", false); + m_confString[ADMIN_PASSWORD] = getGlobalString("adminPassword", ""); + m_confNumber[ADMIN_CONNECTIONS_LIMIT] = getGlobalNumber("adminConnectionsLimit", 1); + m_confBool[ADMIN_LOCALHOST_ONLY] = getGlobalBool("adminLocalhostOnly", true); + m_confBool[ADMIN_REQUIRE_LOGIN] = getGlobalBool("adminRequireLogin", true); + m_confString[ADMIN_ENCRYPTION] = getGlobalString("adminEncryption", ""); + m_confString[ADMIN_ENCRYPTION_DATA] = getGlobalString("adminEncryptionData", ""); + m_confBool[ADDONS_PREMIUM] = getGlobalBool("addonsOnlyPremium", true); + m_confBool[UNIFIED_SPELLS] = getGlobalBool("unifiedSpells", true); + m_confBool[OPTIONAL_WAR_ATTACK_ALLY] = getGlobalBool("optionalWarAttackableAlly", false); + m_confNumber[VIPLIST_DEFAULT_LIMIT] = getGlobalNumber("vipListDefaultLimit", 20); + m_confNumber[VIPLIST_DEFAULT_PREMIUM_LIMIT] = getGlobalNumber("vipListDefaultPremiumLimit", 100); + m_confNumber[STAMINA_DESTROY_LOOT] = getGlobalNumber("staminaLootLimit", 840); + m_confNumber[FIST_BASE_ATTACK] = getGlobalNumber("fistBaseAttack", 7); + m_confBool[ALLOW_MOUNTS] = getGlobalBool("useMounts", true); + m_confBool[UNMOUNT_PLAYER_IN_PZ] = getGlobalBool("unmountPlayerInPz", true); + m_confBool[ENABLE_COOLDOWNS] = getGlobalBool("enableCooldowns", true); + m_confBool[MONSTER_SPAWN_WALKBACK] = getGlobalBool("monsterSpawnWalkback", true); + m_confNumber[PVP_BLESSING_THRESHOLD] = getGlobalNumber("pvpBlessingThreshold", 40); + m_confNumber[FAIRFIGHT_TIMERANGE] = getGlobalNumber("fairFightTimeRange", 60); + m_confNumber[DEFAULT_DEPOT_SIZE_PREMIUM] = getGlobalNumber("defaultDepotSizePremium", 2000); + m_confNumber[DEFAULT_DEPOT_SIZE] = getGlobalNumber("defaultDepotSize", 2000); + m_confBool[USE_CAPACITY] = getGlobalBool("useCapacity", true); + m_confBool[DAEMONIZE] = getGlobalBool("daemonize", false); + m_confBool[TIBIA_SLOTS] = getGlobalBool("tibiaClassicSlots", true); + m_confBool[SKIP_ITEMS_VERSION] = getGlobalBool("skipItemsVersionCheck", false); + m_confBool[SILENT_LUA] = getGlobalBool("disableLuaErrors", false); + m_confNumber[MAIL_ATTEMPTS] = getGlobalNumber("mailMaxAttempts", 20); + m_confNumber[MAIL_BLOCK] = getGlobalNumber("mailBlockPeriod", 3600000); + m_confNumber[MAIL_ATTEMPTS_FADE] = getGlobalNumber("mailAttemptsFadeTime", 600000); + m_confBool[ROOK_SYSTEM] = getGlobalBool("useRookSystem", false); + m_confNumber[ROOK_TOWN] = getGlobalNumber("rookTownId", 1); + m_confNumber[ROOK_LEVELTO] = getGlobalNumber("rookLevelToGetRooked", 5); + m_confNumber[ROOK_TOLEVEL] = getGlobalNumber("rookLevelToLeaveRook", 8); + m_confBool[HOUSE_SKIP_INIT_RENT] = getGlobalBool("houseSkipInitialRent", true); + m_confBool[HOUSE_PROTECTION] = getGlobalBool("houseProtection", true); + m_confBool[FAIRFIGHT_REDUCTION] = getGlobalBool("useFairfightReduction", true); + m_confNumber[MYSQL_RECONNECTION_ATTEMPTS] = getGlobalNumber("mysqlReconnectionAttempts", 3); + m_confBool[MARKET_ENABLED] = getGlobalBool("marketEnabled", true); + m_confBool[MARKET_PREMIUM] = getGlobalBool("premiumToCreateMarketOffer", true); + m_confNumber[CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES] = getGlobalNumber("checkExpiredMarketOffersEachMinutes", 60); + m_confNumber[MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER] = getGlobalNumber("maxMarketOffersAtATimePerPlayer", 100); m_loaded = true; return true; @@ -299,7 +364,33 @@ bool ConfigManager::reload() if(!m_loaded) return false; - return load(); + uint32_t tmp = m_confNumber[HOUSE_PRICE]; + if(!load()) + return false; + + if((uint32_t)m_confNumber[HOUSE_PRICE] == tmp) + return true; + + for(HouseMap::iterator it = Houses::getInstance()->getHouseBegin(); + it != Houses::getInstance()->getHouseEnd(); ++it) + { + uint32_t price = it->second->getTilesCount() * m_confNumber[HOUSE_PRICE]; + if(m_confBool[HOUSE_RENTASPRICE]) + { + uint32_t rent = it->second->getRent(); + if(!m_confBool[HOUSE_PRICEASRENT] && it->second->getPrice() != rent) + price = rent; + } + + it->second->setPrice(price); + if(m_confBool[HOUSE_PRICEASRENT]) + it->second->setRent(price); + + if(!it->second->getOwner()) + it->second->updateDoorDescription(); + } + + return true; } const std::string& ConfigManager::getString(uint32_t _what) const @@ -308,7 +399,7 @@ const std::string& ConfigManager::getString(uint32_t _what) const return m_confString[_what]; if(!m_startup) - std::cout << "[Warning - ConfigManager::getString] " << _what << std::endl; + std::clog << "[Warning - ConfigManager::getString] " << _what << std::endl; return m_confString[DUMMY_STR]; } @@ -319,18 +410,18 @@ bool ConfigManager::getBool(uint32_t _what) const return m_confBool[_what]; if(!m_startup) - std::cout << "[Warning - ConfigManager::getBool] " << _what << std::endl; + std::clog << "[Warning - ConfigManager::getBool] " << _what << std::endl; return false; } -int32_t ConfigManager::getNumber(uint32_t _what) const +int64_t ConfigManager::getNumber(uint32_t _what) const { if(m_loaded && _what < LAST_NUMBER_CONFIG) return m_confNumber[_what]; if(!m_startup) - std::cout << "[Warning - ConfigManager::getNumber] " << _what << std::endl; + std::clog << "[Warning - ConfigManager::getNumber] " << _what << std::endl; return 0; } @@ -341,7 +432,7 @@ double ConfigManager::getDouble(uint32_t _what) const return m_confDouble[_what]; if(!m_startup) - std::cout << "[Warning - ConfigManager::getDouble] " << _what << std::endl; + std::clog << "[Warning - ConfigManager::getDouble] " << _what << std::endl; return 0; } @@ -354,11 +445,11 @@ bool ConfigManager::setString(uint32_t _what, const std::string& _value) return true; } - std::cout << "[Warning - ConfigManager::setString] " << _what << std::endl; + std::clog << "[Warning - ConfigManager::setString] " << _what << std::endl; return false; } -bool ConfigManager::setNumber(uint32_t _what, int32_t _value) +bool ConfigManager::setNumber(uint32_t _what, int64_t _value) { if(_what < LAST_NUMBER_CONFIG) { @@ -366,6 +457,18 @@ bool ConfigManager::setNumber(uint32_t _what, int32_t _value) return true; } - std::cout << "[Warning - ConfigManager::setNumber] " << _what << std::endl; + std::clog << "[Warning - ConfigManager::setNumber] " << _what << std::endl; + return false; +} + +bool ConfigManager::setBool(uint32_t _what, bool _value) +{ + if(_what < LAST_BOOL_CONFIG) + { + m_confBool[_what] = _value; + return true; + } + + std::clog << "[Warning - ConfigManager::setBool] " << _what << std::endl; return false; } diff --git a/configmanager.h b/configmanager.h index 007ef87..e862ebe 100644 --- a/configmanager.h +++ b/configmanager.h @@ -31,8 +31,8 @@ class ConfigManager CONFIG_FILE, MAP_NAME, HOUSE_RENT_PERIOD, + HOUSE_STORAGE, LOGIN_MSG, - FIRST_MSG, SERVER_NAME, OWNER_NAME, OWNER_EMAIL, @@ -51,26 +51,37 @@ class ConfigManager #endif SQL_FILE, ENCRYPTION_TYPE, + RSA_PRIME1, + RSA_PRIME2, + RSA_PUBLIC, + RSA_MODULUS, + RSA_PRIVATE, MAP_AUTHOR, RUNFILE, - OUT_LOG, - ERROR_LOG, + OUTPUT_LOG, DATA_DIRECTORY, + LOGS_DIRECTORY, PREFIX_CHANNEL_LOGS, CORES_USED, MAILBOX_DISABLED_TOWNS, + MANAGER_PASSWORD, + ADMIN_PASSWORD, + ADMIN_ENCRYPTION, + ADMIN_ENCRYPTION_DATA, + GAME_PORT, LAST_STRING_CONFIG /* this must be the last one */ }; enum number_config_t { LOGIN_TRIES = 0, + MYSQL_RECONNECTION_ATTEMPTS, RETRY_TIMEOUT, LOGIN_TIMEOUT, LOGIN_PORT, - GAME_PORT, ADMIN_PORT, STATUS_PORT, + MANAGER_PORT, SQL_PORT, SQL_KEEPALIVE, MAX_PLAYERS, @@ -78,13 +89,15 @@ class ConfigManager HUNTING_DURATION, DEFAULT_DESPAWNRANGE, DEFAULT_DESPAWNRADIUS, - RATE_SPAWN, + RATE_SPAWN_MIN, + RATE_SPAWN_MAX, SPAWNPOS_X, SPAWNPOS_Y, SPAWNPOS_Z, SPAWNTOWN_ID, ALLOW_CLONES, GLOBALSAVE_H, + GLOBALSAVE_M, START_LEVEL, START_MAGICLEVEL, HOUSE_PRICE, @@ -93,6 +106,7 @@ class ConfigManager HIGHSCORES_UPDATETIME, ACTIONS_DELAY_INTERVAL, EX_ACTIONS_DELAY_INTERVAL, + CUSTOM_ACTIONS_DELAY_INTERVAL, CRITICAL_HIT_CHANCE, PROTECTION_LEVEL, ENCRYPTION, @@ -105,14 +119,13 @@ class ConfigManager WHITE_SKULL_TIME, RED_SKULL_LENGTH, BLACK_SKULL_LENGTH, - MAX_VIOLATIONCOMMENT_SIZE, NOTATIONS_TO_BAN, WARNINGS_TO_FINALBAN, WARNINGS_TO_DELETION, BAN_LENGTH, KILLS_BAN_LENGTH, FINALBAN_LENGTH, - IPBANISHMENT_LENGTH, + IPBAN_LENGTH, MAX_PLAYER_SUMMONS, FIELD_OWNERSHIP, WORLD_ID, @@ -130,7 +143,7 @@ class ConfigManager STAMINA_LIMIT_TOP, STAMINA_LIMIT_BOTTOM, BLESS_REDUCTION_BASE, - BLESS_REDUCTION_DECREAMENT, + BLESS_REDUCTION_DECREMENT, BLESS_REDUCTION, NICE_LEVEL, EXPERIENCE_COLOR, @@ -139,26 +152,51 @@ class ConfigManager DEATH_CONTAINER, MAXIMUM_DOOR_LEVEL, DEATH_ASSISTS, - RED_DAILY_LIMIT, - RED_WEEKLY_LIMIT, - RED_MONTHLY_LIMIT, - BLACK_DAILY_LIMIT, - BLACK_WEEKLY_LIMIT, - BLACK_MONTHLY_LIMIT, - BAN_DAILY_LIMIT, - BAN_WEEKLY_LIMIT, - BAN_MONTHLY_LIMIT, + FRAG_LIMIT, + FRAG_SECOND_LIMIT, + FRAG_THIRD_LIMIT, + RED_LIMIT, + RED_SECOND_LIMIT, + RED_THIRD_LIMIT, + BLACK_LIMIT, + BLACK_SECOND_LIMIT, + BLACK_THIRD_LIMIT, + BAN_LIMIT, + BAN_SECOND_LIMIT, + BAN_THIRD_LIMIT, BLACK_SKULL_DEATH_HEALTH, BLACK_SKULL_DEATH_MANA, DEATHLIST_REQUIRED_TIME, EXPERIENCE_SHARE_ACTIVITY, - ITEMLIMIT_PROTECTIONZONE, - ITEMLIMIT_HOUSETILE, + TILE_LIMIT, + PROTECTION_TILE_LIMIT, + HOUSE_TILE_LIMIT, + TRADE_LIMIT, SQUARE_COLOR, LOOT_MESSAGE, LOOT_MESSAGE_TYPE, - NAME_REPORT_TYPE, HOUSE_CLEAN_OLD, + MANAGER_CONNECTIONS_LIMIT, + ADMIN_CONNECTIONS_LIMIT, + VIPLIST_DEFAULT_LIMIT, + VIPLIST_DEFAULT_PREMIUM_LIMIT, + STAMINA_DESTROY_LOOT, + FIST_BASE_ATTACK, + PVP_BLESSING_THRESHOLD, + FAIRFIGHT_TIMERANGE, + DEFAULT_DEPOT_SIZE_PREMIUM, + DEFAULT_DEPOT_SIZE, + MAIL_ATTEMPTS_FADE, + MAIL_ATTEMPTS, + MAIL_BLOCK, + ALLOWED_MAX_PACKETS, + ROOK_TOWN, + ROOK_LEVELTO, + ROOK_TOLEVEL, + SERVICE_THREADS, + MARKET_OFFER_DURATION, + CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES, + MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER, LAST_NUMBER_CONFIG /* this must be the last one */ }; @@ -189,6 +227,8 @@ class ConfigManager enum bool_config_t { GLOBALSAVE_ENABLED = 0, + SCRIPT_SYSTEM, + START_CLOSED, START_CHOOSEVOC, ON_OR_OFF_CHARLIST, ONE_PLAYER_ON_ACCOUNT, @@ -199,23 +239,23 @@ class ConfigManager SHUTDOWN_AT_GLOBALSAVE, CLEAN_MAP_AT_GLOBALSAVE, FREE_PREMIUM, - ADMIN_LOGS_ENABLED, GENERATE_ACCOUNT_NUMBER, + GENERATE_ACCOUNT_SALT, BANK_SYSTEM, - REMOVE_PREMIUM_ON_INIT, + INIT_PREMIUM_UPDATE, TELEPORT_SUMMONS, TELEPORT_PLAYER_SUMMONS, PVP_TILE_IGNORE_PROTECTION, DISPLAY_CRITICAL_HIT, - ADVANCING_SKILL_LEVEL, CLEAN_PROTECTED_ZONES, SPELL_NAME_INSTEAD_WORDS, EMOTE_SPELLS, REPLACE_KICK_ON_LOGIN, PREMIUM_FOR_PROMOTION, - SHOW_HEALING_DAMAGE, - BROADCAST_BANISHMENTS, + SHOW_HEALTH_CHANGE, + SHOW_MANA_CHANGE, SAVE_GLOBAL_STORAGE, + SAVE_PLAYER_DATA, INGAME_GUILD_MANAGEMENT, HOUSE_BUY_AND_SELL, HOUSE_NEED_PREMIUM, @@ -228,39 +268,61 @@ class ConfigManager AIMBOT_HOTKEY_ENABLED, FORCE_CLOSE_SLOW_CONNECTION, EXPERIENCE_STAGES, + BLESSINGS, BLESSING_ONLY_PREMIUM, BED_REQUIRE_PREMIUM, ALLOW_CHANGECOLORS, LOGIN_ONLY_LOGINSERVER, STOP_ATTACK_AT_EXIT, DISABLE_OUTFITS_PRIVILEGED, - OPTIMIZE_DB_AT_STARTUP, - OLD_CONDITION_ACCURACY, + OPTIMIZE_DATABASE, STORE_TRASH, - HOUSE_STORAGE, - TRUNCATE_LOGS, + TRUNCATE_LOG, TRACER_BOX, STORE_DIRECTION, DISPLAY_LOGGING, STAMINA_BONUS_PREMIUM, - BAN_UNKNOWN_BYTES, ALLOW_CHANGEADDONS, GHOST_INVISIBLE_EFFECT, - SHOW_HEALING_DAMAGE_MONSTER, + SHOW_HEALTH_CHANGE_MONSTER, + SHOW_MANA_CHANGE_MONSTER, CHECK_CORPSE_OWNER, BUFFER_SPELL_FAILURE, - CONFIM_OUTDATED_VERSION, + CONFIRM_OUTDATED_VERSION, PREMIUM_SKIP_WAIT, GUILD_HALLS, DEATH_LIST, - BIND_IP_ONLY, GHOST_SPELL_EFFECTS, PVPZONE_ADDMANASPENT, + PVPZONE_RECOVERMANA, USE_BLACK_SKULL, + USE_FRAG_HANDLER, ALLOW_FIGHTBACK, VIPLIST_PER_PLAYER, - USE_FRAG_HANDLER, + MANAGER_LOCALHOST_ONLY, + MANAGER_LOGS, + ADMIN_LOGS, + ADMIN_LOCALHOST_ONLY, + ADMIN_REQUIRE_LOGIN, ADDONS_PREMIUM, + OPTIONAL_WAR_ATTACK_ALLY, + BIND_ONLY_GLOBAL_ADDRESS, + UNIFIED_SPELLS, + ALLOW_MOUNTS, + UNMOUNT_PLAYER_IN_PZ, + ENABLE_COOLDOWNS, + MONSTER_SPAWN_WALKBACK, + USE_CAPACITY, + DAEMONIZE, + TIBIA_SLOTS, + SKIP_ITEMS_VERSION, + SILENT_LUA, + HOUSE_SKIP_INIT_RENT, + HOUSE_PROTECTION, + ROOK_SYSTEM, + FAIRFIGHT_REDUCTION, + MARKET_ENABLED, + MARKET_PREMIUM, LAST_BOOL_CONFIG /* this must be the last one */ }; @@ -268,41 +330,46 @@ class ConfigManager bool reload(); void startup() {m_startup = false;} + bool isRunning() const {return !m_startup;} + bool isLoaded() const {return m_loaded;} + const std::string& getString(uint32_t _what) const; bool getBool(uint32_t _what) const; - int32_t getNumber(uint32_t _what) const; + int64_t getNumber(uint32_t _what) const; double getDouble(uint32_t _what) const; bool setString(uint32_t _what, const std::string& _value); - bool setNumber(uint32_t _what, int32_t _value); + bool setNumber(uint32_t _what, int64_t _value); + bool setBool(uint32_t _what, bool _value); - void getValue(const std::string& key, lua_State* _L) {LuaScriptInterface::getValue(key, L, _L);} + void getValue(const std::string& key, lua_State* _L) {LuaInterface::getValue(key, L, _L);} private: + static void moveValue(lua_State* fromL, lua_State* toL); + std::string getGlobalString(const std::string& _identifier, const std::string& _default = "") { - return LuaScriptInterface::getGlobalString(L, _identifier, _default); + return LuaInterface::getGlobalString(L, _identifier, _default); } bool getGlobalBool(const std::string& _identifier, bool _default = false) { - return LuaScriptInterface::getGlobalBool(L, _identifier, _default); + return LuaInterface::getGlobalBool(L, _identifier, _default); } - int32_t getGlobalNumber(const std::string& _identifier, const int32_t _default = 0) + int64_t getGlobalNumber(const std::string& _identifier, const int64_t _default = 0) { - return LuaScriptInterface::getGlobalNumber(L, _identifier, _default); + return LuaInterface::getGlobalNumber(L, _identifier, _default); } double getGlobalDouble(const std::string& _identifier, const double _default = 0) { - return LuaScriptInterface::getGlobalDouble(L, _identifier, _default); + return LuaInterface::getGlobalDouble(L, _identifier, _default); } bool m_loaded, m_startup; lua_State* L; - static void moveValue(lua_State* fromL, lua_State* toL); std::string m_confString[LAST_STRING_CONFIG]; bool m_confBool[LAST_BOOL_CONFIG]; - int32_t m_confNumber[LAST_NUMBER_CONFIG]; + int64_t m_confNumber[LAST_NUMBER_CONFIG]; double m_confDouble[LAST_DOUBLE_CONFIG]; }; #endif diff --git a/configure.ac b/configure.ac index 4b1019c..9aa5460 100644 --- a/configure.ac +++ b/configure.ac @@ -1,12 +1,17 @@ AC_PREREQ([2.50]) -AC_INIT([TheForgottenServer], [0.3.5]) -AM_INIT_AUTOMAKE([1.10 foreign]) +AC_INIT([TheForgottenServer], [0.4]) +AM_INIT_AUTOMAKE([1.10 foreign silent-rules]) AC_CONFIG_SRCDIR([account.h]) AM_CONFIG_HEADER([config.h]) +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) AC_PROG_CXX AC_LANG(C++) +OPTIONAL_FLAGS="" +DEBUG_FLAGS="-O0 -march=native -fomit-frame-pointer" +#DEBUG_FLAGS="-O3 -march=native -fomit-frame-pointer" + # check if we want server diag enabled serverDiag=no AC_ARG_ENABLE(server-diag, [ --enable-server-diag enable server diagnostic], @@ -23,13 +28,13 @@ AC_ARG_ENABLE(login-server, [ --enable-login-server enable login server mode], loginServer=yes ) -# check if we want remote control feature -AM_CONDITIONAL(REMOTE_CONTROL, false) -remoteControl=no -AC_ARG_ENABLE(remote-control, [ --enable-remote-control enable remote control feature], - [OPTIONAL_FLAGS="$OPTIONAL_FLAGS -D__REMOTE_CONTROL__"] - AM_CONDITIONAL(REMOTE_CONTROL, true) - remoteControl=yes +# check if we want OTAdmin protocol +AM_CONDITIONAL(OT_ADMIN, false) +otAdmin=no +AC_ARG_ENABLE(ot-admin, [ --enable-ot-admin enable OTAdmin protocol], + [OPTIONAL_FLAGS="$OPTIONAL_FLAGS -D__OTADMIN__"] + AM_CONDITIONAL(OT_ADMIN, true) + otAdmin=yes ) # check if we want otserv allocator enabled @@ -52,49 +57,62 @@ AC_ARG_ENABLE(root-permission, [ --enable-root-permission enable running on roo [OPTIONAL_FLAGS="$OPTIONAL_FLAGS -D__ROOT_PERMISSION__"] rootPermission=yes ) -AC_SUBST(OPTIONAL_FLAGS) + +# check if we want to use luajit instead of lua +luaJIT=no +AC_ARG_ENABLE(luajit, [ --enable-luajit enable luajit instead of lua], + [OPTIONAL_FLAGS="$OPTIONAL_FLAGS -D__LUAJIT__"] + luaJIT=yes +) + +# check if we want to cache ground items +groundCache=no +AC_ARG_ENABLE(groundcache, [ --enable-groundcache enable caching of ground items], + [OPTIONAL_FLAGS="$OPTIONAL_FLAGS -D__GROUND_CACHE__"] + groundCache=yes +) # check if we want a debug build debugBuild=no -AC_ARG_ENABLE(debug, [ --enable-debug enable debuging], - [DEBUG_FLAGS="-D__DEBUG__ -D__DEBUG_MOVESYS__ -D__DEBUG_CHAT__ -D__DEBUG_EXCEPTION_REPORT__ -D__DEBUG_HOUSES__ -D__DEBUG_LUASCRIPTS__ -D__DEBUG_MAILBOX__ -D__DEBUG_NET__ -D__DEBUG_NET_DETAIL__ -D__DEBUG_RAID__ -D__DEBUG_SCHEDULER__ -D__DEBUG_SPAWN__ -D__SQL_QUERY_DEBUG__"] +AC_ARG_ENABLE(debug, [ --enable-debug enable debuging], + [DEBUG_FLAGS="-D__DEBUG__ -D__DEBUG_MOVESYS__ -D__DEBUG_CHAT__ -D__DEBUG_HOUSES__ -D__DEBUG_LUASCRIPTS__ -D__DEBUG_MAILBOX__ -D__DEBUG_NET__ -D__DEBUG_NET_DETAIL__ -D__DEBUG_RAID__ -D__DEBUG_SCHEDULER__ -D__DEBUG_SPAWN__ -D__SQL_QUERY_DEBUG__ -O0 -g3"] debugBuild=yes ) AC_SUBST(DEBUG_FLAGS) AM_CONDITIONAL(USE_MYSQL, false) useMySQL=no +AM_CONDITIONAL(USE_MYSQLPP, false) +useMySQLpp=no AM_CONDITIONAL(USE_PGSQL, false) usePostgreSQL=no -AM_CONDITIONAL(USE_ODBC, false) -useODBC=no AM_CONDITIONAL(USE_SQLITE, false) useSQLite=no # check if we want mysql enabled -AC_ARG_ENABLE(mysql, [ --enable-mysql enable MySQL support], [ +AC_ARG_ENABLE(mysql, [ --enable-mysql enable MySQL support], [ AM_CONDITIONAL(USE_MYSQL, true) MYSQL_FLAGS=-D__USE_MYSQL__ AC_SUBST(MYSQL_FLAGS) useMySQL=yes ]) +# check if we want mysql++ enabled +AC_ARG_ENABLE(mysqlpp, [ --enable-mysqlpp enable MySQL++Connector support], [ + AM_CONDITIONAL(USE_MYSQLPP, true) + MYSQLPP_FLAGS=-D__USE_MYSQLPP__ + AC_SUBST(MYSQLPP_FLAGS) + useMySQLpp=yes +]) + # check if we want postgresql enabled -AC_ARG_ENABLE(pgsql, [ --enable-pgsql enable PostgreSQL support], [ +AC_ARG_ENABLE(pgsql, [ --enable-pgsql enable PostgreSQL support], [ AM_CONDITIONAL(USE_PGSQL, true) - PGSQL_FLAGS=-D__USE_PGSQL__ + PGSQL_FLAGS="-D__USE_PGSQL__ -I`pg_config --includedir`" AC_SUBST(PGSQL_FLAGS) usePostgreSQL=yes ]) -# check if we want odbc enabled -AC_ARG_ENABLE(odbc, [ --enable-odbc enable ODBC support], [ - AM_CONDITIONAL(USE_ODBC, true) - ODBC_FLAGS=-D__USE_ODBC__ - AC_SUBST(ODBC_FLAGS) - useODBC=yes -]) - # check if we want sqlite enabled AC_ARG_ENABLE(sqlite, [ --enable-sqlite enable SQLite support], [ AM_CONDITIONAL(USE_SQLITE, true) @@ -103,7 +121,7 @@ AC_ARG_ENABLE(sqlite, [ --enable-sqlite enable SQLite support], [ useSQLite=yes ]) -#check if we want the profiler +# check if we want the profiler AC_ARG_ENABLE(profiler, [ --enable-profiler enable profiler support], [PROFILER_FLAGS=-pg]) AC_SUBST(PROFILER_FLAGS) @@ -130,17 +148,25 @@ AC_CHECK_TYPES([ptrdiff_t]) AC_FUNC_MALLOC AC_FUNC_MEMCMP AC_FUNC_REALLOC -AC_CHECK_FUNCS([floor ftime gethostbyname gethostname memset pow sqrt strcasecmp strncasecmp strstr strtol]) +AC_CHECK_FUNCS([ceil floor ftime gethostbyname gethostname memset pow sqrt strcasecmp strncasecmp strstr strtol]) -# check for libxml2 -AM_PATH_XML2(2.6.5, , AC_MSG_ERROR([you need libxml2 >= 2.6.5 to compile theforgottenserver!])) +# check for xml2 +AM_PATH_XML2(2.6.5, , AC_MSG_ERROR([You need libxml2 >= 2.6.5 to compile theforgottenserver!])) LIBS="$LIBS $XML_LIBS" -#check GMP -AC_CHECK_HEADERS([gmp.h], ,[AC_MSG_ERROR("GMP header not found.")]) -AC_CHECK_LIB(gmp, __gmpz_init2, ,[AC_MSG_ERROR("Linking against GMP failed.")]) +# check for zlib +AC_CHECK_HEADERS([zlib.h], ,[AC_MSG_ERROR("zlib header not found.")]) +AC_CHECK_LIB(z, main, ,[AC_MSG_ERROR("Linking against zlib library failed.")]) + +# check for pthread +AC_CHECK_HEADERS([pthread.h], ,[AC_MSG_ERROR("pthread header not found.")]) +AC_CHECK_LIB(pthread, main, ,[AC_MSG_ERROR("Linking against pthread library failed)]) + +# check for OpenSSL +AC_CHECK_HEADERS([openssl/rsa.h openssl/bn.h openssl/err.h openssl/sha.h openssl/md5.h], , [AC_MSG_ERROR("Required OpenSSL headers not found.")]) +AC_CHECK_LIB(crypto, main, , [AC_MSG_ERROR("Linking against OpenSSL library failed")]) -#check for boost +# check for boost AC_CHECK_LIB(boost_thread-gcc-mt, main, , [ AC_CHECK_LIB(boost_thread-mt, main, , [ AC_CHECK_LIB(boost_thread, main, , [ @@ -181,44 +207,88 @@ AC_CHECK_LIB(boost_filesystem-gcc-mt, main, , [ ]) ]) -# check for boost::asio -AC_CHECK_HEADERS([boost/asio.hpp], , [AC_MSG_ERROR("boost::asio header not found.")]) +# check for boost::unordered_set (should only check if gnu compiler version is below 4) +# AC_CHECK_HEADERS([boost/tr1/unordered_set.hpp], , [AC_MSG_ERROR("boost::unordered_set header not found. Please update your boost to at least 1.40.")]) -# check for boost::unordered_set -AC_CHECK_HEADERS([boost/tr1/unordered_set.hpp], , [AC_MSG_ERROR("boost::unordered_set header not found.")]) +# check for Lua +if test "$luaJIT" = "yes" ; then + PKG_CHECK_MODULES(LUA, luajit, , [ + AC_CHECK_HEADERS([luajit-2.0/lua.hpp], ,[AC_MSG_ERROR("LuaJIT header not found.")]) + AC_CHECK_LIB(luajit, main, , [ + AC_CHECK_LIB(luajit-5.1, main, , [AC_MSG_ERROR("Linking against LuaJIT library failed.")]) + ]) + ]) +else + PKG_CHECK_MODULES(LUA, lua5.1 >= 5.1, , [ + AC_CHECK_HEADERS([lua.h], , [ + OPTIONAL_FLAGS="$OPTIONAL_FLAGS -D__ALT_LUA_PATH__" + AC_CHECK_HEADERS([lua5.1/lua.h], , [AC_MSG_ERROR("Lua header not found.")]) + ]) + AC_CHECK_LIB(lua, main, , [ + AC_CHECK_LIB(lua5.1, main, , [AC_MSG_ERROR("Linking against Lua library failed.")]) + ]) + ]) +fi -#check lua 5.1 -PKG_CHECK_MODULES(LUA, lua5.1 >= 5.1, , [ - AC_CHECK_HEADERS([lua.hpp], , [AC_MSG_ERROR("lua header not found.")]) - AC_CHECK_LIB(lua, main, , [AC_MSG_ERROR("Linking against lua failed.")]) -]) +AC_SUBST(OPTIONAL_FLAGS) AC_SUBST(LUA_CFLAGS) AC_SUBST(LUA_LIBS) +if test "$useMySQL" = "no" ; then + if test "$useMySQLpp" = "no" ; then + if test "$useSQLite" = "no" ; then + if test "$usePostgreSQL" = "no" ; then + AM_CONDITIONAL(USE_SQLITE, true) + SQLITE_FLAGS=-D__USE_SQLITE__ + AC_SUBST(SQLITE_FLAGS) + useSQLite=yes + + echo WARNING: Any of available database drivers was enabled, using SQLite as default. To enable a database driver, configure with --enable-\ \(example: --enable-mysqlpp\) + echo + fi + fi + fi +fi + # check for mysql if it is enabled if test -n "$MYSQL_FLAGS"; then - AC_CHECK_HEADERS([mysql/mysql.h],[MYSQL_LIBS=-lmysqlclient],[AC_MSG_ERROR("mysql headers missing.")]) - AC_CHECK_LIB(mysqlclient, main,[],[AC_MSG_ERROR("Linking against mysql-client failed.")]) + AC_CHECK_HEADERS([mysql/mysql.h],[MYSQL_LIBS=-lmysqlclient],[AC_MSG_ERROR("MySQL headers missing.")]) + AC_CHECK_LIB(mysqlclient, main,[],[ + if test -d /usr/lib64/mysql ; then + echo "NOTICE -- Copying mysqlclient files into the /usr/lib64 directory." + echo "NOTICE -- If mysqlclient is not found, please re-run the configure script." + cp -Rsf /usr/lib64/mysql/libmysqlclient* /usr/lib64 + AC_CHECK_LIB(mysqlclient, main,[], [AC_MSG_ERROR("Linking against mysqlclient failed.")]) + elif test -d /usr/lib/mysql ; then + echo "NOTICE -- Copying mysqlclient files into the /usr/lib directory." + echo "NOTICE -- If mysqlclient is not found, please re-run the configure script." + cp -Rsf /usr/lib/mysql/libmysqlclient* /usr/lib + AC_CHECK_LIB(mysqlclient, main,[], [AC_MSG_ERROR("Linking against mysqlclient failed.")]) + else + AC_MSG_ERROR("Linking against mysql-client failed.") + fi + ]) + AC_SUBST(MYSQL_LIBS) fi +# check for mysqlcppconn if it is enabled +if test -n "$MYSQLPP_FLAGS"; then + AC_CHECK_HEADERS([cppconn/config.h],[MYSQLPP_LIBS=-lmysqlcppconn],[AC_MSG_ERROR("MySQL++Connector headers missing.")]) + AC_CHECK_LIB(mysqlcppconn, main,[],[AC_MSG_ERROR("Linking against libmysqlcppconn failed.")]) + AC_SUBST(MYSQLPP_LIBS) +fi + # check for postgresql if it is enabled if test -n "$PGSQL_FLAGS"; then - AC_CHECK_HEADERS([postgresql/libpq-fe.h],[PGSQL_LIBS=-lpg],[AC_MSG_ERROR("postgresql headers missing.")]) + AC_CHECK_HEADERS([`pg_config --includedir`/libpq-fe.h],[PGSQL_LIBS=-lpg],[AC_MSG_ERROR("PostgreSQL headers missing.")]) AC_CHECK_LIB(pq, main,[],[AC_MSG_ERROR("Linking against libpq failed.")]) AC_SUBST(PGSQL_LIBS) fi -# check for odbc if it is enabled -if test -n "$ODBC_FLAGS"; then - AC_CHECK_HEADERS([sql.h sqlext.h sqltypes.h],[ODBC_LIBS=-lodbc],[AC_MSG_ERROR("unixodbc headers missing.")]) - AC_CHECK_LIB(odbc, main, [], [AC_MSG_ERROR("Linking against odbc failed.")]) - AC_SUBST(ODBC_LIBS) -fi - # check for sqlite if it is enabled if test -n "$SQLITE_FLAGS"; then - AC_CHECK_HEADERS([sqlite3.h],[SQLITE_LIBS=-lsqlite3],[AC_MSG_ERROR("sqlite3 headers missing.")]) + AC_CHECK_HEADERS([sqlite3.h],[SQLITE_LIBS=-lsqlite3],[AC_MSG_ERROR("SQLite3 headers missing.")]) AC_CHECK_LIB(sqlite3, main,[],[AC_MSG_ERROR("Linking against sqlite3 failed.")]) AC_SUBST(SQLITE_LIBS) fi @@ -227,30 +297,23 @@ AC_CONFIG_FILES([Makefile]) AC_OUTPUT echo -#echo The Forgotten Server 0.3.5 +#echo The Forgotten Server 0.4 echo $PACKAGE $VERSION echo +echo Using LuaJIT................ : $luaJIT +echo Using ground cache.......... : $groundCache echo Server diagnostics.......... : $serverDiag -echo Login server mode........... : $loginServer -echo Remote control.............. : $remoteControl -echo Root run permission......... : $rootPermission echo Home-directory configuration : $homedirConf +echo Root run permission......... : $rootPermission +echo Login server mode........... : $loginServer +echo OTAdmin protocol............ : $otAdmin echo OTServ custom allocator..... : $otservAllocator echo Debug build................. : $debugBuild echo echo Build with MySQL............ : $useMySQL +echo Build with MySQL++Connector. : $useMySQLpp echo Build with SQLite........... : $useSQLite echo Build with PostgreSQL....... : $usePostgreSQL -echo Build with ODBC............. : $useODBC echo -if test "$useMySQL" = "no" ; then - if test "$useSQLite" = "no" ; then - if test "$usePostgreSQL" = "no" ; then - if test "$useODBC" = "no" ; then - echo Warning: no database driver was enabled! To enable a database driver, configure with --enable-\ \(example: --enable-mysql\). - echo - fi - fi - fi -fi -echo Configure complete, now you may type \'make\'. + +echo Configure complete, now you may type \'./build.sh\'. diff --git a/connection.cpp b/connection.cpp index 64accc6..35863c9 100644 --- a/connection.cpp +++ b/connection.cpp @@ -43,7 +43,7 @@ Connection_ptr ConnectionManager::createConnection(boost::asio::ip::tcp::socket* boost::asio::io_service& io_service, ServicePort_ptr servicer) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Creating new Connection" << std::endl; + std::clog << "Creating new Connection" << std::endl; #endif boost::recursive_mutex::scoped_lock lockClass(m_connectionManagerLock); Connection_ptr connection = boost::shared_ptr(new Connection(socket, io_service, servicer)); @@ -55,21 +55,21 @@ Connection_ptr ConnectionManager::createConnection(boost::asio::ip::tcp::socket* void ConnectionManager::releaseConnection(Connection_ptr connection) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Releasing connection" << std::endl; + std::clog << "Releasing connection" << std::endl; #endif boost::recursive_mutex::scoped_lock lockClass(m_connectionManagerLock); - std::list::iterator it =std::find(m_connections.begin(), m_connections.end(), connection); + std::list::iterator it = std::find(m_connections.begin(), m_connections.end(), connection); if(it != m_connections.end()) m_connections.erase(it); else - std::cout << "[Error - ConnectionManager::releaseConnection] Connection not found" << std::endl; + std::clog << "[Error - ConnectionManager::releaseConnection] Connection not found" << std::endl; } void ConnectionManager::shutdown() { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Closing all connections" << std::endl; + std::clog << "Closing all connections" << std::endl; #endif boost::recursive_mutex::scoped_lock lockClass(m_connectionManagerLock); for(std::list::iterator it = m_connections.begin(); it != m_connections.end(); ++it) @@ -80,7 +80,7 @@ void ConnectionManager::shutdown() (*it)->m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, error); (*it)->m_socket->close(error); } - catch(boost::system::system_error&) {} + catch(std::exception&) {} } m_connections.clear(); @@ -90,7 +90,7 @@ void Connection::close() { //any thread #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Connection::close" << std::endl; + std::clog << "Connection::close" << std::endl; #endif boost::recursive_mutex::scoped_lock lockClass(m_connectionLock); if(m_connectionState == CONNECTION_STATE_CLOSED || m_connectionState == CONNECTION_STATE_REQUEST_CLOSE) @@ -127,7 +127,7 @@ void ConnectionManager::addAttempt(uint32_t clientIp, int32_t protocolId, bool s ipLoginMap[clientIp] = tmp; it = ipLoginMap.find(clientIp); - } + } if(it->second.loginsAmount > g_config.getNumber(ConfigManager::LOGIN_TRIES)) it->second.loginsAmount = 0; @@ -160,7 +160,7 @@ bool ConnectionManager::acceptConnection(uint32_t clientIp) ipConnectMap[clientIp] = tmp; return true; - } + } it->second.count++; if(it->second.blockTime > currentTime) @@ -186,12 +186,12 @@ void Connection::closeConnection() { //dispatcher thread #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Connection::closeConnection" << std::endl; + std::clog << "Connection::closeConnection" << std::endl; #endif m_connectionLock.lock(); if(m_connectionState != CONNECTION_STATE_REQUEST_CLOSE) { - std::cout << "[Error - Connection::closeConnection] m_connectionState = " << m_connectionState << std::endl; + std::clog << "[Error - Connection::closeConnection] m_connectionState = " << m_connectionState << std::endl; m_connectionLock.unlock(); return; } @@ -217,13 +217,13 @@ void Connection::closeConnection() void Connection::closeSocket() { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Connection::closeSocket" << std::endl; + std::clog << "Connection::closeSocket" << std::endl; #endif m_connectionLock.lock(); if(m_socket->is_open()) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Closing socket" << std::endl; + std::clog << "Closing socket" << std::endl; #endif m_pendingRead = m_pendingWrite = 0; try @@ -233,14 +233,18 @@ void Connection::closeSocket() if(error) { if(error != boost::asio::error::not_connected) + { PRINT_ASIO_ERROR("Shutdown"); + } } m_socket->close(error); if(error) + { PRINT_ASIO_ERROR("Close"); + } } - catch(boost::system::system_error& e) + catch(std::exception& e) { if(m_logError) { @@ -278,7 +282,7 @@ void Connection::onStop() m_socket->close(); } } - catch(boost::system::system_error&) {} + catch(std::exception&) {} delete m_socket; m_socket = NULL; @@ -294,7 +298,7 @@ void Connection::deleteConnection() { m_service.dispatch(boost::bind(&Connection::onStop, this)); } - catch(boost::system::system_error& e) + catch(std::exception& e) { if(m_logError) { @@ -322,10 +326,10 @@ void Connection::accept() // Read size of the first packet boost::asio::async_read(getHandle(), - boost::asio::buffer(m_msg.getBuffer(), NetworkMessage::headerLength), + boost::asio::buffer(m_msg.buffer(), NETWORK_HEADER_SIZE), boost::bind(&Connection::parseHeader, shared_from_this(), boost::asio::placeholders::error)); } - catch(boost::system::system_error& e) + catch(std::exception& e) { if(m_logError) { @@ -342,7 +346,7 @@ void Connection::parseHeader(const boost::system::error_code& error) m_readTimer.cancel(); int32_t size = m_msg.decodeHeader(); - if(error || size <= 0 || size >= NETWORKMESSAGE_MAXSIZE - 16) + if(error || size <= 0 || size >= NETWORK_MAX_SIZE - 16) handleReadError(error); if(m_connectionState != CONNECTION_STATE_OPEN || m_readError) @@ -361,11 +365,11 @@ void Connection::parseHeader(const boost::system::error_code& error) boost::weak_ptr(shared_from_this()), boost::asio::placeholders::error)); // Read packet content - m_msg.setMessageLength(size + NetworkMessage::headerLength); - boost::asio::async_read(getHandle(), boost::asio::buffer(m_msg.getBodyBuffer(), size), + m_msg.setSize(size + NETWORK_HEADER_SIZE); + boost::asio::async_read(getHandle(), boost::asio::buffer(m_msg.bodyBuffer(), size), boost::bind(&Connection::parsePacket, shared_from_this(), boost::asio::placeholders::error)); } - catch(boost::system::system_error& e) + catch(std::exception& e) { if(m_logError) { @@ -393,14 +397,14 @@ void Connection::parsePacket(const boost::system::error_code& error) } --m_pendingRead; - uint32_t length = m_msg.getMessageLength() - m_msg.getReadPos() - 4, checksumReceived = m_msg.PeekU32(), checksum = 0; + uint32_t length = m_msg.size() - m_msg.position() - 4, checksumReceived = m_msg.get(true), checksum = 0; if(length > 0) - checksum = adlerChecksum((uint8_t*)(m_msg.getBuffer() + m_msg.getReadPos() + 4), length); + checksum = adlerChecksum((uint8_t*)(m_msg.buffer() + m_msg.position() + 4), length); bool checksumEnabled = false; if(checksumReceived == checksum) { - m_msg.SkipBytes(4); + m_msg.skip(4); checksumEnabled = true; } @@ -422,7 +426,7 @@ void Connection::parsePacket(const boost::system::error_code& error) m_protocol->setConnection(shared_from_this()); } else - m_msg.SkipBytes(1); // Skip protocol + m_msg.skip(1); // Skip protocol m_protocol->onRecvFirstMessage(m_msg); } @@ -438,10 +442,10 @@ void Connection::parsePacket(const boost::system::error_code& error) // Wait to the next packet boost::asio::async_read(getHandle(), - boost::asio::buffer(m_msg.getBuffer(), NetworkMessage::headerLength), + boost::asio::buffer(m_msg.buffer(), NETWORK_HEADER_SIZE), boost::bind(&Connection::parseHeader, shared_from_this(), boost::asio::placeholders::error)); } - catch(boost::system::system_error& e) + catch(std::exception& e) { if(m_logError) { @@ -457,7 +461,7 @@ void Connection::parsePacket(const boost::system::error_code& error) bool Connection::send(OutputMessage_ptr msg) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Connection::send init" << std::endl; + std::clog << "Connection::send init" << std::endl; #endif m_connectionLock.lock(); if(m_connectionState != CONNECTION_STATE_OPEN || m_writeError) @@ -473,19 +477,19 @@ bool Connection::send(OutputMessage_ptr msg) msg->getProtocol()->onSendMessage(msg); #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Connection::send " << msg->getMessageLength() << std::endl; + std::clog << "Connection::send " << msg->size() << std::endl; #endif internalSend(msg); } else if(m_pendingWrite > 100 && g_config.getBool(ConfigManager::FORCE_CLOSE_SLOW_CONNECTION)) { - std::cout << "NOTICE: Forcing slow connection to disconnect!" << std::endl; + std::clog << "NOTICE: Forcing slow connection to disconnect!" << std::endl; close(); } else - { + { #ifdef __DEBUG_NET__ - std::cout << "Connection::send Adding to queue " << msg->getMessageLength() << std::endl; + std::clog << "Connection::send Adding to queue " << msg->size() << std::endl; #endif OutputMessagePool::getInstance()->autoSend(msg); } @@ -505,10 +509,10 @@ void Connection::internalSend(OutputMessage_ptr msg) boost::weak_ptr(shared_from_this()), boost::asio::placeholders::error)); boost::asio::async_write(getHandle(), - boost::asio::buffer(msg->getOutputBuffer(), msg->getMessageLength()), + boost::asio::buffer(msg->getOutputBuffer(), msg->size()), boost::bind(&Connection::onWrite, shared_from_this(), msg, boost::asio::placeholders::error)); } - catch(boost::system::system_error& e) + catch(std::exception& e) { if(m_logError) { @@ -530,10 +534,22 @@ uint32_t Connection::getIP() const return 0; } +uint32_t Connection::getEndpoint() const +{ + //ip is expressed in network byte order + boost::system::error_code error; + const boost::asio::ip::tcp::endpoint ip = m_socket->local_endpoint(error); + if(!error) + return htonl(ip.address().to_v4().to_ulong()); + + PRINT_ASIO_ERROR("Getting local ip"); + return 0; +} + void Connection::onWrite(OutputMessage_ptr msg, const boost::system::error_code& error) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "onWrite" << std::endl; + std::clog << "onWrite" << std::endl; #endif m_connectionLock.lock(); m_writeTimer.cancel(); @@ -608,7 +624,7 @@ void Connection::handleReadTimeout(boost::weak_ptr weak, const boost if(shared_ptr connection = weak.lock()) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Connection::handleReadTimeout" << std::endl; + std::clog << "Connection::handleReadTimeout" << std::endl; #endif connection->onReadTimeout(); } @@ -646,7 +662,7 @@ void Connection::handleWriteTimeout(boost::weak_ptr weak, const boos if(shared_ptr connection = weak.lock()) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Connection::handleWriteTimeout" << std::endl; + std::clog << "Connection::handleWriteTimeout" << std::endl; #endif connection->onWriteTimeout(); } diff --git a/connection.h b/connection.h index fbceb97..c06c433 100644 --- a/connection.h +++ b/connection.h @@ -37,8 +37,7 @@ typedef boost::shared_ptr ServicePort_ptr; #ifdef __DEBUG_NET__ #define PRINT_ASIO_ERROR(description) \ - std::cout << "[Error - " << __FUNCTION__ << "] " << description << " - " - << error.message() << " (" << error.message() << ")" << std::endl; + std::clog << "[Error - " << __FUNCTION__ << "] " << description << " - " << error.message() << " (" << error.message() << ")" << std::endl; #else #define PRINT_ASIO_ERROR(x) #endif @@ -131,6 +130,7 @@ class Connection : public boost::enable_shared_from_this, boost::non boost::asio::ip::tcp::socket& getHandle() {return *m_socket;} uint32_t getIP() const; + uint32_t getEndpoint() const; void handle(Protocol* protocol); void accept(); diff --git a/const.h b/const.h index bc002df..64a47be 100644 --- a/const.h +++ b/const.h @@ -19,177 +19,222 @@ #define __CONST__ #include "definitions.h" +enum OperatingSystem_t +{ + CLIENTOS_LINUX = 0x01, + CLIENTOS_WINDOWS = 0x02, + CLIENTOS_FLASH = 0x03 +}; + +enum ChannelEvent_t +{ + CHANNELEVENT_JOIN = 0, + CHANNELEVENT_LEAVE = 1, + CHANNELEVENT_INVITE = 2, + CHANNELEVENT_EXCLUDE = 3 +}; + +enum ReportType_t +{ + REPORT_NAME = 0x00, + REPORT_STATEMENT = 0x01, + REPORT_BOT = 0x02 +}; + enum MagicEffect_t { - MAGIC_EFFECT_DRAW_BLOOD = 0x00, - MAGIC_EFFECT_LOSE_ENERGY = 0x01, - MAGIC_EFFECT_POFF = 0x02, - MAGIC_EFFECT_BLOCKHIT = 0x03, - MAGIC_EFFECT_EXPLOSION_AREA = 0x04, + MAGIC_EFFECT_DRAW_BLOOD = 0x00, + MAGIC_EFFECT_LOSE_ENERGY = 0x01, + MAGIC_EFFECT_POFF = 0x02, + MAGIC_EFFECT_BLOCKHIT = 0x03, + MAGIC_EFFECT_EXPLOSION_AREA = 0x04, MAGIC_EFFECT_EXPLOSION_DAMAGE = 0x05, - MAGIC_EFFECT_FIRE_AREA = 0x06, - MAGIC_EFFECT_YELLOW_RINGS = 0x07, - MAGIC_EFFECT_POISON_RINGS = 0x08, - MAGIC_EFFECT_HIT_AREA = 0x09, - MAGIC_EFFECT_TELEPORT = 0x0A, //10 - MAGIC_EFFECT_ENERGY_DAMAGE = 0x0B, //11 - MAGIC_EFFECT_WRAPS_BLUE = 0x0C, //12 - MAGIC_EFFECT_WRAPS_RED = 0x0D, //13 - MAGIC_EFFECT_WRAPS_GREEN = 0x0E, //14 - MAGIC_EFFECT_HITBY_FIRE = 0x0F, //15 - MAGIC_EFFECT_POISON = 0x10, //16 - MAGIC_EFFECT_MORT_AREA = 0x11, //17 - MAGIC_EFFECT_SOUND_GREEN = 0x12, //18 - MAGIC_EFFECT_SOUND_RED = 0x13, //19 - MAGIC_EFFECT_POISON_AREA = 0x14, //20 - MAGIC_EFFECT_SOUND_YELLOW = 0x15, //21 - MAGIC_EFFECT_SOUND_PURPLE = 0x16, //22 - MAGIC_EFFECT_SOUND_BLUE = 0x17, //23 - MAGIC_EFFECT_SOUND_WHITE = 0x18, //24 - MAGIC_EFFECT_BUBBLES = 0x19, //25 - MAGIC_EFFECT_CRAPS = 0x1A, //26 - MAGIC_EFFECT_GIFT_WRAPS = 0x1B, //27 + MAGIC_EFFECT_FIRE_AREA = 0x06, + MAGIC_EFFECT_YELLOW_RINGS = 0x07, + MAGIC_EFFECT_POISON_RINGS = 0x08, + MAGIC_EFFECT_HIT_AREA = 0x09, + MAGIC_EFFECT_TELEPORT = 0x0A, //10 + MAGIC_EFFECT_ENERGY_DAMAGE = 0x0B, //11 + MAGIC_EFFECT_WRAPS_BLUE = 0x0C, //12 + MAGIC_EFFECT_WRAPS_RED = 0x0D, //13 + MAGIC_EFFECT_WRAPS_GREEN = 0x0E, //14 + MAGIC_EFFECT_HITBY_FIRE = 0x0F, //15 + MAGIC_EFFECT_POISON = 0x10, //16 + MAGIC_EFFECT_MORT_AREA = 0x11, //17 + MAGIC_EFFECT_SOUND_GREEN = 0x12, //18 + MAGIC_EFFECT_SOUND_RED = 0x13, //19 + MAGIC_EFFECT_POISON_AREA = 0x14, //20 + MAGIC_EFFECT_SOUND_YELLOW = 0x15, //21 + MAGIC_EFFECT_SOUND_PURPLE = 0x16, //22 + MAGIC_EFFECT_SOUND_BLUE = 0x17, //23 + MAGIC_EFFECT_SOUND_WHITE = 0x18, //24 + MAGIC_EFFECT_BUBBLES = 0x19, //25 + MAGIC_EFFECT_CRAPS = 0x1A, //26 + MAGIC_EFFECT_GIFT_WRAPS = 0x1B, //27 MAGIC_EFFECT_FIREWORK_YELLOW = 0x1C, //28 - MAGIC_EFFECT_FIREWORK_RED = 0x1D, //29 - MAGIC_EFFECT_FIREWORK_BLUE = 0x1E, //30 - MAGIC_EFFECT_STUN = 0x1F, //31 - MAGIC_EFFECT_SLEEP = 0x20, //32 - MAGIC_EFFECT_WATERCREATURE = 0x21, //33 - MAGIC_EFFECT_GROUNDSHAKER = 0x22, //34 - MAGIC_EFFECT_HEARTS = 0x23, //35 - MAGIC_EFFECT_FIREATTACK = 0x24, //36 - MAGIC_EFFECT_ENERGY_AREA = 0x25, //37 - MAGIC_EFFECT_SMALLCLOUDS = 0x26, //38 - MAGIC_EFFECT_HOLYDAMAGE = 0x27, //39 - MAGIC_EFFECT_BIGCLOUDS = 0x28, //40 - MAGIC_EFFECT_ICEAREA = 0x29, //41 - MAGIC_EFFECT_ICETORNADO = 0x2A, //42 - MAGIC_EFFECT_ICEATTACK = 0x2B, //43 - MAGIC_EFFECT_STONES = 0x2C, //44 - MAGIC_EFFECT_SMALLPLANTS = 0x2D, //45 - MAGIC_EFFECT_CARNIPHILA = 0x2E, //46 - MAGIC_EFFECT_PURPLEENERGY = 0x2F, //47 - MAGIC_EFFECT_YELLOWENERGY = 0x30, //48 - MAGIC_EFFECT_HOLYAREA = 0x31, //49 - MAGIC_EFFECT_BIGPLANTS = 0x32, //50 - MAGIC_EFFECT_CAKE = 0x33, //51 - MAGIC_EFFECT_GIANTICE = 0x34, //52 - MAGIC_EFFECT_WATERSPLASH = 0x35, //53 - MAGIC_EFFECT_PLANTATTACK = 0x36, //54 - MAGIC_EFFECT_TUTORIALARROW = 0x37, //55 - MAGIC_EFFECT_TUTORIALSQUARE = 0x38, //56 + MAGIC_EFFECT_FIREWORK_RED = 0x1D, //29 + MAGIC_EFFECT_FIREWORK_BLUE = 0x1E, //30 + MAGIC_EFFECT_STUN = 0x1F, //31 + MAGIC_EFFECT_SLEEP = 0x20, //32 + MAGIC_EFFECT_WATERCREATURE = 0x21, //33 + MAGIC_EFFECT_GROUNDSHAKER = 0x22, //34 + MAGIC_EFFECT_HEARTS = 0x23, //35 + MAGIC_EFFECT_FIREATTACK = 0x24, //36 + MAGIC_EFFECT_ENERGY_AREA = 0x25, //37 + MAGIC_EFFECT_SMALLCLOUDS = 0x26, //38 + MAGIC_EFFECT_HOLYDAMAGE = 0x27, //39 + MAGIC_EFFECT_BIGCLOUDS = 0x28, //40 + MAGIC_EFFECT_ICEAREA = 0x29, //41 + MAGIC_EFFECT_ICETORNADO = 0x2A, //42 + MAGIC_EFFECT_ICEATTACK = 0x2B, //43 + MAGIC_EFFECT_STONES = 0x2C, //44 + MAGIC_EFFECT_SMALLPLANTS = 0x2D, //45 + MAGIC_EFFECT_CARNIPHILA = 0x2E, //46 + MAGIC_EFFECT_PURPLEENERGY = 0x2F, //47 + MAGIC_EFFECT_YELLOWENERGY = 0x30, //48 + MAGIC_EFFECT_HOLYAREA = 0x31, //49 + MAGIC_EFFECT_BIGPLANTS = 0x32, //50 + MAGIC_EFFECT_CAKE = 0x33, //51 + MAGIC_EFFECT_GIANTICE = 0x34, //52 + MAGIC_EFFECT_WATERSPLASH = 0x35, //53 + MAGIC_EFFECT_PLANTATTACK = 0x36, //54 + MAGIC_EFFECT_TUTORIALARROW = 0x37, //55 + MAGIC_EFFECT_TUTORIALSQUARE = 0x38, //56 MAGIC_EFFECT_MIRRORHORIZONTAL = 0x39, //57 - MAGIC_EFFECT_MIRRORVERTICAL = 0x3A, //58 + MAGIC_EFFECT_MIRRORVERTICAL = 0x3A, //58 MAGIC_EFFECT_SKULLHORIZONTAL = 0x3B, //59 - MAGIC_EFFECT_SKULLVERTICAL = 0x3C, //60 - MAGIC_EFFECT_ASSASSIN = 0x3D, //61 + MAGIC_EFFECT_SKULLVERTICAL = 0x3C, //60 + MAGIC_EFFECT_ASSASSIN = 0x3D, //61 MAGIC_EFFECT_STEPSHORIZONTAL = 0x3E, //62 - MAGIC_EFFECT_BLOODYSTEPS = 0x3F, //63 - MAGIC_EFFECT_STEPSVERTICAL = 0x40, //64 - MAGIC_EFFECT_YALAHARIGHOST = 0x41, //65 - MAGIC_EFFECT_BATS = 0x42, //66 - MAGIC_EFFECT_SMOKE = 0x43, //67 - MAGIC_EFFECT_INSECTS = 0x44, //68 - MAGIC_EFFECT_LAST = MAGIC_EFFECT_INSECTS, + MAGIC_EFFECT_BLOODYSTEPS = 0x3F, //63 + MAGIC_EFFECT_STEPSVERTICAL = 0x40, //64 + MAGIC_EFFECT_YALAHARIGHOST = 0x41, //65 + MAGIC_EFFECT_BATS = 0x42, //66 + MAGIC_EFFECT_SMOKE = 0x43, //67 + MAGIC_EFFECT_INSECTS = 0x44, //68 + MAGIC_EFFECT_DRAGONHEAD = 0x45, //69 + MAGIC_EFFECT_ORCSHAMAN = 0x46, //70 + MAGIC_EFFECT_ORCSHAMAN_FIRE = 0x47, //71 + MAGIC_EFFECT_THUNDER = 0x48, //72 + MAGIC_EFFECT_FERUMBRAS = 0x49, //73 + MAGIC_EFFECT_CONFETTIHORIZONTAL = 0x4A, //74 + MAGIC_EFFECT_CONFETTIVERTICAL = 0x4B, //75 + MAGIC_EFFECT_LAST = MAGIC_EFFECT_CONFETTIVERTICAL, //for internal use, dont send to client - MAGIC_EFFECT_NONE = 0xFF, - MAGIC_EFFECT_UNKNOWN = 0xFFFF + MAGIC_EFFECT_NONE = 0xFF, + MAGIC_EFFECT_UNKNOWN = 0xFFFF }; enum ShootEffect_t { - SHOOT_EFFECT_SPEAR = 0x00, - SHOOT_EFFECT_BOLT = 0x01, - SHOOT_EFFECT_ARROW = 0x02, - SHOOT_EFFECT_FIRE = 0x03, - SHOOT_EFFECT_ENERGY = 0x04, + SHOOT_EFFECT_SPEAR = 0x00, + SHOOT_EFFECT_BOLT = 0x01, + SHOOT_EFFECT_ARROW = 0x02, + SHOOT_EFFECT_FIRE = 0x03, + SHOOT_EFFECT_ENERGY = 0x04, SHOOT_EFFECT_POISONARROW = 0x05, - SHOOT_EFFECT_BURSTARROW = 0x06, + SHOOT_EFFECT_BURSTARROW = 0x06, SHOOT_EFFECT_THROWINGSTAR = 0x07, SHOOT_EFFECT_THROWINGKNIFE = 0x08, - SHOOT_EFFECT_SMALLSTONE = 0x09, - SHOOT_EFFECT_DEATH = 0x0A, //10 - SHOOT_EFFECT_LARGEROCK = 0x0B, //11 - SHOOT_EFFECT_SNOWBALL = 0x0C, //12 - SHOOT_EFFECT_POWERBOLT = 0x0D, //13 + SHOOT_EFFECT_SMALLSTONE = 0x09, + SHOOT_EFFECT_DEATH = 0x0A, //10 + SHOOT_EFFECT_LARGEROCK = 0x0B, //11 + SHOOT_EFFECT_SNOWBALL = 0x0C, //12 + SHOOT_EFFECT_POWERBOLT = 0x0D, //13 SHOOT_EFFECT_POISONFIELD = 0x0E, //14 SHOOT_EFFECT_INFERNALBOLT = 0x0F, //15 SHOOT_EFFECT_HUNTINGSPEAR = 0x10, //16 SHOOT_EFFECT_ENCHANTEDSPEAR = 0x11, //17 - SHOOT_EFFECT_REDSTAR = 0x12, //18 - SHOOT_EFFECT_GREENSTAR = 0x13, //19 - SHOOT_EFFECT_ROYALSPEAR = 0x14, //20 + SHOOT_EFFECT_REDSTAR = 0x12, //18 + SHOOT_EFFECT_GREENSTAR = 0x13, //19 + SHOOT_EFFECT_ROYALSPEAR = 0x14, //20 SHOOT_EFFECT_SNIPERARROW = 0x15, //21 - SHOOT_EFFECT_ONYXARROW = 0x16, //22 + SHOOT_EFFECT_ONYXARROW = 0x16, //22 SHOOT_EFFECT_PIERCINGBOLT = 0x17, //23 SHOOT_EFFECT_WHIRLWINDSWORD = 0x18, //24 SHOOT_EFFECT_WHIRLWINDAXE = 0x19, //25 SHOOT_EFFECT_WHIRLWINDCLUB = 0x1A, //26 SHOOT_EFFECT_ETHEREALSPEAR = 0x1B, //27 - SHOOT_EFFECT_ICE = 0x1C, //28 - SHOOT_EFFECT_EARTH = 0x1D, //29 - SHOOT_EFFECT_HOLY = 0x1E, //30 + SHOOT_EFFECT_ICE = 0x1C, //28 + SHOOT_EFFECT_EARTH = 0x1D, //29 + SHOOT_EFFECT_HOLY = 0x1E, //30 SHOOT_EFFECT_SUDDENDEATH = 0x1F, //31 - SHOOT_EFFECT_FLASHARROW = 0x20, //32 + SHOOT_EFFECT_FLASHARROW = 0x20, //32 SHOOT_EFFECT_FLAMMINGARROW = 0x21, //33 SHOOT_EFFECT_SHIVERARROW = 0x22, //34 - SHOOT_EFFECT_ENERGYBALL = 0x23, //35 - SHOOT_EFFECT_SMALLICE = 0x24, //36 - SHOOT_EFFECT_SMALLHOLY = 0x25, //37 - SHOOT_EFFECT_SMALLEARTH = 0x26, //38 - SHOOT_EFFECT_EARTHARROW = 0x27, //39 - SHOOT_EFFECT_EXPLOSION = 0x28, //40 - SHOOT_EFFECT_CAKE = 0x29, //41 - SHOOT_EFFECT_LAST = SHOOT_EFFECT_CAKE, + SHOOT_EFFECT_ENERGYBALL = 0x23, //35 + SHOOT_EFFECT_SMALLICE = 0x24, //36 + SHOOT_EFFECT_SMALLHOLY = 0x25, //37 + SHOOT_EFFECT_SMALLEARTH = 0x26, //38 + SHOOT_EFFECT_EARTHARROW = 0x27, //39 + SHOOT_EFFECT_EXPLOSION = 0x28, //40 + SHOOT_EFFECT_CAKE = 0x29, //41 + SHOOT_EFFECT_UNK1 = 0x2A, //42, DEBUG! + SHOOT_EFFECT_TARSALARROW = 0x2B, //43 + SHOOT_EFFECT_VORTEXBOLT = 0x2C, //44 + SHOOT_EFFECT_UNK2 = 0x2D, //45, DEBUG! + SHOOT_EFFECT_FOOTBALL = 0x2E, //46 + SHOOT_EFFECT_LAST = SHOOT_EFFECT_FOOTBALL, //for internal use, dont send to client - SHOOT_EFFECT_WEAPONTYPE = 0xFE, //254 - SHOOT_EFFECT_NONE = 0xFF, - SHOOT_EFFECT_UNKNOWN = 0xFFFF -}; - -enum SpeakClasses -{ - SPEAK_CLASS_NONE = 0x00, - SPEAK_CLASS_FIRST = 0x01, - SPEAK_SAY = SPEAK_CLASS_FIRST, - SPEAK_WHISPER = 0x02, - SPEAK_YELL = 0x03, - SPEAK_PRIVATE_PN = 0x04, - SPEAK_PRIVATE_NP = 0x05, - SPEAK_PRIVATE = 0x06, - SPEAK_CHANNEL_Y = 0x07, - SPEAK_CHANNEL_W = 0x08, - SPEAK_RVR_CHANNEL = 0x09, - SPEAK_RVR_ANSWER = 0x0A, - SPEAK_RVR_CONTINUE = 0x0B, - SPEAK_BROADCAST = 0x0C, - SPEAK_CHANNEL_RN = 0x0D, //red - #c text - SPEAK_PRIVATE_RED = 0x0E, //@name@text - SPEAK_CHANNEL_O = 0x0F, - //SPEAK_UNKNOWN_1 = 0x10, - SPEAK_CHANNEL_RA = 0x11, //red anonymous - #d text - //SPEAK_UNKNOWN_2 = 0x12, - SPEAK_MONSTER_SAY = 0x13, - SPEAK_MONSTER_YELL = 0x14, - SPEAK_CLASS_LAST = SPEAK_MONSTER_YELL + SHOOT_EFFECT_WEAPONTYPE = 0xFE, //254 + SHOOT_EFFECT_NONE = 0xFF, + SHOOT_EFFECT_UNKNOWN = 0xFFFF }; enum MessageClasses { - MSG_CLASS_FIRST = 0x12, - MSG_STATUS_CONSOLE_RED = MSG_CLASS_FIRST, /*Red message in the console*/ - MSG_EVENT_ORANGE = 0x13, /*Orange message in the console*/ - MSG_STATUS_CONSOLE_ORANGE = 0x14, /*Orange message in the console*/ - MSG_STATUS_WARNING = 0x15, /*Red message in game window and in the console*/ - MSG_EVENT_ADVANCE = 0x16, /*White message in game window and in the console*/ - MSG_EVENT_DEFAULT = 0x17, /*White message at the bottom of the game window and in the console*/ - MSG_STATUS_DEFAULT = 0x18, /*White message at the bottom of the game window and in the console*/ - MSG_INFO_DESCR = 0x19, /*Green message in game window and in the console*/ - MSG_STATUS_SMALL = 0x1A, /*White message at the bottom of the game window"*/ - MSG_STATUS_CONSOLE_BLUE = 0x1B, /*Blue message in the console*/ - MSG_CLASS_LAST = MSG_STATUS_CONSOLE_BLUE + MSG_NONE = 0x00, + + MSG_SPEAK_SAY = 0x01, + MSG_SPEAK_WHISPER = 0x02, + MSG_SPEAK_YELL = 0x03, + MSG_PRIVATE_FROM = 0x04, + MSG_PRIVATE_TO = 0x05, + MSG_CHANNEL_MANAGEMENT = 0x06, + MSG_CHANNEL = 0x07, + MSG_CHANNEL_HIGHLIGHT = 0x08, + MSG_SPEAK_SPELL = 0x09, + MSG_NPC_FROM = 0x0A, + MSG_NPC_TO = 0x0B, + MSG_GAMEMASTER_BROADCAST = 0x0C, + MSG_GAMEMASTER_CHANNEL = 0x0D, + MSG_GAMEMASTER_PRIVATE_FROM = 0x0E, + MSG_GAMEMASTER_PRIVATE_TO = 0x0F, + MSG_SPEAK_MONSTER_SAY = 0x22, + MSG_SPEAK_MONSTER_YELL = 0x23, + + MSG_SPEAK_FIRST = MSG_SPEAK_SAY, + MSG_SPEAK_LAST = MSG_GAMEMASTER_PRIVATE_TO, + MSG_SPEAK_MONSTER_FIRST = MSG_SPEAK_MONSTER_SAY, + MSG_SPEAK_MONSTER_LAST = MSG_SPEAK_MONSTER_YELL, + + MSG_STATUS_CONSOLE_BLUE = 0x04, /*Teal message in local chat*/ + MSG_STATUS_CONSOLE_RED = 0x0C, /*Red message in console*/ + MSG_STATUS_DEFAULT = 0x10, /*White message at the bottom of the game window and in the console*/ + MSG_STATUS_WARNING = 0x11, /*Red message in game window and in the console*/ + MSG_EVENT_ADVANCE = 0x12, /*White message in game window and in the console*/ + MSG_STATUS_SMALL = 0x13, /*White message at the bottom of the game window"*/ + MSG_INFO_DESCR = 0x14, /*Green message in game window and in the console*/ + MSG_DAMAGE_DEALT = 0x15, + MSG_DAMAGE_RECEIVED = 0x16, + MSG_HEALED = 0x17, + MSG_EXPERIENCE = 0x18, + MSG_DAMAGE_OTHERS = 0x19, + MSG_HEALED_OTHERS = 0x1A, + MSG_EXPERIENCE_OTHERS = 0x1B, + MSG_EVENT_DEFAULT = 0x1C, /*White message at the bottom of the game window and in the console*/ + MSG_LOOT = 0x1D, /*Green message in game window and in the console*/ + MSG_TRADE_NPC = 0x1E, /*Green message in game window and in the console*/ + MSG_EVENT_GUILD = 0x1F, /*Green message in game window and in the console*/ + MSG_PARTY_MANAGEMENT = 0x20, /*Green message in game window and in the console*/ + MSG_PARTY = 0x21, /*Green message in game window and in the console*/ + MSG_EVENT_ORANGE = 0x22, /*Orange message in local chat*/ + MSG_STATUS_CONSOLE_ORANGE = 0x23, /*Orange message in local chat*/ + MSG_REPORT = 0x24, /*White message in game window and in the console*/ + MSG_HOTKEY_USE = 0x25, /*Green message in game window*/ + MSG_TUTORIAL_HINT = 0x26 }; enum MapMarks_t @@ -205,7 +250,7 @@ enum MapMarks_t MAPMARK_SWORD = 0x08, MAPMARK_FLAG = 0x09, MAPMARK_LOCK = 0x0A, - MAPMARK_BAG = 0x0B, + MAPMARK_BAG = 0x0B, MAPMARK_SKULL = 0x0C, MAPMARK_DOLLAR = 0x0D, MAPMARK_REDNORTH = 0x0E, @@ -218,39 +263,42 @@ enum MapMarks_t enum FluidColors_t { - FLUID_EMPTY = 0x00, - FLUID_BLUE = 0x01, - FLUID_RED = 0x02, - FLUID_BROWN = 0x03, - FLUID_GREEN = 0x04, + FLUID_EMPTY = 0x00, + FLUID_BLUE = 0x01, + FLUID_RED = 0x02, + FLUID_BROWN = 0x03, + FLUID_GREEN = 0x04, FLUID_YELLOW = 0x05, - FLUID_WHITE = 0x06, + FLUID_WHITE = 0x06, FLUID_PURPLE = 0x07 }; enum FluidTypes_t { - FLUID_NONE = FLUID_EMPTY, - FLUID_WATER = FLUID_BLUE, - FLUID_BLOOD = FLUID_RED, - FLUID_BEER = FLUID_BROWN, - FLUID_SLIME = FLUID_GREEN, + FLUID_NONE = FLUID_EMPTY, + FLUID_WATER = FLUID_BLUE, + FLUID_BLOOD = FLUID_RED, + FLUID_BEER = FLUID_BROWN, + FLUID_SLIME = FLUID_GREEN, FLUID_LEMONADE = FLUID_YELLOW, - FLUID_MILK = FLUID_WHITE, - FLUID_MANA = FLUID_PURPLE, + FLUID_MILK = FLUID_WHITE, + FLUID_MANA = FLUID_PURPLE, - FLUID_LIFE = FLUID_RED + 8, - FLUID_OIL = FLUID_BROWN + 8, - FLUID_URINE = FLUID_YELLOW + 8, + FLUID_LIFE = FLUID_RED + 8, + FLUID_OIL = FLUID_BROWN + 8, + FLUID_URINE = FLUID_YELLOW + 8, FLUID_COCONUTMILK = FLUID_WHITE + 8, - FLUID_WINE = FLUID_PURPLE + 8, + FLUID_WINE = FLUID_PURPLE + 8, - FLUID_MUD = FLUID_BROWN + 16, + FLUID_MUD = FLUID_BROWN + 16, FLUID_FRUITJUICE = FLUID_YELLOW + 16, - FLUID_LAVA = FLUID_RED + 24, - FLUID_RUM = FLUID_BROWN + 24, - FLUID_SWAMP = FLUID_GREEN + 24, + FLUID_LAVA = FLUID_RED + 24, + FLUID_RUM = FLUID_BROWN + 24, + FLUID_SWAMP = FLUID_GREEN + 24, + + FLUID_TEA = FLUID_BROWN + 32, + FLUID_MEAD = FLUID_BROWN + 40 }; const uint8_t reverseFluidMap[] = @@ -293,86 +341,101 @@ const uint8_t fluidMap[] = CLIENTFLUID_PURPLE }; -enum SquareColor_t +enum Color_t { - SQ_COLOR_NONE = 256, - SQ_COLOR_BLACK = 0, + COLOR_BLACK = 0, + COLOR_BLUE = 5, + COLOR_GREEN = 18, + COLOR_LIGHTGREEN = 66, + COLOR_DARKBROWN = 78, + COLOR_LIGHTBLUE = 89, + COLOR_MAYABLUE = 95, + COLOR_DARKRED = 108, + COLOR_DARKPURPLE = 112, + COLOR_BROWN = 120, + COLOR_GREY = 129, + COLOR_TEAL = 143, + COLOR_DARKPINK = 152, + COLOR_PURPLE = 154, + COLOR_DARKORANGE = 156, + COLOR_RED = 180, + COLOR_PINK = 190, + COLOR_ORANGE = 192, + COLOR_DARKYELLOW = 205, + COLOR_YELLOW = 210, + COLOR_WHITE = 215, + + COLOR_NONE = 255, + COLOR_UNKNOWN = 256 }; -enum TextColor_t +enum Icons_t { - TEXTCOLOR_BLUE = 5, - TEXTCOLOR_GREEN = 18, - TEXTCOLOR_TEAL = 35, - TEXTCOLOR_LIGHTGREEN = 66, - TEXTCOLOR_DARKBROWN = 78, - TEXTCOLOR_LIGHTBLUE = 89, - TEXTCOLOR_DARKPURPLE = 112, - TEXTCOLOR_BROWN = 120, - TEXTCOLOR_GREY = 129, - TEXTCOLOR_DARKRED = 144, - TEXTCOLOR_DARKPINK = 152, - TEXTCOLOR_PURPLE = 154, - TEXTCOLOR_DARKORANGE = 156, - TEXTCOLOR_RED = 180, - TEXTCOLOR_PINK = 190, - TEXTCOLOR_ORANGE = 192, - TEXTCOLOR_DARKYELLOW = 205, - TEXTCOLOR_YELLOW = 210, - TEXTCOLOR_WHITE = 215, - - TEXTCOLOR_NONE = 255, - TEXTCOLOR_UNKNOWN = 256 + ICON_NONE = 0, + ICON_POISON = 1 << 0, + ICON_BURN = 1 << 1, + ICON_ENERGY = 1 << 2, + ICON_DRUNK = 1 << 3, + ICON_MANASHIELD = 1 << 4, + ICON_PARALYZE = 1 << 5, + ICON_HASTE = 1 << 6, + ICON_SWORDS = 1 << 7, + ICON_DROWNING = 1 << 8, + ICON_FREEZING = 1 << 9, + ICON_DAZZLED = 1 << 10, + ICON_CURSED = 1 << 11, + ICON_BUFF = 1 << 12, + ICON_PZBLOCK = 1 << 13, + ICON_PZ = 1 << 14, + ICON_BLEED = 1 << 15, + ICON_HUNGRY = 1 << 16 }; -enum Icons_t +enum skills_t { - ICON_NONE = 0, - ICON_POISON = 1 << 0, - ICON_BURN = 1 << 1, - ICON_ENERGY = 1 << 2, - ICON_DRUNK = 1 << 3, - ICON_MANASHIELD = 1 << 4, - ICON_PARALYZE = 1 << 5, - ICON_HASTE = 1 << 6, - ICON_SWORDS = 1 << 7, - ICON_DROWNING = 1 << 8, - ICON_FREEZING = 1 << 9, - ICON_DAZZLED = 1 << 10, - ICON_CURSED = 1 << 11, - ICON_BUFF = 1 << 12, - ICON_PZ = 1 << 13, - ICON_PROTECTIONZONE = 1 << 14 + SKILL_FIRST = 0, + SKILL_FIST = SKILL_FIRST, + SKILL_CLUB, + SKILL_SWORD, + SKILL_AXE, + SKILL_DIST, + SKILL_SHIELD, + SKILL_FISH, + SKILL__MAGLEVEL, + SKILL__LEVEL, + SKILL__EXPERIENCE, + SKILL_LAST = SKILL_FISH, + SKILL__LAST = SKILL__EXPERIENCE }; enum WeaponType_t { WEAPON_NONE = 0, - WEAPON_SWORD = 1, - WEAPON_CLUB = 2, - WEAPON_AXE = 3, - WEAPON_SHIELD = 4, - WEAPON_DIST = 5, - WEAPON_WAND = 6, - WEAPON_AMMO = 7, - WEAPON_FIST = 8 + WEAPON_SWORD, + WEAPON_CLUB, + WEAPON_AXE, + WEAPON_DIST, + WEAPON_SHIELD, + WEAPON_FIST, + WEAPON_WAND, + WEAPON_AMMO }; enum Ammo_t { AMMO_NONE = 0, - AMMO_BOLT = 1, - AMMO_ARROW = 2, - AMMO_SPEAR = 3, - AMMO_THROWINGSTAR = 4, - AMMO_THROWINGKNIFE = 5, - AMMO_STONE = 6, - AMMO_SNOWBALL = 7 + AMMO_BOLT, + AMMO_ARROW, + AMMO_SPEAR, + AMMO_THROWINGSTAR, + AMMO_THROWINGKNIFE, + AMMO_STONE, + AMMO_SNOWBALL }; enum AmmoAction_t { - AMMOACTION_NONE, + AMMOACTION_NONE = 0, AMMOACTION_REMOVECOUNT, AMMOACTION_REMOVECHARGE, AMMOACTION_MOVE, @@ -381,84 +444,261 @@ enum AmmoAction_t enum WieldInfo_t { - WIELDINFO_LEVEL = 1, - WIELDINFO_MAGLV = 2, - WIELDINFO_VOCREQ = 4, - WIELDINFO_PREMIUM = 8 + WIELDINFO_LEVEL = 1, + WIELDINFO_MAGLV = 2, + WIELDINFO_VOCREQ = 4, + WIELDINFO_PREMIUM = 8 }; enum Skulls_t { SKULL_NONE = 0, - SKULL_YELLOW = 1, - SKULL_GREEN = 2, - SKULL_WHITE = 3, - SKULL_RED = 4, - SKULL_BLACK = 5, - SKULL_LAST = SKULL_BLACK + SKULL_YELLOW, + SKULL_GREEN, + SKULL_WHITE, + SKULL_RED, + SKULL_BLACK, + SKULL_ORANGE, + SKULL_LAST = SKULL_ORANGE }; enum PartyShields_t { SHIELD_NONE = 0, - SHIELD_WHITEYELLOW = 1, - SHIELD_WHITEBLUE = 2, - SHIELD_BLUE = 3, - SHIELD_YELLOW = 4, - SHIELD_BLUE_SHAREDEXP = 5, - SHIELD_YELLOW_SHAREDEXP = 6, - SHIELD_BLUE_NOSHAREDEXP_BLINK = 7, - SHIELD_YELLOW_NOSHAREDEXP_BLINK = 8, - SHIELD_BLUE_NOSHAREDEXP = 9, - SHIELD_YELLOW_NOSHAREDEXP = 10, + SHIELD_WHITEYELLOW, + SHIELD_WHITEBLUE, + SHIELD_BLUE, + SHIELD_YELLOW, + SHIELD_BLUE_SHAREDEXP, + SHIELD_YELLOW_SHAREDEXP, + SHIELD_BLUE_NOSHAREDEXP_BLINK, + SHIELD_YELLOW_NOSHAREDEXP_BLINK, + SHIELD_BLUE_NOSHAREDEXP, + SHIELD_YELLOW_NOSHAREDEXP, SHIELD_LAST = SHIELD_YELLOW_NOSHAREDEXP }; +enum GuildEmblems_t +{ + EMBLEM_NONE = 0, + EMBLEM_GREEN, + EMBLEM_RED, + EMBLEM_BLUE +}; + +enum SpellGroup_t +{ + SPELLGROUP_NONE = 0, + SPELLGROUP_ATTACK = 1, + SPELLGROUP_HEALING = 2, + SPELLGROUP_SUPPORT = 3, + SPELLGROUP_SPECIAL = 4 +}; + +enum Spells_t +{ + SPELL_NONE = 0x00, + SPELL_LIGHT_HEALING = 0x01, + SPELL_INTENSE_HEALING = 0x02, + SPELL_ULTIMATE_HEALING = 0x03, + SPELL_INTENSE_HEALING_RUNE = 0x04, + SPELL_ULTIMATE_HEALING_RUNE = 0x05, + SPELL_HASTE = 0x06, + SPELL_LIGHT_MAGIC_MISSILE = 0x07, + SPELL_HEAVY_MAGIC_MISSILE = 0x08, + SPELL_SUMMON_CREATURE = 0x09, + SPELL_LIGHT = 0x0A, + SPELL_GREAT_LIGHT = 0x0B, + SPELL_CONVINCE_CREATURE = 0x0C, + SPELL_ENERGY_WAVE = 0x0D, + SPELL_CHAMELEON = 0x0E, + SPELL_FIREBALL = 0x0F, + SPELL_GREAT_FIREBALL = 0x10, + SPELL_FIREBOMB = 0x11, + SPELL_EXPLOSION = 0x12, + SPELL_FIRE_WAVE = 0x13, + SPELL_FIND_PERSON = 0x14, + SPELL_SUDDEN_DEATH = 0x15, + SPELL_ENERGY_BEAM = 0x16, + SPELL_GREAT_ENERGY_BEAM = 0x17, + SPELL_HELLS_CORE = 0x18, + SPELL_FIRE_FIELD = 0x19, + SPELL_POISON_FIELD = 0x1A, + SPELL_ENERGY_FIELD = 0x1B, + SPELL_FIRE_WALL = 0x1C, + SPELL_CURE_POISON = 0x1D, + SPELL_DESTROY_FIELD = 0x1E, + SPELL_ANTIDOTE_RUNE = 0x1F, + SPELL_POISON_WALL = 0x20, + SPELL_ENERGY_WALL = 0x21, + SPELL_UNKNOWN_1 = 0x22, + SPELL_UNKNOWN_2 = 0x23, + SPELL_SALVATION = 0x24, + SPELL_MOVE = 0x25, + SPELL_CREATURE_ILLUSION = 0x26, + SPELL_STRONG_HASTE = 0x27, + SPELL_UNKNOWN_3 = 0x28, + SPELL_UNKNOWN_4 = 0x29, + SPELL_FOOD = 0x2A, + SPELL_STRONG_ICE_WAVE = 0x2B, + SPELL_MAGIC_SHIELD = 0x2C, + SPELL_INVISIBLE = 0x2D, + SPELL_UNKNOWN_5 = 0x2E, + SPELL_UNKNOWN_6 = 0x2F, + SPELL_POISONED_ARROW = 0x30, + SPELL_EXPLOSIVE_ARROW = 0x31, + SPELL_SOULFIRE = 0x32, + SPELL_CONJURE_ARROW = 0x33, + SPELL_RETRIEVE_FRIEND = 0x34, + SPELL_UNKNOWN_7 = 0x35, + SPELL_PARALYZE = 0x36, + SPELL_ENERGYBOMB = 0x37, + SPELL_WRATH_OF_NATURE = 0x38, + SPELL_STRONG_ETHEREAL_SPEAR = 0x39, + SPELL_UNKNOWN_8 = 0x3A, + SPELL_FRONT_SWEEP = 0x3B, + SPELL_UNKNOWN_9 = 0x3C, + SPELL_BRUTAL_STRIKE = 0x3D, + SPELL_ANNIHILATION = 0x3E, + SPELL_UNKNOWN_10 = 0x3F, + SPELL_UNKNOWN_11 = 0x40, + SPELL_UNKNOWN_12 = 0x41, + SPELL_UNKNOWN_13 = 0x42, + SPELL_UNKNOWN_14 = 0x43, + SPELL_UNKNOWN_15 = 0x44, + SPELL_UNKNOWN_16 = 0x45, + SPELL_UNKNOWN_17 = 0x46, + SPELL_INVITE_GUESTS = 0x47, + SPELL_INVITE_SUBOWNERS = 0x48, + SPELL_KICK_GUEST = 0x49, + SPELL_EDIT_DOOR = 0x4A, + SPELL_ULTIMATE_LIGHT = 0x4B, + SPELL_MAGIC_ROPE = 0x4C, + SPELL_STALAGMITE = 0x4D, + SPELL_DESINTEGRATE = 0x4E, + SPELL_CONJURE_BOLT = 0x4F, + SPELL_BERSERK = 0x50, + SPELL_LEVITATE = 0x51, + SPELL_MASS_HEALING = 0x52, + SPELL_ANIMATE_DEAD = 0x53, + SPELL_HEAL_FRIEND = 0x54, + SPELL_UNDEAD_LEGION = 0x55, + SPELL_MAGIC_WALL = 0x56, + SPELL_DEATH_STRIKE = 0x57, + SPELL_ENERGY_STRIKE = 0x58, + SPELL_FLAME_STRIKE = 0x59, + SPELL_CANCEL_INVISIBILITY = 0x5A, + SPELL_POISONBOMB = 0x5B, + SPELL_ENCHANT_STAFF = 0x5C, + SPELL_CHALLENGE = 0x5D, + SPELL_WILD_GROWTH = 0x5E, + SPELL_POWER_BOLT = 0x5F, + SPELL_UNKNOWN_18 = 0x60, + SPELL_UNKNOWN_19 = 0x61, + SPELL_UNKNOWN_20 = 0x62, + SPELL_UNKNOWN_21 = 0x63, + SPELL_UNKNOWN_22 = 0x64, + SPELL_UNKNOWN_23 = 0x65, + SPELL_UNKNOWN_24 = 0x66, + SPELL_UNKNOWN_25 = 0x67, + SPELL_UNKNOWN_26 = 0x68, + SPELL_FIERCE_BERSERK = 0x69, + SPELL_GROUNDSHAKER = 0x6A, + SPELL_WHIRLWIND_THROW = 0x6B, + SPELL_SNIPER_ARROW = 0x6C, + SPELL_PIERCING_BOLT = 0x6D, + SPELL_ENCHANT_SPEAR = 0x6E, + SPELL_ETHEREAL_SPEAR = 0x6F, + SPELL_ICE_STRIKE = 0x70, + SPELL_TERRA_STRIKE = 0x71, + SPELL_ICICLE = 0x72, + SPELL_AVALANCHE = 0x73, + SPELL_STONE_SHOWER = 0x74, + SPELL_THUNDERSTORM = 0x75, + SPELL_ETERNAL_WINTER = 0x76, + SPELL_RAGE_OF_THE_SKIES = 0x77, + SPELL_TERRA_WAVE = 0x78, + SPELL_ICE_WAVE = 0x79, + SPELL_DIVINE_MISSILE = 0x7A, + SPELL_WOUND_CLEANSING = 0x7B, + SPELL_DIVINE_CALDERA = 0x7C, + SPELL_DIVINE_HEALING = 0x7D, + SPELL_TRAIN_PARTY = 0x7E, + SPELL_PROTECT_PARTY = 0x7F, + SPELL_HEAL_PARTY = 0x80, + SPELL_ENCHANT_PARTY = 0x81, + SPELL_HOLY_MISSILE = 0x82, + SPELL_CHARGE = 0x83, + SPELL_PROTECTOR = 0x84, + SPELL_BLOOD_RAGE = 0x85, + SPELL_SWIFT_FOOT = 0x86, + SPELL_SHARPSHOOTER = 0x87, + SPELL_UNKNOWN_27 = 0x88, + SPELL_UNKNOWN_28 = 0x89, + SPELL_IGNITE = 0x8A, + SPELL_CURSE = 0x8B, + SPELL_ELECTRIFY = 0x8C, + SPELL_INFLICT_WOUND = 0x8D, + SPELL_ENVENOM = 0x8E, + SPELL_HOLY_FLASH = 0x8F, + SPELL_CURE_BLEEDING = 0x90, + SPELL_CURE_BURNING = 0x91, + SPELL_CURE_ELECTRIFICATION = 0x92, + SPELL_CURE_CURSE = 0x93, + SPELL_PHYSICAL_STRIKE = 0x94, + SPELL_LIGHTNING = 0x95, + SPELL_STRONG_FLAME_STRIKE = 0x96, + SPELL_STRONG_ENERGY_STRIKE = 0x97, + SPELL_STRONG_ICE_STRIKE = 0x98, + SPELL_STRONG_TERRA_STRIKE = 0x99, + SPELL_ULTIMATE_FLAME_STRIKE = 0x9A, + SPELL_ULTIMATE_ENERGY_STRIKE = 0x9B, + SPELL_ULTIMATE_ICE_STRIKE = 0x9C, + SPELL_ULTIMATE_TERRA_STRIKE = 0x9D, + SPELL_INTENSE_WOUND_CLEANSING = 0x9E, + SPELL_RECOVERY = 0x9F, + SPELL_INTENSE_RECOVERY = 0xA0 +}; + enum item_t { - ITEM_FIREFIELD = 1492, - ITEM_FIREFIELD_SAFE = 1500, + ITEM_FIREFIELD = 1492, + ITEM_FIREFIELD_SAFE = 1500, - ITEM_POISONFIELD = 1496, + ITEM_POISONFIELD = 1496, ITEM_POISONFIELD_SAFE = 1503, - ITEM_ENERGYFIELD = 1495, + ITEM_ENERGYFIELD = 1495, ITEM_ENERGYFIELD_SAFE = 1504, - ITEM_MAGICWALL = 1497, - ITEM_MAGICWALL_SAFE = 11095, + ITEM_MAGICWALL = 1497, + ITEM_MAGICWALL_SAFE = 11098, - ITEM_WILDGROWTH = 1499, - ITEM_WILDGROWTH_SAFE = 11096, + ITEM_WILDGROWTH = 1499, + ITEM_WILDGROWTH_SAFE = 11099, - ITEM_DEPOT = 2594, - ITEM_LOCKER = 2589, - ITEM_GLOWING_SWITCH = 11060, + ITEM_DEPOT = 2594, + ITEM_LOCKER = 2589, + ITEM_INBOX = 14404, + ITEM_MARKET = 14405, - ITEM_MALE_CORPSE = 6080, - ITEM_FEMALE_CORPSE = 6081, + ITEM_MALE_CORPSE = 3058, + ITEM_FEMALE_CORPSE = 3065, - ITEM_MEAT = 2666, - ITEM_HAM = 2671, - ITEM_GRAPE = 2681, - ITEM_APPLE = 2674, - ITEM_BREAD = 2689, - ITEM_ROLL = 2690, - ITEM_CHEESE = 2696, + ITEM_FULLSPLASH = 2016, + ITEM_SMALLSPLASH = 2019, - ITEM_FULLSPLASH = 2016, - ITEM_SMALLSPLASH = 2019, - - ITEM_PARCEL = 2595, - ITEM_PARCEL_STAMPED = 2596, - ITEM_LETTER = 2597, - ITEM_LETTER_STAMPED = 2598, - ITEM_LABEL = 2599, + ITEM_PARCEL = 2595, + ITEM_PARCEL_STAMPED = 2596, + ITEM_LETTER = 2597, + ITEM_LETTER_STAMPED = 2598, + ITEM_LABEL = 2599, ITEM_WATERBALL_SPLASH = 7711, - ITEM_WATERBALL = 7956, + ITEM_WATERBALL = 7956, - ITEM_HOUSE_TRANSFER = 1968 //read-only + ITEM_STEALTH_RING = 2202, + ITEM_HOUSE_TRANSFER = 1968 //read-only }; enum PlayerFlags @@ -467,49 +707,51 @@ enum PlayerFlags PlayerFlag_CannotAttackPlayer, //2^1 = 2 PlayerFlag_CannotAttackMonster, //2^2 = 4 PlayerFlag_CannotBeAttacked, //2^3 = 8 - PlayerFlag_CanConvinceAll, //2^4 = 16 - PlayerFlag_CanSummonAll, //2^5 = 32 - PlayerFlag_CanIllusionAll, //2^6 = 64 + PlayerFlag_CanConvinceAll, //2^4 = 16 + PlayerFlag_CanSummonAll, //2^5 = 32 + PlayerFlag_CanIllusionAll, //2^6 = 64 PlayerFlag_CanSenseInvisibility, //2^7 = 128 PlayerFlag_IgnoredByMonsters, //2^8 = 256 - PlayerFlag_NotGainInFight, //2^9 = 512 - PlayerFlag_HasInfiniteMana, //2^10 = 1024 - PlayerFlag_HasInfiniteSoul, //2^11 = 2048 - PlayerFlag_HasNoExhaustion, //2^12 = 4096 - PlayerFlag_CannotUseSpells, //2^13 = 8192 + PlayerFlag_NotGainInFight, //2^9 = 512 + PlayerFlag_HasInfiniteMana, //2^10 = 1024 + PlayerFlag_HasInfiniteSoul, //2^11 = 2048 + PlayerFlag_HasNoExhaustion, //2^12 = 4096 + PlayerFlag_CannotUseSpells, //2^13 = 8192 PlayerFlag_CannotPickupItem, //2^14 = 16384 - PlayerFlag_CanAlwaysLogin, //2^15 = 32768 - PlayerFlag_CanBroadcast, //2^16 = 65536 - PlayerFlag_CanEditHouses, //2^17 = 131072 - PlayerFlag_CannotBeBanned, //2^18 = 262144 - PlayerFlag_CannotBePushed, //2^19 = 524288 + PlayerFlag_CanAlwaysLogin, //2^15 = 32768 + PlayerFlag_CanBroadcast, //2^16 = 65536 + PlayerFlag_CanEditHouses, //2^17 = 131072 + PlayerFlag_CannotBeBanned, //2^18 = 262144 + PlayerFlag_CannotBePushed, //2^19 = 524288 PlayerFlag_HasInfiniteCapacity, //2^20 = 1048576 PlayerFlag_CanPushAllCreatures, //2^21 = 2097152 PlayerFlag_CanTalkRedPrivate, //2^22 = 4194304 PlayerFlag_CanTalkRedChannel, //2^23 = 8388608 PlayerFlag_TalkOrangeHelpChannel, //2^24 = 16777216 PlayerFlag_NotGainExperience, //2^25 = 33554432 - PlayerFlag_NotGainMana, //2^26 = 67108864 - PlayerFlag_NotGainHealth, //2^27 = 134217728 - PlayerFlag_NotGainSkill, //2^28 = 268435456 - PlayerFlag_SetMaxSpeed, //2^29 = 536870912 - PlayerFlag_SpecialVIP, //2^30 = 1073741824 - PlayerFlag_NotGenerateLoot, //2^31 = 2147483648 - PlayerFlag_CanTalkRedChannelAnonymous, //2^32 = 4294967296 + PlayerFlag_NotGainMana, //2^26 = 67108864 + PlayerFlag_NotGainHealth, //2^27 = 134217728 + PlayerFlag_NotGainSkill, //2^28 = 268435456 + PlayerFlag_SetMaxSpeed, //2^29 = 536870912 + PlayerFlag_SpecialVIP, //2^30 = 1073741824 + PlayerFlag_NotGenerateLoot, //2^31 = 2147483648 + PlayerFlag_CanTalkRedChannelAnonymous, //2^32 = 4294967296 PlayerFlag_IgnoreProtectionZone, //2^33 = 8589934592 PlayerFlag_IgnoreSpellCheck, //2^34 = 17179869184 PlayerFlag_IgnoreEquipCheck, //2^35 = 34359738368 - PlayerFlag_CannotBeMuted, //2^36 = 68719476736 - PlayerFlag_IsAlwaysPremium, //2^37 = 137438953472 - PlayerFlag_CanAnswerRuleViolations, //2^38 = 274877906944 - PlayerFlag_39, //ignore //2^39 = 549755813888 //not used by us + PlayerFlag_CannotBeMuted, //2^36 = 68719476736 + PlayerFlag_IsAlwaysPremium, //2^37 = 137438953472 + PlayerFlag_38, //2^38 = 274877906944 //obsolete, can be re-used + PlayerFlag_39, //2^39 = 549755813888 //not used by us PlayerFlag_ShowGroupNameInsteadOfVocation, //2^40 = 1099511627776 PlayerFlag_HasInfiniteStamina, //2^41 = 2199023255552 - PlayerFlag_CannotMoveItems, //2^42 = 4398046511104 + PlayerFlag_CannotMoveItems, //2^42 = 4398046511104 PlayerFlag_CannotMoveCreatures, //2^43 = 8796093022208 - PlayerFlag_CanReportBugs, //2^44 = 17592186044416 - PlayerFlag_45, //ignore //2^45 = 35184372088832 //not used by us - PlayerFlag_CannotBeSeen, //2^46 = 70368744177664 + PlayerFlag_CanReportBugs, //2^44 = 17592186044416 + PlayerFlag_45, //2^45 = 35184372088832 //not used by us + PlayerFlag_CannotBeSeen, //2^46 = 70368744177664 + PlayerFlag_HideHealth, //2^47 = 140737488355328 + PlayerFlag_CanPassThroughAllCreatures, //2^48 = 281474976710656 PlayerFlag_LastFlag }; @@ -519,31 +761,44 @@ enum PlayerCustomFlags PlayerCustomFlag_AllowIdle = 0, //2^0 = 1 PlayerCustomFlag_CanSeePosition, //2^1 = 2 PlayerCustomFlag_CanSeeItemDetails, //2^2 = 4 - PlayerCustomFlag_CanSeeCreatureDetails, //2^3 = 8 + PlayerCustomFlag_CanSeeCreatureDetails, //2^3 = 8 PlayerCustomFlag_NotSearchable, //2^4 = 16 - PlayerCustomFlag_GamemasterPrivileges, //2^5 = 32 + PlayerCustomFlag_GamemasterPrivileges, //2^5 = 32 PlayerCustomFlag_CanThrowAnywhere, //2^6 = 64 PlayerCustomFlag_CanPushAllItems, //2^7 = 128 PlayerCustomFlag_CanMoveAnywhere, //2^8 = 256 PlayerCustomFlag_CanMoveFromFar, //2^9 = 512 - PlayerCustomFlag_CanLoginMultipleCharacters, //2^10 = 1024 (account flag) - PlayerCustomFlag_HasFullLight, //2^11 = 2048 + PlayerCustomFlag_CanUseFar, //2^10 = 1024 + PlayerCustomFlag_CanLoginMultipleCharacters,//2^11 = 2048 (account flag) PlayerCustomFlag_CanLogoutAnytime, //2^12 = 4096 (account flag) - PlayerCustomFlag_HideLevel, //2^13 = 8192 + PlayerCustomFlag_HideLevel, //2^13 = 8192 PlayerCustomFlag_IsProtected, //2^14 = 16384 - PlayerCustomFlag_IsImmune, //2^15 = 32768 + PlayerCustomFlag_IsImmune, //2^15 = 32768 PlayerCustomFlag_NotGainSkull, //2^16 = 65536 - PlayerCustomFlag_NotGainUnjustified, //2^17 = 131072 - PlayerCustomFlag_IgnorePacification, //2^18 = 262144 + PlayerCustomFlag_NotGainUnjustified, //2^17 = 131072 + PlayerCustomFlag_IgnorePacification, //2^18 = 262144 PlayerCustomFlag_IgnoreLoginDelay, //2^19 = 524288 PlayerCustomFlag_CanStairhop, //2^20 = 1048576 PlayerCustomFlag_CanTurnhop, //2^21 = 2097152 PlayerCustomFlag_IgnoreHouseRent, //2^22 = 4194304 PlayerCustomFlag_CanWearAllAddons, //2^23 = 8388608 + PlayerCustomFlag_IsWalkable, //2^24 = 16777216 + PlayerCustomFlag_CanUseAllMounts, //2^25 = 33554432 + PlayerCustomFlag_HasFullLight, //2^26 = 67108864 PlayerCustomFlag_LastFlag }; +struct MessageDetails +{ + int32_t value; + Color_t color; + MessageDetails* sub; + + MessageDetails(int32_t value = 0, Color_t color = COLOR_WHITE): + value(value), color(color), sub(NULL) {} +}; + //Reserved player storage key ranges //[10000000 - 20000000] #define PSTRG_RESERVED_RANGE_START 10000000 @@ -557,9 +812,20 @@ enum PlayerCustomFlags #define PSTRG_OUTFITSID_RANGE_START (PSTRG_RESERVED_RANGE_START + 1500) #define PSTRG_OUTFITSID_RANGE_SIZE 500 -#define NETWORKMESSAGE_MAXSIZE 15360 -#define IPBAN_FLAG 128 +//[2000 - 2010] +#define PSTRG_MOUNTS_RANGE_START (PSTRG_RESERVED_RANGE_START + 2000) +#define PSTRG_MOUNTS_RANGE_SIZE 10 +#define PSTRG_MOUNTS_CURRENTMOUNT (PSTRG_MOUNTS_RANGE_START + PSTRG_MOUNTS_RANGE_SIZE) + +#define NETWORK_CRYPTOHEADER_SIZE 8 +#define NETWORK_RETRY_TIMEOUT 5000 +#define NETWORK_DEFAULT_SIZE 4096 +#define NETWORK_HEADER_SIZE 2 +#define NETWORK_MAX_SIZE 16384 + #define LOCALHOST 2130706433 +#define SWIMMING_OUTFIT 267 +#define GRATIS_PREMIUM 65535 #define IS_IN_KEYRANGE(key, range) (key >= PSTRG_##range##_START && ((key - PSTRG_##range##_START) < PSTRG_##range##_SIZE)) #endif diff --git a/container.cpp b/container.cpp index f77a713..a202885 100644 --- a/container.cpp +++ b/container.cpp @@ -75,7 +75,7 @@ Attr_ReadValue Container::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_CONTAINER_ITEMS: { uint32_t count; - if(!propStream.GET_ULONG(count)) + if(!propStream.getLong(count)) return ATTR_READ_ERROR; serializationCount = count; @@ -142,6 +142,10 @@ std::stringstream& Container::getContentDescription(std::stringstream& s) const Container* evil = const_cast(this); for(ContainerIterator it = evil->begin(); it != evil->end(); ++it) { + Container* tmp = (*it)->getContainer(); + if(tmp && !tmp->empty()) + continue; + if(!begin) s << ", "; else @@ -264,7 +268,7 @@ void Container::onRemoveContainerItem(uint32_t index, Item* item) } ReturnValue Container::__queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const + uint32_t flags, Creature* actor/* = NULL*/) const { if(((flags & FLAG_CHILDISOWNER) == FLAG_CHILDISOWNER)) { @@ -292,12 +296,15 @@ ReturnValue Container::__queryAdd(int32_t index, const Thing* thing, uint32_t co } } - if(index == INDEX_WHEREEVER && !((flags & FLAG_NOLIMIT) == FLAG_NOLIMIT) && full()) - return RET_CONTAINERNOTENOUGHROOM; + if((flags & FLAG_NOLIMIT) != FLAG_NOLIMIT) + { + if(id == ITEM_INBOX || (index == INDEX_WHEREEVER && full())) + return RET_CONTAINERNOTENOUGHROOM; + } const Cylinder* topParent = getTopParent(); if(topParent != this) - return topParent->__queryAdd(INDEX_WHEREEVER, item, count, flags | FLAG_CHILDISOWNER); + return topParent->__queryAdd(INDEX_WHEREEVER, item, count, flags | FLAG_CHILDISOWNER, actor); return RET_NOERROR; } @@ -322,15 +329,33 @@ ReturnValue Container::__queryMaxCount(int32_t index, const Thing* thing, uint32 if(item->isStackable()) { uint32_t n = 0; - if(index != INDEX_WHEREEVER) + if(index == INDEX_WHEREEVER) + { + //Iterate through every item and check how much free stackable slots there is. + uint32_t slotIndex = 0; + for(ItemList::const_iterator cit = itemlist.begin(); cit != itemlist.end(); ++cit, ++slotIndex) + { + if((*cit) != item && (*cit)->getID() == item->getID() && (*cit)->getItemCount() < 100) + { + uint32_t remainder = (100 - (*cit)->getItemCount()); + if(__queryAdd(slotIndex, item, remainder, flags) == RET_NOERROR) + n += remainder; + } + } + } + else { - const Thing* destThing = __getThing(index); + const Thing* destThing = __getThing(index-1); const Item* destItem = NULL; if(destThing) destItem = destThing->getItem(); - if(destItem && destItem->getID() == item->getID()) - n = 100 - destItem->getItemCount(); + if(destItem && destItem->getID() == item->getID() && destItem->getItemCount() < 100) + { + uint32_t remainder = 100 - destItem->getItemCount(); + if(__queryAdd(index, item, remainder, flags) == RET_NOERROR) + n = remainder; + } } maxQueryCount = freeSlots * 100 + n; @@ -347,7 +372,7 @@ ReturnValue Container::__queryMaxCount(int32_t index, const Thing* thing, uint32 return RET_NOERROR; } -ReturnValue Container::__queryRemove(const Thing* thing, uint32_t count, uint32_t flags) const +ReturnValue Container::__queryRemove(const Thing* thing, uint32_t count, uint32_t flags, Creature*) const { int32_t index = __getIndexOfThing(thing); if(index == -1) @@ -360,8 +385,8 @@ ReturnValue Container::__queryRemove(const Thing* thing, uint32_t count, uint32_ if(count == 0 || (item->isStackable() && count > item->getItemCount())) return RET_NOTPOSSIBLE; - if(item->isNotMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags)) - return RET_NOTMOVEABLE; + if(!item->isMovable() && !hasBitSet(FLAG_IGNORENOTMOVABLE, flags)) + return RET_NOTMOVABLE; return RET_NOERROR; } @@ -377,44 +402,63 @@ Cylinder* Container::__queryDestination(int32_t& index, const Thing* thing, Item Container* parentContainer = dynamic_cast(getParent()); if(parentContainer) return parentContainer; - else - return this; + + return this; } - else if(index == 255 /*add wherever*/) + + if(index == 255 /*add wherever*/) { index = INDEX_WHEREEVER; *destItem = NULL; - return this; } - else + else if(index >= (int32_t)capacity()) { - if(index >= (int32_t)capacity()) - { - /* - if you have a container, maximize it to show all 20 slots - then you open a bag that is inside the container you will have a bag with 8 slots - and a "grey" area where the other 12 slots where from the container - if you drop the item on that grey area - the client calculates the slot position as if the bag has 20 slots - */ - index = INDEX_WHEREEVER; - } + /* + if you have a container, maximize it to show all 20 slots + then you open a bag that is inside the container you will have a bag with 8 slots + and a "grey" area where the other 12 slots where from the container + if you drop the item on that grey area the client calculates the slot position + as if the bag has 20 slots + */ - if(index != INDEX_WHEREEVER) - { - Thing* destThing = __getThing(index); - if(destThing) - *destItem = destThing->getItem(); + index = INDEX_WHEREEVER; + *destItem = NULL; + } - if(Cylinder* subCylinder = dynamic_cast(*destItem)) + const Item* item = thing->getItem(); + if(!item) + return this; + + if(!((flags & FLAG_IGNOREAUTOSTACK) == FLAG_IGNOREAUTOSTACK) + && item->isStackable() && item->getParent() != this) + { + //try to find a suitable item to stack with + uint32_t n = itemlist.size(); + for(ItemList::reverse_iterator cit = itemlist.rbegin(); cit != itemlist.rend(); ++cit, --n) + { + if((*cit)->getID() == item->getID() && (*cit)->getItemCount() < 100) { - index = INDEX_WHEREEVER; - *destItem = NULL; - return subCylinder; + *destItem = (*cit); + index = n; + return this; } } } + if(index != INDEX_WHEREEVER) + { + Thing* destThing = __getThing(index); + if(destThing) + *destItem = destThing->getItem(); + + if(Cylinder* subCylinder = dynamic_cast(*destItem)) + { + index = INDEX_WHEREEVER; + *destItem = NULL; + return subCylinder; + } + } + return this; } @@ -423,36 +467,30 @@ void Container::__addThing(Creature* actor, Thing* thing) return __addThing(actor, 0, thing); } -void Container::__addThing(Creature* actor, int32_t index, Thing* thing) +void Container::__addThing(Creature*, int32_t index, Thing* thing) { if(index >= (int32_t)capacity()) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Container::__addThing], index:" << index << ", index >= capacity()" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Container::__addThing], index:" << index << ", index >= capacity()" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } Item* item = thing->getItem(); - if(item == NULL) + if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Container::__addThing] item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Container::__addThing] item == NULL" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } #ifdef __DEBUG_MOVESYS__ - if(index != INDEX_WHEREEVER) + if(index != INDEX_WHEREEVER && size() >= capacity()) { - if(size() >= capacity()) - { - std::cout << "Failure: [Container::__addThing] size() >= capacity()" << std::endl; - DEBUG_REPORT - return /*RET_CONTAINERNOTENOUGHROOM*/; - } + std::clog << "Failure: [Container::__addThing] size() >= capacity()" << std::endl; + return /*RET_CONTAINERNOTENOUGHROOM*/; } #endif @@ -474,18 +512,16 @@ void Container::__updateThing(Thing* thing, uint16_t itemId, uint32_t count) if(index == -1) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Container::__updateThing] index == -1" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Container::__updateThing] index == -1" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } Item* item = thing->getItem(); - if(item == NULL) + if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Container::__updateThing] item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Container::__updateThing] item == NULL" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -510,11 +546,10 @@ void Container::__updateThing(Thing* thing, uint16_t itemId, uint32_t count) void Container::__replaceThing(uint32_t index, Thing* thing) { Item* item = thing->getItem(); - if(item == NULL) + if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Container::__replaceThing] item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Container::__replaceThing] item == NULL" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -525,15 +560,14 @@ void Container::__replaceThing(uint32_t index, Thing* thing) { if(count == index) break; - else - ++count; + + ++count; } if(cit == itemlist.end()) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Container::__updateThing] item not found" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Container::__updateThing] item not found" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -560,11 +594,10 @@ void Container::__replaceThing(uint32_t index, Thing* thing) void Container::__removeThing(Thing* thing, uint32_t count) { Item* item = thing->getItem(); - if(item == NULL) + if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Container::__removeThing] item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Container::__removeThing] item == NULL" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -573,8 +606,7 @@ void Container::__removeThing(Thing* thing, uint32_t count) if(index == -1) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Container::__removeThing] index == -1" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Container::__removeThing] index == -1" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -583,8 +615,7 @@ void Container::__removeThing(Thing* thing, uint32_t count) if(cit == itemlist.end()) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Container::__removeThing] item not found" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Container::__removeThing] item not found" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -625,7 +656,7 @@ void Container::__removeThing(Thing* thing, uint32_t count) Thing* Container::__getThing(uint32_t index) const { - if(index < 0 || index > size()) + if(index > size()) return NULL; uint32_t count = 0; @@ -664,54 +695,29 @@ int32_t Container::__getLastIndex() const return size(); } -uint32_t Container::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/, bool itemCount /*= true*/) const +uint32_t Container::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) const { uint32_t count = 0; - - Item* item = NULL; for(ItemList::const_iterator it = itemlist.begin(); it != itemlist.end(); ++it) { - item = (*it); - if(item && item->getID() == itemId && (subType == -1 || subType == item->getSubType())) - { - if(!itemCount) - { - if(item->isRune()) - count += item->getCharges(); - else - count += item->getItemCount(); - } - else - count += item->getItemCount(); - } + if((*it) && (*it)->getID() == itemId && (subType == -1 || subType == (*it)->getSubType())) + count += (*it)->getItemCount(); } return count; } std::map& Container::__getAllItemTypeCount(std::map& countMap, bool itemCount /*= true*/) const + uint32_t>& countMap) const { - Item* item = NULL; for(ItemList::const_iterator it = itemlist.begin(); it != itemlist.end(); ++it) - { - item = (*it); - if(!itemCount) - { - if(item->isRune()) - countMap[item->getID()] += item->getCharges(); - else - countMap[item->getID()] += item->getItemCount(); - } - else - countMap[item->getID()] += item->getItemCount(); - } + countMap[(*it)->getID()] += (*it)->getItemCount(); return countMap; } void Container::postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link /*= LINK_OWNER*/) + int32_t index, CylinderLink_t/* link = LINK_OWNER*/) { Cylinder* topParent = getTopParent(); if(!topParent->getCreature()) @@ -730,7 +736,7 @@ void Container::postAddNotification(Creature* actor, Thing* thing, const Cylinde } void Container::postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link /*= LINK_OWNER*/) + int32_t index, bool isCompleteRemoval, CylinderLink_t/* link = LINK_OWNER*/) { Cylinder* topParent = getTopParent(); if(!topParent->getCreature()) @@ -739,16 +745,13 @@ void Container::postRemoveNotification(Creature* actor, Thing* thing, const Cyli { //let the tile class notify surrounding players if(topParent->getParent()) - topParent->getParent()->postRemoveNotification(actor, thing, - newParent, index, isCompleteRemoval, LINK_NEAR); + topParent->getParent()->postRemoveNotification(actor, thing, newParent, index, isCompleteRemoval, LINK_NEAR); } else - topParent->postRemoveNotification(actor, thing, newParent, - index, isCompleteRemoval, LINK_PARENT); + topParent->postRemoveNotification(actor, thing, newParent, index, isCompleteRemoval, LINK_PARENT); } else - topParent->postRemoveNotification(actor, thing, newParent, - index, isCompleteRemoval, LINK_TOPPARENT); + topParent->postRemoveNotification(actor, thing, newParent, index, isCompleteRemoval, LINK_TOPPARENT); } void Container::__internalAddThing(Thing* thing) @@ -756,10 +759,14 @@ void Container::__internalAddThing(Thing* thing) __internalAddThing(0, thing); } -void Container::__internalAddThing(uint32_t index, Thing* thing) +void Container::__internalAddThing(uint32_t +#ifdef __DEBUG_MOVESYS__ + index +#endif + , Thing* thing) { #ifdef __DEBUG_MOVESYS__ - std::cout << "[Container::__internalAddThing] index: " << index << std::endl; + std::clog << "[Container::__internalAddThing] index: " << index << std::endl; #endif if(!thing) return; @@ -768,7 +775,7 @@ void Container::__internalAddThing(uint32_t index, Thing* thing) if(item == NULL) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Container::__internalAddThing] item == NULL" << std::endl; + std::clog << "Failure: [Container::__internalAddThing] item == NULL" << std::endl; #endif return; } diff --git a/container.h b/container.h index a0ca2c6..1a9623a 100644 --- a/container.h +++ b/container.h @@ -35,8 +35,10 @@ class ContainerIterator ContainerIterator& operator=(const ContainerIterator& rhs); bool operator==(const ContainerIterator& rhs); bool operator!=(const ContainerIterator& rhs); + ContainerIterator& operator++(); ContainerIterator operator++(int32_t); + Item* operator*(); Item* operator->(); @@ -67,12 +69,18 @@ class Container : public Item, public Cylinder bool unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream); std::string getContentDescription() const; - uint32_t getItemHoldingCount() const; + virtual uint32_t getItemHoldingCount() const; virtual double getWeight() const; - uint32_t capacity() const {return maxSize;} + uint32_t capacity() const {return maxSize ? maxSize : std::min(255U, (uint32_t)itemlist.size() + 1);} uint32_t size() const {return (uint32_t)itemlist.size();} - bool full() const {return itemlist.size() >= maxSize;} + bool full() const + { + if(maxSize) + return itemlist.size() >= maxSize; + + return true; + } bool empty() const {return itemlist.empty();} void addItem(Item* item); @@ -104,10 +112,10 @@ class Container : public Item, public Cylinder virtual const Creature* getCreature() const {return NULL;} virtual ReturnValue __queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const; + uint32_t flags, Creature* actor = NULL) const; virtual ReturnValue __queryMaxCount(int32_t index, const Thing* thing, uint32_t count, uint32_t& maxQueryCount, uint32_t flags) const; - virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, uint32_t flags) const; + virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, uint32_t flags, Creature* actor = NULL) const; virtual Cylinder* __queryDestination(int32_t& index, const Thing* thing, Item** destItem, uint32_t& flags); @@ -125,15 +133,13 @@ class Container : public Item, public Cylinder virtual int32_t __getFirstIndex() const; virtual int32_t __getLastIndex() const; - virtual uint32_t __getItemTypeCount(uint16_t itemId, int32_t subType = -1, - bool itemCount = true) const; - virtual std::map& __getAllItemTypeCount(std::map& countMap, bool itemCount = true) const; + virtual uint32_t __getItemTypeCount(uint16_t itemId, int32_t subType = -1) const; + virtual std::map& __getAllItemTypeCount(std::map& countMap) const; virtual void postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link = LINK_OWNER); + int32_t index, CylinderLink_t link = LINK_OWNER); virtual void postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link = LINK_OWNER); + int32_t index, bool isCompleteRemoval, CylinderLink_t link = LINK_OWNER); virtual void __internalAddThing(Thing* thing); virtual void __internalAddThing(uint32_t index, Thing* thing); diff --git a/creature.cpp b/creature.cpp index 31e4eb4..29fdb06 100644 --- a/creature.cpp +++ b/creature.cpp @@ -49,9 +49,10 @@ Creature::Creature() lootDrop = LOOT_DROP_FULL; skillLoss = true; hideName = hideHealth = cannotMove = false; - speakType = SPEAK_CLASS_NONE; + speakType = MSG_NONE; skull = SKULL_NONE; partyShield = SHIELD_NONE; + guildEmblem = EMBLEM_NONE; health = 1000; healthMax = 1000; @@ -70,6 +71,7 @@ Creature::Creature() hasFollowPath = false; removed = false; eventWalk = 0; + cancelNextWalk = false; forceUpdateFollowPath = false; isMapLoaded = false; isUpdatingPath = false; @@ -84,7 +86,6 @@ Creature::Creature() walkUpdateTicks = 0; checkVector = -1; - scriptEventsBitField = 0; onIdleStatus(); } @@ -136,6 +137,21 @@ bool Creature::canSeeCreature(const Creature* creature) const return creature == this || (!creature->isGhost() && (!creature->isInvisible() || canSeeInvisibility())); } +bool Creature::canWalkthrough(const Creature* creature) const +{ + if(creature == this) + return true; + + if(const Creature* _master = creature->getMaster()) + { + if(_master != this && canWalkthrough(_master)) + return true; + } + + return creature->isGhost() || creature->isWalkable() || (master && + master != creature && master->canWalkthrough(creature)); +} + int64_t Creature::getTimeSinceLastMove() const { if(lastStep) @@ -195,10 +211,14 @@ void Creature::onThink(uint32_t interval) if(isUpdatingPath) { isUpdatingPath = false; - getPathToFollowCreature(); + goToFollowCreature(); } +#ifndef __GROUPED_ATTACKS__ + onAttacking(interval / EVENT_CREATURECOUNT); +#else onAttacking(interval); +#endif executeConditions(interval); CreatureEventList thinkEvents = getCreatureEvents(CREATURE_EVENT_THINK); @@ -208,16 +228,20 @@ void Creature::onThink(uint32_t interval) void Creature::onAttacking(uint32_t interval) { - if(!attackedCreature) + if(!attackedCreature || attackedCreature->getHealth() < 1) return; + bool deny = false; CreatureEventList attackEvents = getCreatureEvents(CREATURE_EVENT_ATTACK); for(CreatureEventList::iterator it = attackEvents.begin(); it != attackEvents.end(); ++it) { - if(!(*it)->executeAttack(this, attackedCreature) && attackedCreature) - setAttackedCreature(NULL); + if(!(*it)->executeAction(this, attackedCreature) && !deny) + deny = true; } + if(deny) + setAttackedCreature(NULL); + if(!attackedCreature) return; @@ -233,12 +257,23 @@ void Creature::onWalk() { Direction dir; uint32_t flags = FLAG_IGNOREFIELDDAMAGE; - if(getNextStep(dir, flags) && g_game.internalMoveCreature(this, dir, flags) != RET_NOERROR) + if(!getNextStep(dir, flags)) + { + if(listWalkDir.empty()) + onWalkComplete(); + + stopEventWalk(); + } + else if(g_game.internalMoveCreature(this, dir, flags) != RET_NOERROR) forceUpdateFollowPath = true; } - if(listWalkDir.empty()) - onWalkComplete(); + if(cancelNextWalk) + { + cancelNextWalk = false; + listWalkDir.clear(); + onWalkAborted(); + } if(eventWalk) { @@ -249,33 +284,43 @@ void Creature::onWalk() void Creature::onWalk(Direction& dir) { - if(!hasCondition(CONDITION_DRUNK)) + int32_t drunk = -1; + if(!isSuppress(CONDITION_DRUNK)) + { + Condition* condition = NULL; + for(ConditionList::const_iterator it = conditions.begin(); it != conditions.end(); ++it) + { + if(!(condition = *it) || condition->getType() != CONDITION_DRUNK) + continue; + + int32_t subId = condition->getSubId(); + if((!condition->getEndTime() || condition->getEndTime() >= OTSYS_TIME()) && subId > drunk) + drunk = subId; + } + } + + if(drunk < 0) return; - uint32_t r = random_range(0, 16); - if(r > 4) + drunk += 25; + int32_t r = random_range(1, 100); + if(r > drunk) return; - switch(r) - { - case 0: - dir = NORTH; - break; - case 1: - dir = WEST; - break; - case 3: - dir = SOUTH; - break; - case 4: - dir = EAST; - break; - } + int32_t tmp = (drunk / 5); + if(r <= tmp) + dir = NORTH; + else if(r <= (tmp * 2)) + dir = WEST; + else if(r <= (tmp * 3)) + dir = SOUTH; + else if(r <= (tmp * 4)) + dir = EAST; - g_game.internalCreatureSay(this, SPEAK_MONSTER_SAY, "Hicks!", isGhost()); + g_game.internalCreatureSay(this, MSG_SPEAK_MONSTER_SAY, "Hicks!", isGhost()); } -bool Creature::getNextStep(Direction& dir, uint32_t& flags) +bool Creature::getNextStep(Direction& dir, uint32_t&) { if(listWalkDir.empty()) return false; @@ -288,26 +333,26 @@ bool Creature::getNextStep(Direction& dir, uint32_t& flags) bool Creature::startAutoWalk(std::list& listDir) { - if(getPlayer() && getPlayer()->getNoMove()) - { - getPlayer()->sendCancelWalk(); - return false; - } - listWalkDir = listDir; - addEventWalk(); + addEventWalk(listDir.size() == 1); return true; } -void Creature::addEventWalk() +void Creature::addEventWalk(bool firstStep/* = false*/) { - if(eventWalk) + cancelNextWalk = false; + if(getStepSpeed() < 1 || eventWalk) return; - int64_t ticks = getEventStepTicks(); - if(ticks > 0) - eventWalk = Scheduler::getInstance().addEvent(createSchedulerTask(ticks, - boost::bind(&Game::checkCreatureWalk, &g_game, getID()))); + int64_t ticks = getEventStepTicks(firstStep); + if(ticks < 1) + return; + + if(ticks == 1) + g_game.checkCreatureWalk(getID()); + + eventWalk = Scheduler::getInstance().addEvent(createSchedulerTask(std::max((int64_t)SCHEDULER_MINTICKS, ticks), + boost::bind(&Game::checkCreatureWalk, &g_game, id))); } void Creature::stopEventWalk() @@ -317,42 +362,22 @@ void Creature::stopEventWalk() Scheduler::getInstance().stopEvent(eventWalk); eventWalk = 0; - if(!listWalkDir.empty()) - { - listWalkDir.clear(); - onWalkAborted(); - } -} - -void Creature::internalCreatureDisappear(const Creature* creature, bool isLogout) -{ - if(attackedCreature == creature) - { - setAttackedCreature(NULL); - onAttackedCreatureDisappear(isLogout); - } - - if(followCreature == creature) - { - setFollowCreature(NULL); - onFollowCreatureDisappear(isLogout); - } } void Creature::updateMapCache() { - const Position& myPos = getPosition(); - Position pos(0, 0, myPos.z); + const Position& pos = getPosition(); + Position dest(0, 0, pos.z); Tile* tile = NULL; for(int32_t y = -((mapWalkHeight - 1) / 2); y <= ((mapWalkHeight - 1) / 2); ++y) { for(int32_t x = -((mapWalkWidth - 1) / 2); x <= ((mapWalkWidth - 1) / 2); ++x) { - pos.x = myPos.x + x; - pos.y = myPos.y + y; - if((tile = g_game.getTile(pos.x, pos.y, myPos.z))) - updateTileCache(tile, pos); + dest.x = pos.x + x; + dest.y = pos.y + y; + if((tile = g_game.getTile(dest))) + updateTileCache(tile, dest); } } } @@ -379,7 +404,7 @@ void Creature::updateTileCache(const Tile* tile, int32_t dx, int32_t dy) } #ifdef __DEBUG__ else - std::cout << "Creature::updateTileCache out of range." << std::endl; + std::clog << "Creature::updateTileCache out of range." << std::endl; #endif } @@ -390,6 +415,12 @@ void Creature::updateTileCache(const Tile* tile, const Position& pos) updateTileCache(tile, pos.x - myPos.x, pos.y - myPos.y); } +void Creature::updateTileCache(const Tile* tile) +{ + if(isMapLoaded && tile->getPosition().z == getPosition().z) + updateTileCache(tile, tile->getPosition()); +} + int32_t Creature::getWalkCache(const Position& pos) const { if(!useCacheMap()) @@ -412,10 +443,10 @@ int32_t Creature::getWalkCache(const Position& pos) const if(tile && (tile->__queryAdd(0, this, 1, FLAG_PATHFINDING | FLAG_IGNOREFIELDDAMAGE) == RET_NOERROR)) { if(!localMapCache[y][x]) - std::cout << "Wrong cache value" << std::endl; + std::clog << "Wrong cache value" << std::endl; } else if(localMapCache[y][x]) - std::cout << "Wrong cache value" << std::endl; + std::clog << "Wrong cache value" << std::endl; #endif if(localMapCache[y][x]) @@ -428,21 +459,21 @@ int32_t Creature::getWalkCache(const Position& pos) const return 2; } -void Creature::onAddTileItem(const Tile* tile, const Position& pos, const Item* item) +void Creature::onAddTileItem(const Tile* tile, const Position& pos, const Item*) { if(isMapLoaded && pos.z == getPosition().z) updateTileCache(tile, pos); } -void Creature::onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem, - const ItemType& oldType, const Item* newItem, const ItemType& newType) +void Creature::onUpdateTileItem(const Tile* tile, const Position& pos, const Item*, + const ItemType& oldType, const Item*, const ItemType& newType) { if(isMapLoaded && (oldType.blockSolid || oldType.blockPathFind || newType.blockPathFind || newType.blockSolid) && pos.z == getPosition().z) updateTileCache(tile, pos); } -void Creature::onRemoveTileItem(const Tile* tile, const Position& pos, const ItemType& iType, const Item* item) +void Creature::onRemoveTileItem(const Tile* tile, const Position& pos, const ItemType& iType, const Item*) { if(isMapLoaded && (iType.blockSolid || iType.blockPathFind || iType.isGroundTile()) && pos.z == getPosition().z) @@ -463,11 +494,19 @@ void Creature::onCreatureAppear(const Creature* creature) updateTileCache(creature->getTile(), creature->getPosition()); } -void Creature::onCreatureDisappear(const Creature* creature, bool isLogout) +void Creature::internalCreatureDisappear(const Creature* creature, bool isLogout) { - internalCreatureDisappear(creature, true); - if(creature != this && isMapLoaded && creature->getPosition().z == getPosition().z) - updateTileCache(creature->getTile(), creature->getPosition()); + if(attackedCreature == creature) + { + setAttackedCreature(NULL); + onTargetDisappear(isLogout); + } + + if(followCreature == creature) + { + setFollowCreature(NULL); + onFollowCreatureDisappear(isLogout); + } } void Creature::onRemovedCreature() @@ -484,7 +523,7 @@ void Creature::onChangeZone(ZoneType_t zone) internalCreatureDisappear(attackedCreature, false); } -void Creature::onAttackedCreatureChangeZone(ZoneType_t zone) +void Creature::onTargetChangeZone(ZoneType_t zone) { if(zone == ZONE_PROTECTION) internalCreatureDisappear(attackedCreature, false); @@ -495,19 +534,21 @@ void Creature::onCreatureMove(const Creature* creature, const Tile* newTile, con { if(creature == this) { + if(!oldTile->floorChange() && !oldTile->positionChange()) + setLastPosition(oldPos); + lastStep = OTSYS_TIME(); lastStepCost = 1; - - setLastPosition(oldPos); if(!teleport) { - if(oldPos.z != newPos.z || (std::abs(newPos.x - oldPos.x) >= 1 && std::abs(newPos.y - oldPos.y) >= 1)) - lastStepCost = 2; + if(std::abs(newPos.x - oldPos.x) >= 1 && std::abs(newPos.y - oldPos.y) >= 1) + lastStepCost = 3; } else stopEventWalk(); - if(!summons.empty()) + if(!summons.empty() && (!g_config.getBool(ConfigManager::TELEPORT_SUMMONS) || + (g_config.getBool(ConfigManager::TELEPORT_PLAYER_SUMMONS) && !getPlayer()))) { std::list::iterator cit; std::list despawnList; @@ -516,7 +557,7 @@ void Creature::onCreatureMove(const Creature* creature, const Tile* newTile, con const Position pos = (*cit)->getPosition(); if((std::abs(pos.z - newPos.z) > 2) || (std::max(std::abs(( newPos.x) - pos.x), std::abs((newPos.y - 1) - pos.y)) > 30)) - despawnList.push_back((*cit)); + despawnList.push_back(*cit); } for(cit = despawnList.begin(); cit != despawnList.end(); ++cit) @@ -646,7 +687,7 @@ void Creature::onCreatureMove(const Creature* creature, const Tile* newTile, con boost::bind(&Game::checkCreatureAttack, &g_game, getID()))); if(newTile->getZone() != oldTile->getZone()) - onAttackedCreatureChangeZone(attackedCreature->getZone()); + onTargetChangeZone(attackedCreature->getZone()); } else internalCreatureDisappear(attackedCreature, false); @@ -679,10 +720,8 @@ bool Creature::onDeath() if(it->isNameKill()) continue; - bool lastHit = it == deathList.begin(); - uint32_t flags = KILLFLAG_NONE; - if(lastHit) - flags |= (uint32_t)KILLFLAG_LASTHIT; + if(it == deathList.begin()) + it->setLast(); if(i < size) { @@ -696,24 +735,21 @@ bool Creature::onDeath() { if(std::find(justifyVec.begin(), justifyVec.end(), tmp) == justifyVec.end()) { - flags |= (uint32_t)KILLFLAG_JUSTIFY; + it->setJustify(); justifyVec.push_back(tmp); } tmp = NULL; } - if(!it->getKillerCreature()->onKilledCreature(this, flags) && lastHit) + if(!it->getKillerCreature()->onKilledCreature(this, (*it)) && it->isLast()) return false; - - if(hasBitSet((uint32_t)KILLFLAG_UNJUSTIFIED, flags)) - it->setUnjustified(true); } for(CountMap::iterator it = damageMap.begin(); it != damageMap.end(); ++it) { if((tmp = g_game.getCreatureByID(it->first))) - tmp->onAttackedCreatureKilled(this); + tmp->onTargetKilled(this); } dropCorpse(deathList); @@ -777,17 +813,25 @@ void Creature::dropCorpse(DeathList deathList) DeathList Creature::getKillers() { DeathList list; + CountMap::const_iterator it; + Creature* lhc = NULL; - if(!(lhc = g_game.getCreatureByID(lastHitCreature))) - list.push_back(DeathEntry(getCombatName(lastDamageSource), 0)); + if((lhc = g_game.getCreatureByID(lastHitCreature))) + { + int32_t damage = 0; + if((it = damageMap.find(lastHitCreature)) != damageMap.end()) + damage = it->second.total; + + list.push_back(DeathEntry(lhc, damage)); + } else - list.push_back(DeathEntry(lhc, 0)); + list.push_back(DeathEntry(getCombatName(lastDamageSource), 0)); int32_t requiredTime = g_config.getNumber(ConfigManager::DEATHLIST_REQUIRED_TIME); int64_t now = OTSYS_TIME(); CountBlock_t cb; - for(CountMap::const_iterator it = damageMap.begin(); it != damageMap.end(); ++it) + for(it = damageMap.begin(); it != damageMap.end(); ++it) { cb = it->second; if((now - cb.ticks) > requiredTime) @@ -832,7 +876,7 @@ bool Creature::hasBeenAttacked(uint32_t attackerId) const return false; } -Item* Creature::createCorpse(DeathList deathList) +Item* Creature::createCorpse(DeathList) { return Item::CreateItem(getLookCorpse()); } @@ -855,7 +899,7 @@ void Creature::changeMana(int32_t manaChange) mana = std::max((int32_t)0, mana + manaChange); } -bool Creature::getStorage(const uint32_t key, std::string& value) const +bool Creature::getStorage(const std::string& key, std::string& value) const { StorageMap::const_iterator it = storageMap.find(key); if(it != storageMap.end()) @@ -868,7 +912,7 @@ bool Creature::getStorage(const uint32_t key, std::string& value) const return false; } -bool Creature::setStorage(const uint32_t key, const std::string& value) +bool Creature::setStorage(const std::string& key, const std::string& value) { storageMap[key] = value; return true; @@ -883,7 +927,7 @@ void Creature::gainHealth(Creature* caster, int32_t healthGain) int32_t effectiveGain = getHealth() - prevHealth; if(caster) - caster->onTargetCreatureGainHealth(this, effectiveGain); + caster->onTargetGainHealth(this, effectiveGain); } else changeHealth(healthGain); @@ -896,7 +940,7 @@ void Creature::drainHealth(Creature* attacker, CombatType_t combatType, int32_t changeHealth(-damage); if(attacker) - attacker->onAttackedCreatureDrainHealth(this, damage); + attacker->onTargetDrainHealth(this, damage); } void Creature::drainMana(Creature* attacker, CombatType_t combatType, int32_t damage) @@ -906,11 +950,11 @@ void Creature::drainMana(Creature* attacker, CombatType_t combatType, int32_t da changeMana(-damage); if(attacker) - attacker->onAttackedCreatureDrainMana(this, damage); + attacker->onTargetDrainMana(this, damage); } BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, - bool checkDefense/* = false*/, bool checkArmor/* = false*/) + bool checkDefense/* = false*/, bool checkArmor/* = false*/, bool/* reflect = true*/, bool/* field = false*/, bool/* element = false*/) { BlockType_t blockType = BLOCK_NONE; if(isImmune(combatType)) @@ -969,8 +1013,8 @@ BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int3 if(attacker) { - attacker->onAttackedCreature(this); - attacker->onAttackedCreatureBlockHit(this, blockType); + attacker->onTarget(this); + attacker->onTargetBlockHit(this, blockType); } onAttacked(); @@ -992,7 +1036,7 @@ bool Creature::setAttackedCreature(Creature* creature) attackedCreature = creature; if(attackedCreature) { - onAttackedCreature(attackedCreature); + onTarget(attackedCreature); attackedCreature->onAttacked(); } @@ -1002,7 +1046,7 @@ bool Creature::setAttackedCreature(Creature* creature) return true; } -void Creature::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const +void Creature::getPathSearchParams(const Creature*, FindPathParams& fpp) const { fpp.fullPathSearch = !hasFollowPath; fpp.clearSight = true; @@ -1010,7 +1054,7 @@ void Creature::getPathSearchParams(const Creature* creature, FindPathParams& fpp fpp.minTargetDist = fpp.maxTargetDist = 1; } -void Creature::getPathToFollowCreature() +void Creature::goToFollowCreature() { if(followCreature) { @@ -1028,7 +1072,7 @@ void Creature::getPathToFollowCreature() onFollowCreatureComplete(followCreature); } -bool Creature::setFollowCreature(Creature* creature, bool fullPathSearch /*= false*/) +bool Creature::setFollowCreature(Creature* creature, bool /*fullPathSearch = false*/) { if(creature) { @@ -1058,6 +1102,7 @@ bool Creature::setFollowCreature(Creature* creature, bool fullPathSearch /*= fal followCreature = NULL; } + g_game.updateCreatureWalk(id); onFollowCreature(creature); return true; } @@ -1072,11 +1117,19 @@ double Creature::getDamageRatio(Creature* attacker) const attackerDamage += it->second.total; } - return attackerDamage / totalDamage; + return (totalDamage ? attackerDamage / totalDamage : 0); +} + +double Creature::getGainedExperience(Creature* attacker) const +{ + return getDamageRatio(attacker) * (double)getLostExperience(); } void Creature::addDamagePoints(Creature* attacker, int32_t damagePoints) { + if(damagePoints < 0) + return; + uint32_t attackerId = 0; if(attacker) attackerId = attacker->getID(); @@ -1116,20 +1169,34 @@ void Creature::addHealPoints(Creature* caster, int32_t healthPoints) void Creature::onAddCondition(ConditionType_t type, bool hadCondition) { - if(type == CONDITION_INVISIBLE) - { - if(!hadCondition) - g_game.internalCreatureChangeVisible(this, VISIBLE_DISAPPEAR); - } - else if(type == CONDITION_PARALYZE) + switch(type) { - if(hasCondition(CONDITION_HASTE)) - removeCondition(CONDITION_HASTE); - } - else if(type == CONDITION_HASTE) - { - if(hasCondition(CONDITION_PARALYZE)) - removeCondition(CONDITION_PARALYZE); + case CONDITION_INVISIBLE: + { + if(!hadCondition) + g_game.internalCreatureChangeVisible(this, VISIBLE_DISAPPEAR); + + break; + } + + case CONDITION_PARALYZE: + { + if(hasCondition(CONDITION_HASTE, -1, false)) + removeCondition(CONDITION_HASTE); + + break; + } + + case CONDITION_HASTE: + { + if(hasCondition(CONDITION_PARALYZE, -1, false)) + removeCondition(CONDITION_PARALYZE); + + break; + } + + default: + break; } } @@ -1139,7 +1206,7 @@ void Creature::onEndCondition(ConditionType_t type) g_game.internalCreatureChangeVisible(this, VISIBLE_APPEAR); } -void Creature::onTickCondition(ConditionType_t type, int32_t interval, bool& _remove) +void Creature::onTickCondition(ConditionType_t type, int32_t, bool& _remove) { if(const MagicField* field = getTile()->getFieldItem()) { @@ -1154,16 +1221,28 @@ void Creature::onTickCondition(ConditionType_t type, int32_t interval, bool& _re case CONDITION_POISON: _remove = field->getCombatType() != COMBAT_EARTHDAMAGE; break; + case CONDITION_FREEZING: + _remove = field->getCombatType() != COMBAT_ICEDAMAGE; + break; + case CONDITION_DAZZLED: + _remove = field->getCombatType() != COMBAT_HOLYDAMAGE; + break; + case CONDITION_CURSED: + _remove = field->getCombatType() != COMBAT_DEATHDAMAGE; + break; case CONDITION_DROWN: _remove = field->getCombatType() != COMBAT_DROWNDAMAGE; break; + case CONDITION_BLEEDING: + _remove = field->getCombatType() != COMBAT_PHYSICALDAMAGE; + break; default: break; } } } -void Creature::onCombatRemoveCondition(const Creature* attacker, Condition* condition) +void Creature::onCombatRemoveCondition(const Creature*, Condition* condition) { removeCondition(condition); } @@ -1177,60 +1256,72 @@ void Creature::onIdleStatus() } } -void Creature::onAttackedCreatureDrainHealth(Creature* target, int32_t points) +void Creature::onTargetDrainHealth(Creature* target, int32_t points) +{ + onTargetDrain(target, points); +} + +void Creature::onTargetDrainMana(Creature* target, int32_t points) +{ + onTargetDrain(target, points); +} + +void Creature::onTargetDrain(Creature* target, int32_t points) { - onAttackedCreatureDrain(target, points); + if(points >= 0) + target->addDamagePoints(this, points); } -void Creature::onAttackedCreatureDrainMana(Creature* target, int32_t points) +void Creature::onTargetGainHealth(Creature* target, int32_t points) { - onAttackedCreatureDrain(target, points); + onTargetGain(target, points); } -void Creature::onAttackedCreatureDrain(Creature* target, int32_t points) +void Creature::onTargetGainMana(Creature* target, int32_t points) { - target->addDamagePoints(this, points); + onTargetGain(target, points); } -void Creature::onTargetCreatureGainHealth(Creature* target, int32_t points) +void Creature::onTargetGain(Creature* target, int32_t points) { - target->addHealPoints(this, points); + if(points > 0) + target->addHealPoints(this, points); } -void Creature::onAttackedCreatureKilled(Creature* target) +void Creature::onTargetKilled(Creature* target) { if(target == this) return; - double gainExp = target->getGainedExperience(this); - onGainExperience(gainExp, !target->getPlayer(), false); + double exp = target->getGainedExperience(this); + onGainExperience(exp, target, false); } -bool Creature::onKilledCreature(Creature* target, uint32_t& flags) +bool Creature::onKilledCreature(Creature* target, DeathEntry& entry) { bool ret = true; if(master) - ret = master->onKilledCreature(target, flags); + ret = master->onKilledCreature(target, entry); CreatureEventList killEvents = getCreatureEvents(CREATURE_EVENT_KILL); - if(!hasBitSet((uint32_t)KILLFLAG_LASTHIT, flags)) + if(!entry.isLast()) { for(CreatureEventList::iterator it = killEvents.begin(); it != killEvents.end(); ++it) - (*it)->executeKill(this, target, false); + (*it)->executeKill(this, target, entry); return true; } for(CreatureEventList::iterator it = killEvents.begin(); it != killEvents.end(); ++it) { - if(!(*it)->executeKill(this, target, true) && ret) + if(!(*it)->executeKill(this, target, entry) && ret) ret = false; } return ret; } -void Creature::onGainExperience(double& gainExp, bool fromMonster, bool multiplied) +void Creature::onGainExperience(double& gainExp, Creature* target, bool multiplied) { if(gainExp <= 0) return; @@ -1238,7 +1329,7 @@ void Creature::onGainExperience(double& gainExp, bool fromMonster, bool multipli if(master) { gainExp = gainExp / 2; - master->onGainExperience(gainExp, fromMonster, multiplied); + master->onGainExperience(gainExp, target, multiplied); } else if(!multiplied) gainExp *= g_config.getDouble(ConfigManager::RATE_EXPERIENCE); @@ -1247,12 +1338,38 @@ void Creature::onGainExperience(double& gainExp, bool fromMonster, bool multipli if(color < 0) color = random_range(0, 255); + const Position& targetPos = getPosition(); + + SpectatorVec list; + g_game.getSpectators(list, targetPos, false, false, Map::maxViewportX, Map::maxViewportX, + Map::maxViewportY, Map::maxViewportY); + std::stringstream ss; - ss << (uint64_t)gainExp; - g_game.addAnimatedText(getPosition(), (uint8_t)color, ss.str()); + ss << ucfirst(getNameDescription()) << " gained " << (uint64_t)gainExp << " experience points."; + + SpectatorVec textList; + for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) + { + if(!(*it)->getPlayer()) + continue; + + if((*it) != this) + textList.push_back(*it); + } + + MessageDetails* details = new MessageDetails((int32_t)gainExp, (Color_t)color); + g_game.addStatsMessage(textList, MSG_EXPERIENCE_OTHERS, ss.str(), targetPos, details); + if(Player* player = getPlayer()) + { + ss.str(""); + ss << "You gained " << (uint64_t)gainExp << " experience points."; + player->sendStatsMessage(MSG_EXPERIENCE, ss.str(), targetPos, details); + } + + delete details; } -void Creature::onGainSharedExperience(double& gainExp, bool fromMonster, bool multiplied) +void Creature::onGainSharedExperience(double& gainExp, Creature* target, bool multiplied) { if(gainExp <= 0) return; @@ -1260,7 +1377,7 @@ void Creature::onGainSharedExperience(double& gainExp, bool fromMonster, bool mu if(master) { gainExp = gainExp / 2; - master->onGainSharedExperience(gainExp, fromMonster, multiplied); + master->onGainSharedExperience(gainExp, target, multiplied); } else if(!multiplied) gainExp *= g_config.getDouble(ConfigManager::RATE_EXPERIENCE); @@ -1269,9 +1386,35 @@ void Creature::onGainSharedExperience(double& gainExp, bool fromMonster, bool mu if(color < 0) color = random_range(0, 255); + const Position& targetPos = getPosition(); + + SpectatorVec list; + g_game.getSpectators(list, targetPos, false, false, Map::maxViewportX, Map::maxViewportX, + Map::maxViewportY, Map::maxViewportY); + std::stringstream ss; - ss << (uint64_t)gainExp; - g_game.addAnimatedText(getPosition(), (uint8_t)color, ss.str()); + ss << ucfirst(getNameDescription()) << " gained " << (uint64_t)gainExp << " experience points."; + + SpectatorVec textList; + for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) + { + if(!(*it)->getPlayer()) + continue; + + if((*it) != this) + textList.push_back(*it); + } + + MessageDetails* details = new MessageDetails((int32_t)gainExp, (Color_t)color); + g_game.addStatsMessage(textList, MSG_EXPERIENCE_OTHERS, ss.str(), targetPos, details); + if(Player* player = getPlayer()) + { + ss.str(""); + ss << "You gained " << (uint64_t)gainExp << " experience points."; + player->sendStatsMessage(MSG_EXPERIENCE, ss.str(), targetPos, details); + } + + delete details; } void Creature::addSummon(Creature* creature) @@ -1347,7 +1490,7 @@ bool Creature::addCombatCondition(Condition* condition) void Creature::removeCondition(ConditionType_t type) { - for(ConditionList::iterator it = conditions.begin(); it != conditions.end();) + for(ConditionList::iterator it = conditions.begin(); it != conditions.end(); ) { if((*it)->getType() != type) { @@ -1366,7 +1509,7 @@ void Creature::removeCondition(ConditionType_t type) void Creature::removeCondition(ConditionType_t type, ConditionId_t id) { - for(ConditionList::iterator it = conditions.begin(); it != conditions.end();) + for(ConditionList::iterator it = conditions.begin(); it != conditions.end(); ) { if((*it)->getType() != type || (*it)->getId() != id) { @@ -1409,7 +1552,7 @@ void Creature::removeCondition(const Creature* attacker, ConditionType_t type) void Creature::removeConditions(ConditionEnd_t reason, bool onlyPersistent/* = true*/) { - for(ConditionList::iterator it = conditions.begin(); it != conditions.end();) + for(ConditionList::iterator it = conditions.begin(); it != conditions.end(); ) { if(onlyPersistent && !(*it)->isPersistent()) { @@ -1439,7 +1582,7 @@ Condition* Creature::getCondition(ConditionType_t type, ConditionId_t id, uint32 void Creature::executeConditions(uint32_t interval) { - for(ConditionList::iterator it = conditions.begin(); it != conditions.end();) + for(ConditionList::iterator it = conditions.begin(); it != conditions.end(); ) { if((*it)->executeCondition(this, interval)) { @@ -1461,13 +1604,14 @@ bool Creature::hasCondition(ConditionType_t type, int32_t subId/* = 0*/, bool ch if(isSuppress(type)) return false; + Condition* condition = NULL; for(ConditionList::const_iterator it = conditions.begin(); it != conditions.end(); ++it) { - if((*it)->getType() != type || (subId != -1 && (*it)->getSubId() != (uint32_t)subId)) + if(!(condition = *it) || condition->getType() != type || + (subId != -1 && condition->getSubId() != (uint32_t)subId)) continue; - if(!checkTime || g_config.getBool(ConfigManager::OLD_CONDITION_ACCURACY) - || !(*it)->getEndTime() || (*it)->getEndTime() >= OTSYS_TIME()) + if(!checkTime || !condition->getEndTime() || condition->getEndTime() >= OTSYS_TIME()) return true; } @@ -1489,7 +1633,7 @@ bool Creature::isSuppress(ConditionType_t type) const return ((getConditionSuppressions() & (uint32_t)type) == (uint32_t)type); } -std::string Creature::getDescription(int32_t lookDistance) const +std::string Creature::getDescription(int32_t) const { return "a creature"; } @@ -1497,7 +1641,7 @@ std::string Creature::getDescription(int32_t lookDistance) const int32_t Creature::getStepDuration(Direction dir) const { if(dir == NORTHWEST || dir == NORTHEAST || dir == SOUTHWEST || dir == SOUTHEAST) - return getStepDuration() * 2; + return getStepDuration() << 1; return getStepDuration(); } @@ -1518,13 +1662,16 @@ int32_t Creature::getStepDuration() const return ((1000 * Item::items[tile->ground->getID()].speed) / stepSpeed) * lastStepCost; } -int64_t Creature::getEventStepTicks() const +int64_t Creature::getEventStepTicks(bool onlyDelay/* = false*/) const { int64_t ret = getWalkDelay(); if(ret > 0) return ret; - return getStepDuration(); + if(!onlyDelay) + return getStepDuration(); + + return 1; } void Creature::getCreatureLight(LightInfo& light) const @@ -1532,7 +1679,7 @@ void Creature::getCreatureLight(LightInfo& light) const light = internalLight; } -void Creature::setNormalCreatureLight() +void Creature::resetLight() { internalLight.level = internalLight.color = 0; } @@ -1540,7 +1687,7 @@ void Creature::setNormalCreatureLight() bool Creature::registerCreatureEvent(const std::string& name) { CreatureEvent* event = g_creatureEvents->getEventByName(name); - if(!event) //check for existance + if(!event || !event->isLoaded()) //check for existance return false; for(CreatureEventList::iterator it = eventsList.begin(); it != eventsList.end(); ++it) @@ -1549,26 +1696,47 @@ bool Creature::registerCreatureEvent(const std::string& name) return false; } - if(!hasEventRegistered(event->getEventType())) //there's no such type registered yet, so set the bit in the bitfield - scriptEventsBitField |= ((uint32_t)1 << event->getEventType()); - eventsList.push_back(event); return true; } -CreatureEventList Creature::getCreatureEvents(CreatureEventType_t type) +bool Creature::unregisterCreatureEvent(const std::string& name) { - CreatureEventList retList; - if(!hasEventRegistered(type)) - return retList; + CreatureEvent* event = g_creatureEvents->getEventByName(name); + if(!event || !event->isLoaded()) //check for existance + return false; + for(CreatureEventList::iterator it = eventsList.begin(); it != eventsList.end(); ++it) + { + if((*it) != event) + continue; + + eventsList.erase(it); + return true; // we shouldn't have a duplicate + } + + return false; +} + +void Creature::unregisterCreatureEvent(CreatureEventType_t type) +{ for(CreatureEventList::iterator it = eventsList.begin(); it != eventsList.end(); ++it) { if((*it)->getEventType() == type) - retList.push_back(*it); + it = eventsList.erase(it); + } +} + +CreatureEventList Creature::getCreatureEvents(CreatureEventType_t type) +{ + CreatureEventList list; + for(CreatureEventList::iterator it = eventsList.begin(); it != eventsList.end(); ++it) + { + if((*it)->getEventType() == type && (*it)->isLoaded()) + list.push_back(*it); } - return retList; + return list; } FrozenPathingConditionCall::FrozenPathingConditionCall(const Position& _targetPos) diff --git a/creature.h b/creature.h index 55adf26..2c88c65 100644 --- a/creature.h +++ b/creature.h @@ -57,14 +57,6 @@ enum lootDrop_t LOOT_DROP_NONE }; -enum killflags_t -{ - KILLFLAG_NONE = 0, - KILLFLAG_LASTHIT = 1 << 0, - KILLFLAG_JUSTIFY = 1 << 1, - KILLFLAG_UNJUSTIFIED = 1 << 2 -}; - enum Visible_t { VISIBLE_NONE = 0, @@ -77,12 +69,14 @@ enum Visible_t struct FindPathParams { bool fullPathSearch, clearSight, allowDiagonal, keepDistance; + uint16_t maxClosedNodes; int32_t maxSearchDist, minTargetDist, maxTargetDist; FindPathParams() { fullPathSearch = clearSight = allowDiagonal = true; - maxSearchDist = minTargetDist = maxTargetDist = -1; keepDistance = false; + maxClosedNodes = 100; + maxSearchDist = minTargetDist = maxTargetDist = -1; } }; @@ -90,25 +84,41 @@ struct DeathLessThan; struct DeathEntry { DeathEntry(std::string name, int32_t dmg): - data(name), damage(dmg), unjustified(false) {} + data(name), damage(dmg), last(false), justify(false), unjustified(false) {} DeathEntry(Creature* killer, int32_t dmg): - data(killer), damage(dmg), unjustified(false) {} - void setUnjustified(bool v) {unjustified = v;} + data(killer), damage(dmg), last(false), justify(false), unjustified(false) {} bool isCreatureKill() const {return data.type() == typeid(Creature*);} bool isNameKill() const {return !isCreatureKill();} + + void setWar(War_t v) {war = v;} + War_t getWar() const {return war;} + + void setLast() {last = true;} + bool isLast() const {return last;} + + void setJustify() {justify = true;} + bool isJustify() const {return justify;} + + void setUnjustified() {unjustified = true;} bool isUnjustified() const {return unjustified;} const std::type_info& getKillerType() const {return data.type();} + int32_t getDamage() const {return damage;} + Creature* getKillerCreature() const {return boost::any_cast(data);} std::string getKillerName() const {return boost::any_cast(data);} protected: + friend struct DeathLessThan; + boost::any data; int32_t damage; - bool unjustified; + War_t war; - friend struct DeathLessThan; + bool last; + bool justify; + bool unjustified; }; struct DeathLessThan @@ -119,7 +129,7 @@ struct DeathLessThan typedef std::vector DeathList; typedef std::list CreatureEventList; typedef std::list ConditionList; -typedef std::map StorageMap; +typedef std::map StorageMap; class Map; class Tile; @@ -133,7 +143,11 @@ class Item; class Container; #define EVENT_CREATURECOUNT 10 +#ifndef __GROUPED_ATTACKS__ +#define EVENT_CREATURE_THINK_INTERVAL 1000 +#else #define EVENT_CREATURE_THINK_INTERVAL 500 +#endif #define EVENT_CHECK_CREATURE_INTERVAL (EVENT_CREATURE_THINK_INTERVAL / EVENT_CREATURECOUNT) class FrozenPathingConditionCall @@ -161,13 +175,14 @@ class Creature : public AutoId, virtual public Thing virtual ~Creature(); virtual Creature* getCreature() {return this;} - virtual const Creature* getCreature()const {return this;} + virtual const Creature* getCreature() const {return this;} virtual Player* getPlayer() {return NULL;} virtual const Player* getPlayer() const {return NULL;} virtual Npc* getNpc() {return NULL;} virtual const Npc* getNpc() const {return NULL;} virtual Monster* getMonster() {return NULL;} virtual const Monster* getMonster() const {return NULL;} + virtual CreatureType_t getType() const = 0; virtual const std::string& getName() const = 0; virtual const std::string& getNameDescription() const = 0; @@ -194,18 +209,19 @@ class Creature : public AutoId, virtual public Thing virtual bool canSee(const Position& pos) const; virtual bool canSeeCreature(const Creature* creature) const; - virtual bool canWalkthrough(const Creature* creature) const {return creature->isWalkable() || creature->isGhost();} + virtual bool canWalkthrough(const Creature* creature) const; Direction getDirection() const {return direction;} void setDirection(Direction dir) {direction = dir;} bool getHideName() const {return hideName;} void setHideName(bool v) {hideName = v;} + bool getHideHealth() const {return hideHealth;} void setHideHealth(bool v) {hideHealth = v;} - SpeakClasses getSpeakType() const {return speakType;} - void setSpeakType(SpeakClasses type) {speakType = type;} + MessageClasses getSpeakType() const {return speakType;} + void setSpeakType(MessageClasses type) {speakType = type;} Position getMasterPosition() const {return masterPosition;} void setMasterPosition(const Position& pos, uint32_t radius = 1) {masterPosition = pos; masterRadius = radius;} @@ -221,8 +237,7 @@ class Creature : public AutoId, virtual public Thing int32_t getStepDuration(Direction dir) const; int32_t getStepDuration() const; - void getPathToFollowCreature(); - int64_t getEventStepTicks() const; + int64_t getEventStepTicks(bool onlyDelay = false) const; int64_t getTimeSinceLastMove() const; virtual int32_t getStepSpeed() const {return getSpeed();} @@ -232,7 +247,10 @@ class Creature : public AutoId, virtual public Thing int32_t oldSpeed = getSpeed(); varSpeed = varSpeedDelta; if(getSpeed() <= 0) + { stopEventWalk(); + cancelNextWalk = true; + } else if(oldSpeed <= 0 && !listWalkDir.empty()) addEventWalk(); } @@ -246,7 +264,7 @@ class Creature : public AutoId, virtual public Thing virtual int32_t getMaxMana() const {return manaMax;} const Outfit_t getCurrentOutfit() const {return currentOutfit;} - const void setCurrentOutfit(Outfit_t outfit) {currentOutfit = outfit;} + void setCurrentOutfit(Outfit_t outfit) {currentOutfit = outfit;} const Outfit_t getDefaultOutfit() const {return defaultOutfit;} bool isInvisible() const {return hasCondition(CONDITION_INVISIBLE, -1, false);} @@ -257,8 +275,10 @@ class Creature : public AutoId, virtual public Thing //walk functions bool startAutoWalk(std::list& listDir); - void addEventWalk(); + void stopWalk() {cancelNextWalk = true;} + void addEventWalk(bool firstStep = false); void stopEventWalk(); + void goToFollowCreature(); //walk events virtual void onWalk(Direction& dir); @@ -270,14 +290,14 @@ class Creature : public AutoId, virtual public Thing virtual bool setFollowCreature(Creature* creature, bool fullPathSearch = false); //follow events - virtual void onFollowCreature(const Creature* creature) {} - virtual void onFollowCreatureComplete(const Creature* creature) {} + virtual void onFollowCreature(const Creature*) {} + virtual void onFollowCreatureComplete(const Creature*) {} //combat functions Creature* getAttackedCreature() {return attackedCreature;} virtual bool setAttackedCreature(Creature* creature); virtual BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, - bool checkDefense = false, bool checkArmor = false); + bool checkDefense = false, bool checkArmor = false, bool reflect = true, bool field = false, bool element = false); void setMaster(Creature* creature) {master = creature;} Creature* getMaster() {return master;} @@ -321,9 +341,9 @@ class Creature : public AutoId, virtual public Thing virtual void changeMana(int32_t manaChange); void changeMaxMana(uint32_t manaChange) {manaMax = manaChange;} - virtual bool getStorage(const uint32_t key, std::string& value) const; - virtual bool setStorage(const uint32_t key, const std::string& value); - virtual void eraseStorage(const uint32_t key) {storageMap.erase(key);} + virtual bool getStorage(const std::string& key, std::string& value) const; + virtual bool setStorage(const std::string& key, const std::string& value); + virtual void eraseStorage(const std::string& key) {storageMap.erase(key);} inline StorageMap::const_iterator getStorageBegin() const {return storageMap.begin();} inline StorageMap::const_iterator getStorageEnd() const {return storageMap.end();} @@ -332,43 +352,45 @@ class Creature : public AutoId, virtual public Thing virtual void drainHealth(Creature* attacker, CombatType_t combatType, int32_t damage); virtual void drainMana(Creature* attacker, CombatType_t combatType, int32_t damage); - virtual bool challengeCreature(Creature* creature) {return false;} - virtual bool convinceCreature(Creature* creature) {return false;} + virtual bool challengeCreature(Creature*) {return false;} + virtual bool convinceCreature(Creature*) {return false;} virtual bool onDeath(); - virtual double getGainedExperience(Creature* attacker) const {return getDamageRatio(attacker) * (double)getLostExperience();} + virtual double getGainedExperience(Creature* attacker) const; void addDamagePoints(Creature* attacker, int32_t damagePoints); void addHealPoints(Creature* caster, int32_t healthPoints); bool hasBeenAttacked(uint32_t attackerId) const; //combat event functions virtual void onAddCondition(ConditionType_t type, bool hadCondition); - virtual void onAddCombatCondition(ConditionType_t type, bool hadCondition) {} + virtual void onAddCombatCondition(ConditionType_t, bool) {} virtual void onEndCondition(ConditionType_t type); virtual void onTickCondition(ConditionType_t type, int32_t interval, bool& _remove); virtual void onCombatRemoveCondition(const Creature* attacker, Condition* condition); - virtual void onAttackedCreature(Creature* target) {} - virtual void onSummonAttackedCreature(Creature* summon, Creature* target) {} + virtual void onTarget(Creature*) {} + virtual void onSummonTarget(Creature*, Creature*) {} virtual void onAttacked() {} - virtual void onAttackedCreatureDrainHealth(Creature* target, int32_t points); - virtual void onSummonAttackedCreatureDrainHealth(Creature* summon, Creature* target, int32_t points) {} - virtual void onAttackedCreatureDrainMana(Creature* target, int32_t points); - virtual void onSummonAttackedCreatureDrainMana(Creature* summon, Creature* target, int32_t points) {} - virtual void onAttackedCreatureDrain(Creature* target, int32_t points); - virtual void onSummonAttackedCreatureDrain(Creature* summon, Creature* target, int32_t points) {} - virtual void onTargetCreatureGainHealth(Creature* target, int32_t points); - virtual void onAttackedCreatureKilled(Creature* target); - virtual bool onKilledCreature(Creature* target, uint32_t& flags); - virtual void onGainExperience(double& gainExp, bool fromMonster, bool multiplied); - virtual void onGainSharedExperience(double& gainExp, bool fromMonster, bool multiplied); - virtual void onAttackedCreatureBlockHit(Creature* target, BlockType_t blockType) {} - virtual void onBlockHit(BlockType_t blockType) {} + virtual void onTargetDrainHealth(Creature* target, int32_t points); + virtual void onSummonTargetDrainHealth(Creature*, Creature*, int32_t) {} + virtual void onTargetDrainMana(Creature* target, int32_t points); + virtual void onSummonTargetDrainMana(Creature*, Creature*, int32_t) {} + virtual void onTargetDrain(Creature* target, int32_t points); + virtual void onSummonTargetDrain(Creature*, Creature*, int32_t) {} + virtual void onTargetGainHealth(Creature* target, int32_t points); + virtual void onTargetGainMana(Creature* target, int32_t points); + virtual void onTargetGain(Creature* target, int32_t points); + virtual void onTargetKilled(Creature* target); + virtual bool onKilledCreature(Creature* target, DeathEntry& entry); + virtual void onGainExperience(double& gainExp, Creature* target, bool multiplied); + virtual void onGainSharedExperience(double& gainExp, Creature* target, bool multiplied); + virtual void onTargetBlockHit(Creature*, BlockType_t) {} + virtual void onBlockHit(BlockType_t) {} virtual void onChangeZone(ZoneType_t zone); - virtual void onAttackedCreatureChangeZone(ZoneType_t zone); + virtual void onTargetChangeZone(ZoneType_t zone); virtual void onIdleStatus(); virtual void getCreatureLight(LightInfo& light) const; - virtual void setNormalCreatureLight(); + virtual void resetLight(); void setCreatureLight(LightInfo& light) {internalLight = light;} virtual void onThink(uint32_t interval); @@ -380,45 +402,56 @@ class Creature : public AutoId, virtual public Thing virtual void onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem, const ItemType& oldType, const Item* newItem, const ItemType& newType); virtual void onRemoveTileItem(const Tile* tile, const Position& pos, const ItemType& iType, const Item* item); - virtual void onUpdateTile(const Tile* tile, const Position& pos) {} + virtual void onUpdateTile(const Tile*, const Position&) {} virtual void onCreatureAppear(const Creature* creature); - virtual void onCreatureDisappear(const Creature* creature, bool isLogout); + virtual void onCreatureDisappear(const Creature* creature, bool) {internalCreatureDisappear(creature, true);} virtual void onCreatureMove(const Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport); - virtual void onAttackedCreatureDisappear(bool isLogout) {} - virtual void onFollowCreatureDisappear(bool isLogout) {} + virtual void onTargetDisappear(bool) {} + virtual void onFollowCreatureDisappear(bool) {} - virtual void onCreatureTurn(const Creature* creature) {} - virtual void onCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, - Position* pos = NULL) {} + virtual void onCreatureTurn(const Creature*) {} + virtual void onCreatureSay(const Creature*, MessageClasses, const std::string&, + Position* = NULL) {} - virtual void onCreatureChangeOutfit(const Creature* creature, const Outfit_t& outfit) {} - virtual void onCreatureConvinced(const Creature* convincer, const Creature* creature) {} - virtual void onCreatureChangeVisible(const Creature* creature, Visible_t visible) {} + virtual void onCreatureChangeOutfit(const Creature*, const Outfit_t&) {} + virtual void onCreatureConvinced(const Creature*, const Creature*) {} + virtual void onCreatureChangeVisible(const Creature*, Visible_t) {} virtual void onPlacedCreature() {} virtual void onRemovedCreature(); virtual WeaponType_t getWeaponType() {return WEAPON_NONE;} - virtual bool getCombatValues(int32_t& min, int32_t& max) {return false;} + virtual bool getCombatValues(int32_t&, int32_t&) {return false;} virtual void setSkull(Skulls_t newSkull) {skull = newSkull;} virtual Skulls_t getSkull() const {return skull;} - virtual Skulls_t getSkullClient(const Creature* creature) const {return creature->getSkull();} + virtual Skulls_t getSkullType(const Creature* creature) const {return creature->getSkull();} virtual void setShield(PartyShields_t newPartyShield) {partyShield = newPartyShield;} virtual PartyShields_t getShield() const {return partyShield;} virtual PartyShields_t getPartyShield(const Creature* creature) const {return creature->getShield();} - void setDropLoot(lootDrop_t _lootDrop) {lootDrop = _lootDrop;} - void setLossSkill(bool _skillLoss) {skillLoss = _skillLoss;} + virtual void setEmblem(GuildEmblems_t newGuildEmblem) {guildEmblem = newGuildEmblem;} + virtual GuildEmblems_t getEmblem() const {return guildEmblem;} + virtual GuildEmblems_t getGuildEmblem(const Creature* creature) const {return creature->getEmblem();} + + virtual void setDropLoot(lootDrop_t _lootDrop) {lootDrop = _lootDrop;} + virtual void setLossSkill(bool _skillLoss) {skillLoss = _skillLoss;} + bool getLossSkill() const {return skillLoss;} - void setNoMove(bool _cannotMove) {cannotMove = _cannotMove;} + void setNoMove(bool _cannotMove) + { + cannotMove = _cannotMove; + cancelNextWalk = true; + } bool getNoMove() const {return cannotMove;} //creature script events bool registerCreatureEvent(const std::string& name); + bool unregisterCreatureEvent(const std::string& name); + void unregisterCreatureEvent(CreatureEventType_t type); CreatureEventList getCreatureEvents(CreatureEventType_t type); virtual void setParent(Cylinder* cylinder) @@ -456,7 +489,7 @@ class Creature : public AutoId, virtual public Thing int32_t mana, manaMax; bool hideName, hideHealth, cannotMove; - SpeakClasses speakType; + MessageClasses speakType; Outfit_t currentOutfit; Outfit_t defaultOutfit; @@ -472,6 +505,7 @@ class Creature : public AutoId, virtual public Thing lootDrop_t lootDrop; Skulls_t skull; PartyShields_t partyShield; + GuildEmblems_t guildEmblem; Direction direction; ConditionList conditions; LightInfo internalLight; @@ -483,6 +517,7 @@ class Creature : public AutoId, virtual public Thing //follow variables Creature* followCreature; uint32_t eventWalk; + bool cancelNextWalk; std::list listWalkDir; uint32_t walkUpdateTicks; bool hasFollowPath; @@ -493,15 +528,15 @@ class Creature : public AutoId, virtual public Thing struct CountBlock_t { uint32_t total; - int64_t ticks, start; + int64_t start, ticks; CountBlock_t(uint32_t points) { - start = ticks = OTSYS_TIME(); total = points; + start = ticks = OTSYS_TIME(); } - CountBlock_t() {start = ticks = total = 0;} + CountBlock_t() {total = start = ticks = 0;} }; typedef std::map CountMap; @@ -509,7 +544,7 @@ class Creature : public AutoId, virtual public Thing CountMap healMap; CreatureEventList eventsList; - uint32_t scriptEventsBitField, blockCount, blockTicks, lastHitCreature; + uint32_t blockCount, blockTicks, lastHitCreature; CombatType_t lastDamageSource; #ifdef __DEBUG__ @@ -517,10 +552,12 @@ class Creature : public AutoId, virtual public Thing #endif void updateMapCache(); + void updateTileCache(const Tile* tile); void updateTileCache(const Tile* tile, int32_t dx, int32_t dy); void updateTileCache(const Tile* tile, const Position& pos); - bool hasEventRegistered(CreatureEventType_t event) const {return (0 != (scriptEventsBitField & ((uint32_t)1 << event)));} + void internalCreatureDisappear(const Creature* creature, bool isLogout); + virtual bool hasExtraSwing() {return false;} virtual uint16_t getLookCorpse() const {return 0;} @@ -531,14 +568,13 @@ class Creature : public AutoId, virtual public Thing DeathList getKillers(); virtual Item* createCorpse(DeathList deathList); - virtual void dropLoot(Container* corpse) {} + virtual void dropLoot(Container*) {} virtual void dropCorpse(DeathList deathList); - virtual void doAttacking(uint32_t interval) {} - void internalCreatureDisappear(const Creature* creature, bool isLogout); + virtual void doAttacking(uint32_t) {} friend class Game; friend class Map; - friend class LuaScriptInterface; + friend class LuaInterface; }; #endif diff --git a/creatureevent.cpp b/creatureevent.cpp index db62426..842b237 100644 --- a/creatureevent.cpp +++ b/creatureevent.cpp @@ -20,27 +20,32 @@ #endif #include "creatureevent.h" -#include "player.h" #include "tools.h" +#include "monster.h" +#include "player.h" + +extern CreatureEvents* g_creatureEvents; + CreatureEvents::CreatureEvents(): -m_interface("CreatureScript Interface") + m_interface("CreatureScript Interface") { m_interface.initState(); } CreatureEvents::~CreatureEvents() { - CreatureEventList::iterator it; - for(it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) - delete it->second; + for(CreatureEventList::iterator it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) + delete (*it); + + m_creatureEvents.clear(); } void CreatureEvents::clear() { //clear creature events for(CreatureEventList::iterator it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) - it->second->clearEvent(); + (*it)->clearEvent(); //clear lua state m_interface.reInitState(); @@ -55,7 +60,7 @@ Event* CreatureEvents::getEvent(const std::string& nodeName) return NULL; } -bool CreatureEvents::registerEvent(Event* event, xmlNodePtr p, bool override) +bool CreatureEvents::registerEvent(Event* event, xmlNodePtr, bool override) { CreatureEvent* creatureEvent = dynamic_cast(event); if(!creatureEvent) @@ -63,33 +68,33 @@ bool CreatureEvents::registerEvent(Event* event, xmlNodePtr p, bool override) if(creatureEvent->getEventType() == CREATURE_EVENT_NONE) { - std::cout << "[Error - CreatureEvents::registerEvent] Trying to register event without type!" << std::endl; + std::clog << "[Error - CreatureEvents::registerEvent] Trying to register event without type!" << std::endl; return false; } - if(CreatureEvent* oldEvent = getEventByName(creatureEvent->getName(), false)) + if(CreatureEvent* oldEvent = getEventByName(creatureEvent->getName())) { //if there was an event with the same type that is not loaded (happens when realoading), it is reused - if(oldEvent->getEventType() == creatureEvent->getEventType() && (!oldEvent->isLoaded() || override)) - oldEvent->copyEvent(creatureEvent); + if(oldEvent->getEventType() == creatureEvent->getEventType()) + { + if(!oldEvent->isLoaded() || override) + oldEvent->copyEvent(creatureEvent); - /*delete creatureEvent; - return override;*/ - return false; + return override; + } } //if not, register it normally - m_creatureEvents[creatureEvent->getName()] = creatureEvent; + m_creatureEvents.push_back(creatureEvent); return true; } -CreatureEvent* CreatureEvents::getEventByName(const std::string& name, bool forceLoaded /*= true*/) +CreatureEvent* CreatureEvents::getEventByName(const std::string& name) { - CreatureEventList::iterator it = m_creatureEvents.find(name); - if(it != m_creatureEvents.end()) + for(CreatureEventList::iterator it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) { - if(!forceLoaded || it->second->isLoaded()) - return it->second; + if((*it)->getName() == name) + return (*it); } return NULL; @@ -101,8 +106,8 @@ bool CreatureEvents::playerLogin(Player* player) bool result = true; for(CreatureEventList::iterator it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) { - if(it->second->getEventType() == CREATURE_EVENT_LOGIN && - !it->second->executeLogin(player) && result) + if((*it)->getEventType() == CREATURE_EVENT_LOGIN && + (*it)->isLoaded() && !(*it)->executePlayer(player) && result) result = false; } @@ -115,99 +120,144 @@ bool CreatureEvents::playerLogout(Player* player, bool forceLogout) bool result = true; for(CreatureEventList::iterator it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) { - if(it->second->getEventType() == CREATURE_EVENT_LOGOUT && - !it->second->executeLogout(player, forceLogout) && result) + if((*it)->getEventType() == CREATURE_EVENT_LOGOUT && (*it)->isLoaded() + && !(*it)->executeLogout(player, forceLogout) && result) + result = false; + } + + return result; +} + +bool CreatureEvents::monsterSpawn(Monster* monster) +{ + //fire global event if is registered + bool result = true; + for(CreatureEventList::iterator it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) + { + if((*it)->getEventType() == CREATURE_EVENT_SPAWN_GLOBAL && + (*it)->isLoaded() && !(*it)->executeSpawn(monster) && result) result = false; } - return forceLogout || result; + return result; } -///////////////////////////////////// +CreatureEventType_t CreatureEvents::getType(const std::string& type) +{ + CreatureEventType_t _type = CREATURE_EVENT_NONE; + if(type == "login") + _type = CREATURE_EVENT_LOGIN; + else if(type == "logout") + _type = CREATURE_EVENT_LOGOUT; + else if(type == "channeljoin") + _type = CREATURE_EVENT_CHANNEL_JOIN; + else if(type == "channelleave") + _type = CREATURE_EVENT_CHANNEL_LEAVE; + else if(type == "channelrequest") + _type = CREATURE_EVENT_CHANNEL_REQUEST; + else if(type == "advance") + _type = CREATURE_EVENT_ADVANCE; + else if(type == "mailsend") + _type = CREATURE_EVENT_MAIL_SEND; + else if(type == "mailreceive") + _type = CREATURE_EVENT_MAIL_RECEIVE; + else if(type == "traderequest") + _type = CREATURE_EVENT_TRADE_REQUEST; + else if(type == "tradeaccept") + _type = CREATURE_EVENT_TRADE_ACCEPT; + else if(type == "textedit") + _type = CREATURE_EVENT_TEXTEDIT; + else if(type == "houseedit") + _type = CREATURE_EVENT_HOUSEEDIT; + else if(type == "reportbug") + _type = CREATURE_EVENT_REPORTBUG; + else if(type == "reportviolation") + _type = CREATURE_EVENT_REPORTVIOLATION; + else if(type == "thankyou") + _type = CREATURE_EVENT_THANKYOU; + else if(type == "look") + _type = CREATURE_EVENT_LOOK; + else if(type == "spawn" || type == "spawn-single") + _type = CREATURE_EVENT_SPAWN_SINGLE; + else if(type == "spawnall" || type == "spawn-global") + _type = CREATURE_EVENT_SPAWN_GLOBAL; + else if(type == "think") + _type = CREATURE_EVENT_THINK; + else if(type == "direction") + _type = CREATURE_EVENT_DIRECTION; + else if(type == "outfit") + _type = CREATURE_EVENT_OUTFIT; + else if(type == "statschange") + _type = CREATURE_EVENT_STATSCHANGE; + else if(type == "areacombat") + _type = CREATURE_EVENT_COMBAT_AREA; + else if(type == "throw") + _type = CREATURE_EVENT_THROW; + else if(type == "push") + _type = CREATURE_EVENT_PUSH; + else if(type == "target") + _type = CREATURE_EVENT_TARGET; + else if(type == "follow") + _type = CREATURE_EVENT_FOLLOW; + else if(type == "combat") + _type = CREATURE_EVENT_COMBAT; + else if(type == "attack") + _type = CREATURE_EVENT_ATTACK; + else if(type == "cast") + _type = CREATURE_EVENT_CAST; + else if(type == "kill") + _type = CREATURE_EVENT_KILL; + else if(type == "death") + _type = CREATURE_EVENT_DEATH; + else if(type == "preparedeath") + _type = CREATURE_EVENT_PREPAREDEATH; + + return _type; +} -CreatureEvent::CreatureEvent(LuaScriptInterface* _interface): +CreatureEvent::CreatureEvent(LuaInterface* _interface): Event(_interface) { m_type = CREATURE_EVENT_NONE; - m_isLoaded = false; + m_loaded = false; +} + +CreatureEvent::CreatureEvent(const CreatureEvent* copy): +Event(copy) +{ + m_type = copy->m_type; + m_loaded = copy->m_loaded; + m_scriptId = copy->m_scriptId; + m_interface = copy->m_interface; + m_scripted = copy->m_scripted; + m_loaded = copy->m_loaded; + m_scriptData = copy->m_scriptData; } bool CreatureEvent::configureEvent(xmlNodePtr p) { - std::string str; - if(!readXMLString(p, "name", str)) + std::string strValue; + if(!readXMLString(p, "name", strValue)) { - std::cout << "[Error - CreatureEvent::configureEvent] No name for creature event." << std::endl; + std::clog << "[Error - CreatureEvent::configureEvent] No name for creature event." << std::endl; return false; } - m_eventName = str; - if(!readXMLString(p, "type", str)) + m_eventName = strValue; + if(!readXMLString(p, "type", strValue)) { - std::cout << "[Error - CreatureEvent::configureEvent] No type for creature event." << std::endl; + std::clog << "[Error - CreatureEvent::configureEvent] No type for creature event." << std::endl; return false; } - std::string tmpStr = asLowerCaseString(str); - if(tmpStr == "login") - m_type = CREATURE_EVENT_LOGIN; - else if(tmpStr == "logout") - m_type = CREATURE_EVENT_LOGOUT; - else if(tmpStr == "joinchannel") - m_type = CREATURE_EVENT_CHANNEL_JOIN; - else if(tmpStr == "leavechannel") - m_type = CREATURE_EVENT_CHANNEL_LEAVE; - else if(tmpStr == "advance") - m_type = CREATURE_EVENT_ADVANCE; - else if(tmpStr == "sendmail") - m_type = CREATURE_EVENT_MAIL_SEND; - else if(tmpStr == "receivemail") - m_type = CREATURE_EVENT_MAIL_RECEIVE; - else if(tmpStr == "traderequest") - m_type = CREATURE_EVENT_TRADE_REQUEST; - else if(tmpStr == "tradeaccept") - m_type = CREATURE_EVENT_TRADE_ACCEPT; - else if(tmpStr == "textedit") - m_type = CREATURE_EVENT_TEXTEDIT; - else if(tmpStr == "reportbug") - m_type = CREATURE_EVENT_REPORTBUG; - else if(tmpStr == "look") - m_type = CREATURE_EVENT_LOOK; - else if(tmpStr == "think") - m_type = CREATURE_EVENT_THINK; - else if(tmpStr == "direction") - m_type = CREATURE_EVENT_DIRECTION; - else if(tmpStr == "outfit") - m_type = CREATURE_EVENT_OUTFIT; - else if(tmpStr == "statschange") - m_type = CREATURE_EVENT_STATSCHANGE; - else if(tmpStr == "areacombat") - m_type = CREATURE_EVENT_COMBAT_AREA; - else if(tmpStr == "push") - m_type = CREATURE_EVENT_PUSH; - else if(tmpStr == "target") - m_type = CREATURE_EVENT_TARGET; - else if(tmpStr == "follow") - m_type = CREATURE_EVENT_FOLLOW; - else if(tmpStr == "combat") - m_type = CREATURE_EVENT_COMBAT; - else if(tmpStr == "attack") - m_type = CREATURE_EVENT_ATTACK; - else if(tmpStr == "cast") - m_type = CREATURE_EVENT_CAST; - else if(tmpStr == "kill") - m_type = CREATURE_EVENT_KILL; - else if(tmpStr == "death") - m_type = CREATURE_EVENT_DEATH; - else if(tmpStr == "preparedeath") - m_type = CREATURE_EVENT_PREPAREDEATH; - else + m_type = g_creatureEvents->getType(asLowerCaseString(strValue)); + if(m_type == CREATURE_EVENT_NONE) { - std::cout << "[Error - CreatureEvent::configureEvent] No valid type for creature event." << str << std::endl; + std::clog << "[Error - CreatureEvent::configureEvent] No valid type for creature event: " << strValue << "." << std::endl; return false; } - m_isLoaded = true; + m_loaded = true; return true; } @@ -219,10 +269,15 @@ std::string CreatureEvent::getScriptEventName() const return "onLogin"; case CREATURE_EVENT_LOGOUT: return "onLogout"; + case CREATURE_EVENT_SPAWN_SINGLE: + case CREATURE_EVENT_SPAWN_GLOBAL: + return "onSpawn"; case CREATURE_EVENT_CHANNEL_JOIN: - return "onJoinChannel"; + return "onChannelJoin"; case CREATURE_EVENT_CHANNEL_LEAVE: - return "onLeaveChannel"; + return "onChannelLeave"; + case CREATURE_EVENT_CHANNEL_REQUEST: + return "onChannelRequest"; case CREATURE_EVENT_THINK: return "onThink"; case CREATURE_EVENT_ADVANCE: @@ -234,21 +289,29 @@ std::string CreatureEvent::getScriptEventName() const case CREATURE_EVENT_OUTFIT: return "onOutfit"; case CREATURE_EVENT_MAIL_SEND: - return "onSendMail"; + return "onMailSend"; case CREATURE_EVENT_MAIL_RECEIVE: - return "onReceiveMail"; + return "onMailReceive"; case CREATURE_EVENT_TRADE_REQUEST: return "onTradeRequest"; case CREATURE_EVENT_TRADE_ACCEPT: return "onTradeAccept"; case CREATURE_EVENT_TEXTEDIT: return "onTextEdit"; + case CREATURE_EVENT_HOUSEEDIT: + return "onHouseEdit"; case CREATURE_EVENT_REPORTBUG: return "onReportBug"; + case CREATURE_EVENT_REPORTVIOLATION: + return "onReportViolation"; + case CREATURE_EVENT_THANKYOU: + return "onThankYou"; case CREATURE_EVENT_STATSCHANGE: return "onStatsChange"; case CREATURE_EVENT_COMBAT_AREA: return "onAreaCombat"; + case CREATURE_EVENT_THROW: + return "onThrow"; case CREATURE_EVENT_PUSH: return "onPush"; case CREATURE_EVENT_TARGET: @@ -280,27 +343,38 @@ std::string CreatureEvent::getScriptEventParams() const switch(m_type) { case CREATURE_EVENT_LOGIN: + case CREATURE_EVENT_SPAWN_SINGLE: + case CREATURE_EVENT_SPAWN_GLOBAL: return "cid"; case CREATURE_EVENT_LOGOUT: return "cid, forceLogout"; case CREATURE_EVENT_CHANNEL_JOIN: case CREATURE_EVENT_CHANNEL_LEAVE: return "cid, channel, users"; + case CREATURE_EVENT_CHANNEL_REQUEST: + return "cid, channel, custom"; case CREATURE_EVENT_ADVANCE: return "cid, skill, oldLevel, newLevel"; case CREATURE_EVENT_LOOK: return "cid, thing, position, lookDistance"; case CREATURE_EVENT_MAIL_SEND: - return "cid, receiver, item, openBox"; + return "cid, target, item, openBox"; case CREATURE_EVENT_MAIL_RECEIVE: - return "cid, sender, item, openBox"; + return "cid, target, item, openBox"; case CREATURE_EVENT_TRADE_REQUEST: - case CREATURE_EVENT_TRADE_ACCEPT: return "cid, target, item"; + case CREATURE_EVENT_TRADE_ACCEPT: + return "cid, target, item, targetItem"; case CREATURE_EVENT_TEXTEDIT: return "cid, item, newText"; + case CREATURE_EVENT_HOUSEEDIT: + return "cid, house, list, text"; case CREATURE_EVENT_REPORTBUG: return "cid, comment"; + case CREATURE_EVENT_REPORTVIOLATION: + return "cid, type, reason, name, comment, translation, statementId"; + case CREATURE_EVENT_THANKYOU: + return "cid, statementId"; case CREATURE_EVENT_THINK: return "cid, interval"; case CREATURE_EVENT_DIRECTION: @@ -310,15 +384,19 @@ std::string CreatureEvent::getScriptEventParams() const return "cid, attacker, type, combat, value"; case CREATURE_EVENT_COMBAT_AREA: return "cid, ground, position, aggressive"; + case CREATURE_EVENT_THROW: + return "cid, item, fromPosition, toPosition"; case CREATURE_EVENT_PUSH: + return "cid, target, ground, position"; + case CREATURE_EVENT_COMBAT: + return "cid, target, aggressive"; case CREATURE_EVENT_TARGET: case CREATURE_EVENT_FOLLOW: - case CREATURE_EVENT_COMBAT: case CREATURE_EVENT_ATTACK: case CREATURE_EVENT_CAST: return "cid, target"; case CREATURE_EVENT_KILL: - return "cid, target, lastHit"; + return "cid, target, damage, flags, war"; case CREATURE_EVENT_DEATH: return "cid, corpse, deathList"; case CREATURE_EVENT_PREPAREDEATH: @@ -336,7 +414,13 @@ void CreatureEvent::copyEvent(CreatureEvent* creatureEvent) m_scriptId = creatureEvent->m_scriptId; m_interface = creatureEvent->m_interface; m_scripted = creatureEvent->m_scripted; - m_isLoaded = creatureEvent->m_isLoaded; + + std::string* oldScript = m_scriptData; + m_scriptData = creatureEvent->m_scriptData; + + m_loaded = creatureEvent->m_loaded; + if(oldScript) + delete oldScript; } void CreatureEvent::clearEvent() @@ -344,10 +428,10 @@ void CreatureEvent::clearEvent() m_scriptId = 0; m_interface = NULL; m_scripted = EVENT_SCRIPT_FALSE; - m_isLoaded = false; + m_loaded = false; } -uint32_t CreatureEvent::executeLogin(Player* player) +uint32_t CreatureEvent::executePlayer(Player* player) { //onLogin(cid) if(m_interface->reserveEnv()) @@ -359,7 +443,9 @@ uint32_t CreatureEvent::executeLogin(Player* player) std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(player) << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -375,7 +461,7 @@ uint32_t CreatureEvent::executeLogin(Player* player) #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -392,7 +478,7 @@ uint32_t CreatureEvent::executeLogin(Player* player) } else { - std::cout << "[Error - CreatureEvent::executeLogin] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executePlayer] Call stack overflow." << std::endl; return 0; } } @@ -411,7 +497,9 @@ uint32_t CreatureEvent::executeLogout(Player* player, bool forceLogout) scriptstream << "local cid = " << env->addThing(player) << std::endl; scriptstream << "local forceLogout = " << (forceLogout ? "true" : "false") << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -427,7 +515,7 @@ uint32_t CreatureEvent::executeLogout(Player* player, bool forceLogout) #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -446,14 +534,14 @@ uint32_t CreatureEvent::executeLogout(Player* player, bool forceLogout) } else { - std::cout << "[Error - CreatureEvent::executeLogout] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeLogout] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeChannelJoin(Player* player, uint16_t channelId, UsersMap usersMap) +uint32_t CreatureEvent::executeChannel(Player* player, uint16_t channelId, UsersMap usersMap) { - //onJoinChannel(cid, channel, users) + //onChannel[Join/Leave](cid, channel, users) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); @@ -468,73 +556,9 @@ uint32_t CreatureEvent::executeChannelJoin(Player* player, uint16_t channelId, U for(UsersMap::iterator it = usersMap.begin(); it != usersMap.end(); ++it) scriptstream << "users:insert(" << env->addThing(it->second) << ")" << std::endl; - scriptstream << m_scriptData; - bool result = true; - if(m_interface->loadBuffer(scriptstream.str())) - { - lua_State* L = m_interface->getState(); - result = m_interface->getGlobalBool(L, "_result", true); - } - - m_interface->releaseEnv(); - return result; - } - else - { - #ifdef __DEBUG_LUASCRIPTS__ - char desc[35]; - sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); - #endif - - env->setScriptId(m_scriptId, m_interface); - env->setRealPos(player->getPosition()); - - lua_State* L = m_interface->getState(); - m_interface->pushFunction(m_scriptId); - - lua_pushnumber(L, env->addThing(player)); - lua_pushnumber(L, channelId); - - UsersMap::iterator it = usersMap.begin(); - lua_newtable(L); - for(int32_t i = 1; it != usersMap.end(); ++it, ++i) - { - lua_pushnumber(L, i); - lua_pushnumber(L, env->addThing(it->second)); - lua_settable(L, -3); - } - - bool result = m_interface->callFunction(3); - m_interface->releaseEnv(); - return result; - } - } - else - { - std::cout << "[Error - CreatureEvent::executeChannelJoin] Call stack overflow." << std::endl; - return 0; - } -} + if(m_scriptData) + scriptstream << *m_scriptData; -uint32_t CreatureEvent::executeChannelLeave(Player* player, uint16_t channelId, UsersMap usersMap) -{ - //onLeaveChannel(cid, channel, users) - if(m_interface->reserveEnv()) - { - ScriptEnviroment* env = m_interface->getEnv(); - if(m_scripted == EVENT_SCRIPT_BUFFER) - { - env->setRealPos(player->getPosition()); - std::stringstream scriptstream; - scriptstream << "local cid = " << env->addThing(player) << std::endl; - - scriptstream << "local channel = " << channelId << std::endl; - scriptstream << "local users = {}" << std::endl; - for(UsersMap::iterator it = usersMap.begin(); it != usersMap.end(); ++it) - scriptstream << "users:insert(" << env->addThing(it->second) << ")" << std::endl; - - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -550,7 +574,7 @@ uint32_t CreatureEvent::executeChannelLeave(Player* player, uint16_t channelId, #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -578,7 +602,7 @@ uint32_t CreatureEvent::executeChannelLeave(Player* player, uint16_t channelId, } else { - std::cout << "[Error - CreatureEvent::executeChannelLeave] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeChannel] Call stack overflow." << std::endl; return 0; } } @@ -599,7 +623,9 @@ uint32_t CreatureEvent::executeAdvance(Player* player, skills_t skill, uint32_t scriptstream << "local oldLevel = " << oldLevel << std::endl; scriptstream << "local newLevel = " << newLevel << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -615,7 +641,7 @@ uint32_t CreatureEvent::executeAdvance(Player* player, skills_t skill, uint32_t #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -637,14 +663,14 @@ uint32_t CreatureEvent::executeAdvance(Player* player, skills_t skill, uint32_t } else { - std::cout << "[Error - CreatureEvent::executeAdvance] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeAdvance] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeMailSend(Player* player, Player* receiver, Item* item, bool openBox) +uint32_t CreatureEvent::executeMail(Player* player, Player* target, Item* item, bool openBox) { - //onSendMail(cid, receiver, item, openBox) + //onMail[Send/Receive](cid, target, item, openBox) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); @@ -653,12 +679,14 @@ uint32_t CreatureEvent::executeMailSend(Player* player, Player* receiver, Item* env->setRealPos(player->getPosition()); std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(player) << std::endl; + scriptstream << "local target = " << env->addThing(target) << std::endl; - scriptstream << "local receiver = " << env->addThing(receiver) << std::endl; env->streamThing(scriptstream, "item", item, env->addThing(item)); scriptstream << "local openBox = " << (openBox ? "true" : "false") << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -674,7 +702,7 @@ uint32_t CreatureEvent::executeMailSend(Player* player, Player* receiver, Item* #ifdef __DEBUG_LUASCRIPTS__ char desc[30]; sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -684,9 +712,9 @@ uint32_t CreatureEvent::executeMailSend(Player* player, Player* receiver, Item* m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(player)); - lua_pushnumber(L, env->addThing(receiver)); + lua_pushnumber(L, env->addThing(target)); - LuaScriptInterface::pushThing(L, item, env->addThing(item)); + LuaInterface::pushThing(L, item, env->addThing(item)); lua_pushboolean(L, openBox); bool result = m_interface->callFunction(4); @@ -696,14 +724,14 @@ uint32_t CreatureEvent::executeMailSend(Player* player, Player* receiver, Item* } else { - std::cout << "[Error - CreatureEvent::executeMailSend] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeMail] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeMailReceive(Player* player, Player* sender, Item* item, bool openBox) +uint32_t CreatureEvent::executeTradeRequest(Player* player, Player* target, Item* item) { - //onReceiveMail(cid, sender, item, openBox) + //onTradeRequest(cid, target, item) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); @@ -713,11 +741,12 @@ uint32_t CreatureEvent::executeMailReceive(Player* player, Player* sender, Item* std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(player) << std::endl; - scriptstream << "local sender = " << env->addThing(sender) << std::endl; + scriptstream << "local target = " << env->addThing(target) << std::endl; env->streamThing(scriptstream, "item", item, env->addThing(item)); - scriptstream << "local openBox = " << (openBox ? "true" : "false") << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -731,9 +760,9 @@ uint32_t CreatureEvent::executeMailReceive(Player* player, Player* sender, Item* else { #ifdef __DEBUG_LUASCRIPTS__ - char desc[30]; + char desc[35]; sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -743,26 +772,24 @@ uint32_t CreatureEvent::executeMailReceive(Player* player, Player* sender, Item* m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(player)); - lua_pushnumber(L, env->addThing(sender)); - - LuaScriptInterface::pushThing(L, item, env->addThing(item)); - lua_pushboolean(L, openBox); + lua_pushnumber(L, env->addThing(target)); + LuaInterface::pushThing(L, item, env->addThing(item)); - bool result = m_interface->callFunction(4); + bool result = m_interface->callFunction(3); m_interface->releaseEnv(); return result; } } else { - std::cout << "[Error - CreatureEvent::executeMailReceive] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeTradeRequest] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeTradeRequest(Player* player, Player* target, Item* item) +uint32_t CreatureEvent::executeTradeAccept(Player* player, Player* target, Item* item, Item* targetItem) { - //onTradeRequest(cid, target, item) + //onTradeAccept(cid, target, item, targetItem) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); @@ -775,7 +802,9 @@ uint32_t CreatureEvent::executeTradeRequest(Player* player, Player* target, Item scriptstream << "local target = " << env->addThing(target) << std::endl; env->streamThing(scriptstream, "item", item, env->addThing(item)); - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -791,7 +820,7 @@ uint32_t CreatureEvent::executeTradeRequest(Player* player, Player* target, Item #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -802,23 +831,24 @@ uint32_t CreatureEvent::executeTradeRequest(Player* player, Player* target, Item lua_pushnumber(L, env->addThing(player)); lua_pushnumber(L, env->addThing(target)); - LuaScriptInterface::pushThing(L, item, env->addThing(item)); + LuaInterface::pushThing(L, item, env->addThing(item)); + LuaInterface::pushThing(L, targetItem, env->addThing(targetItem)); - bool result = m_interface->callFunction(3); + bool result = m_interface->callFunction(4); m_interface->releaseEnv(); return result; } } else { - std::cout << "[Error - CreatureEvent::executeTradeRequest] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeTradeAccept] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeTradeAccept(Player* player, Player* target, Item* item, Item* targetItem) +uint32_t CreatureEvent::executeLook(Player* player, Thing* thing, const Position& position, int16_t stackpos, int32_t lookDistance) { - //onTradeAccept(cid, target, item, targetItem) + //onLook(cid, thing, position, lookDistance) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); @@ -828,10 +858,13 @@ uint32_t CreatureEvent::executeTradeAccept(Player* player, Player* target, Item* std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(player) << std::endl; - scriptstream << "local target = " << env->addThing(target) << std::endl; - env->streamThing(scriptstream, "item", item, env->addThing(item)); + scriptstream << "local thing = " << env->addThing(thing) << std::endl; + env->streamPosition(scriptstream, "position", position, stackpos); + scriptstream << "local lookDistance = " << lookDistance << std::endl; + + if(m_scriptData) + scriptstream << *m_scriptData; - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -845,9 +878,9 @@ uint32_t CreatureEvent::executeTradeAccept(Player* player, Player* target, Item* else { #ifdef __DEBUG_LUASCRIPTS__ - char desc[35]; + char desc[30]; sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -857,9 +890,10 @@ uint32_t CreatureEvent::executeTradeAccept(Player* player, Player* target, Item* m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(player)); - lua_pushnumber(L, env->addThing(target)); - LuaScriptInterface::pushThing(L, item, env->addThing(item)); - LuaScriptInterface::pushThing(L, targetItem, env->addThing(targetItem)); + LuaInterface::pushThing(L, thing, env->addThing(thing)); + + LuaInterface::pushPosition(L, position, stackpos); + lua_pushnumber(L, lookDistance); bool result = m_interface->callFunction(4); m_interface->releaseEnv(); @@ -868,28 +902,26 @@ uint32_t CreatureEvent::executeTradeAccept(Player* player, Player* target, Item* } else { - std::cout << "[Error - CreatureEvent::executeTradeAccept] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeLook] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeLook(Player* player, Thing* thing, const Position& position, int16_t stackpos, int32_t lookDistance) +uint32_t CreatureEvent::executeSpawn(Monster* monster) { - //onLook(cid, thing, position, lookDistance) + //onSpawn(cid) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { - env->setRealPos(player->getPosition()); + env->setRealPos(monster->getPosition()); std::stringstream scriptstream; - scriptstream << "local cid = " << env->addThing(player) << std::endl; + scriptstream << "local cid = " << env->addThing(monster) << std::endl; - scriptstream << "local thing = " << env->addThing(thing) << std::endl; - env->streamPosition(scriptstream, "position", position, stackpos); - scriptstream << "local lookDistance = " << lookDistance << std::endl; + if(m_scriptData) + scriptstream << *m_scriptData; - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -903,31 +935,26 @@ uint32_t CreatureEvent::executeLook(Player* player, Thing* thing, const Position else { #ifdef __DEBUG_LUASCRIPTS__ - char desc[30]; - sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + char desc[35]; + sprintf(desc, "%s", monster->getName().c_str()); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); - env->setRealPos(player->getPosition()); + env->setRealPos(monster->getPosition()); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); + lua_pushnumber(L, env->addThing(monster)); - lua_pushnumber(L, env->addThing(player)); - LuaScriptInterface::pushThing(L, thing, env->addThing(thing)); - - LuaScriptInterface::pushPosition(L, position, stackpos); - lua_pushnumber(L, lookDistance); - - bool result = m_interface->callFunction(4); + bool result = m_interface->callFunction(1); m_interface->releaseEnv(); return result; } } else { - std::cout << "[Error - CreatureEvent::executeLook] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeSpawn] Call stack overflow." << std::endl; return 0; } } @@ -947,7 +974,9 @@ uint32_t CreatureEvent::executeDirection(Creature* creature, Direction old, Dire scriptstream << "local old = " << old << std::endl; scriptstream << "local current = " << current << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -963,7 +992,7 @@ uint32_t CreatureEvent::executeDirection(Creature* creature, Direction old, Dire #ifdef __DEBUG_LUASCRIPTS__ char desc[30]; sprintf(desc, "%s", creature->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -983,7 +1012,7 @@ uint32_t CreatureEvent::executeDirection(Creature* creature, Direction old, Dire } else { - std::cout << "[Error - CreatureEvent::executeDirection] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeDirection] Call stack overflow." << std::endl; return 0; } } @@ -1003,7 +1032,9 @@ uint32_t CreatureEvent::executeOutfit(Creature* creature, const Outfit_t& old, c env->streamOutfit(scriptstream, "old", old); env->streamOutfit(scriptstream, "current", current); - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1019,7 +1050,7 @@ uint32_t CreatureEvent::executeOutfit(Creature* creature, const Outfit_t& old, c #ifdef __DEBUG_LUASCRIPTS__ char desc[30]; sprintf(desc, "%s", creature->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -1029,8 +1060,8 @@ uint32_t CreatureEvent::executeOutfit(Creature* creature, const Outfit_t& old, c m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(creature)); - LuaScriptInterface::pushOutfit(L, old); - LuaScriptInterface::pushOutfit(L, current); + LuaInterface::pushOutfit(L, old); + LuaInterface::pushOutfit(L, current); bool result = m_interface->callFunction(3); m_interface->releaseEnv(); @@ -1039,7 +1070,7 @@ uint32_t CreatureEvent::executeOutfit(Creature* creature, const Outfit_t& old, c } else { - std::cout << "[Error - CreatureEvent::executeOutfit] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeOutfit] Call stack overflow." << std::endl; return 0; } } @@ -1058,7 +1089,9 @@ uint32_t CreatureEvent::executeThink(Creature* creature, uint32_t interval) scriptstream << "local cid = " << env->addThing(creature) << std::endl; scriptstream << "local interval = " << interval << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1074,7 +1107,7 @@ uint32_t CreatureEvent::executeThink(Creature* creature, uint32_t interval) #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", creature->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -1093,7 +1126,7 @@ uint32_t CreatureEvent::executeThink(Creature* creature, uint32_t interval) } else { - std::cout << "[Error - CreatureEvent::executeThink] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeThink] Call stack overflow." << std::endl; return 0; } } @@ -1116,7 +1149,9 @@ uint32_t CreatureEvent::executeStatsChange(Creature* creature, Creature* attacke scriptstream << "local combat = " << (uint32_t)combat << std::endl; scriptstream << "local value = " << value << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1132,7 +1167,7 @@ uint32_t CreatureEvent::executeStatsChange(Creature* creature, Creature* attacke #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", creature->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -1155,7 +1190,7 @@ uint32_t CreatureEvent::executeStatsChange(Creature* creature, Creature* attacke } else { - std::cout << "[Error - CreatureEvent::executeStatsChange] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeStatsChange] Call stack overflow." << std::endl; return 0; } } @@ -1176,7 +1211,9 @@ uint32_t CreatureEvent::executeCombatArea(Creature* creature, Tile* tile, bool a env->streamPosition(scriptstream, "position", tile->getPosition(), 0); scriptstream << "local aggressive = " << (aggressive ? "true" : "false") << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1192,7 +1229,7 @@ uint32_t CreatureEvent::executeCombatArea(Creature* creature, Tile* tile, bool a #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << creature->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); @@ -1202,9 +1239,9 @@ uint32_t CreatureEvent::executeCombatArea(Creature* creature, Tile* tile, bool a m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(creature)); - LuaScriptInterface::pushThing(L, tile->ground, env->addThing(tile->ground)); + LuaInterface::pushThing(L, tile->ground, env->addThing(tile->ground)); - LuaScriptInterface::pushPosition(L, tile->getPosition(), 0); + LuaInterface::pushPosition(L, tile->getPosition(), 0); lua_pushboolean(L, aggressive); bool result = m_interface->callFunction(4); @@ -1214,14 +1251,14 @@ uint32_t CreatureEvent::executeCombatArea(Creature* creature, Tile* tile, bool a } else { - std::cout << "[Error - CreatureEvent::executeAreaCombat] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeAreaCombat] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeCombat(Creature* creature, Creature* target) +uint32_t CreatureEvent::executeCombat(Creature* creature, Creature* target, bool aggressive) { - //onCombat(cid, target) + //onCombat(cid, target, aggressive) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); @@ -1232,62 +1269,11 @@ uint32_t CreatureEvent::executeCombat(Creature* creature, Creature* target) scriptstream << "local cid = " << env->addThing(creature) << std::endl; scriptstream << "local target = " << env->addThing(target) << std::endl; + scriptstream << "local aggressive = " << (aggressive ? "true" : "false") << std::endl; - scriptstream << m_scriptData; - bool result = true; - if(m_interface->loadBuffer(scriptstream.str())) - { - lua_State* L = m_interface->getState(); - result = m_interface->getGlobalBool(L, "_result", true); - } - - m_interface->releaseEnv(); - return result; - } - else - { - #ifdef __DEBUG_LUASCRIPTS__ - std::stringstream desc; - desc << creature->getName(); - env->setEventDesc(desc.str()); - #endif - - env->setScriptId(m_scriptId, m_interface); - env->setRealPos(creature->getPosition()); - - lua_State* L = m_interface->getState(); - m_interface->pushFunction(m_scriptId); - - lua_pushnumber(L, env->addThing(creature)); - lua_pushnumber(L, env->addThing(target)); - - bool result = m_interface->callFunction(2); - m_interface->releaseEnv(); - return result; - } - } - else - { - std::cout << "[Error - CreatureEvent::executeCombat] Call stack overflow." << std::endl; - return 0; - } -} - -uint32_t CreatureEvent::executeAttack(Creature* creature, Creature* target) -{ - //onAttack(cid, target) - if(m_interface->reserveEnv()) - { - ScriptEnviroment* env = m_interface->getEnv(); - if(m_scripted == EVENT_SCRIPT_BUFFER) - { - env->setRealPos(creature->getPosition()); - std::stringstream scriptstream; - - scriptstream << "local cid = " << env->addThing(creature) << std::endl; - scriptstream << "local target = " << env->addThing(target) << std::endl; + if(m_scriptData) + scriptstream << *m_scriptData; - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1303,7 +1289,7 @@ uint32_t CreatureEvent::executeAttack(Creature* creature, Creature* target) #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << creature->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); @@ -1314,15 +1300,16 @@ uint32_t CreatureEvent::executeAttack(Creature* creature, Creature* target) lua_pushnumber(L, env->addThing(creature)); lua_pushnumber(L, env->addThing(target)); + lua_pushboolean(L, aggressive); - bool result = m_interface->callFunction(2); + bool result = m_interface->callFunction(3); m_interface->releaseEnv(); return result; } } else { - std::cout << "[Error - CreatureEvent::executeAttack] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeCombat] Call stack overflow." << std::endl; return 0; } } @@ -1345,7 +1332,9 @@ uint32_t CreatureEvent::executeCast(Creature* creature, Creature* target/* = NUL else scriptstream << "nil"; - scriptstream << std::endl << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1361,7 +1350,7 @@ uint32_t CreatureEvent::executeCast(Creature* creature, Creature* target/* = NUL #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << creature->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); @@ -1380,16 +1369,26 @@ uint32_t CreatureEvent::executeCast(Creature* creature, Creature* target/* = NUL } else { - std::cout << "[Error - CreatureEvent::executeCast] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeCast] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeKill(Creature* creature, Creature* target, bool lastHit) +uint32_t CreatureEvent::executeKill(Creature* creature, Creature* target, const DeathEntry& entry) { - //onKill(cid, target, lastHit) + //onKill(cid, target, damage, flags) if(m_interface->reserveEnv()) { + uint32_t flags = 0; + if(entry.isLast()) + flags |= 1; + + if(entry.isJustify()) + flags |= 2; + + if(entry.isUnjustified()) + flags |= 4; + ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { @@ -1398,9 +1397,13 @@ uint32_t CreatureEvent::executeKill(Creature* creature, Creature* target, bool l scriptstream << "local cid = " << env->addThing(creature) << std::endl; scriptstream << "local target = " << env->addThing(target) << std::endl; - scriptstream << "local lastHit = " << (lastHit ? "true" : "false") << std::endl; + scriptstream << "local damage = " << entry.getDamage() << std::endl; + scriptstream << "local flags = " << flags << std::endl; + scriptstream << "local war = " << entry.getWar().war << std::endl; + + if(m_scriptData) + scriptstream << *m_scriptData; - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1416,7 +1419,7 @@ uint32_t CreatureEvent::executeKill(Creature* creature, Creature* target, bool l #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << creature->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); @@ -1427,16 +1430,19 @@ uint32_t CreatureEvent::executeKill(Creature* creature, Creature* target, bool l lua_pushnumber(L, env->addThing(creature)); lua_pushnumber(L, env->addThing(target)); - lua_pushboolean(L, lastHit); - bool result = m_interface->callFunction(3); + lua_pushnumber(L, entry.getDamage()); + lua_pushnumber(L, flags); + lua_pushnumber(L, entry.getWar().war); + + bool result = m_interface->callFunction(5); m_interface->releaseEnv(); return result; } } else { - std::cout << "[Error - CreatureEvent::executeKill] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeKill] Call stack overflow." << std::endl; return 0; } } @@ -1466,7 +1472,9 @@ uint32_t CreatureEvent::executeDeath(Creature* creature, Item* corpse, DeathList scriptstream << ")" << std::endl; } - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1482,7 +1490,7 @@ uint32_t CreatureEvent::executeDeath(Creature* creature, Item* corpse, DeathList #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", creature->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -1492,7 +1500,7 @@ uint32_t CreatureEvent::executeDeath(Creature* creature, Item* corpse, DeathList m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(creature)); - LuaScriptInterface::pushThing(L, corpse, env->addThing(corpse)); + LuaInterface::pushThing(L, corpse, env->addThing(corpse)); lua_newtable(L); DeathList::iterator it = deathList.begin(); @@ -1514,7 +1522,7 @@ uint32_t CreatureEvent::executeDeath(Creature* creature, Item* corpse, DeathList } else { - std::cout << "[Error - CreatureEvent::executeDeath] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeDeath] Call stack overflow." << std::endl; return 0; } } @@ -1543,7 +1551,9 @@ uint32_t CreatureEvent::executePrepareDeath(Creature* creature, DeathList deathL scriptstream << ")" << std::endl; } - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1559,7 +1569,7 @@ uint32_t CreatureEvent::executePrepareDeath(Creature* creature, DeathList deathL #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", creature->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -1591,12 +1601,12 @@ uint32_t CreatureEvent::executePrepareDeath(Creature* creature, DeathList deathL } else { - std::cout << "[Error - CreatureEvent::executePrepareDeath] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executePrepareDeath] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeTextEdit(Player* player, Item* item, std::string newText) +uint32_t CreatureEvent::executeTextEdit(Player* player, Item* item, const std::string& newText) { //onTextEdit(cid, item, newText) if(m_interface->reserveEnv()) @@ -1609,9 +1619,11 @@ uint32_t CreatureEvent::executeTextEdit(Player* player, Item* item, std::string scriptstream << "local cid = " << env->addThing(player) << std::endl; env->streamThing(scriptstream, "item", item, env->addThing(item)); - scriptstream << "local newText = " << newText.c_str() << std::endl; + scriptstream << "local newText = " << newText << std::endl; + + if(m_scriptData) + scriptstream << *m_scriptData; - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1627,7 +1639,7 @@ uint32_t CreatureEvent::executeTextEdit(Player* player, Item* item, std::string #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -1637,7 +1649,7 @@ uint32_t CreatureEvent::executeTextEdit(Player* player, Item* item, std::string m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(player)); - LuaScriptInterface::pushThing(L, item, env->addThing(item)); + LuaInterface::pushThing(L, item, env->addThing(item)); lua_pushstring(L, newText.c_str()); bool result = m_interface->callFunction(3); @@ -1647,12 +1659,71 @@ uint32_t CreatureEvent::executeTextEdit(Player* player, Item* item, std::string } else { - std::cout << "[Error - CreatureEvent::executeTextEdit] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeTextEdit] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeReportBug(Player* player, std::string comment) +uint32_t CreatureEvent::executeHouseEdit(Player* player, uint32_t houseId, uint32_t listId, const std::string& text) +{ + //onHouseEdit(cid, houseId, listId, text) + if(m_interface->reserveEnv()) + { + ScriptEnviroment* env = m_interface->getEnv(); + if(m_scripted == EVENT_SCRIPT_BUFFER) + { + env->setRealPos(player->getPosition()); + std::stringstream scriptstream; + scriptstream << "local cid = " << env->addThing(player) << std::endl; + scriptstream << "local house = " << houseId << std::endl; + + scriptstream << "local list = " << listId << std::endl; + scriptstream << "local text = " << text << std::endl; + if(m_scriptData) + scriptstream << *m_scriptData; + + bool result = true; + if(m_interface->loadBuffer(scriptstream.str())) + { + lua_State* L = m_interface->getState(); + result = m_interface->getGlobalBool(L, "_result", true); + } + + m_interface->releaseEnv(); + return result; + } + else + { + #ifdef __DEBUG_LUASCRIPTS__ + char desc[35]; + sprintf(desc, "%s", player->getName().c_str()); + env->setEvent(desc); + #endif + + env->setScriptId(m_scriptId, m_interface); + env->setRealPos(player->getPosition()); + + lua_State* L = m_interface->getState(); + m_interface->pushFunction(m_scriptId); + + lua_pushnumber(L, env->addThing(player)); + lua_pushnumber(L, houseId); + lua_pushnumber(L, listId); + lua_pushstring(L, text.c_str()); + + bool result = m_interface->callFunction(4); + m_interface->releaseEnv(); + return result; + } + } + else + { + std::clog << "[Error - CreatureEvent::executeHouseEdit] Call stack overflow." << std::endl; + return 0; + } +} + +uint32_t CreatureEvent::executeReportBug(Player* player, const std::string& comment) { //onReportBug(cid, comment) if(m_interface->reserveEnv()) @@ -1664,9 +1735,11 @@ uint32_t CreatureEvent::executeReportBug(Player* player, std::string comment) std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(player) << std::endl; - scriptstream << "local comment = " << comment.c_str() << std::endl; + scriptstream << "local comment = " << comment << std::endl; + + if(m_scriptData) + scriptstream << *m_scriptData; - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1682,7 +1755,7 @@ uint32_t CreatureEvent::executeReportBug(Player* player, std::string comment) #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -1701,14 +1774,83 @@ uint32_t CreatureEvent::executeReportBug(Player* player, std::string comment) } else { - std::cout << "[Error - CreatureEvent::executeReportBug] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeReportBug] Call stack overflow." << std::endl; + return 0; + } +} + +uint32_t CreatureEvent::executeReportViolation(Player* player, ReportType_t type, uint8_t reason, + const std::string& name, const std::string& comment, const std::string& translation, uint32_t statementId) +{ + //onReportViolation(cid, type, reason, name, comment, translation, statementId) + if(m_interface->reserveEnv()) + { + ScriptEnviroment* env = m_interface->getEnv(); + if(m_scripted == EVENT_SCRIPT_BUFFER) + { + env->setRealPos(player->getPosition()); + std::stringstream scriptstream; + + scriptstream << "local cid = " << env->addThing(player) << std::endl; + scriptstream << "local type = " << type << std::endl; + scriptstream << "local reason = " << (uint16_t)reason << std::endl; + + scriptstream << "local name = " << name << std::endl; + scriptstream << "local comment = " << comment << std::endl; + scriptstream << "local translation = " << translation << std::endl; + + scriptstream << "local statementId = " << statementId << std::endl; + if(m_scriptData) + scriptstream << *m_scriptData; + + bool result = true; + if(m_interface->loadBuffer(scriptstream.str())) + { + lua_State* L = m_interface->getState(); + result = m_interface->getGlobalBool(L, "_result", true); + } + + m_interface->releaseEnv(); + return result; + } + else + { + #ifdef __DEBUG_LUASCRIPTS__ + char desc[35]; + sprintf(desc, "%s", player->getName().c_str()); + env->setEvent(desc); + #endif + + env->setScriptId(m_scriptId, m_interface); + env->setRealPos(player->getPosition()); + + lua_State* L = m_interface->getState(); + m_interface->pushFunction(m_scriptId); + + lua_pushnumber(L, env->addThing(player)); + lua_pushnumber(L, type); + lua_pushnumber(L, reason); + + lua_pushstring(L, name.c_str()); + lua_pushstring(L, comment.c_str()); + lua_pushstring(L, translation.c_str()); + lua_pushnumber(L, statementId); + + bool result = m_interface->callFunction(7); + m_interface->releaseEnv(); + return result; + } + } + else + { + std::clog << "[Error - CreatureEvent::executeReportViolation] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executePush(Player* player, Creature* target) +uint32_t CreatureEvent::executeChannelRequest(Player* player, const std::string& channel, bool isPrivate, bool custom) { - //onPush(cid, target) + //onChannelRequest(cid, channel, custom) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); @@ -1718,9 +1860,133 @@ uint32_t CreatureEvent::executePush(Player* player, Creature* target) std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(player) << std::endl; + if(!isPrivate) + scriptstream << "local channel = " << atoi(channel.c_str()) << std::endl; + else + scriptstream << "local channel = " << channel << std::endl; + + scriptstream << "local custom = " << (custom ? "true" : "false") << std::endl; + if(m_scriptData) + scriptstream << *m_scriptData; + + bool result = true; + if(m_interface->loadBuffer(scriptstream.str())) + { + lua_State* L = m_interface->getState(); + result = m_interface->getGlobalBool(L, "_result", true); + } + + m_interface->releaseEnv(); + return result; + } + else + { + #ifdef __DEBUG_LUASCRIPTS__ + char desc[35]; + sprintf(desc, "%s", player->getName().c_str()); + env->setEvent(desc); + #endif + + env->setScriptId(m_scriptId, m_interface); + env->setRealPos(player->getPosition()); + + lua_State* L = m_interface->getState(); + m_interface->pushFunction(m_scriptId); + + lua_pushnumber(L, env->addThing(player)); + if(!isPrivate) + lua_pushnumber(L, atoi(channel.c_str())); + else + lua_pushstring(L, channel.c_str()); + + lua_pushboolean(L, custom); + bool result = m_interface->callFunction(3); + m_interface->releaseEnv(); + return result; + } + } + else + { + std::clog << "[Error - CreatureEvent::executeChannelRequest] Call stack overflow." << std::endl; + return 0; + } +} + +uint32_t CreatureEvent::executeThankYou(Player* player, uint32_t statementId) +{ + //onThankYou(cid, statementId) + if(m_interface->reserveEnv()) + { + ScriptEnviroment* env = m_interface->getEnv(); + if(m_scripted == EVENT_SCRIPT_BUFFER) + { + env->setRealPos(player->getPosition()); + std::stringstream scriptstream; + + scriptstream << "local cid = " << env->addThing(player) << std::endl; + scriptstream << "local statementId = " << statementId << std::endl; + + if(m_scriptData) + scriptstream << *m_scriptData; + + bool result = true; + if(m_interface->loadBuffer(scriptstream.str())) + { + lua_State* L = m_interface->getState(); + result = m_interface->getGlobalBool(L, "_result", true); + } + + m_interface->releaseEnv(); + return result; + } + else + { + #ifdef __DEBUG_LUASCRIPTS__ + char desc[35]; + sprintf(desc, "%s", player->getName().c_str()); + env->setEvent(desc); + #endif + + env->setScriptId(m_scriptId, m_interface); + env->setRealPos(player->getPosition()); + + lua_State* L = m_interface->getState(); + m_interface->pushFunction(m_scriptId); + + lua_pushnumber(L, env->addThing(player)); + lua_pushnumber(L, statementId); + + bool result = m_interface->callFunction(2); + m_interface->releaseEnv(); + return result; + } + } + else + { + std::clog << "[Error - CreatureEvent::executeThankYou] Call stack overflow." << std::endl; + return 0; + } +} + +uint32_t CreatureEvent::executePush(Player* player, Creature* target, Tile* tile) +{ + //onPush(cid, target, ground, position) + if(m_interface->reserveEnv()) + { + ScriptEnviroment* env = m_interface->getEnv(); + if(m_scripted == EVENT_SCRIPT_BUFFER) + { + env->setRealPos(player->getPosition()); + std::stringstream scriptstream; + scriptstream << "local cid = " << env->addThing(player) << std::endl; + scriptstream << "local target = " << env->addThing(target) << std::endl; + env->streamThing(scriptstream, "ground", tile->ground, env->addThing(tile->ground)); + env->streamPosition(scriptstream, "position", tile->getPosition(), 0); + + if(m_scriptData) + scriptstream << *m_scriptData; - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1736,7 +2002,7 @@ uint32_t CreatureEvent::executePush(Player* player, Creature* target) #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << player->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); @@ -1748,33 +2014,40 @@ uint32_t CreatureEvent::executePush(Player* player, Creature* target) lua_pushnumber(L, env->addThing(player)); lua_pushnumber(L, env->addThing(target)); - bool result = m_interface->callFunction(2); + LuaInterface::pushThing(L, tile->ground, env->addThing(tile->ground)); + LuaInterface::pushPosition(L, tile->getPosition(), 0); + + bool result = m_interface->callFunction(4); m_interface->releaseEnv(); return result; } } else { - std::cout << "[Error - CreatureEvent::executePush] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executePush] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeTarget(Creature* creature, Creature* target) +uint32_t CreatureEvent::executeThrow(Player* player, Item* item, const Position& fromPosition, const Position& toPosition) { - //onTarget(cid, target) + //onThrow(cid, item, fromPosition, toPosition) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { - env->setRealPos(creature->getPosition()); + env->setRealPos(player->getPosition()); std::stringstream scriptstream; + scriptstream << "local cid = " << env->addThing(player) << std::endl; - scriptstream << "local cid = " << env->addThing(creature) << std::endl; - scriptstream << "local target = " << env->addThing(target) << std::endl; + env->streamThing(scriptstream, "item", item, env->addThing(item)); + env->streamPosition(scriptstream, "fromPosition", fromPosition, 0); + env->streamPosition(scriptstream, "toPosition", toPosition, 0); + + if(m_scriptData) + scriptstream << *m_scriptData; - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1789,34 +2062,37 @@ uint32_t CreatureEvent::executeTarget(Creature* creature, Creature* target) { #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; - desc << creature->getName(); - env->setEventDesc(desc.str()); + desc << player->getName(); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); - env->setRealPos(creature->getPosition()); + env->setRealPos(player->getPosition()); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); - lua_pushnumber(L, env->addThing(creature)); - lua_pushnumber(L, env->addThing(target)); + lua_pushnumber(L, env->addThing(player)); + LuaInterface::pushThing(L, item, env->addThing(item)); - bool result = m_interface->callFunction(2); + LuaInterface::pushPosition(L, fromPosition, 0); + LuaInterface::pushPosition(L, toPosition, 0); + + bool result = m_interface->callFunction(4); m_interface->releaseEnv(); return result; } } else { - std::cout << "[Error - CreatureEvent::executeTarget] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executePush] Call stack overflow." << std::endl; return 0; } } -uint32_t CreatureEvent::executeFollow(Creature* creature, Creature* target) +uint32_t CreatureEvent::executeAction(Creature* creature, Creature* target) { - //onFollow(cid, target) + //on[Target/Follow/Attack](cid, target) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); @@ -1828,7 +2104,9 @@ uint32_t CreatureEvent::executeFollow(Creature* creature, Creature* target) scriptstream << "local cid = " << env->addThing(creature) << std::endl; scriptstream << "local target = " << env->addThing(target) << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1844,7 +2122,7 @@ uint32_t CreatureEvent::executeFollow(Creature* creature, Creature* target) #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << creature->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); @@ -1863,7 +2141,7 @@ uint32_t CreatureEvent::executeFollow(Creature* creature, Creature* target) } else { - std::cout << "[Error - CreatureEvent::executeFollow] Call stack overflow." << std::endl; + std::clog << "[Error - CreatureEvent::executeAction] Call stack overflow." << std::endl; return 0; } } diff --git a/creatureevent.h b/creatureevent.h index 21e758b..f1ff4b2 100644 --- a/creatureevent.h +++ b/creatureevent.h @@ -27,8 +27,11 @@ enum CreatureEventType_t CREATURE_EVENT_NONE, CREATURE_EVENT_LOGIN, CREATURE_EVENT_LOGOUT, + CREATURE_EVENT_SPAWN_SINGLE, + CREATURE_EVENT_SPAWN_GLOBAL, CREATURE_EVENT_CHANNEL_JOIN, CREATURE_EVENT_CHANNEL_LEAVE, + CREATURE_EVENT_CHANNEL_REQUEST, CREATURE_EVENT_ADVANCE, CREATURE_EVENT_LOOK, CREATURE_EVENT_DIRECTION, @@ -38,10 +41,14 @@ enum CreatureEventType_t CREATURE_EVENT_TRADE_REQUEST, CREATURE_EVENT_TRADE_ACCEPT, CREATURE_EVENT_TEXTEDIT, + CREATURE_EVENT_HOUSEEDIT, CREATURE_EVENT_REPORTBUG, + CREATURE_EVENT_REPORTVIOLATION, + CREATURE_EVENT_THANKYOU, CREATURE_EVENT_THINK, CREATURE_EVENT_STATSCHANGE, CREATURE_EVENT_COMBAT_AREA, + CREATURE_EVENT_THROW, CREATURE_EVENT_PUSH, CREATURE_EVENT_TARGET, CREATURE_EVENT_FOLLOW, @@ -61,7 +68,9 @@ enum StatsChange_t STATSCHANGE_MANALOSS }; +class Monster; class CreatureEvent; + class CreatureEvents : public BaseEvents { public: @@ -71,8 +80,10 @@ class CreatureEvents : public BaseEvents // global events bool playerLogin(Player* player); bool playerLogout(Player* player, bool forceLogout); + bool monsterSpawn(Monster* monster); - CreatureEvent* getEventByName(const std::string& name, bool forceLoaded = true); + CreatureEvent* getEventByName(const std::string& name); + CreatureEventType_t getType(const std::string& type); protected: virtual std::string getScriptBaseName() const {return "creaturescripts";} @@ -81,11 +92,11 @@ class CreatureEvents : public BaseEvents virtual Event* getEvent(const std::string& nodeName); virtual bool registerEvent(Event* event, xmlNodePtr p, bool override); - virtual LuaScriptInterface& getInterface() {return m_interface;} - LuaScriptInterface m_interface; + virtual LuaInterface& getInterface() {return m_interface;} + LuaInterface m_interface; //creature events - typedef std::map CreatureEventList; + typedef std::vector CreatureEventList; CreatureEventList m_creatureEvents; }; @@ -96,12 +107,13 @@ typedef std::map UsersMap; class CreatureEvent : public Event { public: - CreatureEvent(LuaScriptInterface* _interface); + CreatureEvent(LuaInterface* _interface); + CreatureEvent(const CreatureEvent* copy); virtual ~CreatureEvent() {} virtual bool configureEvent(xmlNodePtr p); - bool isLoaded() const {return m_isLoaded;} + bool isLoaded() const {return m_loaded;} const std::string& getName() const {return m_eventName;} CreatureEventType_t getEventType() const {return m_type;} @@ -109,30 +121,33 @@ class CreatureEvent : public Event void clearEvent(); //scripting - uint32_t executeLogin(Player* player); + uint32_t executePlayer(Player* player); uint32_t executeLogout(Player* player, bool forceLogout); - uint32_t executeChannelJoin(Player* player, uint16_t channelId, UsersMap usersMap); - uint32_t executeChannelLeave(Player* player, uint16_t channelId, UsersMap usersMap); + uint32_t executeSpawn(Monster* monster); + uint32_t executeChannel(Player* player, uint16_t channelId, UsersMap usersMap); + uint32_t executeChannelRequest(Player* player, const std::string& channel, bool isPrivate, bool custom); uint32_t executeAdvance(Player* player, skills_t skill, uint32_t oldLevel, uint32_t newLevel); uint32_t executeLook(Player* player, Thing* thing, const Position& position, int16_t stackpos, int32_t lookDistance); - uint32_t executeMailSend(Player* player, Player* receiver, Item* item, bool openBox); - uint32_t executeMailReceive(Player* player, Player* sender, Item* item, bool openBox); + uint32_t executeMail(Player* player, Player* target, Item* item, bool openBox); uint32_t executeTradeRequest(Player* player, Player* target, Item* item); uint32_t executeTradeAccept(Player* player, Player* target, Item* item, Item* targetItem); - uint32_t executeTextEdit(Player* player, Item* item, std::string newText); - uint32_t executeReportBug(Player* player, std::string comment); + uint32_t executeTextEdit(Player* player, Item* item, const std::string& newText); + uint32_t executeHouseEdit(Player* player, uint32_t houseId, uint32_t listId, const std::string& text); + uint32_t executeReportBug(Player* player, const std::string& comment); + uint32_t executeReportViolation(Player* player, ReportType_t type, uint8_t reason, const std::string& name, + const std::string& comment, const std::string& translation, uint32_t statementId); + uint32_t executeThankYou(Player* player, uint32_t statementId); uint32_t executeThink(Creature* creature, uint32_t interval); uint32_t executeDirection(Creature* creature, Direction old, Direction current); uint32_t executeOutfit(Creature* creature, const Outfit_t& old, const Outfit_t& current); uint32_t executeStatsChange(Creature* creature, Creature* attacker, StatsChange_t type, CombatType_t combat, int32_t value); uint32_t executeCombatArea(Creature* creature, Tile* tile, bool isAggressive); - uint32_t executePush(Player* player, Creature* target); - uint32_t executeTarget(Creature* creature, Creature* target); - uint32_t executeFollow(Creature* creature, Creature* target); - uint32_t executeCombat(Creature* creature, Creature* target); - uint32_t executeAttack(Creature* creature, Creature* target); + uint32_t executePush(Player* player, Creature* target, Tile* tile); + uint32_t executeThrow(Player* player, Item* item, const Position& fromPosition, const Position& toPosition); + uint32_t executeCombat(Creature* creature, Creature* target, bool aggressive); + uint32_t executeAction(Creature* creature, Creature* target); uint32_t executeCast(Creature* creature, Creature* target = NULL); - uint32_t executeKill(Creature* creature, Creature* target, bool lastHit); + uint32_t executeKill(Creature* creature, Creature* target, const DeathEntry& entry); uint32_t executeDeath(Creature* creature, Item* corpse, DeathList deathList); uint32_t executePrepareDeath(Creature* creature, DeathList deathList); // @@ -141,7 +156,7 @@ class CreatureEvent : public Event virtual std::string getScriptEventName() const; virtual std::string getScriptEventParams() const; - bool m_isLoaded; + bool m_loaded; std::string m_eventName; CreatureEventType_t m_type; }; diff --git a/cylinder.h b/cylinder.h index 65b65e8..4edb615 100644 --- a/cylinder.h +++ b/cylinder.h @@ -23,18 +23,19 @@ class Item; class Creature; -enum cylinderflags_t +enum CylinderFlags_t { FLAG_NOLIMIT = 1, //Bypass limits like capacity/container limits, blocking items/creatures etc. - FLAG_IGNOREBLOCKITEM = 2, //Bypass moveable blocking item checks + FLAG_IGNOREBLOCKITEM = 2, //Bypass movable blocking item checks FLAG_IGNOREBLOCKCREATURE = 4, //Bypass creature checks FLAG_CHILDISOWNER = 8, //Used by containers to query capacity of the carrier (player) FLAG_PATHFINDING = 16, //An additional check is done for floor changing/teleport items - FLAG_IGNOREFIELDDAMAGE = 32, //Bypass field damage checks - FLAG_IGNORENOTMOVEABLE = 64 //Bypass check for movability + FLAG_IGNOREFIELDDAMAGE = 32, //Bypass field damage hecks + FLAG_IGNORENOTMOVABLE = 64, //Bypass check for movability + FLAG_IGNOREAUTOSTACK = 128 //__queryDestination will not try to stack items together }; -enum cylinderlink_t +enum CylinderLink_t { LINK_OWNER, LINK_PARENT, @@ -69,7 +70,7 @@ class Cylinder * \returns ReturnValue holds the return value */ virtual ReturnValue __queryAdd(int32_t index, const Thing* Item, uint32_t count, - uint32_t flags) const = 0; + uint32_t flags, Creature* actor = NULL) const = 0; /** * Query the cylinder how much it can accept @@ -91,7 +92,7 @@ class Cylinder * \param flags optional flags to modifiy the default behaviour * \returns ReturnValue holds the return value */ - virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, uint32_t flags) const = 0; + virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, uint32_t flags, Creature* actor = NULL) const = 0; /** * Query the destination cylinder @@ -151,7 +152,7 @@ class Cylinder * \param link holds the relation the object has to the cylinder */ virtual void postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link = LINK_OWNER) = 0; + int32_t index, CylinderLink_t link = LINK_OWNER) = 0; /** * Is sent after an operation (move/remove) to update internal values @@ -162,14 +163,14 @@ class Cylinder * \param link holds the relation the object has to the cylinder */ virtual void postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link = LINK_OWNER) = 0; + int32_t index, bool isCompleteRemoval, CylinderLink_t link = LINK_OWNER) = 0; /** * Gets the index of an object * \param thing the object to get the index value from * \returns the index of the object, returns -1 if not found */ - virtual int32_t __getIndexOfThing(const Thing* thing) const {return -1;} + virtual int32_t __getIndexOfThing(const Thing*) const {return -1;} /** * Returns the first index @@ -187,7 +188,7 @@ class Cylinder * Gets the object based on index * \returns the object, returns NULL if not found */ - virtual Thing* __getThing(uint32_t index) const {return NULL;} + virtual Thing* __getThing(uint32_t) const {return NULL;} /** * Get the amount of items of a certain type @@ -196,8 +197,7 @@ class Cylinder * \param itemCount if set to true it will only count items and not other subtypes like charges * \param returns the amount of items of the asked item type */ - virtual uint32_t __getItemTypeCount(uint16_t itemId, int32_t subType = -1, - bool itemCount = true) const {return 0;} + virtual uint32_t __getItemTypeCount(uint16_t, int32_t = -1) const {return 0;} /** * Get the amount of items of a all types @@ -206,20 +206,20 @@ class Cylinder * \param returns a map mapping item id to count (same as first argument) */ virtual std::map& __getAllItemTypeCount(std::map& countMap, bool itemCount = true) const {return countMap;} + uint32_t>& countMap) const {return countMap;} /** * Adds an object to the cylinder without sending to the client(s) * \param thing is the object to add */ - virtual void __internalAddThing(Thing* thing) {} + virtual void __internalAddThing(Thing*) {} /** * Adds an object to the cylinder without sending to the client(s) * \param thing is the object to add * \param index points to the destination index (inventory slot/container position) */ - virtual void __internalAddThing(uint32_t index, Thing* thing) {} + virtual void __internalAddThing(uint32_t, Thing*) {} virtual void __startDecaying() {} }; @@ -241,25 +241,24 @@ class VirtualCylinder : public Cylinder virtual Creature* getCreature() {return NULL;} virtual const Creature* getCreature() const {return NULL;} - virtual ReturnValue __queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const {return RET_NOTPOSSIBLE;} - virtual ReturnValue __queryMaxCount(int32_t index, const Thing* thing, uint32_t count, - uint32_t& maxQueryCount, uint32_t flags) const {return RET_NOTPOSSIBLE;} - virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, - uint32_t flags) const {return (thing->getParent() == this ? RET_NOERROR : RET_NOTPOSSIBLE);} - virtual Cylinder* __queryDestination(int32_t& index, const Thing* thing, Item** destItem, - uint32_t& flags) {return NULL;} + virtual ReturnValue __queryAdd(int32_t, const Thing*, uint32_t, + uint32_t, Creature* = NULL) const {return RET_NOTPOSSIBLE;} + virtual ReturnValue __queryMaxCount(int32_t, const Thing*, uint32_t, + uint32_t&, uint32_t) const {return RET_NOTPOSSIBLE;} + virtual ReturnValue __queryRemove(const Thing* thing, uint32_t, + uint32_t, Creature* = NULL) const {return (thing->getParent() == this ? RET_NOERROR : RET_NOTPOSSIBLE);} + virtual Cylinder* __queryDestination(int32_t&, const Thing*, Item**, + uint32_t&) {return NULL;} - virtual void __addThing(Creature* actor, Thing* thing) {} - virtual void __addThing(Creature* actor, int32_t index, Thing* thing) {} - virtual void __updateThing(Thing* thing, uint16_t itemId, uint32_t count) {} - virtual void __replaceThing(uint32_t index, Thing* thing) {} - virtual void __removeThing(Thing* thing, uint32_t count) {} + virtual void __addThing(Creature*, Thing*) {} + virtual void __addThing(Creature*, int32_t, Thing*) {} + virtual void __updateThing(Thing*, uint16_t, uint32_t) {} + virtual void __replaceThing(uint32_t, Thing*) {} + virtual void __removeThing(Thing*, uint32_t) {} - virtual void postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link = LINK_OWNER) {} - virtual void postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, bool isCompleteRemoval, - cylinderlink_t link = LINK_OWNER) {} + virtual void postAddNotification(Creature*, Thing*, const Cylinder*, + int32_t, CylinderLink_t/* link = LINK_OWNER*/) {} + virtual void postRemoveNotification(Creature*, Thing*, const Cylinder*, + int32_t, bool, CylinderLink_t/* link = LINK_OWNER*/) {} }; #endif diff --git a/data/XML/admin.xml b/data/XML/admin.xml deleted file mode 100644 index ec9d3fc..0000000 --- a/data/XML/admin.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/data/XML/channels.xml b/data/XML/channels.xml index 8ddfdd2..fe50b91 100644 --- a/data/XML/channels.xml +++ b/data/XML/channels.xml @@ -3,23 +3,21 @@ - - - - - + + + + - + - - - + + + diff --git a/data/XML/groups.xml b/data/XML/groups.xml index ddb88c4..1a3b073 100644 --- a/data/XML/groups.xml +++ b/data/XML/groups.xml @@ -1,9 +1,9 @@ - - - - - + + + + + diff --git a/data/XML/mounts.xml b/data/XML/mounts.xml new file mode 100644 index 0000000..5d23417 --- /dev/null +++ b/data/XML/mounts.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/XML/outfits.xml b/data/XML/outfits.xml index 6b5ac27..24d5ad5 100644 --- a/data/XML/outfits.xml +++ b/data/XML/outfits.xml @@ -105,61 +105,91 @@ - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + diff --git a/data/XML/servers.xml b/data/XML/servers.xml index 615d058..22926ec 100644 --- a/data/XML/servers.xml +++ b/data/XML/servers.xml @@ -1,4 +1,4 @@ - + diff --git a/data/XML/vocations.xml b/data/XML/vocations.xml index f00b531..0ee2890 100644 --- a/data/XML/vocations.xml +++ b/data/XML/vocations.xml @@ -1,55 +1,55 @@ - + - + - + - + - + - + - + - + - + + + @@ -26,8 +29,8 @@ - - + + @@ -63,108 +66,50 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -172,20 +117,16 @@ - - - - - + + - - + @@ -210,78 +151,71 @@ - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + diff --git a/data/actions/lib/actions.lua b/data/actions/lib/actions.lua index 41491df..57c7c1f 100644 --- a/data/actions/lib/actions.lua +++ b/data/actions/lib/actions.lua @@ -1,8 +1,64 @@ +SPOTS = { 384, 418, 8278, 8592 } +ROPABLE = { 294, 369, 370, 383, 392, 408, 409, 427, 428, 430, 462, 469, 470, 482, 484, 485, 489, 924, 3135, 3136, 7933, 7938, 8170, 8286, 8285, + 8284, 8281, 8280, 8279, 8277, 8276, 8323, 8380, 8567, 8585, 8596, 8595, 8249, 8250, 8251, 8252, 8253, 8254, 8255, 8256, 8972, 9606, 9625 } + +HOLES = { 468, 481, 483, 7932, 8579 } +SAND_HOLES = { [9059] = 489, [8568] = 8567 } +SAND = { 231, 9059 } + +JUNGLE_GRASS = { 2782, 3985 } +SPIDER_WEB = { 7538, 7539 } +BAMBOO_FENCE = { 3798, 3799 } +WILD_GROWTH = { 1499, 11099 } + +PUMPKIN = 2683 +PUMPKIN_HEAD = 2096 + +POOL = 2016 + +SPECIAL_FOODS = { + [9992] = "Gulp.", [9993] = "Chomp.", [9994] = "Chomp.", [9995] = "Chomp.", [9997] = "Yum.", + [9998] = "Munch.", [9999] = "Chomp.", [10000] = "Mmmm.", [10001] = "Smack.", [12540] = "Yum.", + [12542] = "Gulp.", [12543] = "?", [12544] = "Slurp!" +} + +DOORS = { + [1209] = 1211, [1212] = 1214, [1231] = 1233, [1234] = 1236, [1249] = 1251, [1252] = 1254, [3535] = 3537, [3544] = 3546, [4913] = 4915, [4916] = 4918, + [5098] = 5100, [5107] = 5109, [5116] = 5118, [5125] = 5127, [5134] = 5136, [5137] = 5139, [5140] = 5142, [5143] = 5145, [5278] = 5280, [5281] = 5283, + [5732] = 5734, [5735] = 5737, [6192] = 6194, [6195] = 6197, [6249] = 6251, [6252] = 6254, [6891] = 6893, [6900] = 6902, [7033] = 7035, [7042] = 7044, + [8541] = 8543, [8544] = 8546, [9165] = 9167, [9168] = 9170, [9267] = 9269, [9270] = 9272, [10268] = 10270, [10271] = 10273, [10468] = 10470, + [10477] = 10479, [10775] = 10777, [10784] = 10786, [12092] = 12094, [12099] = 12101, [12188] = 12190, [12197] = 12199 +} + function destroyItem(cid, itemEx, toPosition) if(itemEx.uid <= 65535 or itemEx.actionid > 0) then return false end + if(isInArray(SPIDER_WEB, itemEx.itemid)) then + if math.random(3) == 1 then + doTransformItem(itemEx.uid, (itemEx.itemid + 6)) + doDecayItem(itemEx.uid) + end + + doSendMagicEffect(toPosition, CONST_ME_POFF) + return true + end + + if(isInArray(BAMBOO_FENCE, itemEx.itemid)) then + if math.random(3) == 1 then + if(itemEx.itemid == BAMBOO_FENCE[1]) then + doTransformItem(itemEx.uid, (itemEx.itemid + 161)) + elseif(itemEx.itemid == BAMBOO_FENCE[2]) then + doTransformItem(itemEx.uid, (itemEx.itemid + 159)) + end + doDecayItem(itemEx.uid) + end + + doSendMagicEffect(toPosition, CONST_ME_POFF) + return true + end + if not(isInArray({1770, 2098, 1774, 2064, 2094, 2095, 1619, 2602, 3805, 3806}, itemEx.itemid) or (itemEx.itemid >= 1724 and itemEx.itemid <= 1741) or (itemEx.itemid >= 2581 and itemEx.itemid <= 2588) or @@ -51,3 +107,152 @@ function destroyItem(cid, itemEx, toPosition) doSendMagicEffect(toPosition, CONST_ME_POFF) return true end + +TOOLS = {} +TOOLS.ROPE = function(cid, item, fromPosition, itemEx, toPosition) + if(toPosition.x == CONTAINER_POSITION) then + doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) + return true + end + + toPosition.stackpos = STACKPOS_GROUND + errors(false) + local ground = getThingFromPos(toPosition) + errors(true) + if(isInArray(SPOTS, ground.itemid)) then + doTeleportThing(cid, {x = toPosition.x, y = toPosition.y + 1, z = toPosition.z - 1}, false) + return true + elseif(isInArray(ROPABLE, itemEx.itemid)) then + local canOnlyRopePlayers = getBooleanFromString(getConfigValue('canOnlyRopePlayers')) + local hole = getThingFromPos({x = toPosition.x, y = toPosition.y, z = toPosition.z + 1, stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE}) + if(canOnlyRopePlayers) then + if(isPlayer(hole.uid) and (not isPlayerGhost(hole.uid) or getPlayerGhostAccess(cid) >= getPlayerGhostAccess(hole.uid))) then + doTeleportThing(hole.uid, {x = toPosition.x, y = toPosition.y + 1, z = toPosition.z}, false) + else + doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) + end + else + if(hole.itemid > 0) then + doTeleportThing(hole.uid, {x = toPosition.x, y = toPosition.y + 1, z = toPosition.z}, false) + else + doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) + end + end + + return true + end + + return false +end + +TOOLS.PICK = function(cid, item, fromPosition, itemEx, toPosition) + errors(false) + local ground = getThingFromPos({x = toPosition.x, y = toPosition.y, z = toPosition.z + 1, stackpos = STACKPOS_GROUND}) + errors(true) + if(isInArray(SPOTS, ground.itemid) and isInArray({354, 355}, itemEx.itemid)) then + doTransformItem(itemEx.uid, 392) + doDecayItem(itemEx.uid) + + doSendMagicEffect(toPosition, CONST_ME_POFF) + return true + end + + if(itemEx.itemid == 7200) then + doTransformItem(itemEx.uid, 7236) + doSendMagicEffect(toPosition, CONST_ME_BLOCKHIT) + return true + end + + return false +end + +TOOLS.MACHETE = function(cid, item, fromPosition, itemEx, toPosition, destroy) + if(isInArray(JUNGLE_GRASS, itemEx.itemid)) then + doTransformItem(itemEx.uid, itemEx.itemid - 1) + doDecayItem(itemEx.uid) + return true + end + + if(isInArray(SPIDER_WEB, itemEx.itemid)) then + if math.random(3) == 1 then + doTransformItem(itemEx.uid, (itemEx.itemid + 6)) + doDecayItem(itemEx.uid) + end + doSendMagicEffect(toPosition, CONST_ME_POFF) + return true + end + + if(isInArray(BAMBOO_FENCE, itemEx.itemid)) then + if math.random(3) == 1 then + if(itemEx.itemid == BAMBOO_FENCE[1]) then + doTransformItem(itemEx.uid, (itemEx.itemid + 161)) + elseif(itemEx.itemid == BAMBOO_FENCE[2]) then + doTransformItem(itemEx.uid, (itemEx.itemid + 159)) + end + doDecayItem(itemEx.uid) + end + doSendMagicEffect(toPosition, CONST_ME_POFF) + return true + end + + if(isInArray(WILD_GROWTH, itemEx.itemid)) then + doSendMagicEffect(toPosition, CONST_ME_POFF) + doRemoveItem(itemEx.uid) + return true + end + + return destroy and destroyItem(cid, itemEx, toPosition) or false +end + +TOOLS.SHOVEL = function(cid, item, fromPosition, itemEx, toPosition) + if(isInArray(HOLES, itemEx.itemid)) then + if(itemEx.itemid ~= 8579) then + itemEx.itemid = itemEx.itemid + 1 + else + itemEx.itemid = 8585 + end + + doTransformItem(itemEx.uid, itemEx.itemid) + doDecayItem(itemEx.uid) + return true + elseif(SAND_HOLES[itemEx.itemid] ~= nil) then + doSendMagicEffect(toPosition, CONST_ME_POFF) + doTransformItem(itemEx.uid, SAND_HOLES[itemEx.itemid]) + + doDecayItem(itemEx.uid) + return true + elseif(isInArray(SAND,itemEx.itemid) and not isRookie(cid)) then + local rand = math.random(1, 100) + if(rand >= 1 and rand <= 5) then + doCreateItem(ITEM_SCARAB_COIN, 1, toPosition) + elseif(rand > 85) then + doCreateMonster("Scarab", toPosition, false) + end + + doSendMagicEffect(toPosition, CONST_ME_POFF) + return true + end + + return false +end + +TOOLS.SCYTHE = function(cid, item, fromPosition, itemEx, toPosition, destroy) + if(itemEx.itemid == 2739) then + doTransformItem(itemEx.uid, 2737) + doCreateItem(2694, 1, toPosition) + + doDecayItem(itemEx.uid) + return true + end + + return destroy and destroyItem(cid, itemEx, toPosition) or false +end + +TOOLS.KNIFE = function(cid, item, fromPosition, itemEx, toPosition) + if(itemEx.itemid ~= PUMPKIN) then + return false + end + + doTransformItem(itemEx.uid, PUMPKIN_HEAD) + return true +end \ No newline at end of file diff --git a/data/actions/scripts/default.lua b/data/actions/scripts/default.lua new file mode 100644 index 0000000..dbfc3a5 --- /dev/null +++ b/data/actions/scripts/default.lua @@ -0,0 +1,8 @@ +function onUse(cid, item, fromPosition, itemEx, toPosition) + local item = getItemInfo(item.itemid) + if(item.weaponType == WEAPON_SWORD or item.weaponType == WEAPON_CLUB or item.weaponType == WEAPON_AXE) then + return destroyItem(cid, itemEx, toPosition) + end + + return false +end \ No newline at end of file diff --git a/data/actions/scripts/foods/blessed_steak.lua b/data/actions/scripts/foods/blessed_steak.lua new file mode 100644 index 0000000..8274e33 --- /dev/null +++ b/data/actions/scripts/foods/blessed_steak.lua @@ -0,0 +1,12 @@ +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + doCreatureAddMana(cid, getCreatureMaxMana(cid) - getCreatureMana(cid)) + doRemoveItem(item.uid, 1) + + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/carrot_cake.lua b/data/actions/scripts/foods/carrot_cake.lua new file mode 100644 index 0000000..cd98994 --- /dev/null +++ b/data/actions/scripts/foods/carrot_cake.lua @@ -0,0 +1,18 @@ +local condition = createConditionObject(CONDITION_ATTRIBUTES) +setConditionParam(condition, CONDITION_PARAM_TICKS, 60 * 60 * 1000) +setConditionParam(condition, CONDITION_PARAM_SKILL_DISTANCE, 10) + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + if(not doAddCondition(cid, condition)) then + return true + end + + doRemoveItem(item.uid, 1) + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/coconut_shrimp_bake.lua b/data/actions/scripts/foods/coconut_shrimp_bake.lua new file mode 100644 index 0000000..9b4af6c --- /dev/null +++ b/data/actions/scripts/foods/coconut_shrimp_bake.lua @@ -0,0 +1,26 @@ +local HOTD = {5461, 12541} + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + local helmet = getPlayerSlotItem(cid, CONST_SLOT_HEAD) + if(not isInArray(HOTD, helmet.itemid) or not isUnderWater(cid)) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_SMALL, "You should only eat this dish when wearing a helmet of the deep and walking underwater.") + return true + end + + local itemInfo = getItemInfo(HOTD[2]) + if(helmet.itemid ~= HOTD[2]) then + doTransformItem(helmet.uid, HOTD[2]) + doChangeSpeed(cid, itemInfo.abilities.speed) + end + + doItemSetAttribute(helmet.uid, "duration", itemInfo.decayTime * 1000) + doRemoveItem(item.uid, 1) + + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/demonic_candy_ball.lua b/data/actions/scripts/foods/demonic_candy_ball.lua new file mode 100644 index 0000000..572bad6 --- /dev/null +++ b/data/actions/scripts/foods/demonic_candy_ball.lua @@ -0,0 +1,51 @@ +local condition = {} + +local conditions = { + CONDITION_ATTRIBUTES, + CONDITION_HASTE, + CONDITION_LIGHT, + CONDITION_INVISIBLE +} + +local attributes = { + CONDITION_PARAM_SKILL_SHIELD, + CONDITION_PARAM_SKILL_DISTANCE, + CONDITION_PARAM_SKILL_MELEE, + CONDITION_PARAM_STAT_MAGICLEVEL +} + +condition[CONDITION_HASTE] = createConditionObject(CONDITION_HASTE) +setConditionParam(condition[CONDITION_HASTE], CONDITION_PARAM_TICKS, 60 * 60 * 1000) +setConditionParam(condition[CONDITION_HASTE], CONDITION_PARAM_SUBID, 2) +setConditionParam(condition[CONDITION_HASTE], CONDITION_PARAM_SPEED, 60) + +condition[CONDITION_ATTRIBUTES] = createConditionObject(CONDITION_ATTRIBUTES) +setConditionParam(condition[CONDITION_ATTRIBUTES], CONDITION_PARAM_TICKS, 60 * 60 * 1000) + +condition[CONDITION_LIGHT] = createConditionObject(CONDITION_LIGHT) +setConditionParam(condition[CONDITION_LIGHT], CONDITION_PARAM_LIGHT_LEVEL, 9) +setConditionParam(condition[CONDITION_LIGHT], CONDITION_PARAM_LIGHT_COLOR, 215) +setConditionParam(condition[CONDITION_LIGHT], CONDITION_PARAM_TICKS, 60 * 60 * 1000) + +condition[CONDITION_INVISIBLE] = createConditionObject(CONDITION_INVISIBLE) +setConditionParam(condition[CONDITION_INVISIBLE], CONDITION_PARAM_TICKS, 10 * 60 * 1000) + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + local random_condition = conditions[math.random(#conditions)] + if(random_condition == CONDITION_ATTRIBUTES) then + setConditionParam(condition[CONDITION_ATTRIBUTES], attributes[math.random(#attributes)], math.random(3,15)) + end + + if(not doAddCondition(cid, condition[random_condition])) then + return true + end + + doRemoveItem(item.uid, 1) + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/filled_jalapeno_peppers.lua b/data/actions/scripts/foods/filled_jalapeno_peppers.lua new file mode 100644 index 0000000..5a540c6 --- /dev/null +++ b/data/actions/scripts/foods/filled_jalapeno_peppers.lua @@ -0,0 +1,18 @@ +local condition = createConditionObject(CONDITION_HASTE) +setConditionParam(condition, CONDITION_PARAM_TICKS, 60 * 60 * 1000) +setConditionParam(condition, CONDITION_PARAM_SPEED, 40) + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + if(not doAddCondition(cid, condition)) then + return true + end + + doRemoveItem(item.uid, 1) + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/food.lua b/data/actions/scripts/foods/food.lua new file mode 100644 index 0000000..8ef4ee1 --- /dev/null +++ b/data/actions/scripts/foods/food.lua @@ -0,0 +1,56 @@ +local FOODS, MAX_FOOD = { + [2328] = {84, "Gulp."}, [2362] = {48, "Yum."}, [2666] = {180, "Munch."}, [2667] = {144, "Munch."}, + [2668] = {120, "Mmmm."}, [2669] = {204, "Munch."}, [2670] = {48, "Gulp."}, [2671] = {360, "Chomp."}, + [2672] = {720, "Chomp."}, [2673] = {60, "Yum."}, [2674] = {72, "Yum."}, [2675] = {156, "Yum."}, + [2676] = {96, "Yum."}, [2677] = {12, "Yum."}, [2678] = {216, "Slurp."}, [2679] = {12, "Yum."}, + [2680] = {24, "Yum."}, [2681] = {108, "Yum."}, [2682] = {240, "Yum."}, [2683] = {204, "Munch."}, + [2684] = {60, "Crunch."}, [2685] = {72, "Munch."}, [2686] = {108, "Crunch."}, [2687] = {24, "Crunch."}, + [2688] = {24, "Mmmm."}, [2689] = {120, "Crunch."}, [2690] = {72, "Crunch."}, [2691] = {96, "Crunch."}, + [2695] = {72, "Gulp."}, [2696] = {108, "Smack."}, [8112] = {108, "Urgh."}, [2769] = {60, "Crunch."}, [2787] = {108, "Crunch."}, + [2788] = {48, "Munch."}, [2789] = {264, "Munch."}, [2790] = {360, "Crunch."}, [2791] = {108, "Crunch."}, + [2792] = {72, "Crunch."}, [2793] = {144, "Crunch."}, [2794] = {36, "Crunch."}, [2795] = {432, "Crunch."}, + [2796] = {300, "Crunch."}, [5097] = {48, "Yum."}, [5678] = {96, "Gulp."}, [6125] = {96, "Mmmm."}, + [6278] = {120, "Mmmm."}, [6279] = {180, "Mmmm."}, [6393] = {144, "Mmmm."}, [6394] = {180, "Mmmm."}, + [6501] = {240, "Mmmm."}, [6541] = {72, "Gulp."}, [6542] = {72, "Gulp."}, [6543] = {72, "Gulp."}, + [6544] = {72, "Gulp."}, [6545] = {72, "Gulp."}, [6569] = {12, "Mmmm."}, [6574] = {60, "Mmmm."}, + [7158] = {300, "Munch."}, [7159] = {180, "Munch."}, [7245] = {84, "Munch."}, [7372] = {0, "Slurp."}, + [7373] = {0, "Slurp."}, [7374] = {0, "Slurp."}, [7375] = {0, "Slurp."}, [7376] = {0, "Slurp."}, + [7377] = {0, "Slurp."}, [7963] = {720, "Munch."}, [8838] = {120, "Gulp."}, [8839] = {60, "Yum."}, + [8840] = {12, "Yum."}, [8841] = {12, "Urgh."}, [8842] = {84, "Munch."}, [8843] = {60, "Crunch."}, + [8844] = {12, "Gulp."}, [8845] = {60, "Munch."}, [8847] = {132, "Yum."}, [9005] = {88, "Slurp."}, + [9114] = {60, "Crunch."}, [9996] = {0, "Slurp."}, [10454] = {0, "Your head begins to feel better."}, + [11246] = {310, "Yum."}, [11429] = {150, "Mmmm."}, [12415] = {360, "Yum."}, [12416] = {130, "Munch."}, + [12417] = {60, "Crunch."}, [12418] = {80, "Crunch."}, [12637] = {510, "Gulp."}, [12638] = {260, "Yum."}, + [12639] = {18, "Munch."} +}, 1200 + +function onUse(cid, item, fromPosition, itemEx, toPosition) + if(item.itemid == 6280) then + if(fromPosition.x == CONTAINER_POSITION) then + fromPosition = getThingPosition(cid) + end + + doCreatureSay(cid, getPlayerName(cid) .. " blew out the candle.", TALKTYPE_MONSTER) + doTransformItem(item.uid, item.itemid - 1) + + doSendMagicEffect(fromPosition, CONST_ME_POFF) + return true + end + + local food = FOODS[item.itemid] + if(food == nil) then + return false + end + + local size = food[1] + if(getPlayerFood(cid) + size > MAX_FOOD) then + doPlayerSendCancel(cid, "You are full.") + return true + end + + doPlayerFeed(cid, size) + doRemoveItem(item.uid, 1) + + doCreatureSay(cid, food[2], TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/fried_tropical_terrorbird.lua b/data/actions/scripts/foods/fried_tropical_terrorbird.lua new file mode 100644 index 0000000..03d04d8 --- /dev/null +++ b/data/actions/scripts/foods/fried_tropical_terrorbird.lua @@ -0,0 +1,18 @@ +local condition = createConditionObject(CONDITION_ATTRIBUTES) +setConditionParam(condition, CONDITION_PARAM_TICKS, 60 * 60 * 1000) +setConditionParam(condition, CONDITION_PARAM_STAT_MAGICPOINTS, 5) + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + if(not doAddCondition(cid, condition)) then + return true + end + + doRemoveItem(item.uid, 1) + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/hydra_tongue_salad.lua b/data/actions/scripts/foods/hydra_tongue_salad.lua new file mode 100644 index 0000000..71a5429 --- /dev/null +++ b/data/actions/scripts/foods/hydra_tongue_salad.lua @@ -0,0 +1,22 @@ +local conditions = { + CONDITION_POISON, CONDITION_FIRE, CONDITION_ENERGY, + CONDITION_PARALYZE, CONDITION_DRUNK, CONDITION_DROWN, + CONDITION_FREEZING, CONDITION_DAZZLED, CONDITION_CURSED +} + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + for _, condition in ipairs(conditions) do + if(getCreatureCondition(cid, condition)) then + doRemoveCondition(cid, condition) + end + end + + doRemoveItem(item.uid, 1) + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/northern_fishburger.lua b/data/actions/scripts/foods/northern_fishburger.lua new file mode 100644 index 0000000..d6f6f91 --- /dev/null +++ b/data/actions/scripts/foods/northern_fishburger.lua @@ -0,0 +1,18 @@ +local condition = createConditionObject(CONDITION_ATTRIBUTES) +setConditionParam(condition, CONDITION_PARAM_TICKS, 60 * 60 * 1000) +setConditionParam(condition, CONDITION_PARAM_SKILL_FISHING, 50) + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + if(not doAddCondition(cid, condition)) then + return true + end + + doRemoveItem(item.uid, 1) + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/pot_of_blackjack.lua b/data/actions/scripts/foods/pot_of_blackjack.lua new file mode 100644 index 0000000..4d37d47 --- /dev/null +++ b/data/actions/scripts/foods/pot_of_blackjack.lua @@ -0,0 +1,39 @@ +local config = { + MSG_EAT = "You take a gulp from the large bowl, but there's still some blackjack in it.", + MSG_GONE = "You take the last gulp from the large bowl. No leftovers!" +} + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + local addHealth = getCreatureMaxHealth(cid) - getCreatureHealth(cid) + + if(item.actionid < 3500) then + doSetItemActionId(item.uid, 3500) + if(addHealth > 0) then + doCreatureAddHealth(cid, addHealth) + end + doPlayerSendTextMessage(cid, MESSAGE_STATUS_DEFAULT, config.MSG_EAT) + + elseif(item.actionid >= 3500 and item.actionid <= 3501) then + doSetItemActionId(item.uid, item.actionid + 1) + if(addHealth > 0) then + doCreatureAddHealth(cid, addHealth) + end + doPlayerSendTextMessage(cid, MESSAGE_STATUS_DEFAULT, config.MSG_EAT) + + elseif(item.actionid == 3502) then + doSetItemActionId(item.uid, item.actionid + 1) + if(addHealth > 0) then + doCreatureAddHealth(cid, addHealth) + end + doPlayerSendTextMessage(cid, MESSAGE_STATUS_DEFAULT, config.MSG_GONE) + doRemoveItem(item.uid, 1) + end + + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/roasted_dragon_wings.lua b/data/actions/scripts/foods/roasted_dragon_wings.lua new file mode 100644 index 0000000..135ef6d --- /dev/null +++ b/data/actions/scripts/foods/roasted_dragon_wings.lua @@ -0,0 +1,18 @@ +local condition = createConditionObject(CONDITION_ATTRIBUTES) +setConditionParam(condition, CONDITION_PARAM_TICKS, 60 * 60 * 1000) +setConditionParam(condition, CONDITION_PARAM_SKILL_SHIELD, 10) + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + if(not doAddCondition(cid, condition)) then + return true + end + + doRemoveItem(item.uid, 1) + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/rotworm_stew.lua b/data/actions/scripts/foods/rotworm_stew.lua new file mode 100644 index 0000000..acd7214 --- /dev/null +++ b/data/actions/scripts/foods/rotworm_stew.lua @@ -0,0 +1,12 @@ +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + doCreatureAddHealth(cid, getCreatureMaxHealth(cid) - getCreatureHealth(cid)) + doRemoveItem(item.uid, 1) + + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/sweet_mangonaise_elixir.lua b/data/actions/scripts/foods/sweet_mangonaise_elixir.lua new file mode 100644 index 0000000..acd0e12 --- /dev/null +++ b/data/actions/scripts/foods/sweet_mangonaise_elixir.lua @@ -0,0 +1,38 @@ +local config = { + amount = 10 +} +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + local ring = getPlayerSlotItem(cid, CONST_SLOT_RING) + if(food == nil) then + return false + end + + if(ring.itemid == 0) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_SMALL, "You may want to equip a ring before eating this.") + doSendMagicEffect(fromPosition, CONST_ME_POFF) + return true + end + + if(getItemInfo(ring.itemid).showDuration) then + local capRequired, pFreeCap = (getItemInfo(ring.itemid).weight * config.amount), getPlayerFreeCap(cid) + + if(pFreeCap < capRequired) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_SMALL, "You may want to free some capacity before doing this.") + doSendMagicEffect(fromPosition, CONST_ME_POFF) + return true + end + + for i=1,config.amount do + doPlayerAddItemEx(cid, doCopyItem(ring).uid, true) + end + else + doPlayerSendTextMessage(cid, MESSAGE_STATUS_SMALL, "You may want to equip the correct type of ring before eating this.") + doSendMagicEffect(fromPosition, CONST_ME_POFF) + return true + end + + doRemoveItem(item.uid, 1) + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/foods/veggie_casserole.lua b/data/actions/scripts/foods/veggie_casserole.lua new file mode 100644 index 0000000..8307c26 --- /dev/null +++ b/data/actions/scripts/foods/veggie_casserole.lua @@ -0,0 +1,18 @@ +local condition = createConditionObject(CONDITION_ATTRIBUTES) +setConditionParam(condition, CONDITION_PARAM_TICKS, 60 * 60 * 1000) +setConditionParam(condition, CONDITION_PARAM_SKILL_MELEEPERCENT, 150) + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local food = SPECIAL_FOODS[item.itemid] + if(food == nil) then + return false + end + + if(not doAddCondition(cid, condition)) then + return true + end + + doRemoveItem(item.uid, 1) + doCreatureSay(cid, food, TALKTYPE_MONSTER) + return true +end diff --git a/data/actions/scripts/liquids/antidote_potion.lua b/data/actions/scripts/liquids/antidote_potion.lua index a6349d1..d203757 100644 --- a/data/actions/scripts/liquids/antidote_potion.lua +++ b/data/actions/scripts/liquids/antidote_potion.lua @@ -1,31 +1,77 @@ -local EMPTY_POTION = 7636 +-- NOTE: This needs update acccording to potions.lua! +local config = { + removeOnUse = "no", + usableOnTarget = "yes", -- can be used on target? (fe. healing friend) + range = -1, + realAnimation = "no", -- make text effect visible only for players in range 1x1 + flask = 7636, + splash = 41 +} + +config.removeOnUse = getBooleanFromString(config.removeOnUse) +config.usableOnTarget = getBooleanFromString(config.usableOnTarget) +config.realAnimation = getBooleanFromString(config.realAnimation) local combat = createCombatObject() -setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_HEALING) setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) setCombatParam(combat, COMBAT_PARAM_TARGETCASTERORTOPMOST, true) setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) setCombatParam(combat, COMBAT_PARAM_DISPEL, CONDITION_POISON) -local exhaust = createConditionObject(CONDITION_EXHAUST) -setConditionParam(exhaust, CONDITION_PARAM_TICKS, getConfigInfo('timeBetweenExActions')) - function onUse(cid, item, fromPosition, itemEx, toPosition) - if(not isPlayer(itemEx.uid)) then - return false + if(not isPlayer(itemEx.uid) or (not config.usableOnTarget and cid ~= itemEx.uid)) then + if(config.splash < 1) then + return true + end + + if(toPosition.x == CONTAINER_POSITION) then + toPosition = getThingPosition(item.uid) + end + + doDecayItem(doCreateItem(POOL, config.splash, toPosition)) + doRemoveItem(item.uid, 1) + if(not config.flask or config.removeOnUse) then + return true + end + + if(fromPosition.x ~= CONTAINER_POSITION) then + doCreateItem(config.flask, fromPosition) + else + doPlayerAddItem(cid, config.flask, 1) + end + + return true end - if(hasCondition(cid, CONDITION_EXHAUST_HEAL)) then - doPlayerSendDefaultCancel(cid, RETURNVALUE_YOUAREEXHAUSTED) + if(config.range > 0 and cid ~= itemEx.uid and getDistanceBetween(getCreaturePosition(cid), getCreaturePosition(itemEx.uid)) > config.range) then return true end if(not doCombat(cid, combat, numberToVariant(itemEx.uid))) then - return false + return true + end + + doSendMagicEffect(getThingPosition(itemEx.uid), CONST_ME_MAGIC_BLUE) + if(not config.realAnimation) then + doCreatureSay(itemEx.uid, "Aaaah...", TALKTYPE_ORANGE_1) + else + for i, tid in ipairs(getSpectators(getThingPosition(itemEx.uid), 1, 1)) do + if(isPlayer(tid)) then + doCreatureSay(itemEx.uid, "Aaaah...", TALKTYPE_ORANGE_1, false, tid) + end + end + end + + doRemoveItem(item.uid, 1) + if(not config.flask or config.removeOnUse) then + return true + end + + if(fromPosition.x ~= CONTAINER_POSITION) then + doCreateItem(config.flask, fromPosition) + else + doPlayerAddItem(cid, config.flask, 1) end - doAddCondition(cid, exhaust) - doCreatureSay(itemEx.uid, "Aaaah...", TALKTYPE_ORANGE_1) - doTransformItem(item.uid, EMPTY_POTION) return true end diff --git a/data/actions/scripts/liquids/berserk_potion.lua b/data/actions/scripts/liquids/berserk_potion.lua index 2fa0142..485f422 100644 --- a/data/actions/scripts/liquids/berserk_potion.lua +++ b/data/actions/scripts/liquids/berserk_potion.lua @@ -2,11 +2,12 @@ local condition = createConditionObject(CONDITION_ATTRIBUTES) setConditionParam(condition, CONDITION_PARAM_TICKS, 10 * 60 * 1000) -- 10 minutes setConditionParam(condition, CONDITION_PARAM_SKILL_MELEE, 5) setConditionParam(condition, CONDITION_PARAM_SKILL_SHIELD, -10) +setConditionParam(condition, CONDITION_PARAM_SUBID, 99) function onUse(cid, item, fromPosition, itemEx, toPosition) if(doAddCondition(cid, condition)) then doSendMagicEffect(fromPosition, CONST_ME_MAGIC_RED) - doRemoveItem(item.uid) + doRemoveItem(item.uid, 1) end return true diff --git a/data/actions/scripts/liquids/bullseye_potion.lua b/data/actions/scripts/liquids/bullseye_potion.lua index eff6b1b..b0d2406 100644 --- a/data/actions/scripts/liquids/bullseye_potion.lua +++ b/data/actions/scripts/liquids/bullseye_potion.lua @@ -2,11 +2,12 @@ local condition = createConditionObject(CONDITION_ATTRIBUTES) setConditionParam(condition, CONDITION_PARAM_TICKS, 10 * 60 * 1000) -- 10 minutes setConditionParam(condition, CONDITION_PARAM_SKILL_DISTANCE, 5) setConditionParam(condition, CONDITION_PARAM_SKILL_SHIELD, -10) +setConditionParam(condition, CONDITION_PARAM_SUBID, 99) function onUse(cid, item, fromPosition, itemEx, toPosition) if(doAddCondition(cid, condition)) then doSendMagicEffect(fromPosition, CONST_ME_MAGIC_RED) - doRemoveItem(item.uid) + doRemoveItem(item.uid, 1) end return true diff --git a/data/actions/scripts/liquids/containers.lua b/data/actions/scripts/liquids/containers.lua index 6edd355..9a5a682 100644 --- a/data/actions/scripts/liquids/containers.lua +++ b/data/actions/scripts/liquids/containers.lua @@ -1,21 +1,26 @@ -local DISTILLERY = {5513, 5514, 5469, 5470} local ITEM_RUM_FLASK = 5553 -local ITEM_POOL = 2016 local TYPE_EMPTY = 0 local TYPE_WATER = 1 local TYPE_BLOOD = 2 local TYPE_BEER = 3 local TYPE_SLIME = 4 +local TYPE_LEMONADE = 5 +local TYPE_MILK = 6 local TYPE_MANA_FLUID = 7 local TYPE_LIFE_FLUID = 10 local TYPE_OIL = 11 +local TYPE_URINE = 13 +local TYPE_COCONUT_MILK = 14 local TYPE_WINE = 15 local TYPE_MUD = 19 +local TYPE_FRUIT_JUICE = 21 local TYPE_LAVA = 26 local TYPE_RUM = 27 local TYPE_SWAMP = 28 +local TYPE_TEA = 35 +local distillery = {[5513] = 5469, [5514] = 5470} local oilLamps = {[2046] = 2044} local casks = {[1771] = TYPE_WATER, [1772] = TYPE_BEER, [1773] = TYPE_WINE} local alcoholDrinks = {TYPE_BEER, TYPE_WINE, TYPE_RUM} @@ -32,56 +37,60 @@ setConditionParam(poison, CONDITION_PARAM_STARTVALUE, -5) -- The damage the cond setConditionParam(poison, CONDITION_PARAM_TICKINTERVAL, 4000) -- Delay between damages setConditionParam(poison, CONDITION_PARAM_FORCEUPDATE, true) -- Re-update condition when adding it(ie. min/max value) -local exhaust = createConditionObject(CONDITION_EXHAUST) -setConditionParam(exhaust, CONDITION_PARAM_TICKS, (getConfigInfo('timeBetweenExActions') - 100)) +local burn = createConditionObject(CONDITION_FIRE) +setConditionParam(burn, CONDITION_PARAM_DELAYED, true) -- Condition will delay the first damage from when it's added +setConditionParam(burn, CONDITION_PARAM_MINVALUE, -70) -- Minimum damage the condition can do at total +setConditionParam(burn, CONDITION_PARAM_MAXVALUE, -150) -- Maximum damage +setConditionParam(burn, CONDITION_PARAM_STARTVALUE, -10) -- The damage the condition will do on the first hit +setConditionParam(burn, CONDITION_PARAM_TICKINTERVAL, 10000) -- Delay between damages +setConditionParam(burn, CONDITION_PARAM_FORCEUPDATE, true) -- Re-update condition when adding it(ie. min/max value) function onUse(cid, item, fromPosition, itemEx, toPosition) - if(itemEx.uid == cid) then + if(isPlayer(itemEx.uid)) then if(item.type == TYPE_EMPTY) then doPlayerSendCancel(cid, "It is empty.") return true end if(item.type == TYPE_MANA_FLUID) then - if(hasCondition(cid, CONDITION_EXHAUST_HEAL)) then - doPlayerSendDefaultCancel(cid, RETURNVALUE_YOUAREEXHAUSTED) - return true - end - - if(not doPlayerAddMana(cid, math.random(80, 160))) then + if(not doPlayerAddMana(itemEx.uid, math.random(80, 160))) then return false end - doCreatureSay(cid, "Aaaah...", TALKTYPE_ORANGE_1) + doCreatureSay(itemEx.uid, "Aaaah...", TALKTYPE_MONSTER) doSendMagicEffect(toPosition, CONST_ME_MAGIC_BLUE) - doAddCondition(cid, exhaust) elseif(item.type == TYPE_LIFE_FLUID) then - if(hasCondition(cid, CONDITION_EXHAUST_HEAL)) then - doPlayerSendDefaultCancel(cid, RETURNVALUE_YOUAREEXHAUSTED) - return true - end - - if(not doCreatureAddHealth(cid, math.random(40, 75))) then + if(not doCreatureAddHealth(itemEx.uid, math.random(40, 75))) then return false end - doCreatureSay(cid, "Aaaah...", TALKTYPE_ORANGE_1) + doCreatureSay(itemEx.uid, "Aaaah...", TALKTYPE_MONSTER) doSendMagicEffect(toPosition, CONST_ME_MAGIC_BLUE) - doAddCondition(cid, exhaust) - elseif(isInArray(alcoholDrinks, item.type)) then - if(not doTargetCombatCondition(0, cid, drunk, CONST_ME_NONE)) then - return false - end + elseif(itemEx.uid == cid) then + if(isInArray(alcoholDrinks, item.type)) then + if(not doTargetCombatCondition(0, cid, drunk, CONST_ME_NONE)) then + return false + end - doCreatureSay(cid, "Aaah...", TALKTYPE_ORANGE_1) - elseif(isInArray(poisonDrinks, item.type)) then - if(not doTargetCombatCondition(0, cid, poison, CONST_ME_NONE)) then - return false - end + doCreatureSay(cid, "Aaah...", TALKTYPE_MONSTER) + elseif(isInArray(poisonDrinks, item.type)) then + if(not doTargetCombatCondition(0, cid, poison, CONST_ME_NONE)) then + return false + end + + doCreatureSay(cid, "Urgh!", TALKTYPE_MONSTER) + elseif(item.type == TYPE_LAVA) then + if(not doTargetCombatCondition(0, cid, burn, CONST_ME_NONE)) then + return false + end - doCreatureSay(cid, "Urgh!", TALKTYPE_ORANGE_1) + doCreatureSay(cid, "Urgh!", TALKTYPE_MONSTER) + else + doCreatureSay(cid, "Gulp.", TALKTYPE_MONSTER) + end else - doCreatureSay(cid, "Gulp.", TALKTYPE_ORANGE_1) + doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) + return true end doChangeTypeItem(item.uid, TYPE_EMPTY) @@ -90,14 +99,15 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) if(not isCreature(itemEx.uid)) then if(item.type == TYPE_EMPTY) then - if(item.itemid == ITEM_RUM_FLASK and isInArray(DISTILLERY, itemEx.itemid)) then - if(itemEx.actionid == 100) then - doItemEraseAttribute(itemEx.uid, "description") - doItemEraseAttribute(itemEx.uid, "aid") + if(item.itemid == ITEM_RUM_FLASK) then + local tmp = distillery[itemEx.itemid] + if(tmp ~= nil) then + doTransformItem(itemEx.uid, tmp) doChangeTypeItem(item.uid, TYPE_RUM) else doPlayerSendCancel(cid, "You have to process the bunch into the distillery to get rum.") end + return true end @@ -107,14 +117,13 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) return true end - if(casks[itemEx.itemid] ~= nil) then - doChangeTypeItem(item.uid, casks[itemEx.itemid]) - return true + local tmp = casks[itemEx.itemid] + if(tmp == nil) then + tmp = getFluidSourceType(itemEx.itemid) end - local fluidEx = getFluidSourceType(itemEx.itemid) - if(fluidEx ~= false) then - doChangeTypeItem(item.uid, fluidEx) + if(tmp) then + doChangeTypeItem(item.uid, tmp) return true end @@ -122,18 +131,25 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) return true end - if(item.type == TYPE_OIL and oilLamps[itemEx.itemid] ~= nil) then - doTransformItem(itemEx.uid, oilLamps[itemEx.itemid]) + local tmp = oilLamps[itemEx.itemid] + if(item.type == TYPE_OIL and tmp ~= nil) then + doTransformItem(itemEx.uid, tmp) doChangeTypeItem(item.uid, TYPE_NONE) return true end + if(isItemFluidContainer(itemEx.itemid) and itemEx.type == TYPE_EMPTY) then + doChangeTypeItem(itemEx.uid, itemEx.type) + doChangeTypeItem(item.uid, TYPE_EMPTY) + return true + end + if(hasProperty(itemEx.uid, CONST_PROP_BLOCKSOLID)) then return false end end - doDecayItem(doCreateItem(ITEM_POOL, item.type, toPosition)) + doDecayItem(doCreateItem(POOL, item.type, toPosition)) doChangeTypeItem(item.uid, TYPE_EMPTY) return true end diff --git a/data/actions/scripts/liquids/mastermind_potion.lua b/data/actions/scripts/liquids/mastermind_potion.lua index 68651bb..e7204d2 100644 --- a/data/actions/scripts/liquids/mastermind_potion.lua +++ b/data/actions/scripts/liquids/mastermind_potion.lua @@ -2,6 +2,7 @@ local condition = createConditionObject(CONDITION_ATTRIBUTES) setConditionParam(condition, CONDITION_PARAM_TICKS, 10 * 60 * 1000) -- 10 minutes setConditionParam(condition, CONDITION_PARAM_STAT_MAGICLEVEL, 3) setConditionParam(condition, CONDITION_PARAM_SKILL_SHIELD, -10) +setConditionParam(condition, CONDITION_PARAM_SUBID, 99) function onUse(cid, item, fromPosition, itemEx, toPosition) if(not isSorcerer(cid) and not isDruid(cid)) then @@ -11,7 +12,7 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) if(doAddCondition(cid, condition)) then doSendMagicEffect(fromPosition, CONST_ME_MAGIC_RED) - doRemoveItem(item.uid) + doRemoveItem(item.uid, 1) doCreatureSay(cid, "You feel smarter.", TALKTYPE_ORANGE_1, cid) end diff --git a/data/actions/scripts/liquids/potions.lua b/data/actions/scripts/liquids/potions.lua index 0282dc9..ef0f9ad 100644 --- a/data/actions/scripts/liquids/potions.lua +++ b/data/actions/scripts/liquids/potions.lua @@ -1,33 +1,68 @@ local config = { removeOnUse = "no", usableOnTarget = "yes", -- can be used on target? (fe. healing friend) - splashable = "no", - realAnimation = "no", -- make text effect visible only for players in range 1x1 - healthMultiplier = 1.0, - manaMultiplier = 1.0 + splashable = "yes", + range = -1, + area = {1, 1} -- if not set correctly, the message will be sent only to user of the item } -config.removeOnUse = getBooleanFromString(config.removeOnUse) -config.usableOnTarget = getBooleanFromString(config.usableOnTarget) -config.splashable = getBooleanFromString(config.splashable) -config.realAnimation = getBooleanFromString(config.realAnimation) +local multiplier = { + health = 1.0, + mana = 1.0 +} local POTIONS = { - [8704] = {empty = 7636, splash = 2, health = {50, 100}}, -- small health potion - [7618] = {empty = 7636, splash = 2, health = {100, 200}}, -- health potion - [7588] = {empty = 7634, splash = 2, health = {200, 400}, level = 50, vocations = {3, 4, 7, 8}, vocStr = "knights and paladins"}, -- strong health potion - [7591] = {empty = 7635, splash = 2, health = {500, 700}, level = 80, vocations = {4, 8}, vocStr = "knights"}, -- great health potion - [8473] = {empty = 7635, splash = 2, health = {800, 1000}, level = 130, vocations = {4, 8}, vocStr = "knights"}, -- ultimate health potion + [8704] = {empty = 7636, splash = 42, health = {50, 100}}, -- small health potion + [7618] = {empty = 7636, splash = 42, health = {100, 200}}, -- health potion + [7588] = {empty = 7634, splash = 42, health = {200, 400}, level = 50, vocations = {3, 4, 7, 8}, vocStr = "knights and paladins"}, -- strong health potion + [7591] = {empty = 7635, splash = 42, health = {500, 700}, level = 80, vocations = {4, 8}, vocStr = "knights"}, -- great health potion + [8473] = {empty = 7635, splash = 42, health = {800, 1000}, level = 130, vocations = {4, 8}, vocStr = "knights"}, -- ultimate health potion - [7620] = {empty = 7636, splash = 7, mana = {70, 130}}, -- mana potion - [7589] = {empty = 7634, splash = 7, mana = {110, 190}, level = 50, vocations = {1, 2, 3, 5, 6, 7}, vocStr = "sorcerers, druids and paladins"}, -- strong mana potion - [7590] = {empty = 7635, splash = 7, mana = {200, 300}, level = 80, vocations = {1, 2, 5, 6}, vocStr = "sorcerers and druids"}, -- great mana potion + [7620] = {empty = 7636, splash = 47, mana = {70, 130}}, -- mana potion + [7589] = {empty = 7634, splash = 47, mana = {110, 190}, level = 50, vocations = {1, 2, 3, 5, 6, 7}, vocStr = "sorcerers, druids and paladins"}, -- strong mana potion + [7590] = {empty = 7635, splash = 47, mana = {200, 300}, level = 80, vocations = {1, 2, 5, 6}, vocStr = "sorcerers and druids"}, -- great mana potion - [8472] = {empty = 7635, splash = 3, health = {200, 400}, mana = {110, 190}, level = 80, vocations = {3, 7}, vocStr = "paladins"} -- great spirit potion + [8472] = {empty = 7635, splash = 43, health = {200, 400}, mana = {110, 190}, level = 80, vocations = {3, 7}, vocStr = "paladins"} -- great spirit potion } -local exhaust = createConditionObject(CONDITION_EXHAUST) -setConditionParam(exhaust, CONDITION_PARAM_TICKS, (getConfigInfo('timeBetweenExActions') - 100)) +for index, potion in pairs(POTIONS) do + if(type(index) == 'number')then + for k, v in pairs(config) do + if(not potion[k]) then + potion[k] = v + end + end + + if(potion.removeOnUse) then + potion.removeOnUse = getBooleanFromString(potion.removeOnUse) + end + + if(potion.usableOnTarget) then + potion.usableOnTarget = getBooleanFromString(potion.usableOnTarget) + end + + if(potion.splashable) then + potion.splashable = getBooleanFromString(potion.splashable) + end + + if(type(potion.health) == 'table' and table.maxn(potion.health) > 1) then + potion.health[1] = math.ceil(potion.health[1] * multiplier.health) + potion.health[2] = math.ceil(potion.health[2] * multiplier.health) + else + potion.health = nil + end + + + if(type(potion.mana) == 'table' and table.maxn(potion.mana) > 1) then + potion.mana[1] = math.ceil(potion.mana[1] * multiplier.mana) + potion.mana[2] = math.ceil(potion.mana[2] * multiplier.mana) + else + potion.mana = nil + end + + POTIONS[index] = potion + end +end function onUse(cid, item, fromPosition, itemEx, toPosition) local potion = POTIONS[item.itemid] @@ -35,59 +70,73 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) return false end - if(not isPlayer(itemEx.uid) or (not config.usableOnTarget and cid ~= itemEx.uid)) then - if(not config.splashable) then + if(not isPlayer(itemEx.uid) or (not potion.usableOnTarget and cid ~= itemEx.uid)) then + if(not potion.splashable or not potion.splash) then return false end if(toPosition.x == CONTAINER_POSITION) then - toPosition = getThingPos(item.uid) + toPosition = getThingPosition(item.uid) end - doDecayItem(doCreateItem(2016, potion.splash, toPosition)) - doTransformItem(item.uid, potion.empty) - return true - end + doDecayItem(doCreateItem(POOL, potion.splash, toPosition)) + doRemoveItem(item.uid, 1) + if(not potion.empty or potion.removeOnUse) then + return true + end + + if(fromPosition.x ~= CONTAINER_POSITION) then + doCreateItem(potion.empty, fromPosition) + else + doPlayerAddItem(cid, potion.empty, 1) + end - if(hasCondition(cid, CONDITION_EXHAUST_HEAL)) then - doPlayerSendDefaultCancel(cid, RETURNVALUE_YOUAREEXHAUSTED) return true end - if(((potion.level and getPlayerLevel(cid) < potion.level) or (potion.vocations and not isInArray(potion.vocations, getPlayerVocation(cid)))) and + if(((potion.level and getPlayerLevel(itemEx.uid) < potion.level) or (potion.vocations and not isInArray(potion.vocations, getPlayerVocation(itemEx.uid)))) and not getPlayerCustomFlagValue(cid, PLAYERCUSTOMFLAG_GAMEMASTERPRIVILEGES)) then - doCreatureSay(itemEx.uid, "Only " .. potion.vocStr .. (potion.level and (" of level " .. potion.level) or "") .. " or above may drink this fluid.", TALKTYPE_ORANGE_1) + doCreatureSay(itemEx.uid, "Only " .. potion.vocStr .. (potion.level and (" of level " .. potion.level) or "") .. " or above may drink this fluid.", TALKTYPE_MONSTER, false, cid) return true end - local health = potion.health - if(health and not doCreatureAddHealth(itemEx.uid, math.ceil(math.random(health[1], health[2]) * config.healthMultiplier))) then + if(potion.range > 0 and cid ~= itemEx.uid and getDistanceBetween(getThingPosition(cid), getThingPosition(itemEx.uid)) > potion.range and not getPlayerCustomFlagValue(cid, PLAYERCUSTOMFLAG_CANUSEFAR)) then + doPlayerSendDefaultCancel(cid, RETURNVALUE_TOOFARAWAY) + return true + end + + if(potion.health and not doTargetCombatHealth(cid, itemEx.uid, COMBAT_HEALING, potion.health[1], potion.health[2], CONST_ME_MAGIC_BLUE, false)) then return false end - local mana = potion.mana - if(mana and not doPlayerAddMana(itemEx.uid, math.ceil(math.random(mana[1], mana[2]) * config.manaMultiplier))) then + if(potion.mana and not doTargetCombatMana(cid, itemEx.uid, potion.mana[1], potion.mana[2], CONST_ME_MAGIC_BLUE, false)) then return false end - doSendMagicEffect(getThingPos(itemEx.uid), CONST_ME_MAGIC_BLUE) - if(not realAnimation) then - doCreatureSay(itemEx.uid, "Aaaah...", TALKTYPE_ORANGE_1) - else - for i, tid in ipairs(getSpectators(getCreaturePosition(cid), 1, 1)) do + if(type(potion.area) == 'table' and table.maxn(potion.area) > 1) then + for i, tid in ipairs(getSpectators(getThingPosition(itemEx.uid), potion.area[1], potion.area[2])) do if(isPlayer(tid)) then - doCreatureSay(itemEx.uid, "Aaaah...", TALKTYPE_ORANGE_1, false, tid) + doCreatureSay(itemEx.uid, "Aaaah...", TALKTYPE_MONSTER, false, tid) end end + else + doCreatureSay(itemEx.uid, "Aaaah...", TALKTYPE_MONSTER, false, itemEx.uid) + if(itemEx.uid ~= cid) then + doCreatureSay(itemEx.uid, "Aaaah...", TALKTYPE_MONSTER, false, cid) + end end - doAddCondition(cid, exhaust) - if(not potion.empty or config.removeOnUse) then - doRemoveItem(item.uid) + doRemoveItem(item.uid, 1) + if(not potion.empty or potion.removeOnUse) then return true end - doTransformItem(item.uid, potion.empty) + if(fromPosition.x ~= CONTAINER_POSITION) then + doCreateItem(potion.empty, fromPosition) + else + doPlayerAddItem(cid, potion.empty, 1) + end + return true end diff --git a/data/actions/scripts/other/blessings.lua b/data/actions/scripts/other/blessings.lua new file mode 100644 index 0000000..15aad59 --- /dev/null +++ b/data/actions/scripts/other/blessings.lua @@ -0,0 +1,13 @@ +local BLESSINGS = {"Wisdom of Solitude", "Spark of the Phoenix", "Fire of the Suns", "Spiritual Shielding", "Embrace of Tibia", "Twist of Fate"} +function onUse(cid, item, fromPosition, itemEx, toPosition) + local result = "" + for i = 1, (table.maxn(BLESSINGS) - 1) do + result = (getPlayerBlessing(cid, i) and result .. (result:len() > 0 and ", " or "") .. BLESSINGS[i] or result) + end + + if(getPlayerPVPBlessing(cid)) then + result = result .. ", " .. BLESSINGS[table.maxn(BLESSINGS)] + end + + return doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, result:len() > 0 and "Currently you have the following blessings: " .. result .. "." or "You do not have any blessing.") +end diff --git a/data/actions/scripts/other/blueberrybush.lua b/data/actions/scripts/other/blueberrybush.lua index d80ab11..dc81a5f 100644 --- a/data/actions/scripts/other/blueberrybush.lua +++ b/data/actions/scripts/other/blueberrybush.lua @@ -1,7 +1,4 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) doCreateItem(2677, 3, fromPosition) - doTransformItem(item.uid, 2786) - - doDecayItem(item.uid) - return true + return false end diff --git a/data/actions/scripts/other/changegold.lua b/data/actions/scripts/other/changegold.lua deleted file mode 100644 index a064032..0000000 --- a/data/actions/scripts/other/changegold.lua +++ /dev/null @@ -1,33 +0,0 @@ -local coins = { - [ITEM_GOLD_COIN] = { - to = ITEM_PLATINUM_COIN, effect = TEXTCOLOR_YELLOW - }, - [ITEM_PLATINUM_COIN] = { - from = ITEM_GOLD_COIN, to = ITEM_CRYSTAL_COIN, effect = TEXTCOLOR_LIGHTBLUE - }, - [ITEM_CRYSTAL_COIN] = { - from = ITEM_PLATINUM_COIN, effect = TEXTCOLOR_TEAL - } -} - -function onUse(cid, item, fromPosition, itemEx, toPosition) - if(getPlayerFlagValue(cid, PLAYERFLAG_CANNOTPICKUPITEM)) then - return false - end - - local coin = coins[item.itemid] - if(not coin) then - return false - end - - if(coin.to ~= nil and item.type == ITEMCOUNT_MAX) then - doChangeTypeItem(item.uid, item.type - item.type) - doPlayerAddItem(cid, coin.to, 1) - doSendAnimatedText(fromPosition, "$$$", coins[coin.to].effect) - elseif(coin.from ~= nil) then - doChangeTypeItem(item.uid, item.type - 1) - doPlayerAddItem(cid, coin.from, ITEMCOUNT_MAX) - doSendAnimatedText(fromPosition, "$$$", coins[coin.from].effect) - end - return true -end diff --git a/data/actions/scripts/other/constructionkits.lua b/data/actions/scripts/other/constructionkits.lua index 2ba9dde..9393dfa 100644 --- a/data/actions/scripts/other/constructionkits.lua +++ b/data/actions/scripts/other/constructionkits.lua @@ -1,9 +1,9 @@ local CONSTRUCTIONS = { - [3901] = 1650, [3902] = 1658, [3903] = 1666, [3904] = 1670, [3905] = 3813, [3906] = 3817, [3907] = 2093, [3908] = 2603, [3909] = 1614, [3910] = 1615, - [3911] = 1616, [3912] = 1619, [3913] = 3805, [3914] = 3807, [3915] = 1740, [3916] = 1774, [3917] = 2084, [3918] = 2095, [3919] = 3809, [3920] = 3832, - [3921] = 1714, [3922] = 2107, [3923] = 2104, [3924] = 7670, [3925] = 1740, [3926] = 2080, [3927] = 2098, [3928] = 1676, [3929] = 2101, [3930] = 1739, - [3931] = 2105, [3932] = 1724, [3933] = 1728, [3934] = 1732, [3935] = 1775, [3936] = 3812, [3937] = 2064, [3938] = 6371, [5086] = 1738, [5087] = 1741, [5088] = 1770, - [6114] = 2106, [6115] = 2034, [6372] = 3811, [6373] = 1736, [7503] = 1750, [7700] = 5928, [7960] = 3821, [7961] = 3811, [7962] = 2582, [8692] = 8688, [8693] = 7486 + [3901] = 1652, [3902] = 1658, [3903] = 1666, [3904] = 1670, [3905] = 3813, [3906] = 3817, [3907] = 3821, [3908] = 1619, [3909] = 1614, [3910] = 1615, + [3911] = 1616, [3912] = 2604, [3913] = 3805, [3914] = 3807, [3915] = 1716, [3916] = 1724, [3917] = 1728, [3918] = 1732, [3919] = 3809, [3920] = 3811, + [3921] = 2084, [3922] = 2095, [3923] = 2098, [3924] = 2064, [3925] = 1674, [3926] = 2080, [3927] = 1442, [3928] = 1446, [3929] = 2034, [3930] = 1447, + [3931] = 2101, [3932] = 1774, [3933] = 2105, [3934] = 2117, [3935] = 2582, [3936] = 3832, [3937] = 1775, [3938] = 1750, [5086] = 5056, [5087] = 5055, [5088] = 5046, + [6114] = 6109, [6115] = 6111, [6372] = 6356, [6373] = 6371, [8692] = 8688, [9974] = 9975 } function onUse(cid, item, fromPosition, itemEx, toPosition) diff --git a/data/actions/scripts/other/createbread.lua b/data/actions/scripts/other/createbread.lua index a9a9240..76ee34f 100644 --- a/data/actions/scripts/other/createbread.lua +++ b/data/actions/scripts/other/createbread.lua @@ -1,15 +1,17 @@ local LIQUID_CONTAINERS = {1775, 2005, 2006, 2007, 2008, 2009, 2011, 2012, 2013, 2014, 2015, 2023, 2031, 2032, 2033} + function onUse(cid, item, fromPosition, itemEx, toPosition) if(item.itemid == 2692 and isInArray(LIQUID_CONTAINERS, itemEx.itemid) and itemEx.type == 1) then doChangeTypeItem(item.uid, item.type - 1) doPlayerAddItem(cid, 2693, 1) + doChangeTypeItem(itemEx.uid, item.type - item.type) + return true elseif(itemEx.itemid == 1381) then doChangeTypeItem(item.uid, item.type - 1) doPlayerAddItem(cid, 2692, 1) - else - return false + return true end - return true + return false end diff --git a/data/actions/scripts/other/decayto.lua b/data/actions/scripts/other/decayto.lua deleted file mode 100644 index 271ab98..0000000 --- a/data/actions/scripts/other/decayto.lua +++ /dev/null @@ -1,29 +0,0 @@ -local ITEM_IDS = { - [2041] = 2042, - [2042] = 2041, - [2044] = 2045, - [2045] = 2044, - [2047] = 2048, - [2048] = 2047, - [2050] = 2051, - [2051] = 2050, - [2052] = 2053, - [2053] = 2051, - [2054] = 2055, - [2054] = 2055, - -- crystal pedestals - [9976] = 9977, - [9977] = 9978, - [9978] = 9979, - [9979] = 9976 -} - -function onUse(cid, item, fromPosition, itemEx, toPosition) - if(not ITEM_IDS[item.itemid]) then - return false - end - - doTransformItem(item.uid, ITEM_IDS[item.itemid]) - doDecayItem(item.uid) - return true -end diff --git a/data/actions/scripts/other/destroy.lua b/data/actions/scripts/other/destroy.lua deleted file mode 100644 index 006cf2b..0000000 --- a/data/actions/scripts/other/destroy.lua +++ /dev/null @@ -1,3 +0,0 @@ -function onUse(cid, item, fromPosition, itemEx, toPosition) - return destroyItem(cid, itemEx, toPosition) -end diff --git a/data/actions/scripts/other/dice.lua b/data/actions/scripts/other/dice.lua index 4ef6e64..ed51620 100644 --- a/data/actions/scripts/other/dice.lua +++ b/data/actions/scripts/other/dice.lua @@ -4,7 +4,12 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) end local value = math.random(5792, 5797) + for i, pid in ipairs(getSpectators(getThingPosition(item.uid), 3, 3)) do + if(isPlayer(pid)) then + doCreatureSay(cid, getCreatureName(cid) .. ' rolled a ' .. value - 5791 .. '.', TALKTYPE_MONSTER, false, pid) + end + end + doTransformItem(item.uid, value) - doCreatureSay(cid, getCreatureName(cid) .. ' rolled a ' .. value - 5791 .. '.', TALKTYPE_ORANGE_1) return true end diff --git a/data/actions/scripts/other/dolls.lua b/data/actions/scripts/other/dolls.lua new file mode 100644 index 0000000..28ff6fe --- /dev/null +++ b/data/actions/scripts/other/dolls.lua @@ -0,0 +1,83 @@ +local DOLLS = { + [5080] = {"Hug me."}, + [5669] = { + "It's not winning that matters, but winning in style.", + "Today's your lucky day. Probably.", + "Do not meddle in the affairs of dragons, for you are crunchy and taste good with ketchup.", + "That is one stupid question.", + "You'll need more rum for that.", + "Do or do not. There is no try.", + "You should do something you always wanted to.", + "If you walk under a ladder and it falls down on you it probably means bad luck.", + "Never say 'oops'. Always say 'Ah, interesting!'", + "Five steps east, fourteen steps south, two steps north and seventeen steps west!" + }, + [6566] = { + "Fchhhhhh!", + "Zchhhhhh!", + "Grooaaaaar*cough*", + "Aaa... CHOO!", + "You... will.... burn!!" + }, + [6388] = {"Merry Christmas "}, + [6512] = { + "Ho ho ho", + "Jingle bells, jingle bells...", + "Have you been naughty?", + "Have you been nice?", + "Merry Christmas!", + "Can you stop squeezing me now... I'm starting to feel a little sick." + }, + [8974] = {"ARE YOU PREPARED TO FACE YOUR DESTINY?"}, + [8977] = { + "Weirdo, you're a weirdo! Actually all of you are!", + "Pie for breakfast, pie for lunch and pie for dinner!", + "All hail the control panel!", + "I own, Tibiacity owns, perfect match!", + "Hug me! Feed me! Hail me!" + }, + [8981] = { + "It's news to me.", + "News, updated as infrequently as possible!", + "Extra! Extra! Read all about it!", + "Fresh off the press!" + }, + [8982] = { + "Hail TibiaNordic!", + "So cold..", + "Run, mammoth!" + } +} + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local doll = DOLLS[item.itemid] + if(doll == nil) then + return false + end + + if(fromPosition.x == CONTAINER_POSITION) then + fromPosition = getThingPosition(cid) + end + + local random = math.random(1, table.maxn(doll)) + local sound = doll[random] + if(item.itemid == 6566) then + if(random == 3) then + doSendMagicEffect(fromPosition, CONST_ME_POFF) + elseif(random == 4) then + doSendMagicEffect(fromPosition, CONST_ME_FIREAREA) + elseif(random == 5) then + doTargetCombatHealth(0, cid, COMBAT_PHYSICALDAMAGE, -1, -1, CONST_ME_EXPLOSIONHIT) + end + elseif(item.itemid == 5669) then + doSendMagicEffect(fromPosition, CONST_ME_MAGIC_RED) + doTransformItem(item.uid, item.itemid + 1) + doDecayItem(item.uid) + elseif(item.itemid == 6388) then + doSendMagicEffect(fromPosition, CONST_ME_SOUND_YELLOW) + sound = sound .. getCreatureName(cid) .. "." + end + + doCreatureSay(cid, sound, TALKTYPE_MONSTER, false, 0, fromPosition) + return true +end diff --git a/data/actions/scripts/other/doors.lua b/data/actions/scripts/other/doors.lua index 5c459eb..361f749 100644 --- a/data/actions/scripts/other/doors.lua +++ b/data/actions/scripts/other/doors.lua @@ -1,16 +1,7 @@ -local function checkStackpos(item, position) - position.stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE - local thing = getThingFromPos(position) - - position.stackpos = STACKPOS_TOP_FIELD - local field = getThingFromPos(position) - - return (item.uid == thing.uid or thing.itemid < 100 or field.itemid == 0) -end - -local function doorEnter(cid, item, toPosition) - doTransformItem(item.uid, item.itemid + 1) - doTeleportThing(cid, toPosition) +local function doorEnter(cid, uid, id, position) + doTransformItem(uid, id) + doTeleportThing(cid, position) + return true end function onUse(cid, item, fromPosition, itemEx, toPosition) @@ -19,153 +10,109 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) return true end - if(getItemLevelDoor(item.itemid) > 0) then - if(item.actionid == 189) then + local locked = DOORS[item.itemid] + if(locked) then + doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "It is locked.") + return true + end + + local door = getItemInfo(item.itemid) + if(door.levelDoor > 0) then + if(item.aid == 189) then if(not isPremium(cid)) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end - doorEnter(cid, item, toPosition) - return true + return doorEnter(cid, item.uid, door.transformUseTo, toPosition) end - local gender = item.actionid - 186 - if(isInArray({PLAYERSEX_FEMALE, PLAYERSEX_MALE, PLAYERSEX_GAMEMASTER}, gender)) then + local gender = item.aid - 186 + if(isInArray({PLAYERSEX_FEMALE, PLAYERSEX_MALE}, gender)) then if(gender ~= getPlayerSex(cid)) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end - doorEnter(cid, item, toPosition) - return true + return doorEnter(cid, item.uid, door.transformUseTo, toPosition) end - local skull = item.actionid - 180 + local skull = item.aid - 180 if(skull >= SKULL_NONE and skull <= SKULL_BLACK) then if(skull ~= getCreatureSkullType(cid)) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end - doorEnter(cid, item, toPosition) - return true + return doorEnter(cid, item.uid, door.transformUseTo, toPosition) end - local group = item.actionid - 150 + local group = item.aid - 150 if(group >= 0 and group < 30) then if(group > getPlayerGroupId(cid)) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end - doorEnter(cid, item, toPosition) - return true + return doorEnter(cid, item.uid, door.transformUseTo, toPosition) end - local vocation = item.actionid - 100 + local vocation = item.aid - 100 if(vocation >= 0 and vocation < 50) then - local playerVocationInfo = getVocationInfo(getPlayerVocation(cid)) - if(playerVocationInfo.id ~= vocation and playerVocationInfo.fromVocation ~= vocation) then + local vocationEx = getVocationInfo(getPlayerVocation(cid)) + if(vocationEx.id ~= vocation and vocationEx.fromVocation ~= vocation) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end - doorEnter(cid, item, toPosition) - return true + return doorEnter(cid, item.uid, door.transformUseTo, toPosition) end - if(item.actionid == 190 or (item.actionid ~= 0 and getPlayerLevel(cid) >= (item.actionid - getItemLevelDoor(item.itemid)))) then - doorEnter(cid, item, toPosition) - else - doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") + if(item.aid == 190 or (item.aid ~= 0 and getPlayerLevel(cid) >= (item.aid - door.levelDoor))) then + return doorEnter(cid, item.uid, door.transformUseTo, toPosition) end + doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end - if(isInArray(specialDoors, item.itemid)) then - if(item.actionid == 100 or (item.actionid ~= 0 and getPlayerStorageValue(cid, item.actionid) > 0)) then - doorEnter(cid, item, toPosition) - else - doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "The door seems to be sealed against unwanted intruders.") + if(door.specialDoor) then + if(item.aid == 100 or (item.aid ~= 0 and getCreatureStorage(cid, item.aid) > 0)) then + return doorEnter(cid, item.uid, door.transformUseTo, toPosition) end + doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "The door seems to be sealed against unwanted intruders.") return true end - if(isInArray(keys, item.itemid)) then - if(itemEx.actionid > 0) then - if(item.actionid == itemEx.actionid and doors[itemEx.itemid] ~= nil) then - doTransformItem(itemEx.uid, doors[itemEx.itemid]) - return true - end - - doPlayerSendCancel(cid, "The key does not match.") - return true - end - - return false - end - - if(isInArray(horizontalOpenDoors, item.itemid) and checkStackpos(item, fromPosition)) then - local newPosition = toPosition - newPosition.y = newPosition.y + 1 - local doorPosition = fromPosition - doorPosition.stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE - local doorCreature = getThingfromPos(doorPosition) - if(doorCreature.itemid ~= 0) then - local pzDoorPosition = getTileInfo(doorPosition).protection - local pzNewPosition = getTileInfo(newPosition).protection - if((pzDoorPosition and not pzNewPosition and doorCreature.uid ~= cid) or - (not pzDoorPosition and pzNewPosition and doorCreature.uid == cid and isPlayerPzLocked(cid))) then - doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - else - doTeleportThing(doorCreature.uid, newPosition) - if(not isInArray(closingDoors, item.itemid)) then - doTransformItem(item.uid, item.itemid - 1) - end - end - - return true - end - - doTransformItem(item.uid, item.itemid - 1) + toPosition.stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE + local fields, thing = getTileItemsByType(fromPosition, ITEM_TYPE_MAGICFIELD), getThingFromPosition(toPosition) + if(item.uid ~= thing.uid and thing.itemid >= 100 and table.maxn(fields) ~= 0) then return true end - if(isInArray(verticalOpenDoors, item.itemid) and checkStackpos(item, fromPosition)) then - local newPosition = toPosition - newPosition.x = newPosition.x + 1 - local doorPosition = fromPosition - doorPosition.stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE - local doorCreature = getThingfromPos(doorPosition) - if(doorCreature.itemid ~= 0) then - if(getTileInfo(doorPosition).protection and not getTileInfo(newPosition).protection and doorCreature.uid ~= cid) then - doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - else - doTeleportThing(doorCreature.uid, newPosition) - if(not isInArray(closingDoors, item.itemid)) then - doTransformItem(item.uid, item.itemid - 1) - end - end + local doorCreature = getThingFromPosition(toPosition) + if(doorCreature.itemid ~= 0) then + toPosition.x = toPosition.x + 1 + local query = doTileQueryAdd(doorCreature.uid, toPosition, 20) -- allow to stack outside doors, but not on teleports or floor changing tiles + if(query == RETURNVALUE_NOTPOSSIBLE) then + toPosition.x = toPosition.x - 1 + toPosition.y = toPosition.y + 1 + query = doTileQueryAdd(doorCreature.uid, toPosition, 20) -- repeat until found + end + if(query ~= RETURNVALUE_NOERROR) then + doPlayerSendDefaultCancel(cid, query) return true end - doTransformItem(item.uid, item.itemid - 1) - return true - end - - if(doors[item.itemid] ~= nil and checkStackpos(item, fromPosition)) then - if(item.actionid == 0) then - doTransformItem(item.uid, doors[item.itemid]) - else - doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "It is locked.") + doTeleportThing(doorCreature.uid, toPosition) + if(not door.closingDoor) then + doTransformItem(item.uid, door.transformUseTo) end return true end return false -end +end \ No newline at end of file diff --git a/data/actions/scripts/other/enchanting.lua b/data/actions/scripts/other/enchanting.lua index 096336b..b717b31 100644 --- a/data/actions/scripts/other/enchanting.lua +++ b/data/actions/scripts/other/enchanting.lua @@ -1,6 +1,7 @@ local config = { + hardcoreManaSpent = getConfigValue("addManaSpentInPvPZone"), manaCost = 300, - soulCost = 2 + soulCost = 2, } function onUse(cid, item, fromPosition, itemEx, toPosition) @@ -21,6 +22,14 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) return true end + if(item.itemid == 7761 and isInArray({9949, 9954}, itemEx.itemid)) then + doTransformItem(itemEx.uid, itemEx.itemid - 1) + doRemoveItem(item.uid, 1) + + doSendMagicEffect(toPosition, CONST_ME_MAGIC_GREEN) + return true + end + if(isInArray(enchantableGems, item.itemid)) then local subtype = item.type if(subtype == 0) then @@ -44,11 +53,14 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) return false end - doTransformItem(item.uid, enchantedGems[a]) doPlayerAddMana(cid, -mana) doPlayerAddSoul(cid, -soul) - doPlayerAddSpentMana(cid, mana) + doTransformItem(item.uid, enchantedGems[a]) + if(not getPlayerFlagValue(cid, PlayerFlag_NotGainMana) and (not getTileInfo(getThingPosition(cid)).hardcore or config.hardcoreManaSpent)) then + doPlayerAddSpentMana(cid, mana) + end + doSendMagicEffect(fromPosition, CONST_ME_HOLYDAMAGE) return true end diff --git a/data/actions/scripts/other/fireworksrocket.lua b/data/actions/scripts/other/fireworksrocket.lua index c149555..ef13f7f 100644 --- a/data/actions/scripts/other/fireworksrocket.lua +++ b/data/actions/scripts/other/fireworksrocket.lua @@ -1,12 +1,9 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) if(fromPosition.x ~= CONTAINER_POSITION) then - fireworksEffect = math.random(CONST_ME_FIREWORK_YELLOW, CONST_ME_FIREWORK_BLUE) - doSendMagicEffect(fromPosition, fireworksEffect) + doSendMagicEffect(fromPosition, math.random(CONST_ME_FIREWORK_YELLOW, CONST_ME_FIREWORK_BLUE)) else - doSendMagicEffect(fromPosition, CONST_ME_HITBYFIRE) - doSendMagicEffect(fromPosition, CONST_ME_EXPLOSIONAREA) - doCreatureSay(cid, "Ouch! Rather place it on the ground next time.", TALKTYPE_ORANGE_1) - doCreatureAddHealth(cid, -10) + doCreatureSay(cid, "Ouch! Rather place it on the ground next time.", TALKTYPE_MONSTER) + doTargetCombatHealth(0, cid, COMBAT_PHYSICALDAMAGE, -1, -1, CONST_ME_EXPLOSIONHIT) end doRemoveItem(cid, item.uid, 1) diff --git a/data/actions/scripts/other/food.lua b/data/actions/scripts/other/food.lua deleted file mode 100644 index 8512d19..0000000 --- a/data/actions/scripts/other/food.lua +++ /dev/null @@ -1,93 +0,0 @@ -local FOODS = -{ - [2362] = {8, "Crunch."}, - [2666] = {15, "Munch."}, - [2667] = {12, "Munch."}, - [2668] = {10, "Mmmm."}, - [2669] = {17, "Munch."}, - [2670] = {4, "Gulp."}, - [2671] = {30, "Chomp."}, - [2672] = {60, "Chomp."}, - [2673] = {5, "Yum."}, - [2674] = {6, "Yum."}, - [2675] = {13, "Yum."}, - [2676] = {8, "Yum."}, - [2677] = {1, "Yum."}, - [2678] = {18, "Slurp."}, - [2679] = {1, "Yum."}, - [2680] = {2, "Yum."}, - [2681] = {9, "Yum."}, - [2682] = {20, "Yum."}, - [2683] = {17, "Munch."}, - [2684] = {8, "Crunch."}, - [2685] = {6, "Munch."}, - [2686] = {9, "Crunch."}, - [2687] = {2, "Crunch."}, - [2688] = {9, "Munch."}, - [2689] = {10, "Crunch."}, - [2690] = {3, "Crunch."}, - [2691] = {8, "Crunch."}, - [2792] = {6, "Munch."}, - [2793] = {9, "Munch."}, - [2695] = {6, "Gulp."}, - [2696] = {9, "Smack."}, - [2787] = {9, "Munch."}, - [2788] = {4, "Munch."}, - [2789] = {22, "Munch."}, - [2790] = {30, "Munch."}, - [2791] = {30, "Munch."}, - [2792] = {6, "Munch."}, - [2794] = {3, "Munch."}, - [2795] = {36, "Munch."}, - [2796] = {5, "Munch."}, - [2793] = {9, "Munch."}, - [5097] = {4, "Yum."}, - [6125] = {8, "Gulp."}, - [6278] = {10, "Mmmm."}, - [6279] = {15, "Mmmm."}, - [6393] = {12, "Mmmm."}, - [6394] = {15, "Mmmm."}, - [6501] = {20, "Mmmm."}, - [6541] = {6, "Gulp."}, - [6542] = {6, "Gulp."}, - [6543] = {6, "Gulp."}, - [6544] = {6, "Gulp."}, - [6545] = {6, "Gulp."}, - [6569] = {1, "Mmmm."}, - [6574] = {4, "Mmmm."}, - [7158] = {15, "Munch."}, - [7159] = {13, "Munch."}, - [7372] = {7, "Yum."}, - [7373] = {7, "Yum."}, - [7374] = {7, "Yum."}, - [7375] = {7, "Yum."}, - [7376] = {7, "Yum."}, - [7377] = {7, "Yum."}, - [7909] = {4, "Crunch."}, - [8838] = {7, "Gulp."}, - [8839] = {5, "Yum."}, - [8840] = {2, "Yum."}, - [8841] = {3, "Urgh."}, - [8842] = {3, "Munch."}, - [8843] = {3, "Crunch."}, - [8844] = {3, "Gulp."}, - [8845] = {2, "Munch."}, - [8847] = {11, "Yum."} -} - -function onUse(cid, item, fromPosition, itemEx, toPosition) - local food = FOODS[item.itemid] - if(not food) then - return false - end - - if((getPlayerFood(cid) + food[1]) >= 400) then - doPlayerSendCancel(cid, "You are full.") - return true - end - - doPlayerFeed(cid, food[1] * 4) - doCreatureSay(cid, food[2], TALKTYPE_ORANGE_1) - doRemoveItem(item.uid, 1) - return true -end diff --git a/data/actions/scripts/other/furniturebeds.lua b/data/actions/scripts/other/furniturebeds.lua index dc42adc..d9f019f 100644 --- a/data/actions/scripts/other/furniturebeds.lua +++ b/data/actions/scripts/other/furniturebeds.lua @@ -1,8 +1,8 @@ local BEDS = { - [7904] = {{7811, 7812}, {7813, 7814}}, -- green kit - [7905] = {{7815, 7816}, {7817, 7818}}, -- red kit - [7906] = {{7819, 7820}, {7821, 7822}}, -- yellow kit - [7907] = {{1754, 1755}, {1760, 1761}} -- removal kit + [7904] = {{1754, 1755}, {1760, 1761}}, -- blue kit + [7905] = {{7811, 7812}, {7813, 7814}}, -- green kit + [7906] = {{7815, 7816}, {7817, 7818}}, -- red kit + [7907] = {{7819, 7820}, {7821, 7822}} -- yellow kit } local function internalBedTransform(item, itemEx, toPosition, ids) diff --git a/data/actions/scripts/other/keys.lua b/data/actions/scripts/other/keys.lua new file mode 100644 index 0000000..31e658c --- /dev/null +++ b/data/actions/scripts/other/keys.lua @@ -0,0 +1,39 @@ +REVERSE_DOORS, CHILD_DOORS = {}, {} +for k, v in pairs(DOORS) do + REVERSE_DOORS[v] = k + + local tmp = getItemInfo(v) + if(tmp.transformUseTo ~= 0) then + CHILD_DOORS[tmp.transformUseTo] = k + end +end + +function onUse(cid, item, fromPosition, itemEx, toPosition) + if(item.aid > 0 and itemEx.aid > 0) then + if(isPlayerPzLocked(cid) and getTileInfo(toPosition).protection) then + doPlayerSendDefaultCancel(cid, RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE) + return true + end + + local doors = DOORS[itemEx.itemid] + if(not doors) then + doors = REVERSE_DOORS[itemEx.itemid] + end + + if(not doors) then + doors = CHILD_DOORS[itemEx.itemid] + end + + if(doors) then + if(item.actionid ~= itemEx.actionid) then + doPlayerSendCancel(cid, "The key does not match.") + else + doTransformItem(itemEx.uid, doors) + end + + return true + end + end + + return false +end \ No newline at end of file diff --git a/data/actions/scripts/other/music.lua b/data/actions/scripts/other/music.lua index ce9abb9..b08eab4 100644 --- a/data/actions/scripts/other/music.lua +++ b/data/actions/scripts/other/music.lua @@ -5,8 +5,8 @@ local DIDGERIDOO = 3952 local CORNUCOPIA = 2369 local PARTY_TRUMPET = 6572 local USED_PARTY_TRUMPET = 6573 -local GREEN_NOTES = {2070, 2071, 2073, 2075, 2076, 2078, 2367, 2374} +local GREEN_NOTES = {2070, 2071, 2073, 2075, 2076, 2078, 2367, 2374} function onUse(cid, item, fromPosition, itemEx, toPosition) local random = math.random(1, 5) if(item.itemid == BIRD_CAGE) then @@ -40,7 +40,7 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) position.x = position.x + 1 doSendMagicEffect(fromPosition, CONST_ME_SOUND_PURPLE) - doSummonCreature("Wolf", pos) + doSummonCreature("Wolf", position) else local effect = CONST_ME_SOUND_BLUE if(item.itemid == HORN) then diff --git a/data/actions/scripts/other/present.lua b/data/actions/scripts/other/present.lua new file mode 100644 index 0000000..588cb2c --- /dev/null +++ b/data/actions/scripts/other/present.lua @@ -0,0 +1,7 @@ +function onUse(cid, item, fromPosition, itemEx, toPosition) + doSendMagicEffect(fromPosition, CONST_ME_EXPLOSIONAREA) + doCreatureSay(cid, "KABOOOOOOOOOOM!", TALKTYPE_MONSTER) + + doRemoveItem(item.uid, 1) + return true +end diff --git a/data/actions/scripts/other/pumpkinhead.lua b/data/actions/scripts/other/pumpkinhead.lua new file mode 100644 index 0000000..9b62e98 --- /dev/null +++ b/data/actions/scripts/other/pumpkinhead.lua @@ -0,0 +1,15 @@ +local PUMPKIN_HEAD_LIGHT = 2097 +local CANDLE = 2048 +local KNIFE = 2566 + +function onUse(cid, item, fromPosition, itemEx, toPosition) + if(item.itemid == PUMPKIN_HEAD and itemEx.itemid == CANDLE) then + doTransformItem(item.uid, PUMPKIN_HEAD_LIGHT) + doRemoveItem(itemEx.uid) + + doDecayItem(item.uid) + return true + end + + return item.itemid == KNIFE and TOOLS.KNIFE(cid, item, fromPosition, itemEx, toPosition) +end diff --git a/data/actions/scripts/other/spellwand.lua b/data/actions/scripts/other/spellwand.lua new file mode 100644 index 0000000..7ddaa5c --- /dev/null +++ b/data/actions/scripts/other/spellwand.lua @@ -0,0 +1,20 @@ +local config = { + outfits = {"rat", "green frog", "chicken"}, -- possible outfits + duration = 45, -- duration of the outfit in seconds + breakChance = 1 -- a chance of losing the wand +} + +function onUse(cid, item, fromPosition, itemEx, toPosition) + if(math.random(1, 100) <= config.breakChance) then + doSummonCreature("Sheep", toPosition) -- There should be Mad Sheep, but we don't have Mad Sheep :( + doRemoveItem(item.uid, 1) + return true + end + + if(isPlayer(itemEx.uid)) then + doSetMonsterOutfit(itemEx.uid, config.outfits[math.random(1, table.maxn(config.outfits))], config.duration * 1000) + doSendMagicEffect(toPosition, CONST_ME_MAGIC_BLUE) + end + + return true +end diff --git a/data/actions/scripts/other/spideregg.lua b/data/actions/scripts/other/spideregg.lua new file mode 100644 index 0000000..6143571 --- /dev/null +++ b/data/actions/scripts/other/spideregg.lua @@ -0,0 +1,17 @@ +function onUse(cid, item, fromPosition, itemEx, toPosition) + local rand, effect = math.random(1, 100), CONST_ME_TELEPORT + if((rand >= 50) and (rand < 83)) then + doSummonCreature("Spider", fromPosition) + elseif((rand >= 83) and (rand < 97)) then + doSummonCreature("Poison Spider", fromPosition) + elseif((rand >= 97) and (rand < 100)) then + doSummonCreature("Tarantula", fromPosition) + elseif(rand == 100) then + doSummonCreature("Giant Spider", fromPosition) + else + effect = CONST_ME_POFF + end + + doSendMagicEffect(fromPosition, effect) + return false +end diff --git a/data/actions/scripts/other/stuffeddragon.lua b/data/actions/scripts/other/stuffeddragon.lua deleted file mode 100644 index 17db7dd..0000000 --- a/data/actions/scripts/other/stuffeddragon.lua +++ /dev/null @@ -1,23 +0,0 @@ --- --- Stuffed Dragon (sid: 5791) --- --- TODO: --- Make "You... will.... burn!!" more rare. --- - -local SOUNDS = {"Fchhhhhh!", "Zchhhhhh!", "Grooaaaaar*cough*", "Aaa... CHOO!", "You... will.... burn!!"} - -function onUse(cid, item, fromPosition, itemEx, toPosition) - local random = math.random(1, table.maxn(SOUNDS)) - if(fromPosition.x ~= CONTAINER_POSITION) then - doCreatureSay(cid, SOUNDS[random], TALKTYPE_MONSTER, false, 0, fromPosition) - else - doCreatureSay(cid, SOUNDS[random], TALKTYPE_MONSTER) - end - - if(random == 5) then -- "You... will.... burn!!" - doTargetCombatHealth(0, cid, COMBAT_PHYSICALDAMAGE, -1, -1, CONST_ME_EXPLOSIONHIT) - end - - return true -end diff --git a/data/actions/scripts/other/taming.lua b/data/actions/scripts/other/taming.lua new file mode 100644 index 0000000..1c5a81d --- /dev/null +++ b/data/actions/scripts/other/taming.lua @@ -0,0 +1,137 @@ +--[[ + The 'CHANCE' values that I'm not sure about have been defaulted to 40% + TODO: Get real FAIL_MSG and SUCCESS_MSG for some of the tammings + + Info: + [xxxxx] = tamming itemid + NAME = name of the creature tamming item is used on + ID = mount id for storage (check out data/XML/mounts.xml) + TYPE = type of creature taming item is used on (TYPE_MONSTER/TYPE_NPC/TYPE_ACTION/TYPE_UNIQUE) + CHANCE = X/100% + FAIL_MSG = { {action (ACTION_RUN/ACTION_BREAK/ACTION_NONE/ACTION_ALL), "message"} } + SUCCESS_MSG = "message" +]] + +local ACTION_RUN, ACTION_BREAK, ACTION_NONE, ACTION_ALL = 1, 2, 3, 4 +local TYPE_MONSTER, TYPE_NPC, TYPE_ACTION, TYPE_UNIQUE = 1, 2, 3, 4 + +local config = { + [5907] = {NAME = 'Bear', ID = 3, TYPE = TYPE_MONSTER, CHANCE = 20, FAIL_MSG = { {1, "The bear ran away."}, {2, "Oh no! The slingshot broke."}, {3, "The bear is trying to hit you with its claws."} }, SUCCESS_MSG = "You have tamed the bear."}, + [13295] = {NAME = 'Black Sheep', ID = 4, TYPE = TYPE_MONSTER, CHANCE = 25, FAIL_MSG = { {1, "The black sheep ran away."}, {2, "Oh no! The reins were torn."}, {3, "The black sheep is trying to run away."} }, SUCCESS_MSG = "You have tamed the sheep."}, + [13293] = {NAME = 'Midnight Panther', ID = 5, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "The panther has escaped."}, {2, "The whip broke."} }, SUCCESS_MSG = "You have tamed the panther."}, + [13298] = {NAME = 'Terror Bird', ID = 2, TYPE = TYPE_MONSTER, CHANCE = 15, FAIL_MSG = { {1, "The bird ran away."}, {3, "The terror bird is pecking you."} }, SUCCESS_MSG = "You have tamed the bird."}, + [13247] = {NAME = 'Boar', ID = 10, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "The boar has run away"}, {3, "The boar attacks you."} }, SUCCESS_MSG = "You have tamed the boar."}, + [13305] = {NAME = 'Crustacea Gigantica', ID = 7, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "The crustacea has run away."}, {2, "The crustacea ate the shrimp."} }, SUCCESS_MSG = "You have tamed the crustacea."}, + [13291] = {NAME = 'Undead Cavebear', ID = 12, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "The undead bear has run away."} }, SUCCESS_MSG = "You have tamed the skeleton."}, + [13307] = {NAME = 'Wailing Widow', ID = 1, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "The widow has run away."}, {2, "The widow has eaten the sweet bait."} }, SUCCESS_MSG = "You have tamed the widow."}, + [13292] = {NAME = 'Tin Lizzard', ID = 8, TYPE = TYPE_NPC, CHANCE = 40, FAIL_MSG = { {2, "The key broke inside."} }, SUCCESS_MSG = "You have started the Tin Lizzard!"}, + [13294] = {NAME = 'Draptor', ID = 6, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "The draptor has run away."}, {3, "The draptor has fled."} }, SUCCESS_MSG = "You have tamed the draptor."}, + [13536] = {NAME = 'Crystal Wolf', ID = 16, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "The wolf has run away."} }, SUCCESS_MSG = "You have tamed the wolf."}, + [13539] = {NAME = 'White Deer', ID = 18, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {2, "The cone broke."}, {3, "The deer has fled in fear."} }, SUCCESS_MSG = "You have tamed the deer."}, + [13538] = {NAME = 'Panda', ID = 19, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {4, "Panda ate the leaves and ran away."} }, SUCCESS_MSG = "You have tamed the panda."}, + [13535] = {NAME = 'Dromedary', ID = 20, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "Dromedary has run away."} }, SUCCESS_MSG = "You have tamed the dromedary."}, + [13498] = {NAME = 'King Scorpion', ID = 21, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "The scorpion has vanished."}, {2, "Scorpion broken the sceptre."} }, SUCCESS_MSG = "You have tamed the scorpion"}, + [13537] = {NAME = 'Donkey', ID = 13, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "The witch has escaped!"} }, SUCCESS_MSG = "You have tamed the mule."}, + [13938] = {NAME = 'Uniwheel', ID = 15, TYPE = TYPE_NPC, CHANCE = 40, FAIL_MSG = { {2, "The oil is having no effect."} }, SUCCESS_MSG = "You have found an Uniwheel."}, + [13508] = {NAME = 'Slug', ID = 14, TYPE = TYPE_MONSTER, CHANCE = 40, FAIL_MSG = { {1, "The slug has run away."}, {3, "The drug had no effect."} }, SUCCESS_MSG = "You have tamed the slug."}, + [13939] = {NAME = 'War Horse', ID = 23, TYPE = TYPE_MONSTER, CHANCE = 15, FAIL_MSG = { {1, "The horse runs away."}, {2, "The horse ate the oats."} }, SUCCESS_MSG = "You have tamed the horse."} +} + +local function doFailAction(cid, mount, pos, item, itemEx) + local action, effect = mount.FAIL_MSG[math.random(1, table.maxn(mount.FAIL_MSG))], CONST_ME_POFF + if(action[1] == ACTION_RUN) then + doRemoveCreature(itemEx.uid) + elseif(action[1] == ACTION_BREAK) then + effect = CONST_ME_BLOCKHIT + doRemoveItem(item.uid, 1) + elseif(action[1] == ACTION_ALL) then + doRemoveCreature(itemEx.uid) + doRemoveItem(item.uid, 1) + end + + doSendMagicEffect(pos, effect) + doCreatureSay(cid, action[2], TALKTYPE_MONSTER_SAY) + return action +end + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local mount = config[item.itemid] + if(mount == nil or getPlayerMount(cid, mount.ID)) then + return false + end + + local rand = math.random(1, 100) + --Monster Mount + if(isMonster(itemEx.uid) and not isSummon(itemEx.uid) and mount.TYPE == TYPE_MONSTER) then + if(mount.NAME == getCreatureName(itemEx.uid)) then + if(rand > mount.CHANCE) then + doFailAction(cid, mount, toPosition, item, itemEx) + return true + end + + doPlayerAddMount(cid, mount.ID) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, mount.SUCCESS_MSG) + + doCreatureSay(cid, mount.SUCCESS_MSG, TALKTYPE_MONSTER_SAY) + doRemoveCreature(itemEx.uid) + + doSendMagicEffect(toPosition, CONST_ME_POFF) + doRemoveItem(item.uid, 1) + return true + end + --NPC Mount + elseif(isNpc(itemEx.uid) and mount.TYPE == TYPE_NPC) then + if(mount.NAME == getCreatureName(itemEx.uid)) then + if(rand > mount.CHANCE) then + doFailAction(cid, mount, toPosition, item, itemEx) + return true + end + + doPlayerAddMount(cid, mount.ID) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, mount.SUCCESS_MSG) + + doCreatureSay(cid, mount.SUCCESS_MSG, TALKTYPE_MONSTER_SAY) + + doSendMagicEffect(toPosition, CONST_ME_MAGIC_GREEN) + doRemoveItem(item.uid, 1) + return true + end + --Action Mount + elseif(itemEx.actionid > 0 and mount.TYPE == TYPE_ACTION) then + if(mount.NAME == itemEx.actionid) then + if(rand > mount.CHANCE) then + doFailAction(cid, mount, toPosition, item, itemEx) + return true + end + + doPlayerAddMount(cid, mount.ID) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, mount.SUCCESS_MSG) + + doCreatureSay(cid, mount.SUCCESS_MSG, TALKTYPE_MONSTER_SAY) + doSendMagicEffect(toPosition, CONST_ME_MAGIC_GREEN) + + doRemoveItem(item.uid, 1) + return true + end + --Unique Mount + elseif(itemEx.uid <= 65535 and mount.TYPE == TYPE_UNIQUE) then + if(mount.NAME == itemEx.uid) then + if(rand > mount.CHANCE) then + doFailAction(cid, mount, toPosition, item, itemEx) + return true + end + + doPlayerAddMount(cid, mount.ID) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, mount.SUCCESS_MSG) + + doCreatureSay(cid, mount.SUCCESS_MSG, TALKTYPE_MONSTER_SAY) + doSendMagicEffect(toPosition, CONST_ME_MAGIC_GREEN) + + doRemoveItem(item.uid, 1) + return true + end + end + + return false +end + diff --git a/data/actions/scripts/other/teleport.lua b/data/actions/scripts/other/teleport.lua index 146edac..77e8bea 100644 --- a/data/actions/scripts/other/teleport.lua +++ b/data/actions/scripts/other/teleport.lua @@ -1,4 +1,5 @@ -local UP_FLOORS = {1386, 3678, 5543, 8599, 10035} +local UP_FLOORS = {1386, 3678, 5543, 8599, 10035, 13010} +local FIELDS = {1497, 1499, 11095, 11096} local DRAW_WELL = 1369 function onUse(cid, item, fromPosition, itemEx, toPosition) @@ -6,19 +7,28 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) return false end + local check = false fromPosition.stackpos = STACKPOS_GROUND if(isInArray(UP_FLOORS, item.itemid)) then fromPosition.z = fromPosition.z - 1 fromPosition.y = fromPosition.y + 1 - if(doTileQueryAdd(cid, fromPosition, 4, false) ~= RETURNVALUE_NOERROR) then - fromPosition.y = fromPosition.y - 2 + if(doTileQueryAdd(cid, fromPosition, 38) ~= RETURNVALUE_NOERROR) then + local field = getTileItemByType(fromPosition, ITEM_TYPE_MAGICFIELD) + if(field.uid == 0 or not isInArray(FIELDS, field.itemid)) then + fromPosition.y = fromPosition.y - 2 + else + check = true + end end else fromPosition.z = fromPosition.z + 1 end - if(doTileQueryAdd(cid, fromPosition, 4, false) ~= RETURNVALUE_NOERROR) then - return false + if(not check and doTileQueryAdd(cid, fromPosition, 38) ~= RETURNVALUE_NOERROR) then + local field = getTileItemByType(fromPosition, ITEM_TYPE_MAGICFIELD) + if(field.uid == 0 or not isInArray(FIELDS, field.itemid)) then + return false + end end local pos, dir = getCreaturePosition(cid), SOUTH diff --git a/data/actions/scripts/other/trap.lua b/data/actions/scripts/other/trap.lua deleted file mode 100644 index 37e1994..0000000 --- a/data/actions/scripts/other/trap.lua +++ /dev/null @@ -1,5 +0,0 @@ -function onUse(cid, item, fromPosition, itemEx, toPosition) - doTransformItem(item.uid, item.itemid - 1) - doSendMagicEffect(fromPosition, CONST_ME_POFF) - return true -end diff --git a/data/actions/scripts/other/watch.lua b/data/actions/scripts/other/watch.lua index d7029f5..5109df6 100644 --- a/data/actions/scripts/other/watch.lua +++ b/data/actions/scripts/other/watch.lua @@ -4,28 +4,16 @@ local config = { } function onUse(cid, item, fromPosition, itemEx, toPosition) - local _time = "" + local str = "" if(config.tibianTime) then - local varh = (os.date('%M') * 60 + os.date('%S')) / 150 - local tibH = math.floor(varh) - local tibM = math.floor(60 * (varh - tibH)) - - if(tibH < 10) then - tibH = '0' .. tibH - end - if(tibM < 10) then - tibM = '0' .. tibM - end - - _time = tibH .. ':' .. tibM + local var = getTibiaTime() + str = var.hours .. ':' .. var.minutes + elseif(config.twentyFour) then + str = os.date('%H:%M') else - if(config.twentyFour) then - _time = os.date('%H:%M') - else - _time = os.date('%I:%M %p') - end + str = os.date('%I:%M %p') end - doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "The time is " .. _time .. ".") + doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "The time is " .. str .. ".") return true end diff --git a/data/actions/scripts/other/windows.lua b/data/actions/scripts/other/windows.lua index f077790..e10ddb3 100644 --- a/data/actions/scripts/other/windows.lua +++ b/data/actions/scripts/other/windows.lua @@ -1,10 +1,12 @@ local WINDOWS = { + [5303] = 6448, [5304] = 6449, [6438] = 6436, [6436] = 6438, [6439] = 6437, [6437] = 6439, [6442] = 6440, [6440] = 6442, [6443] = 6441, [6441] = 6443, [6446] = 6444, [6444] = 6446, [6447] = 6445, [6445] = 6447, + [6448] = 5303, [6449] = 5304, [6452] = 6450, [6450] = 6452, [6453] = 6451, [6451] = 6453, [6456] = 6454, [6454] = 6456, @@ -31,7 +33,22 @@ local WINDOWS = { function onUse(cid, item, fromPosition, itemEx, toPosition) local window = WINDOWS[item.itemid] - if(not window or (not getHouseFromPos(getCreaturePosition(cid)) and not getPlayerCustomFlagValue(cid, PLAYERCUSTOMFLAG_GAMEMASTERPRIVILEGES))) then + if(not window) then + return false + end + + local house, position = getHouseFromPos(fromPosition), fromPosition + if(not house) then + position.y = position.y - 1 + house = getHouseFromPos(position) + if(not house) then + position.y = position.y + 1 + position.x = position.x - 1 + house = getHouseFromPos(position) + end + end + + if(house and getHouseFromPos(getThingPosition(cid)) ~= house and not getPlayerCustomFlagValue(cid, PLAYERCUSTOMFLAG_GAMEMASTERPRIVILEGES)) then return false end diff --git a/data/actions/scripts/quests/annihilator.lua b/data/actions/scripts/quests/annihilator.lua index 44eb2c2..0d9d4b2 100644 --- a/data/actions/scripts/quests/annihilator.lua +++ b/data/actions/scripts/quests/annihilator.lua @@ -1,7 +1,7 @@ local config = { daily = "no", -- allow only one enter per day? (like in global Tibia) level = 100, - storage = 30015 + storage = 30015, entry = { {x = 247, y = 659, z = 13}, diff --git a/data/actions/scripts/quests/system.lua b/data/actions/scripts/quests/system.lua index 986efa9..e797a93 100644 --- a/data/actions/scripts/quests/system.lua +++ b/data/actions/scripts/quests/system.lua @@ -20,7 +20,7 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) end end - if(getPlayerStorageValue(cid, storage) > 0) then + if(getCreatureStorage(cid, storage) > 0) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "It is empty.") return true end @@ -93,10 +93,9 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) result = "You have found a reward weighing " .. getItemWeight(reward.uid) .. " oz. It is too heavy or you have not enough space." else result = "You have found " .. result .. "." - setPlayerStorageValue(cid, storage, 1) + doCreatureSetStorage(cid, storage, 1) if(questsExperience[storage] ~= nil) then - doPlayerAddExp(cid, questsExperience[storage]) - doSendAnimatedText(getCreaturePosition(cid), questsExperience[storage], TEXTCOLOR_WHITE) + doPlayerAddExpEx(cid, questsExperience[storage]) end end diff --git a/data/actions/scripts/tools/blessed_wooden_stake.lua b/data/actions/scripts/tools/blessed_wooden_stake.lua deleted file mode 100644 index cf6116f..0000000 --- a/data/actions/scripts/tools/blessed_wooden_stake.lua +++ /dev/null @@ -1,38 +0,0 @@ -local config = { - level = 2 -} - -local DUSTS = { - -- Demons - [2956] = {25000, 5905}, - - -- Vampires - [2916] = {25000, 5906} -} - -function onUse(cid, item, fromPosition, itemEx, toPosition) - if(getPlayerLevel(cid) < config.level) then - doPlayerSendCancel(cid, "You have to be at least Level " .. config.level .. " to use this tool.") - return true - end - - local dust = DUSTS[itemEx.itemid] - if(not dust) then - doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - return true - end - - local random = math.random(1, 100000) - if(random <= dust[1]) then - doSendMagicEffect(toPosition, CONST_ME_GROUNDSHAKER) - doPlayerAddItem(cid, dust[2], 1) - elseif(dust[3] and random >= dust[3]) then - doSendMagicEffect(toPosition, CONST_ME_GROUNDSHAKER) - doPlayerAddItem(cid, dust[4], 1) - else - doSendMagicEffect(toPosition, CONST_ME_POFF) - end - - doTransformItem(itemEx.uid, itemEx.itemid + 1) - return true -end diff --git a/data/actions/scripts/tools/fishing.lua b/data/actions/scripts/tools/fishing.lua index 07cf126..b137fa8 100644 --- a/data/actions/scripts/tools/fishing.lua +++ b/data/actions/scripts/tools/fishing.lua @@ -5,10 +5,48 @@ local config = { holes = {7236}, corpses = { - -- [corpse] = {items} - [2025] = { - -- {itemid, countmax, chance} - -- TODO: Water elemental and Massive Water Elemental loot... + -- [corpse] = {[aid] = { {itemid, countmax, chance} }} + [10499] = { + [101] = { + {2226, 1, 16000}, --fish bone + {2238, 1, 15000}, --leather boots + {2148, 1, 15000}, --gold coin + {2376, 1, 14000}, --sword + {2152, 1, 13000}, --platinum + {7589, 1, 13000}, --strong mana potion + {7588, 1, 13000}, --strong health potion + {2168, 1, 11500}, --life ring + {2167, 1, 15000}, --energy ring + {9810, 1, 9500}, --rusty armor + {9813, 1, 9500}, --rusty legs + {7632, 1, 8600}, --giant shimmering pearl + {7633, 1, 8600}, --giant shimmering pearl + {7158, 1, 3100}, --rainbow trout + {7159, 1, 3100}, --green perch + {2146, 1, 11500}, --small sapphire + {2149, 2, 11500}, --small emerald + {10220, 1, 1500} --leviathan's amulet + }, + [102] = { + {2226, 1, 16000}, --fish bone + {2238, 1, 15000}, --leather boots + {2148, 1, 15000}, --gold coin + {2376, 1, 14000}, --sword + {2152, 1, 14000}, --platinum + {7589, 1, 14000}, --strong mana potion + {7588, 1, 14000}, --strong health potion + {2168, 1, 15500}, --life ring + {2167, 1, 16000}, --energy ring + {9810, 1, 11500}, --rusty armor + {9813, 1, 11500}, --rusty legs + {7632, 1, 9600}, --giant shimmering pearl + {7633, 1, 9600}, --giant shimmering pearl + {7158, 1, 5100}, --rainbow trout + {7159, 1, 5100}, --green perch + {2146, 1, 13500}, --small sapphire + {2149, 2, 13500}, --small emerald + {10220, 1, 2500} --leviathan's amulet + } } }, checkCorpseOwner = getConfigValue("checkCorpseOwner"), @@ -39,29 +77,42 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) end local corpse = config.corpses[itemEx.itemid] - if(corpse ~= nil) then - local owner = getItemAttribute(cid, "corpseowner") - if(owner ~= 0 and owner ~= getPlayerGUID(cid) and config.checkCorpseOwner) then - doPlayerSendDefaultCancel(cid, RETURNVALUE_YOUARENOTTHEOWNER) - return true - end + if(corpse ~= nil and corpse ~= 0) then + corpse = corpse[itemEx.actionid] + if(corpse ~= nil and corpse ~= 0) then + if(config.checkCorpseOwner and not getPlayerCustomFlagValue(cid, PLAYERCUSTOMFLAG_GAMEMASTERPRIVILEGES)) then + local owner = getItemAttribute(itemEx.uid, "corpseowner") + if(owner ~= 0 and owner ~= nil and owner ~= getPlayerGUID(cid)) then + doPlayerSendDefaultCancel(cid, RETURNVALUE_YOUARENOTTHEOWNER) + return true + end + end - local chance, items = math.random(0, 100000) / config.rateLoot, {} - for _, data in ipairs(corpse) do - if(data[3] >= chance) then - local tmp = {data[1], math.random(1, data[2])} - table.insert(items, tmp) + local chance, items, default, max = math.random(0, 100000) / config.rateLoot, {}, {}, 0 + for _, data in ipairs(corpse) do + if(data[3] >= chance) then + local tmp = {data[1], math.random(1, data[2])} + table.insert(items, tmp) + end + if(data[3] > max) then + default = data + max = data[3] + end end - end - local itemCount = table.maxn(items) - if(itemCount > 0) then - local loot = items[math.random(1, itemCount)] - doPlayerAddItem(cid, loot[1], loot[2]) - end + local itemCount = table.maxn(items) + if(itemCount > 0) then + local loot = items[math.random(1, itemCount)] + doPlayerAddItem(cid, loot[1], loot[2]) + else + doPlayerAddItem(cid, default[1], default[2]) + end - doTransformItem(itemEx.uid, itemEx.uid + 1) - return true + doTransformItem(itemEx.uid, getItemInfo(itemEx.itemid).decayTo) + doSendMagicEffect(toPosition, CONST_ME_WATERSPLASH) + doDecayItem(itemEx.uid) + return true + end end if(not isInArray(config.fishable, itemEx.itemid)) then diff --git a/data/actions/scripts/tools/machete.lua b/data/actions/scripts/tools/machete.lua index d5ba27b..fee9f7a 100644 --- a/data/actions/scripts/tools/machete.lua +++ b/data/actions/scripts/tools/machete.lua @@ -1,24 +1,3 @@ -local JUNGLE_GRASS = {2782, 3985} -local SPIDER_WEB = {7538, 7539} - function onUse(cid, item, fromPosition, itemEx, toPosition) - if(isInArray(JUNGLE_GRASS, itemEx.itemid)) then - doTransformItem(itemEx.uid, itemEx.itemid - 1) - doDecayItem(itemEx.uid) - return true - end - - if(isInArray(SPIDER_WEB, itemEx.itemid)) then - doTransformItem(itemEx.uid, (itemEx.itemid + 6)) - doDecayItem(itemEx.uid) - return true - end - - if(itemEx.itemid == 1499) then - doSendMagicEffect(toPosition, CONST_ME_POFF) - doRemoveItem(itemEx.uid) - return true - end - - return destroyItem(cid, itemEx, toPosition) + return TOOLS.MACHETE(cid, item, fromPosition, itemEx, toPosition, true) end diff --git a/data/actions/scripts/tools/obsidian_knife.lua b/data/actions/scripts/tools/obsidian_knife.lua deleted file mode 100644 index 016714b..0000000 --- a/data/actions/scripts/tools/obsidian_knife.lua +++ /dev/null @@ -1,56 +0,0 @@ -local config = { - level = 2 -} - -local SKINS = { - -- Minotaurs - [2830] = {25000, 5878}, - [2871] = {25000, 5878}, - [2866] = {25000, 5878}, - [2876] = {25000, 5878}, - [3090] = {25000, 5878}, - - -- Lizards - [4259] = {25000, 5876}, - [4262] = {25000, 5876}, - [4256] = {25000, 5876}, - - -- Dragons - [3104] = {25000, 5877}, - [2844] = {25000, 5877}, - - -- Dragon Lords - [2881] = {25000, 5948}, - - -- Behemoths - [2931] = {25000, 5893}, - - -- Bone Beasts - [3031] = {25000, 5925} -} - -function onUse(cid, item, fromPosition, itemEx, toPosition) - if(getPlayerLevel(cid) < config.level) then - doPlayerSendCancel(cid, "You have to be at least Level " .. config.level .. " to use this tool.") - return true - end - - local skin = SKINS[itemEx.itemid] - if(not skin) then - doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - return true - end - - local random, effect = math.random(1, 100000), CONST_ME_GROUNDSHAKER - if(random <= skin[1]) then - doPlayerAddItem(cid, skin[2], 1) - elseif(skin[3] and random >= skin[3]) then - doPlayerAddItem(cid, skin[4], 1) - else - effect = CONST_ME_POFF - end - - doSendMagicEffect(toPosition, effect) - doTransformItem(itemEx.uid, itemEx.itemid + 1) - return true -end diff --git a/data/actions/scripts/tools/pick.lua b/data/actions/scripts/tools/pick.lua index 6e00bbd..dc5fca1 100644 --- a/data/actions/scripts/tools/pick.lua +++ b/data/actions/scripts/tools/pick.lua @@ -1,16 +1,3 @@ -function onUse(cid, item, fromPosition, itemEx, toPosition) - if((itemEx.uid <= 65535 or itemEx.actionid > 0) and isInArray({354, 355}, itemEx.itemid)) then - doTransformItem(itemEx.uid, 392) - doDecayItem(itemEx.uid) - doSendMagicEffect(toPosition, CONST_ME_POFF) - return true - end - - if(itemEx.itemid == 7200) then - doTransformItem(itemEx.uid, 7236) - doSendMagicEffect(toPosition, CONST_ME_BLOCKHIT) - return true - end - - return false +function onUse(...) + return TOOLS.PICK(...) end diff --git a/data/actions/scripts/tools/rope.lua b/data/actions/scripts/tools/rope.lua index 53b9908..74b8b93 100644 --- a/data/actions/scripts/tools/rope.lua +++ b/data/actions/scripts/tools/rope.lua @@ -1,36 +1,3 @@ -local spotId = {384, 418, 8278, 8592} -local holeId = { - 294, 369, 370, 383, 392, - 408, 409, 427, 428, 430, - 462, 469, 470, 482, 484, - 485, 489, 924, 3135, 3136, - 7933, 7938, 8170, 8286, 8285, - 8284, 8281, 8280, 8279, 8277, - 8276, 8323, 8380, 8567, 8585, - 8596, 8595, 8249, 8250, 8251, - 8252, 8253, 8254, 8255, 8256, - 8972, 9606, 9625 -} - -function onUse(cid, item, fromPosition, itemEx, toPosition) - if(toPosition.x == CONTAINER_POSITION) then - doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - return true - end - - local itemGround = getThingFromPos(toPosition) - if(isInArray(spotId, itemGround.itemid)) then - doTeleportThing(cid, {x = toPosition.x, y = toPosition.y + 1, z = toPosition.z - 1}, false) - elseif(isInArray(holeId, itemEx.itemid)) then - local hole = getThingFromPos({x = toPosition.x, y = toPosition.y, z = toPosition.z + 1, stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE}) - if(hole.itemid > 0) then - doTeleportThing(hole.uid, {x = toPosition.x, y = toPosition.y + 1, z = toPosition.z}, false) - else - doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - end - else - return false - end - - return true +function onUse(...) + return TOOLS.ROPE(...) end diff --git a/data/actions/scripts/tools/scythe.lua b/data/actions/scripts/tools/scythe.lua index 4da5925..fb0f524 100644 --- a/data/actions/scripts/tools/scythe.lua +++ b/data/actions/scripts/tools/scythe.lua @@ -1,9 +1,3 @@ function onUse(cid, item, fromPosition, itemEx, toPosition) - if(itemEx.itemid == 2739) then - doTransformItem(itemEx.uid, 2737) - doCreateItem(2694, 1, toPosition) - doDecayItem(itemEx.uid) - return true - end - return destroyItem(cid, itemEx, toPosition) + return TOOLS.SCYTHE(cid, item, fromPosition, itemEx, toPosition, true) end diff --git a/data/actions/scripts/tools/shovel.lua b/data/actions/scripts/tools/shovel.lua index b906144..e4c6444 100644 --- a/data/actions/scripts/tools/shovel.lua +++ b/data/actions/scripts/tools/shovel.lua @@ -1,28 +1,3 @@ -local holes = {468, 481, 483, 7932, 8579} -local sand = {231, 9059} - -function onUse(cid, item, fromPosition, itemEx, toPosition) - if(isInArray(holes, itemEx.itemid)) then - local newId = itemEx.itemid + 1 - if(itemEx.itemid == 8579) then - newId = 8585 - end - - doTransformItem(itemEx.uid, newId) - doDecayItem(itemEx.uid) - elseif(isInArray(sand, itemEx.itemid)) then - local rand = math.random(1, 100) - if(itemEx.actionid == 100 and rand <= 20) then - doTransformItem(itemEx.uid, 489) - doDecayItem(itemEx.uid) - elseif(rand >= 1 and rand <= 5) then - doCreateItem(2159, 1, toPosition) - elseif(rand > 85) then - doCreateMonster("Scarab", toPosition, false) - end - - doSendMagicEffect(toPosition, CONST_ME_POFF) - end - - return true +function onUse(...) + return TOOLS.SHOVEL(...) end diff --git a/data/actions/scripts/tools/skinning.lua b/data/actions/scripts/tools/skinning.lua new file mode 100644 index 0000000..4cb2b94 --- /dev/null +++ b/data/actions/scripts/tools/skinning.lua @@ -0,0 +1,80 @@ +local SKINS = { + [5908] = { + -- Minotaurs + [2830] = {25000, 5878}, + [2871] = {25000, 5878}, + [2866] = {25000, 5878}, + [2876] = {25000, 5878}, + [3090] = {25000, 5878}, + + -- Low Class Lizards + [4259] = {25000, 5876}, + [4262] = {25000, 5876}, + [4256] = {25000, 5876}, + + -- High Class Lizards + [11288] = {25000, 5876}, + [11280] = {25000, 5876}, + [11272] = {25000, 5876}, + [11284] = {25000, 5876}, + + -- Dragons + [3104] = {25000, 5877}, + [2844] = {25000, 5877}, + + -- Dragon Lords + [2881] = {25000, 5948}, + + -- Behemoths + [2931] = { { 10000, 5930 }, { 35000, 5893 } }, + + -- Bone Beasts + [3031] = {25000, 5925}, + + -- The Mutated Pumpkin + [8961] = { { 5000, 7487 }, { 10000, 7737 }, { 20000, 6492 }, { 30000, 8860 }, { 45000, 2683 }, { 60000, 2096 }, { 90000, 9005, 50 } } + }, + [5942] = { + -- Demon + [2916] = {25000, 5906}, + + -- Vampire + [2956] = {25000, 5905} + } +} + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local skin = SKINS[item.itemid][itemEx.itemid] + if(skin == nil or getItemAttribute(itemEx.uid, "summon") ~= nil) then + doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) + return true + end + + local random, effect, transform = math.random(1, 100000), CONST_ME_MAGIC_GREEN, true + if(type(skin[1]) == 'table') then + local added = false + for _, _skin in ipairs(skin) do + if(random <= _skin[1]) then + doPlayerAddItem(cid, _skin[2], _skin[3] or 1) + added = true + break + end + end + + if(not added and itemEx.itemid == 8961) then + effect = CONST_ME_POFF + transform = false + end + elseif(random <= skin[1]) then + doPlayerAddItem(cid, skin[2], skin[3] or 1) + else + effect = CONST_ME_POFF + end + + doSendMagicEffect(toPosition, effect) + if(transform) then + doTransformItem(itemEx.uid, itemEx.itemid + 1) + end + + return true +end diff --git a/data/actions/scripts/tools/squeeze.lua b/data/actions/scripts/tools/squeeze.lua new file mode 100644 index 0000000..8402031 --- /dev/null +++ b/data/actions/scripts/tools/squeeze.lua @@ -0,0 +1,52 @@ +local config = { + functions = { + [10511] = { -- sneaky stabber of eliteness + TOOLS.ROPE, + TOOLS.SHOVEL, + TOOLS.PICK, + TOOLS.MACHETE, + TOOLS.KNIFE + }, + [10513] = { -- squeezing gear of girlpower + TOOLS.ROPE, + TOOLS.SHOVEL, + TOOLS.PICK, + TOOLS.MACHETE, + TOOLS.SCYTHE + }, + [10515] = { -- whacking driller of fate + TOOLS.ROPE, + TOOLS.SHOVEL, + TOOLS.PICK, + TOOLS.MACHETE, + TOOLS.KNIFE + } + }, + jamChance = 10 +} + +function onUse(cid, item, fromPosition, itemEx, toPosition) + local funcs = config.functions[item.itemid] + if(funcs == nil) then + return false + end + + local result = false + for _, func in ipairs(funcs) do + if(func(cid, item, fromPosition, itemEx, toPosition)) then + result = true + break + end + end + + if(not result) then + return false + end + + if(math.random(1, 100) <= config.jamChance) then + doTransformItem(item.uid, item.itemid + 1) + doDecayItem(item.uid) + end + + return true +end \ No newline at end of file diff --git a/data/creaturescripts/creaturescripts.xml b/data/creaturescripts/creaturescripts.xml index f0d117b..363c62b 100644 --- a/data/creaturescripts/creaturescripts.xml +++ b/data/creaturescripts/creaturescripts.xml @@ -2,9 +2,14 @@ - - - + + + + + + + + diff --git a/data/creaturescripts/lib/creaturescripts.lua b/data/creaturescripts/lib/creaturescripts.lua deleted file mode 100644 index 6116bcc..0000000 --- a/data/creaturescripts/lib/creaturescripts.lua +++ /dev/null @@ -1 +0,0 @@ --- empty file -- diff --git a/data/creaturescripts/scripts/advancesave.lua b/data/creaturescripts/scripts/advancesave.lua index 3794f14..d2dfbf3 100644 --- a/data/creaturescripts/scripts/advancesave.lua +++ b/data/creaturescripts/scripts/advancesave.lua @@ -1,9 +1,19 @@ local config = { - savePlayersOnAdvance = true + savePlayer = true, + healPlayerOnLevel = true } function onAdvance(cid, skill, oldLevel, newLevel) - if(config.savePlayersOnAdvance) then + if(skill == SKILL__EXPERIENCE) then + return true + end + + if(skill == SKILL__LEVEL and config.healPlayerOnLevel) then + doCreatureAddHealth(cid, getCreatureMaxHealth(cid) - getCreatureHealth(cid)) + doCreatureAddMana(cid, getCreatureMaxMana(cid) - getCreatureMana(cid)) + end + + if(config.savePlayer) then doPlayerSave(cid, true) end diff --git a/data/creaturescripts/scripts/ban/action.lua b/data/creaturescripts/scripts/ban/action.lua new file mode 100644 index 0000000..93c2534 --- /dev/null +++ b/data/creaturescripts/scripts/ban/action.lua @@ -0,0 +1,32 @@ +local ACCESS = { + [1] = { 8 }, + [2] = { 1, 2, 4, 5, 7, 9 }, + [3] = { 1, 2, 3, 4, 5, 6, 7, 9 } +} + +function onChannelRequest(cid, channel, custom) + unregisterCreatureEvent(cid, "Ban_Action") + if(not custom or type(channel) ~= 'number') then + doPlayerSendCancel(cid, "Invalid action.") + return false + end + + if(not isInArray(ACCESS[getPlayerAccess(cid)], channel)) then + doPlayerSendCancel(cid, "You cannot do this action.") + return false + end + + local output = "Name:\n\nComment:\n" + if(isInArray({1, 5}, channel)) then + output = "Name:\n\n(Optional) Length:\n\nComment:\n" + end + + doShowTextDialog(cid, 2599, output, true, 1024) + doCreatureSetStorage(cid, "banConfig", table.serialize({ + type = (channel > 4 and 2 or 1), + subType = channel + })) + + registerCreatureEvent(cid, "Ban_Finish") + return false +end \ No newline at end of file diff --git a/data/creaturescripts/scripts/ban/finish.lua b/data/creaturescripts/scripts/ban/finish.lua new file mode 100644 index 0000000..0677850 --- /dev/null +++ b/data/creaturescripts/scripts/ban/finish.lua @@ -0,0 +1,236 @@ +local config = { + banLength = getConfigValue('banLength'), + finalBanLength = getConfigValue('finalBanLength'), + ipBanLength = getConfigValue('ipBanLength'), + notationsToBan = getConfigValue('notationsToBan'), + warningsToFinalBan = getConfigValue('warningsToFinalBan'), + warningsToDeletion = getConfigValue('warningsToDeletion') +} + +function onTextEdit(cid, item, text) + unregisterCreatureEvent(cid, "Ban_Finish") + if(item.itemid ~= 2599) then + return true + end + + local data = table.unserialize(getCreatureStorage(cid, "banConfig")) + if(not data.type) then + return true + end + + if(text:len() == 0) then + return false + end + + text = text:explode("\n") + if(not data.subType or isInArray({1, 5}, data.subType)) then + if(text[1] ~= "Name:" or text[3] ~= "(Optional) Length:" or text[5] ~= "Comment:") then + doPlayerSendCancel(cid, "Invalid format.") + return false + end + + local size = table.maxn(text) + if(size > 6) then + data.comment = "" + for i = 6, size do + data.comment = data.comment .. text[i] .. "\n" + end + else + data.comment = text[6] + end + + if(text[4]:len() > 0) then + data.length = loadstring("return " .. text[4])() + end + elseif(text[1] ~= "Name:" or text[3] ~= "Comment:") then + doPlayerSendCancel(cid, "Invalid format.") + return false + else + data.comment = text[4] + end + + data.name = text[2] + if(data.type == 1) then + errors(false) + local player = getPlayerGUIDByName(data.name, true) + + errors(true) + if(not player) then + doPlayerSendCancel(cid, "Player not found.") + return false + end + + local account = getAccountIdByName(data.name) + if(account == 0 or getAccountFlagValue(cid, PLAYERFLAG_CANNOTBEBANNED)) then + doPlayerSendCancel(cid, "You cannot take action on this player.") + return false + end + + local warnings, warning = getAccountWarnings(account), 1 + if(data.subType == 1) then + if(not tonumber(data.length)) then + data.length = os.time() + config.banLength + if((warnings + 1) >= config.warningsToDeletion) then + data.length = -1 + elseif((warnings + 1) >= config.warningsToFinalBan) then + data.length = os.time() + config.finalBanLength + end + else + data.length = os.time() + data.length + end + + doAddAccountBanishment(account, player, data.length, data.comment, getPlayerGUID(cid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " (warnings: " .. (warnings + 1) .. ") has been banned.") + elseif(data.subType == 2) then + doAddAccountBanishment(account, player, config.finalBanLength, data.comment, getPlayerGUID(cid)) + if(warnings < config.warningsToFinalBan) then + warning = config.warningsToFinalBan - warnings + end + + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " (warnings: " .. warning .. ") has been banned.") + elseif(data.subType == 3) then + doAddAccountBanishment(account, player, -1, data.comment, getPlayerGUID(cid)) + if(warnings < config.warningsToDeletion) then + warning = config.warningsToDeletion - warnings + end + + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " (warnings: " .. warning .. ") has been deleted.") + elseif(data.subType == 4) then + local notations = getNotationsCount(account) + 1 + if(notations >= config.notationsToBan) then + data.length = os.time() + config.banLength + if((warnings + 1) >= config.warningsToDeletion) then + data.length = -1 + elseif((warnings + 1) >= config.warningsToFinalBan) then + data.length = os.time() + config.finalBanLength + end + + doAddAccountBanishment(account, player, data.length, data.comment, getPlayerGUID(cid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " (warnings: " .. (warnings + 1) .. ") has been banned reaching notations limit.") + else + doAddNotation(account, player, data.comment, getPlayerGUID(cid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " (account notations: " .. notations .. ") has been noted.") + warning = 0 + end + end + + if(warning > 0) then + doAddAccountWarnings(account, warning) + doRemoveNotations(account) + + local pid = getPlayerByGUID(player) + if(pid) then + doPlayerSendTextMessage(pid, MESSAGE_STATUS_WARNING, "You have been banned.") + doSendMagicEffect(getThingPosition(pid), CONST_ME_MAGIC_GREEN) + addEvent(valid(doRemoveCreature), 1000, pid, true) + end + end + elseif(data.type == 2) then + errors(false) + local player = getPlayerGUIDByName(data.name, true) + + errors(true) + if(not player) then + doPlayerSendCancel(cid, "Player not found.") + return false + end + + local account = getAccountIdByName(data.name) + if(account == 0 or getAccountFlagValue(account, PLAYERFLAG_CANNOTBEBANNED)) then + doPlayerSendCancel(cid, "You cannot take action on this player.") + return false + end + + data.subType = data.subType - 4 + if(data.subType == 1) then + if(not tonumber(data.length)) then + local warnings = getAccountWarnings(account) + 1 + data.length = os.time() + config.banLength + if(warnings >= config.warningsToDeletion) then + data.length = -1 + elseif(warnings >= config.warningsToFinalBan) then + data.length = os.time() + config.finalBanLength + end + else + data.length = os.time() + data.length + end + + doAddPlayerBanishment(data.name, 3, data.length, data.comment, getPlayerGUID(cid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " has been banned.") + + local pid = getPlayerByGUID(player) + if(pid) then + doPlayerSendTextMessage(pid, MESSAGE_STATUS_WARNING, "You have been banned.") + doSendMagicEffect(getThingPosition(pid), CONST_ME_MAGIC_GREEN) + addEvent(valid(doRemoveCreature), 1000, pid, true) + end + elseif(data.subType == 2) then + doAddPlayerBanishment(data.name, 3, -1, data.comment, getPlayerGUID(cid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " has been deleted.") + elseif(data.subType == 3) then + local warnings, notations = getAccountWarnings(account) + 1, getNotationsCount(account, player) + 1 + if(notations >= config.notationsToBan) then + data.length = os.time() + config.banLength + if(warnings >= config.warningsToDeletion) then + data.length = -1 + elseif(warnings >= config.warningsToFinalBan) then + data.length = os.time() + config.finalBanLength + end + + doAddPlayerBanishment(account, 3, data.length, data.comment, getPlayerGUID(cid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " has been banned reaching notations limit.") + + local pid = getPlayerByGUID(player) + if(pid) then + doPlayerSendTextMessage(pid, MESSAGE_STATUS_WARNING, "You have been banned.") + doSendMagicEffect(getThingPosition(pid), CONST_ME_MAGIC_GREEN) + addEvent(valid(doRemoveCreature), 1000, pid, true) + end + else + doAddNotation(account, player, data.comment, getPlayerGUID(cid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " (notations: " .. notations .. ") has been noted.") + end + elseif(data.subType == 4) then + doAddPlayerBanishment(data.name, 1, -1, data.comment, getPlayerGUID(cid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " has been reported.") + elseif(data.subType == 5) then + doAddPlayerBanishment(data.name, 2, -1, data.comment, getPlayerGUID(cid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " has been namelocked.") + + local pid = getPlayerByGUID(player) + if(pid) then + doPlayerSendTextMessage(pid, MESSAGE_STATUS_WARNING, "You have been banned.") + doSendMagicEffect(getThingPosition(pid), CONST_ME_MAGIC_GREEN) + addEvent(valid(doRemoveCreature), 1000, pid, true) + end + end + elseif(data.type == 3) then + local ip = getIpByName(data.name) + if(not ip) then + doPlayerSendCancel(cid, "Player not found.") + return false + end + + local account = getAccountIdByName(data.name) + if(account == 0 or getAccountFlagValue(account, PLAYERFLAG_CANNOTBEBANNED)) then + doPlayerSendCancel(cid, "You cannot take action on this player.") + return false + end + + if(not tonumber(data.length)) then + data.length = config.ipBanLength + end + + doAddIpBanishment(ip, 4294967295, os.time() + data.length, data.comment, getPlayerGUID(cid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_RED, getPlayerNameByGUID(player) .. " has been banned on IP: " .. doConvertIntegerToIp(ip) .. ".") + + local pid = getPlayerByGUID(player) + if(pid) then + doPlayerSendTextMessage(pid, MESSAGE_STATUS_WARNING, "You have been banned.") + doSendMagicEffect(getThingPosition(pid), CONST_ME_MAGIC_GREEN) + addEvent(valid(doRemoveCreature), 1000, pid, true) + end + end + + return false +end diff --git a/data/creaturescripts/scripts/ban/type.lua b/data/creaturescripts/scripts/ban/type.lua new file mode 100644 index 0000000..7430a8a --- /dev/null +++ b/data/creaturescripts/scripts/ban/type.lua @@ -0,0 +1,85 @@ +local TYPES, ACCESS = { + { + event = "Ban_Action", + actions = { + [1] = "Banishment", + [2] = "Banishment + Final Warning", + [3] = "Deletion", + [4] = "Notation" + } + }, + { + event = "Ban_Action", + actions = { + [5] = "Banishment", + [6] = "Deletion", + [7] = "Notation", + [8] = "Report", + [9] = "Lock" + } + }, + { + event = "Ban_Finish" + } +}, +{ + type = { + [1] = { 1 }, + [2] = { 1 }, + [3] = { 1, 2 }, + [4] = { 1, 2 }, + [5] = { 1, 2, 3 } + }, + action = { + [1] = { 8 }, + [2] = { 8 }, + [3] = { 1, 4, 5, 7, 9 }, + [4] = { 1, 2, 4, 5, 7, 9 }, + [5] = { 1, 2, 3, 4, 5, 6, 7, 9 }, + } +} + +function onChannelRequest(cid, channel, custom) + unregisterCreatureEvent(cid, "Ban_Type") + if(not custom or type(channel) ~= 'number') then + doPlayerSendCancel(cid, "Invalid action.") + return false + end + + local type = TYPES[channel] + if(not type) then + doPlayerSendCancel(cid, "Invalid action.") + return false + end + + local access = getPlayerAccess(cid) + if(not isInArray(ACCESS.type[access], channel)) then + doPlayerSendCancel(cid, "You cannot do this action.") + return false + end + + registerCreatureEvent(cid, type.event) + if(type.actions) then + access = ACCESS.action[access] + if(not access or table.maxn(access) == 0) then + return false + end + + local actions = {} + for _, action in ipairs(access) do + local tmp = type.actions[action] + if(tmp) then + actions[action] = tmp + end + end + + doPlayerSendChannels(cid, actions) + else + doShowTextDialog(cid, 2599, "Name:\n\n(Optional) Length:\n\nComment:\n", true, 1024) + doCreatureSetStorage(cid, "banConfig", table.serialize({ + type = channel + })) + end + + return false +end \ No newline at end of file diff --git a/data/creaturescripts/scripts/guild.lua b/data/creaturescripts/scripts/guild.lua new file mode 100644 index 0000000..16b40fb --- /dev/null +++ b/data/creaturescripts/scripts/guild.lua @@ -0,0 +1,31 @@ +function onChannelJoin(cid, channelId, users) + if(channelId ~= CHANNEL_GUILD) then + return true + end + + for _, pid in users do + doPlayerSendChannelMessage(pid, "", "Player " .. getCreatureName(cid) .. " has entered the channel.", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + end + + local guildId = getPlayerGuildId(cid) + if(guildId and guildId ~= 0) then + local guildMotd = getGuildMotd(guildId) + if(guildMotd and guildMotd ~= "") then + addEvent(valid(doPlayerSendChannelMessage), 150, cid, "", "Message of the Day: " .. guildMotd, TALKTYPE_GAMEMASTER_CHANNEL, CHANNEL_GUILD) + end + end + + return true +end + +function onChannelLeave(cid, channelId, users) + if(channelId ~= CHANNEL_GUILD) then + return true + end + + for _, pid in users do + doPlayerSendChannelMessage(pid, "", "Player " .. getCreatureName(cid) .. " has left the channel.", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + end + + return true +end \ No newline at end of file diff --git a/data/creaturescripts/scripts/guildmotd.lua b/data/creaturescripts/scripts/guildmotd.lua deleted file mode 100644 index c09cb61..0000000 --- a/data/creaturescripts/scripts/guildmotd.lua +++ /dev/null @@ -1,13 +0,0 @@ -function onJoinChannel(cid, channelId, users) - if(channelId == CHANNEL_GUILD) then - local guildId = getPlayerGuildId(cid) - if(guildId and guildId ~= 0) then - local guildMotd = getGuildMotd(guildId) - if(guildMotd and guildMotd ~= "") then - addEvent(doPlayerSendChannelMessage, 150, cid, "", "Message of the Day: " .. guildMotd, TALKTYPE_CHANNEL_W, CHANNEL_GUILD) - end - end - end - - return true -end diff --git a/data/creaturescripts/scripts/idle.lua b/data/creaturescripts/scripts/idle.lua index ab683c6..ae8cfb9 100644 --- a/data/creaturescripts/scripts/idle.lua +++ b/data/creaturescripts/scripts/idle.lua @@ -4,7 +4,7 @@ local config = { } function onThink(cid, interval) - if(getTileInfo(getCreaturePosition(cid)).nologout or getCreatureNoMove(cid) or + if(getTileInfo(getCreaturePosition(cid)).noLogout or getCreatureNoMove(cid) or getPlayerCustomFlagValue(cid, PLAYERCUSTOMFLAG_ALLOWIDLE)) then return true end diff --git a/data/creaturescripts/scripts/login.lua b/data/creaturescripts/scripts/login.lua index 90f26fd..97d3ffe 100644 --- a/data/creaturescripts/scripts/login.lua +++ b/data/creaturescripts/scripts/login.lua @@ -22,11 +22,11 @@ function onLogin(cid) doPlayerSendTextMessage(cid, MESSAGE_STATUS_DEFAULT, str) elseif(accountManager == MANAGER_NAMELOCK) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, "Hello, it appears that your character has been namelocked, what would you like as your new name?") + addEvent(valid(doCreatureSay), 500, cid, "Hello, it appears that your character has been locked for name violating rules, what new name would you like to have?", TALKTYPE_PRIVATE_NP, true, cid) elseif(accountManager == MANAGER_ACCOUNT) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, "Hello, type 'account' to manage your account and if you want to start over then type 'cancel'.") + addEvent(valid(doCreatureSay), 500, cid, "Hello, type {account} to manage your account. If you would like to start over, type {cancel} anywhere.", TALKTYPE_PRIVATE_NP, true, cid) else - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, "Hello, type 'account' to create an account or type 'recover' to recover an account.") + addEvent(valid(doCreatureSay), 500, cid, "Hello, type {account} to create an account or {recover} to recover an account.", TALKTYPE_PRIVATE_NP, true, cid) end if(not isPlayerGhost(cid)) then @@ -34,7 +34,7 @@ function onLogin(cid) end registerCreatureEvent(cid, "Mail") - registerCreatureEvent(cid, "GuildMotd") + registerCreatureEvent(cid, "GuildEvents") registerCreatureEvent(cid, "Idle") if(config.useFragHandler) then @@ -42,6 +42,7 @@ function onLogin(cid) end registerCreatureEvent(cid, "ReportBug") + registerCreatureEvent(cid, "ThankYou") registerCreatureEvent(cid, "AdvanceSave") return true end diff --git a/data/creaturescripts/scripts/mail.lua b/data/creaturescripts/scripts/mail.lua index 9a34b71..6413b44 100644 --- a/data/creaturescripts/scripts/mail.lua +++ b/data/creaturescripts/scripts/mail.lua @@ -1,4 +1,4 @@ -function onReceiveMail(cid, sender, item, openBox) +function onMailReceive(cid, target, item, openBox) if(openBox) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "New mail has arrived.") end diff --git a/data/creaturescripts/scripts/thankyou.lua b/data/creaturescripts/scripts/thankyou.lua new file mode 100644 index 0000000..1d9d3dd --- /dev/null +++ b/data/creaturescripts/scripts/thankyou.lua @@ -0,0 +1,4 @@ +function onThankYou(cid, statementId) + -- TODO :) + return true +end \ No newline at end of file diff --git a/data/globalevents/globalevents.xml b/data/globalevents/globalevents.xml index 5faeb7d..e23e1bf 100644 --- a/data/globalevents/globalevents.xml +++ b/data/globalevents/globalevents.xml @@ -1,10 +1,11 @@ - - + + - - + + + - + diff --git a/data/globalevents/lib/globalevents.lua b/data/globalevents/lib/globalevents.lua deleted file mode 100644 index e69de29..0000000 diff --git a/data/globalevents/scripts/clean.lua b/data/globalevents/scripts/clean.lua index ab782c8..5d3a10c 100644 --- a/data/globalevents/scripts/clean.lua +++ b/data/globalevents/scripts/clean.lua @@ -1,11 +1,11 @@ -function executeClean() +function executeClean(interval) doCleanMap() - doBroadcastMessage("Game map cleaned, next clean in 2 hours.") + doBroadcastMessage("Game map cleaned, next clean in " .. table.concat(string.timediff(interval / 1000)) .. ".") return true end -function onThink(interval, lastExecution, thinkInterval) +function onThink(interval) doBroadcastMessage("Game map cleaning within 30 seconds, please pick up your items!") - addEvent(executeClean, 30000) + addEvent(executeClean, 30000, interval) return true end diff --git a/data/globalevents/scripts/init.lua b/data/globalevents/scripts/init.lua new file mode 100644 index 0000000..d857718 --- /dev/null +++ b/data/globalevents/scripts/init.lua @@ -0,0 +1,92 @@ +local config = { + creationTime = 7 * 86400, + checkTime = 7 * 86400, + viceCount = 4, + memberCount = 10 +} + +function onStartup() + local data, time, result = {}, os.time(), db.getResult("SELECT `id`, `ownerid`, `creationdata`, `checkdata` FROM `guilds` WHERE `world_id` = " .. getConfigValue('worldId') .. ";") + if(result:getID() ~= -1) then + repeat + data[result:getDataInt("id")] = {result:getDataInt("ownerid"), result:getDataInt("creationdata"), result:getDataInt("checkdata")} + until not(result:next()) + result:free() + end + + for id, v in ipairs(data) do + local owner, created, check = v[1], v[2], v[3] + if(created < (time - config.creationTime)) then + result = db.getResult("SELECT `id`, `level` FROM `guild_ranks` WHERE `guild_id` = " .. id .. ";") + if(result:getID() ~= -1) then + local rank, ranks = 0, {} + repeat + ranks[result:getDataInt("id")] = result:getDataInt("level") + if(result:getDataInt("level") == 1) then + rank = result:getDataInt("id") + end + until not(result:next()) + result:free() + + local members = {0, 0, 0, 0} + for k, v in ipairs(ranks) do + result = db.getResult("SELECT COUNT(`id`) AS `count` FROM `players` WHERE `rank_id` = " .. k .. ";") + if(result:getID() ~= -1) then + members[v] = members[v] + result:getDataInt("count") + result:free() + end + + if(v == 2) then + result = db.getResult("SELECT `p`.`id` FROM `players` p LEFT JOIN `accounts` a ON `p`.`account_id` = `a`.`id` WHERE `p`.`rank_id` = " .. k .. " AND (`a`.`premdays` = 0 OR (`a`.`lastday` + (`a`.`premdays` * 86400) <= 0));") + if(result:getID() ~= -1) then + local demote = "" + repeat + demote = demote .. result:getDataInt("id") .. "," + members[2] = members[2] - 1 + members[1] = members[1] + 1 + until not(result:next()) + result:free() + + if(demote ~= "" and rank ~= 0) then + db.executeQuery("UPDATE `players` SET `rank_id` = " .. rank .. " WHERE `id` IN (" .. demote:sub(1, -2) .. ");") + end + end + end + end + + for i = 1, 3 do + members[4] = members[4] + members[i] + end + + if(members[2] < config.viceCount or members[4] < config.memberCount) then + if(check == 0) then + db.executeQuery("UPDATE `guilds` SET `checkdata` = " .. (time + config.checkTime) .. " WHERE `id` = " .. id .. ";") + elseif(check < time) then + local tmp = "" + for rank, _ in ipairs(ranks) do + tmp = tmp .. rank .. "," + end + + db.executeQuery("UPDATE `players` SET `rank_id` = 0, `guildnick` = '', `guildjoin` = 0 WHERE `rank_id` IN (" .. tmp:sub(1, -2) .. ");") + db.executeQuery("DELETE FROM `guilds` WHERE `id` = " .. id .. ";") + end + end + end + end + end + + db.executeQuery("UPDATE `players` SET `online` = 0 WHERE `world_id` = " .. getConfigValue('worldId') .. " AND `online` > 0;") + db.executeQuery("UPDATE `bans` SET `active` = 0 WHERE `expires` <= " .. time .. " AND `expires` >= 0 AND `active` = 1") + db.executeQuery("DELETE FROM `guild_wars` WHERE `status` = 0 AND `begin` < " .. (time - 2 * 86400) .. ";") + + db.executeQuery("TRUNCATE TABLE `player_statements`;") + return true +end + +function onGlobalSave() + if(getGameState() ~= GAMESTATE_CLOSING) then + return onStartup() + end + + return true +end diff --git a/data/globalevents/scripts/save.lua b/data/globalevents/scripts/save.lua index 61c35b2..71d9025 100644 --- a/data/globalevents/scripts/save.lua +++ b/data/globalevents/scripts/save.lua @@ -1,35 +1,25 @@ local config = { broadcast = {120, 30}, - shallow = "no", + flags = 13, delay = 120, events = 30 } -config.shallow = getBooleanFromString(config.shallow) - local function executeSave(seconds) if(isInArray(config.broadcast, seconds)) then - local text = "" - if(not config.shallow) then - text = "Full s" - else - text = "S" - end - - text = text .. "erver save within " .. seconds .. " seconds, please mind it may freeze!" - doBroadcastMessage(text) + doBroadcastMessage("Server save within " .. seconds .. " seconds, please mind it may freeze!") end if(seconds > 0) then addEvent(executeSave, config.events * 1000, seconds - config.events) else - doSaveServer(config.shallow) + doSaveServer(config.flags) end end -function onThink(interval, lastExecution, thinkInterval) +function onThink(interval) if(table.maxn(config.broadcast) == 0) then - doSaveServer(config.shallow) + doSaveServer(config.flags) else executeSave(config.delay) end diff --git a/data/globalevents/scripts/start.lua b/data/globalevents/scripts/start.lua deleted file mode 100644 index 2db6372..0000000 --- a/data/globalevents/scripts/start.lua +++ /dev/null @@ -1,4 +0,0 @@ -function onStartup() - db.executeQuery("UPDATE `players` SET `online` = 0 WHERE `world_id` = " .. getConfigValue('worldId') .. ";") - return true -end diff --git a/data/items/items.otb b/data/items/items.otb index bc2e9c0221db5fddde1a8d5ba5f0a6cc70aedb21..76ed62ee3ec3e1fe99b3d0739be32558e4c5b5a1 100644 GIT binary patch delta 302578 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
XqG;APQJ9&-&QY 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 #$N%C}6$p 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 diff --git a/data/items/items.xml b/data/items/items.xml index 85215a7..b2f8c4b 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -1,17378 +1,23520 @@ - + - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - + - + - + - + - + - + - + - + - + - - - + + + - - - + + + - + - + - - - - + + + + - + + - + - - - + + + - + - + - + + - + - + - + - - + + - + - + - + + - - - + + + - + + + + + - - - - - + + + + - - + + - + - + - + + + + - - - - + + + - + - + - + - + - + + + + - - - - + + + + + + - - - - + + + - + - - + + - - - + + + - - - + + + - - - + + + + + + - - + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - + - - - - + + + + - + - - - - - - + + + + + + + + + + + - + + - - - + + + - - + + + - + + - + - - + + + - + + - - + + + - + + - - + + + + - + + + - - + + + + - + + + - - - + + + + - + + + - - - + + + + - + + + - - - + + + - - + + + - + + - - - + + + - - + + + - + + - - + + + - + + - - + + + - + + - - + + + + - + + + - - + + + + - + + + - - - + + + + - + + + - - - + + + + - + + + - - - + + + - - + + + - + + - - - + + + - - + + + - + + - - + + + + - + + + - - + + + + - + + + - - - + + + + - + + + - - - + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + - + - - - - - - - - + + + + + + + + - + - - - + + + - + - + - - - - + + + + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + - + - + - + - + - + - + - + - - + + - - - - + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + - - - - - - - - + + + + + + + + - + - + - + - + - + - + - - - - - - + + + + + + - - + + - - + + - - - + + + + + + + + - - + + - - - - + + + + - - + + - - - + + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + - + - - - + + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - - - + + + + - + - - - - + + + + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + + - + + - + + - - - - - - - - + + + + + + + + + - - + + - + - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + - - + + + - - + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - + - + - + - - + + - - + + - - + + - - + + - + - + - - + + - - + + - - - + + + - - + + - + - - + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - + + - - + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + - + - + - + - + - + - + - - + + - + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + - - + + - - - - + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + - - + + + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + - - - - + + + + + - - - + + + - - + + - - - - + + + + - - - - + + + + + - + - + - - + + - + - + + - - + + - - + + + + + - - + + - - + + - - + + + + + + + + + + + - + - + - + - - + + - - + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + + + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - - + + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + + + + + + + + + + + + + - - - - - - + + + - - - + + + + - + - - + + + - - - + + + + - + - - + + + - - - + + + + - - - + - - + + + - - - + + + + - - + + + - - - + + + + - - + + + - - - + + + + - - - + - - - + + + + + + + + + + + + + + + - - - - - - - - + + + + - - + + + + + + + + + + + + + + - - - - + + + + + + + + - + - + - + - + - + - + - + - + + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + + + + + + + + + + - - - + + + - + - + - + - + - + - - + + - + - + - + - + - - - - - - + + + + + + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - + - - + + - - - + + + - - + + - - + + - - + + - + - - + + - - + + - + - - - + + + - - + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + - + - - + + - - - - - - + + + + + + - - - + + + + - - - - + + + + + - - - - - + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - + - - - - + + + + - + - + - + - - + + - + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - + - + - + - - - - + + + + - - + + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - - + + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + + - - - - - - - - - - + + + + + + + + + + + - - - - - - - + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - - + + + - + - + - + - + - + + - - - + + + - - - + + + - + + - - - + + + - - - + + + - + + - - - + + + - - - + + + - + + - - - + + + - + + - - - + + + - - - + + + - + + - - - + + + - - - + + + - - - + + + - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + + - - - + + + - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + + - - - + + + - + + - - - + + + - - - + + + - + + - - - + + + - + + - - - + + + - - - + + + - + - + - - + + - - + + - - - - - + + + + + - - + + - - - - - + + + + + - + - - + + - - + + - + - + - - + + + - + - + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - - - + + + + - - - - - - + + + + + + - + - + + - + - + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - + - + - - + + - - + + - + - + - + - + - + - + + - - - + + + - + - + - + - + - + - + - + - + - + - - + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - - - - + + + + + + + + - - - - + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - + - - - - - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + - - + + - + - + - + - + - + - + - + - + - + - - + + - - + + - + - - - + + + - - + + - - - + + + - - + + - - - + + + - + - - - - - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - + + + + + + + + + + + - - - + + + - - + + - - - + + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - + + - - + + - - + + - + - - + + - - + + - - - + + + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - - + + - + - - + + - - + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - + + + - - + + - + - - + + - + - + - + - - - - - - - - + + + + + + + + - + - + - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + + + + + + + + - - - - - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - + + + - - + + - - + + - + - - - - - - + + + + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - - + + + + + - - - + + + - - + + - - - - - - + + + + + + - - - - - + + + + + - - - + + + - - + + - - - - - - + + + + + + - - - - - + + + + + - - - - - + + + + + - - + + - - - - - + + + + + - - - - + + + + - - - - + + + + - + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - + + + - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - + + + + - - + + - - - - - - + + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - - - - + + + + + + - - - - - + + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - + + + + - + - - - - + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - + + + + - + - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - + - - - - - - + + + + + + - - - - - + + + + + - - - - + + + + - + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - + + + - + - + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - + - - - - - + + + + + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - - + + + + + + - - - - - + + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - - - - + + + + + + - + - + - + - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - - - + + + + + + - - + + - + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - + - - - + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - + - + - - - - - - - - - - - - - + + + + + + + + + + + + + - + + - + - + + - + + - + - - - - - - - - - + + + + + + + + + - + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - + + + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - - - + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - - + + - - + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - - + + + - - - - - - - - - - + + + + + + + + + + + + + + - + - + - - + + - - + + - + - + - + + + + + + + + + - - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - + - + - + - - - + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - + + + - + - - + + - - - - + + + + - + - - - + + + - - + + - - - + + + - - - + + + - - - + + + - + - + - + - + - + - + - + - - - - + + + - + - + - - - - + + + + - - + + - - - - + + + + - - + + - + - + - + - - - - - + + + - - - - + + + - - - - + + + - - + + - - - - + + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - + - - + + + - - - - + + + + - - - + + + - - + + + - + + - - - + + + - - + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - + - - + + - - - + + + - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - + - - + + - + - - + + - + - - + + - + - - - + + + + + + + - - + + - - + + - + - + - + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - + + + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - + + + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - + + + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - + + + - - - + + + - - + + + - + + - - - + + + - - + + + - + + - - - + + + - - + + + - + + - - - + + + - - + + + - - - - - - - - + + + + + + + + + - + - + - + - - + + - - - + + + - - + + + - + + - - - + + + - - + + + - + + - - + + + - + + - - + + + - + + - - + + + + - + + + - - + + + + - + + + - - - + + + + - + + + - - - + + + + - + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + - - - + + + - - + + - - + + - - + + - + - + - - - - - - - - + + + + + + + + + - + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - + + + - - + + + - + + - - + + + - + + - - + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - + - - - - - + + + + + - - - + + + - - - - + + + + - - + + - + - - + + - + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - - - - - - + + + + + + + + + + - + - + - - + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - + + + + + - - - - + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - + + + + + - - - - + + + + - - - + + + - + - - + + - + - + - + - + - - - - - - + + + + + + - - - - + + + + - - - - + + + + + - + - - + + - + + - + + - - + + - + + - + + - - + + - - - + + + - + + - - - + + + - - + + - + - - - + + + - - + + - - + + - - + + - + - + - - - - - + + + + + - - - - + + + + - - - - - - - - + + + + + + + + + - + + + - + - - + + - + - + - + + + + + - + - - - - + + + + + - - + + - - - - - - + + + + + + + + - + - + + + - + + + - + + + - + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - + - + - + - - + + - + - + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - + + - + - - + + - + - + - - - + + + - + - - + + - - + + - - + + - - - - + + + + - + - - + + - - + + - + + - - + + - + - + - + - + - + - + - + - - - + + + - - + + - + - + - + - + - - - + + + - + - - - + + + - - - + + + - + - + - + - - - - + + + + - - - - + + + + - - - + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - + + + - + - - + + - - + + - - + + - + - - + + - - - + + + - + - - + + - + - - - + + + - - - + + + - + - - + + - - + + - + - - + + + - + - - + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - + - - - + + + - - - + + + - + - + - - + + - - + + - - - - - + + + + + - + - - + + - - + + - - + + - + - - - + + + - - + + + - - + + - - + + - - - + + + - - - + + + - - + + - - + + - - + + - - + + - + + - - - - - + + + - - - - + + + + + - - + + + - + - + - - - + + + - - - - - - - - - - - - + + + + + + + + + + + + - + - - - - + + + + - - - + + + - - + + + - + + - - - + + + - - + + + - + + - - + + + - + + - - + + + - + + - - + + + + - + + + - - + + + + - + + + - - - + + + + - + + + - - - + + + + - - - - - - - - - + + + + + + + + + + + - - - + + + - - + + + - + + - - - + + + - - + + + - + + - - + + + - + + - - + + + - + + - - + + + + - + + + - - + + + + - + + + - - - + + + + - + + + - - - + + + + - - - - - - + + + + + + + + - + - + - + - + - - - - + + + + - - - - - + + + + + - - - - - - - - + + + + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - - - + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - + + - - - - + + + + - - - - - + + + + + - + - + - + - + - + - + - + - + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - + - - - + + + - - - + + - - + + - - - - + + + + - - + + - + - - - - - - - - - + + + + + + + + + - - - - + + + + - - + + - - + + - + - - + + - + - - + + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - - - + + + - - + + - + - - - + + + - - + + - + - + - + - - + + - + - + - + - - + + - - + + - + - - + + - - - - - - - - + + + + + + + + - - - - - + + + + + - - - + + + - - - + + + - - + + - + - + - + - - - - - - + + + + + + - - - - - - - - + + + + + + + + - - + + + - - - + + + - - - - - + + + + + - + - + - + - + - + - - - - + + + + - + - + - + - + - + - + - + - + - + - - - - - + + + + + - - - - - + + + + + - + - - + + - - - - - + + + + + - + - + - + - + - - + + - + - + - + - - - + + + - + - + - + - + - - - + + + - + - + - - - + + + - + - - - + + + - + - - + + - - + + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + + - + - - + + - - + + + - + + - - + + + - + + - - + + + - + + - - + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - + + + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - + + + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + - - + + - + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - + + + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - + + + - + - + - - + + + - + + - - + + + - + + + + + - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + - + - - - + + + - + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + - + - - + + - + - + - + - + - + - + - + - + - - + + - + - - - + + + + + - + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - + - + - + - + - + - + - + - + - + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - + + - - + + - + - + - - + + - + - - + + - - + + + - - + + - - + + - - - - - - - - + + + + + + + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - - + + + - - + + - + - - - - + + + + + - - + + + - + - + - + - - + + + - + - - + + - - + + - - + + + - - + + - - + + - - - + + + + + + + - + - - - + + + + - - - + + + + - + - - + + - - + + - - - - - - - - - - - - + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + - - + + - - + + - - + + - + - + - + - + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - + + + - - - + + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - + + + + + + - - - + + - - + + - - + + - - - - - + + + + + - - - - - + + + + - - - + + + - - - + + + - - + + - + - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - + - - - + + + - + - - + + + + + - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - + + + + - + - - - - + + + + - + - - - + + + - + - - + + - + - + - - - + + + - + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - + + - - + + - - + + - - + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - + + + - - - + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - - + + + + + - + - + - - - + + + - + - + - - - - - + + + + + - - - - - + + + + + - - - + + + + - - - + + + + - - - - - + + + + + - - - - - + + + + + + + + - - - - + + + + + + + + - - - - + - - - + + + + - + - + - - + + - - - + + + - - + + - - + + + + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + - - - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + + - - - + + + - - - + + + - - - + + + - - + + - + - - + + - - - - - - - + + + + + + + + + - + - + - + - - + + - + + + + - - + + - - + + + + + - + - + - - + + + - - - + + + - + - + - - - + + + - + - - - + + + - + - - - - - - - - + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - + + + - - + + - + - + - + - + - + - + - + - + - + - + - - - + + + - + + + + + + - - + - + - + - - + + - + - + - + - + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + + - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + - + + - - - + + + + + + + + + - + + - - + + + - + + - - + + + - + + - - + + + + - + + + - - + + + + - + + + - - - + + + + - + + + - - - + + + + - + + + - + - - - - - - - + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + - - - + + + - + - + - - - - + + + + - - - - - - - - + + + + + + + + - + - + - - - - + + + + - - - - + + + + - - - - - - - - + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + - - - + + + + - - - - + + + + - + + + + - + - - + + - + - - + + - + - + - - + + + + + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + - - + + - - + + - + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - - - - + + + + + + + + + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - + + + - - + + - - + + - - + + - - + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - + - - - - - + + + + + - + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + - - - - - + + + + - - - - - + + + + - - - - + + + - - - - - + + + + - - - - - + + + + - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + - + - + - - + + - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - + + - - + + + + + + + + + + - - - + + - - - - + - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - - - + + + + - + + - - - + + + - + + - - - + + + - + + - - + + + + - + + + - - + + + + - + + + - - - + + + + - + + + - - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - + + + + + + + + + - + + - - - + + + - + + - - - + + + - + + - - + + + + - + + + - - + + + + - + + + - - - + + + + - + + + - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - + + - - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - + - - - - + + + + - - - - + + + + - - - - + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - + - + - + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + - - - + + + - - - - + + + + - - - - - + + + + + - - - - - - + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + + - + - + + - + + - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - + - + - + - + - + - - - - - - - - - + + + + + + + + + + - - - - - - - - + + + + + + + - - + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + + - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - + + + + + + + + + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - + - + - + - + + + + - - + + - - + + + + + + + + + + + + - - - + + + + + - - - + + + + + - - - + + + + + - - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - - - - + + + + + - - - - + + + + - - - + + + - - - - - + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + - - - - + + + + + + + + - - - + + - - + + + - - - + + + + - - - + + - - - - - - + + - - - + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - + - - + + + + + + + + - + + - + + + + - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - - - - - + + + + + + + - + - + + + + - - + + - - + + - + - - - + + + - - + + + - + + - - - + + + - - + + + - + + - - + + + - + + - - + + + - + + - - + + + + - + + + - - + + + + - + + + - - - + + + + - + + + - - - + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - + + + + + + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - - + + + + - + + + - - - + + + - - + + + - + + - - + + + - + + - - - + + + + - + + + - - + + + + - + + + - + - - + + - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - - + - - - + + + + - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - + + + - + - - - - - + + + + + - - + + - - + + + + - - + + + + - - + + + + - - + + + + - - - + + - - - + + + + - - + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - - + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + - - - + + - - - + + + - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + - - + + + + - - - - + + + + - + + + + + + + + + + + + + + + + + + + + - - - + + + + - - + + + + - - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + - - - - + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - - + + + + - - - - - - + + - - - - + + + + - - + + + - - - + + - - - + + - - - + + - - - - - - + + - - - - + + - - + + - - - - + + - - + + - - + + + - - + + - - + + - - + + + - - + + - - + + + - - + + - - + + - - + + - - + + - - - + + - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + - - + + + + - - + + + + - - + + + + - - + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + - - + + - + - - + + - - + + + - - - - + + - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/items/randomization.xml b/data/items/randomization.xml index 0471d5e..6135074 100644 --- a/data/items/randomization.xml +++ b/data/items/randomization.xml @@ -14,7 +14,7 @@ - + diff --git a/data/lib/000-constant.lua b/data/lib/000-constant.lua index e065b15..8917179 100644 --- a/data/lib/000-constant.lua +++ b/data/lib/000-constant.lua @@ -57,6 +57,7 @@ CONDITION_PARAM_SKILL_FISHINGPERCENT = 42 CONDITION_PARAM_PERIODICDAMAGE = 43 CONDITION_PARAM_BUFF = 44 CONDITION_PARAM_SUBID = 45 +CONDITION_PARAM_FIELD = 46 COMBAT_PARAM_TYPE = 1 COMBAT_PARAM_EFFECT = 2 @@ -72,6 +73,8 @@ COMBAT_PARAM_TARGETPLAYERSORSUMMONS = 11 COMBAT_PARAM_DIFFERENTAREADAMAGE = 12 COMBAT_PARAM_HITEFFECT = 13 COMBAT_PARAM_HITCOLOR = 14 +COMBAT_PARAM_ELEMENTTYPE = 15 +COMBAT_PARAM_ELEMENTDAMAGE = 16 CALLBACK_PARAM_LEVELMAGICVALUE = 1 CALLBACK_PARAM_SKILLVALUE = 2 @@ -97,7 +100,7 @@ CONDITION_NONE = 0 CONDITION_POISON = 1 CONDITION_FIRE = 2 CONDITION_ENERGY = 4 -CONDITION_PHYSICAL = 8 +CONDITION_BLEEDING = 8 CONDITION_HASTE = 16 CONDITION_PARALYZE = 32 CONDITION_OUTFIT = 64 @@ -119,16 +122,35 @@ CONDITION_CURSED = 1048576 CONDITION_PACIFIED = 2097152 CONDITION_GAMEMASTER = 4194304 CONDITION_HUNTING = 8388608 +CONDITION_SPELLCOOLDOWN = 16777216 + +CONDITIONID_DEFAULT = -1 +CONDITIONID_COMBAT = 0 +CONDITIONID_HEAD = 1 +CONDITIONID_NECKLACE = 2 +CONDITIONID_BACKPACK = 3 +CONDITIONID_ARMOR = 4 +CONDITIONID_RIGHT = 5 +CONDITIONID_LEFT = 6 +CONDITIONID_LEGS = 7 +CONDITIONID_FEET = 8 +CONDITIONID_RING = 9 +CONDITIONID_AMMO = 10 +CONDITIONID_OUTFIT = 11 EXHAUST_OTHER = 0 -EXHAUST_COMBAT = 1 -EXHAUST_HEALING = 2 -EXHAUST_WEAPON = 3 +EXHAUST_SPELLGROUP_NONE = 1 +EXHAUST_SPELLGROUP_ATTACK = 2 +EXHAUST_SPELLGROUP_HEALING = 3 +EXHAUST_SPELLGROUP_SUPPORT = 4 +EXHAUST_SPELLGROUP_SPECIAL = 5 +EXHAUST_MELEE = 6 MUTED_BUFFER = 0 MUTED_YELL = 1 -MUTED_TRADE = 2 -MUTED_TRADE_ROOK = 3 +MUTED_MAIL = 2 +MUTED_TRADE = 3 +MUTED_TRADE_ROOK = 4 GAMEMASTER_INVISIBLE = 0 GAMEMASTER_IGNORE = 1 @@ -216,8 +238,15 @@ CONST_ME_YALAHARIGHOST = 65 CONST_ME_BATS = 66 CONST_ME_SMOKE = 67 CONST_ME_INSECTS = 68 +CONST_ME_DRAGONHEAD = 69 +CONST_ME_ORCSHAMAN = 70 +CONST_ME_ORCSHAMAN_FIRE = 71 +CONST_ME_THUNDER = 72 +CONST_ME_FERUMBRAS = 73 +CONST_ME_CONFETTIHORIZONTAL = 74 +CONST_ME_CONFETTIVERTICAL = 75 CONST_ME_NONE = 255 -CONST_ME_LAST = CONST_ME_INSECTS +CONST_ME_LAST = CONST_ME_CONFETTIVERTICAL CONST_ANI_SPEAR = 0 CONST_ANI_BOLT = 1 @@ -261,30 +290,37 @@ CONST_ANI_SMALLEARTH = 38 CONST_ANI_EARTHARROW = 39 CONST_ANI_EXPLOSION = 40 CONST_ANI_CAKE = 41 +CONST_ANI_UNK1 = 42 -- Debug! +CONST_ANI_TARSALARROW = 43 +CONST_ANI_VORTEXBOLT = 44 +CONST_ANI_UNK2 = 45 -- Debug! +CONST_ANI_FOOTBALL = 46 CONST_ANI_WEAPONTYPE = 254 CONST_ANI_NONE = 255 -CONST_ANI_LAST = CONST_ANI_CAKE +CONST_ANI_LAST = CONST_ANI_FOOTBALL TALKTYPE_FIRST = 1 TALKTYPE_SAY = TALKTYPE_FIRST TALKTYPE_WHISPER = 2 TALKTYPE_YELL = 3 -TALKTYPE_PRIVATE_PN = 4 -TALKTYPE_PRIVATE_NP = 5 -TALKTYPE_PRIVATE = 6 -TALKTYPE_CHANNEL_Y = 7 -TALKTYPE_CHANNEL_W = 8 -TALKTYPE_RVR_CHANNEL = 9 -TALKTYPE_RVR_ANSWER = 10 -TALKTYPE_RVR_CONTINUE = 11 -TALKTYPE_BROADCAST = 12 -TALKTYPE_CHANNEL_RN = 13 -TALKTYPE_PRIVATE_RED = 14 -TALKTYPE_CHANNEL_O = 15 -TALKTYPE_CHANNEL_RA = 17 -TALKTYPE_MONSTER = 19 -TALKTYPE_MONSTER_YELL = 20 -TALKTYPE_LAST = TALKTYPE_MONSTER_YELL +TALKTYPE_PRIVATE_FROM = 4 +TALKTYPE_PRIVATE_TO = 5 +TALKTYPE_CHANNEL = 7 +TALKTYPE_CHANNEL_HIGHLIGHT = 8 +TALKTYPE_SPELL = 9 +TALKTYPE_PRIVATE_NP = 10 +TALKTYPE_PRIVATE_PN = 11 +TALKTYPE_GAMEMASTER_BROADCAST = 12 +TALKTYPE_GAMEMASTER_CHANNEL = 13 +TALKTYPE_GAMEMASTER_PRIVATE_FROM = 14 +TALKTYPE_GAMEMASTER_PRIVATE_TO = 15 +TALKTYPE_MONSTER_SAY = 34 +TALKTYPE_MONSTER_YELL = 35 + +TALKTYPE_FIRST = TALKTYPE_SAY +TALKTYPE_LAST = TALKTYPE_GAMEMASTER_PRIVATE_TO +TALKTYPE_MONSTER_FIRST = TALKTYPE_MONSTER_SAY +TALKTYPE_MONSTER_LAST = TALKTYPE_MONSTER_YELL TALKTYPE_TYPES = { ["say"] = TALKTYPE_SAY, @@ -292,32 +328,45 @@ TALKTYPE_TYPES = { ["yell"] = TALKTYPE_YELL, ["private-playernpc"] = TALKTYPE_PRIVATE_PN, ["private-npcplayer"] = TALKTYPE_PRIVATE_NP, - ["private"] = TALKTYPE_PRIVATE, - ["channel-yellow"] = TALKTYPE_CHANNEL_Y, - ["channel-white"] = TALKTYPE_CHANNEL_W, - ["rvr-channel"] = TALKTYPE_RVR_CHANNEL, - ["rvr-answer"] = TALKTYPE_RVR_ANSWER, - ["rvr-continue"] = TALKTYPE_RVR_CONTINUE, - ["broadcast"] = TALKTYPE_BROADCAST, - ["channel-red"] = TALKTYPE_CHANNEL_RN, - ["channel-orange"] = TALKTYPE_CHANNEL_O, - ["channel-redanonymous"] = TALKTYPE_CHANNEL_RA, - ["monster"] = TALKTYPE_MONSTER, + ["private"] = TALKTYPE_PRIVATE_FROM, + ["channel-yellow"] = TALKTYPE_CHANNEL, + ["channel-white"] = TALKTYPE_CHANNEL_MANAGEMENT, + ["broadcast"] = TALKTYPE_GAMEMASTER_BROADCAST, + ["channel-red"] = TALKTYPE_GAMEMASTER_CHANNEL, + ["private-red"] = TALKTYPE_GAMEMASTER_PRIVATE_FROM, + ["channel-orange"] = TALKTYPE_CHANNEL_HIGHLIGHT, + ["monster"] = TALKTYPE_MONSTER_SAY, ["monster-yell"] = TALKTYPE_MONSTER_YELL } -MESSAGE_FIRST = 18 -MESSAGE_STATUS_CONSOLE_RED = MESSAGE_FIRST -MESSAGE_EVENT_ORANGE = 19 -MESSAGE_STATUS_CONSOLE_ORANGE = 20 -MESSAGE_STATUS_WARNING = 21 -MESSAGE_EVENT_ADVANCE = 22 -MESSAGE_EVENT_DEFAULT = 23 -MESSAGE_STATUS_DEFAULT = 24 -MESSAGE_INFO_DESCR = 25 -MESSAGE_STATUS_SMALL = 26 -MESSAGE_STATUS_CONSOLE_BLUE = 27 -MESSAGE_LAST = MESSAGE_STATUS_CONSOLE_BLUE +MESSAGE_STATUS_CONSOLE_BLUE = 4 +MESSAGE_STATUS_CONSOLE_RED = 12 +MESSAGE_STATUS_DEFAULT = 16 +MESSAGE_STATUS_WARNING = 17 +MESSAGE_EVENT_ADVANCE = 18 +MESSAGE_STATUS_SMALL = 19 +MESSAGE_INFO_DESCR = 20 +MESSAGE_DAMAGE_DEALT = 21 +MESSAGE_DAMAGE_RECEIVED = 22 +MESSAGE_HEALED = 23 +MESSAGE_EXPERIENCE = 24 +MESSAGE_DAMAGE_OTHERS = 25 +MESSAGE_HEALED_OTHERS = 26 +MESSAGE_EXPERIENCE_OTHERS = 27 +MESSAGE_EVENT_DEFAULT = 28 +MESSAGE_LOOT = 29 +MESSAGE_TRADE_NPC = 30 +MESSAGE_EVENT_GUILD = 31 +MESSAGE_PARTY_MANAGEMENT = 32 +MESSAGE_PARTY = 33 +MESSAGE_EVENT_ORANGE = 34 +MESSAGE_STATUS_CONSOLE_ORANGE = 35 +MESSAGE_REPORT = 36 +MESSAGE_HOTKEY_USE = 37 +MESSAGE_TUTORIAL_HINT = 38 + +MESSAGE_FIRST = MESSAGE_STATUS_CONSOLE_BLUE +MESSAGE_LAST = MESSAGE_TUTORIAL_HINT MESSAGE_TYPES = { ["advance"] = MESSAGE_EVENT_ADVANCE, @@ -336,14 +385,15 @@ MESSAGE_TYPES = { COLOR_BLACK = 0 COLOR_BLUE = 5 COLOR_GREEN = 18 -COLOR_TEAL = 35 COLOR_LIGHTGREEN = 66 COLOR_DARKBROWN = 78 COLOR_LIGHTBLUE = 89 +COLOR_MAYABLUE = 95 +COLOR_DARKRED = 108 COLOR_DARKPURPLE = 112 COLOR_BROWN = 120 COLOR_GREY = 129 -COLOR_DARKRED = 144 +COLOR_TEAL = 143 COLOR_DARKPINK = 152 COLOR_PURPLE = 154 COLOR_DARKORANGE = 156 @@ -376,6 +426,7 @@ MAPMARK_REDWEST = 17 MAPMARK_GREENNORTH = 18 MAPMARK_GREENSOUTH = 19 +ITEM_TYPE_NONE = 0 ITEM_TYPE_DEPOT = 1 ITEM_TYPE_MAILBOX = 2 ITEM_TYPE_TRASHHOLDER = 3 @@ -384,7 +435,15 @@ ITEM_TYPE_DOOR = 5 ITEM_TYPE_MAGICFIELD = 6 ITEM_TYPE_TELEPORT = 7 ITEM_TYPE_BED = 8 -ITEM_TYPE_LEVELDOOR = 9 +ITEM_TYPE_KEY = 9 +ITEM_TYPE_RUNE = 10 + +ITEM_GROUP_NONE = 0 +ITEM_GROUP_GROUND = 1 +ITEM_GROUP_CONTAINER = 2 +ITEM_GROUP_CHARGES = 6 +ITEM_GROUP_SPLASH = 11 +ITEM_GROUP_FLUID = 12 CONST_PROP_BLOCKSOLID = 0 CONST_PROP_HASHEIGHT = 1 @@ -392,10 +451,26 @@ CONST_PROP_BLOCKPROJECTILE = 2 CONST_PROP_BLOCKPATHFIND = 3 CONST_PROP_ISVERTICAL = 4 CONST_PROP_ISHORIZONTAL = 5 -CONST_PROP_MOVEABLE = 6 -CONST_PROP_BLOCKINGANDNOTMOVEABLE = 7 +CONST_PROP_MOVABLE = 6 +CONST_PROP_BLOCKINGANDNOTMOVABLE = 7 CONST_PROP_SUPPORTHANGABLE = 8 +CHASEMODE_STANDSTILL = 0 +CHASEMODE_FOLLOW = 1 + +FIGHTMODE_ATTACK = 0 +FIGHTMODE_BALANCED = 1 +FIGHTMODE_DEFENSE = 2 + +SECUREMODE_ON = 0 +SECUREMODE_OFF = 1 + +TRADE_NONE = 0 +TRADE_INITIATED = 1 +TRADE_ACCEPT = 2 +TRADE_ACKNOWLEDGE = 3 +TRADE_TRANSFER = 4 + MANAGER_NONE = 0 MANAGER_NEW = 1 MANAGER_ACCOUNT = 2 @@ -403,7 +478,6 @@ MANAGER_NAMELOCK = 3 PLAYERSEX_FEMALE = 0 PLAYERSEX_MALE = 1 -PLAYERSEX_GAMEMASTER = 2 PLAYERLOSS_EXPERIENCE = 0 PLAYERLOSS_MANA = 1 @@ -416,7 +490,8 @@ STATSCHANGE_HEALTHLOSS = 1 STATSCHANGE_MANAGAIN = 2 STATSCHANGE_MANALOSS = 3 -SKILL_FIST = 0 +SKILL_FIRST = 0 +SKILL_FIST = SKILL_FIRST SKILL_CLUB = 1 SKILL_SWORD = 2 SKILL_AXE = 3 @@ -425,6 +500,10 @@ SKILL_SHIELD = 5 SKILL_FISHING = 6 SKILL__MAGLEVEL = 7 SKILL__LEVEL = 8 +SKILL__EXPERIENCE = 9 +SKILL_LAST = SKILL_FISHING +SKILL__PRE_LAST = SKILL__LEVEL +SKILL__LAST = SKILL__EXPERIENCE SKILL_NAMES = { [SKILL_FIST] = "fist fighting", @@ -453,9 +532,9 @@ SKILL_IDS = { ["magic"] = SKILL__MAGLEVEL } -GUILDLEVEL_MEMBER = 1 -GUILDLEVEL_VICE = 2 -GUILDLEVEL_LEADER = 3 +GUILD_MEMBER = 1 +GUILD_VICE = 2 +GUILD_LEADER = 3 SKULL_NONE = 0 SKULL_YELLOW = 1 @@ -463,7 +542,8 @@ SKULL_GREEN = 2 SKULL_WHITE = 3 SKULL_RED = 4 SKULL_BLACK = 5 -SKULL_LAST = SKULL_BLACK +SKULL_ORANGE = 6 +SKULL_LAST = SKULL_ORANGE SHIELD_NONE = 0 SHIELD_WHITEYELLOW = 1 @@ -478,15 +558,19 @@ SHIELD_BLUE_NOSHAREDEXP = 9 SHIELD_YELLOW_NOSHAREDEXP = 10 SHIELD_LAST = SHIELD_YELLOW_NOSHAREDEXP -WORLDTYPE_NO_PVP = 1 -WORLDTYPE_PVP = 2 -WORLDTYPE_PVP_ENFORCED = 3 +EMBLEM_NONE = 0 +EMBLEM_GREEN = 1 +EMBLEM_RED = 2 +EMBLEM_BLUE = 3 -DATABASE_ENGINE_NONE = 0 -DATABASE_ENGINE_MYSQL = 1 -DATABASE_ENGINE_SQLITE = 2 -DATABASE_ENGINE_POSTGRESQL = 3 -DATABASE_ENGINE_ODBC = 4 +WORLDTYPE_OPTIONAL = 1 +WORLDTYPE_OPEN = 2 +WORLDTYPE_HARDCORE = 3 + +DATABASE_NONE = 0 +DATABASE_MYSQL = 1 +DATABASE_SQLITE = 2 +DATABASE_POSTGRESQL = 3 GAMESTATE_STARTUP = 1 GAMESTATE_INIT = 2 @@ -504,28 +588,27 @@ RELOAD_GAMESERVERS = 5 RELOAD_GLOBALEVENTS = 6 RELOAD_GROUPS = 7 RELOAD_HIGHSCORES = 8 -RELOAD_HOUSEPRICES = 9 RELOAD_ITEMS = 10 RELOAD_MONSTERS = 11 -RELOAD_MOVEEVENTS = 12 -RELOAD_NPCS = 13 -RELOAD_OUTFITS = 14 -RELOAD_QUESTS = 15 -RELOAD_RAIDS = 16 -RELOAD_SPELLS = 17 -RELOAD_STAGES = 18 -RELOAD_TALKACTIONS = 19 -RELOAD_VOCATIONS = 20 -RELOAD_WEAPONS = 21 -RELOAD_MODS = 22 -RELOAD_ALL = 23 +RELOAD_MOUNTS = 12 +RELOAD_MOVEEVENTS = 13 +RELOAD_NPCS = 14 +RELOAD_OUTFITS = 15 +RELOAD_QUESTS = 16 +RELOAD_RAIDS = 17 +RELOAD_SPELLS = 18 +RELOAD_STAGES = 19 +RELOAD_TALKACTIONS = 20 +RELOAD_VOCATIONS = 21 +RELOAD_WEAPONS = 22 +RELOAD_MODS = 23 +RELOAD_ALL = 24 BAN_NONE = 0 BAN_IP = 1 BAN_PLAYER = 2 BAN_ACCOUNT = 3 BAN_NOTATION = 4 -BAN_STATEMENT = 5 PLAYERBAN_NONE = 0 PLAYERBAN_REPORT = 1 @@ -534,42 +617,33 @@ PLAYERBAN_BANISHMENT = 3 CHANNEL_GUILD = 0 CHANNEL_PARTY = 1 -CHANNEL_RVR = 3 -CHANNEL_HELP = 9 +CHANNEL_HELP = 7 CHANNEL_DEFAULT = 0xFFFE CHANNEL_PRIVATE = 0xFFFF STACKPOS_GROUND = 0 STACKPOS_TOP_CREATURE = 253 STACKPOS_TOP_FIELD = 254 -STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE = 255 +STACKPOS_TOP_MOVABLE_ITEM_OR_CREATURE = 255 WEAPON_NONE = 0 WEAPON_SWORD = 1 WEAPON_CLUB = 2 WEAPON_AXE = 3 -WEAPON_SHIELD = 4 -WEAPON_DIST = 5 -WEAPON_WAND = 6 -WEAPON_AMMO = 7 -WEAPON_FIST = 8 - -ACTION_NOTATION = 0 -ACTION_NAMEREPORT = 1 -ACTION_BANISHMENT = 2 -ACTION_BANREPORT = 3 -ACTION_BANFINAL = 4 -ACTION_BANREPORTFINAL = 5 -ACTION_STATEMENT = 6 -ACTION_DELETION = 7 -ACTION_NAMELOCK = 8 -ACTION_BANLOCK = 9 -ACTION_BANLOCKFINAL = 10 -ACTION_PLACEHOLDER = 11 +WEAPON_DIST = 4 +WEAPON_SHIELD = 5 +WEAPON_FIST = 6 +WEAPON_WAND = 7 +WEAPON_AMMO = 8 + +RECURSE_FIRST = -1 +RECURSE_NONE = 0 +RECURSE_ALL = 1 ITEM_GOLD_COIN = 2148 ITEM_PLATINUM_COIN = 2152 ITEM_CRYSTAL_COIN = 2160 +ITEM_SCARAB_COIN = 2159 ITEM_FISH = 2667 ITEM_WORM = 3976 ITEM_MECHANICAL_FISH = 10224 @@ -580,79 +654,91 @@ ITEM_RAINBOW_TROUT = 7158 ITEM_NORTHERN_PIKE = 2669 ITEM_PARCEL = 2595 ITEM_LABEL = 2599 -ITEM_GLOWING_SWITCH = 11060 - -RETURNVALUE_DONTSHOWMESSAGE = 0 -RETURNVALUE_NOERROR = 1 -RETURNVALUE_NOTPOSSIBLE = 2 -RETURNVALUE_NOTENOUGHROOM = 3 -RETURNVALUE_PLAYERISPZLOCKED = 4 -RETURNVALUE_PLAYERISNOTINVITED = 5 -RETURNVALUE_CANNOTTHROW = 6 -RETURNVALUE_THEREISNOWAY = 7 -RETURNVALUE_DESTINATIONOUTOFREACH = 8 -RETURNVALUE_CREATUREBLOCK = 9 -RETURNVALUE_NOTMOVEABLE = 10 -RETURNVALUE_DROPTWOHANDEDITEM = 11 -RETURNVALUE_BOTHHANDSNEEDTOBEFREE = 12 -RETURNVALUE_CANONLYUSEONEWEAPON = 13 -RETURNVALUE_NEEDEXCHANGE = 14 -RETURNVALUE_CANNOTBEDRESSED = 15 -RETURNVALUE_PUTTHISOBJECTINYOURHAND = 16 -RETURNVALUE_PUTTHISOBJECTINBOTHHANDS = 17 -RETURNVALUE_TOOFARAWAY = 18 -RETURNVALUE_FIRSTGODOWNSTAIRS = 19 -RETURNVALUE_FIRSTGOUPSTAIRS = 20 -RETURNVALUE_CONTAINERNOTENOUGHROOM = 21 -RETURNVALUE_NOTENOUGHCAPACITY = 22 -RETURNVALUE_CANNOTPICKUP = 23 -RETURNVALUE_THISISIMPOSSIBLE = 24 -RETURNVALUE_DEPOTISFULL = 25 -RETURNVALUE_CREATUREDOESNOTEXIST = 26 -RETURNVALUE_CANNOTUSETHISOBJECT = 27 -RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE = 28 -RETURNVALUE_NOTREQUIREDLEVELTOUSERUNE = 29 -RETURNVALUE_YOUAREALREADYTRADING = 30 -RETURNVALUE_THISPLAYERISALREADYTRADING = 31 -RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT = 32 -RETURNVALUE_DIRECTPLAYERSHOOT = 33 -RETURNVALUE_NOTENOUGHLEVEL = 34 -RETURNVALUE_NOTENOUGHMAGICLEVEL = 35 -RETURNVALUE_NOTENOUGHMANA = 36 -RETURNVALUE_NOTENOUGHSOUL = 37 -RETURNVALUE_YOUAREEXHAUSTED = 38 -RETURNVALUE_PLAYERISNOTREACHABLE = 39 -RETURNVALUE_CANONLYUSETHISRUNEONCREATURES = 40 -RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE = 41 -RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER = 42 -RETURNVALUE_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE = 43 -RETURNVALUE_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE = 44 -RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE = 45 -RETURNVALUE_YOUCANONLYUSEITONCREATURES = 46 -RETURNVALUE_CREATUREISNOTREACHABLE = 47 -RETURNVALUE_TURNSECUREMODETOATTACKUNMARKEDPLAYERS = 48 -RETURNVALUE_YOUNEEDPREMIUMACCOUNT = 49 -RETURNVALUE_YOUNEEDTOLEARNTHISSPELL = 50 -RETURNVALUE_YOURVOCATIONCANNOTUSETHISSPELL = 51 -RETURNVALUE_YOUNEEDAWEAPONTOUSETHISSPELL = 52 -RETURNVALUE_PLAYERISPZLOCKEDLEAVEPVPZONE = 53 -RETURNVALUE_PLAYERISPZLOCKEDENTERPVPZONE = 54 -RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE = 55 -RETURNVALUE_YOUCANNOTLOGOUTHERE = 56 -RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL = 57 -RETURNVALUE_CANNOTCONJUREITEMHERE = 58 -RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS = 59 -RETURNVALUE_NAMEISTOOAMBIGUOUS = 60 -RETURNVALUE_CANONLYUSEONESHIELD = 61 -RETURNVALUE_YOUARENOTTHEOWNER = 62 -RETURNVALUE_YOUMAYNOTCASTAREAONBLACKSKULL = 63 -RETURNVALUE_TILEISFULL = 64 +ITEM_ACTION_BOOK = 1977 +ITEM_MAGIC_WALL = 1497 +ITEM_WILD_GROWTH = 1499 + +ITEM_FOOD_MEAT = 2666 +ITEM_FOOD_HAM = 2671 +ITEM_FOOD_GRAPE = 2681 +ITEM_FOOD_APLE = 2674 +ITEM_FOOD_BREAD = 2689 +ITEM_FOOD_ROOL = 2690 +ITEM_FOOD_CHEESE = 2696 + +RETURNVALUE_NOERROR = 0 +RETURNVALUE_NOTPOSSIBLE = 1 +RETURNVALUE_NOTENOUGHROOM = 2 +RETURNVALUE_PLAYERISPZLOCKED = 3 +RETURNVALUE_PLAYERISNOTINVITED = 4 +RETURNVALUE_CANNOTTHROW = 5 +RETURNVALUE_THEREISNOWAY = 6 +RETURNVALUE_DESTINATIONOUTOFREACH = 7 +RETURNVALUE_CREATUREBLOCK = 8 +RETURNVALUE_NOTMOVABLE = 9 +RETURNVALUE_DROPTWOHANDEDITEM = 10 +RETURNVALUE_BOTHHANDSNEEDTOBEFREE = 11 +RETURNVALUE_CANONLYUSEONEWEAPON = 12 +RETURNVALUE_NEEDEXCHANGE = 13 +RETURNVALUE_CANNOTBEDRESSED = 14 +RETURNVALUE_PUTTHISOBJECTINYOURHAND = 15 +RETURNVALUE_PUTTHISOBJECTINBOTHHANDS = 16 +RETURNVALUE_TOOFARAWAY = 17 +RETURNVALUE_FIRSTGODOWNSTAIRS = 18 +RETURNVALUE_FIRSTGOUPSTAIRS = 19 +RETURNVALUE_CONTAINERNOTENOUGHROOM = 20 +RETURNVALUE_NOTENOUGHCAPACITY = 21 +RETURNVALUE_CANNOTPICKUP = 22 +RETURNVALUE_THISISIMPOSSIBLE = 23 +RETURNVALUE_DEPOTISFULL = 24 +RETURNVALUE_CREATUREDOESNOTEXIST = 25 +RETURNVALUE_CANNOTUSETHISOBJECT = 26 +RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE = 27 +RETURNVALUE_NOTREQUIREDLEVELTOUSERUNE = 28 +RETURNVALUE_YOUAREALREADYTRADING = 29 +RETURNVALUE_THISPLAYERISALREADYTRADING = 30 +RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT = 31 +RETURNVALUE_DIRECTPLAYERSHOOT = 32 +RETURNVALUE_NOTENOUGHLEVEL = 33 +RETURNVALUE_NOTENOUGHMAGICLEVEL = 34 +RETURNVALUE_NOTENOUGHMANA = 35 +RETURNVALUE_NOTENOUGHSOUL = 36 +RETURNVALUE_YOUAREEXHAUSTED = 37 +RETURNVALUE_PLAYERISNOTREACHABLE = 38 +RETURNVALUE_CANONLYUSETHISRUNEONCREATURES = 39 +RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE = 40 +RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER = 41 +RETURNVALUE_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE = 42 +RETURNVALUE_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE = 43 +RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE = 44 +RETURNVALUE_YOUCANONLYUSEITONCREATURES = 45 +RETURNVALUE_CREATUREISNOTREACHABLE = 46 +RETURNVALUE_TURNSECUREMODETOATTACKUNMARKEDPLAYERS = 47 +RETURNVALUE_YOUNEEDPREMIUMACCOUNT = 48 +RETURNVALUE_YOUNEEDTOLEARNTHISSPELL = 49 +RETURNVALUE_YOURVOCATIONCANNOTUSETHISSPELL = 50 +RETURNVALUE_YOUNEEDAWEAPONTOUSETHISSPELL = 51 +RETURNVALUE_PLAYERISPZLOCKEDLEAVEPVPZONE = 52 +RETURNVALUE_PLAYERISPZLOCKEDENTERPVPZONE = 53 +RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE = 54 +RETURNVALUE_YOUCANNOTLOGOUTHERE = 55 +RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL = 56 +RETURNVALUE_CANNOTCONJUREITEMHERE = 57 +RETURNVALUE_TILEISFULL = 58 +RETURNVALUE_NAMEISTOOAMBIGUOUS = 59 +RETURNVALUE_CANONLYUSEONESHIELD = 60 +RETURNVALUE_YOUARENOTTHEOWNER = 61 +RETURNVALUE_YOUMAYNOTCASTAREAONBLACKSKULL = 62 +RETURNVALUE_NOTENOUGHSKILL = 63 CONTAINER_POSITION = 0xFFFF EMPTY_STORAGE = -1 EMPTY_RESULT = -1 ITEMCOUNT_MAX = 100 +WAR_GUILD = 0 +WAR_ENEMY = 1 + AUTOID_PLAYERS = 0x10000000 AUTOID_MONSTERS = 0x40000000 AUTOID_NPCS = 0x80000000 @@ -695,7 +781,7 @@ PLAYERFLAG_IGNORESPELLCHECK = 34 PLAYERFLAG_IGNOREWEAPONCHECK = 35 PLAYERFLAG_CANNOTBEMUTED = 36 PLAYERFLAG_ISALWAYSPREMIUM = 37 -PLAYERFLAG_CANANSWERRULEVIOLATIONS = 38 +PLAYERFLAG_38 = 38 -- ignore PLAYERFLAG_39 = 39 -- ignore PLAYERFLAG_SHOWGROUPINSTEADOFVOCATION = 40 PLAYERFLAG_HASINFINITESTAMINA = 41 @@ -704,6 +790,8 @@ PLAYERFLAG_CANNOTMOVECREATURES = 43 PLAYERFLAG_CANREPORTBUGS = 44 PLAYERFLAG_45 = 45 -- ignore PLAYERFLAG_CANNOTBESEEN = 46 +PLAYERFLAG_HIDEHEALTH = 47 +PLAYERFLAG_CANPASSTHROUGHALLCREATURES = 48 PLAYERCUSTOMFLAG_ALLOWIDLE = 0 PLAYERCUSTOMFLAG_CANSEEPOSITION = 1 @@ -715,39 +803,40 @@ PLAYERCUSTOMFLAG_CANTHROWANYWHERE = 6 PLAYERCUSTOMFLAG_CANPUSHALLITEMS = 7 PLAYERCUSTOMFLAG_CANMOVEANYWHERE = 8 PLAYERCUSTOMFLAG_CANMOVEFROMFAR = 9 -PLAYERCUSTOMFLAG_CANLOGINMULTIPLECHARACTERS = 10 -PLAYERCUSTOMFLAG_HASFULLLIGHT = 11 +PLAYERCUSTOMFLAG_CANUSEFAR = 10 +PLAYERCUSTOMFLAG_CANLOGINMULTIPLECHARACTERS = 11 PLAYERCUSTOMFLAG_CANLOGOUTANYTIME = 12 PLAYERCUSTOMFLAG_HIDELEVEL = 13 PLAYERCUSTOMFLAG_ISPROTECTED = 14 PLAYERCUSTOMFLAG_ISIMMUNE = 15 PLAYERCUSTOMFLAG_NOTGAINSKULL = 16 PLAYERCUSTOMFLAG_NOTGAINUNJUSTIFIED = 17 -PLAYERCUSTOMFLAG_HIDELEVEL = 18 -PLAYERCUSTOMFLAG_IGNOREPACIFICATION = 19 +PLAYERCUSTOMFLAG_IGNOREPACIFICATION = 18 +PLAYERCUSTOMFLAG_IGNORELOGINDELAY = 19 PLAYERCUSTOMFLAG_CANSTAIRHOP = 20 PLAYERCUSTOMFLAG_CANTURNHOP = 21 PLAYERCUSTOMFLAG_IGNOREHOUSERENT = 22 PLAYERCUSTOMFLAG_CANWEARALLADDONS = 23 +PLAYERCUSTOMFLAG_ISWALKABLE = 24 +PLAYERCUSTOMFLAG_CANUSEALLMOUNTS = 25 +PLAYERCUSTOMFLAG_HASFULLLIGHT = 26 -maleOutfits = {128, 129, 130, 131, 132, 133, 134, 143, 144, 145, 146, 151, 152, 153, 154, 251, 268, 273, 278, 289, 325, 328, 335} -femaleOutfits = {136, 137, 138, 139, 140, 141, 142, 147, 148, 149, 150, 155, 156, 157, 158, 252, 269, 270, 279, 288, 324, 329, 336} +maleOutfits = {128, 129, 130, 131, 132, 133, 134, 143, 144, 145, 146, 151, 152, 153, 154, 251, 268, 273, 278, 289, 325, 328, 335, 367, 430, 432, 463, 465, 472} +femaleOutfits = {136, 137, 138, 139, 140, 141, 142, 147, 148, 149, 150, 155, 156, 157, 158, 252, 269, 270, 279, 288, 324, 329, 336, 366, 431, 433, 464, 466, 471} +mountOutfits = {368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 387, 388, 389, 390, 392, 401, 402, 405, 406, 421, 426, 437, 438, 447, 450} -doors = {[1209] = 1211, [1210] = 1211, [1212] = 1214, [1213] = 1214, [1219] = 1220, [1221] = 1222, [1231] = 1233, [1232] = 1233, [1234] = 1236, [1235] = 1236, [1237] = 1238, [1239] = 1240, [1249] = 1251, [1250] = 1251, [1252] = 1254, [1253] = 1254, [1539] = 1540, [1541] = 1542, [3535] = 3537, [3536] = 3537, [3538] = 3539, [3544] = 3546, [3545] = 3546, [3547] = 3548, [4913] = 4915, [4914] = 4915, [4916] = 4918, [4917] = 4918, [5082] = 5083, [5084] = 5085, [5098] = 5100, [5099] = 5100, [5101] = 5102, [5107] = 5109, [5108] = 5109, [5110] = 5111, [5116] = 5118, [5117] = 5118, [5119] = 5120, [5125] = 5127, [5126] = 5127, [5128] = 5129, [5134] = 5136, [5135] = 5136, [5137] = 5139, [5138] = 5139, [5140] = 5142, [5141] = 5142, [5143] = 5145, [5144] = 5145, [5278] = 5280, [5279] = 5280, [5281] = 5283, [5282] = 5283, [5284] = 5285, [5286] = 5287, [5515] = 5516, [5517] = 5518, [5732] = 5734, [5733] = 5734, [5735] = 5737, [5736] = 5737, [6192] = 6194, [6193] = 6194, [6195] = 6197, [6196] = 6197, [6198] = 6199, [6200] = 6201, [6249] = 6251, [6250] = 6251, [6252] = 6254, [6253] = 6254, [6255] = 6256, [6257] = 6258, [6795] = 6796, [6797] = 6798, [6799] = 6800, [6801] = 6802, [6891] = 6893, [6892] = 6893, [6894] = 6895, [6900] = 6902, [6901] = 6902, [6903] = 6904, [7033] = 7035, [7034] = 7035, [7036] = 7037, [7042] = 7044, [7043] = 7044, [7045] = 7046, [7054] = 7055, [7056] = 7057, [8541] = 8543, [8542] = 8543, [8544] = 8546, [8545] = 8546, [8547] = 8548, [8549] = 8550, [9165] = 9167, [9166] = 9167, [9168] = 9170, [9169] = 9170, [9171] = 9172, [9173] = 9174, [9267] = 9269, [9268] = 9269, [9270] = 9272, [9271] = 9272, [9273] = 9274, [9275] = 9276, [10276] = 10277, [10274] = 10275, [10268] = 10270, [10269] = 10270, [10271] = 10273, [10272] = 10273, [10471] = 10472, [10480] = 10481, [10477] = 10479, [10478] = 10479, [10468] = 10470, [10469] = 10470, [10774] = 10776, [10775] = 10776, [10779] = 10780, [10781] = 10782, [10783] = 10785, [10784] = 10785, [10788] = 10789, [10790] = 10791} -closingDoors = {1224, 1226, 1228, 1230, 1242, 1244, 1246, 1248, 1256, 1258, 1260, 1262, 3541, 3543, 3550, 3552, 5104, 5106, 5113, 5115, 5122, 5124, 5131, 5133, 5289, 5291, 5293, 5295, 6203, 6205, 6207, 6209, 6260, 6262, 6264, 6266, 6897, 6899, 6906, 6908, 7039, 7041, 7048, 7050, 8552, 8554, 8556, 8558, 9176, 9178, 9180, 9182, 9278, 9280, 9282, 9284, 10279, 10281, 10283, 10285, 10474, 10476, 10483, 10485, 10780, 10782, 10789, 10791} -verticalOpenDoors = {1211, 1220, 1224, 1228, 1233, 1238, 1242, 1246, 1251, 1256, 1260, 1540, 3546, 3548, 3550, 3552, 4915, 5083, 5109, 5111, 5113, 5115, 5127, 5129, 5131, 5133, 5142, 5145, 5283, 5285, 5289, 5293, 5516, 5737, 5749, 6194, 6199, 6203, 6207, 6251, 6256, 6260, 6264, 6798, 6802, 6902, 6904, 6906, 6908, 7044, 7046, 7048, 7050, 7055, 8543, 8548, 8552, 8556, 9167, 9172, 9269, 9274, 9278, 9282, 10270, 10275, 10279, 10283, 10479, 10481, 10483, 10485, 10789, 10791} -horizontalOpenDoors = {1214, 1222, 1226, 1230, 1236, 1240, 1244, 1248, 1254, 1258, 1262, 1542, 3537, 3539, 3541, 3543, 4918, 5085, 5100, 5102, 5104, 5106, 5118, 5120, 5122, 5124, 5136, 5139, 5280, 5287, 5291, 5295, 5518, 5734, 5746, 6197, 6201, 6205, 6209, 6254, 6258, 6262, 6266, 6796, 6800, 6893, 6895, 6897, 6899, 7035, 7037, 7039, 7041, 7057, 8546, 8550, 8554, 8558, 9170, 9174, 9272, 9276, 9280, 9284, 10273, 10277, 10281, 10285, 10470, 10472, 10474, 10476, 10780, 10782} -specialDoors = {1223, 1225, 1241, 1243, 1255, 1257, 3542, 3551, 5105, 5114, 5123, 5132, 5288, 5290, 5745, 5748, 6202, 6204, 6259, 6261, 6898, 6907, 7040, 7049, 8551, 8553, 9175, 9177, 9277, 9279, 10278, 10280, 10475, 10484, 10781, 10790} -keys = {2086, 2087, 2088, 2089, 2090, 2091, 2092, 10032, 10091} +underWater = {5405, 5406, 5407, 5408, 5409, 5410} enchantableGems = {2147, 2146, 2149, 2150} enchantableItems = {2383, 7383, 7384, 7406, 7402, 2429, 2430, 7389, 7380, 2454, 2423, 2445, 7415, 7392, 2391, 2544, 8905} + enchantingAltars = { {7504, 7505, 7506, 7507}, {7508, 7509, 7510, 7511}, {7516, 7517, 7518, 7519}, {7512, 7513, 7514, 7515} } + enchantedGems = {7760, 7759, 7761, 7762} enchantedItems = { [2383] = {7744, 7763, 7854, 7869}, @@ -768,3 +857,43 @@ enchantedItems = { [2544] = {7840, 7839, 7850, 7838}, [8905] = {8906, 8907, 8909, 8908} } + +Config_meta = +{ + __index = function(t, k) + if(k == "type") then + return function() + return "Config" + end + end + + return getConfigValue(k) + end, + + __newindex = function(t, k, v) + return false + end +} + +CONFIG = {} +setmetatable(CONFIG, Config_meta) + +Storage_meta = +{ + __index = function(t, k) + if(k == "type") then + return function() + return "Storage" + end + end + + return getStorage(k) + end, + + __newindex = function(t, k, v) + return doSetStorage(k, v) + end +} + +STORAGE = {} +setmetatable(STORAGE, Storage_meta) diff --git a/data/lib/001-class.lua b/data/lib/001-class.lua index 32445ad..4f45a8d 100644 --- a/data/lib/001-class.lua +++ b/data/lib/001-class.lua @@ -30,7 +30,7 @@ function createClass(parent) return false end - + function newClass:setAttributes(attributes) for k, v in pairs(attributes) do newClass[k] = v diff --git a/data/lib/002-wait.lua b/data/lib/002-wait.lua index 2d44302..0a68336 100644 --- a/data/lib/002-wait.lua +++ b/data/lib/002-wait.lua @@ -8,15 +8,15 @@ function runThread(co) end function createThread(data) - local dataType, func = type(data), nil + local dataType, fn = type(data), nil if(dataType == 'string') then - func = loadstring(data) + fn = loadstring(data) elseif(dataType == 'function') then - func = data + fn = data end - if(func ~= nil) then - local co = coroutine.create(func) + if(fn ~= nil) then + local co = coroutine.create(fn) runThread(co) else print("[createThread]", "Invalid data specified.") diff --git a/data/lib/004-database.lua b/data/lib/004-database.lua index 07aacde..46fe063 100644 --- a/data/lib/004-database.lua +++ b/data/lib/004-database.lua @@ -1,3 +1,9 @@ +db.updateQueryLimitOperator = db.updateLimiter +db.stringComparisonOperator = db.stringComparer +db.stringComparison = db.stringComparer +db.executeQuery = db.query +db.quote = db.escapeString + if(result == nil) then print("> WARNING: Couldn't load database lib.") return diff --git a/data/lib/011-string.lua b/data/lib/011-string.lua index 934d03a..3c80b53 100644 --- a/data/lib/011-string.lua +++ b/data/lib/011-string.lua @@ -33,7 +33,7 @@ string.expand = function (str) return string.gsub(str, "$(%w+)", function(n) return _G[n] end) end -string.timediff = function (diff) +string.diff = function (diff) local format = { {"week", diff / 60 / 60 / 24 / 7}, {"day", diff / 60 / 60 / 24 % 7}, @@ -53,3 +53,19 @@ string.timediff = function (diff) return t end +string.timediff = string.diff + +string.boolean = function (input) + local tmp = type(input) + if(tmp == 'boolean') then + return input + end + + if(tmp == 'number') then + return input > 0 + end + + local str = string.lower(tostring(input)) + return (str == "yes" or str == "true" or (tonumber(str) ~= nil and tonumber(str) > 0)) +end +getBooleanFromString = string.boolean \ No newline at end of file diff --git a/data/lib/012-table.lua b/data/lib/012-table.lua index 6d9f544..bc2bcfa 100644 --- a/data/lib/012-table.lua +++ b/data/lib/012-table.lua @@ -1,4 +1,31 @@ -table.find = function (table, value) +table.append = table.insert +table.empty = function (t) + return next(t) == nil +end + +table.size = function (t) + local size = 0 + for i, n in pairs(table) do + size = size + 1 + end + + return size +end + +table.find = function (table, value, sensitive) + local sensitive = sensitive or true + if(not sensitive and type(value) == "string") then + for i, v in pairs(table) do + if(type(v) == "string") then + if(v:lower() == value:lower()) then + return i + end + end + end + + return nil + end + for i, v in pairs(table) do if(v == value) then return i @@ -7,6 +34,7 @@ table.find = function (table, value) return nil end +table.getPos = table.find table.contains = function (txt, str) for i, v in pairs(str) do @@ -32,19 +60,19 @@ end table.countElements = table.count table.getCombinations = function (table, num) - local a, number, select, newlist = {}, #table, num, {} + local a, number, select, newList = {}, table.size(table), num, {} for i = 1, select do - a[#a + 1] = i + table.insert(a, i) end - local newthing = {} + local newThing = {} while(true) do - local newrow = {} + local newRow = {} for i = 1, select do - newrow[#newrow + 1] = table[a[i]] + table.insert(newRow, table[a[i]]) end - newlist[#newlist + 1] = newrow + table.insert(newList, newRow) i = select while(a[i] == (number - select + i)) do i = i - 1 @@ -60,5 +88,44 @@ table.getCombinations = function (table, num) end end - return newlist + return newList +end + +function table.serialize(x, recur) + local t = type(x) + recur = recur or {} + + if(t == nil) then + return "nil" + elseif(t == "string") then + return string.format("%q", x) + elseif(t == "number") then + return tostring(x) + elseif(t == "boolean") then + return x and "true" or "false" + elseif(getmetatable(x)) then + error("Can not serialize a table that has a metatable associated with it.") + elseif(t == "table") then + if(table.find(recur, x)) then + error("Can not serialize recursive tables.") + end + table.append(recur, x) + + local s = "{" + for k, v in pairs(x) do + s = s .. "[" .. table.serialize(k, recur) .. "]" .. " = " .. table.serialize(v, recur) .. ", " + end + + return s:sub(0, s:len() - 2) .. "}" + end + + error("Can not serialize value of type '" .. t .. "'.") +end + +function table.unserialize(str) + if(type(str) ~= 'string' or str:len() == 0) then + return {} + end + + return loadstring("return " .. str)() end diff --git a/data/lib/013-math.lua b/data/lib/013-math.lua new file mode 100644 index 0000000..c66d8ff --- /dev/null +++ b/data/lib/013-math.lua @@ -0,0 +1,3 @@ +math.round = function(num, idp) + return tonumber(string.format("%." .. (idp or 0) .. "f", num)) +end \ No newline at end of file diff --git a/data/lib/032-position.lua b/data/lib/032-position.lua index ccf6a01..f35cb59 100644 --- a/data/lib/032-position.lua +++ b/data/lib/032-position.lua @@ -1,5 +1,6 @@ function isInRange(position, fromPosition, toPosition) - return (position.x >= fromPosition.x and position.y >= fromPosition.y and position.z >= fromPosition.z and position.x <= toPosition.x and position.y <= toPosition.y and position.z <= toPosition.z) + return (position.x >= fromPosition.x and position.y >= fromPosition.y and position.z >= fromPosition.z + and position.x <= toPosition.x and position.y <= toPosition.y and position.z <= toPosition.z) end function getDistanceBetween(fromPosition, toPosition) @@ -13,7 +14,7 @@ function getDistanceBetween(fromPosition, toPosition) end function getDirectionTo(pos1, pos2) - local dir = NORTH + local dir = SOUTH if(pos1.x > pos2.x) then dir = WEST if(pos1.y > pos2.y) then @@ -28,19 +29,17 @@ function getDirectionTo(pos1, pos2) elseif(pos1.y < pos2.y) then dir = SOUTHEAST end - else - if(pos1.y > pos2.y) then - dir = NORTH - elseif(pos1.y < pos2.y) then - dir = SOUTH - end + elseif(pos1.y > pos2.y) then + dir = NORTH + elseif(pos1.y < pos2.y) then + dir = SOUTH end return dir end function getCreatureLookPosition(cid) - return getPosByDir(getThingPos(cid), getCreatureLookDirection(cid)) + return getPositionByDirection(getThingPosition(cid), getCreatureLookDirection(cid)) end function getPositionByDirection(position, direction, size) @@ -84,3 +83,20 @@ function getArea(position, x, y) return t end + +function Position(x, y, z, stackpos) + local position = {x = 0, y = 0, z = 0} + if(isNumeric(x .. y .. z)) then + position = {x = x, y = y, z = z} + if(isNumeric(stackpos)) then + position.stackpos = stackpos + end + end + + return position +end + +function isValidPosition(position) + return (isNumeric(position.x .. position.y .. position.z) and position.x > 0 + and position.y > 0 and position.z >= 0 and position.z <= 15) +end diff --git a/data/lib/033-ip.lua b/data/lib/033-ip.lua index db33e2e..81d019c 100644 --- a/data/lib/033-ip.lua +++ b/data/lib/033-ip.lua @@ -79,7 +79,7 @@ function doConvertIpToInteger(str) if(index ~= 32) then return 0, 0 end - + index = 0 for b in maskString:gmatch("(%d+)%.?") do local t = tonumber(b) @@ -97,7 +97,7 @@ function doConvertIpToInteger(str) if(index ~= 32) then return 0, 0 end - + return ip, mask end diff --git a/data/lib/034-exhaustion.lua b/data/lib/034-exhaustion.lua index 998f1cf..5797389 100644 --- a/data/lib/034-exhaustion.lua +++ b/data/lib/034-exhaustion.lua @@ -5,7 +5,7 @@ exhaustion = return false end - return getPlayerStorageValue(cid, storage) >= os.time(t) + return getPlayerStorageValue(cid, storage) >= os.time() end, get = function (cid, storage) @@ -15,7 +15,7 @@ exhaustion = local exhaust = getPlayerStorageValue(cid, storage) if(exhaust > 0) then - local left = exhaust - os.time(t) + local left = exhaust - os.time() if(left >= 0) then return left end @@ -25,7 +25,7 @@ exhaustion = end, set = function (cid, storage, time) - setPlayerStorageValue(cid, storage, os.time(t) + time) + setPlayerStorageValue(cid, storage, os.time() + time) end, make = function (cid, storage, time) diff --git a/data/lib/050-function.lua b/data/lib/050-function.lua index b62b302..4d90aa8 100644 --- a/data/lib/050-function.lua +++ b/data/lib/050-function.lua @@ -36,14 +36,6 @@ function doPlayerTakeItem(cid, itemid, amount) return getPlayerItemCount(cid, itemid) >= amount and doPlayerRemoveItem(cid, itemid, amount) end -function doPlayerBuyItem(cid, itemid, count, cost, charges) - return doPlayerRemoveMoney(cid, cost) and doPlayerGiveItem(cid, itemid, count, charges) -end - -function doPlayerBuyItemContainer(cid, containerid, itemid, count, cost, charges) - return doPlayerRemoveMoney(cid, cost) and doPlayerGiveItemContainer(cid, containerid, itemid, count, charges) -end - function doPlayerSellItem(cid, itemid, count, cost) if(not doPlayerTakeItem(cid, itemid, count)) then return false @@ -83,8 +75,12 @@ function doPlayerDepositMoney(cid, amount) return true end +function doPlayerAddStamina(cid, minutes) + return doPlayerSetStamina(cid, getPlayerStamina(cid) + minutes) +end + function isPremium(cid) - return (isPlayer(cid) and (getPlayerPremiumDays(cid) > 0 or getBooleanFromString(getConfigInfo('freePremium')))) + return (isPlayer(cid) and (getPlayerPremiumDays(cid) > 0 or getBooleanFromString(getConfigValue('freePremium')))) end function getMonthDayEnding(day) @@ -107,8 +103,12 @@ function getArticle(str) return str:find("[AaEeIiOoUuYy]") == 1 and "an" or "a" end -function isNumber(str) - return tonumber(str) ~= nil +function doNumberFormat(i) + local str, found = string.gsub(i, "(%d)(%d%d%d)$", "%1,%2", 1), 0 + repeat + str, found = string.gsub(str, "(%d)(%d%d%d),", "%1,%2,", 1) + until found == 0 + return str end function doPlayerAddAddons(cid, addon) @@ -121,31 +121,18 @@ function doPlayerAddAddons(cid, addon) end end -function doPlayerWithdrawAllMoney(cid) - return doPlayerWithdrawMoney(cid, getPlayerBalance(cid)) -end - -function doPlayerDepositAllMoney(cid) - return doPlayerDepositMoney(cid, getPlayerMoney(cid)) -end - -function doPlayerTransferAllMoneyTo(cid, target) - return doPlayerTransferMoneyTo(cid, target, getPlayerBalance(cid)) -end - -function playerExists(name) - return getPlayerGUIDByName(name) ~= 0 -end - -function getTibiaTime() - local minutes = getWorldTime() - local hours = 0 +function getTibiaTime(num) + local minutes, hours = getWorldTime(), 0 while (minutes > 60) do hours = hours + 1 minutes = minutes - 60 end - return {hours = hours, minutes = minutes} + if(num) then + return {hours = hours, minutes = minutes} + end + + return {hours = hours < 10 and '0' .. hours or '' .. hours, minutes = minutes < 10 and '0' .. minutes or '' .. minutes} end function doWriteLogFile(file, text) @@ -165,133 +152,23 @@ function getExperienceForLevel(lv) end function doMutePlayer(cid, time) - local condition = createConditionObject(CONDITION_MUTED) - setConditionParam(condition, CONDITION_PARAM_TICKS, time * 1000) - return doAddCondition(cid, condition) -end - -function getPlayerGroupName(cid) - return getGroupInfo(getPlayerGroupId(cid)).name -end - -function getPlayerVocationName(cid) - return getVocationInfo(getPlayerVocation(cid)).name -end - -function getPromotedVocation(vid) - return getVocationInfo(vid).promotedVocation -end - -function doPlayerRemovePremiumDays(cid, days) - return doPlayerAddPremiumDays(cid, -days) -end - -function getPlayerMasterPos(cid) - return getTownTemplePosition(getPlayerTown(cid)) -end - -function getHouseOwner(houseId) - return getHouseInfo(houseId).owner -end - -function getHouseName(houseId) - return getHouseInfo(houseId).name -end - -function getHouseEntry(houseId) - return getHouseInfo(houseId).entry -end - -function getHouseRent(houseId) - return getHouseInfo(houseId).rent -end + local condition = createConditionObject(CONDITION_MUTED, (time == -1 and time or time * 1000)) + return doAddCondition(cid, condition, false) -function getHousePrice(houseId) - return getHouseInfo(houseId).price end -function getHouseTown(houseId) - return getHouseInfo(houseId).town -end - -function getHouseTilesCount(houseId) - return getHouseInfo(houseId).tiles -end - -function getItemNameById(itemid) - return getItemDescriptionsById(itemid).name -end - -function getItemPluralNameById(itemid) - return getItemDescriptionsById(itemid).plural -end - -function getItemArticleById(itemid) - return getItemDescriptionsById(itemid).article -end - -function getItemName(uid) - return getItemDescriptions(uid).name -end - -function getItemPluralName(uid) - return getItemDescriptions(uid).plural -end - -function getItemArticle(uid) - return getItemDescriptions(uid).article -end - -function getItemText(uid) - return getItemDescriptions(uid).text -end - -function getItemSpecialDescription(uid) - return getItemDescriptions(uid).special -end - -function getItemWriter(uid) - return getItemDescriptions(uid).writer -end - -function getItemDate(uid) - return getItemDescriptions(uid).date -end - -function getTilePzInfo(pos) - return getTileInfo(pos).protection -end - -function getTileZoneInfo(pos) - local tmp = getTileInfo(pos) - if(tmp.pvp) then - return 2 - end - - if(tmp.nopvp) then - return 1 - end - - return 0 -end - -function doShutdown() - return doSetGameState(GAMESTATE_SHUTDOWN) -end - -function doSummonCreature(name, pos, displayError) - local displayError, cid = displayError or true, doCreateMonster(name, pos, displayError) +function doSummonCreature(name, pos) + local cid = doCreateMonster(name, pos, false, false) if(not cid) then - cid = doCreateNpc(name, pos, displayError) + cid = doCreateNpc(name, pos) end return cid end -function getOnlinePlayers() - local tmp = getPlayersOnline() +function getPlayersOnlineEx() local players = {} - for i, cid in ipairs(tmp) do + for i, cid in ipairs(getPlayersOnline()) do table.insert(players, getCreatureName(cid)) end @@ -308,11 +185,7 @@ function isPlayer(cid) end function isPlayerGhost(cid) - if(not isPlayer(cid)) then - return false - end - - return getCreatureCondition(cid, CONDITION_GAMEMASTER, GAMEMASTER_INVISIBLE) or getPlayerFlagValue(cid, PLAYERFLAG_CANNOTBESEEN) + return isPlayer(cid) and (getCreatureCondition(cid, CONDITION_GAMEMASTER, GAMEMASTER_INVISIBLE, CONDITIONID_DEFAULT) or getPlayerFlagValue(cid, PLAYERFLAG_CANNOTBESEEN)) end function isMonster(cid) @@ -320,19 +193,17 @@ function isMonster(cid) end function isNpc(cid) - return isCreature(cid) and cid >= AUTOID_NPCS + -- Npc IDs are over int32_t range (which is default for lua_pushnumber), + -- therefore number is always a negative value. + return isCreature(cid) and (cid < 0 or cid >= AUTOID_NPCS) end -function doPlayerSetExperienceRate(cid, value) - return doPlayerSetRate(cid, SKILL__LEVEL, value) -end - -function doPlayerSetMagicRate(cid, value) - return doPlayerSetRate(cid, SKILL__MAGLEVEL, value) +function isUnderWater(cid) + return isInArray(underWater, getTileInfo(getCreaturePosition(cid)).itemid) end function doPlayerAddLevel(cid, amount, round) - local experience, level = 0, getPlayerLevel(cid) + local experience, level, amount = 0, getPlayerLevel(cid), amount or 1 if(amount > 0) then experience = getExperienceForLevel(level + amount) - (round and getPlayerExperience(cid) or getExperienceForLevel(level)) else @@ -343,43 +214,33 @@ function doPlayerAddLevel(cid, amount, round) end function doPlayerAddMagLevel(cid, amount) + local amount = amount or 1 for i = 1, amount do - doPlayerAddSpentMana(cid, (getPlayerRequiredMana(cid, getPlayerMagLevel(cid, true) + 1) - getPlayerSpentMana(cid)) / getConfigInfo('rateMagic')) + doPlayerAddSpentMana(cid, getPlayerRequiredMana(cid, getPlayerMagLevel(cid, true) + 1) - getPlayerSpentMana(cid), false) end + return true -end +end function doPlayerAddSkill(cid, skill, amount, round) + local amount = amount or 1 if(skill == SKILL__LEVEL) then return doPlayerAddLevel(cid, amount, round) elseif(skill == SKILL__MAGLEVEL) then return doPlayerAddMagLevel(cid, amount) end - return doPlayerAddSkillTry(cid, skill, (getPlayerRequiredSkillTries(cid, skill, getPlayerSkillLevel(cid, skill) + 1) - getPlayerSkillTries(cid, skill)) / getConfigInfo('rateSkill')) -end - -function getPartyLeader(cid) - local party = getPartyMembers(cid) - if(type(party) ~= 'table') then - return 0 + for i = 1, amount do + doPlayerAddSkillTry(cid, skill, getPlayerRequiredSkillTries(cid, skill, getPlayerSkillLevel(cid, skill) + 1) - getPlayerSkillTries(cid, skill), false) end - return party[1] -end - -function isInParty(cid) - return type(getPartyMembers(cid)) == 'table' + return true end function isPrivateChannel(channelId) return channelId >= CHANNEL_PRIVATE end -function doPlayerResetIdleTime(cid) - return doPlayerSetIdleTime(cid, 0) -end - function doBroadcastMessage(text, class) local class = class or MESSAGE_STATUS_WARNING if(type(class) == 'string') then @@ -393,8 +254,7 @@ function doBroadcastMessage(text, class) return false end - local players = getPlayersOnline() - for _, pid in ipairs(players) do + for _, pid in ipairs(getPlayersOnline()) do doPlayerSendTextMessage(pid, class, text) end @@ -419,8 +279,7 @@ function doPlayerBroadcastMessage(cid, text, class, checkFlag, ghost) return false end - local players = getPlayersOnline() - for _, pid in ipairs(players) do + for _, pid in ipairs(getPlayersOnline()) do doCreatureSay(cid, text, class, ghost, pid) end @@ -428,27 +287,14 @@ function doPlayerBroadcastMessage(cid, text, class, checkFlag, ghost) return true end -function getBooleanFromString(input) - local tmp = type(input) - if(tmp == 'boolean') then - return input - end - - if(tmp == 'number') then - return input > 0 - end - - local str = string.lower(tostring(input)) - return (str == "yes" or str == "true" or (tonumber(str) ~= nil and tonumber(str) > 0)) -end - function doCopyItem(item, attributes) - local attributes = attributes or false + local attributes = ((type(attributes) == 'table') and attributes or { "aid" }) local ret = doCreateItemEx(item.itemid, item.type) - if(attributes) then - if(item.actionid > 0) then - doItemSetAttribute(ret, "aid", item.actionid) + for _, key in ipairs(attributes) do + local value = getItemAttribute(item.uid, key) + if(value ~= nil) then + doItemSetAttribute(ret, key, value) end end @@ -464,34 +310,6 @@ function doCopyItem(item, attributes) return getThing(ret) end -function doRemoveThing(uid) - if(isCreature(uid)) then - return doRemoveCreature(uid) - end - - return doRemoveItem(uid) -end - -function setAttackFormula(combat, type, minl, maxl, minm, maxm, min, max) - local min, max = min or 0, max or 0 - return setCombatFormula(combat, type, -1, 0, -1, 0, minl, maxl, minm, maxm, min, max) -end - -function setHealingFormula(combat, type, minl, maxl, minm, maxm, min, max) - local min, max = min or 0, max or 0 - return setCombatFormula(combat, type, 1, 0, 1, 0, minl, maxl, minm, maxm, min, max) -end - -function doChangeTypeItem(uid, subtype) - local thing = getThing(uid) - if(thing.itemid < 100) then - return false - end - - local subtype = subtype or 1 - return doTransformItem(thing.uid, thing.itemid, subtype) -end - function doSetItemText(uid, text, writer, date) local thing = getThing(uid) if(thing.itemid < 100) then @@ -509,33 +327,6 @@ function doSetItemText(uid, text, writer, date) return true end -function getFluidSourceType(itemid) - local item = getItemInfo(itemid) - return item and item.fluidSource or false -end - -function getDepotId(uid) - return getItemAttribute(uid, "depotid") or false -end - -function getItemDescriptions(uid) - local thing = getThing(uid) - if(thing.itemid < 100) then - return false - end - - local item = getItemInfo(thing.itemid) - return { - name = getItemAttribute(uid, "name") or item.name, - plural = getItemAttribute(uid, "pluralname") or item.plural, - article = getItemAttribute(uid, "article") or item.article, - special = getItemAttribute(uid, "description") or "", - text = getItemAttribute(uid, "text") or "", - writer = getItemAttribute(uid, "writer") or "", - date = getItemAttribute(uid, "date") or 0 - } -end - function getItemWeightById(itemid, count, precision) local item, count, precision = getItemInfo(itemid), count or 1, precision or false if(not item) then @@ -548,115 +339,78 @@ function getItemWeightById(itemid, count, precision) end local weight = item.weight * count - --[[if(precision) then - return weight - end - - local t = string.explode(tostring(weight), ".") - if(table.maxn(t) == 2) then - return tonumber(t[1] .. "." .. string.sub(t[2], 1, 2)) - end]]-- - - return weight + return precission and weight or math.round(weight, 2) end -function getItemWeaponType(uid) - local thing = getThing(uid) - if(thing.itemid < 100) then - return false - end - - return getItemInfo(thing.itemid).weaponType +function choose(...) + local arg = {...} + return arg[math.random(1, table.maxn(arg))] end -function getItemRWInfo(uid) - local thing = getThing(uid) - if(thing.itemid < 100) then +function doPlayerAddExpEx(cid, amount) + if(not doPlayerAddExp(cid, amount)) then return false end - local item, flags = getItemInfo(thing.itemid), 0 - if(item.readable) then - flags = 1 - end + local position = getThingPosition(cid) + doPlayerSendTextMessage(cid, MESSAGE_EXPERIENCE, "You gained " .. amount .. " experience.", amount, COLOR_WHITE, position) - if(item.writable) then - flags = flags + 2 + local spectators, name = getSpectators(position, 7, 7), getCreatureName(cid) + for _, pid in ipairs(spectators) do + if(isPlayer(pid) and cid ~= pid) then + doPlayerSendTextMessage(pid, MESSAGE_EXPERIENCE_OTHERS, name .. " gained " .. amount .. " experience.", amount, COLOR_WHITE, position) + end end - return flags -end - -function getItemLevelDoor(itemid) - local item = getItemInfo(itemid) - return item and item.levelDoor or false -end - -function isItemStackable(itemid) - local item = getItemInfo(itemid) - return item and item.stackable or false -end - -function isItemRune(itemid) - local item = getItemInfo(itemid) - return item and item.clientCharges or false -end - -function isItemDoor(itemid) - local item = getItemInfo(itemid) - return item and item.type == 5 or false -end - -function isItemContainer(itemid) - local item = getItemInfo(itemid) - return item and item.group == 2 or false -end - -function isItemFluidContainer(itemid) - local item = getItemInfo(itemid) - return item and item.group == 12 or false + return true end -function isItemMovable(itemid) - local item = getItemInfo(itemid) - return item and item.movable or false -end +function getItemTopParent(uid) + local parent = getItemParent(uid) + if(not parent or parent.uid == 0) then + return nil + end -function isCorpse(uid) - local thing = getThing(uid) - if(thing.itemid < 100) then - return false + while(true) do + local tmp = getItemParent(parent.uid) + if(tmp and tmp.uid ~= 0) then + parent = tmp + else + break + end end - local item = getItemInfo(thing.itemid) - return item and item.corpseType ~= 0 or false + return parent end -function getContainerCapById(itemid) - local item = getItemInfo(itemid) - if(not item or item.group ~= 2) then - return false +function getItemHolder(uid) + local parent = getItemParent(uid) + if(not parent or parent.uid == 0) then + return nil end - return item.maxItems -end - -function getMonsterAttackSpells(name) - local monster = getMonsterInfo(name) - return monster and monster.attacks or false -end + local holder = nil + while(true) do + local tmp = getItemParent(parent.uid) + if(tmp and tmp.uid ~= 0) then + if(tmp.itemid == 1) then -- a creature + holder = tmp + break + end -function getMonsterHealingSpells(name) - local monster = getMonsterInfo(name) - return monster and monster.defenses or false -end + parent = tmp + else + break + end + end -function getMonsterLootList(name) - local monster = getMonsterInfo(name) - return monster and monster.loot or false + return holder end -function getMonsterSummonList(name) - local monster = getMonsterInfo(name) - return monster and monster.summons or false -end +function valid(f) + return function(p, ...) + if(isCreature(p)) then + return f(p, ...) + end + end +end \ No newline at end of file diff --git a/data/lib/100-shortcut.lua b/data/lib/100-shortcut.lua new file mode 100644 index 0000000..23207ce --- /dev/null +++ b/data/lib/100-shortcut.lua @@ -0,0 +1,351 @@ +function doSendAnimatedText(...) + print("doSendAnimatedText is now a deprecated function.") + return true +end + +function doPlayerSendToChannel(cid, target, type, text, channel, time) + return doCreatureChannelSay(cid, target, text, type, channel) +end + +function getItemWeaponType(uid) + local thing = getThing(uid) + if(thing.itemid < 100) then + return false + end + + return getItemInfo(thing.itemid).weaponType +end + +function getItemRWInfo(uid) + local thing = getThing(uid) + if(thing.itemid < 100) then + return false + end + + local item, flags = getItemInfo(thing.itemid), 0 + if(item.readable) then + flags = 1 + end + + if(item.writable) then + flags = flags + 2 + end + + return flags +end + +function getItemLevelDoor(itemid) + local item = getItemInfo(itemid) + return item and item.levelDoor or false +end + +function isContainer(uid) + local thing = getThing(uid) + return thing.uid > 0 and thing.items ~= nil +end + +function isItemStackable(itemid) + local item = getItemInfo(itemid) + return item and item.stackable or false +end + +function isItemRune(itemid) + local item = getItemInfo(itemid) + return item and item.type == ITEM_TYPE_RUNE or false +end + +function isItemDoor(itemid) + local item = getItemInfo(itemid) + return item and item.type == ITEM_TYPE_DOOR or false +end + +function isItemContainer(itemid) + local item = getItemInfo(itemid) + return item and item.group == ITEM_GROUP_CONTAINER or false +end + +function isItemFluidContainer(itemid) + local item = getItemInfo(itemid) + return item and item.group == ITEM_GROUP_FLUID or false +end + +function isItemMovable(itemid) + local item = getItemInfo(itemid) + return item and item.movable or false +end + +function isCorpse(uid) + local thing = getThing(uid) + if(thing.itemid < 100) then + return false + end + + local item = getItemInfo(thing.itemid) + return item and item.corpseType ~= 0 or false +end + +function getContainerCapById(itemid) + local item = getItemInfo(itemid) + if(not item or item.group ~= 2) then + return false + end + + return item.maxItems +end + +function getMonsterAttackSpells(name) + local monster = getMonsterInfo(name) + return monster and monster.attacks or false +end + +function getMonsterHealingSpells(name) + local monster = getMonsterInfo(name) + return monster and monster.defenses or false +end + +function getMonsterLootList(name) + local monster = getMonsterInfo(name) + return monster and monster.loot or false +end + +function getMonsterSummonList(name) + local monster = getMonsterInfo(name) + return monster and monster.summons or false +end + +function doItemSetActionId(uid, aid) + return doItemSetAttribute(uid, "aid", aid) +end + +function getFluidSourceType(itemid) + local item = getItemInfo(itemid) + return item and item.fluidSource or false +end + +function getDepotId(uid) + return getItemAttribute(uid, "depotid") or false +end + +function getItemDescriptions(uid) + local thing = getThing(uid) + if(thing.itemid < 100) then + return false + end + + local item = getItemInfo(thing.itemid) + return { + name = getItemAttribute(uid, "name") or item.name, + plural = getItemAttribute(uid, "pluralname") or item.plural, + article = getItemAttribute(uid, "article") or item.article, + special = getItemAttribute(uid, "description") or "", + text = getItemAttribute(uid, "text") or "", + writer = getItemAttribute(uid, "writer") or "", + date = getItemAttribute(uid, "date") or 0 + } +end + +function doRemoveThing(uid) + if(isCreature(uid)) then + return doRemoveCreature(uid) + end + + return doRemoveItem(uid) +end + +function setAttackFormula(combat, type, minl, maxl, minm, maxm, min, max) + local min, max = min or 0, max or 0 + return setCombatFormula(combat, type, -1, 0, -1, 0, minl, maxl, minm, maxm, -min, -max) +end + +function setHealingFormula(combat, type, minl, maxl, minm, maxm, min, max) + local min, max = min or 0, max or 0 + return setCombatFormula(combat, type, 1, 0, 1, 0, minl, maxl, minm, maxm, min, max) +end + +function doChangeTypeItem(uid, subtype) + local thing = getThing(uid) + if(thing.itemid < 100) then + return false + end + + local subtype = subtype or 1 + return doTransformItem(thing.uid, thing.itemid, subtype) +end + +function doPlayerResetIdleTime(cid) + return doPlayerSetIdleTime(cid, 0) +end + +function doPlayerSetExperienceRate(cid, value) + return doPlayerSetRate(cid, SKILL__LEVEL, value) +end + +function doPlayerSetMagicRate(cid, value) + return doPlayerSetRate(cid, SKILL__MAGLEVEL, value) +end + +function isSummon(cid) + return getCreatureMaster(cid) and getCreatureMaster(cid) ~= cid +end + +function getPartyLeader(cid) + local party = getPartyMembers(cid) + if(type(party) ~= 'table') then + return 0 + end + + return party[1] +end + +function isInParty(cid) + return type(getPartyMembers(cid)) == 'table' +end + +function doShutdown() + return doSetGameState(GAMESTATE_SHUTDOWN) +end + +function getPlayerGroupName(cid) + return getGroupInfo(getPlayerGroupId(cid)).name +end + +function getPlayerVocationName(cid) + return getVocationInfo(getPlayerVocation(cid)).name +end + +function getPromotedVocation(vid) + return getVocationInfo(vid).promotedVocation +end + +function doPlayerRemovePremiumDays(cid, days) + return doPlayerAddPremiumDays(cid, -days) +end + +function getPlayerMasterPos(cid) + return getTownTemplePosition(getPlayerTown(cid)) +end + +function getHouseOwner(houseId) + return getHouseInfo(houseId).owner +end + +function getHouseName(houseId) + return getHouseInfo(houseId).name +end + +function getHouseEntry(houseId) + return getHouseInfo(houseId).entry +end + +function getHouseRent(houseId) + return getHouseInfo(houseId).rent +end + +function getHousePrice(houseId) + return getHouseInfo(houseId).price +end + +function getHouseTown(houseId) + return getHouseInfo(houseId).town +end + +function getHouseDoorsCount(houseId) + return table.maxn(getHouseInfo(houseId).doors) +end + +function getHouseBedsCount(houseId) + return table.maxn(getHouseInfo(houseId).beds) +end + +function getHouseTilesCount(houseId) + return table.maxn(getHouseInfo(houseId).tiles) +end + +function getItemNameById(itemid) + return getItemDescriptionsById(itemid).name +end + +function getItemPluralNameById(itemid) + return getItemDescriptionsById(itemid).plural +end + +function getItemArticleById(itemid) + return getItemDescriptionsById(itemid).article +end + +function getItemName(uid) + return getItemDescriptions(uid).name +end + +function getItemPluralName(uid) + return getItemDescriptions(uid).plural +end + +function getItemArticle(uid) + return getItemDescriptions(uid).article +end + +function getItemText(uid) + return getItemDescriptions(uid).text +end + +function getItemSpecialDescription(uid) + return getItemDescriptions(uid).special +end + +function doSetItemSpecialDescription(uid, str) + return doItemSetAttribute(uid, "description", str) +end + +function getItemWriter(uid) + return getItemDescriptions(uid).writer +end + +function getItemDate(uid) + return getItemDescriptions(uid).date +end + +function getTilePzInfo(pos) + return getTileInfo(pos).protection +end + +function getTileZoneInfo(pos) + local tmp = getTileInfo(pos) + if(tmp.pvp) then + return 2 + end + + if(tmp.nopvp) then + return 1 + end + + return 0 +end + +function playerExists(name, multiworld) + return getPlayerGUIDByName(name, multiworld or false) ~= nil +end + +function doPlayerWithdrawAllMoney(cid) + return doPlayerWithdrawMoney(cid, getPlayerBalance(cid)) +end + +function doPlayerDepositAllMoney(cid) + return doPlayerDepositMoney(cid, getPlayerMoney(cid)) +end + +function doPlayerTransferAllMoneyTo(cid, target) + return doPlayerTransferMoneyTo(cid, target, getPlayerBalance(cid)) +end + +function isNumeric(str) + return tonumber(str) ~= nil +end + +function doPlayerBuyItem(cid, itemid, count, cost, charges) + return doPlayerRemoveMoney(cid, cost) and doPlayerGiveItem(cid, itemid, count, charges) +end + +function doPlayerBuyItemContainer(cid, containerid, itemid, count, cost, charges) + return doPlayerRemoveMoney(cid, cost) and doPlayerGiveItemContainer(cid, containerid, itemid, count, charges) +end \ No newline at end of file diff --git a/data/lib/100-compat.lua b/data/lib/101-compat.lua similarity index 69% rename from data/lib/100-compat.lua rename to data/lib/101-compat.lua index 6d84fde..fe713ec 100644 --- a/data/lib/100-compat.lua +++ b/data/lib/101-compat.lua @@ -1,38 +1,47 @@ --[[ * File containing deprecated functions and constants used by alot of scripts and other engines ]]-- + TRUE = true FALSE = false LUA_ERROR = false LUA_NO_ERROR = true LUA_NULL = nil +TALKTYPE_CHANNEL_W = TALKTYPE_CHANNEL +TALKTYPE_CHANNEL_Y = TALKTYPE_CHANNEL +TALKTYPE_CHANNEL_O = TALKTYPE_CHANNEL_HIGHLIGHT +TALKTYPE_BROADCAST = TALKTYPE_GAMEMASTER_BROADCAST +TALKTYPE_CHANNEL_RN = TALKTYPE_GAMEMASTER_CHANNEL +TALKTYPE_PRIVATE_RED = TALKTYPE_GAMEMASTER_PRIVATE_FROM TALKTYPE_CHANNEL_R1 = TALKTYPE_CHANNEL_RN TALKTYPE_CHANNEL_R2 = TALKTYPE_CHANNEL_RA -TALKTYPE_ORANGE_1 = TALKTYPE_MONSTER +TALKTYPE_CHANNEL_RA = TALKTYPE_CHANNEL_W +TALKTYPE_ORANGE_1 = TALKTYPE_MONSTER_SAY TALKTYPE_ORANGE_2 = TALKTYPE_MONSTER_YELL +TALKTYPE_MONSTER = TALKTYPE_MONSTER_SAY -TEXTCOLOR_BLACK = 0 -TEXTCOLOR_BLUE = 5 -TEXTCOLOR_GREEN = 18 -TEXTCOLOR_TEAL = 35 -TEXTCOLOR_LIGHTGREEN = 66 -TEXTCOLOR_DARKBROWN = 78 -TEXTCOLOR_LIGHTBLUE = 89 -TEXTCOLOR_DARKPURPLE = 112 -TEXTCOLOR_BROWN = 120 -TEXTCOLOR_GREY = 129 -TEXTCOLOR_DARKRED = 144 -TEXTCOLOR_DARKPINK = 152 -TEXTCOLOR_PURPLE = 154 -TEXTCOLOR_DARKORANGE = 156 -TEXTCOLOR_RED = 180 -TEXTCOLOR_PINK = 190 -TEXTCOLOR_ORANGE = 192 -TEXTCOLOR_DARKYELLOW = 205 -TEXTCOLOR_YELLOW = 210 -TEXTCOLOR_WHITE = 215 -TEXTCOLOR_NONE = 255 +TEXTCOLOR_BLACK = COLOR_BLACK +TEXTCOLOR_BLUE = COLOR_BLUE +TEXTCOLOR_GREEN = COLOR_GREEN +TEXTCOLOR_LIGHTGREEN = COLOR_LIGHTGREEN +TEXTCOLOR_DARKBROWN = COLOR_DARKBROWN +TEXTCOLOR_LIGHTBLUE = COLOR_LIGHTBLUE +TEXTCOLOR_DARKRED = COLOR_DARKRED +TEXTCOLOR_DARKPURPLE = COLOR_DARKPURPLE +TEXTCOLOR_BROWN = COLOR_BROWN +TEXTCOLOR_GREY = COLOR_GREY +TEXTCOLOR_TEAL = COLOR_TEAL +TEXTCOLOR_DARKPINK = COLOR_DARKPINK +TEXTCOLOR_PURPLE = COLOR_PURPLE +TEXTCOLOR_DARKORANGE = COLOR_DARKORANGE +TEXTCOLOR_RED = COLOR_RED +TEXTCOLOR_PINK = COLOR_PINK +TEXTCOLOR_ORANGE = COLOR_ORANGE +TEXTCOLOR_DARKYELLOW = COLOR_DARKYELLOW +TEXTCOLOR_YELLOW = COLOR_YELLOW +TEXTCOLOR_WHITE = COLOR_WHITE +TEXTCOLOR_NONE = COLOR_NONE CONDITION_PARAM_STAT_MAXHITPOINTS = CONDITION_PARAM_STAT_MAXHEALTH CONDITION_PARAM_STAT_MAXMANAPOINTS = CONDITION_PARAM_STAT_MAXMANA @@ -43,18 +52,34 @@ CONDITION_PARAM_STAT_MAXMANAPOINTSPERCENT = CONDITION_PARAM_STAT_MAXMANAPERCENT CONDITION_PARAM_STAT_SOULPOINTSPERCENT = CONDITION_PARAM_STAT_SOULPERCENT CONDITION_PARAM_STAT_MAGICPOINTSPERCENT = CONDITION_PARAM_STAT_MAGICLEVELPERCENT +CONDITION_PHYSICAL = CONDITION_BLEEDING + STACKPOS_FIRST_ITEM_ABOVE_GROUNDTILE = 1 STACKPOS_SECOND_ITEM_ABOVE_GROUNDTILE = 2 STACKPOS_THIRD_ITEM_ABOVE_GROUNDTILE = 3 STACKPOS_FOURTH_ITEM_ABOVE_GROUNDTILE = 4 STACKPOS_FIFTH_ITEM_ABOVE_GROUNDTILE = 5 -WORLD_TYPE_NO_PVP = 1 -WORLD_TYPE_PVP = 2 -WORLD_TYPE_PVP_ENFORCED = 3 +WORLD_TYPE_NO_PVP = WORLDTYPE_OPTIONAL +WORLD_TYPE_PVP = WORLDTYPE_OPEN +WORLD_TYPE_PVP_ENFORCED = WORLDTYPE_HARDCORE + +WORLDTYPE_NO_PVP = WORLDTYPE_OPTIONAL +WORLDTYPE_PVP = WORLDTYPE_OPEN +WORLDTYPE_PVP_ENFORCED = WORLDTYPE_HARDCORE + +GUILDLEVEL_MEMBER = GUILD_MEMBER +GUILDLEVEL_VICE = GUILD_VICE +GUILDLEVEL_LEADER = GUILD_LEADER + +DATABASE_ENGINE_NONE = DATABASE_NONE +DATABASE_ENGINE_MYSQL = DATABASE_MYSQL +DATABASE_ENGINE_SQLITE = DATABASE_SQLITE +DATABASE_ENGINE_POSTGRESQL = DATABASE_POSTGRESQL +DATABASE_ENGINE_ODBC = DATABASE_ODBC -CHANNEL_STAFF = 2 -CHANNEL_COUNSELOR = 4 +CHANNEL_STAFF = 4 +CHANNEL_COUNSELOR = 2 CHANNEL_GAMECHAT = 5 CHANNEL_TRADE = 6 CHANNEL_TRADEROOK = 7 @@ -66,9 +91,17 @@ BANTYPE_BANISHMENT = 3 BANTYPE_NOTATION = 4 BANTYPE_DELETION = 3 +CONST_PROP_MOVEABLE = CONST_PROP_MOVABLE +CONST_PROP_BLOCKINGANDNOTMOVEABLE = CONST_PROP_BLOCKINGANDNOTMOVABLE + +CONDITION_EARTH = CONDITION_POISON + +STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE = STACKPOS_TOP_MOVABLE_ITEM_OR_CREATURE + +RETURNVALUE_NOTMOVEABLE = RETURNVALUE_NOTMOVABLE + SKILLS = SKILL_NAMES -table.getPos = table.find doSetCreatureDropLoot = doCreatureSetDropLoot doPlayerSay = doCreatureSay doPlayerAddMana = doCreatureAddMana @@ -83,7 +116,8 @@ getCreaturePos = getCreaturePosition creatureGetPosition = getCreaturePosition getPlayerMana = getCreatureMana getPlayerMaxMana = getCreatureMaxMana -hasCondition = getCreatureCondition +hasCondition = hasCreatureCondition +getCreatureCondition = hasCreatureCondition isMoveable = isMovable isItemMoveable = isItemMovable saveData = saveServer @@ -94,7 +128,9 @@ getCreatureSkull = getCreatureSkullType getAccountNumberByName = getAccountIdByName getIPByName = getIpByName getPlayersByIP = getPlayersByIp +getThingFromPos = getThingFromPosition getThingfromPos = getThingFromPos +getHouseFromPos = getHouseFromPosition getPlayersByAccountNumber = getPlayersByAccountId getIPByPlayerName = getIpByName getPlayersByIPNumber = getPlayersByIp @@ -109,6 +145,7 @@ cleanHouse = doCleanHouse cleanMap = doCleanMap shutdown = doShutdown mayNotMove = doCreatureSetNoMove +getTileItemsByType = getTileItemByType doPlayerSetNoMove = doCreatureSetNoMove getPlayerNoMove = getCreatureNoMove getConfigInfo = getConfigValue @@ -129,6 +166,7 @@ setPlayerStorageValue = doPlayerSetStorageValue getPlayerStorageValue = getCreatureStorage getGlobalStorageValue = getStorage setGlobalStorageValue = doSetStorage +getPlayerMount = canPlayerRideMount setPlayerBalance = doPlayerSetBalance doAddMapMark = doPlayerAddMapMark doSendTutorial = doPlayerSendTutorial @@ -144,8 +182,9 @@ hasProperty = hasItemProperty hasClient = hasPlayerClient print = std.cout getPosByDir = getPositionByDirection -db.updateQueryLimitOperator = db.updateLimiter -db.stringComparisonOperator = db.stringComparison +isNumber = isNumeric +doSetItemActionId = doItemSetActionId +getOnlinePlayers = getPlayersOnlineEx PlayerFlag_CannotUseCombat = 0 PlayerFlag_CannotAttackPlayer = 1 @@ -185,7 +224,7 @@ PlayerFlag_IgnoreSpellCheck = 34 PlayerFlag_IgnoreWeaponCheck = 35 PlayerFlag_CannotBeMuted = 36 PlayerFlag_IsAlwaysPremium = 37 -PlayerFlag_CanAnswerRuleViolations = 38 +PlayerFlag_38 = 38 PlayerFlag_39 = 39 -- ignore PlayerFlag_ShowGroupNameInsteadOfVocation = 40 PlayerFlag_HasInfiniteStamina = 41 @@ -194,6 +233,8 @@ PlayerFlag_CannotMoveCreatures = 43 PlayerFlag_CanReportBugs = 44 PlayerFlag_45 = 45 -- ignore PlayerFlag_CannotBeSeen = 46 +PlayerFlag_HideHealth = 47 +PlayerFlag_CanPassThroughAllCreatures = 48 PlayerCustomFlag_AllowIdle = 0 PlayerCustomFlag_CanSeePosition = 1 @@ -205,16 +246,20 @@ PlayerCustomFlag_CanThrowAnywhere = 6 PlayerCustomFlag_CanPushAllItems = 7 PlayerCustomFlag_CanMoveAnywhere = 8 PlayerCustomFlag_CanMoveFromFar = 9 -PlayerCustomFlag_CanLoginMultipleCharacters = 10 -PlayerCustomFlag_HasFullLight = 11 +PlayerCustomFlag_CanUseFar = 10 +PlayerCustomFlag_CanLoginMultipleCharacters = 11 PlayerCustomFlag_CanLogoutAnytime = 12 PlayerCustomFlag_HideLevel = 13 PlayerCustomFlag_IsProtected = 14 PlayerCustomFlag_IsImmune = 15 PlayerCustomFlag_NotGainSkull = 16 PlayerCustomFlag_NotGainUnjustified = 17 -PlayerCustomFlag_HideLevel = 18 -PlayerCustomFlag_IgnorePacification = 19 +PlayerCustomFlag_IgnorePacification = 18 +PlayerCustomFlag_IgnoreLoginDelay = 19 PlayerCustomFlag_CanStairhop = 20 PlayerCustomFlag_CanTurnhop = 21 PlayerCustomFlag_IgnoreHouseRent = 22 +PlayerCustomFlag_CanWearAllAddons = 23 +PlayerCustomFlag_IsWalkable = 24 +PlayerCustomFlag_CanUseAllMounts = 25 +PlayerCustomFlag_HasFullLight = 26 \ No newline at end of file diff --git a/data/monster/Amazons/amazon.xml b/data/monster/Amazons/amazon.xml index 043164c..46bb8af 100644 --- a/data/monster/Amazons/amazon.xml +++ b/data/monster/Amazons/amazon.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -33,19 +33,15 @@ - - + + + + - - - - - - - - - + + + diff --git a/data/monster/Amazons/valkyrie.xml b/data/monster/Amazons/valkyrie.xml index 380ef9a..677e5fc 100644 --- a/data/monster/Amazons/valkyrie.xml +++ b/data/monster/Amazons/valkyrie.xml @@ -18,7 +18,7 @@ - + @@ -39,23 +39,21 @@ - - - - - - + + + + + - - - - - - - - - - - + + + + + + + + + + diff --git a/data/monster/Annelids/carrion worm.xml b/data/monster/Annelids/carrion worm.xml index 523518f..12d975a 100644 --- a/data/monster/Annelids/carrion worm.xml +++ b/data/monster/Annelids/carrion worm.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -27,26 +27,14 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Annelids/rotworm.xml b/data/monster/Annelids/rotworm.xml index 327749f..23938cb 100644 --- a/data/monster/Annelids/rotworm.xml +++ b/data/monster/Annelids/rotworm.xml @@ -1,4 +1,4 @@ - + @@ -18,23 +18,18 @@ - + - + - - - - - - - - - - - - + + + + + + + diff --git a/data/monster/Apes/kongra.xml b/data/monster/Apes/kongra.xml index 67e2c73..caf7d05 100644 --- a/data/monster/Apes/kongra.xml +++ b/data/monster/Apes/kongra.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -38,17 +38,13 @@ - - - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Apes/merlkin.xml b/data/monster/Apes/merlkin.xml index 0b87aef..9ed34d9 100644 --- a/data/monster/Apes/merlkin.xml +++ b/data/monster/Apes/merlkin.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -44,8 +44,6 @@ - - @@ -56,14 +54,14 @@ - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Apes/sibang.xml b/data/monster/Apes/sibang.xml index 7923cb9..bfdf099 100644 --- a/data/monster/Apes/sibang.xml +++ b/data/monster/Apes/sibang.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -35,8 +35,6 @@ - - @@ -47,12 +45,13 @@ - - - - - - - + + + + + + + + diff --git a/data/monster/Aracnids/crystal spider.xml b/data/monster/Arachnids/crystal spider.xml similarity index 57% rename from data/monster/Aracnids/crystal spider.xml rename to data/monster/Arachnids/crystal spider.xml index b2f83b4..8a66dfe 100644 --- a/data/monster/Aracnids/crystal spider.xml +++ b/data/monster/Arachnids/crystal spider.xml @@ -1,4 +1,4 @@ - + @@ -19,14 +19,14 @@ - + - - + + @@ -40,7 +40,6 @@ - @@ -50,22 +49,22 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/data/monster/Aracnids/giant spider.xml b/data/monster/Arachnids/giant spider.xml similarity index 55% rename from data/monster/Aracnids/giant spider.xml rename to data/monster/Arachnids/giant spider.xml index 89198f1..1cb1706 100644 --- a/data/monster/Aracnids/giant spider.xml +++ b/data/monster/Arachnids/giant spider.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -35,8 +35,6 @@ - - @@ -45,24 +43,19 @@ - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - - diff --git a/data/monster/Aracnids/poison spider.xml b/data/monster/Arachnids/poison spider.xml similarity index 68% rename from data/monster/Aracnids/poison spider.xml rename to data/monster/Arachnids/poison spider.xml index d90fb6f..ad2be90 100644 --- a/data/monster/Aracnids/poison spider.xml +++ b/data/monster/Arachnids/poison spider.xml @@ -1,4 +1,4 @@ - + @@ -18,21 +18,15 @@ - + - - - - - - - - + + diff --git a/data/monster/Arachnids/sandstone scorpion.xml b/data/monster/Arachnids/sandstone scorpion.xml new file mode 100644 index 0000000..11e9ed9 --- /dev/null +++ b/data/monster/Arachnids/sandstone scorpion.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Aracnids/scorpion.xml b/data/monster/Arachnids/scorpion.xml similarity index 80% rename from data/monster/Aracnids/scorpion.xml rename to data/monster/Arachnids/scorpion.xml index 294175d..8c72678 100644 --- a/data/monster/Aracnids/scorpion.xml +++ b/data/monster/Arachnids/scorpion.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -27,4 +27,7 @@ + + + diff --git a/data/monster/Aracnids/spider.xml b/data/monster/Arachnids/spider.xml similarity index 76% rename from data/monster/Aracnids/spider.xml rename to data/monster/Arachnids/spider.xml index e8c8406..b41cc2a 100644 --- a/data/monster/Aracnids/spider.xml +++ b/data/monster/Arachnids/spider.xml @@ -1,4 +1,4 @@ - + @@ -18,13 +18,14 @@ - + - + + diff --git a/data/monster/Aracnids/tarantula.xml b/data/monster/Arachnids/tarantula.xml similarity index 71% rename from data/monster/Aracnids/tarantula.xml rename to data/monster/Arachnids/tarantula.xml index 8f36a6a..ec5ee9a 100644 --- a/data/monster/Aracnids/tarantula.xml +++ b/data/monster/Arachnids/tarantula.xml @@ -18,7 +18,7 @@ - + @@ -40,15 +40,11 @@ - - - - - - - - - - + + + + + + diff --git a/data/monster/Arachnids/wailing widow.xml b/data/monster/Arachnids/wailing widow.xml new file mode 100644 index 0000000..d979ba6 --- /dev/null +++ b/data/monster/Arachnids/wailing widow.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Arena/greenhorn/achad.xml b/data/monster/Arena/greenhorn/achad.xml index 3ced840..7460785 100644 --- a/data/monster/Arena/greenhorn/achad.xml +++ b/data/monster/Arena/greenhorn/achad.xml @@ -1,21 +1,30 @@ - + - + + + + + - + - + + + + + + diff --git a/data/monster/Arena/greenhorn/axeitus headbanger.xml b/data/monster/Arena/greenhorn/axeitus headbanger.xml index 6440def..ea5453a 100644 --- a/data/monster/Arena/greenhorn/axeitus headbanger.xml +++ b/data/monster/Arena/greenhorn/axeitus headbanger.xml @@ -1,15 +1,20 @@ - + + + + + + - + - + @@ -18,6 +23,11 @@ + + + + + diff --git a/data/monster/Arena/greenhorn/bloodpaw.xml b/data/monster/Arena/greenhorn/bloodpaw.xml index f0592c0..8131dea 100644 --- a/data/monster/Arena/greenhorn/bloodpaw.xml +++ b/data/monster/Arena/greenhorn/bloodpaw.xml @@ -1,17 +1,29 @@ - + - + + + + + + + + - + + + + + + diff --git a/data/monster/Arena/greenhorn/bovinus.xml b/data/monster/Arena/greenhorn/bovinus.xml index 58e5fb2..881118c 100644 --- a/data/monster/Arena/greenhorn/bovinus.xml +++ b/data/monster/Arena/greenhorn/bovinus.xml @@ -1,16 +1,28 @@ - + - + + + + + + + + - + + + + + + diff --git a/data/monster/Arena/greenhorn/colerian the barbarian.xml b/data/monster/Arena/greenhorn/colerian the barbarian.xml index 09e8ed7..00cb973 100644 --- a/data/monster/Arena/greenhorn/colerian the barbarian.xml +++ b/data/monster/Arena/greenhorn/colerian the barbarian.xml @@ -1,15 +1,20 @@ - + + + + + + - + - + @@ -19,6 +24,11 @@ + + + + + diff --git a/data/monster/Arena/greenhorn/cursed gladiator.xml b/data/monster/Arena/greenhorn/cursed gladiator.xml index a600089..b3fa051 100644 --- a/data/monster/Arena/greenhorn/cursed gladiator.xml +++ b/data/monster/Arena/greenhorn/cursed gladiator.xml @@ -1,15 +1,20 @@ - + + + + + + - + - + @@ -20,6 +25,8 @@ + + diff --git a/data/monster/Arena/greenhorn/frostfur.xml b/data/monster/Arena/greenhorn/frostfur.xml index f1b43e5..9bb2e7f 100644 --- a/data/monster/Arena/greenhorn/frostfur.xml +++ b/data/monster/Arena/greenhorn/frostfur.xml @@ -1,18 +1,29 @@ - + - - + + + + + + + + - + + + + + + diff --git a/data/monster/Arena/greenhorn/orcus the cruel.xml b/data/monster/Arena/greenhorn/orcus the cruel.xml index 47ccb15..4880baf 100644 --- a/data/monster/Arena/greenhorn/orcus the cruel.xml +++ b/data/monster/Arena/greenhorn/orcus the cruel.xml @@ -1,16 +1,20 @@ - + - + + + + + - + - + @@ -22,6 +26,8 @@ + + diff --git a/data/monster/Arena/greenhorn/rocky.xml b/data/monster/Arena/greenhorn/rocky.xml index 239c4d0..0b4031a 100644 --- a/data/monster/Arena/greenhorn/rocky.xml +++ b/data/monster/Arena/greenhorn/rocky.xml @@ -1,17 +1,20 @@ - + - + + + + + - - + - + @@ -23,6 +26,11 @@ + + + + + diff --git a/data/monster/Arena/greenhorn/the hairy one.xml b/data/monster/Arena/greenhorn/the hairy one.xml index 1134bda..654fa05 100644 --- a/data/monster/Arena/greenhorn/the hairy one.xml +++ b/data/monster/Arena/greenhorn/the hairy one.xml @@ -1,21 +1,31 @@ - + - + + + + + - + + - + + + + + + diff --git a/data/monster/Arena/scrapper/avalanche.xml b/data/monster/Arena/scrapper/avalanche.xml index df9be7a..0fd590e 100644 --- a/data/monster/Arena/scrapper/avalanche.xml +++ b/data/monster/Arena/scrapper/avalanche.xml @@ -1,16 +1,20 @@ - + - + + + + + - + - + @@ -27,6 +31,8 @@ + + diff --git a/data/monster/Arena/scrapper/drasilla.xml b/data/monster/Arena/scrapper/drasilla.xml index abc9f59..0857b26 100644 --- a/data/monster/Arena/scrapper/drasilla.xml +++ b/data/monster/Arena/scrapper/drasilla.xml @@ -2,24 +2,20 @@ - - + - - - - + @@ -38,6 +34,8 @@ + + diff --git a/data/monster/Arena/scrapper/grimgor guteater.xml b/data/monster/Arena/scrapper/grimgor guteater.xml index 257786c..90223cc 100644 --- a/data/monster/Arena/scrapper/grimgor guteater.xml +++ b/data/monster/Arena/scrapper/grimgor guteater.xml @@ -2,24 +2,19 @@ - - + - - - - - + @@ -31,6 +26,8 @@ + + diff --git a/data/monster/Arena/scrapper/kreebosh the exile.xml b/data/monster/Arena/scrapper/kreebosh the exile.xml index 1dfee21..101842e 100644 --- a/data/monster/Arena/scrapper/kreebosh the exile.xml +++ b/data/monster/Arena/scrapper/kreebosh the exile.xml @@ -1,16 +1,20 @@ - + + + + + + - - + - + @@ -36,6 +40,8 @@ + + diff --git a/data/monster/Arena/scrapper/slim.xml b/data/monster/Arena/scrapper/slim.xml index 6dd289a..6ee5433 100644 --- a/data/monster/Arena/scrapper/slim.xml +++ b/data/monster/Arena/scrapper/slim.xml @@ -2,7 +2,6 @@ - @@ -12,14 +11,10 @@ - - - - - + @@ -34,6 +29,8 @@ + + diff --git a/data/monster/Arena/scrapper/spirit of earth.xml b/data/monster/Arena/scrapper/spirit of earth.xml index fca1830..7c62f0b 100644 --- a/data/monster/Arena/scrapper/spirit of earth.xml +++ b/data/monster/Arena/scrapper/spirit of earth.xml @@ -2,23 +2,19 @@ - - + - - - - + @@ -26,6 +22,8 @@ + + diff --git a/data/monster/Arena/scrapper/spirit of fire.xml b/data/monster/Arena/scrapper/spirit of fire.xml index eb2cdc3..0e6f58f 100644 --- a/data/monster/Arena/scrapper/spirit of fire.xml +++ b/data/monster/Arena/scrapper/spirit of fire.xml @@ -2,23 +2,19 @@ - - + - - - - + @@ -33,6 +29,8 @@ + + diff --git a/data/monster/Arena/scrapper/spirit of water.xml b/data/monster/Arena/scrapper/spirit of water.xml index c275b43..168eb9d 100644 --- a/data/monster/Arena/scrapper/spirit of water.xml +++ b/data/monster/Arena/scrapper/spirit of water.xml @@ -2,7 +2,6 @@ - @@ -11,19 +10,15 @@ - - - - + - - + - + @@ -33,6 +28,8 @@ + + diff --git a/data/monster/Arena/scrapper/the dark dancer.xml b/data/monster/Arena/scrapper/the dark dancer.xml index f9cb142..180c2a3 100644 --- a/data/monster/Arena/scrapper/the dark dancer.xml +++ b/data/monster/Arena/scrapper/the dark dancer.xml @@ -1,17 +1,20 @@ - + - + + + + + - - + - + @@ -40,6 +43,8 @@ + + diff --git a/data/monster/Arena/scrapper/the hag.xml b/data/monster/Arena/scrapper/the hag.xml index 9949359..2f54f61 100644 --- a/data/monster/Arena/scrapper/the hag.xml +++ b/data/monster/Arena/scrapper/the hag.xml @@ -1,17 +1,20 @@ - + - + + + + + - - + @@ -31,6 +34,8 @@ + + diff --git a/data/monster/Arena/warlord/darakan the executioner.xml b/data/monster/Arena/warlord/darakan the executioner.xml index a9881e5..c6a65d1 100644 --- a/data/monster/Arena/warlord/darakan the executioner.xml +++ b/data/monster/Arena/warlord/darakan the executioner.xml @@ -2,24 +2,19 @@ - - - + + - - - - - + @@ -34,6 +29,8 @@ + + diff --git a/data/monster/Arena/warlord/deathbringer.xml b/data/monster/Arena/warlord/deathbringer.xml index 33c4927..e8ee7d1 100644 --- a/data/monster/Arena/warlord/deathbringer.xml +++ b/data/monster/Arena/warlord/deathbringer.xml @@ -2,23 +2,19 @@ - - + - - - - + @@ -43,6 +39,8 @@ + + diff --git a/data/monster/Arena/warlord/fallen mooh'tah master ghar.xml b/data/monster/Arena/warlord/fallen mooh'tah master ghar.xml index 7ba23dc..d36a1f5 100644 --- a/data/monster/Arena/warlord/fallen mooh'tah master ghar.xml +++ b/data/monster/Arena/warlord/fallen mooh'tah master ghar.xml @@ -1,16 +1,20 @@ - + - + + + + + - + - + @@ -30,6 +34,8 @@ + + diff --git a/data/monster/Arena/warlord/gnorre chyllson.xml b/data/monster/Arena/warlord/gnorre chyllson.xml index 77de46c..7599cfa 100644 --- a/data/monster/Arena/warlord/gnorre chyllson.xml +++ b/data/monster/Arena/warlord/gnorre chyllson.xml @@ -2,24 +2,19 @@ - - + - - - - - + @@ -38,6 +33,8 @@ + + diff --git a/data/monster/Arena/warlord/norgle glacierbeard.xml b/data/monster/Arena/warlord/norgle glacierbeard.xml index 2360a03..d069b3b 100644 --- a/data/monster/Arena/warlord/norgle glacierbeard.xml +++ b/data/monster/Arena/warlord/norgle glacierbeard.xml @@ -2,25 +2,20 @@ - - - + + - - - - - - + + @@ -28,6 +23,8 @@ + + diff --git a/data/monster/Arena/warlord/svoren the mad.xml b/data/monster/Arena/warlord/svoren the mad.xml index e5762fa..12c7b53 100644 --- a/data/monster/Arena/warlord/svoren the mad.xml +++ b/data/monster/Arena/warlord/svoren the mad.xml @@ -2,24 +2,19 @@ - - - + + - - - - - + @@ -31,6 +26,8 @@ + + diff --git a/data/monster/Arena/warlord/the masked marauder.xml b/data/monster/Arena/warlord/the masked marauder.xml index 7e0b1c2..d0b50fa 100644 --- a/data/monster/Arena/warlord/the masked marauder.xml +++ b/data/monster/Arena/warlord/the masked marauder.xml @@ -2,23 +2,19 @@ - - + - - - - + @@ -29,6 +25,8 @@ + + diff --git a/data/monster/Arena/warlord/the obliverator.xml b/data/monster/Arena/warlord/the obliverator.xml index 0e6b085..f304af7 100644 --- a/data/monster/Arena/warlord/the obliverator.xml +++ b/data/monster/Arena/warlord/the obliverator.xml @@ -2,7 +2,6 @@ - @@ -12,19 +11,15 @@ - - - - - + - + @@ -41,6 +36,8 @@ + + diff --git a/data/monster/Arena/warlord/the pit lord.xml b/data/monster/Arena/warlord/the pit lord.xml index 7a12b04..790548d 100644 --- a/data/monster/Arena/warlord/the pit lord.xml +++ b/data/monster/Arena/warlord/the pit lord.xml @@ -2,24 +2,19 @@ - - + - - - - - + @@ -37,6 +32,8 @@ + + diff --git a/data/monster/Arena/warlord/webster.xml b/data/monster/Arena/warlord/webster.xml index 60653e5..d797955 100644 --- a/data/monster/Arena/warlord/webster.xml +++ b/data/monster/Arena/warlord/webster.xml @@ -2,24 +2,19 @@ - - + - - - - - + @@ -37,6 +32,8 @@ + + diff --git a/data/monster/Barbarians/barbarian bloodwalker.xml b/data/monster/Barbarians/barbarian bloodwalker.xml index 3a34aa9..68e41a4 100644 --- a/data/monster/Barbarians/barbarian bloodwalker.xml +++ b/data/monster/Barbarians/barbarian bloodwalker.xml @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ - + @@ -41,20 +41,17 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Barbarians/barbarian brutetamer.xml b/data/monster/Barbarians/barbarian brutetamer.xml index 919d0c3..8e84085 100644 --- a/data/monster/Barbarians/barbarian brutetamer.xml +++ b/data/monster/Barbarians/barbarian brutetamer.xml @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ - + @@ -55,16 +55,18 @@ - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Barbarians/barbarian headsplitter.xml b/data/monster/Barbarians/barbarian headsplitter.xml index 9ed9c19..86199ef 100644 --- a/data/monster/Barbarians/barbarian headsplitter.xml +++ b/data/monster/Barbarians/barbarian headsplitter.xml @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ - + @@ -41,17 +41,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Barbarians/barbarian skullhunter.xml b/data/monster/Barbarians/barbarian skullhunter.xml index 07ebadc..8afa766 100644 --- a/data/monster/Barbarians/barbarian skullhunter.xml +++ b/data/monster/Barbarians/barbarian skullhunter.xml @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ - + @@ -40,12 +40,17 @@ - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Bears/bear.xml b/data/monster/Bears/bear.xml index 7001451..ec1a8f6 100644 --- a/data/monster/Bears/bear.xml +++ b/data/monster/Bears/bear.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -32,9 +32,9 @@ - - - - + + + + diff --git a/data/monster/Bears/panda.xml b/data/monster/Bears/panda.xml index 78ddef5..6e71c36 100644 --- a/data/monster/Bears/panda.xml +++ b/data/monster/Bears/panda.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -30,8 +30,8 @@ - - - + + + diff --git a/data/monster/Bears/polar bear.xml b/data/monster/Bears/polar bear.xml index fe9b8e4..9cf5f2c 100644 --- a/data/monster/Bears/polar bear.xml +++ b/data/monster/Bears/polar bear.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -32,8 +32,8 @@ - - - + + + diff --git a/data/monster/Bears/undead cavebear.xml b/data/monster/Bears/undead cavebear.xml new file mode 100644 index 0000000..f4920a4 --- /dev/null +++ b/data/monster/Bears/undead cavebear.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bio-Elementals/bog raider.xml b/data/monster/Bio-Elementals/bog raider.xml index 6d85c8e..17cdaff 100644 --- a/data/monster/Bio-Elementals/bog raider.xml +++ b/data/monster/Bio-Elementals/bog raider.xml @@ -1,6 +1,6 @@ - - + + @@ -19,7 +19,7 @@ - + @@ -55,13 +55,15 @@ - - - - - - - - + + + + + + + + + + diff --git a/data/monster/Bio-Elementals/carniphila.xml b/data/monster/Bio-Elementals/carniphila.xml index efb5bf1..1925b2e 100644 --- a/data/monster/Bio-Elementals/carniphila.xml +++ b/data/monster/Bio-Elementals/carniphila.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -42,12 +42,13 @@ - - - - - - - + + + + + + + + diff --git a/data/monster/Bio-Elementals/defiler.xml b/data/monster/Bio-Elementals/defiler.xml index 77c588c..47dde62 100644 --- a/data/monster/Bio-Elementals/defiler.xml +++ b/data/monster/Bio-Elementals/defiler.xml @@ -1,5 +1,5 @@ - - + + @@ -18,7 +18,7 @@ - + @@ -45,9 +45,17 @@ - - - - + + + + + + + + + + + + diff --git a/data/monster/Bio-Elementals/haunted treeling.xml b/data/monster/Bio-Elementals/haunted treeling.xml index 62e2d6a..afac53d 100644 --- a/data/monster/Bio-Elementals/haunted treeling.xml +++ b/data/monster/Bio-Elementals/haunted treeling.xml @@ -18,7 +18,7 @@ - + @@ -40,20 +40,16 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Bio-Elementals/slime summon.xml b/data/monster/Bio-Elementals/slime summon.xml index b3e0388..7eb31a8 100644 --- a/data/monster/Bio-Elementals/slime summon.xml +++ b/data/monster/Bio-Elementals/slime summon.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -30,4 +30,4 @@ - + \ No newline at end of file diff --git a/data/monster/Bio-Elementals/slime.xml b/data/monster/Bio-Elementals/slime.xml index dc4a315..ccdaafe 100644 --- a/data/monster/Bio-Elementals/slime.xml +++ b/data/monster/Bio-Elementals/slime.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + diff --git a/data/monster/Bio-Elementals/slug.xml b/data/monster/Bio-Elementals/slug.xml new file mode 100644 index 0000000..24f0305 --- /dev/null +++ b/data/monster/Bio-Elementals/slug.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bio-Elementals/son of verminor.xml b/data/monster/Bio-Elementals/son of verminor.xml index bb9b7ae..62545a5 100644 --- a/data/monster/Bio-Elementals/son of verminor.xml +++ b/data/monster/Bio-Elementals/son of verminor.xml @@ -18,7 +18,7 @@ - + diff --git a/data/monster/Bio-Elementals/spit nettle.xml b/data/monster/Bio-Elementals/spit nettle.xml index 393f22e..37bc71b 100644 --- a/data/monster/Bio-Elementals/spit nettle.xml +++ b/data/monster/Bio-Elementals/spit nettle.xml @@ -1,4 +1,4 @@ - + @@ -37,4 +37,11 @@ + + + + + + + diff --git a/data/monster/Birds/chicken.xml b/data/monster/Birds/chicken.xml index 72718b2..83c8b65 100644 --- a/data/monster/Birds/chicken.xml +++ b/data/monster/Birds/chicken.xml @@ -17,18 +17,15 @@ - - - - - - - + + + + diff --git a/data/monster/Birds/flamingo.xml b/data/monster/Birds/flamingo.xml index 1c13e01..d0cbf5f 100644 --- a/data/monster/Birds/flamingo.xml +++ b/data/monster/Birds/flamingo.xml @@ -16,12 +16,5 @@ - - - - - - - diff --git a/data/monster/Birds/parrot.xml b/data/monster/Birds/parrot.xml index 4a0676a..f3c78a1 100644 --- a/data/monster/Birds/parrot.xml +++ b/data/monster/Birds/parrot.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -37,8 +37,4 @@ - - - - diff --git a/data/monster/Birds/penguin.xml b/data/monster/Birds/penguin.xml index 3117795..8baa108 100644 --- a/data/monster/Birds/penguin.xml +++ b/data/monster/Birds/penguin.xml @@ -1,4 +1,4 @@ - + @@ -17,16 +17,16 @@ - + - - - - + + + + diff --git a/data/monster/Birds/seagull.xml b/data/monster/Birds/seagull.xml index de9b1ca..0707513 100644 --- a/data/monster/Birds/seagull.xml +++ b/data/monster/Birds/seagull.xml @@ -18,7 +18,7 @@ - + - + \ No newline at end of file diff --git a/data/monster/Birds/terror bird.xml b/data/monster/Birds/terror bird.xml index f4d9b7d..fb95491 100644 --- a/data/monster/Birds/terror bird.xml +++ b/data/monster/Birds/terror bird.xml @@ -18,7 +18,7 @@ - + @@ -34,25 +34,11 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/data/monster/Blobs/acid blob.xml b/data/monster/Blobs/acid blob.xml index 823f31d..c853fa6 100644 --- a/data/monster/Blobs/acid blob.xml +++ b/data/monster/Blobs/acid blob.xml @@ -18,7 +18,7 @@ - + @@ -41,6 +41,6 @@ - + diff --git a/data/monster/Blobs/death blob.xml b/data/monster/Blobs/death blob.xml index 17ede55..9abdca2 100644 --- a/data/monster/Blobs/death blob.xml +++ b/data/monster/Blobs/death blob.xml @@ -18,7 +18,7 @@ - + @@ -32,6 +32,6 @@ - + diff --git a/data/monster/Blobs/mercury blob.xml b/data/monster/Blobs/mercury blob.xml index d6d2fe0..7b94396 100644 --- a/data/monster/Blobs/mercury blob.xml +++ b/data/monster/Blobs/mercury blob.xml @@ -18,7 +18,7 @@ - + @@ -46,6 +46,6 @@ - + diff --git a/data/monster/Beholders/beholder.xml b/data/monster/Bonelords/bonelord.xml similarity index 66% rename from data/monster/Beholders/beholder.xml rename to data/monster/Bonelords/bonelord.xml index d41bc5c..ec1eb69 100644 --- a/data/monster/Beholders/beholder.xml +++ b/data/monster/Bonelords/bonelord.xml @@ -1,5 +1,5 @@ - - + + @@ -18,7 +18,7 @@ - + @@ -58,22 +58,16 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Beholders/braindeath.xml b/data/monster/Bonelords/braindeath.xml similarity index 69% rename from data/monster/Beholders/braindeath.xml rename to data/monster/Bonelords/braindeath.xml index 6f4c23b..27cc746 100644 --- a/data/monster/Beholders/braindeath.xml +++ b/data/monster/Bonelords/braindeath.xml @@ -18,7 +18,7 @@ - + @@ -68,25 +68,16 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Beholders/elder beholder.xml b/data/monster/Bonelords/elder bonelord.xml similarity index 65% rename from data/monster/Beholders/elder beholder.xml rename to data/monster/Bonelords/elder bonelord.xml index d89b111..f9f9160 100644 --- a/data/monster/Beholders/elder beholder.xml +++ b/data/monster/Bonelords/elder bonelord.xml @@ -1,5 +1,5 @@ - - + + @@ -18,7 +18,7 @@ - + @@ -66,25 +66,19 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Beholders/gazer.xml b/data/monster/Bonelords/gazer.xml similarity index 62% rename from data/monster/Beholders/gazer.xml rename to data/monster/Bonelords/gazer.xml index 227b877..06344e4 100644 --- a/data/monster/Beholders/gazer.xml +++ b/data/monster/Bonelords/gazer.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -45,22 +45,4 @@ - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Bosses/annihilon.xml b/data/monster/Bosses/annihilon.xml index 79bd0cc..bd318ef 100644 --- a/data/monster/Bosses/annihilon.xml +++ b/data/monster/Bosses/annihilon.xml @@ -58,12 +58,8 @@ - - - - - - - + + + diff --git a/data/monster/Bosses/apprentice sheng.xml b/data/monster/Bosses/apprentice sheng.xml index 3c59ad5..ba831bf 100644 --- a/data/monster/Bosses/apprentice sheng.xml +++ b/data/monster/Bosses/apprentice sheng.xml @@ -53,14 +53,10 @@ - - - - - - - - - + + + + + diff --git a/data/monster/Bosses/barbaria.xml b/data/monster/Bosses/barbaria.xml new file mode 100644 index 0000000..3c589c2 --- /dev/null +++ b/data/monster/Bosses/barbaria.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/baron brute.xml b/data/monster/Bosses/baron brute.xml new file mode 100644 index 0000000..df6e118 --- /dev/null +++ b/data/monster/Bosses/baron brute.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/bones.xml b/data/monster/Bosses/bones.xml index f6fc6db..2802f42 100644 --- a/data/monster/Bosses/bones.xml +++ b/data/monster/Bosses/bones.xml @@ -32,13 +32,6 @@ - - - - - - - @@ -59,14 +52,10 @@ - - - - - - - - - + + + + + diff --git a/data/monster/Bosses/chizzoron the distorter.xml b/data/monster/Bosses/chizzoron the distorter.xml new file mode 100644 index 0000000..edadb0a --- /dev/null +++ b/data/monster/Bosses/chizzoron the distorter.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Bosses/countess sorrow.xml b/data/monster/Bosses/countess sorrow.xml index ebe3d4c..04e8d24 100644 --- a/data/monster/Bosses/countess sorrow.xml +++ b/data/monster/Bosses/countess sorrow.xml @@ -37,8 +37,6 @@ - - diff --git a/data/monster/Bosses/demodras.xml b/data/monster/Bosses/demodras.xml index e93ba97..6231d20 100644 --- a/data/monster/Bosses/demodras.xml +++ b/data/monster/Bosses/demodras.xml @@ -62,17 +62,13 @@ - - - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Bosses/dharalion.xml b/data/monster/Bosses/dharalion.xml index b30e6a2..b083b59 100644 --- a/data/monster/Bosses/dharalion.xml +++ b/data/monster/Bosses/dharalion.xml @@ -41,12 +41,9 @@ - - - @@ -72,16 +69,12 @@ - - - - - - - - - - - + + + + + + + diff --git a/data/monster/Birds/dire penguin.xml b/data/monster/Bosses/dire penguin.xml similarity index 75% rename from data/monster/Birds/dire penguin.xml rename to data/monster/Bosses/dire penguin.xml index 0f4e4f5..88437d7 100644 --- a/data/monster/Birds/dire penguin.xml +++ b/data/monster/Bosses/dire penguin.xml @@ -1,4 +1,4 @@ - + @@ -17,7 +17,7 @@ - + @@ -27,11 +27,13 @@ - - + + + + diff --git a/data/monster/Bosses/dreadwing.xml b/data/monster/Bosses/dreadwing.xml new file mode 100644 index 0000000..1c95bb1 --- /dev/null +++ b/data/monster/Bosses/dreadwing.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/esmeralda.xml b/data/monster/Bosses/esmeralda.xml new file mode 100644 index 0000000..507e254 --- /dev/null +++ b/data/monster/Bosses/esmeralda.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/fatality.xml b/data/monster/Bosses/fatality.xml new file mode 100644 index 0000000..0af3275 --- /dev/null +++ b/data/monster/Bosses/fatality.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Bosses/fernfang.xml b/data/monster/Bosses/fernfang.xml index 8911ea5..fbdf876 100644 --- a/data/monster/Bosses/fernfang.xml +++ b/data/monster/Bosses/fernfang.xml @@ -66,13 +66,9 @@ - - - - - - - - + + + + diff --git a/data/monster/Bosses/ferumbras.xml b/data/monster/Bosses/ferumbras.xml index 856119f..a32a940 100644 --- a/data/monster/Bosses/ferumbras.xml +++ b/data/monster/Bosses/ferumbras.xml @@ -79,16 +79,12 @@ - - - - - - - - - - - + + + + + + + diff --git a/data/monster/Bosses/fluffy.xml b/data/monster/Bosses/fluffy.xml index a7366a0..57c3cf4 100644 --- a/data/monster/Bosses/fluffy.xml +++ b/data/monster/Bosses/fluffy.xml @@ -34,13 +34,6 @@ - - - - - - - @@ -56,12 +49,8 @@ - - - - - - - + + + diff --git a/data/monster/Bosses/general murius.xml b/data/monster/Bosses/general murius.xml index 21c5a09..b8c123b 100644 --- a/data/monster/Bosses/general murius.xml +++ b/data/monster/Bosses/general murius.xml @@ -52,22 +52,14 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Bosses/ghazbaran.xml b/data/monster/Bosses/ghazbaran.xml index d8c1807..de5e6fe 100644 --- a/data/monster/Bosses/ghazbaran.xml +++ b/data/monster/Bosses/ghazbaran.xml @@ -51,7 +51,6 @@ - @@ -76,12 +75,8 @@ - - - - - - - + + + diff --git a/data/monster/Bosses/glitterscale.xml b/data/monster/Bosses/glitterscale.xml new file mode 100644 index 0000000..95766d0 --- /dev/null +++ b/data/monster/Bosses/glitterscale.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Bosses/grand mother foulscale.xml b/data/monster/Bosses/grand mother foulscale.xml new file mode 100644 index 0000000..d19810d --- /dev/null +++ b/data/monster/Bosses/grand mother foulscale.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/grorlam.xml b/data/monster/Bosses/grorlam.xml index e913674..e8a85df 100644 --- a/data/monster/Bosses/grorlam.xml +++ b/data/monster/Bosses/grorlam.xml @@ -47,14 +47,10 @@ - - - - - - - - - + + + + + diff --git a/data/monster/Bosses/hairman the huge.xml b/data/monster/Bosses/hairman the huge.xml new file mode 100644 index 0000000..8538a8c --- /dev/null +++ b/data/monster/Bosses/hairman the huge.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/handmaiden.xml b/data/monster/Bosses/handmaiden.xml index 62f534e..f54d4b3 100644 --- a/data/monster/Bosses/handmaiden.xml +++ b/data/monster/Bosses/handmaiden.xml @@ -40,7 +40,6 @@ - diff --git a/data/monster/Bosses/hellgorak.xml b/data/monster/Bosses/hellgorak.xml index ce23736..3e1087b 100644 --- a/data/monster/Bosses/hellgorak.xml +++ b/data/monster/Bosses/hellgorak.xml @@ -67,11 +67,7 @@ - - - - - - + + diff --git a/data/monster/Bosses/heoni.xml b/data/monster/Bosses/heoni.xml new file mode 100644 index 0000000..a28f375 --- /dev/null +++ b/data/monster/Bosses/heoni.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/hide.xml b/data/monster/Bosses/hide.xml new file mode 100644 index 0000000..20e1c94 --- /dev/null +++ b/data/monster/Bosses/hide.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/koshei the deathless.xml b/data/monster/Bosses/koshei the deathless.xml index 366f8d8..14e40ce 100644 --- a/data/monster/Bosses/koshei the deathless.xml +++ b/data/monster/Bosses/koshei the deathless.xml @@ -66,15 +66,12 @@ --platinium amulet --blue robe --lightning boots - - - --castle shield - --stealth ring - --dirty cape - - --spell book - --gold ring - --liche staff - + --castle shield + --stealth ring + --dirty cape + + --spell book + --gold ring + --liche staff diff --git a/data/monster/Bosses/latrivan.xml b/data/monster/Bosses/latrivan.xml index 83e066c..cf8a6c3 100644 --- a/data/monster/Bosses/latrivan.xml +++ b/data/monster/Bosses/latrivan.xml @@ -43,12 +43,8 @@ - - - - - - - + + + diff --git a/data/monster/Bosses/leviathan.xml b/data/monster/Bosses/leviathan.xml index 5dde5f3..a8a007b 100644 --- a/data/monster/Bosses/leviathan.xml +++ b/data/monster/Bosses/leviathan.xml @@ -50,14 +50,9 @@ - - - - - - + diff --git a/data/monster/Bosses/madareth.xml b/data/monster/Bosses/madareth.xml index 4d448b9..4a7c78a 100644 --- a/data/monster/Bosses/madareth.xml +++ b/data/monster/Bosses/madareth.xml @@ -35,11 +35,7 @@ - - - - - - + + diff --git a/data/monster/Bosses/man in the cave.xml b/data/monster/Bosses/man in the cave.xml index dadc860..905fb40 100644 --- a/data/monster/Bosses/man in the cave.xml +++ b/data/monster/Bosses/man in the cave.xml @@ -28,12 +28,8 @@ - - - - @@ -54,14 +50,10 @@ - - - - - - - - - + + + + + diff --git a/data/monster/Bosses/massacre.xml b/data/monster/Bosses/massacre.xml index 33ae84a..69c6eb4 100644 --- a/data/monster/Bosses/massacre.xml +++ b/data/monster/Bosses/massacre.xml @@ -29,7 +29,6 @@ - diff --git a/data/monster/Bosses/minishabaal.xml b/data/monster/Bosses/minishabaal.xml index 246c6aa..6ab4532 100644 --- a/data/monster/Bosses/minishabaal.xml +++ b/data/monster/Bosses/minishabaal.xml @@ -40,14 +40,6 @@ - - - - - - - - @@ -75,15 +67,11 @@ - - - - - - - - - - + + + + + + diff --git a/data/monster/Bosses/morgaroth.xml b/data/monster/Bosses/morgaroth.xml index b45a3dd..b739525 100644 --- a/data/monster/Bosses/morgaroth.xml +++ b/data/monster/Bosses/morgaroth.xml @@ -78,16 +78,12 @@ - - - - - - - - - - - + + + + + + + diff --git a/data/monster/Bosses/mr. punish.xml b/data/monster/Bosses/mr. punish.xml index 02f54d1..9e4f490 100644 --- a/data/monster/Bosses/mr. punish.xml +++ b/data/monster/Bosses/mr. punish.xml @@ -23,14 +23,9 @@ - - - - - diff --git a/data/monster/Bosses/munster.xml b/data/monster/Bosses/munster.xml index 16d1fe0..3db6a18 100644 --- a/data/monster/Bosses/munster.xml +++ b/data/monster/Bosses/munster.xml @@ -22,17 +22,6 @@ - - - - - - - - - - - @@ -42,12 +31,8 @@ - - - - - - - + + + diff --git a/data/monster/Bosses/necropharus.xml b/data/monster/Bosses/necropharus.xml index 2b00e41..87fd8b4 100644 --- a/data/monster/Bosses/necropharus.xml +++ b/data/monster/Bosses/necropharus.xml @@ -33,12 +33,8 @@ - - - - @@ -61,22 +57,18 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Bosses/pythius the rotten.xml b/data/monster/Bosses/pythius the rotten.xml new file mode 100644 index 0000000..8da8eb9 --- /dev/null +++ b/data/monster/Bosses/pythius the rotten.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/shardhead.xml b/data/monster/Bosses/shardhead.xml new file mode 100644 index 0000000..19d0ffa --- /dev/null +++ b/data/monster/Bosses/shardhead.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/snake god essence.xml b/data/monster/Bosses/snake god essence.xml new file mode 100644 index 0000000..d110585 --- /dev/null +++ b/data/monster/Bosses/snake god essence.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/snake thing.xml b/data/monster/Bosses/snake thing.xml new file mode 100644 index 0000000..34b9a9f --- /dev/null +++ b/data/monster/Bosses/snake thing.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/stonecracker.xml b/data/monster/Bosses/stonecracker.xml new file mode 100644 index 0000000..6ef6035 --- /dev/null +++ b/data/monster/Bosses/stonecracker.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/the abomination.xml b/data/monster/Bosses/the abomination.xml index 28eb299..53fd598 100644 --- a/data/monster/Bosses/the abomination.xml +++ b/data/monster/Bosses/the abomination.xml @@ -39,12 +39,7 @@ - - - - - diff --git a/data/monster/Bosses/the blightfather.xml b/data/monster/Bosses/the blightfather.xml new file mode 100644 index 0000000..c27eaeb --- /dev/null +++ b/data/monster/Bosses/the blightfather.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Bosses/the bloodtusk.xml b/data/monster/Bosses/the bloodtusk.xml new file mode 100644 index 0000000..57aad6c --- /dev/null +++ b/data/monster/Bosses/the bloodtusk.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/the evil eye.xml b/data/monster/Bosses/the evil eye.xml index 68a0db6..8eed16b 100644 --- a/data/monster/Bosses/the evil eye.xml +++ b/data/monster/Bosses/the evil eye.xml @@ -73,12 +73,8 @@ - - - - - - - + + + \ No newline at end of file diff --git a/data/monster/Bosses/the handmaiden.xml b/data/monster/Bosses/the handmaiden.xml index 53ebffe..98ff467 100644 --- a/data/monster/Bosses/the handmaiden.xml +++ b/data/monster/Bosses/the handmaiden.xml @@ -39,7 +39,6 @@ - diff --git a/data/monster/Bosses/the horned fox.xml b/data/monster/Bosses/the horned fox.xml index c37cd1b..d939a99 100644 --- a/data/monster/Bosses/the horned fox.xml +++ b/data/monster/Bosses/the horned fox.xml @@ -58,18 +58,14 @@ - - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Bosses/the imperor.xml b/data/monster/Bosses/the imperor.xml index 447ab10..85cb8d5 100644 --- a/data/monster/Bosses/the imperor.xml +++ b/data/monster/Bosses/the imperor.xml @@ -49,8 +49,6 @@ - - @@ -74,21 +72,17 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Bosses/the many.xml b/data/monster/Bosses/the many.xml new file mode 100644 index 0000000..4e1ff17 --- /dev/null +++ b/data/monster/Bosses/the many.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/the noxious spawn.xml b/data/monster/Bosses/the noxious spawn.xml new file mode 100644 index 0000000..c6514e8 --- /dev/null +++ b/data/monster/Bosses/the noxious spawn.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/the old widow.xml b/data/monster/Bosses/the old widow.xml index 97b4cdc..f552826 100644 --- a/data/monster/Bosses/the old widow.xml +++ b/data/monster/Bosses/the old widow.xml @@ -40,12 +40,9 @@ - - - @@ -60,15 +57,11 @@ - - - - - - - - - - + + + + + + \ No newline at end of file diff --git a/data/monster/Bosses/the plasmother.xml b/data/monster/Bosses/the plasmother.xml new file mode 100644 index 0000000..d0be0e4 --- /dev/null +++ b/data/monster/Bosses/the plasmother.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/the snapper.xml b/data/monster/Bosses/the snapper.xml new file mode 100644 index 0000000..537414e --- /dev/null +++ b/data/monster/Bosses/the snapper.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/tibia bug.xml b/data/monster/Bosses/tibia bug.xml index c956a37..af351d4 100644 --- a/data/monster/Bosses/tibia bug.xml +++ b/data/monster/Bosses/tibia bug.xml @@ -29,15 +29,9 @@ - - - - - - diff --git a/data/monster/Bosses/undead minion.xml b/data/monster/Bosses/undead minion.xml index 78b0c56..59bbeef 100644 --- a/data/monster/Bosses/undead minion.xml +++ b/data/monster/Bosses/undead minion.xml @@ -25,15 +25,10 @@ - - - - - diff --git a/data/monster/Demons/ungreez.xml b/data/monster/Bosses/ungreez.xml similarity index 86% rename from data/monster/Demons/ungreez.xml rename to data/monster/Bosses/ungreez.xml index 904aadd..c4591d5 100644 --- a/data/monster/Demons/ungreez.xml +++ b/data/monster/Bosses/ungreez.xml @@ -18,7 +18,7 @@ - + @@ -44,10 +44,8 @@ - - @@ -59,8 +57,9 @@ - - - + + + + diff --git a/data/monster/Bosses/ushuriel.xml b/data/monster/Bosses/ushuriel.xml index 68d4168..64a81f2 100644 --- a/data/monster/Bosses/ushuriel.xml +++ b/data/monster/Bosses/ushuriel.xml @@ -60,18 +60,14 @@ - - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Bosses/warlord ruzad.xml b/data/monster/Bosses/warlord ruzad.xml new file mode 100644 index 0000000..bc1eb9f --- /dev/null +++ b/data/monster/Bosses/warlord ruzad.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/xenia.xml b/data/monster/Bosses/xenia.xml index 89cb0fd..5cb0c62 100644 --- a/data/monster/Bosses/xenia.xml +++ b/data/monster/Bosses/xenia.xml @@ -31,13 +31,6 @@ - - - - - - - @@ -50,16 +43,12 @@ - - - - - - - - - - - + + + + + + + diff --git a/data/monster/Bosses/yakchal.xml b/data/monster/Bosses/yakchal.xml new file mode 100644 index 0000000..fcfbf5b --- /dev/null +++ b/data/monster/Bosses/yakchal.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Bosses/zugurosh.xml b/data/monster/Bosses/zugurosh.xml index 0a163cf..b7a6f68 100644 --- a/data/monster/Bosses/zugurosh.xml +++ b/data/monster/Bosses/zugurosh.xml @@ -65,16 +65,12 @@ - - - - - - - - - - - + + + + + + + diff --git a/data/monster/Bosses/zulazza the corruptor.xml b/data/monster/Bosses/zulazza the corruptor.xml new file mode 100644 index 0000000..f3deff9 --- /dev/null +++ b/data/monster/Bosses/zulazza the corruptor.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Canines/crystal wolf.xml b/data/monster/Canines/crystal wolf.xml new file mode 100644 index 0000000..ea12524 --- /dev/null +++ b/data/monster/Canines/crystal wolf.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Canines/dog.xml b/data/monster/Canines/dog.xml index 573ae65..6895513 100644 --- a/data/monster/Canines/dog.xml +++ b/data/monster/Canines/dog.xml @@ -17,21 +17,7 @@ - - - - - - - - - - - - - - diff --git a/data/monster/Canines/gnarlhound.xml b/data/monster/Canines/gnarlhound.xml new file mode 100644 index 0000000..5220157 --- /dev/null +++ b/data/monster/Canines/gnarlhound.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Canines/husky.xml b/data/monster/Canines/husky.xml index 6add040..4b90e61 100644 --- a/data/monster/Canines/husky.xml +++ b/data/monster/Canines/husky.xml @@ -17,13 +17,10 @@ - - - - + \ No newline at end of file diff --git a/data/monster/Canines/war wolf.xml b/data/monster/Canines/war wolf.xml index d914288..4ac7bf3 100644 --- a/data/monster/Canines/war wolf.xml +++ b/data/monster/Canines/war wolf.xml @@ -18,7 +18,7 @@ - + @@ -32,8 +32,9 @@ - - - + + + + diff --git a/data/monster/Canines/werewolf.xml b/data/monster/Canines/werewolf.xml index f5f6fbc..0572fbd 100644 --- a/data/monster/Canines/werewolf.xml +++ b/data/monster/Canines/werewolf.xml @@ -18,7 +18,7 @@ - + @@ -45,20 +45,25 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Canines/winter wolf.xml b/data/monster/Canines/winter wolf.xml index 2179c18..0c650ac 100644 --- a/data/monster/Canines/winter wolf.xml +++ b/data/monster/Canines/winter wolf.xml @@ -18,7 +18,7 @@ - + @@ -29,7 +29,7 @@ - - + + diff --git a/data/monster/Canines/wolf.xml b/data/monster/Canines/wolf.xml index d48945a..4fbb8ad 100644 --- a/data/monster/Canines/wolf.xml +++ b/data/monster/Canines/wolf.xml @@ -18,7 +18,7 @@ - + @@ -28,8 +28,8 @@ - - - + + + diff --git a/data/monster/Chakoyas/chakoya toolshaper.xml b/data/monster/Chakoyas/chakoya toolshaper.xml index bf7e3a8..48b9315 100644 --- a/data/monster/Chakoyas/chakoya toolshaper.xml +++ b/data/monster/Chakoyas/chakoya toolshaper.xml @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ - + @@ -39,8 +39,16 @@ - - - + + + + + + + + + + + diff --git a/data/monster/Chakoyas/chakoya tribewarden.xml b/data/monster/Chakoyas/chakoya tribewarden.xml index d33bfe4..9475b13 100644 --- a/data/monster/Chakoyas/chakoya tribewarden.xml +++ b/data/monster/Chakoyas/chakoya tribewarden.xml @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ - + @@ -36,11 +36,16 @@ - - + + + - - - + + + + + + + - + \ No newline at end of file diff --git a/data/monster/Chakoyas/chakoya windcaller.xml b/data/monster/Chakoyas/chakoya windcaller.xml index 53a15e2..9f40884 100644 --- a/data/monster/Chakoyas/chakoya windcaller.xml +++ b/data/monster/Chakoyas/chakoya windcaller.xml @@ -1,4 +1,4 @@ - + @@ -19,9 +19,7 @@ - - - + @@ -45,8 +43,13 @@ - - - + + + + + + + + diff --git a/data/monster/Crustaceans/blood crab.xml b/data/monster/Crustaceans/blood crab.xml index 01ab1b9..8ada57f 100644 --- a/data/monster/Crustaceans/blood crab.xml +++ b/data/monster/Crustaceans/blood crab.xml @@ -1,4 +1,4 @@ - + @@ -29,11 +29,10 @@ - - - - - - + + + + + diff --git a/data/monster/Crustaceans/crab.xml b/data/monster/Crustaceans/crab.xml index dda8b1d..7e613e9 100644 --- a/data/monster/Crustaceans/crab.xml +++ b/data/monster/Crustaceans/crab.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -31,8 +31,8 @@ - - - + + + diff --git a/data/monster/Crustaceans/crustacea gigantica.xml b/data/monster/Crustaceans/crustacea gigantica.xml new file mode 100644 index 0000000..0e5f734 --- /dev/null +++ b/data/monster/Crustaceans/crustacea gigantica.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Cryo-Elementals/ice golem.xml b/data/monster/Cryo-Elementals/ice golem.xml index c90f520..0faf30f 100644 --- a/data/monster/Cryo-Elementals/ice golem.xml +++ b/data/monster/Cryo-Elementals/ice golem.xml @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ - + @@ -47,19 +47,17 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Cultists/acolyte of the cult.xml b/data/monster/Cultists/acolyte of the cult.xml index 650fb3c..befcc10 100644 --- a/data/monster/Cultists/acolyte of the cult.xml +++ b/data/monster/Cultists/acolyte of the cult.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -52,20 +52,16 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Cultists/adept of the cult.xml b/data/monster/Cultists/adept of the cult.xml index 223c965..e3f7529 100644 --- a/data/monster/Cultists/adept of the cult.xml +++ b/data/monster/Cultists/adept of the cult.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -48,20 +48,21 @@ - - - - - - - + + - - - - - + + + + + + + + + + + diff --git a/data/monster/Cultists/enlightened of the cult.xml b/data/monster/Cultists/enlightened of the cult.xml index 1772563..5272a01 100644 --- a/data/monster/Cultists/enlightened of the cult.xml +++ b/data/monster/Cultists/enlightened of the cult.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -50,24 +50,21 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/data/monster/Cultists/novice of the cult.xml b/data/monster/Cultists/novice of the cult.xml index 0e2f35d..30c8295 100644 --- a/data/monster/Cultists/novice of the cult.xml +++ b/data/monster/Cultists/novice of the cult.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -50,19 +50,15 @@ - - - + + + + + - - - - - - - - - - + + + + diff --git a/data/monster/Demons/dark torturer.xml b/data/monster/Demons/dark torturer.xml index 6a7af97..834cced 100644 --- a/data/monster/Demons/dark torturer.xml +++ b/data/monster/Demons/dark torturer.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -47,22 +47,24 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Demons/demon.xml b/data/monster/Demons/demon.xml index 52ba53b..0f2a8ec 100644 --- a/data/monster/Demons/demon.xml +++ b/data/monster/Demons/demon.xml @@ -18,7 +18,7 @@ - + @@ -64,38 +64,40 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Demons/destroyer.xml b/data/monster/Demons/destroyer.xml index 210725d..0614bc9 100644 --- a/data/monster/Demons/destroyer.xml +++ b/data/monster/Demons/destroyer.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -46,19 +46,29 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Demons/diabolic imp.xml b/data/monster/Demons/diabolic imp.xml index a8cf99b..54b1a02 100644 --- a/data/monster/Demons/diabolic imp.xml +++ b/data/monster/Demons/diabolic imp.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -55,23 +55,25 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Demons/fire devil.xml b/data/monster/Demons/fire devil.xml index e3096f8..98b5e5a 100644 --- a/data/monster/Demons/fire devil.xml +++ b/data/monster/Demons/fire devil.xml @@ -1,5 +1,5 @@ - - + + @@ -18,7 +18,7 @@ - + @@ -43,22 +43,16 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Demons/fury.xml b/data/monster/Demons/fury.xml index 9b3151e..703d604 100644 --- a/data/monster/Demons/fury.xml +++ b/data/monster/Demons/fury.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -49,7 +49,7 @@ - + @@ -57,20 +57,27 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Demons/gozzler.xml b/data/monster/Demons/gozzler.xml index 4ea9b68..1bfe23f 100644 --- a/data/monster/Demons/gozzler.xml +++ b/data/monster/Demons/gozzler.xml @@ -18,7 +18,7 @@ - + @@ -46,20 +46,16 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Demons/hand of cursed fate.xml b/data/monster/Demons/hand of cursed fate.xml index 6d7ac7c..fd1fd2f 100644 --- a/data/monster/Demons/hand of cursed fate.xml +++ b/data/monster/Demons/hand of cursed fate.xml @@ -1,5 +1,5 @@ - - + + @@ -18,7 +18,7 @@ - + @@ -51,16 +51,23 @@ - + + + + - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Canines/hellhound.xml b/data/monster/Demons/hellhound.xml similarity index 62% rename from data/monster/Canines/hellhound.xml rename to data/monster/Demons/hellhound.xml index 9a416c2..75a369a 100644 --- a/data/monster/Canines/hellhound.xml +++ b/data/monster/Demons/hellhound.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -53,22 +53,24 @@ - - - - - + + + + + + + + - - - - - - - - - - - + + + + + + + + + + diff --git a/data/monster/Demons/hellspawn.xml b/data/monster/Demons/hellspawn.xml index ba7dd0a..9f2269e 100644 --- a/data/monster/Demons/hellspawn.xml +++ b/data/monster/Demons/hellspawn.xml @@ -18,10 +18,10 @@ - + - + @@ -39,27 +39,33 @@ - - - - - + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Demons/juggernaut.xml b/data/monster/Demons/juggernaut.xml index fb9461f..9774b16 100644 --- a/data/monster/Demons/juggernaut.xml +++ b/data/monster/Demons/juggernaut.xml @@ -1,5 +1,5 @@ - - + + @@ -18,14 +18,14 @@ - + - + - + @@ -46,24 +46,33 @@ - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Demons/nightmare scion.xml b/data/monster/Demons/nightmare scion.xml index da44648..82217f0 100644 --- a/data/monster/Demons/nightmare scion.xml +++ b/data/monster/Demons/nightmare scion.xml @@ -18,7 +18,7 @@ - + @@ -51,22 +51,18 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Demons/nightmare.xml b/data/monster/Demons/nightmare.xml index c1c2d1a..e00a1bf 100644 --- a/data/monster/Demons/nightmare.xml +++ b/data/monster/Demons/nightmare.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -56,27 +56,23 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Demons/nightstalker.xml b/data/monster/Demons/nightstalker.xml index 7c6e559..4b8e950 100644 --- a/data/monster/Demons/nightstalker.xml +++ b/data/monster/Demons/nightstalker.xml @@ -18,10 +18,10 @@ - + - + @@ -69,21 +69,17 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Demons/plaguesmith.xml b/data/monster/Demons/plaguesmith.xml index 6fb6774..b7d423a 100644 --- a/data/monster/Demons/plaguesmith.xml +++ b/data/monster/Demons/plaguesmith.xml @@ -1,5 +1,5 @@ - - + + @@ -18,7 +18,7 @@ - + @@ -55,21 +55,34 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Djinns/blue djinn.xml b/data/monster/Djinns/blue djinn.xml index 8771c51..5036f57 100644 --- a/data/monster/Djinns/blue djinn.xml +++ b/data/monster/Djinns/blue djinn.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -55,19 +55,16 @@ - - - - + + + + + + + + + - - - - - - - - - + diff --git a/data/monster/Djinns/efreet.xml b/data/monster/Djinns/efreet.xml index 020306e..2a2416f 100644 --- a/data/monster/Djinns/efreet.xml +++ b/data/monster/Djinns/efreet.xml @@ -1,5 +1,5 @@ - + @@ -18,7 +18,7 @@ - + @@ -67,23 +67,20 @@ - - - - - + + + + + - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Djinns/green djinn.xml b/data/monster/Djinns/green djinn.xml index b280959..ebdd7b0 100644 --- a/data/monster/Djinns/green djinn.xml +++ b/data/monster/Djinns/green djinn.xml @@ -18,7 +18,7 @@ - + @@ -56,19 +56,16 @@ - - - - - + + + - - - - - - - - + + + + + + + diff --git a/data/monster/Djinns/marid.xml b/data/monster/Djinns/marid.xml index 5c2ac94..467c657 100644 --- a/data/monster/Djinns/marid.xml +++ b/data/monster/Djinns/marid.xml @@ -1,5 +1,5 @@ - + @@ -18,7 +18,7 @@ - + @@ -69,22 +69,21 @@ - - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Dragons/dragon hatchling.xml b/data/monster/Dragons/dragon hatchling.xml index 9a4cfb9..e6fb3b0 100644 --- a/data/monster/Dragons/dragon hatchling.xml +++ b/data/monster/Dragons/dragon hatchling.xml @@ -17,7 +17,7 @@ - + @@ -45,13 +45,8 @@ - - - - - - - - + + + diff --git a/data/monster/Dragons/dragon lord hatchling.xml b/data/monster/Dragons/dragon lord hatchling.xml index 6d91ba2..d8dd512 100644 --- a/data/monster/Dragons/dragon lord hatchling.xml +++ b/data/monster/Dragons/dragon lord hatchling.xml @@ -17,17 +17,17 @@ - - + + - + - + - + @@ -42,7 +42,7 @@ - + @@ -55,15 +55,11 @@ - - - - - - - - - - + + + + + + diff --git a/data/monster/Dragons/dragon lord.xml b/data/monster/Dragons/dragon lord.xml index 53e756d..bb994de 100644 --- a/data/monster/Dragons/dragon lord.xml +++ b/data/monster/Dragons/dragon lord.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -50,29 +50,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/data/monster/Dragons/dragon.xml b/data/monster/Dragons/dragon.xml index 70c9afb..de46a03 100644 --- a/data/monster/Dragons/dragon.xml +++ b/data/monster/Dragons/dragon.xml @@ -1,4 +1,4 @@ - + @@ -18,12 +18,12 @@ - - + + - + @@ -47,31 +47,28 @@ - - - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Dragons/draptor.xml b/data/monster/Dragons/draptor.xml new file mode 100644 index 0000000..bd01112 --- /dev/null +++ b/data/monster/Dragons/draptor.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Dragons/frost dragon hatchling.xml b/data/monster/Dragons/frost dragon hatchling.xml index 7c33a2e..a017cda 100644 --- a/data/monster/Dragons/frost dragon hatchling.xml +++ b/data/monster/Dragons/frost dragon hatchling.xml @@ -17,7 +17,7 @@ - + @@ -28,14 +28,13 @@ - - - + - + + @@ -43,13 +42,9 @@ - - - - - - - - + + + + diff --git a/data/monster/Dragons/frost dragon.xml b/data/monster/Dragons/frost dragon.xml index 840aafe..b0f8959 100644 --- a/data/monster/Dragons/frost dragon.xml +++ b/data/monster/Dragons/frost dragon.xml @@ -1,4 +1,4 @@ - + @@ -18,23 +18,26 @@ - - + + - + - + + + + - + @@ -46,9 +49,9 @@ - - - + + + @@ -56,25 +59,25 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Dragons/ghastly dragon.xml b/data/monster/Dragons/ghastly dragon.xml new file mode 100644 index 0000000..0fd689e --- /dev/null +++ b/data/monster/Dragons/ghastly dragon.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Dragons/hydra.xml b/data/monster/Dragons/hydra.xml deleted file mode 100644 index 583c86b..0000000 --- a/data/monster/Dragons/hydra.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Skeletons/undead dragon.xml b/data/monster/Dragons/undead dragon.xml similarity index 62% rename from data/monster/Skeletons/undead dragon.xml rename to data/monster/Dragons/undead dragon.xml index 14a1874..51d5b94 100644 --- a/data/monster/Skeletons/undead dragon.xml +++ b/data/monster/Dragons/undead dragon.xml @@ -18,7 +18,7 @@ - + @@ -66,22 +66,33 @@ - - + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Dragons/wyrm.xml b/data/monster/Dragons/wyrm.xml index fc6db43..8dfc5cf 100644 --- a/data/monster/Dragons/wyrm.xml +++ b/data/monster/Dragons/wyrm.xml @@ -19,20 +19,22 @@ - - - - + + + + + + - - + + - + - + @@ -40,26 +42,32 @@ + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Dwarves/dwarf geomancer.xml b/data/monster/Dwarves/dwarf geomancer.xml index 12b7575..ddd8bd3 100644 --- a/data/monster/Dwarves/dwarf geomancer.xml +++ b/data/monster/Dwarves/dwarf geomancer.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -50,16 +50,18 @@ - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Dwarves/dwarf guard.xml b/data/monster/Dwarves/dwarf guard.xml index 4aa7b0e..9b8a518 100644 --- a/data/monster/Dwarves/dwarf guard.xml +++ b/data/monster/Dwarves/dwarf guard.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -34,22 +34,18 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Dwarves/dwarf soldier.xml b/data/monster/Dwarves/dwarf soldier.xml index ae11551..d3acabd 100644 --- a/data/monster/Dwarves/dwarf soldier.xml +++ b/data/monster/Dwarves/dwarf soldier.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -33,23 +33,19 @@ - - - + + + + + - - - - - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Dwarves/dwarf.xml b/data/monster/Dwarves/dwarf.xml index a58b92b..d63f79e 100644 --- a/data/monster/Dwarves/dwarf.xml +++ b/data/monster/Dwarves/dwarf.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -30,18 +30,16 @@ - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Dworcs/dworc fleshhunter.xml b/data/monster/Dworcs/dworc fleshhunter.xml index 1735756..798682f 100644 --- a/data/monster/Dworcs/dworc fleshhunter.xml +++ b/data/monster/Dworcs/dworc fleshhunter.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -36,22 +36,16 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Dworcs/dworc venomsniper.xml b/data/monster/Dworcs/dworc venomsniper.xml index 5079fe6..a91186a 100644 --- a/data/monster/Dworcs/dworc venomsniper.xml +++ b/data/monster/Dworcs/dworc venomsniper.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -37,22 +37,16 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Dworcs/dworc voodoomaster.xml b/data/monster/Dworcs/dworc voodoomaster.xml index 21cc271..104aad6 100644 --- a/data/monster/Dworcs/dworc voodoomaster.xml +++ b/data/monster/Dworcs/dworc voodoomaster.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -61,23 +61,16 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Elephants/elephant.xml b/data/monster/Elephants/elephant.xml index b4065c5..8842ab6 100644 --- a/data/monster/Elephants/elephant.xml +++ b/data/monster/Elephants/elephant.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -33,13 +33,9 @@ - - - - - - - - + + + + diff --git a/data/monster/Elephants/mammoth.xml b/data/monster/Elephants/mammoth.xml index a711fce..50d61a1 100644 --- a/data/monster/Elephants/mammoth.xml +++ b/data/monster/Elephants/mammoth.xml @@ -18,7 +18,7 @@ - + @@ -33,8 +33,11 @@ - - - + + + + + + diff --git a/data/monster/Elves/elf arcanist.xml b/data/monster/Elves/elf arcanist.xml index 7022a02..d091e3f 100644 --- a/data/monster/Elves/elf arcanist.xml +++ b/data/monster/Elves/elf arcanist.xml @@ -1,4 +1,4 @@ - + @@ -52,30 +52,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Elves/elf scout.xml b/data/monster/Elves/elf scout.xml index cb8889b..e980871 100644 --- a/data/monster/Elves/elf scout.xml +++ b/data/monster/Elves/elf scout.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -39,19 +39,17 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Elves/elf.xml b/data/monster/Elves/elf.xml index 1a5a9ae..544abb0 100644 --- a/data/monster/Elves/elf.xml +++ b/data/monster/Elves/elf.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -39,22 +39,14 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Energy-Elementals/charged energy elemental.xml b/data/monster/Energy-Elementals/charged energy elemental.xml index b959cc6..84e53a4 100644 --- a/data/monster/Energy-Elementals/charged energy elemental.xml +++ b/data/monster/Energy-Elementals/charged energy elemental.xml @@ -19,7 +19,7 @@ - + @@ -42,8 +42,9 @@ - - - + + + + diff --git a/data/monster/Energy-Elementals/energy elemental.xml b/data/monster/Energy-Elementals/energy elemental.xml index eb56df6..7defcd7 100644 --- a/data/monster/Energy-Elementals/energy elemental.xml +++ b/data/monster/Energy-Elementals/energy elemental.xml @@ -19,7 +19,7 @@ - + @@ -49,10 +49,19 @@ - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Energy-Elementals/energy overlord.xml b/data/monster/Energy-Elementals/energy overlord.xml index 68e9fc3..8914ae7 100644 --- a/data/monster/Energy-Elementals/energy overlord.xml +++ b/data/monster/Energy-Elementals/energy overlord.xml @@ -19,7 +19,7 @@ - + @@ -47,9 +47,10 @@ - - - - + + + + + diff --git a/data/monster/Energy-Elementals/massive energy elemental.xml b/data/monster/Energy-Elementals/massive energy elemental.xml index 351690a..29b8761 100644 --- a/data/monster/Energy-Elementals/massive energy elemental.xml +++ b/data/monster/Energy-Elementals/massive energy elemental.xml @@ -19,7 +19,7 @@ - + @@ -41,4 +41,18 @@ + + + + + + + + + + + + + + diff --git a/data/monster/Energy-Elementals/overcharged energy elemental.xml b/data/monster/Energy-Elementals/overcharged energy elemental.xml index 9d1d2b6..6dd7a10 100644 --- a/data/monster/Energy-Elementals/overcharged energy elemental.xml +++ b/data/monster/Energy-Elementals/overcharged energy elemental.xml @@ -19,7 +19,7 @@ - + @@ -45,10 +45,13 @@ - - - - - + + + + + + + + diff --git a/data/monster/Felines/cat.xml b/data/monster/Felines/cat.xml index 57c220b..a45e7f7 100644 --- a/data/monster/Felines/cat.xml +++ b/data/monster/Felines/cat.xml @@ -25,4 +25,4 @@ - + \ No newline at end of file diff --git a/data/monster/Felines/lion.xml b/data/monster/Felines/lion.xml index 8d81756..52e6405 100644 --- a/data/monster/Felines/lion.xml +++ b/data/monster/Felines/lion.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -31,8 +31,8 @@ - - - + + + diff --git a/data/monster/Felines/midnight panther.xml b/data/monster/Felines/midnight panther.xml new file mode 100644 index 0000000..e314f1f --- /dev/null +++ b/data/monster/Felines/midnight panther.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Felines/tiger.xml b/data/monster/Felines/tiger.xml index 9bdbc3c..5f9595c 100644 --- a/data/monster/Felines/tiger.xml +++ b/data/monster/Felines/tiger.xml @@ -18,7 +18,7 @@ - + @@ -30,9 +30,8 @@ - - - - + + + diff --git a/data/monster/Frogs/azure frog.xml b/data/monster/Frogs/azure frog.xml index 113b026..a8f12db 100644 --- a/data/monster/Frogs/azure frog.xml +++ b/data/monster/Frogs/azure frog.xml @@ -18,7 +18,7 @@ - + @@ -29,7 +29,7 @@ - - + + diff --git a/data/monster/Frogs/coral frog.xml b/data/monster/Frogs/coral frog.xml index 45459d7..3150d1e 100644 --- a/data/monster/Frogs/coral frog.xml +++ b/data/monster/Frogs/coral frog.xml @@ -18,14 +18,14 @@ - + - - + + diff --git a/data/monster/Frogs/crimson frog.xml b/data/monster/Frogs/crimson frog.xml index 6b605b7..a81e299 100644 --- a/data/monster/Frogs/crimson frog.xml +++ b/data/monster/Frogs/crimson frog.xml @@ -18,14 +18,14 @@ - + - - + + diff --git a/data/monster/Frogs/green frog.xml b/data/monster/Frogs/green frog.xml index 3023a93..5cfaf09 100644 --- a/data/monster/Frogs/green frog.xml +++ b/data/monster/Frogs/green frog.xml @@ -18,11 +18,11 @@ - + - + \ No newline at end of file diff --git a/data/monster/Frogs/orchid frog.xml b/data/monster/Frogs/orchid frog.xml index b381817..b60ba11 100644 --- a/data/monster/Frogs/orchid frog.xml +++ b/data/monster/Frogs/orchid frog.xml @@ -1,4 +1,4 @@ - + @@ -18,11 +18,11 @@ - + - - + + diff --git a/data/monster/Frogs/toad.xml b/data/monster/Frogs/toad.xml index bf0dceb..ce0010a 100644 --- a/data/monster/Frogs/toad.xml +++ b/data/monster/Frogs/toad.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -34,11 +34,11 @@ - - - - - - + + + + + + diff --git a/data/monster/Geo-Elementals/damaged worker golem.xml b/data/monster/Geo-Elementals/damaged worker golem.xml index ad2033a..e581f24 100644 --- a/data/monster/Geo-Elementals/damaged worker golem.xml +++ b/data/monster/Geo-Elementals/damaged worker golem.xml @@ -18,7 +18,7 @@ - + @@ -45,14 +45,10 @@ + + + + - - - - - - - - diff --git a/data/monster/Geo-Elementals/earth elemental.xml b/data/monster/Geo-Elementals/earth elemental.xml index 03f3727..c59610c 100644 --- a/data/monster/Geo-Elementals/earth elemental.xml +++ b/data/monster/Geo-Elementals/earth elemental.xml @@ -19,7 +19,7 @@ - + @@ -45,9 +45,17 @@ - - - - + + + + + + + + + + + + diff --git a/data/monster/Geo-Elementals/earth overlord.xml b/data/monster/Geo-Elementals/earth overlord.xml index 772d90a..3af0975 100644 --- a/data/monster/Geo-Elementals/earth overlord.xml +++ b/data/monster/Geo-Elementals/earth overlord.xml @@ -19,7 +19,7 @@ - + @@ -44,8 +44,13 @@ - - - + + + + + + + + diff --git a/data/monster/Geo-Elementals/gargoyle.xml b/data/monster/Geo-Elementals/gargoyle.xml index eaf07af..b72c35f 100644 --- a/data/monster/Geo-Elementals/gargoyle.xml +++ b/data/monster/Geo-Elementals/gargoyle.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -45,18 +45,17 @@ - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Geo-Elementals/jagged earth elemental.xml b/data/monster/Geo-Elementals/jagged earth elemental.xml index 2776848..259bb10 100644 --- a/data/monster/Geo-Elementals/jagged earth elemental.xml +++ b/data/monster/Geo-Elementals/jagged earth elemental.xml @@ -19,7 +19,7 @@ - + @@ -44,9 +44,14 @@ - - - - + + + + + + + + + diff --git a/data/monster/Geo-Elementals/massive earth elemental.xml b/data/monster/Geo-Elementals/massive earth elemental.xml index 905541a..a5782eb 100644 --- a/data/monster/Geo-Elementals/massive earth elemental.xml +++ b/data/monster/Geo-Elementals/massive earth elemental.xml @@ -19,7 +19,7 @@ - + @@ -45,8 +45,20 @@ - - - + + + + + + + + + + + + + + + diff --git a/data/monster/Sorcerers/medusa.xml b/data/monster/Geo-Elementals/medusa.xml similarity index 65% rename from data/monster/Sorcerers/medusa.xml rename to data/monster/Geo-Elementals/medusa.xml index 2f869e1..90030a3 100644 --- a/data/monster/Sorcerers/medusa.xml +++ b/data/monster/Geo-Elementals/medusa.xml @@ -18,7 +18,7 @@ - + @@ -55,20 +55,22 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/data/monster/Geo-Elementals/muddy earth elemental.xml b/data/monster/Geo-Elementals/muddy earth elemental.xml index a496f09..b8db37c 100644 --- a/data/monster/Geo-Elementals/muddy earth elemental.xml +++ b/data/monster/Geo-Elementals/muddy earth elemental.xml @@ -19,7 +19,7 @@ - + @@ -41,8 +41,10 @@ - - - + + + + + diff --git a/data/monster/Geo-Elementals/stone golem.xml b/data/monster/Geo-Elementals/stone golem.xml index 6b5e452..e463046 100644 --- a/data/monster/Geo-Elementals/stone golem.xml +++ b/data/monster/Geo-Elementals/stone golem.xml @@ -18,7 +18,7 @@ - + @@ -33,17 +33,15 @@ - - - - - - - - - - - - + + + + + + + + + + diff --git a/data/monster/Geo-Elementals/war golem.xml b/data/monster/Geo-Elementals/war golem.xml index 69a9e25..bf96062 100644 --- a/data/monster/Geo-Elementals/war golem.xml +++ b/data/monster/Geo-Elementals/war golem.xml @@ -18,7 +18,7 @@ - + @@ -59,20 +59,30 @@ - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Geo-Elementals/worker golem.xml b/data/monster/Geo-Elementals/worker golem.xml index c2b55b8..d691347 100644 --- a/data/monster/Geo-Elementals/worker golem.xml +++ b/data/monster/Geo-Elementals/worker golem.xml @@ -18,7 +18,7 @@ - + @@ -49,27 +49,23 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Ghosts/ghost.xml b/data/monster/Ghosts/ghost.xml index 2272f9d..316775a 100644 --- a/data/monster/Ghosts/ghost.xml +++ b/data/monster/Ghosts/ghost.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -28,6 +28,7 @@ + @@ -37,20 +38,14 @@ - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Ghosts/phantasm summon.xml b/data/monster/Ghosts/phantasm summon.xml index 7570287..06eeccd 100644 --- a/data/monster/Ghosts/phantasm summon.xml +++ b/data/monster/Ghosts/phantasm summon.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -54,7 +54,4 @@ - - - diff --git a/data/monster/Ghosts/phantasm.xml b/data/monster/Ghosts/phantasm.xml index d4dde1a..1c54125 100644 --- a/data/monster/Ghosts/phantasm.xml +++ b/data/monster/Ghosts/phantasm.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -58,12 +58,20 @@ - - - - - - - + + + + + + + + + + + + + + + diff --git a/data/monster/Ghosts/spectre.xml b/data/monster/Ghosts/spectre.xml index 01534b7..7bdfcd9 100644 --- a/data/monster/Ghosts/spectre.xml +++ b/data/monster/Ghosts/spectre.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -52,24 +52,24 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Ghosts/wisp.xml b/data/monster/Ghosts/wisp.xml index 27a4218..ee72eaf 100644 --- a/data/monster/Ghosts/wisp.xml +++ b/data/monster/Ghosts/wisp.xml @@ -8,19 +8,19 @@ - - + + - + - + - - + + @@ -37,13 +37,12 @@ - - + + - + - diff --git a/data/monster/Giants/behemoth.xml b/data/monster/Giants/behemoth.xml index b7a9c80..70bf011 100644 --- a/data/monster/Giants/behemoth.xml +++ b/data/monster/Giants/behemoth.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -47,33 +47,28 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Giants/cyclops drone.xml b/data/monster/Giants/cyclops drone.xml index 753ae80..224ec0e 100644 --- a/data/monster/Giants/cyclops drone.xml +++ b/data/monster/Giants/cyclops drone.xml @@ -17,7 +17,7 @@ - + @@ -37,12 +37,15 @@ - - - - - - - + + + + + + + + + + diff --git a/data/monster/Giants/cyclops smith.xml b/data/monster/Giants/cyclops smith.xml index 71987d9..2f9c796 100644 --- a/data/monster/Giants/cyclops smith.xml +++ b/data/monster/Giants/cyclops smith.xml @@ -17,7 +17,7 @@ - + @@ -37,13 +37,19 @@ - - - - - + + + + + - - + + + + + + + + diff --git a/data/monster/Giants/cyclops.xml b/data/monster/Giants/cyclops.xml index 019b214..c13078d 100644 --- a/data/monster/Giants/cyclops.xml +++ b/data/monster/Giants/cyclops.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -35,21 +35,18 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Giants/frost giant.xml b/data/monster/Giants/frost giant.xml index 8783a81..71f7719 100644 --- a/data/monster/Giants/frost giant.xml +++ b/data/monster/Giants/frost giant.xml @@ -19,7 +19,7 @@ - + @@ -34,20 +34,19 @@ - + + + - + + + + + + - - - - - - - - - + diff --git a/data/monster/Giants/frost giantess.xml b/data/monster/Giants/frost giantess.xml index 9d8903f..44dc319 100644 --- a/data/monster/Giants/frost giantess.xml +++ b/data/monster/Giants/frost giantess.xml @@ -19,7 +19,7 @@ - + @@ -37,20 +37,21 @@ - + + + - - - + + + + + + + - - - - - - - - + + + diff --git a/data/monster/Giants/yeti.xml b/data/monster/Giants/yeti.xml index e9342ce..85cff78 100644 --- a/data/monster/Giants/yeti.xml +++ b/data/monster/Giants/yeti.xml @@ -18,7 +18,7 @@ - + @@ -33,24 +33,18 @@ - + - - - - - - - - - - - - + + + + + + diff --git a/data/monster/Goblins/goblin assassin.xml b/data/monster/Goblins/goblin assassin.xml index 8de71db..e891fec 100644 --- a/data/monster/Goblins/goblin assassin.xml +++ b/data/monster/Goblins/goblin assassin.xml @@ -17,7 +17,7 @@ - + @@ -41,19 +41,16 @@ - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Goblins/goblin leader.xml b/data/monster/Goblins/goblin leader.xml index 5dc5e47..68f2ed1 100644 --- a/data/monster/Goblins/goblin leader.xml +++ b/data/monster/Goblins/goblin leader.xml @@ -18,7 +18,7 @@ - + @@ -37,20 +37,16 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Goblins/goblin scavenger.xml b/data/monster/Goblins/goblin scavenger.xml index b340002..650e1f7 100644 --- a/data/monster/Goblins/goblin scavenger.xml +++ b/data/monster/Goblins/goblin scavenger.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -41,20 +41,16 @@ - - - - - - - - - - - - - - + + + + + + + + + + + - diff --git a/data/monster/Goblins/goblin.xml b/data/monster/Goblins/goblin.xml index d48a1e1..de2bd39 100644 --- a/data/monster/Goblins/goblin.xml +++ b/data/monster/Goblins/goblin.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -38,21 +38,16 @@ - - - - + + + + + + + + - - - - - - - - - - - + + diff --git a/data/monster/Goblins/grynch clan goblin.xml b/data/monster/Goblins/grynch clan goblin.xml index c96808a..119c0e5 100644 --- a/data/monster/Goblins/grynch clan goblin.xml +++ b/data/monster/Goblins/grynch clan goblin.xml @@ -1,5 +1,5 @@ - + @@ -25,14 +25,7 @@ - - - - - - - @@ -63,55 +56,47 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Grunts/boar.xml b/data/monster/Grunts/boar.xml new file mode 100644 index 0000000..79f2b2d --- /dev/null +++ b/data/monster/Grunts/boar.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Misc/pig.xml b/data/monster/Grunts/pig.xml similarity index 77% rename from data/monster/Misc/pig.xml rename to data/monster/Grunts/pig.xml index 208a628..89d5589 100644 --- a/data/monster/Misc/pig.xml +++ b/data/monster/Grunts/pig.xml @@ -1,6 +1,6 @@ - + - + @@ -22,7 +22,7 @@ - - + + diff --git a/data/monster/Hydro-Elementals/ice overlord.xml b/data/monster/Hydro-Elementals/ice overlord.xml index 625e152..71d615f 100644 --- a/data/monster/Hydro-Elementals/ice overlord.xml +++ b/data/monster/Hydro-Elementals/ice overlord.xml @@ -1,5 +1,5 @@ - + @@ -19,7 +19,7 @@ - + @@ -39,16 +39,17 @@ - - + - - - + + + + + diff --git a/data/monster/Hydro-Elementals/massive water elemental.xml b/data/monster/Hydro-Elementals/massive water elemental.xml index 56ad62f..9b425a7 100644 --- a/data/monster/Hydro-Elementals/massive water elemental.xml +++ b/data/monster/Hydro-Elementals/massive water elemental.xml @@ -1,7 +1,7 @@ - + @@ -18,7 +18,7 @@ - + @@ -47,5 +47,6 @@ + diff --git a/data/monster/Hydro-Elementals/roaring water elemental.xml b/data/monster/Hydro-Elementals/roaring water elemental.xml index 3b07004..501df48 100644 --- a/data/monster/Hydro-Elementals/roaring water elemental.xml +++ b/data/monster/Hydro-Elementals/roaring water elemental.xml @@ -1,5 +1,5 @@ - + @@ -19,7 +19,7 @@ - + @@ -39,12 +39,14 @@ + - - - - - + + + + + + diff --git a/data/monster/Hydro-Elementals/slick water elemental.xml b/data/monster/Hydro-Elementals/slick water elemental.xml index 9d8244d..9a041dd 100644 --- a/data/monster/Hydro-Elementals/slick water elemental.xml +++ b/data/monster/Hydro-Elementals/slick water elemental.xml @@ -1,5 +1,5 @@ - + @@ -19,7 +19,7 @@ - + @@ -40,14 +40,16 @@ + - + - - - + + + + diff --git a/data/monster/Hydro-Elementals/water elemental.xml b/data/monster/Hydro-Elementals/water elemental.xml index 7755f9d..7008e0b 100644 --- a/data/monster/Hydro-Elementals/water elemental.xml +++ b/data/monster/Hydro-Elementals/water elemental.xml @@ -1,7 +1,7 @@ - + - + @@ -18,7 +18,7 @@ - + @@ -47,5 +47,6 @@ + diff --git a/data/monster/Insects/ancient scarab.xml b/data/monster/Insects/ancient scarab.xml index 85fd388..babd580 100644 --- a/data/monster/Insects/ancient scarab.xml +++ b/data/monster/Insects/ancient scarab.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -51,20 +51,19 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Insects/blue butterfly.xml b/data/monster/Insects/blue butterfly.xml index 5e16cdb..25ca293 100644 --- a/data/monster/Insects/blue butterfly.xml +++ b/data/monster/Insects/blue butterfly.xml @@ -22,14 +22,8 @@ - - - - - - - + \ No newline at end of file diff --git a/data/monster/Insects/brimstone bug.xml b/data/monster/Insects/brimstone bug.xml new file mode 100644 index 0000000..940268c --- /dev/null +++ b/data/monster/Insects/brimstone bug.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Insects/bug.xml b/data/monster/Insects/bug.xml index ae25129..3e05d8c 100644 --- a/data/monster/Insects/bug.xml +++ b/data/monster/Insects/bug.xml @@ -1,4 +1,4 @@ - + @@ -18,15 +18,14 @@ - + - - - + + diff --git a/data/monster/Insects/centipede.xml b/data/monster/Insects/centipede.xml index a632ecb..f35ea15 100644 --- a/data/monster/Insects/centipede.xml +++ b/data/monster/Insects/centipede.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -28,18 +28,9 @@ - - - - - - - - - - - - - + + + + diff --git a/data/monster/Insects/cockroach.xml b/data/monster/Insects/cockroach.xml index 3b1a477..32f7da3 100644 --- a/data/monster/Insects/cockroach.xml +++ b/data/monster/Insects/cockroach.xml @@ -23,6 +23,6 @@ - + diff --git a/data/monster/Insects/insect swarm.xml b/data/monster/Insects/insect swarm.xml new file mode 100644 index 0000000..ae63fa6 --- /dev/null +++ b/data/monster/Insects/insect swarm.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Insects/insectoid scout.xml b/data/monster/Insects/insectoid scout.xml new file mode 100644 index 0000000..6cfac95 --- /dev/null +++ b/data/monster/Insects/insectoid scout.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Insects/lancer beetle.xml b/data/monster/Insects/lancer beetle.xml new file mode 100644 index 0000000..9c47b72 --- /dev/null +++ b/data/monster/Insects/lancer beetle.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Insects/larva.xml b/data/monster/Insects/larva.xml index e6ec340..14a6448 100644 --- a/data/monster/Insects/larva.xml +++ b/data/monster/Insects/larva.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -31,8 +31,7 @@ - - - + + diff --git a/data/monster/Insects/pink butterfly.xml b/data/monster/Insects/purple butterfly.xml similarity index 83% rename from data/monster/Insects/pink butterfly.xml rename to data/monster/Insects/purple butterfly.xml index f156d23..efa88f3 100644 --- a/data/monster/Insects/pink butterfly.xml +++ b/data/monster/Insects/purple butterfly.xml @@ -22,14 +22,8 @@ - - - - - - - + \ No newline at end of file diff --git a/data/monster/Insects/red butterfly.xml b/data/monster/Insects/red butterfly.xml index fde52b9..634e8f0 100644 --- a/data/monster/Insects/red butterfly.xml +++ b/data/monster/Insects/red butterfly.xml @@ -22,14 +22,8 @@ - - - - - - - + \ No newline at end of file diff --git a/data/monster/Insects/sandcrawler.xml b/data/monster/Insects/sandcrawler.xml new file mode 100644 index 0000000..0f5cbd6 --- /dev/null +++ b/data/monster/Insects/sandcrawler.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Insects/scarab.xml b/data/monster/Insects/scarab.xml index bbf8717..2435cbf 100644 --- a/data/monster/Insects/scarab.xml +++ b/data/monster/Insects/scarab.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -39,18 +39,13 @@ - - - - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Insects/terramite.xml b/data/monster/Insects/terramite.xml new file mode 100644 index 0000000..f92d08b --- /dev/null +++ b/data/monster/Insects/terramite.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Insects/wasp.xml b/data/monster/Insects/wasp.xml index 7c07e1d..babf162 100644 --- a/data/monster/Insects/wasp.xml +++ b/data/monster/Insects/wasp.xml @@ -18,7 +18,7 @@ - + @@ -29,6 +29,6 @@ - + - + \ No newline at end of file diff --git a/data/monster/Insects/yellow butterfly.xml b/data/monster/Insects/yellow butterfly.xml index 855e508..8d278e7 100644 --- a/data/monster/Insects/yellow butterfly.xml +++ b/data/monster/Insects/yellow butterfly.xml @@ -21,15 +21,4 @@ - - - - - - - - - - - - + \ No newline at end of file diff --git a/data/monster/Isle of Evil/berserker chicken.xml b/data/monster/Isle of Evil/berserker chicken.xml new file mode 100644 index 0000000..ae04187 --- /dev/null +++ b/data/monster/Isle of Evil/berserker chicken.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/boogey.xml b/data/monster/Isle of Evil/boogey.xml new file mode 100644 index 0000000..5d4ed93 --- /dev/null +++ b/data/monster/Isle of Evil/boogey.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/demon parrot.xml b/data/monster/Isle of Evil/demon parrot.xml new file mode 100644 index 0000000..0aac9be --- /dev/null +++ b/data/monster/Isle of Evil/demon parrot.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/dirtbeard.xml b/data/monster/Isle of Evil/dirtbeard.xml new file mode 100644 index 0000000..b61407c --- /dev/null +++ b/data/monster/Isle of Evil/dirtbeard.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/docter perhaps.xml b/data/monster/Isle of Evil/docter perhaps.xml new file mode 100644 index 0000000..ff3f278 --- /dev/null +++ b/data/monster/Isle of Evil/docter perhaps.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/doom deer.xml b/data/monster/Isle of Evil/doom deer.xml new file mode 100644 index 0000000..c7087e9 --- /dev/null +++ b/data/monster/Isle of Evil/doom deer.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/evil mastermind.xml b/data/monster/Isle of Evil/evil mastermind.xml new file mode 100644 index 0000000..1678592 --- /dev/null +++ b/data/monster/Isle of Evil/evil mastermind.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/evil sheep lord.xml b/data/monster/Isle of Evil/evil sheep lord.xml new file mode 100644 index 0000000..035f39f --- /dev/null +++ b/data/monster/Isle of Evil/evil sheep lord.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/evil sheep.xml b/data/monster/Isle of Evil/evil sheep.xml new file mode 100644 index 0000000..f1e8cff --- /dev/null +++ b/data/monster/Isle of Evil/evil sheep.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/hot dog.xml b/data/monster/Isle of Evil/hot dog.xml new file mode 100644 index 0000000..c8ce33a --- /dev/null +++ b/data/monster/Isle of Evil/hot dog.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/infernal frog.xml b/data/monster/Isle of Evil/infernal frog.xml new file mode 100644 index 0000000..5df4a9c --- /dev/null +++ b/data/monster/Isle of Evil/infernal frog.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/killer rabbit.xml b/data/monster/Isle of Evil/killer rabbit.xml new file mode 100644 index 0000000..f0f1488 --- /dev/null +++ b/data/monster/Isle of Evil/killer rabbit.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/mephiles.xml b/data/monster/Isle of Evil/mephiles.xml new file mode 100644 index 0000000..dd61b54 --- /dev/null +++ b/data/monster/Isle of Evil/mephiles.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/monstor.xml b/data/monster/Isle of Evil/monstor.xml new file mode 100644 index 0000000..9faf333 --- /dev/null +++ b/data/monster/Isle of Evil/monstor.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Isle of Evil/vampire pig.xml b/data/monster/Isle of Evil/vampire pig.xml new file mode 100644 index 0000000..737f6a5 --- /dev/null +++ b/data/monster/Isle of Evil/vampire pig.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/battlemaster zunzu.xml b/data/monster/Lizards/battlemaster zunzu.xml new file mode 100644 index 0000000..6899263 --- /dev/null +++ b/data/monster/Lizards/battlemaster zunzu.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/draken abomination.xml b/data/monster/Lizards/draken abomination.xml new file mode 100644 index 0000000..9888082 --- /dev/null +++ b/data/monster/Lizards/draken abomination.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/draken elite.xml b/data/monster/Lizards/draken elite.xml new file mode 100644 index 0000000..16e1967 --- /dev/null +++ b/data/monster/Lizards/draken elite.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/draken spellweaver.xml b/data/monster/Lizards/draken spellweaver.xml new file mode 100644 index 0000000..34cd9fc --- /dev/null +++ b/data/monster/Lizards/draken spellweaver.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/draken warmaster.xml b/data/monster/Lizards/draken warmaster.xml new file mode 100644 index 0000000..f89a686 --- /dev/null +++ b/data/monster/Lizards/draken warmaster.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/eternal guardian.xml b/data/monster/Lizards/eternal guardian.xml new file mode 100644 index 0000000..2c8f732 --- /dev/null +++ b/data/monster/Lizards/eternal guardian.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Lizards/lizard abomination.xml b/data/monster/Lizards/lizard abomination.xml new file mode 100644 index 0000000..7af3fb9 --- /dev/null +++ b/data/monster/Lizards/lizard abomination.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/lizard chosen.xml b/data/monster/Lizards/lizard chosen.xml new file mode 100644 index 0000000..918ebad --- /dev/null +++ b/data/monster/Lizards/lizard chosen.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/lizard dragon priest.xml b/data/monster/Lizards/lizard dragon priest.xml new file mode 100644 index 0000000..5ca7f2d --- /dev/null +++ b/data/monster/Lizards/lizard dragon priest.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/lizard high guard.xml b/data/monster/Lizards/lizard high guard.xml new file mode 100644 index 0000000..370b463 --- /dev/null +++ b/data/monster/Lizards/lizard high guard.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/lizard legionnaire.xml b/data/monster/Lizards/lizard legionnaire.xml new file mode 100644 index 0000000..61857f4 --- /dev/null +++ b/data/monster/Lizards/lizard legionnaire.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/lizard sentinel.xml b/data/monster/Lizards/lizard sentinel.xml index 73a6dc5..3d6b207 100644 --- a/data/monster/Lizards/lizard sentinel.xml +++ b/data/monster/Lizards/lizard sentinel.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -37,24 +37,17 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Lizards/lizard snakecharmer.xml b/data/monster/Lizards/lizard snakecharmer.xml index f935729..5a62836 100644 --- a/data/monster/Lizards/lizard snakecharmer.xml +++ b/data/monster/Lizards/lizard snakecharmer.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -51,22 +51,19 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Lizards/lizard templar.xml b/data/monster/Lizards/lizard templar.xml index bcc7b3b..9fe0cbd 100644 --- a/data/monster/Lizards/lizard templar.xml +++ b/data/monster/Lizards/lizard templar.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -31,20 +31,17 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Lizards/lizard zaogun.xml b/data/monster/Lizards/lizard zaogun.xml new file mode 100644 index 0000000..918dcdd --- /dev/null +++ b/data/monster/Lizards/lizard zaogun.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Lizards/wyvern.xml b/data/monster/Lizards/wyvern.xml index 73917f0..13de52f 100644 --- a/data/monster/Lizards/wyvern.xml +++ b/data/monster/Lizards/wyvern.xml @@ -1,5 +1,5 @@ - - + + @@ -18,7 +18,7 @@ - + @@ -47,18 +47,14 @@ - - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Minotaurs/minotaur archer.xml b/data/monster/Minotaurs/minotaur archer.xml index 0fcbc07..4a1ecb3 100644 --- a/data/monster/Minotaurs/minotaur archer.xml +++ b/data/monster/Minotaurs/minotaur archer.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -35,22 +35,19 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Minotaurs/minotaur guard.xml b/data/monster/Minotaurs/minotaur guard.xml index 36ffb05..390c771 100644 --- a/data/monster/Minotaurs/minotaur guard.xml +++ b/data/monster/Minotaurs/minotaur guard.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -32,21 +32,20 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/data/monster/Minotaurs/minotaur mage.xml b/data/monster/Minotaurs/minotaur mage.xml index d37a5b5..d3e6b29 100644 --- a/data/monster/Minotaurs/minotaur mage.xml +++ b/data/monster/Minotaurs/minotaur mage.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -47,23 +47,21 @@ - - - + + + + + + - - - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Minotaurs/minotaur.xml b/data/monster/Minotaurs/minotaur.xml index 2727974..d853912 100644 --- a/data/monster/Minotaurs/minotaur.xml +++ b/data/monster/Minotaurs/minotaur.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -31,22 +31,18 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Misc/badger.xml b/data/monster/Misc/badger.xml index a58683d..2a6e78e 100644 --- a/data/monster/Misc/badger.xml +++ b/data/monster/Misc/badger.xml @@ -18,11 +18,12 @@ - + - - + + + diff --git a/data/monster/Misc/bat.xml b/data/monster/Misc/bat.xml index 5fe0b18..8c5c1ef 100644 --- a/data/monster/Misc/bat.xml +++ b/data/monster/Misc/bat.xml @@ -1,4 +1,4 @@ - + @@ -18,17 +18,13 @@ - + - - - - - + diff --git a/data/monster/Misc/deer.xml b/data/monster/Misc/deer.xml index 1f9aaa0..8f0371c 100644 --- a/data/monster/Misc/deer.xml +++ b/data/monster/Misc/deer.xml @@ -21,19 +21,9 @@ - - - - - - - - - - - - - + + + diff --git a/data/monster/Misc/dromedary.xml b/data/monster/Misc/dromedary.xml new file mode 100644 index 0000000..3c241ef --- /dev/null +++ b/data/monster/Misc/dromedary.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Misc/hacker.xml b/data/monster/Misc/hacker.xml index e711f4f..3a847ab 100644 --- a/data/monster/Misc/hacker.xml +++ b/data/monster/Misc/hacker.xml @@ -29,15 +29,10 @@ - - - - - @@ -57,13 +52,9 @@ - - - - - - - - + + + + diff --git a/data/monster/Misc/horse(brown).xml b/data/monster/Misc/horse(brown).xml new file mode 100644 index 0000000..61c08d1 --- /dev/null +++ b/data/monster/Misc/horse(brown).xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Misc/horse(fire).xml b/data/monster/Misc/horse(fire).xml new file mode 100644 index 0000000..9328175 --- /dev/null +++ b/data/monster/Misc/horse(fire).xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Misc/horse(grey).xml b/data/monster/Misc/horse(grey).xml new file mode 100644 index 0000000..8b59281 --- /dev/null +++ b/data/monster/Misc/horse(grey).xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Misc/hyaena.xml b/data/monster/Misc/hyaena.xml index 4c4f4d7..cca0419 100644 --- a/data/monster/Misc/hyaena.xml +++ b/data/monster/Misc/hyaena.xml @@ -1,4 +1,4 @@ - + @@ -18,11 +18,11 @@ - + - - + + diff --git a/data/monster/Misc/rabbit.xml b/data/monster/Misc/rabbit.xml index c3739ef..b6e0fa7 100644 --- a/data/monster/Misc/rabbit.xml +++ b/data/monster/Misc/rabbit.xml @@ -1,4 +1,4 @@ - + @@ -22,7 +22,7 @@ - + diff --git a/data/monster/Misc/silver rabbit.xml b/data/monster/Misc/silver rabbit.xml index 7f4c89c..f1a3be1 100644 --- a/data/monster/Misc/silver rabbit.xml +++ b/data/monster/Misc/silver rabbit.xml @@ -1,4 +1,4 @@ - + @@ -20,7 +20,8 @@ - - + + + diff --git a/data/monster/Misc/skunk.xml b/data/monster/Misc/skunk.xml index db4b27f..255542e 100644 --- a/data/monster/Misc/skunk.xml +++ b/data/monster/Misc/skunk.xml @@ -1,4 +1,4 @@ - + @@ -18,14 +18,15 @@ - + - - + + + diff --git a/data/monster/Misc/squirrel.xml b/data/monster/Misc/squirrel.xml index 53d5bcc..67d31de 100644 --- a/data/monster/Misc/squirrel.xml +++ b/data/monster/Misc/squirrel.xml @@ -24,7 +24,7 @@ - - + + diff --git a/data/monster/Misc/white deer.xml b/data/monster/Misc/white deer.xml new file mode 100644 index 0000000..d933aec --- /dev/null +++ b/data/monster/Misc/white deer.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Misc/yalahari.xml b/data/monster/Misc/yalahari.xml new file mode 100644 index 0000000..21eae30 --- /dev/null +++ b/data/monster/Misc/yalahari.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Monks/dark monk.xml b/data/monster/Monks/dark monk.xml index 24e25bf..d98f02c 100644 --- a/data/monster/Monks/dark monk.xml +++ b/data/monster/Monks/dark monk.xml @@ -1,5 +1,5 @@ - - + + @@ -18,7 +18,7 @@ - + @@ -45,23 +45,17 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Monks/monk.xml b/data/monster/Monks/monk.xml index 0883526..db1b26e 100644 --- a/data/monster/Monks/monk.xml +++ b/data/monster/Monks/monk.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -42,18 +42,16 @@ - - - + + + + + - - - - - - - - - + + + + + diff --git a/data/monster/Mutated/mutated bat.xml b/data/monster/Mutated/mutated bat.xml index 963c322..1374d79 100644 --- a/data/monster/Mutated/mutated bat.xml +++ b/data/monster/Mutated/mutated bat.xml @@ -18,7 +18,7 @@ - + @@ -40,19 +40,21 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/data/monster/Mutated/mutated human.xml b/data/monster/Mutated/mutated human.xml index 5625917..1389ecb 100644 --- a/data/monster/Mutated/mutated human.xml +++ b/data/monster/Mutated/mutated human.xml @@ -18,7 +18,7 @@ - + @@ -46,22 +46,20 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/data/monster/Mutated/mutated rat.xml b/data/monster/Mutated/mutated rat.xml index a3e75cd..d57d1ce 100644 --- a/data/monster/Mutated/mutated rat.xml +++ b/data/monster/Mutated/mutated rat.xml @@ -18,7 +18,7 @@ - + @@ -51,21 +51,17 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Mutated/mutated tiger.xml b/data/monster/Mutated/mutated tiger.xml index 5d15530..8cfbec8 100644 --- a/data/monster/Mutated/mutated tiger.xml +++ b/data/monster/Mutated/mutated tiger.xml @@ -18,7 +18,7 @@ - + @@ -48,17 +48,14 @@ - - + + + + + - - - - - - - - - + + + diff --git a/data/monster/Necromancers/necromancer.xml b/data/monster/Necromancers/necromancer.xml index 39602e3..fbd21f7 100644 --- a/data/monster/Necromancers/necromancer.xml +++ b/data/monster/Necromancers/necromancer.xml @@ -1,4 +1,4 @@ - + @@ -54,24 +54,18 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Necromancers/priestess.xml b/data/monster/Necromancers/priestess.xml index fd27525..3c145a3 100644 --- a/data/monster/Necromancers/priestess.xml +++ b/data/monster/Necromancers/priestess.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -50,30 +50,22 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/data/monster/Orcs/orc berserker.xml b/data/monster/Orcs/orc berserker.xml index 5d6d73f..9fa430e 100644 --- a/data/monster/Orcs/orc berserker.xml +++ b/data/monster/Orcs/orc berserker.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -35,17 +35,13 @@ - - - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Orcs/orc leader.xml b/data/monster/Orcs/orc leader.xml index c495a05..823c232 100644 --- a/data/monster/Orcs/orc leader.xml +++ b/data/monster/Orcs/orc leader.xml @@ -1,4 +1,4 @@ - + @@ -18,48 +18,36 @@ - + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Orcs/orc marauder.xml b/data/monster/Orcs/orc marauder.xml new file mode 100644 index 0000000..e73c479 --- /dev/null +++ b/data/monster/Orcs/orc marauder.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Orcs/orc rider.xml b/data/monster/Orcs/orc rider.xml index 6b27dd8..12ded37 100644 --- a/data/monster/Orcs/orc rider.xml +++ b/data/monster/Orcs/orc rider.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -36,21 +36,14 @@ - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Orcs/orc shaman.xml b/data/monster/Orcs/orc shaman.xml index bad1712..2c4693a 100644 --- a/data/monster/Orcs/orc shaman.xml +++ b/data/monster/Orcs/orc shaman.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -49,17 +49,13 @@ - - - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Orcs/orc spearman.xml b/data/monster/Orcs/orc spearman.xml index 4b30b87..c39421b 100644 --- a/data/monster/Orcs/orc spearman.xml +++ b/data/monster/Orcs/orc spearman.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -34,16 +34,12 @@ - - - - - - - - - - - + + + + + + + diff --git a/data/monster/Orcs/orc warlord.xml b/data/monster/Orcs/orc warlord.xml index 5fc1f2a..e374557 100644 --- a/data/monster/Orcs/orc warlord.xml +++ b/data/monster/Orcs/orc warlord.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -45,28 +45,26 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Orcs/orc warrior.xml b/data/monster/Orcs/orc warrior.xml index a3f8f95..951ef8c 100644 --- a/data/monster/Orcs/orc warrior.xml +++ b/data/monster/Orcs/orc warrior.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -33,19 +33,14 @@ - - - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Orcs/orc.xml b/data/monster/Orcs/orc.xml index b370cdc..ca96640 100644 --- a/data/monster/Orcs/orc.xml +++ b/data/monster/Orcs/orc.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -33,17 +33,12 @@ - - - - - - - - - - - - + + + + + + + diff --git a/data/monster/Outlaws/assassin.xml b/data/monster/Outlaws/assassin.xml index 8d1a3ee..f395882 100644 --- a/data/monster/Outlaws/assassin.xml +++ b/data/monster/Outlaws/assassin.xml @@ -1,4 +1,4 @@ - + @@ -17,7 +17,7 @@ - + @@ -27,9 +27,6 @@ - - - @@ -47,18 +44,19 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Outlaws/bandit.xml b/data/monster/Outlaws/bandit.xml index cdbe802..18fe71e 100644 --- a/data/monster/Outlaws/bandit.xml +++ b/data/monster/Outlaws/bandit.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -30,16 +30,16 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Outlaws/black knight.xml b/data/monster/Outlaws/black knight.xml index 029e29c..5d77272 100644 --- a/data/monster/Outlaws/black knight.xml +++ b/data/monster/Outlaws/black knight.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -48,30 +48,28 @@ - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/data/monster/Outlaws/crazed beggar.xml b/data/monster/Outlaws/crazed beggar.xml index e9265d1..b567882 100644 --- a/data/monster/Outlaws/crazed beggar.xml +++ b/data/monster/Outlaws/crazed beggar.xml @@ -18,7 +18,7 @@ - + @@ -35,19 +35,19 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Outlaws/feverish citizen.xml b/data/monster/Outlaws/feverish citizen.xml new file mode 100644 index 0000000..e7b860f --- /dev/null +++ b/data/monster/Outlaws/feverish citizen.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Outlaws/gang member.xml b/data/monster/Outlaws/gang member.xml index 4f85b85..e602b2f 100644 --- a/data/monster/Outlaws/gang member.xml +++ b/data/monster/Outlaws/gang member.xml @@ -18,7 +18,7 @@ - + @@ -31,16 +31,14 @@ - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Outlaws/gladiator.xml b/data/monster/Outlaws/gladiator.xml index edc78ab..e1d3f6b 100644 --- a/data/monster/Outlaws/gladiator.xml +++ b/data/monster/Outlaws/gladiator.xml @@ -18,7 +18,7 @@ - + @@ -37,17 +37,15 @@ - - - - - - - - - - - - + + + + + + + + + + diff --git a/data/monster/Outlaws/hero.xml b/data/monster/Outlaws/hero.xml index 5334947..365811e 100644 --- a/data/monster/Outlaws/hero.xml +++ b/data/monster/Outlaws/hero.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -48,31 +48,30 @@ - - + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Outlaws/hunter.xml b/data/monster/Outlaws/hunter.xml index 6934910..d6c5bb4 100644 --- a/data/monster/Outlaws/hunter.xml +++ b/data/monster/Outlaws/hunter.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -35,25 +35,21 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/data/monster/Outlaws/nomad.xml b/data/monster/Outlaws/nomad.xml index a60962b..d669d3d 100644 --- a/data/monster/Outlaws/nomad.xml +++ b/data/monster/Outlaws/nomad.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -34,25 +34,15 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/data/monster/Outlaws/poacher.xml b/data/monster/Outlaws/poacher.xml index ce6e854..30be35c 100644 --- a/data/monster/Outlaws/poacher.xml +++ b/data/monster/Outlaws/poacher.xml @@ -18,7 +18,7 @@ - + @@ -30,13 +30,13 @@ - - - - - - - - + + + + + + + + diff --git a/data/monster/Outlaws/primitive.xml b/data/monster/Outlaws/primitive.xml index fd0e15a..16872de 100644 --- a/data/monster/Outlaws/primitive.xml +++ b/data/monster/Outlaws/primitive.xml @@ -32,13 +32,8 @@ - - - - - @@ -59,13 +54,13 @@ - - - - - - - - + + + + + + + + diff --git a/data/monster/Outlaws/smuggler.xml b/data/monster/Outlaws/smuggler.xml index c548ef6..388bf7b 100644 --- a/data/monster/Outlaws/smuggler.xml +++ b/data/monster/Outlaws/smuggler.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -30,25 +30,16 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Outlaws/stalker.xml b/data/monster/Outlaws/stalker.xml index e8ff9c6..77fb1cf 100644 --- a/data/monster/Outlaws/stalker.xml +++ b/data/monster/Outlaws/stalker.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -37,15 +37,13 @@ - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Outlaws/wild warrior.xml b/data/monster/Outlaws/wild warrior.xml index 2204c1a..568e403 100644 --- a/data/monster/Outlaws/wild warrior.xml +++ b/data/monster/Outlaws/wild warrior.xml @@ -18,7 +18,7 @@ - + @@ -35,21 +35,16 @@ - - - - + + + + + + + + + + - - - - - - - - - - - diff --git a/data/monster/Pharaohs/ashmunrah.xml b/data/monster/Pharaohs/ashmunrah.xml index 1284c95..31c031e 100644 --- a/data/monster/Pharaohs/ashmunrah.xml +++ b/data/monster/Pharaohs/ashmunrah.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -65,26 +65,12 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + diff --git a/data/monster/Pharaohs/dipthrah.xml b/data/monster/Pharaohs/dipthrah.xml index 3410503..4735d1e 100644 --- a/data/monster/Pharaohs/dipthrah.xml +++ b/data/monster/Pharaohs/dipthrah.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -54,27 +54,16 @@ - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - diff --git a/data/monster/Pharaohs/mahrdis.xml b/data/monster/Pharaohs/mahrdis.xml index 0d5f341..fb6b76f 100644 --- a/data/monster/Pharaohs/mahrdis.xml +++ b/data/monster/Pharaohs/mahrdis.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -64,27 +64,16 @@ - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - diff --git a/data/monster/Pharaohs/morguthis.xml b/data/monster/Pharaohs/morguthis.xml index e4157c3..48bd49b 100644 --- a/data/monster/Pharaohs/morguthis.xml +++ b/data/monster/Pharaohs/morguthis.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -65,28 +65,16 @@ - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Pharaohs/omruc.xml b/data/monster/Pharaohs/omruc.xml index 2cdc137..2372c12 100644 --- a/data/monster/Pharaohs/omruc.xml +++ b/data/monster/Pharaohs/omruc.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -58,27 +58,20 @@ - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - diff --git a/data/monster/Pharaohs/rahemos.xml b/data/monster/Pharaohs/rahemos.xml index 39c9744..ea01485 100644 --- a/data/monster/Pharaohs/rahemos.xml +++ b/data/monster/Pharaohs/rahemos.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -71,28 +71,18 @@ - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - diff --git a/data/monster/Pharaohs/thalas.xml b/data/monster/Pharaohs/thalas.xml index fb8a583..f1b2ad4 100644 --- a/data/monster/Pharaohs/thalas.xml +++ b/data/monster/Pharaohs/thalas.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -56,33 +56,16 @@ - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Pharaohs/vashresamun.xml b/data/monster/Pharaohs/vashresamun.xml index ac128f2..b6f49a5 100644 --- a/data/monster/Pharaohs/vashresamun.xml +++ b/data/monster/Pharaohs/vashresamun.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -49,28 +49,17 @@ - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Pirates/pirate buccaneer.xml b/data/monster/Pirates/pirate buccaneer.xml index c3bd4ce..883cc74 100644 --- a/data/monster/Pirates/pirate buccaneer.xml +++ b/data/monster/Pirates/pirate buccaneer.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -42,22 +42,21 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/data/monster/Pirates/pirate corsair.xml b/data/monster/Pirates/pirate corsair.xml index 694bcf0..3e94ecf 100644 --- a/data/monster/Pirates/pirate corsair.xml +++ b/data/monster/Pirates/pirate corsair.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -44,20 +44,20 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/data/monster/Pirates/pirate cutthroat.xml b/data/monster/Pirates/pirate cutthroat.xml index 6b27e63..9188427 100644 --- a/data/monster/Pirates/pirate cutthroat.xml +++ b/data/monster/Pirates/pirate cutthroat.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -40,22 +40,18 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Ghosts/pirate ghost.xml b/data/monster/Pirates/pirate ghost.xml similarity index 75% rename from data/monster/Ghosts/pirate ghost.xml rename to data/monster/Pirates/pirate ghost.xml index c8e2542..bdf993d 100644 --- a/data/monster/Ghosts/pirate ghost.xml +++ b/data/monster/Pirates/pirate ghost.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -48,13 +48,12 @@ - - - - - - - - + + + + + + + diff --git a/data/monster/Pirates/pirate marauder.xml b/data/monster/Pirates/pirate marauder.xml index b2bdf13..3e3b58d 100644 --- a/data/monster/Pirates/pirate marauder.xml +++ b/data/monster/Pirates/pirate marauder.xml @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ - + @@ -41,21 +41,20 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/data/monster/Skeletons/pirate skeleton.xml b/data/monster/Pirates/pirate skeleton.xml similarity index 59% rename from data/monster/Skeletons/pirate skeleton.xml rename to data/monster/Pirates/pirate skeleton.xml index 2679c4e..1f27da5 100644 --- a/data/monster/Skeletons/pirate skeleton.xml +++ b/data/monster/Pirates/pirate skeleton.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -26,15 +26,12 @@ - - - - - - - - - - + + + + + + + diff --git a/data/monster/Pyro-Elementals/blazing fire elemental.xml b/data/monster/Pyro-Elementals/blazing fire elemental.xml index 66fb4d4..b3baf43 100644 --- a/data/monster/Pyro-Elementals/blazing fire elemental.xml +++ b/data/monster/Pyro-Elementals/blazing fire elemental.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -38,7 +38,9 @@ - - + + + + diff --git a/data/monster/Pyro-Elementals/blistering fire elemental.xml b/data/monster/Pyro-Elementals/blistering fire elemental.xml index 6b59f41..9e03dc4 100644 --- a/data/monster/Pyro-Elementals/blistering fire elemental.xml +++ b/data/monster/Pyro-Elementals/blistering fire elemental.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -35,9 +35,10 @@ - + + - - + + diff --git a/data/monster/Pyro-Elementals/fire elemental.xml b/data/monster/Pyro-Elementals/fire elemental.xml index 501b705..6c3f7b3 100644 --- a/data/monster/Pyro-Elementals/fire elemental.xml +++ b/data/monster/Pyro-Elementals/fire elemental.xml @@ -1,7 +1,7 @@ - + - + @@ -18,7 +18,7 @@ - + diff --git a/data/monster/Pyro-Elementals/fire overlord.xml b/data/monster/Pyro-Elementals/fire overlord.xml index 74f55f7..51329cc 100644 --- a/data/monster/Pyro-Elementals/fire overlord.xml +++ b/data/monster/Pyro-Elementals/fire overlord.xml @@ -18,7 +18,7 @@ - + @@ -48,10 +48,10 @@ - - - + + - + + diff --git a/data/monster/Pyro-Elementals/hellfire fighter.xml b/data/monster/Pyro-Elementals/hellfire fighter.xml index cf7c569..84b5d4b 100644 --- a/data/monster/Pyro-Elementals/hellfire fighter.xml +++ b/data/monster/Pyro-Elementals/hellfire fighter.xml @@ -18,7 +18,7 @@ - + @@ -42,22 +42,22 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/data/monster/Pyro-Elementals/massive fire elemental.xml b/data/monster/Pyro-Elementals/massive fire elemental.xml index 344056b..ac4febd 100644 --- a/data/monster/Pyro-Elementals/massive fire elemental.xml +++ b/data/monster/Pyro-Elementals/massive fire elemental.xml @@ -18,7 +18,7 @@ - + diff --git a/data/monster/Quaras/quara constrictor scout.xml b/data/monster/Quaras/quara constrictor scout.xml index 86f0421..62bc4c2 100644 --- a/data/monster/Quaras/quara constrictor scout.xml +++ b/data/monster/Quaras/quara constrictor scout.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -36,13 +36,11 @@ - - - - - - - - + + + + + + diff --git a/data/monster/Quaras/quara constrictor.xml b/data/monster/Quaras/quara constrictor.xml index 95dda51..4f6570d 100644 --- a/data/monster/Quaras/quara constrictor.xml +++ b/data/monster/Quaras/quara constrictor.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -36,6 +36,7 @@ + @@ -44,13 +45,14 @@ - - - - - - - - + + + + + + + + + diff --git a/data/monster/Quaras/quara hydromancer scout.xml b/data/monster/Quaras/quara hydromancer scout.xml index bbe1bc4..871f491 100644 --- a/data/monster/Quaras/quara hydromancer scout.xml +++ b/data/monster/Quaras/quara hydromancer scout.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -45,19 +45,16 @@ - - - + + + + + + + - - - - - - - - - - + + + - + \ No newline at end of file diff --git a/data/monster/Quaras/quara hydromancer.xml b/data/monster/Quaras/quara hydromancer.xml index 59f7142..e647796 100644 --- a/data/monster/Quaras/quara hydromancer.xml +++ b/data/monster/Quaras/quara hydromancer.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -37,6 +37,7 @@ + @@ -47,24 +48,18 @@ - - - - + + - - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Quaras/quara mantassin scout.xml b/data/monster/Quaras/quara mantassin scout.xml index 10a843c..a2cae7c 100644 --- a/data/monster/Quaras/quara mantassin scout.xml +++ b/data/monster/Quaras/quara mantassin scout.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -37,17 +37,13 @@ - - - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Quaras/quara mantassin.xml b/data/monster/Quaras/quara mantassin.xml index 89d6145..1e0ba1f 100644 --- a/data/monster/Quaras/quara mantassin.xml +++ b/data/monster/Quaras/quara mantassin.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -33,23 +33,25 @@ + - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Quaras/quara pincher scout.xml b/data/monster/Quaras/quara pincher scout.xml index fe80746..62f93a5 100644 --- a/data/monster/Quaras/quara pincher scout.xml +++ b/data/monster/Quaras/quara pincher scout.xml @@ -1,4 +1,4 @@ - + @@ -18,15 +18,13 @@ - + - - @@ -44,17 +42,13 @@ - - - - - - - - - - - - + + + + + + + + - + \ No newline at end of file diff --git a/data/monster/Quaras/quara pincher.xml b/data/monster/Quaras/quara pincher.xml index 8efeff3..ff7920d 100644 --- a/data/monster/Quaras/quara pincher.xml +++ b/data/monster/Quaras/quara pincher.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -33,6 +33,7 @@ + @@ -40,17 +41,17 @@ + + + + + - - + + - - - - - - - - + + + diff --git a/data/monster/Quaras/quara predator scout.xml b/data/monster/Quaras/quara predator scout.xml index fffda03..d329211 100644 --- a/data/monster/Quaras/quara predator scout.xml +++ b/data/monster/Quaras/quara predator scout.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -39,16 +39,13 @@ - - - + + + - - - - - - - + + + + diff --git a/data/monster/Quaras/quara predator.xml b/data/monster/Quaras/quara predator.xml index 60beadb..8095544 100644 --- a/data/monster/Quaras/quara predator.xml +++ b/data/monster/Quaras/quara predator.xml @@ -18,7 +18,7 @@ - + @@ -37,24 +37,26 @@ + - - - + + + + - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Rats/cave rat.xml b/data/monster/Rats/cave rat.xml index c1511bb..fbb2987 100644 --- a/data/monster/Rats/cave rat.xml +++ b/data/monster/Rats/cave rat.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -29,8 +29,9 @@ - - - + + + + diff --git a/data/monster/Rats/rat.xml b/data/monster/Rats/rat.xml index d45116e..0bcfea1 100644 --- a/data/monster/Rats/rat.xml +++ b/data/monster/Rats/rat.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -31,8 +31,9 @@ - - - + + + + diff --git a/data/monster/Reptiles/cobra.xml b/data/monster/Reptiles/cobra.xml deleted file mode 100644 index eee63f9..0000000 --- a/data/monster/Reptiles/cobra.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Reptiles/crocodile.xml b/data/monster/Reptiles/crocodile.xml index 30ac6c8..14b75ad 100644 --- a/data/monster/Reptiles/crocodile.xml +++ b/data/monster/Reptiles/crocodile.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -29,11 +29,11 @@ - - - - - - + + + + + + diff --git a/data/monster/Reptiles/hydra.xml b/data/monster/Reptiles/hydra.xml index 583c86b..cbf840a 100644 --- a/data/monster/Reptiles/hydra.xml +++ b/data/monster/Reptiles/hydra.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -52,27 +52,21 @@ - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + diff --git a/data/monster/Reptiles/killer caiman.xml b/data/monster/Reptiles/killer caiman.xml new file mode 100644 index 0000000..7d71e62 --- /dev/null +++ b/data/monster/Reptiles/killer caiman.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Reptiles/sea serpent.xml b/data/monster/Reptiles/sea serpent.xml deleted file mode 100644 index 27c707e..0000000 --- a/data/monster/Reptiles/sea serpent.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Reptiles/serpent spawn.xml b/data/monster/Reptiles/serpent spawn.xml deleted file mode 100644 index 62ca8fe..0000000 --- a/data/monster/Reptiles/serpent spawn.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Reptiles/snake.xml b/data/monster/Reptiles/snake.xml deleted file mode 100644 index e209f07..0000000 --- a/data/monster/Reptiles/snake.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Reptiles/wyvern.xml b/data/monster/Reptiles/wyvern.xml deleted file mode 100644 index 73917f0..0000000 --- a/data/monster/Reptiles/wyvern.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Rifts/rift scythe.xml b/data/monster/Rifts/rift scythe.xml index 5fa2f5e..51007d6 100644 --- a/data/monster/Rifts/rift scythe.xml +++ b/data/monster/Rifts/rift scythe.xml @@ -27,8 +27,8 @@ - - + + @@ -51,4 +51,4 @@ - + \ No newline at end of file diff --git a/data/monster/Rifts/rift worm.xml b/data/monster/Rifts/rift worm.xml index bc9a03a..f605526 100644 --- a/data/monster/Rifts/rift worm.xml +++ b/data/monster/Rifts/rift worm.xml @@ -24,5 +24,3 @@ - - diff --git a/data/monster/Serpents/cobra.xml b/data/monster/Serpents/cobra.xml index eee63f9..13e35ac 100644 --- a/data/monster/Serpents/cobra.xml +++ b/data/monster/Serpents/cobra.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -35,8 +35,4 @@ - - - - diff --git a/data/monster/Serpents/deepling scout.xml b/data/monster/Serpents/deepling scout.xml new file mode 100644 index 0000000..ba55c19 --- /dev/null +++ b/data/monster/Serpents/deepling scout.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Serpents/sea serpent.xml b/data/monster/Serpents/sea serpent.xml index 6ed54ec..28b222f 100644 --- a/data/monster/Serpents/sea serpent.xml +++ b/data/monster/Serpents/sea serpent.xml @@ -1,5 +1,5 @@ - + @@ -14,10 +14,10 @@ - + - + @@ -50,20 +50,26 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Serpents/serpent spawn.xml b/data/monster/Serpents/serpent spawn.xml index 62ca8fe..54ca1f3 100644 --- a/data/monster/Serpents/serpent spawn.xml +++ b/data/monster/Serpents/serpent spawn.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -65,30 +65,30 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Serpents/snake.xml b/data/monster/Serpents/snake.xml index e209f07..4b765f5 100644 --- a/data/monster/Serpents/snake.xml +++ b/data/monster/Serpents/snake.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + diff --git a/data/monster/Serpents/young sea serpent.xml b/data/monster/Serpents/young sea serpent.xml index c786759..dca585f 100644 --- a/data/monster/Serpents/young sea serpent.xml +++ b/data/monster/Serpents/young sea serpent.xml @@ -1,8 +1,8 @@ - - - - + + + + @@ -12,49 +12,59 @@ + - - - - - - - + + + + + + + + + + - - + + + - - - + + - + + + + + + - -- gold coin - -- meat - - -- dragon ham - - - -- gold coin - -- small saphire - --stealth ring - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + diff --git a/data/monster/Shapeshifters/mimic.xml b/data/monster/Shapeshifters/mimic.xml index 8619eec..f4a7303 100644 --- a/data/monster/Shapeshifters/mimic.xml +++ b/data/monster/Shapeshifters/mimic.xml @@ -18,4 +18,4 @@ - + \ No newline at end of file diff --git a/data/monster/Sheeps/black sheep.xml b/data/monster/Sheeps/black sheep.xml index 27f5694..73e249e 100644 --- a/data/monster/Sheeps/black sheep.xml +++ b/data/monster/Sheeps/black sheep.xml @@ -21,22 +21,10 @@ - - - - - - - - - - - - - + diff --git a/data/monster/Sheeps/sheep.xml b/data/monster/Sheeps/sheep.xml index ba6f853..398f153 100644 --- a/data/monster/Sheeps/sheep.xml +++ b/data/monster/Sheeps/sheep.xml @@ -1,4 +1,4 @@ - + @@ -28,8 +28,7 @@ - - - + + diff --git a/data/monster/Skeletons/betrayed wraith.xml b/data/monster/Skeletons/betrayed wraith.xml index 8c1437d..9ececff 100644 --- a/data/monster/Skeletons/betrayed wraith.xml +++ b/data/monster/Skeletons/betrayed wraith.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -56,20 +56,28 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Skeletons/bonebeast.xml b/data/monster/Skeletons/bonebeast.xml index 791315c..74504fb 100644 --- a/data/monster/Skeletons/bonebeast.xml +++ b/data/monster/Skeletons/bonebeast.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -51,18 +51,16 @@ - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/data/monster/Skeletons/demon skeleton.xml b/data/monster/Skeletons/demon skeleton.xml index b9a6f37..5c04314 100644 --- a/data/monster/Skeletons/demon skeleton.xml +++ b/data/monster/Skeletons/demon skeleton.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -30,21 +30,17 @@ - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/data/monster/Skeletons/dreadbeast.xml b/data/monster/Skeletons/dreadbeast.xml index 6332052..065a111 100644 --- a/data/monster/Skeletons/dreadbeast.xml +++ b/data/monster/Skeletons/dreadbeast.xml @@ -38,7 +38,4 @@ - - - diff --git a/data/monster/Skeletons/lost soul.xml b/data/monster/Skeletons/lost soul.xml index 7ba77ad..d95ed7a 100644 --- a/data/monster/Skeletons/lost soul.xml +++ b/data/monster/Skeletons/lost soul.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -52,21 +52,32 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Skeletons/skeleton warrior.xml b/data/monster/Skeletons/skeleton warrior.xml index a00d239..828c43b 100644 --- a/data/monster/Skeletons/skeleton warrior.xml +++ b/data/monster/Skeletons/skeleton warrior.xml @@ -5,20 +5,20 @@ - - - - - - - - - - - + + + + + + + + + + + - + @@ -31,15 +31,13 @@ - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Skeletons/skeleton.xml b/data/monster/Skeletons/skeleton.xml index 9cd64c6..b0fa191 100644 --- a/data/monster/Skeletons/skeleton.xml +++ b/data/monster/Skeletons/skeleton.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -27,17 +27,14 @@ - - - - - - - - - - - - + + + + + + + + + diff --git a/data/monster/Skeletons/undead gladiator.xml b/data/monster/Skeletons/undead gladiator.xml index a5dbd5c..e95a075 100644 --- a/data/monster/Skeletons/undead gladiator.xml +++ b/data/monster/Skeletons/undead gladiator.xml @@ -18,7 +18,7 @@ - + @@ -46,30 +46,27 @@ - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + diff --git a/data/monster/Sorcerers/dark apprentice.xml b/data/monster/Sorcerers/dark apprentice.xml index 12fb1d1..d904757 100644 --- a/data/monster/Sorcerers/dark apprentice.xml +++ b/data/monster/Sorcerers/dark apprentice.xml @@ -18,7 +18,7 @@ - + @@ -47,10 +47,15 @@ - - - - + + + + + + + + + diff --git a/data/monster/Sorcerers/dark magician.xml b/data/monster/Sorcerers/dark magician.xml index 34343cf..2541997 100644 --- a/data/monster/Sorcerers/dark magician.xml +++ b/data/monster/Sorcerers/dark magician.xml @@ -18,7 +18,7 @@ - + @@ -51,10 +51,13 @@ - - - - - + + + + + + + + diff --git a/data/monster/Sorcerers/ice witch.xml b/data/monster/Sorcerers/ice witch.xml index 1b276da..10294ca 100644 --- a/data/monster/Sorcerers/ice witch.xml +++ b/data/monster/Sorcerers/ice witch.xml @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ - + @@ -56,17 +56,17 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/Sorcerers/infernalist.xml b/data/monster/Sorcerers/infernalist.xml index f35aa20..1e48d86 100644 --- a/data/monster/Sorcerers/infernalist.xml +++ b/data/monster/Sorcerers/infernalist.xml @@ -18,13 +18,13 @@ - + - + @@ -65,26 +65,23 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Sorcerers/mad mage.xml b/data/monster/Sorcerers/mad mage.xml new file mode 100644 index 0000000..2c56d96 --- /dev/null +++ b/data/monster/Sorcerers/mad mage.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + item id="7895" chance="800"/> + + diff --git a/data/monster/Sorcerers/mad scientist.xml b/data/monster/Sorcerers/mad scientist.xml index 7ae0729..189f989 100644 --- a/data/monster/Sorcerers/mad scientist.xml +++ b/data/monster/Sorcerers/mad scientist.xml @@ -18,14 +18,14 @@ - + - - + + @@ -51,21 +51,18 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Sorcerers/warlock.xml b/data/monster/Sorcerers/warlock.xml index 0c50ad1..52879f9 100644 --- a/data/monster/Sorcerers/warlock.xml +++ b/data/monster/Sorcerers/warlock.xml @@ -18,7 +18,7 @@ - + @@ -69,30 +69,30 @@ - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/data/monster/Sorcerers/witch.xml b/data/monster/Sorcerers/witch.xml index 4964454..1fb1730 100644 --- a/data/monster/Sorcerers/witch.xml +++ b/data/monster/Sorcerers/witch.xml @@ -18,7 +18,7 @@ - + @@ -41,22 +41,19 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Tortoises/thornback tortoise.xml b/data/monster/Tortoises/thornback tortoise.xml index 05bd5a0..38e5826 100644 --- a/data/monster/Tortoises/thornback tortoise.xml +++ b/data/monster/Tortoises/thornback tortoise.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -28,13 +28,13 @@ - - - - - - - - + + + + + + + + - + \ No newline at end of file diff --git a/data/monster/Tortoises/tortoise.xml b/data/monster/Tortoises/tortoise.xml index 452c612..82a7144 100644 --- a/data/monster/Tortoises/tortoise.xml +++ b/data/monster/Tortoises/tortoise.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -28,14 +28,13 @@ - - - - - - - - - + + + + + + + + diff --git a/data/monster/Traps/deathslicer.xml b/data/monster/Traps/deathslicer.xml index 1abfcc2..27fcc97 100644 --- a/data/monster/Traps/deathslicer.xml +++ b/data/monster/Traps/deathslicer.xml @@ -18,7 +18,7 @@ - + @@ -39,4 +39,4 @@ - + \ No newline at end of file diff --git a/data/monster/Traps/eye of the seven.xml b/data/monster/Traps/eye of the seven.xml index fbf662f..50f82d6 100644 --- a/data/monster/Traps/eye of the seven.xml +++ b/data/monster/Traps/eye of the seven.xml @@ -18,6 +18,7 @@ + @@ -34,6 +35,5 @@ - - + \ No newline at end of file diff --git a/data/monster/Traps/flamethrower.xml b/data/monster/Traps/flamethrower.xml index dba6041..51e09fc 100644 --- a/data/monster/Traps/flamethrower.xml +++ b/data/monster/Traps/flamethrower.xml @@ -19,8 +19,7 @@ - - + @@ -41,7 +40,4 @@ - - - - + \ No newline at end of file diff --git a/data/monster/Traps/lavahole.xml b/data/monster/Traps/lavahole.xml new file mode 100644 index 0000000..f222fd3 --- /dev/null +++ b/data/monster/Traps/lavahole.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Traps/magicthrower.xml b/data/monster/Traps/magicthrower.xml index 5017606..e2cc0de 100644 --- a/data/monster/Traps/magicthrower.xml +++ b/data/monster/Traps/magicthrower.xml @@ -19,8 +19,7 @@ - - + @@ -41,4 +40,4 @@ - + \ No newline at end of file diff --git a/data/monster/Traps/plaguethrower.xml b/data/monster/Traps/plaguethrower.xml index d1c8c0d..107bcc3 100644 --- a/data/monster/Traps/plaguethrower.xml +++ b/data/monster/Traps/plaguethrower.xml @@ -20,7 +20,7 @@ - + @@ -41,7 +41,4 @@ - - - - + \ No newline at end of file diff --git a/data/monster/Traps/poisonthrower.xml b/data/monster/Traps/poisonthrower.xml deleted file mode 100644 index 05e304e..0000000 --- a/data/monster/Traps/poisonthrower.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/data/monster/Traps/shredderthrower.xml b/data/monster/Traps/shredderthrower.xml index fa04f0e..ac6e72f 100644 --- a/data/monster/Traps/shredderthrower.xml +++ b/data/monster/Traps/shredderthrower.xml @@ -40,4 +40,4 @@ - + \ No newline at end of file diff --git a/data/monster/Trolls/frost troll.xml b/data/monster/Trolls/frost troll.xml index 493f39d..9bcf3ea 100644 --- a/data/monster/Trolls/frost troll.xml +++ b/data/monster/Trolls/frost troll.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -33,17 +33,13 @@ - - - - - - - - - - - - + + + + + + + + diff --git a/data/monster/Trolls/island troll.xml b/data/monster/Trolls/island troll.xml index 970e7a9..6c78835 100644 --- a/data/monster/Trolls/island troll.xml +++ b/data/monster/Trolls/island troll.xml @@ -18,7 +18,7 @@ - + @@ -29,22 +29,18 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Trolls/swamp troll.xml b/data/monster/Trolls/swamp troll.xml index 68f8c27..a5c0266 100644 --- a/data/monster/Trolls/swamp troll.xml +++ b/data/monster/Trolls/swamp troll.xml @@ -18,7 +18,7 @@ - + @@ -31,15 +31,12 @@ - - - - - - - - - - + + + + + + + diff --git a/data/monster/Trolls/troll champion.xml b/data/monster/Trolls/troll champion.xml index 2484252..31f1b3b 100644 --- a/data/monster/Trolls/troll champion.xml +++ b/data/monster/Trolls/troll champion.xml @@ -17,7 +17,7 @@ - + @@ -33,14 +33,16 @@ - - - - - - - + + + + + - + + + + + \ No newline at end of file diff --git a/data/monster/Trolls/troll.xml b/data/monster/Trolls/troll.xml index 7323f9a..c5c65c8 100644 --- a/data/monster/Trolls/troll.xml +++ b/data/monster/Trolls/troll.xml @@ -18,7 +18,7 @@ - + @@ -35,21 +35,15 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/banshee.xml b/data/monster/Undead Humanoids/banshee.xml index aad5b33..61675b2 100644 --- a/data/monster/Undead Humanoids/banshee.xml +++ b/data/monster/Undead Humanoids/banshee.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -52,17 +52,27 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/blightwalker.xml b/data/monster/Undead Humanoids/blightwalker.xml index 455aebd..37d2ced 100644 --- a/data/monster/Undead Humanoids/blightwalker.xml +++ b/data/monster/Undead Humanoids/blightwalker.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -48,21 +48,25 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/crypt shambler.xml b/data/monster/Undead Humanoids/crypt shambler.xml index fb3e0af..cae1e52 100644 --- a/data/monster/Undead Humanoids/crypt shambler.xml +++ b/data/monster/Undead Humanoids/crypt shambler.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -38,17 +38,16 @@ + + + + + + + + + + - - - - - - - - - - - diff --git a/data/monster/Undead Humanoids/ghoul.xml b/data/monster/Undead Humanoids/ghoul.xml index ad72550..c43d15a 100644 --- a/data/monster/Undead Humanoids/ghoul.xml +++ b/data/monster/Undead Humanoids/ghoul.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -39,21 +39,15 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/grim reaper.xml b/data/monster/Undead Humanoids/grim reaper.xml index d5ec939..643e2dd 100644 --- a/data/monster/Undead Humanoids/grim reaper.xml +++ b/data/monster/Undead Humanoids/grim reaper.xml @@ -1,5 +1,5 @@ - + @@ -18,7 +18,7 @@ - + @@ -57,24 +57,23 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/lich.xml b/data/monster/Undead Humanoids/lich.xml index d08c3bc..74be8fc 100644 --- a/data/monster/Undead Humanoids/lich.xml +++ b/data/monster/Undead Humanoids/lich.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -61,22 +61,20 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/mummy.xml b/data/monster/Undead Humanoids/mummy.xml index 7a7dc24..dde2bdc 100644 --- a/data/monster/Undead Humanoids/mummy.xml +++ b/data/monster/Undead Humanoids/mummy.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -45,21 +45,18 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/souleater.xml b/data/monster/Undead Humanoids/souleater.xml new file mode 100644 index 0000000..d5a4b5a --- /dev/null +++ b/data/monster/Undead Humanoids/souleater.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/tomb servant.xml b/data/monster/Undead Humanoids/tomb servant.xml new file mode 100644 index 0000000..41f474f --- /dev/null +++ b/data/monster/Undead Humanoids/tomb servant.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/monster/Undead Humanoids/undead mine worker.xml b/data/monster/Undead Humanoids/undead mine worker.xml new file mode 100644 index 0000000..c2871f7 --- /dev/null +++ b/data/monster/Undead Humanoids/undead mine worker.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/undead prospector.xml b/data/monster/Undead Humanoids/undead prospector.xml new file mode 100644 index 0000000..4a9a919 --- /dev/null +++ b/data/monster/Undead Humanoids/undead prospector.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/vampire bride.xml b/data/monster/Undead Humanoids/vampire bride.xml index 5e5799e..942bfe5 100644 --- a/data/monster/Undead Humanoids/vampire bride.xml +++ b/data/monster/Undead Humanoids/vampire bride.xml @@ -18,7 +18,7 @@ - + @@ -49,19 +49,18 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/vampire.xml b/data/monster/Undead Humanoids/vampire.xml index 9c3c31a..d312696 100644 --- a/data/monster/Undead Humanoids/vampire.xml +++ b/data/monster/Undead Humanoids/vampire.xml @@ -1,4 +1,4 @@ - + @@ -18,7 +18,7 @@ - + @@ -56,24 +56,19 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/data/monster/Undead Humanoids/zombie.xml b/data/monster/Undead Humanoids/zombie.xml index 4cd9a8a..1df432c 100644 --- a/data/monster/Undead Humanoids/zombie.xml +++ b/data/monster/Undead Humanoids/zombie.xml @@ -18,7 +18,7 @@ - + @@ -40,18 +40,17 @@ - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/data/monster/monsters.xml b/data/monster/monsters.xml index 4cb9cf5..e834e9d 100644 --- a/data/monster/monsters.xml +++ b/data/monster/monsters.xml @@ -5,17 +5,11 @@ with getting real monster attributes, loot etc [ MISSING MONSTERS (not all listed there) - Ape Bosses - Hairman The Huge - - Barbarians Bosses - Barbaria - Bio-Elemental Bosses Diseased Bill Diseased Dan Diseased Fred - The Plasmother + Slime Puddle Canine Bosses The Big Bad One @@ -23,14 +17,14 @@ Crustaceans Blood Crab (Underwater) - Dragon Bosses - Pythius the Rotten - Dwarves Dwarf Henchman Dwarf Miner Dwarf Dispenser + Frogs + Deathspawn + Giant Bosses Freegoiz Stonecracker @@ -41,9 +35,6 @@ Ghostly Apparition Tormented Ghost - Orc Bosses - Warlord Ruzad - Quara Bosses Inky Sharptooth @@ -57,13 +48,22 @@ Deathspawn Hell Hole Kitty - Lavahole Magic Pillar Mechanical Fighter Pillar Undead Humanoids Undead Jester + Spectral Scum + + Rhinoceros + Stampor + + Misc + Cake Golem + + Rat + Ghost Rat ] You can send your work here: http://otland.net/project.php?projectid=2 @@ -81,13 +81,15 @@ - - - - - - - + + + + + + + + + @@ -136,12 +138,15 @@ + - - - - - + + + + + + + @@ -152,10 +157,10 @@ + - @@ -168,14 +173,20 @@ + + + + + + @@ -187,9 +198,8 @@ - + - @@ -210,23 +220,46 @@ + - + + + + + + + + + + + + + + + + + + + + + + - - + + + @@ -236,6 +269,7 @@ + @@ -255,13 +289,13 @@ + - @@ -276,7 +310,10 @@ + + + @@ -309,6 +346,7 @@ + @@ -323,10 +361,11 @@ - + + @@ -334,7 +373,6 @@ - @@ -354,6 +392,10 @@ + + + + @@ -368,17 +410,35 @@ - + + + + + + + + + + + + + + + + + + + @@ -393,11 +453,16 @@ - + + + + + + @@ -422,6 +487,7 @@ + @@ -439,6 +505,7 @@ + @@ -454,7 +521,9 @@ + + @@ -483,6 +552,7 @@ + @@ -495,6 +565,7 @@ + @@ -511,10 +582,8 @@ - - @@ -523,9 +592,9 @@ - + @@ -535,9 +604,9 @@ + - @@ -558,4 +627,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/data/movements/lib/movements.lua b/data/movements/lib/movements.lua deleted file mode 100644 index 585eb19..0000000 --- a/data/movements/lib/movements.lua +++ /dev/null @@ -1 +0,0 @@ --- Nothing -- diff --git a/data/movements/movements.xml b/data/movements/movements.xml index 138a300..feaaf4a 100644 --- a/data/movements/movements.xml +++ b/data/movements/movements.xml @@ -6,7 +6,9 @@ + + @@ -23,8 +25,8 @@ - - + + @@ -42,6 +44,8 @@ + + @@ -110,10 +114,20 @@ - - - - + + + + + + + + + + + + + + @@ -180,15 +194,26 @@ - - - - + + + + + + + + + + + + + + - + + @@ -203,6 +228,11 @@ + + + + + @@ -254,20 +284,25 @@ - - - - + + + + + + + + + @@ -275,6 +310,7 @@ + @@ -282,6 +318,7 @@ + @@ -289,6 +326,7 @@ + @@ -296,117 +334,138 @@ + + - + + + + + + + + + + + + + + + + + + + + + - - + + + - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - + + + + + + + + + + + + + + + + @@ -414,16 +473,23 @@ + + + - - + + + + + + @@ -431,6 +497,7 @@ + @@ -438,6 +505,7 @@ + @@ -445,6 +513,7 @@ + @@ -452,6 +521,7 @@ + @@ -459,6 +529,7 @@ + @@ -466,6 +537,7 @@ + @@ -473,29 +545,40 @@ - + + - - + + + - - + + + - + + + + + + + + + @@ -503,6 +586,7 @@ + @@ -510,6 +594,7 @@ + @@ -517,6 +602,7 @@ + @@ -524,6 +610,7 @@ + @@ -531,18 +618,22 @@ + + + + @@ -550,6 +641,7 @@ + @@ -557,6 +649,7 @@ + @@ -564,6 +657,7 @@ + @@ -571,6 +665,7 @@ + @@ -578,33 +673,40 @@ + + + + + + + @@ -612,6 +714,7 @@ + @@ -619,11 +722,13 @@ + + @@ -631,6 +736,7 @@ + @@ -638,6 +744,7 @@ + @@ -645,6 +752,7 @@ + @@ -652,61 +760,73 @@ + + + + + + + + + + + + @@ -714,34 +834,67 @@ + - + + - - + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + - + + @@ -749,6 +902,7 @@ + @@ -756,6 +910,7 @@ + @@ -763,8 +918,10 @@ + + @@ -772,6 +929,7 @@ + @@ -779,6 +937,7 @@ + @@ -786,6 +945,7 @@ + @@ -793,20 +953,27 @@ + - - + + + + + + + + @@ -814,6 +981,7 @@ + @@ -821,6 +989,7 @@ + @@ -828,6 +997,7 @@ + @@ -835,6 +1005,7 @@ + @@ -842,6 +1013,7 @@ + @@ -849,8 +1021,24 @@ + + + + + + + + + + + + + + + + diff --git a/data/movements/scripts/closingdoor.lua b/data/movements/scripts/closingdoor.lua index f0d3356..d5c758b 100644 --- a/data/movements/scripts/closingdoor.lua +++ b/data/movements/scripts/closingdoor.lua @@ -1,27 +1,33 @@ -function onStepOut(cid, item, position, fromPosition) - local newPosition = {x = position.x, y = position.y, z = position.z} - if(isInArray(verticalOpenDoors, item.itemid)) then - newPosition.x = newPosition.x + 1 - else +function onStepOut(cid, item, position, lastPosition) + if(getTileInfo(position).creatures > 0) then + return true + end + + local newPosition = {x = position.x + 1, y = position.y, z = position.z} + local query = doTileQueryAdd(cid, newPosition) + if(query == RETURNVALUE_NOTENOUGHROOM) then + newPosition.x = newPosition.x - 1 newPosition.y = newPosition.y + 1 + query = doTileQueryAdd(cid, newPosition) -- repeat until found end - doRelocate(position, newPosition) - local tmpPos = {x = position.x, y = position.y, z = position.z, stackpos = -1} - local tileCount = getTileThingByPos(tmpPos) + if(query == RETURNVALUE_NOERROR and query == RETURNVALUE_NOTENOUGHROOM) then + doRelocate(position, newPosition) + end - local i = 1 - local tmpItem = {uid = 1} - while(tmpItem.uid ~= 0 and i < tileCount) do - tmpPos.stackpos = i - tmpItem = getTileThingByPos(tmpPos) - if(tmpItem.uid ~= item.uid and tmpItem.uid ~= 0 and isMoveable(tmpItem.uid)) then - doRemoveItem(tmpItem.uid) + position.stackpos = -1 + local i, tileItem, tileCount = 1, {uid = 1}, getTileThingByPos(position) + while(tileItem.uid ~= 0 and i < tileCount) do + position.stackpos = i + tileItem = getTileThingByPos(position) + if(tileItem.uid ~= 0 and tileItem.uid ~= item.uid and isMovable(tileItem.uid)) then + doRemoveItem(tileItem.uid) else i = i + 1 end end - doTransformItem(item.uid, item.itemid - 1) + local itemInfo = getItemInfo(item.itemid) + doTransformItem(item.uid, itemInfo.transformUseTo) return true end diff --git a/data/movements/scripts/drown.lua b/data/movements/scripts/drown.lua index a42be2e..f44c407 100644 --- a/data/movements/scripts/drown.lua +++ b/data/movements/scripts/drown.lua @@ -1,16 +1,36 @@ -local condition = createConditionObject(CONDITION_DROWN) -setConditionParam(condition, CONDITION_PARAM_PERIODICDAMAGE, -20) -setConditionParam(condition, CONDITION_PARAM_TICKS, -1) -setConditionParam(condition, CONDITION_PARAM_TICKINTERVAL, 2000) +local conditionDrown = createConditionObject(CONDITION_DROWN) +setConditionParam(conditionDrown, CONDITION_PARAM_PERIODICDAMAGE, -20) +setConditionParam(conditionDrown, CONDITION_PARAM_TICKS, -1) +setConditionParam(conditionDrown, CONDITION_PARAM_TICKINTERVAL, 2000) function onStepIn(cid, item, position, fromPosition) - if(isPlayer(cid)) then - doAddCondition(cid, condition) + if(math.random(1, 10) == 1) then + doSendMagicEffect(position, CONST_ME_BUBBLES) end + + if(not isPlayer(cid)) then + return false + end + + doAddCondition(cid, conditionDrown) return true end -function onStepOut(cid, item, position, fromPosition) +function onStepOut(cid, item, position, lastPosition, fromPosition, toPosition, actor) + if(not isPlayer(cid)) then + return false + end + + local slotItem = getPlayerSlotItem(cid, CONST_SLOT_HEAD) + if(slotItem.uid ~= 0 and slotItem.itemid == 12541) then + toPosition.stackpos = 0 + local ground = getTileThingByPos(toPosition) + if(ground.uid ~= 0 and not isInArray(underWater, ground.itemid)) then + local itemInfo = getItemInfo(slotItem.itemid) + doTransformItem(slotItem.uid, 5461) + end + end + doRemoveCondition(cid, CONDITION_DROWN) return true end diff --git a/data/movements/scripts/hotd.lua b/data/movements/scripts/hotd.lua new file mode 100644 index 0000000..07a0068 --- /dev/null +++ b/data/movements/scripts/hotd.lua @@ -0,0 +1,17 @@ +function onEquip(cid, item, slot, boolean) + --[[ the point of this is to call event ONLY when the duration was started, + so it means the helmet was 'filled up' with the coconut shrimp bake, testing around ]]-- + if(boolean) then + return true + end + + if(isUnderWater(cid)) then + if(item.itemid ~= 5461 or not getItemAttribute(item.uid, "duration")) then + callFunction(cid, item.uid, slot, false) + else + doTransformItem(item.uid, 12541) + end + end + + return true +end diff --git a/data/movements/scripts/junglemaw.lua b/data/movements/scripts/junglemaw.lua new file mode 100644 index 0000000..0dc55ed --- /dev/null +++ b/data/movements/scripts/junglemaw.lua @@ -0,0 +1,12 @@ +function onStepIn(cid, item) + doTargetCombatHealth(0, cid, COMBAT_POISONDAMAGE, -30, -30, CONST_ME_POFF) + doTransformItem(item.uid, item.itemid + 1) + doDecayItem(item.uid) + return true +end + +function onAddItem(moveItem, tileItem, position) + doTransformItem(tileItem.uid, tileItem.itemid + 1) + doSendMagicEffect(position, CONST_ME_POFF) + return true +end diff --git a/data/movements/scripts/swimming.lua b/data/movements/scripts/swimming.lua index 2d5c4cc..f7811f5 100644 --- a/data/movements/scripts/swimming.lua +++ b/data/movements/scripts/swimming.lua @@ -1,54 +1,30 @@ local outfit = {lookType = 267, lookHead = 0, lookBody = 0, lookLegs = 0, lookFeet = 0, lookTypeEx = 0, lookAddons = 0} -local BORDERS = { - [7943] = {x = 0, y = -2, back = SOUTH}, - [7944] = {x = -2, y = 0, back = EAST}, - [7945] = {x = 0, y = 2, back = NORTH}, - [7946] = {x = 2, y = 0, back = WEST}, - [7947] = {x = 2, y = 1, back = WEST}, - [7948] = {x = -2, y = 1, back = NORTH}, - [7949] = {x = 2, y = -1, back = WEST}, - [7950] = {x = -2, y = -1, back = EAST}, - [7951] = {x = 2, y = 2, back = WEST}, - [7952] = {x = -2, y = 2, back = NORTH}, - [7953] = {x = 2, y = -2, back = WEST}, - [7954] = {x = -2, y = -2, back = SOUTH} -} +function onStepIn(cid, item, position, lastPosition, fromPosition, toPosition, actor) + if(hasCondition(cid, CONDITION_OUTFIT, 0, CONDITIONID_COMBAT) and getCreatureOutfit(cid).lookType == outfit.lookType) then + doRemoveCondition(cid, CONDITION_OUTFIT) + if(not isPlayerGhost(cid)) then + doSendMagicEffect(position, CONST_ME_POFF) + end + end -BORDERS[4828] = BORDERS[7943] -BORDERS[4829] = BORDERS[7946] -BORDERS[4830] = BORDERS[7945] -BORDERS[4831] = BORDERS[7944] + return true +end -function onStepIn(cid, item, position, lastPosition, fromPosition, toPosition, actor) +function onStepOut(cid, item, position, lastPosition, fromPosition, toPosition, actor) if(not isPlayer(cid)) then return true end - local border = BORDERS[item.itemid] - if(not border) then - return false - end - - local pos, newPos = getCreaturePosition(cid), {} - newPos = pos - newPos.x = pos.x + border.x - newPos.y = pos.y + border.y - - if(hasCondition(cid, CONDITION_OUTFIT) and getCreatureOutfit(cid).lookType == outfit.lookType) then - doMoveCreature(cid, border.back) - doRemoveCondition(cid, CONDITION_OUTFIT) - else - if(doTileQueryAdd(cid, pos, 4) ~= RETURNVALUE_NOERROR) then + local tmp = getTileInfo(toPosition) + if(tmp.trashHolder) then + if(doTileQueryAdd(cid, toPosition, 4) ~= RETURNVALUE_NOERROR) then return false end - local tmp = getCreaturePosition(cid) - doTeleportThing(cid, newPos) - if(not isPlayerGhost(cid)) then - doSendMagicEffect(tmp, CONST_ME_POFF) - doSendMagicEffect(newPos, CONST_ME_WATERSPLASH) + doSendMagicEffect(fromPosition, CONST_ME_POFF) + doSendMagicEffect(toPosition, CONST_ME_WATERSPLASH) end doRemoveConditions(cid, true) diff --git a/data/movements/scripts/tiles.lua b/data/movements/scripts/tiles.lua index 22243ac..1183cdc 100644 --- a/data/movements/scripts/tiles.lua +++ b/data/movements/scripts/tiles.lua @@ -1,11 +1,9 @@ local config = { + increasing = {[416] = 417, [426] = 425, [446] = 447, [3216] = 3217, [3202] = 3215, [11062] = 11063}, + decreasing = {[417] = 416, [425] = 426, [447] = 446, [3217] = 3216, [3215] = 3202, [11063] = 11062}, maxLevel = getConfigInfo('maximumDoorLevel') } -local increasingItems = {[416] = 417, [426] = 425, [446] = 447, [3216] = 3217, [3202] = 3215, [11059] = 11060} -local decreasingItems = {[417] = 416, [425] = 426, [447] = 446, [3217] = 3216, [3215] = 3202, [11060] = 11059} -local depots = {2589, 2590, 2591, 2592} - local checkCreature = {isPlayer, isMonster, isNpc} local function pushBack(cid, position, fromPosition, displayMessage) doTeleportThing(cid, fromPosition, false) @@ -16,12 +14,12 @@ local function pushBack(cid, position, fromPosition, displayMessage) end function onStepIn(cid, item, position, fromPosition) - if(not increasingItems[item.itemid]) then + if(not config.increasing[item.itemid]) then return false end if(not isPlayerGhost(cid)) then - doTransformItem(item.uid, increasingItems[item.itemid]) + doTransformItem(item.uid, config.increasing[item.itemid]) end if(item.actionid >= 194 and item.actionid <= 196) then @@ -80,15 +78,15 @@ function onStepIn(cid, item, position, fromPosition) local vocation = item.actionid - 100 if(vocation >= 0 and vocation < 50) then - local playerVocationInfo = getVocationInfo(getPlayerVocation(cid)) - if(playerVocationInfo.id ~= vocation and playerVocationInfo.fromVocation ~= vocation) then + local playerVocation = getVocationInfo(getPlayerVocation(cid)) + if(playerVocation.id ~= vocation and playerVocation.fromVocation ~= vocation) then pushBack(cid, position, fromPosition, true) end return true end - if(item.actionid >= 1000 and item.actionid <= config.maxLevel) then + if(item.actionid >= 1000 and item.actionid - 1000 <= config.maxLevel) then if(getPlayerLevel(cid) < item.actionid - 1000) then pushBack(cid, position, fromPosition, true) end @@ -96,45 +94,30 @@ function onStepIn(cid, item, position, fromPosition) return true end - if(item.actionid ~= 0 and getPlayerStorageValue(cid, item.actionid) <= 0) then + if(item.actionid ~= 0 and getCreatureStorage(cid, item.actionid) <= 0) then pushBack(cid, position, fromPosition, true) return true end if(getTileInfo(position).protection) then - local depotPos, depot = getCreatureLookPosition(cid), {} - depotPos.stackpos = STACKPOS_GROUND - while(true) do - if(not getTileInfo(depotPos).depot) then - break - end - - depotPos.stackpos = depotPos.stackpos + 1 - depot = getThingFromPos(depotPos) - if(depot.uid == 0) then - break - end - - if(isInArray(depots, depot.itemid)) then - local depotItems = getPlayerDepotItems(cid, getDepotId(depot.uid)) - doPlayerSendTextMessage(cid, MESSAGE_STATUS_DEFAULT, "Your depot contains " .. depotItems .. " item" .. (depotItems > 1 and "s" or "") .. ".") - break - end + local depotItem = getTileItemByType(getCreatureLookPosition(cid), ITEM_TYPE_DEPOT) + if(depotItem.itemid ~= 0) then + local depotItems = getPlayerDepotItems(cid, getDepotId(depotItem.uid)) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_DEFAULT, "Your depot contains " .. depotItems .. " item" .. (depotItems > 1 and "s" or "") .. ".") + return true end - - return true end return false end function onStepOut(cid, item, position, fromPosition) - if(not decreasingItems[item.itemid]) then + if(not config.decreasing[item.itemid]) then return false end if(not isPlayerGhost(cid)) then - doTransformItem(item.uid, decreasingItems[item.itemid]) + doTransformItem(item.uid, config.decreasing[item.itemid]) return true end diff --git a/data/movements/scripts/walkback.lua b/data/movements/scripts/walkback.lua index b8418f9..6c64ad5 100644 --- a/data/movements/scripts/walkback.lua +++ b/data/movements/scripts/walkback.lua @@ -1,23 +1,18 @@ local SPECIAL_QUESTS = {2001} function onStepIn(cid, item, position, lastPosition, fromPosition, toPosition, actor) - if(not isPlayer(cid)) then + local container = isContainer(item.uid) + if((container and not isInArray(SPECIAL_QUESTS, item.actionid) and + item.uid > 65535) or (fromPosition.x ~= 0 and getTileInfo(fromPosition).floorChange[9]) + or (not container and getTileInfo(position).creatures <= 1)) then return true end - if(isContainer(item.uid)) then - if(not isInArray(SPECIAL_QUESTS, item.actionid) and item.uid > 65535) then - return true - end - elseif(getTileInfo(position).creatures <= 1) then - return true - end - - if(fromPosition.x == 0) then -- player just logged in - fromPosition = getTownTemplePosition(getPlayerTown(cid)) - doSendMagicEffect(fromPosition, CONST_ME_TELEPORT) + if(lastPosition.x == 0) then -- player just logged in + lastPosition = getTownTemplePosition(getPlayerTown(cid)) + doSendMagicEffect(lastPosition, CONST_ME_TELEPORT) end - doTeleportThing(cid, fromPosition, true) + doTeleportThing(cid, lastPosition, true) return true end diff --git a/data/movements/scripts/yellowpillow.lua b/data/movements/scripts/yellowpillow.lua new file mode 100644 index 0000000..0ef1047 --- /dev/null +++ b/data/movements/scripts/yellowpillow.lua @@ -0,0 +1,5 @@ +function onStepIn(cid, item, position, lastPosition, fromPosition, toPosition, actor) + doCreatureSay(cid, "Faaart!", TALKTYPE_MONSTER) + doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF) + return true +end diff --git a/data/npc/Riona.xml b/data/npc/Riona.xml index 0bed7f8..ed6df9a 100644 --- a/data/npc/Riona.xml +++ b/data/npc/Riona.xml @@ -6,4 +6,4 @@ - \ No newline at end of file + diff --git a/data/npc/Varkhal.xml b/data/npc/Varkhal.xml index 4780a55..fa199fc 100644 --- a/data/npc/Varkhal.xml +++ b/data/npc/Varkhal.xml @@ -1,5 +1,10 @@ - + - \ No newline at end of file + + + + + + diff --git a/data/npc/lib/AI/basic.lua b/data/npc/lib/AI/basic.lua new file mode 100644 index 0000000..362d0bc --- /dev/null +++ b/data/npc/lib/AI/basic.lua @@ -0,0 +1,416 @@ +if not AI then + return false +end + +--[[ +@classname + AI.BasicNpc +@description + This class provides innate npc functionality, like thinking, hearing and viewing. Most of the time + what you want to use is an AI.SocialNpc. +]] +local BasicNpc = { + Events = { + --[[@event + The raw onCreatureAppear event, you should not use this often. + If a callback handles/cancels the event, the npc will act as if this creature + is _not_ on the screen, if the creature later moves into VisionRadius, + the npc wont fire the OnVisualizeCreature event, since is _not_ tracking + it + @params + cid creature, the creature + ]] + OnCreatureAppear = AI.UniqueId(), + + --[[@event + The raw onCreatureDisappear event, you should not use this often. + @params + cid creature, the creature about to disappear + ]] + OnCreatureDisappear = AI.UniqueId(), + + --[[@event + This event fires when a creature suddenly moves into this npc VisionRadius, + If you handle/cancel this event, the npc wont track this creature (see + onCreatureAppear) and the event OnMissCreature will _not_ fire when this creature leaves + our vision radius. + @params + cid creature, the creature + pos position, the position where we first saw this creature + ]] + OnVisualizeCreature = AI.UniqueId(), + + --[[@event + This event fires when a previously visualized creature suddenly disspears + from our VisionRadius + @params + cid creature, the creature + reason AI.BasicNpc.CreatureMissReason, the missing reason + ]] + OnMissCreature = AI.UniqueId(), + + --[[@event + The raw onCreatureSay, you should not be using this often. + If you handle/cancel this event, the npc won't be able to hear the creature + speech(i.e no OnHear firing) + @params + cid creature, the creature + type MessageType, the message type + msg string, the message + ]] + OnCreatureSay = AI.UniqueId(), + + --[[@event + This event fires when a creature on this npc hear radius says + something, note that this event will still fire even if the creature + is outside this npc vision radius, use the creatureVisible event param to + check for this + @params + cid creature, the creature + type MessageType, the message type + msg string, the message + creatureVisible boolean, whether the creature is in our vision radius or not + ]] + OnHear = AI.UniqueId(), + + --[[@event + The raw onThink event. + ]] + OnThink = AI.UniqueId(), + + --[[@event + This event is triggered once, when AI.BasicNpc:Register is called + @params + scriptEnv Table, the npc script enviroment (you can define/get functions/variables inside it) + ]] + OnRegister = AI.UniqueId() + }, + + --[[@enum + Values used when triggering OnMissCreature, to distinguish between different causes of missing. + @values + Removed, When the creature is removed from the game + WalkAway, When the creature walks away from our vision radius + ]] + CreatureMissReason = { + Removed = AI.UniqueId(), + WalkAway = AI.UniqueId() + }, + + --[[@property number, The radius (from the npc position) where this npc can see other creatures]] + VisionRadius = 7, + + --[[@property boolean, If enabled this npc will check its enviroment for close players + and fire OnVisualize events, its a bit costly, so use it wisely. + If not enabled, the only kind OnVisualize event this npc will fire + is when a creature logs in near him + ]] + RealTimeVisualization = true, + + --[[@property number, The radius (from the npc position) where this npc can hear other creatures]] + HearRadius = 5, + + --[[@property function, This function will be called before performing any action on a creature, + if it returns false, this npc will ignore this creature + ]] + CreatureFilter = function(npc, cid) + return isPlayer(cid) and not isPlayerGhost(cid) + end, + + --[[@method + Creates a new AI.BasicNpc object + ]] + New = function() end, + + --[[@method + Adds a delayed function call to this npc, the action will be executed in + (current time + delay) miliseconds, passing this npc as the first parameter + and all the extra paramteres passed to AddDelayedAction in the same order. + The call looks like this: + [code]fn(self, param1, ... paramN)[/code] + @params + fn function, the function to be executed + delay number, the number of miliseconds to wait before executing this function + ... mixed, a variable list of parameters to pass as extra arguments to the function + ]] + AddDelayedAction = function(fn, delay, ...) end, + + --[[@method + Adds a variable piece of data to this npc global storage, using id as the identifier. + The data you set using this function will be always available until the npc is + deleted/reloaded + @params + id string/number, the identifier of this piece of data + data mixed, the data to store + ]] + SetGlobalData = function(id, data) end, + + --[[@method + Returns the piece of data associated to this npc with the given identifier. + @params + id string/number, the identifier used in the registration of the wanted piece of data + @returns + mixed, the requested piece of data or nil if there's no data associated with the given id + ]] + GetGlobalData = function(id) end, + + --[[@method + Manual visualization for when RealTimeVisualization is off, + if the given creature is out of this npc vision radius the + creature will not be visualized. + This is NOT a focus function, visualization only means that a npc + is aware of a creature existance. You don't need to call this function + explicitly + @params + cid creature, the creature to visualize + ]] + VisualizeCreature = function(cid) end, + + --[[@method + Manual function for unvisualazing creatures, complement of AI.BasicNpc.VisualizeCreature + @params + cid creature, the creature to unvisualize + ]] + MissCreature = function(cid) end, + + --[[@method + Returns true if the given thing is in this npc VisionRadius + @params + thing thing, the thing + @returns + boolean, whether the given thing is in this npc VisionRadius or not + ]] + IsInVisionRange = function(thing) end, + + --[[@method + Returns true if the given creature is in this npc HearRadius + @params + cid creature, the creature + @returns + boolean, whether the given creature is in this npc HearRadius or not + ]] + IsInHearRange = function(cid) end, + + --[[@method + Returns true if the given position is in the boundaries of the given range + respect to this npc position + @params + position position, the position + range number, the radius comming from this npc position + @returns + boolean, whether the given position is the boundaries of the given range + ]] + IsInRange = function(position, range) end, + + --[[@method + Returns the npc name + @returns + string, the npc name + ]] + GetName = function() end, + + --[[@method + Return the npc current position + @returns + position, the npc current position + ]] + GetPosition = function() end, + + --[[@method + Register this npc to the otserv default npc system, you must call this function at the end of the + npc file, if not, the npc wont work properly. Calling this function will fire the + AI.BasicNpc.Events.OnRegister event + ]] + Register = function() end, +} + +local function actionCompare(a,b) + return a.deliverTime < b.deliverTime +end + +function BasicNpc:New() + local npc = AI.CreateInstance(BasicNpc) + npc.BasicNpc = { + inRangeCreatures = {}, + varPool = {}, + delayedActions = AI.PriorityQueue:New(actionCompare) + } + + -- Register events + for key, event in pairs(BasicNpc.Events) do + npc:RegisterEvent(event) + end + + return npc +end + +function BasicNpc:AddDelayedAction(callback, delay, ...) + self.BasicNpc.delayedActions:Push({ + deliverTime = os.clock() + (delay / 1000), + fn = callback, + params = arg + }) +end + +function BasicNpc:SetGlobalData(id, data) + self.BasicNpc.varPool[id] = data +end + +function BasicNpc:GetGlobalData(id) + return self.BasicNpc.varPool[id] +end + +function BasicNpc:Register() + local scriptEnv = getfenv(2) + function scriptEnv.onThink() + local newInRange = {} + -- Purge removed/out-of-range creatures from us + for _, cid in ipairs(self.BasicNpc.inRangeCreatures) do + local isValid = self:CreatureFilter(cid) + if isValid and self:IsInVisionRange(cid) then + table.insert(newInRange, cid) + else + self:FireOnMissCreature(cid) + end + end + + self.BasicNpc.inRangeCreatures = newInRange + -- Visualize more creatures, a bit costly though + if self.RealTimeVisualization then + local spectators = getSpectators(self:GetPosition(), self.VisionRadius, self.VisionRadius) + for _, cid in ipairs(spectators) do + if self:CreatureFilter(cid) then + self:VisualizeCreature(cid) + end + end + end + + -- Dispatch the event + self:Dispatch({ + eventType = BasicNpc.Events.OnThink + }) + -- Execute delayed actions + while(not self.BasicNpc.delayedActions:IsEmpty() and self.BasicNpc.delayedActions:Top().deliverTime <= os.clock()) do + local action = self.BasicNpc.delayedActions:Pop() + action.fn(self, unpack(action.params)) + end + end + + function scriptEnv.onCreatureAppear(cid) + if not self:CreatureFilter(cid) then + return + end + + if self:Dispatch({ + eventType = BasicNpc.Events.OnCreatureAppear, + cid = cid + }) then + return + end + + self:VisualizeCreature(cid) + end + + function scriptEnv.onCreatureDisappear(cid) + self:Dispatch({ + eventType = BasicNpc.Events.OnCreatureDisappear, + cid = cid + }) + self:MissCreature(cid) + end + + function scriptEnv.onCreatureSay(cid, type, msg) + if not self:CreatureFilter(cid) then + return + end + + if self:Dispatch({ + eventType = BasicNpc.Events.OnCreatureSay, + cid = cid, + type = type, + msg = msg + }) then + return + end + + if self:IsInHearRange(cid) then + self:Dispatch({ + eventType = BasicNpc.Events.OnHear, + cid = cid, + type = type, + msg = msg, + creatureVisible = self:IsInVisionRange(cid) + }) + end + end + + self:Dispatch({ + eventType = BasicNpc.Events.OnRegister, + scriptEnv = scriptEnv + }) +end + +function BasicNpc:VisualizeCreature(cid) + if not self:IsInVisionRange(cid) or self:IsCreatureVisualized(cid) or self:FireOnVisualizeCreature(cid) then + return false + end + + table.insert(self.BasicNpc.inRangeCreatures, cid) + return true +end + +function BasicNpc:IsCreatureVisualized(cid) + return AI.TableFind(self.BasicNpc.inRangeCreatures, cid) and true or false +end + +function BasicNpc:MissCreature(cid) + local index = AI.TableFind(self.BasicNpc.inRangeCreatures, cid) + if index then + self:FireOnMissCreature(cid) + table.remove(self.BasicNpc.inRangeCreatures, index) + end +end + +function BasicNpc:IsInVisionRange(thing) + return self:IsInRange(getThingPosition(thing), self.VisionRadius) +end + +function BasicNpc:IsInHearRange(cid) + return self:IsInRange(getThingPosition(cid), self.HearRadius) +end + +function BasicNpc:IsInRange(pos, range) + local position = self:GetPosition() + return position.z == pos.z and math.max(math.abs(position.x - pos.x), math.abs(position.y - pos.y)) <= range +end + +function BasicNpc:GetPosition() + return getCreaturePosition(getNpcId()) +end + +function BasicNpc:GetName() + return getCreatureName(getNpcId()) +end + +function BasicNpc:FireOnMissCreature(cid) + self:Dispatch({ + eventType = BasicNpc.Events.OnMissCreature, + cid = cid, + reason = isCreature(cid) and + BasicNpc.CreatureMissReason.WalkAway or + BasicNpc.CreatureMissReason.Removed + }) +end + +function BasicNpc:FireOnVisualizeCreature(cid) + return self:Dispatch({ + eventType = BasicNpc.Events.OnVisualizeCreature, + cid = cid, + pos = getCreaturePosition(cid) + }) +end + +-- Register +AI.Inherit(AI.Dispatcher, BasicNpc) +AI.BasicNpc = BasicNpc +return BasicNpc \ No newline at end of file diff --git a/data/npc/lib/AI/containers.lua b/data/npc/lib/AI/containers.lua new file mode 100644 index 0000000..9016716 --- /dev/null +++ b/data/npc/lib/AI/containers.lua @@ -0,0 +1,147 @@ +if not AI then + return false +end + +local Stack = {} +local PriorityQueue = {} +local function lessThan(a, b) return a < b end + +-- Stack +function Stack:New() + local stack = {} + setmetatable(stack, {__index = Stack}) + return stack +end + +function Stack:Push(v) + table.insert(self, v) +end + +function Stack:Pop() + local val = self[table.size(self)] + table.remove(self) + return val +end + +function Stack:Top() + return self[table.size(self)] +end + +function Stack:Size() + return table.size(self) +end + +function Stack:IsEmpty() + return table.size(self) == 0 +end + +-- Queue +--[[function Queue:New() + local queue = {} + setmetatable(queue, {__index = Queue}) + return queue +end + +function Queue:Push(v) + table.insert(self, v) +end + +function Queue:Pop() + local v = self[1] + table.remove(self, 1) + return v +end + +function Queue:Size() + return #self +end + +function Queue:Top() + return self[1] +end + +function Queue:IsEmpty() + return #self == 0 +end ]] + +-- Priority Queue +local function upi(h, lt, obj, i) + local j = math.floor(i / 2) + local hj = h[j] + + while j > 0 and lt(obj, hj) do + h[i] = hj + i, j = j, math.floor(j / 2) + hj = h[j] + end + + h[i] = obj +end + +local function downi(h, lt, obj, i, n) + local j = i * 2 + while j <= n do + local hj = h[j] + if j < n then + local hj1 = h[j + 1] + if lt(hj1, hj) then + j = j + 1 + hj = hj1 + end + end + + if lt(obj, hj) then + break + end + + h[i] = hj + i = j + j = j * 2 + end + + h[i] = obj +end + +function PriorityQueue:New(lt) + local heap = { compareCallback = lt or lessThan } + setmetatable(heap, {__index = PriorityQueue}) + return heap +end + +function PriorityQueue:Top() + return self[1] +end + +function PriorityQueue:Push(v) + return upi(self, self.compareCallback, v, table.size(self) + 1) +end + +function PriorityQueue:Pop() + local size = table.size(self) + assert(size > 0, "PriorityQueue:Pop() Heap is empty") + + local ret = self[1] + if size > 1 then + local last = self[size] + self[1] = last + self[size] = nil + downi(self, self.compareCallback, last, 1, size - 1) + else + self[1] = nil + end + + return ret +end + +function PriorityQueue:Size() + return table.size(self) +end + +function PriorityQueue:IsEmpty() + return table.size(self) == 0 +end + + +-- Register +AI.Stack = Stack +AI.PriorityQueue = PriorityQueue \ No newline at end of file diff --git a/data/npc/lib/AI/declarative_talk.lua b/data/npc/lib/AI/declarative_talk.lua new file mode 100644 index 0000000..e4161c0 --- /dev/null +++ b/data/npc/lib/AI/declarative_talk.lua @@ -0,0 +1,312 @@ +if not AI then + return false +end +--[[ +@classname + AI.DeclarativeTalk +@description + This class is responsible for processing the AI.SocialNpc.Conversation property. + Think about this class as a shorthand yet more powerful version of the common msgcontains structure: + [code] + if msgcontains(...) then + + elseif msgcontains(...) then + + elseif topics[cid] == 1 then + + ... + [/code] + + To work with this class, all you need to do is create an AI.SocialNpc object and set its + Conversation property with the interactions desired, and you are ready to go. + + And now about the syntax, there two main concepts in this pseudo syntax: conditions and actions. + A condition is set of individual tests, that, if passed, further conditions and actions can be executed. + An action is a set of individual operations, that if reached (i.e. a condition leads us to the action) all its operations are executed in the order they were defined. Consider the following example: + + [code] + {[playerSays("quest")] = say("I have no quests for you today!")} + [/code] + + This code shows one condition set, with only one test, and an action set, with one operation. + When the AI.DeclarativeTalk executes this statement, first checks if the player said "quests", if so, then enters the action and executes the only available operation, saying "I have no quests for you today!� + + This may look weird to you if you have no previous experience with Lua tables, please refer to this link + http://lua-users.org/wiki/TablesTutorial if you are having problems understanding this. + + We take advantage of the fact that Lua tables can have nearly every possible value types as a key (functions and tables), to describe the conditions. + And we use the table values to describe the actions. In short: + + [code] + {[{tests}] = {operations}} + [/code] + + We enclose the condition in [] because in lua you must do so if you are using types for the keys other than strings. + + Here's another (working) example: + [code] + MyNpc.Conversation = { + {["quest|mission"] = "I have no missions for you today!"} + } + [/code] + + This time we look that there no functions involved, just plain strings. Since most of the time what you want is to map keywords to messages, we provide this shorthand form, if there�s a string in the condition side of the expression, then its matched against what the player says, if there�s a string in the action side of the expression, then it�s simply outputted to the player. (The | in �quest|missions� is an or-wildcard, look far below to know more) + + Knowing this we can create more complex structures: + [code] + MyNpc.Conversation = { + {["quest|mission"] = "I have no missions for you today!"}, + {["job"] = "My job is amazing"}, + {["pizza"] = "Uhm."} + } + [/code] + + When processing a condition, if all test are passed, the action is executed, and every other condition that is on the same level of the former condition, is ignored. Think about this as a nested If/Else structure: + [code] + if msg == "quest" or msg == "mission" then + ... + elseif msg == "job" then + + elseif msg == "pizza" then + + end + [/code] + + So let�s say we are already talking to a player, the player just said "job". AI.DeclarativeTalk will do the following: + 1. Test "job" against "quest|mission", since a match can�t be found, this condition is marked as failed and the execution continues + 2. Test "job" against "job", an exact match, the condition is passed and now we start executing the actions + 3. The only action is "My job is amazing", which in turn means "say My job is amazing", we do so. + 4. There no more actions or conditions nested in the current execution path. We ended + + The fourth point is important since the way AI.DeclarativeTalk works allows us to nest infinitely large actions/conditions: + + [code] + MyNpc.Conversation = { + {["heal"] = { -- Nested action + -- Nested condition + {[greatherThan(playerHealth, 50)] = "Sorry, your not wounded enought!"}, + + -- Nested 'default' condition + {[true] = function(talk) + addCreatureHealth(talk.player, 100) + return "You have been healed!" + end} + }} + } + [/code] + + As you can see, the code starts getting uglier the more conditions/actions we nest, but we can nest + 3-4 levels without worrying too much about it, if the thing is getting bigger, consider using AI.Talk interface instead. The example is a bit more complex but it shows more of the features of AI.DeclarativeTalk. + First, the use {} (tables) is what allows us to nest and stack actions/conditions. If you look closely, you will notice that all the condition tables we have shown so far have numeric indexes. That allows you to create a flow of execution, and to make sure things won�t get screwed by randomness. Also, the use of 'greatherThan(playerHealth, 50)' and 'true' as tests show us that this class supports various kinds of Lua types for specifying conditions. + + Here's a complete list of types for conditions: + Boolean - If true, the test passes, otherwise it fails. + String - The class will try to match what the player says to this string, if success, the test passes. + Function - The function it�s called with an AI.Talk object, a msg match if there's one, and what the players says as arguments, the returned value is checked against the condition rules. + Table - Each VALUE of it is traversed and checked against the condition rules + Nil - Test fails + Other - Not supported + + And for actions: + String - We output this string to the player and returns true, + Boolean - Returns it, + Function - The function it�s called with an AI.Talk object, a msg match if there's one, and what the players says as arguments, the returned value is checked against the action rules. + Table - Each VALUE of it is traversed and checked against the action rules, if no operation returned false, it returns true. Otherwise as soon as a field returns false, it exits leaving the remaining operations un-executed and returning false. + Other - Returns true + + If at the end of the execution, the resulting value was true (because we executed a valid set of operations successfully) the player talk timeout is reset. You can use this in your custom operations, returning false if you don�t want to refresh the timeout. + + One thing that might still look weird is the use of functions like 'greatherThan(playerHealth, 50)'. These are special functions (you can create your own) located in + ai_stddeclarative.lua, these functions do nothing but returning another function. Yes that�s true, that�s the price we have to pay for using this kind of syntax. This is a brief implementation of greatherThan: + [code] + function greatherThan(a, b) + return function() + return AI.RawValue(a) > AI.RawValue(b) + end + end + [/code] + + AI.RawValue just returns the same value we pass to it if it�s of any type but a function. If it�s a function, executes it and returns what it returned. + Since 'playerHealth' is another function, when the returned function from greatherThan is executed (i.e. when we are executing the whole conversation structure) playerHealth will be called and checked against '50'. + So basically, the use of these condition/action functions ends up expanding at runtime, our last example looks like this actually: + [code] + MyNpc.Conversation = { + {["heal"] = { + {[function() + return playerHealth() > 50 + end] = "Sorry, your not wounded enought!"}, + + ... + }} + } + [/code] + + One thing to remember is, condition/actions functions that have arguments are called with them right off the bat. + Functions with no arguments (like playerHealth) are just put there with no parenthesis. + + An example showing the use of multiple test and actions: + [code] + MyNpc.Conversation = { + {[{"heal", isPzLocked, lessThan(playerHealth, 50)}] = { + "HEAL POWER!", + function(talk) + addCreatureHealth(talk.player, 100) + end, + setTopic('afterHeal') + }}, + + {[true] = "This will execute if any of the above 3 tests fails"} + } + [/code] + + In this last example, you can see the power of the declarative syntax. Short-circuit is implemented in the test processing, so if 'isPzLocked' fails, lessThan(..) will not execute, i.e. as soon as a test fails, the condition set is exited. + + There�re a few more things you can do and I haven't explained. Like breaking the if/else into 2 or more parts for a bit more complex flow execution, but that's pretty situational or maybe even useless at this point. + I will explain that in a further revision. +]] +local DeclarativeTalk = {} +local data = {} + +-- Proccess an if/else block, returns true if a condition was fulfilled and an action executed successfully, +-- returns false if a condition was fulfilled but no action happened, and returns nil if no condition was acceptable +function DeclarativeTalk.ProccessIfElse(node, cid) + for _,reply in ipairs(node) do + for left, right in pairs(reply) do + if DeclarativeTalk.ProccessCondition(left, cid) then + return DeclarativeTalk.ProcessAction(right, cid) + end + end + end + + return nil -- no condition match +end + +-- Returns true if the condition was satisfied, false otherwise +function DeclarativeTalk.ProccessCondition(node, cid) + local t = type(node) + if t == "string" then + --[[ + String parser + "A|B|C" returns true if speech matches either A, B, or C + "Inner {param}" returns true if speech matches the first ocurrence of a {} enclosed string + "%" as a placeholder: "he%" will match hello, hell, hey, etc. MAX 2 PER || ENCLOSED STRING + ]] + for str in string.gmatch(node, "[^|]+") do + local original = string.match(str, "{([^}]+)") or str + str = AI.EscapePattern(original) + + if data[cid].npc.TalkCaseSensitive then + str = str:lower() + end + + local match = AI.FastMatch(data[cid].msg, str:gsub("%%%%", "%%w*", 2), true) + if match then + data[cid].match = match + return true + end + end + + return false + elseif t == "function" then + return DeclarativeTalk.ProccessCondition(node(data[cid].talk, data[cid].match, data[cid].msg)) + elseif t == "table" then + for _, innerNode in ipairs(node) do + local r = DeclarativeTalk.ProccessCondition(innerNode, cid) + if r == false then -- short circuit + return false + end + end + + return table.size(node) > 0 + elseif t == "boolean" then + return node + elseif t == "nil" then + return false -- In left params, we don't tolerate nil results + else + error("AI.DeclarativeTalk() Can't handle conversation param of type " .. t) + end +end + +-- Returns true if we exucuted any realistic action +function DeclarativeTalk.ProcessAction(node, cid) + local t = type(node) + if t == "string" then + data[cid].talk:Say(node) + return true + elseif t == "function" then + return DeclarativeTalk.ProcessAction(node(data[cid].talk, data[cid].match, data[cid].msg), cid) + elseif t == "table" then + --[[ Handle structures such: + ACTION + (IF/ELSE)CONDITION + (IF/ELSE)CONDITION + (ELSE) CONDITION + ACTION + (IF/ELSE) CONDITION + (IF/ELSE) CONDITION + ACTION + ]] + local ret, i, size = false, 1, table.size(node) + -- We need to modify the control variable, hence we use a while loop instead of a for + while(i <= size) do + actionOrCondition = node[i] + if type(actionOrCondition) == "table" then -- condition + -- Here we take all the adjacent conditions(tables) and proccess them + -- as if they were one unique node, creating an If/Else structure + local ifElseNode, k = {}, i + while(type(node[k]) == "table") do + table.insert(ifElseNode, node[k]) + k = k + 1 + end + -- If we end in a false state, we return ingnoring the remaining actions/conditions + -- if it ends true or nil, that means the ifElse block executed and finished, we + -- continue with the next blocks/actions + local r = DeclarativeTalk.ProccessIfElse(ifElseNode, cid) + if r == false then + return false + end + + if r == true then + ret = true + end + + i = k -- skip all the conditions we already proccessed + else --action + -- A simple action, we execute it, if the actions ends in a false state + -- we exit ignoring the remaining actions/blocks + local r = DeclarativeTalk.ProcessAction(actionOrCondition, cid) + if r == false then + return false + end + + ret = r + end + + i = i + 1 + end + + return ret + elseif t == "boolean" then + return node + else -- nil/numbers/whatever, we return true cuz theres no implicit "hey i want to exit!" + return true + end +end + +function DeclarativeTalk.Process(npc, event, talk) + if not npc.Conversation then + return false + end + + data[event.cid] = { + npc = npc, + msg = npc.TalkCaseSensitive and event.msg or event.msg:lower(), + talk = talk + } + local processed = DeclarativeTalk.ProcessAction(npc.Conversation, event.cid) + + data[event.cid] = nil + return processed +end + +AI.DeclarativeTalk = DeclarativeTalk \ No newline at end of file diff --git a/data/npc/lib/AI/dispatcher.lua b/data/npc/lib/AI/dispatcher.lua new file mode 100644 index 0000000..43ed186 --- /dev/null +++ b/data/npc/lib/AI/dispatcher.lua @@ -0,0 +1,81 @@ +if not AI then + return false +end + +local Dispatcher = { + -- Creates a new Dispatcher object + --[[@method + Creates a new Dispatcher object + ]] + New = function() end, + + --[[@method + Register an event type in this dispatcher, if you plan to create your own events, use something like this + to assign them a numeric value: + [code] + MyNpc.MyCustomEvent = AI.UniqueId() + [/code] + This ensures you never overwrite a previous event + @params + eventType number, a numeric id for this event + ]] + RegisterEvent = function(eventType) end, + + --[[@method + Register a callback function for the given eventType in this dispatcher + @params + eventType number, the numeric id of this event + callback function, a function to be called every time the event is triggered + ]] + AddCallback = function(eventType, callback) end, + + --[[@method + Dispatch the event to all its registered callbacks, the unique event argument should be a table + containing custom event information (such as cid, msg, etc) plus one field eventType, containing the id + of event. All the callback functions are called with to arguments, the dispatcher itself and the event object. + If a callback function exits returning true, the event is considered handled and no other callbacks will + be called. You can use code like this to check for custom events: + [code] + if not MyNpc:Dispatch(event) then + -- all the callbacks were called and the event was not handled + else + -- the event was handled by one of the registered callbacks + end + [/code] + @params + event table, the event object containing information appropiate for its eventType + @returns + boolean, whether the event was handled or not + ]] + Dispatch = function(event) end +} + +function Dispatcher:New() + local dispatcher = AI.CreateInstance(self) + dispatcher.Dispatcher = { + callbacks = {} + } + + return dispatcher +end + +function Dispatcher:RegisterEvent(eventType) + self.Dispatcher.callbacks[eventType] = {} +end + +function Dispatcher:AddCallback(eventType, callback) + table.insert(self.Dispatcher.callbacks[eventType], callback) +end + +function Dispatcher:Dispatch(event) + for _, callback in ipairs(self.Dispatcher.callbacks[event.eventType]) do + if callback(self, event) == true then + return true + end + end + + return false +end + +AI.Dispatcher = Dispatcher +return Dispatcher \ No newline at end of file diff --git a/data/npc/lib/AI/focus.lua b/data/npc/lib/AI/focus.lua new file mode 100644 index 0000000..6736675 --- /dev/null +++ b/data/npc/lib/AI/focus.lua @@ -0,0 +1,222 @@ +if not AI then + return false +end + +--[[ +@classname + AI.FocusNpc +@inherits + AI.BasicNpc +@description + A BasicNpc with focusable behaviour, it still dont know how to talk, + for a "normal" npc use AI.SocialNpc +]] +local FocusNpc = { + Events = { + --[[@event + This event is triggered just before focusing a new creature, if handled/canceled + the creature wont get focused. + @params + cid creature, the about to focus creature + ]] + OnAboutToFocus = AI.UniqueId(), + + --[[@event + This event is triggered when a creature is focused. + @params + cid creature, the focused creature + ]] + OnFocus = AI.UniqueId(), + + --[[@event + This event is called when a creature is unfocused, for details, check the + reason field in the event object + @params + cid creature, the unfocused creature + reason AI.FocusNpc.UnfocusReason, the unfocus reason + ]] + OnUnfocus = AI.UniqueId() + }, + + --[[@enum + Values used when triggering OnUnfocus, to distinguish between different causes of unfocusing. + @values + SelfRequested, When the class user calls AI.FocusNpc.Unfocus programmatically + FocusRemoved, When the creature is removed from the game + FocusWalkedAway, When the creature walks away from our vision radius + ]] + UnfocusReason = { + SelfRequested = AI.UniqueId(), + FocusRemoved = AI.BasicNpc.CreatureMissReason.Removed, + FocusWalkedAway = AI.BasicNpc.CreatureMissReason.WalkAway + }, + + --[[@property boolean, Look at our first/only focused creature at every moment ]] + LookAtFocus = true, + + --[[@method + Creates a new focusable npc + ]] + New = function() end, + + --[[@method + Adds a creature to this npc focus list, effectively triggering the OnAboutToFocus and + OnFocus events. If the creature was already focused or if the creature could not be focused, + this function returns false + @params + cid creature, the creature + @returns + boolean, whether the creature was successfully focused or not + ]] + AddFocus = function(cid) end, + + --[[@method + Removes a previosly focused creature from this npc focus list, effectively triggering the OnUnfocus + event. A optional parameter (defaults to SelfRequested) can be specified to set the event unfocus reason. + @params + cid creature, the creature + [reason] AI.FocusNpc.UnfocusReason, the unfocus reason + ]] + RemoveFocus = function(cid, reason) end, + + --[[@method + Returns true if the given creature is already focused + @params + cid creature, the creature + @returns + boolean, whether the given creature is focused or not + ]] + IsFocused = function(cid) end, + + --[[@method + Adds a variable piece of data to the given creature focus storage, all the data stored using + this function will remain until the creature is unfocused + @params + cid creature, the creature + id string/number, the identifier of this piece of data + data mixed, the data to store + ]] + SetFocusData = function(cid, id, data) end, + + --[[@method + Returns the piece of data associated to this npc creature focus storage with the given identifier. + @params + cid creature, the creature + id string/number, the identifier used in the registration of the wanted piece of data + @returns + mixed, the stored data or nil if there's no data associated with the given id + ]] + GetFocusData = function(cid, id) end, + + --[[@method + Returns a table containing all the data stored in this npc creature focus storage + @params + cid creature, the creature + @returns + table, a table(keys=ids, values=data) containing all the data stored for this creature + ]] + GetAllFocusData = function(cid) end +} + +function FocusNpc:New() + local npc = AI.CreateInstance(FocusNpc) + npc.FocusNpc = { + focuses = {}, + focusData = {} + } + + -- Register events + for key, event in pairs(FocusNpc.Events) do + npc:RegisterEvent(event) + end + + -- Register callbacks + npc:AddCallback(AI.BasicNpc.Events.OnMissCreature, self.FocusMissCreatureCallback) + npc:AddCallback(AI.BasicNpc.Events.OnThink, self.FocusOnThinkCallback) + + return npc +end + +function FocusNpc:SetFocusData(cid, id, data) + assert(self:IsFocused(cid), "FocusNpc:SetFocusData() Attempt to associate data to an unfocused creature cid") + self.FocusNpc.focusData[cid][id] = data +end + +function FocusNpc:GetFocusData(cid, id) + assert(self:IsFocused(cid), "FocusNpc:GetFocusData() Attempt to retrieve data from an unfocused creature cid") + return self.FocusNpc.focusData[cid][id] +end + +function FocusNpc:GetAllFocusData(cid) + assert(self:IsFocused(cid), "FocusNpc:GetAllFocusData() Attempt to retrieve data from an unfocused creature cid") + return self.FocusNpc.focusData[cid] +end + +function FocusNpc:IsFocused(cid) + return AI.TableFind(self.FocusNpc.focuses, cid) and true or false +end + +function FocusNpc:AddFocus(cid) + if self:IsFocused(cid) or (not self:IsCreatureVisualized(cid) and not self:VisualizeCreature(cid)) or self:TriggerAboutToFocus(cid) then + return false + end + + table.insert(self.FocusNpc.focuses, cid) + self.FocusNpc.focusData[cid] = {} + self:TriggerFocus(cid) + return true +end + +function FocusNpc:RemoveFocus(cid, reason) + local index = AI.TableFind(self.FocusNpc.focuses, cid) + if index then + self:TriggerUnfocus(cid, reason or FocusNpc.UnfocusReason.SelfRequested) + table.remove(self.FocusNpc.focuses, index) + self.FocusNpc.focusData[cid] = nil + end +end + +function FocusNpc:GetFocuses() + return AI.Clone(self.FocusNpc.focuses) +end + +function FocusNpc:TriggerAboutToFocus(cid) + return self:Dispatch({ + eventType = AI.FocusNpc.Events.OnAboutToFocus, + cid = cid + }) +end + +function FocusNpc:TriggerFocus(cid) + return self:Dispatch({ + eventType = AI.FocusNpc.Events.OnFocus, + cid = cid + }) +end + +function FocusNpc:TriggerUnfocus(cid, reason) + return self:Dispatch({ + eventType = AI.FocusNpc.Events.OnUnfocus, + cid = cid, + reason = reason + }) +end + +function FocusNpc:FocusMissCreatureCallback(event) + self:RemoveFocus(event.cid, event.reason) +end + +function FocusNpc:FocusOnThinkCallback(event) + if self.LookAtFocus then + if table.size(self.FocusNpc.focuses) > 0 then + doNpcSetCreatureFocus(self.FocusNpc.focuses[1]) + else + doNpcSetCreatureFocus(nil) + end + end +end + +-- Register +AI.Inherit(AI.BasicNpc, FocusNpc) +AI.FocusNpc = FocusNpc +return FocusNpc \ No newline at end of file diff --git a/data/npc/lib/AI/social.lua b/data/npc/lib/AI/social.lua new file mode 100644 index 0000000..2b5039c --- /dev/null +++ b/data/npc/lib/AI/social.lua @@ -0,0 +1,538 @@ +if not AI then + return false +end + +--[[ +@classname + AI.SocialNpc +@inherits + AI.FocusNpc +@description + This is the main class to look at to start creating new rich-featured npcs. + The class provides the all the functionality you would expect from an advanced NPC class. + It supports complex conversations aswell as standar greet/farewell replies, 80% of the job of this + class is to make possible all kind of weird/complex conversations you will ever code, easy. + The rest of this class provides you a pluginable architecture, where you can attach Shop, Travel and every + other kind of custom modularized functionality to your npc. + + To create a new instance of this class use: + [code] + local MyNpc = AI.SocialNpc:New() + [/code] + + After that, you can do some basic customization to the npc changing its properties: + [code] + MyNpc.GreetReply = "Oh hi |PLAYERNAME|!" + MyNpc.MaxIdleTime = 20000 + MyNpc.CreatureFilter = function(cid) + return isPlayer(cid) and isPremium(cid) + end + [/code] + + Some properties accept different kind of values/types, for example, GreetReply can be either a string or a function. + You may skip this step since adecuate default values are provided when creating a new npc. + + As with most the AI.Npc classes, you will rarely need to call a npc member function outside a conversation or + other kind of operation (like waypoint walking). Most of the conversation code is handled by two main classes, [classref]AI.Talk[/classref] and + [classref]AI.DeclarativeTalk[/classref]. Both talk classes have built-in support for maitaning a conversation with multiple players at a time and remebering data from all of the conversations, you can use these two classes + within one npc, but such approach is not recommended and might cause unexpected behaviour, as a general rule, you should stick to one of these classes per npc. Both of them are capable + of creating really complex conversations, but AI.DeclarativeTalk tends to be more useful and easy to code for small or keywords-answer types of npc. AI.Talk offers a more lenghtly syntax, but also more + redable and as the npc becomes bigger, conversations created with it start looking smaller and nicer than the declarative code equivalent. + + IMPORTANT! You must call the AI.BasicNpc.Register() function at the end of the npc file, this will take care of making the server aware of your npc. + + Consider this small exmaple: + [code] + local Npc = AI.SocialNpc:New() + Npc.GreetReply = "Hey there |PLAYERNAME|, how's going?" + Npc.FarewellReply = "See you later!" + + Npc.Conversation = { + {['job|occupation'] = "My job is to generate gold every second"} + {['quest|mission'] = "Sorry, no missions for you today!"} + } + + Npc:Register() + [/code] + + Take a look at the AI.Plugin classes, for shop, addons, and promotion examples. (Soon) +@seealso + AI.Talk, AI.DeclarativeTalk, AI.Plugin +]] +local SocialNpc = { + Events = { + --[[@event + This event is triggered every time this npc is about to say something. + If you handle/cancel this event, the message is ignored and not outputed. + @params + cid creature/nil, the creature we are talking to, nil if we are talking in the default channel + msg, the text we are about to output + ]] + OnSay = AI.UniqueId(), + + --[[@event + Triggered when a player says something that matches a keyword from the AI.SocialNpc.GreetKeywords table, inside this npc HEAR radius. + If the hear radius is greater than the vision radius and the player is standing outside this npc VISION radius, then AI.SocialNpc.ComeCloserReply is outputed and this event is not triggered, otherwise + the event is triggered. + If you handle/cancel this event the npc won't start a talk a with the player. + @params + cid creature, the creature + type MessageType, the message type + msg string, the message + keyword string, the keyword that this player used to greet us + ]] + OnPlayerGreetUs = AI.UniqueId(), + + --[[@event + Triggered when a player says something that matches a keyword from the AI.SocialNpc.UngreetKeywords, just before we ungreet the player. + If you handle/cancel this event, the ungreet request will be ignored and the conversation will be intact. + @params + cid creature, the creature + type MessageType, the message type + msg string, the messagekeyword string, + keyword string, the keyword that the player used to ungreet us + ]] + OnPlayerUngreetUs = AI.UniqueId(), + + --[[@event + Triggered after a not handled triggering of AI.SocialNpc.OnPlayerGreetUs, or when a call to AI.SocialNpc.Greet + is programmatically called. + If you handle/cancel this event, no message is going to be outputed, otherwise, AI.SocialNpc.GreetReply is outputed. + Even if you handle this event, the conversation WILL start. + @params + cid creature, the creature, + reason AI.SocialNpc.TalkEndReason, the reason for ungreeting + ]] + OnGreet = AI.UniqueId(), + + --[[@event + Triggered after a not handled triggering of AI.SocialNpc.OnPlayerUngreetUs, when the npc becomes 'bored'(AI.SocialNpc.MaxIdleTime timedout), when a player just walked away from our vision radius or disconnects, or when its + selfrequested by the class user (via AI.SocialNpc.Ungreet or AI.SocialNpc.CustomUngreet). The trigger reason is specified in the event param reason, by one of the values in + AI.SocialNpc.TalkEndReason. If you handle/cancel this event, no 'farewell' message will be outputed, there're different replies for each of the different values from TalkEndReason, none of them will + be outputed, not even the custom message passed to AI.SocialNpc.CustomUngreet. + Is the event is not handled, an approtiate reply is outputed and the conversations ends (along with the focus). Even if you handle this event, the conversation WILL end. + @params + cid creature, the creature, + reason AI.SocialNpc.TalkEndReason, the reason for ungreeting + ]] + OnUngreet = AI.UniqueId(), + }, + + --[[@enum + Values used when triggering OnUngreet, to distinguish between different causes of ungreeting. + @values + PlayerRequested, When the player requested it by saying an appropiate keyword, or when the class user calls AI.SocialNpc.Ungreet or AI.SocialNpc.CustomUngreet programmatically + FocusRemoved, When the player is removed from the game + FocusWalkedAway, When the player walks away from our vision radius + Timedout, When the npc gets bored (i.e AI.SocialNpc.MaxIdleTime timesout) + ]] + TalkEndReason = { + PlayerRequested = AI.FocusNpc.UnfocusReason.SelfRequested, + FocusRemoved = AI.FocusNpc.UnfocusReason.FocusRemoved, + FocusWalkedAway = AI.FocusNpc.UnfocusReason.FocusWalkedAway, + Timedout = AI.UniqueId() + }, + + --[[@property table, they keywords this npc will listen to start a conversation]] + GreetKeywords = {"hi", "hello", "hey"}, + + --[[@property table, they keywords this npc will listen to end a conversation]] + FarewellKeywords = {"bye", "farewell", "cya"}, + + --[[@property table, they keywords this npc will listen when hes asking a binary question and expecting a positive answer]] + AgreementKeywords = {"yes", "ok", "yeah", "affirmative"}, + + --[[@property table, they keywords this npc will listen when hes asking a binary question and expecting a negative answer]] + DisagreementKeywords = {"no", "not", "negative"}, + + --[[@property boolean, if setted to true, GreetKeywords are ignored and a conversation will start from everything a player says]] + StartTalkWithAnyInput = false, + + --[[@property number, The maximum time(in miliseconds) this npc is gonna wait for a player response before ending the talk]] + MaxIdleTime = 300000, + + --[[@property number, The time(in miliseconds) this npc is gonna wait before outputting a requested message]] + ResponseDelay = 350, + + --[[@property mixed, Declarative talk configuration]] + Conversation = nil, + + --[[@property boolean, Case sensitivity (differ upper from lower case strings)]] + TalkCaseSensitive = false, + + --[[@property number/boolean, This is the equivalent to Jiddo's Npc system KEYWORD_BEHAVIOR, except that you can set this + to any integer value, this will recheck up to N previous topics if the current ones doesn't + match, set it to true to recheck all the topics or 0 to disable topic checking. This only works with AI.Talk, you must code + manual remembering for AI.DeclarativeTalk]] + GoUpTimes = 1, + + --[[@property string/function, reply to greet the player, if its a function, its called with an AI.Talk object as unique argument]] + GreetReply = 'Welcome, |PLAYERNAME|! I have been expecting you.', + + --[[@property string/function, reply used when a player tries to greet us, we can hear him (i.e he's inside our hear radius) and we cannot see him (i.e outside our vision radius), if its a function, its called without ANY arguments, since theres no talk created at this time]] + ComeCloserReply = 'Come a little closer so we can talk..', + + --[[@property string/function, reply to ungreet the player in normal conditions, if its a function, its called with an AI.Talk object as unique argument]] + FarewellReply = 'Good bye, |PLAYERNAME|!', + + --[[@property string/function, reply to ungreet the player when MaxIdleTime times out, if its a function, its called with an AI.Talk object as unique argument]] + IdleTimeoutReply= 'Next, please!', + + --[[@property string/function, reply to ungreet the polayer when he walked away from our vision radius, if its a function, its called with an AI.Talk object as unique argument]] + WalkAwayReply = 'How rude!', + + --[[@property string, the string token that is replaced with the player name every time this npc says something]] + PlayerNameToken = 'PLAYERNAME', + + TalkData = 'SocialNpc.talk', + InterestData = 'SocialNpc.interest', + TalkThreadData = 'SocialNpc.talkThread', + PendingUnfocusData = 'SocialNpc.pendingUnfocus', + + --[[@method + Creates a new social NPC + ]] + New = function() end, + + --[[@method + Says something to the default channel, if the second optional argument + is true, the message is sent immediately, otherwise it will be delayed as + specified by AI.SocialNpc.TalkDelay + @params + message string, the message + [noDelay] boolean, defaults to false, if true, the message will not be delayed + ]] + Say = function(message, noDelay) end, + + --[[@method + Says something to a player using the npc channel, you will usually not call this function + directly since AI.Talk.Say is shorter and less error prone. + @params + message string, the message + cid creature, the player + [noDelay] boolean, defaults to false, if true, the message will not be delayed + ]] + SayTo = function(message, cid, noDelay) end, + + --[[@method + Returns the AI.Talk object associated with the given player + @params + cid creature, the player + @returns + AI.Talk, the talk object or nil if there's none + ]] + GetTalk = function(cid) end, + + --[[@method + Returns true if there's an active talk for the given player + @params + cid creature, the player + @returns + boolean, whether we are talking with the given player or not + ]] + IsTalkActive = function(cid) end, + + --[[@method + Greets the given player. This function can only great players that + can be visualized and focused. To check if the greet was successful use + AI.SocialNpc.IsTalkActive. This function is called automatically when a player + starts the talk (via AI.SocialNpc.GreetKeywords or AI.SocialNpc.StartWithAnyInput) + @params + cid creature, the player + ]] + Greet = function(cid) end, + + --[[@method + Ungreets the given player triggering the OnUngreet event with the given reason. + This function is called automatically when a player ends the talk (via AI.SocialNpc.FarewellKeywords) + @params + @returns + ]] + Ungreet = function(cid, reason) end, + + --[[@method + Ungreets the given player using a custom message, triggering the OnUngreet event with + AI.SocialNpc.PlayerRequested as the ungreet reason. You will usually use this function + by calling AI.Talk.End(msg) instead of using it directly. + @params + cid creature, the player + msg string, the custom ungreet message + ]] + CustomUngreet = function(cid, msg) end, + + --[[@method + This function removes the given player from this npc focuses after the delay specified + in AI.SocialNpc.TalkDelay. You shouldn't be using this function directly although it might + come in hand on some situations. + @params + cid creature, the player + ]] + DelayedRemoveFocus = function(cid) end, + + --[[@method + Refreshes the interaction timer for the given player, effectively preventing the ungreet of the player + by timeout reasons. This function is called automatically every time a npc reacts to a player (usually after + saying something) + @params + cid creature, the player + ]] + KeepInterest = function(cid) end, + + --[[@method + Returns a new string in wich all the ocurrences of |AI.SocialNpc.PlayerNameToken| were replaced + by the given player name. This function is called automatically for every message this npc says. + @params + msg string, the base string + cid creature, the player + @returns + string, the newly parsed string + ]] + ParsePlayerName = function(msg, cid) end +} + +local function SayDefault(npc, msg, cid) + if type(msg) == "string" then + npc:SayTo(msg, cid) + elseif type(msg) == "function" then + msg(npc:IsTalkActive(cid) and npc:GetTalk(cid) or nil) + end +end + +local function CreateCoroutine(npc, cid) + npc:SetFocusData(cid, npc.TalkThreadData, coroutine.create(npc.ProcessTalkThread)) +end + +function SocialNpc:New() + local npc = AI.CreateInstance(SocialNpc) + npc.SocialNpc = {} + + -- Register events + for key, event in pairs(SocialNpc.Events) do + npc:RegisterEvent(event) + end + + -- Regiser callbacks + npc:AddCallback(AI.BasicNpc.Events.OnHear, self.SocialOnHearCallback) + npc:AddCallback(AI.BasicNpc.Events.OnThink, self.SocialOnThinkCallback) + npc:AddCallback(AI.FocusNpc.Events.OnFocus, self.SocialOnFocusCallback) + npc:AddCallback(AI.FocusNpc.Events.OnUnfocus, self.SocialOnUnfocusCallback) + + return npc +end + +function SocialNpc:Say(message, noDelay) + if noDelay then + self:TriggerOnSay(message) + else + self:AddDelayedAction(self.Say, self.ResponseDelay, message, true) + end +end + +function SocialNpc:SayTo(message, cid, noDelay) + if noDelay then + self:TriggerOnSay(message, cid) + else + self:AddDelayedAction(self.SayTo, self.ResponseDelay, message, cid, true) + end +end + +function SocialNpc:TriggerOnSay(msg, cid) + if not self:Dispatch({ + eventType = SocialNpc.Events.OnSay, + msg = msg, + cid = cid + }) then + if cid then + selfSay(self:ParsePlayerName(msg, cid), cid) + else + selfSay(msg) + end + end +end + +function SocialNpc:GetTalk(cid) + return self:GetFocusData(cid, self.TalkData) +end + +function SocialNpc:IsTalkActive(cid) + return self:IsFocused(cid) and not self:GetFocusData(cid, self.PendingUnfocusData) +end + +function SocialNpc:CreateTalk(cid) + self:SetFocusData(cid, self.TalkData, AI.Talk:New(self, cid)) + self:SetFocusData(cid, self.PendingUnfocusData, false) + self:KeepInterest(cid) + CreateCoroutine(self, cid) +end + +function SocialNpc:CheckForGreet(_msg) + local greetWord = AI.GetMatch(_msg, self.GreetKeywords, self.TalkCaseSensitive) + local msg = self.TalkCaseSensitive and _msg or _msg:lower() + + if greetWord then + -- Hi NPCNAME + if msg:find((self.TalkCaseSensitive and self:GetName() or self:GetName():lower()), 1, true) then + return greetWord + end + + local otherNpcs = getSpectators(self:GetPosition(), 7, 7) + -- Check for Hi OTHERNPCNAME + for _, npc in ipairs(otherNpcs) do + if msg:find((self.TalkCaseSensitive and getCreatureName(npc) or getCreatureName(npc):lower()), 1, true) then + return false + end + end + + -- No name specified, assume player is talking to us + return greetWord + end + + return false +end + +function SocialNpc:Greet(cid) + if self:AddFocus(cid) and not self:Dispatch({ + eventType = SocialNpc.Events.OnGreet, + cid = cid + }) then + SayDefault(self, self.GreetReply, cid) + end +end + +function SocialNpc:Ungreet(cid, reason, supressUnfocus) + if not self:Dispatch({ + eventType = SocialNpc.Events.OnUngreet, + cid = cid, + reason = reason + }) then + local msg + + if reason == SocialNpc.TalkEndReason.PlayerRequested then + msg = self.FarewellReply + elseif reason == SocialNpc.TalkEndReason.FocusWalkedAway then + msg = self.WalkAwayReply + elseif reason == SocialNpc.TalkEndReason.Timedout then + msg = self.IdleTimeoutReply + end + + if msg then + SayDefault(self, msg, cid) + end + end + + if not supressUnfocus then + self:DelayedRemoveFocus(cid) + end +end + +function SocialNpc:CustomUngreet(cid, msg) + if not self:Dispatch({ + eventType = SocialNpc.Events.OnUngreet, + cid = cid, + reason = SocialNpc.TalkEndReason.PlayerRequested + }) then + self:SayTo(msg, cid) + end + + self:DelayedRemoveFocus(cid) +end + +function SocialNpc:DelayedRemoveFocus(cid) + if self:IsTalkActive(cid) then + self:AddDelayedAction(self.RemoveFocus, self.ResponseDelay, cid) + self:SetFocusData(cid, self.PendingUnfocusData, true) + end +end + +function SocialNpc:KeepInterest(cid) + self:SetFocusData(cid, self.InterestData, os.clock()) +end + +function SocialNpc:SocialOnHearCallback(event) + if self:IsTalkActive(event.cid) then + local talk = self:GetFocusData(event.cid, self.TalkThreadData) + + if coroutine.status(talk) == "dead" then + CreateCoroutine(self, event.cid) + talk = self:GetFocusData(event.cid, self.TalkThreadData) + end + + local success, speechProccessed = coroutine.resume(talk, self, event) + + if not success then + print(debug.traceback(talk), speechProccessed) + return false + end + + if speechProccessed then + self:KeepInterest(event.cid) + end + + return speechProccessed + elseif (self.GreetKeywords or self.StartTalkWithAnyInput) then + local match = self:CheckForGreet(event.msg) + if match or self.StartTalkWithAnyInput then + + if not event.creatureVisible then + SayDefault(self, self.ComeCloserReply, event.cid) + return + end + + if not self:Dispatch(AI.Mix(event, { + eventType = SocialNpc.Events.OnPlayerGreetUs, + keyword = match + })) then + self:Greet(event.cid) + end + + return true + end + end +end + +function SocialNpc:ProcessTalkThread(event) + local talk = self:GetTalk(event.cid) + local processed = talk:Process(event) or AI.DeclarativeTalk.Process(self, event, talk) + + -- Look for standar replys + if not processed and self.FarewellKeywords then + local match = AI.GetMatch(event.msg, self.FarewellKeywords, self.TalkCaseSensitive) + if match and not self:Dispatch(AI.Mix(event, { + eventType = SocialNpc.Events.OnPlayerUngreetUs, + keyword = match + })) then + self:Ungreet(event.cid, SocialNpc.TalkEndReason.PlayerRequested) + processed = true + end + end + + return processed +end + +function SocialNpc:SocialOnThinkCallback(event) + -- Proccess "afk" people + for _, cid in ipairs(self:GetFocuses()) do + if self:IsTalkActive(cid) and (os.clock() - self:GetFocusData(cid, self.InterestData)) > (self.MaxIdleTime / 1000) then + self:Ungreet(cid, SocialNpc.TalkEndReason.Timedout) + end + end +end + +function SocialNpc:ParsePlayerName(msg, cid) + return AI.ReplaceToken(msg, self.PlayerNameToken, getCreatureName(cid)) +end + +function SocialNpc:SocialOnFocusCallback(event) + self:CreateTalk(event.cid) +end + +function SocialNpc:SocialOnUnfocusCallback(event) + if event.reason == AI.FocusNpc.UnfocusReason.FocusWalkedAway then + self:Ungreet(event.cid, event.reason, true) + end +end + +-- Register +AI.Inherit(AI.FocusNpc, SocialNpc) +AI.SocialNpc = SocialNpc +return SocialNpc \ No newline at end of file diff --git a/data/npc/lib/AI/std_declarative.lua b/data/npc/lib/AI/std_declarative.lua new file mode 100644 index 0000000..ae23afb --- /dev/null +++ b/data/npc/lib/AI/std_declarative.lua @@ -0,0 +1 @@ +-- THIS MODULE IS NOT READY YET, SO YOU CAN'T ACTUALLY RUN THE EXAMPLES IN AI_DECLARATIVETALK, HOWEVER THAT DOESNT STOP YOU FOR CREATING YOUR OWN TEST FUNCTIONS \ No newline at end of file diff --git a/data/npc/lib/AI/talk.lua b/data/npc/lib/AI/talk.lua new file mode 100644 index 0000000..93a79b0 --- /dev/null +++ b/data/npc/lib/AI/talk.lua @@ -0,0 +1,396 @@ +if not AI then + return false +end + +--[[ +@classname + AI.DeclarativeTalk +@description + This class provides a topic stack based conversation handling and wrappers some common Npc-to-player conversation functions. +There are two ways to use this class, first, every condition/action function in a AI.DeclarativeTalk gets a talk object(the same if talking to the same player) when its called. The propose of this is to ease the writing of declarative code, the talk object can be used to access common functions, like Say(), or Listen(). Other functions from this class (like Ask, AskAgreement, Response, BadResponse) are intended to be used away from AI.DeclarativeTalk, because they make use of a topic stack, unexpected behavior might occur if you mix declarative code with stack based code. +The second way to use this class, the imperative mode as I like to call it is to make use of callback functions for hear events, building the stack of topics inside them. It�s pretty easy actually: +[code] + MyNpc.GreetReply = function(talk) + talk:Say(�Yo son, this is your first Npc�) + talk:AskAgreemnt(�Do you like me?�, + function() + talk:Say(�I know you�d to love me!�) + end, + function() + talk:Say(�):�) + end) + end +[/code] +While this code might look harsh or difficult, you can cleanup things by creating separated functions: +[code] + local function heLikesMe(talk) + talk:Say(�I know you�d to love me!�) + end + local function heDontLikesMe(talk) + talk:Say(�):�) + end + MyNpc.GreetReply = function(talk) + talk:Say(�Yo son, this is your first Npc�) + talk:AskAgreemnt(�Do you like me?�, heLikesMe, heDontLikesMe) + end +[/code] +It might look tedious to create two separate functions for just replying text! You have 2 choices try with AI.DeclarativeTalk or make use of the convenient reply classes: +[code] + MyNpc.GreetReply = function(talk) + talk:Say(�Yo son, this is your first Npc�) + talk:AskAgreemnt(�Do you like me?�, talk:ReplySay(�I know you�d to love me!�), talk:ReplySay(�):�)) + end +[/code] +The way AI.Talk handles the topics has to be examined carefully. First, a topic is a question or a statement awaiting a response, the topics doesn�t have an ID or some sort of name, they just exists, like in normal conversations. When you ask something using the functions Ask and AskAgreement, a new topic is pushed into the stack, when a player says something, the topic on the top of the stack (that is, the last we pushed into the stack) is retrieved and matched. If the player said something valid (in the above case �Yes� or �Affirmative�) the topic �good response� its executed (in the above case, the �heLikesMe� function), otherwise (�No�, �Negative�) the topic �bad response� its executed. +Lets say we want to ask a player what vocation he would like to become, we might code something like this: +[code] + talk:Ask(�Do you want to become a {Sorcerer}, {Druid}, {Paladin} or {Knight}?�) +[/code] +The enclosed {} strings are used by Ask as �good responses�, so if the player answer any of those, a �good response� function will be called(if any), if a player says anything else, the bad response will be called (if any). +[code] +talk:Ask(�Do you want to become a {Sorcerer}, {Druid}, {Paladin} or {Knight}?�) +talk:Response(function(talk, vocation) + talk:AskAgreement(�So you want to become a � .. vocation .. �eh? Are you sure about this?�, + function() -- Yes + + end, + + function -- No + + end) +end) +[/code] +Immediately after calling AI.Talk.Ask, you can call AI.Talk.Response, this will effectively add the function you pass to Response as a �good response� callback. You can have any amounts of good and bad responses you want. There�s an equivalent AI.Talk.BadResponse, for filling the �bad responses� callbacks. +Now we can examine better the �topic� meaning, we currently have two topics on the stack, the vocation question is our first topic, the agreement question, our second one. When a player says something again, the top topic (the last topic) is pulled from the stack (is not delated thought, just used) and appropriate function responses are called. So, why do we keep the topics on the stack anyway? At any point, the player might want to change his vocation choice, and our oracle will look stupid if we start all over again (assuming we started with �ARE YOU READY!?�), in this case, we simply �go up� into our topic stack (technically the correct thing to say Is �go down in the stack� but it�s the same thing). AI.Talk.GoUp is the function that allow us to do such thing. +Now we can appropriately handle the �No , I want to change my voc� response: +[code] +talk:Ask(�Do you want to become a {Sorcerer}, {Druid}, {Paladin} or {Knight}?�) +talk:Response(function(talk, vocation) + talk:AskAgreement(�So you want to become a � .. vocation .. �eh? Are you sure about this?�, + function() -- Yes + + end, + + function -- No + talk:Say(�Then, what vocation do you want to become?�) + talk:GoUp(1) � Go to the first question WITHOUT REPEATING THE QUESTION + end) +end) + [/code] +Awesome, now our oracle looks smart. A similar thing happens when a player says something that doesn�t match anything in the current topic(i.e. there�s no good response match and we don�t have a bad response callback), AI.Talk will go up a max of AI.SocialNpc.GoUpTimes, trying to find an appropriate handler for what the player says. If no one is found, the player is ignored and the topic stack remains the same (unlike GoUp, this normal �topic finding� procedure will not erase the topics on the stack). If the player is ignored for enough time, he might get kicked as the npc is getting bored (see AI.SocialNpc.TalkTimeout). Then player timeout isn�t refreshed when the npc says something to the player, Talk classes (like AI.Talk and AI.DeclarativeTalk) take care of refreshing the timeout. In the case of AI.Talk the talk is refreshed whenever you add new topics in any of the callback functions (i.e asking another thing like in our example), when you call AI.Talk.GoUp inside any of the callback functions and finally if you return true from any the callback functions. +By making use of closures (http://www.lua.org/pil/6.1.html) we can keep answers from previous question in the local scope: +[code] +talk:Ask(�Do you want to become a {Sorcerer}, {Druid}, {Paladin} or {Knight}?�) +talk:Response(function(talk, vocation) + talk:AskAgreement(�So you want to become a � .. vocation .. �eh? Are you sure about this?�, + function() -- Yes + end, + + function -- No + talk:Say(�You don�t want to be a � .. vocation .. �, what vocation do you want to become then?�) + talk:GoUp(1) + end) +end) + [/code] +But this is not possible if you use functions declared elsewhere like in our second example: +[code] +talk:AskAgreemnt(�Do you like me?�, heLikesMe, heDontLikesMe) +[/code] +This will have a very intuitive solution in a upcoming version, but for now you can make use of the AI.Talk.Var function to store/retrieve data inside the talk object. +If you want to end a conversation at any point, you can use talk:End(msg), if you want to stop the code in a specific point until a player says something again, use talk:Listen(), it�s a very good function but ill cover it on a upcoming review. +]] +AI.Talk = { + --[[@method + Creates a new Talk object, you should almost never want to use this function directly + @params + ]] + New = function(npc, cid) end, + + --[[@method + Stores or retrieve data from this talk internal storage + @params + key string/number, the key of the stored data you want to retrieve or the key you want to assign to the data + value mixed, if not nil, this function will store the data with the given key into this talk object, otherwise the operation is a retrieval + @returns + mixed, the stored/retrieved value + ]] + Var = function(key, value) end, + + --[[@method + Proccesses a AI.BasicNpc.Hear event, this is called automatically. + @params + ]] + Process = function(event) end, + + --[[@method + Removes all the topics from this npc memory (stack) successfully restoring the npc to his greet state (but not greeting again) + ]] + Reset = function() end, + + --[[@method + Stops the execution of caller of this function, until the talk player says something again. This function is cleanly implemented + and you dont need to worry about 'gotchas' or sync errors. + @returns + string, the text that the player just said to this talk npc + ]] + Listen = function() end, + + --[[@method + Ends the talk beetwen the npc and the player, calling the appropiate npc functions. + @params + [msg] string, an optional message to say + ]] + End = function(msg) end, + + --[[@method + Says something to the talk player + @params + msg string, the message + ]] + Say = function(msg) end, + + --[[@method + + @params + ]] + Ask = function(msg) end, + + --[[@method + + @params + ]] + AskAgreement = function(msg, fnYes, fnNo) end, + + --[[@method + + @params + ]] + Response = function(fn) end, + + --[[@method + + @params + ]] + BadResponse = function(fn) end, + + --[[@method + + @params + ]] + ReplySay = function(msg) end, + + --[[@method + + @params + ]] + ReplyEndTalk = function(msg) end, + + --[[@method + + @params + ]] + GoUp = function(times, restream) end +} +AI.TalkTopic = {} + +function AI.Talk:New(npc, cid) + local talk = {} + setmetatable(talk, {__index = AI.Talk}) + + talk.npc = npc + talk.player = cid + talk.topicStack = AI.Stack:New() + talk.varPool = {} + + return talk +end + +function AI.Talk:Var(key, value) + if type(value) == "nil" then + return self.varPool[key] + end + + self.varPool[key] = value + return value +end + +function AI.Talk:Process(event) + if self.topicStack:IsEmpty() then + return false + end + + local msg, topic = event.msg, self.topicStack:Top() + local match = AI.GetMatch(msg, topic.keywords, self.npc.TalkCaseSensitive) + + -- If we can't handle the current player speech, we try to find + -- a handler in a previous topic, going from current topic + -- to a maximun of npc.GoUpTimes previous topics. WE DONT + -- WANT TO DELETE TOPICS WHILE WE FIND AN APPROPIATE HANDLER, + -- as if no handler is found, we simply ignore the player and + -- wait for an appropiate response ON THE CURRENT TOPIC, or + -- just auto ungreet him on timeout + if not match and table.size(topic.badResponses) == 0 and self.npc.GoUpTimes then + local size = self.topicStack:Size() + local minimum = size - (type(self.npcGoUpTimes) == "boolean" and size or self.npc.GoUpTimes) + + -- All loop expresions are evaluated once + for i = size - 1, minimum < 1 and 1 or minimum, -1 do + match = AI.GetMatch(msg, self.topicStack[i].keywords, self.npc.TalkCaseSensitive) + if match then -- Awesome! Player is tricking us... + for k = size - i, 1, -1 do + self.topicStack:Pop() -- Delete topics + end + + topic = self.topicStack:Top() + break + end + end + end + + local prevTopicSize = self.topicStack:Size() + local responses = match and topic.responses.positive or topic.responses.negative + + local continueTalk + for _, callback in ipairs(responses) do + if continueTalk then + callback(self, match) + else + continueTalk = callback(self, match) + end + + -- user ended the talk + if not self.topicStack then + return true -- is this correct? Response: user did handle it, because he called Talk:End(). Yep, it is correct. + end + + if type(continueTalk) == "boolean" and not continueTalk then + break + end + end + + -- ~= is the way to go, user might add new topics or self:GoUp() in the topick stack + if continueTalk == nil and prevTopicSize ~= self.topicStack:Size() then + return true + end + + return continueTalk and true or false +end + +function AI.Talk:Reset() + self.topicStack = AI.Stack:New() + self.varPool = {} +end + +function AI.Talk:Listen() + local self, event = coroutine.yield(true) + return event.msg +end + +function AI.Talk:End(msg) + self.topicStack = nil + if msg then + self.npc:CustomUngreet(self.player, msg) + else + self.npc:Ungreet(self.player, AI.SocialNpc.TalkEndReason.PlayerRequested) + end +end + +function AI.Talk:Say(msg) + self.npc:SayTo(msg, self.player) +end + +function AI.Talk:Ask(msg) + local topic = self:NewTopic() + topic.message = msg + topic.keywords = AI.ExtractKeywords(msg) + self:Say(msg) +end + +function AI.Talk:AskAgreement(msg, yesCallback, noCallback) + local topic = self:NewTopic() + topic.message = msg + topic.keywords = self.npc.AgreementKeywords + + self:ResponseInternal(yesCallback, true) + self:ResponseInternal(noCallback, false) + self:Say(msg) +end + +function AI.Talk:Response(callback) + self:ResponseInternal(callback, true) +end + +function AI.Talk:BadResponse(callback) + self:ResponseInternal(callback, false) +end + +function AI.Talk:ResponseInternal(callback, boolean) + local fn = callback + if type(callback) == "string" then + fn = self:ReplySay(fn) + elseif type(callback) == "boolean" and callback == false then + fn = self:ReplyEndTalk() + elseif type(callback) ~= "function" then + error("AI.Talk:ResponseInternal() Parameter 1 must be a string, a false boolean, or a function") + end + + self:GetTopic():PushResponse(fn, boolean) +end + +function AI.Talk:ReplySay(msg) + return function(talk) + return talk:Say(msg) + end +end + +function AI.Talk:ReplyEndTalk(msg) + return function(talk) + talk:End(msg) + end +end + +function AI.Talk:GoUp(times, announce) + while(times > 0 and not self.topicStack:IsEmpty()) do + times = times - 1 + self.topicStack:Pop() + end + + assert(times == 0, "AI.Talk:GoUp() Stack underflow") + if announce then + self:Say(self.topicStack:Top().message) + end +end + +function AI.Talk:GetTopic() + if self.topicStack:IsEmpty() then + return self:NewTopic() + end + + return self.topicStack:Top() +end + +function AI.Talk:NewTopic() + self.topicStack:Push(AI.TalkTopic:New(self)) + return self.topicStack:Top() +end + +function AI.TalkTopic:New(talk) + local topic = {} + setmetatable(topic, {__index = AI.TalkTopic}) + + topic.talk = talk + topic.responses = { + positive = {}, + negative = {} + } + + return topic +end + +function AI.TalkTopic:PushResponse(fn, good) + if good then + table.insert(self.responses.positive, fn) + else + table.insert(self.responses.negative, fn) + end +end \ No newline at end of file diff --git a/data/npc/lib/_AI.lua b/data/npc/lib/_AI.lua new file mode 100644 index 0000000..ec57f46 --- /dev/null +++ b/data/npc/lib/_AI.lua @@ -0,0 +1,303 @@ +if _G['AI'] ~= nil then + error("_AI.lua: Name colision, there's another 'AI' symbol at the global scope, library will not be loaded.") + return false +end + +--[[ +@namespace + AI +@description + This table serves as a namespace for all the classes and utility functions in the package. +]] +local AI = { + --[[@function + Creates a new object setting the __index metamethod of its metatable to the given table + @params + class table, the class + ]] + CreateInstance = function(class) end, + + -- Inherit class B from class A, works wirh CreateInstance + -- static + --[[@function + Inherit classB from classA (effectively changing classA metatable), works well with CreateInstance, + calling all the constructors of the class hierarchy from top to bottom + @params + classA table, the parent class + classB table, the derived class + ]] + Inherit = function(classA, classB) end, + + --[[@function + Returns an unique integer number that will VARY EVERY TIME YOU RUN THE SERVER + @returns + number, the unique integer + ]] + UniqueId = function() end, + + --[[@function + Returns the numeric index of the first ocurrence of value in table, or nil if no ocurrences were found + @params + table table, the table to search into + value mixed, the value to be searched + @returns + number, the index of the first ocurrence of value in table, if found + nil, if the value isn't found + ]] + TableFind = function(table, value) end, + + --[[@function + Extracs the keywords ({} enclosed strings like {quest}) from msg and returns them as a number indexed table. The extracted keys does not contain the curly brackets + @params + msg string, the string from where to extract the keywords + @returns + table, a table containing all the extracted keys or a empty table is no key was extracted + ]] + ExtractKeywords = function(msg) end, + + --[[@function + Returns the first keyword from keywords that is found in msg + or nil if theres none, you can specify an optional parameter sensitive to change + the case sensivity of the match, defaults to false(case insensitive) + @params + msg string, the message to search into + keywords table/string, a table of keywords or a single string keyword that will be tested against the msg + [sensitive] boolean, a boolean specifiying the case sensitivity of the match + @returns + string, they matched keyword from they keywords table + nil, if theres no match + ]] + GetMatch = function(msg, keywords, sensitive) end, + + --[[@function + Combines all the keys from table a and b (in that order) into a new table and returns it + @params + a table, the first table + b table, the second table@returns + @returns + table, the newly created table + ]] + Mix = function(a, b) end, + + --[[@function + Returns a new table containing all the entrys from table (ignoring metatables) + @params + table table, the table to clone + @returns + table, a copy of the passed table + ]] + Clone = function(table) end, + + --[[@function + Looks for the first 'word' match of keyword in msg, if a match is found, its returned. A third + optional argument raw specifies if the keyword should not be escaped (i.e using AI.EscapePattern(keyword)), + by default it is. + @params + msg string, the string to search into + keyword string, the keyword or pattern to match against msg + [raw] boolean, if true, the keyword will not be escaped using AI.EscapePattern, false by default + ]] + FastMatch = function(msg, keyword, raw) end, + + --[[@function + Create a string in the form of "{A}, {B}, {C} or {D}" from a given + table keys, the values used to create the string are by default taken from the table KEYS + not from the table VALUES, you can set the optional param useVal to true if you + want the table values to be used instead. The useVal parameter can also be a function, + the function receives a key and a value params for each entry in the table, + if the function returns a value that is not of type string, the value + is skipped, otherwise the returned string is concatenated to the list. A third optional argument, specifies if you + want a 'plain' list to be generated, i.e no bracket {} enclosing in the resulting string. + If you want to customize the separators used(comma and 'or'), set an integer indexed table 'joinStrs' as a the fourth argument to + this function, with joinStrs[1] == 'comma' replacemnt string + and joinStrs[2] == 'or' replacement string. + @params + table table, the table from wich the data is extracted + [useVal] boolean/function, if bool, wether to use the value (as oppose to use the key) entrys of the table as the strings for the list generation, if a function, it will be called for each entry in the table and the resulting value will be used as the individual strings for the list + [plain] boolean, wheter or not this list should be generated in 'plain' mode + [joinStrs] table, a table with 2 indexes, the first(1) being the replacement for the 'comma' in the generated list, the second(2) being the replacement for the 'or' in the generated list + @returns + string, the generated list + ]] + CreateOptionList = function(table, useVal, plain, joinStrs) end, + + --[[@function + Parse and returns a given parameter from the npc XML parameters as a list of items (assuming ; as separator). + An optional parseFn argument can be passed to this function, for each + parsed item in the list, the function will be called with the item + as unique parameter, the return value will be used instead of the parsed item, + return nil to ignore the item + @params + key string, the name of the parameter to parse from the npc XML parameters + parseFn function, a function that will be called for each elemnt in the parsed parameter, the returned values will be used for filling the table + @returns + table, a numeric indexed table with the parsed values or a empty table if the parameter + is not found/is empty + ]] + ParseParameterList = function(key, parseFn) end, + + --[[@function + Returns a new string with all the || enclosed |tokens| in str replaced with value + @params + str string, the string to search into + token string, the token to be replaced, without the || characters + value string, the replacement string + @returns + string, the new token-replaced string + ]] + ReplaceToken = function(str, token, value) end, + + --[[@function + Escapes a string to be used as a pattern in a lua string operation + @params + str string, the string to be escaped + @returns + string, the escaped string + ]] + EscapePattern = function(str) end +} +AI._idCounter = 0 + +function AI.CreateInstance(class) + local newInst = {} + if class._parentClass then + newInst = class._parentClass:New() + end + + setmetatable(newInst, {__index = class}) + return newInst +end + +function AI.Inherit(classA, classB) + classB._parentClass = classA + setmetatable(classB, {__index = classA}) +end + +function AI.UniqueId() + AI._idCounter = AI._idCounter + 1 + return AI._idCounter +end + +function AI.TableFind(table, value) + for i, v in pairs(table) do + if(v == value) then + return i + end + end + + return nil +end + +function AI.ExtractKeywords(msg) + local ret = {} + for k in string.gmatch(msg, "{([^}]+)") do + table.insert(ret, k) + end + + return ret +end + +function AI.EscapePattern(str) + return str:gsub("[(%^)(%$)(%()(%))(%%)(%.)(%[)(%])(%*)(%+)(%-)(%?)]", "%%%1") +end + +function AI.GetMatch(msg, keywords, sensitive) + local m = msg + if not sensitive then + m = msg:lower() + end + + if type(keywords) == "string" then + keywords = {keywords} + end + + for _, k in pairs(keywords) do + local match = AI.FastMatch(m, sensitive and k or k:lower()) + if match then + return k + end + end +end + +function AI.FastMatch(msg, keyword, raw) + return msg:match("%f[%w]" .. (raw and keyword or AI.EscapePattern(keyword)) .. "%f[%W]") +end + +function AI.Clone(a) + local ret = {} + for k,v in pairs(a) do + ret[k] = v + end + + return ret +end + +function AI.Mix(a, b) + local ret = {} + for k, v in pairs(a) + do ret[k] = v + end + + for k, v in pairs(b) do + ret[k] = v + end + + return ret +end + +function AI.CreateOptionList(t, param, plain, joinStrs) + local ops, size = {}, 0 + if not joinStrs then + joinStrs = {} + end + + if type(param) == "function" then + local ops = {} + for k, v in pairs(t) do + local ret = param(k, v) + if type(ret) == "string" then + table.insert(ops, ret) + size = size + 1 + end + end + + param = true + t = ops + else + for k,v in pairs(t) do + size = size + 1 + end + end + + local str, counter = "", 1 + for k, v in pairs(t) do + str = str .. (plain and (param and v or k) or "{" .. (param and v or k) .. "}") + if counter < size - 1 then + str = str .. (joinStrs[1] and joinStrs[1] or ",") .. " " + elseif counter == size - 1 then + str = str .. (joinStrs[2] and joinStrs[2] or " or ") + end + end + + return str +end + +function AI.ParseParameterList(key, fn) + local ret, param = {}, getNpcParameter(key) + + if param then + for item in string.gmatch(param, "[^;]+") do + table.insert(ret, fn and fn(item) or item) + end + end + + return ret +end + +function AI.ReplaceToken(str, token, value) + return str:gsub("|" .. AI.EscapePattern(token) .. "|", value) +end + + +_G['AI'] = AI +dodirectory(getDataDir() .. 'npc/lib/AI') \ No newline at end of file diff --git a/data/npc/lib/npcsystem/npcsystem.lua b/data/npc/lib/_npcsystem.lua similarity index 93% rename from data/npc/lib/npcsystem/npcsystem.lua rename to data/npc/lib/_npcsystem.lua index 6b63d3d..3315696 100644 --- a/data/npc/lib/npcsystem/npcsystem.lua +++ b/data/npc/lib/_npcsystem.lua @@ -1,12 +1,9 @@ -- Advanced NPC System (Created by Jiddo), --- Modified by Talaturen. +-- Modified by TheForgottenServer Team. if(NpcSystem == nil) then -- Loads the underlying classes of the npcsystem. - dofile(getDataDir() .. 'npc/lib/npcsystem/keywordhandler.lua') - dofile(getDataDir() .. 'npc/lib/npcsystem/queue.lua') - dofile(getDataDir() .. 'npc/lib/npcsystem/npchandler.lua') - dofile(getDataDir() .. 'npc/lib/npcsystem/modules.lua') + dodirectory(getDataDir() .. 'npc/lib/npcsystem') -- Global npc constants: @@ -14,8 +11,8 @@ if(NpcSystem == nil) then KEYWORD_BEHAVIOR = BEHAVIOR_NORMAL_EXTENDED -- Greeting and unGreeting keywords. For more information look at the top of modules.lua - FOCUS_GREETWORDS = {'hi', 'hello', 'hey'} - FOCUS_FAREWELLWORDS = {'bye', 'farewell', 'cya'} + FOCUS_GREETWORDS = {'hi', 'hello'} + FOCUS_FAREWELLWORDS = {'bye', 'farewell'} -- The word for requesting trade window. For more information look at the top of modules.lua SHOP_TRADEREQUEST = {'offer', 'trade'} diff --git a/data/npc/lib/npc.lua b/data/npc/lib/npc.lua index 9609f00..d9754cc 100644 --- a/data/npc/lib/npc.lua +++ b/data/npc/lib/npc.lua @@ -1,84 +1,92 @@ --- Include the Advanced NPC System -dofile(getDataDir() .. 'npc/lib/npcsystem/npcsystem.lua') - -function selfIdle() - following = false - attacking = false - - selfAttackCreature(0) - target = 0 -end - function selfSayChannel(cid, message) return selfSay(message, cid, false) end -function selfMoveToCreature(id) - if(not id or id == 0) then - return - end +function selfMoveToThing(id) + errors(false) + local thing = getThing(id) - local t = getCreaturePosition(id) - if(not t.x or t.x == nil) then + errors(true) + if(thing.uid == 0) then return end + local t = getThingPosition(id) selfMoveTo(t.x, t.y, t.z) return end -function getNpcDistanceToCreature(id) - if(not id or id == 0) then - selfIdle() +function selfMoveTo(x, y, z) + local position = {x = 0, y = 0, z = 0} + if(type(x) ~= "table") then + position = Position(x, y, z) + else + position = x + end + + if(isValidPosition(position)) then + doSteerCreature(getNpcId(), position) + end +end + +function selfMove(direction, flags) + local flags = flags or 0 + doMoveCreature(getNpcId(), direction, flags) +end + +function selfTurn(direction) + doCreatureSetLookDirection(getNpcId(), direction) +end + +function getNpcDistanceTo(id) + errors(false) + local thing = getThing(id) + + errors(true) + if(thing.uid == 0) then return nil end local c = getCreaturePosition(id) - if(not c.x or c.x == 0) then + if(not isValidPosition(c)) then return nil end local s = getCreaturePosition(getNpcId()) - if(not s.x or s.x == 0 or s.z ~= c.z) then + if(not isValidPosition(s) or s.z ~= c.z) then return nil end return math.max(math.abs(s.x - c.x), math.abs(s.y - c.y)) end -function doMessageCheck(message, keyword) +function doMessageCheck(message, keyword, exact) + local exact = exact or false if(type(keyword) == "table") then - return table.isStrIn(keyword, message) + return isInArray(keyword, message, exact) end - local a, b = message:lower():find(keyword:lower()) - if(a ~= nil and b ~= nil) then - return true + if(exact) then + return message == keyword end - return false + local a, b = message:lower(), keyword:lower() + return a == b or (a:find(b) and not a:find('(%w+)' .. b)) end function doNpcSellItem(cid, itemid, amount, subType, ignoreCap, inBackpacks, backpack) - local amount = amount or 1 - local subType = subType or 1 - local ignoreCap = ignoreCap and true or false + local amount, subType, ignoreCap, inBackpacks, backpack = amount or 1, subType or 0, ignoreCap or false, inBackpacks or false, backpack or 1988 - local item = 0 - if(isItemStackable(itemid)) then - item = doCreateItemEx(itemid, amount) - if(doPlayerAddItemEx(cid, item, ignoreCap) ~= RETURNVALUE_NOERROR) then - return 0, 0 + local item, a = nil, 0 + if(inBackpacks) then + local custom, stackable = 1, isItemStackable(itemid) + if(stackable) then + custom = math.max(1, subType) + subType = 100 end - return amount, 0 - end - - local a = 0 - if(inBackpacks) then - local container = doCreateItemEx(backpack, 1) - local b = 1 - for i = 1, amount do + local container, b = doCreateItemEx(backpack, 1), 1 + for i = 1, amount * custom do item = doAddContainerItem(container, itemid, subType) if(itemid == ITEM_PARCEL) then doAddContainerItem(item, ITEM_LABEL) @@ -98,7 +106,25 @@ function doNpcSellItem(cid, itemid, amount, subType, ignoreCap, inBackpacks, bac end end - return a, b + if(not stackable) then + return a, b + end + + return (a * subType / custom), b + end + + if(isItemStackable(itemid)) then + a = amount * math.max(1, subType) + repeat + local tmp = math.min(100, a) + item = doCreateItemEx(itemid, tmp) + if(doPlayerAddItemEx(cid, item, ignoreCap) ~= RETURNVALUE_NOERROR) then + return 0, 0 + end + + a = a - tmp + until a == 0 + return amount, 0 end for i = 1, amount do @@ -117,38 +143,41 @@ function doNpcSellItem(cid, itemid, amount, subType, ignoreCap, inBackpacks, bac return a, 0 end -function doRemoveItemIdFromPos (id, n, position) - local thing = getThingFromPos({x = position.x, y = position.y, z = position.z, stackpos = 1}) - if(thing.itemid == id) then - doRemoveItem(thing.uid, n) - return true +function doRemoveItemIdFromPosition(id, n, position) + local thing = getTileItemById(position, id) + if(thing.itemid < 101) then + return false end - return false + doRemoveItem(thing.uid, n) + return true end function getNpcName() return getCreatureName(getNpcId()) end -function getNpcPos() - return getCreaturePosition(getNpcId()) +function getNpcPosition() + return getThingPosition(getNpcId()) end function selfGetPosition() - local t = getNpcPos() + local t = getThingPosition(getNpcId()) return t.x, t.y, t.z end msgcontains = doMessageCheck moveToPosition = selfMoveTo -moveToCreature = selfMoveToCreature +moveToCreature = selfMoveToThing +selfMoveToCreature = selfMoveToThing selfMoveToPosition = selfMoveTo -selfGotoIdle = selfIdle isPlayerPremiumCallback = isPremium -doPosRemoveItem = doRemoveItemIdFromPos +doPosRemoveItem = doRemoveItemIdFromPosition +doRemoveItemIdFromPos = doRemoveItemIdFromPosition doNpcBuyItem = doPlayerRemoveItem doNpcSetCreatureFocus = selfFocus getNpcCid = getNpcId getDistanceTo = getNpcDistanceTo -getDistanceToCreature = getNpcDistanceToCreature +getDistanceToCreature = getNpcDistanceTo +getNpcDistanceToCreature = getNpcDistanceTo +getNpcPos = getNpcPosition diff --git a/data/npc/lib/npcsystem/keywordhandler.lua b/data/npc/lib/npcsystem/keywordhandler.lua index 8e62edf..1eac11b 100644 --- a/data/npc/lib/npcsystem/keywordhandler.lua +++ b/data/npc/lib/npcsystem/keywordhandler.lua @@ -1,5 +1,5 @@ -- Advanced NPC System (Created by Jiddo), --- Modified by Talaturen. +-- Modified by TheForgottenServer Team. if(KeywordHandler == nil) then BEHAVIOR_SIMPLE = 1 -- Does not support nested keywords. If you choose this setting you must use a variable such as 'talkState' to keep track of how to handle keywords. @@ -30,6 +30,7 @@ if(KeywordHandler == nil) then obj.callback = func obj.parameters = param obj.children = {} + setmetatable(obj, self) self.__index = self return obj @@ -42,20 +43,20 @@ if(KeywordHandler == nil) then -- Returns true if message contains all patterns/strings found in keywords. function KeywordNode:checkMessage(message) - local ret = true if(self.keywords.callback ~= nil) then return self.keywords.callback(self.keywords, message) end + for i,v in ipairs(self.keywords) do if(type(v) == 'string') then local a, b = string.find(message, v) if(a == nil or b == nil) then - ret = false - break + return false end end end - return ret + + return true end -- Returns the parent of this node or nil if no such node exists. @@ -88,31 +89,36 @@ if(KeywordHandler == nil) then KeywordHandler = { root = nil, - lastNode = nil + lastNode = {} } -- Creates a new keywordhandler with an empty rootnode. function KeywordHandler:new() local obj = {} obj.root = KeywordNode:new(nil, nil, nil) + setmetatable(obj, self) self.__index = self return obj end -- Resets the lastNode field, and this resetting the current position in the node hierarchy to root. - function KeywordHandler:reset() - self.lastNode = nil + function KeywordHandler:reset(cid) + --table.remove(self.lastNode, cid) + if(self.lastNode[cid]) then + self.lastNode[cid] = nil + end end -- Makes sure the correct childNode of lastNode gets a chance to process the message. -- The behavior of this function depends much on the KEYWORD_BEHAVIOR. function KeywordHandler:processMessage(cid, message) - local node = self:getLastNode() + local node = self:getLastNode(cid) if(node == nil) then error('No root node found.') return false end + if(KEYWORD_BEHAVIOR == BEHAVIOR_SIMPLE) then local ret = self:processNodeMessage(node, cid, message) if(ret) then @@ -123,6 +129,7 @@ if(KeywordHandler == nil) then if(ret) then return true end + if(KEYWORD_BEHAVIOR == BEHAVIOR_NORMAL_EXTENDED and node:getParent()) then node = node:getParent() -- Search through the parent. local ret = self:processNodeMessage(node, cid, message) @@ -130,6 +137,7 @@ if(KeywordHandler == nil) then return true end end + if(node ~= self:getRoot()) then node = self:getRoot() -- Search through the root. local ret = self:processNodeMessage(node, cid, message) @@ -138,7 +146,7 @@ if(KeywordHandler == nil) then end end elseif(KEYWORD_BEHAVIOR == BEHAVIOR_COMPLEX) then - while true do + repeat local ret = self:processNodeMessage(node, cid, message) if(ret) then return true @@ -146,13 +154,12 @@ if(KeywordHandler == nil) then if(node:getParent() ~= nil) then node = node:getParent() -- Move one step upwards in the hierarchy. - else - break end - end + until node ~= nil else error('Unknown keyword behavior.') end + return false end @@ -162,16 +169,18 @@ if(KeywordHandler == nil) then local messageLower = string.lower(message) for i, childNode in pairs(node.children) do if(childNode:checkMessage(messageLower)) then - local oldLast = self.lastNode - self.lastNode = childNode + local oldLast = self.lastNode[cid] + self.lastNode[cid] = childNode + childNode.parent = node -- Make sure node is the parent of childNode (as one node can be parent to several nodes). if(childNode:processMessage(cid, message)) then return true - else - self.lastNode = oldLast end + + self.lastNode[cid] = oldLast end end + return false end @@ -181,12 +190,12 @@ if(KeywordHandler == nil) then end -- Returns the last processed keywordnode or root if no last node is found. - function KeywordHandler:getLastNode() + function KeywordHandler:getLastNode(cid) if(KEYWORD_BEHAVIOR == BEHAVIOR_SIMPLE) then return self:getRoot() - else - return self.lastNode or self:getRoot() end + + return self.lastNode[cid] or self:getRoot() end -- Adds a new keyword to the root keywordnode. Returns the new node. @@ -201,13 +210,15 @@ if(KeywordHandler == nil) then if(steps == nil) then steps = 1 end - for i = 1, steps,1 do - if(self.lastNode == nil) then + + for i = 1, steps, 1 do + if(self.lastNode[cid] == nil) then break - else - self.lastNode = self.lastNode:getParent() or self:getRoot() end + + self.lastNode[cid] = self.lastNode[cid]:getParent() or self:getRoot() end - return self.lastNode + + return self.lastNode[cid] end end diff --git a/data/npc/lib/npcsystem/modules.lua b/data/npc/lib/npcsystem/modules.lua index 3648005..dba27a0 100644 --- a/data/npc/lib/npcsystem/modules.lua +++ b/data/npc/lib/npcsystem/modules.lua @@ -1,6 +1,5 @@ -- Advanced NPC System (Created by Jiddo), --- Modified by Talaturen. --- Modified by Elf. +-- Modified by TheForgottenServer Team. if(Modules == nil) then -- Constants used to separate buying from selling. @@ -39,7 +38,8 @@ if(Modules == nil) then function StdModule.say(cid, message, keywords, parameters, node) local npcHandler = parameters.npcHandler if(npcHandler == nil) then - error('StdModule.say called without any npcHandler instance.') + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'StdModule.say - Call without any npcHandler instance.') + return false end local onlyFocus = (parameters.onlyFocus == nil or parameters.onlyFocus == true) @@ -49,9 +49,9 @@ if(Modules == nil) then local parseInfo = {[TAG_PLAYERNAME] = getCreatureName(cid)} npcHandler:say(npcHandler:parseMessage(parameters.text or parameters.message, parseInfo), cid, parameters.publicize and true) - if(parameters.reset == true) then - npcHandler:resetNpc() - elseif(parameters.moveup ~= nil and type(parameters.moveup) == 'number') then + if(parameters.reset) then + npcHandler:resetNpc(cid) + elseif(parameters.moveup and type(parameters.moveup) == 'number') then npcHandler.keywordHandler:moveUp(parameters.moveup) end @@ -65,14 +65,15 @@ if(Modules == nil) then function StdModule.promotePlayer(cid, message, keywords, parameters, node) local npcHandler = parameters.npcHandler if(npcHandler == nil) then - error('StdModule.promotePlayer called without any npcHandler instance.') + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'StdModule.promotePlayer - Call without any npcHandler instance.') + return false end if(not npcHandler:isFocused(cid)) then return false end - if(isPlayerPremiumCallback(cid) or not getBooleanFromString(getConfigValue('premiumForPromotion')) or not(parameters.premium)) then + if(isPremium(cid) or not getBooleanFromString(getConfigValue('premiumForPromotion'))) then if(getPlayerPromotionLevel(cid) >= parameters.promotion) then npcHandler:say('You are already promoted!', cid) elseif(getPlayerLevel(cid) < parameters.level) then @@ -80,28 +81,29 @@ if(Modules == nil) then elseif(not doPlayerRemoveMoney(cid, parameters.cost)) then npcHandler:say('You do not have enough money!', cid) else - setPlayerPromotionLevel(cid, parameters.promotion) + doPlayerSetPromotionLevel(cid, parameters.promotion) npcHandler:say(parameters.text, cid) end else npcHandler:say("You need a premium account in order to get promoted.", cid) end - npcHandler:resetNpc() + npcHandler:resetNpc(cid) return true end function StdModule.learnSpell(cid, message, keywords, parameters, node) local npcHandler = parameters.npcHandler if(npcHandler == nil) then - error('StdModule.learnSpell called without any npcHandler instance.') + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'StdModule.learnSpell - Call without any npcHandler instance.') + return false end if(not npcHandler:isFocused(cid)) then return false end - if(isPlayerPremiumCallback(cid) or not(parameters.premium)) then + if(isPremium(cid) or not(parameters.premium)) then if(getPlayerLearnedInstantSpell(cid, parameters.spellName)) then npcHandler:say('You already know this spell.', cid) elseif(getPlayerLevel(cid) < parameters.level) then @@ -118,46 +120,76 @@ if(Modules == nil) then npcHandler:say('You need a premium account in order to buy ' .. parameters.spellName .. '.', cid) end - npcHandler:resetNpc() + npcHandler:resetNpc(cid) return true end function StdModule.bless(cid, message, keywords, parameters, node) local npcHandler = parameters.npcHandler if(npcHandler == nil) then - error('StdModule.bless called without any npcHandler instance.') + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'StdModule.bless - Call without any npcHandler instance.') + return false + end + + if(not getBooleanFromString(getConfigValue('blessings'))) then + npcHandler:say("Sorry, but Gods moved back my permission to bless anyone.", cid) + return false end if(not npcHandler:isFocused(cid)) then return false end - if(isPlayerPremiumCallback(cid) or not getBooleanFromString(getConfigValue('blessingsOnlyPremium')) or not parameters.premium) then + if(isPremium(cid) or not getBooleanFromString(getConfigValue('blessingsOnlyPremium')) or not parameters.premium) then local price = parameters.baseCost if(getPlayerLevel(cid) > parameters.startLevel) then price = (price + ((math.min(parameters.endLevel, getPlayerLevel(cid)) - parameters.startLevel) * parameters.levelCost)) end - if(getPlayerBlessing(cid, parameters.number)) then - npcHandler:say("Gods have already blessed you with this blessing!", cid) - elseif(not doPlayerRemoveMoney(cid, price)) then - npcHandler:say("You don't have enough money for blessing.", cid) + if(parameters.number > 0) then + if(getPlayerBlessing(cid, parameters.number)) then + npcHandler:say("Gods have already blessed you with this blessing!", cid) + elseif(not doPlayerRemoveMoney(cid, price)) then + npcHandler:say("You don't have enough money for blessing.", cid) + else + npcHandler:say("You have been blessed by one of the five gods!", cid) + doPlayerAddBlessing(cid, parameters.number) + end else - npcHandler:say("You have been blessed by one of the five gods!", cid) - doPlayerAddBlessing(cid, parameters.number) + if(getPlayerPVPBlessing(cid)) then + npcHandler:say("Gods have already blessed you with this blessing!", cid) + elseif(not doPlayerRemoveMoney(cid, price)) then + npcHandler:say("You don't have enough money for blessing.", cid) + else + local any = false + for i = 1, 5 do + if(getPlayerBlessing(cid, i)) then + any = true + break + end + end + + if(any) then + npcHandler:say("You have been blessed by the god of war!", cid) + doPlayerSetPVPBlessing(cid) + else + npcHandler:say("You need to be blessed by at least one god to get this blessing.", cid) + end + end end else npcHandler:say('You need a premium account in order to be blessed.', cid) end - npcHandler:resetNpc() + npcHandler:resetNpc(cid) return true end function StdModule.travel(cid, message, keywords, parameters, node) local npcHandler = parameters.npcHandler if(npcHandler == nil) then - error('StdModule.travel called without any npcHandler instance.') + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'StdModule.travel - Call without any npcHandler instance.') + return false end if(not npcHandler:isFocused(cid)) then @@ -165,25 +197,25 @@ if(Modules == nil) then end local storage, pzLocked = parameters.storageValue or (EMPTY_STORAGE + 1), parameters.allowLocked or false - if(parameters.premium and not isPlayerPremiumCallback(cid)) then - npcHandler:say('I can only allow premium players to travel with me.', cid) + if(parameters.premium and not isPremium(cid)) then + npcHandler:say('I\'m sorry, but you need a premium account in order to travel onboard our ships.', cid) elseif(parameters.level ~= nil and getPlayerLevel(cid) < parameters.level) then npcHandler:say('You must reach level ' .. parameters.level .. ' before I can let you go there.', cid) elseif(parameters.storageId ~= nil and getPlayerStorageValue(cid, parameters.storageId) < storage) then - npcHandler:say(parameters.storageInfo or 'You may not travel there!', cid) + npcHandler:say(parameters.storageInfo or 'You may not travel there yet!', cid) elseif(not pzLocked and isPlayerPzLocked(cid)) then - npcHandler:say('Get out of there with this blood!', cid) + npcHandler:say('First get rid of those blood stains! You are not going to ruin my vehicle!', cid) elseif(not doPlayerRemoveMoney(cid, parameters.cost)) then - npcHandler:say('You do not have enough money.', cid) + npcHandler:say('You don\'t have enough money.', cid) else - npcHandler:say('It was a pleasure doing business with you.', cid) + npcHandler:say('Set the sails!', cid) npcHandler:releaseFocus(cid) doTeleportThing(cid, parameters.destination, false) doSendMagicEffect(parameters.destination, CONST_ME_TELEPORT) end - npcHandler:resetNpc() + npcHandler:resetNpc(cid) return true end @@ -205,6 +237,7 @@ if(Modules == nil) then for i, word in pairs(FOCUS_GREETWORDS) do local obj = {} table.insert(obj, word) + obj.callback = FOCUS_GREETWORDS.callback or FocusModule.messageMatcher handler.keywordHandler:addKeyword(obj, FocusModule.onGreet, {module = self}) end @@ -212,6 +245,7 @@ if(Modules == nil) then for i, word in pairs(FOCUS_FAREWELLWORDS) do local obj = {} table.insert(obj, word) + obj.callback = FOCUS_FAREWELLWORDS.callback or FocusModule.messageMatcher handler.keywordHandler:addKeyword(obj, FocusModule.onFarewell, {module = self}) end @@ -230,6 +264,7 @@ if(Modules == nil) then end parameters.module.npcHandler:onFarewell(cid) + parameters.module.npcHandler:resetNpc(cid) return true end @@ -298,10 +333,10 @@ if(Modules == nil) then if(reply ~= nil) then self:addKeyword(keywords, reply) else - print('[Warning] NpcSystem:', 'Parameter \'' .. 'keyword_reply' .. n .. '\' missing. Skipping...') + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Parameter \'' .. 'keyword_reply' .. n .. '\' missing. Skipping...') end else - print('[Warning] NpcSystem:', 'No keywords found for keyword set #' .. n .. '. Skipping...') + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'No keywords found for keyword set #' .. n .. '. Skipping...') end n = n + 1 @@ -364,7 +399,7 @@ if(Modules == nil) then elseif(i == 6) then premium = getBooleanFromString(tmp) else - print('[Warning] NpcSystem:', 'Unknown parameter found in travel destination parameter.', tmp, destination) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Unknown parameter found in travel destination parameter.', tmp, destination) end i = i + 1 @@ -373,7 +408,7 @@ if(Modules == nil) then if(name ~= nil and pos.x ~= nil and pos.y ~= nil and pos.z ~= nil and cost ~= nil) then self:addDestination(name, pos, cost, premium) else - print('[Warning] NpcSystem:', 'Parameter(s) missing for travel destination:', name, pos, cost, premium) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Parameter(s) missing for travel destination:', name, pos, cost, premium) end end end @@ -387,11 +422,11 @@ if(Modules == nil) then module = self } - local keywords, bringwords = {}, {} + local keywords, bringWords = {}, {} table.insert(keywords, name) - table.insert(bringwords, 'bring me to ' .. name) - self.npcHandler.keywordHandler:addKeyword(bringwords, TravelModule.bring, parameters) + table.insert(bringWords, 'bring me to ' .. name) + self.npcHandler.keywordHandler:addKeyword(bringWords, TravelModule.bring, parameters) local node = self.npcHandler.keywordHandler:addKeyword(keywords, TravelModule.travel, parameters) node:addChildKeywordNode(self.yesNode) @@ -415,25 +450,25 @@ if(Modules == nil) then end local parent = node:getParent():getParameters() - if(isPlayerPremiumCallback(cid) or not parent.premium) then + if(isPremium(cid) or not parent.premium) then if(not isPlayerPzLocked(cid)) then if(doPlayerRemoveMoney(cid, parent.cost)) then - module.npcHandler:say('It was a pleasure doing business with you.', cid) + module.npcHandler:say('Set the sails!', cid) module.npcHandler:releaseFocus(cid) doTeleportThing(cid, parent.destination, true) doSendMagicEffect(parent.destination, CONST_ME_TELEPORT) else - module.npcHandler:say('You do not have enough money.', cid) + module.npcHandler:say('You don\'t have enough money.', cid) end else - module.npcHandler:say('Get out of there with this blood!', cid) + module.npcHandler:say('First get rid of those blood stains! You are not going to ruin my vehicle!', cid) end else - modulenpcHandler:say('I can only allow premium players to travel there.', cid) + module.npcHandler:say('I\'m sorry, but you need a premium account in order to travel onboard our ships.', cid) end - module.npcHandler:resetNpc() + module.npcHandler:resetNpc(cid) return true end @@ -445,7 +480,7 @@ if(Modules == nil) then end module.npcHandler:say(module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_DECLINE), {[TAG_PLAYERNAME] = getCreatureName(cid)}), cid) - module.npcHandler:resetNpc() + module.npcHandler:resetNpc(cid) return true end @@ -455,8 +490,8 @@ if(Modules == nil) then return false end - if((isPlayerPremiumCallback(cid) or not parameters.premium) and not isPlayerPzLocked(cid) and doPlayerRemoveMoney(cid, parameters.cost)) then - module.npcHandler:say('Sure!', cid) + if((isPremium(cid) or not parameters.premium) and not isPlayerPzLocked(cid) and doPlayerRemoveMoney(cid, parameters.cost)) then + module.npcHandler:say('Set the sails!', cid) module.npcHandler:releaseFocus(cid) doTeleportThing(cid, parameters.destination, false) @@ -485,7 +520,7 @@ if(Modules == nil) then end module.npcHandler:say(msg .. ".", cid) - module.npcHandler:resetNpc() + module.npcHandler:resetNpc(cid) return true end @@ -542,10 +577,10 @@ if(Modules == nil) then if(ret ~= nil) then self:parseList(keywords, ret) else - print('[Warning] NpcSystem:', 'Missing \'outfit' .. n .. '\' parameter, skipping...') + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Missing \'outfit' .. n .. '\' parameter, skipping...') end else - print('[Warning] NpcSystem:', 'No keywords found for outfit set #' .. n .. ', skipping...') + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'No keywords found for outfit set #' .. n .. ', skipping...') end n = n + 1 @@ -566,7 +601,7 @@ if(Modules == nil) then elseif(e == 4) then d = tmp else - print('[Warning] NpcSystem:', 'Unknown parameter found in outfit list while parsing ' .. (outfit == nil and 'outfit' or 'item') .. '.', tmp, list) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Unknown parameter found in outfit list while parsing ' .. (outfit == nil and 'outfit' or 'item') .. '.', tmp, list) end e = e + 1 @@ -585,10 +620,10 @@ if(Modules == nil) then items[a] = {b, tmp, c} else - print('[Warning] NpcSystem:', 'Missing parameter(s) for outfit items.', b, c, d) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Missing parameter(s) for outfit items.', b, c, d) end else - print('[Warning] NpcSystem:', 'Missing base parameter for outfit items.', a) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Missing base parameter for outfit items.', a) end end @@ -604,7 +639,7 @@ if(Modules == nil) then if(tmp and table.maxn(items) > 0) then self:addOutfit(keywords, outfit, items) else - print('[Warning] NpcSystem:', 'Invalid outfit, addon or empty items pool.', data) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Invalid outfit, addon or empty items pool.', data) end end end @@ -666,7 +701,7 @@ if(Modules == nil) then items = items .. v[3] end end - + module.npcHandler:say('Do you want ' .. keywords[1] .. ' ' .. (addon == 0 and "outfit" or "addon") .. ' for ' .. items .. '?', cid) return true @@ -679,7 +714,7 @@ if(Modules == nil) then end local parent = node:getParent():getParameters() - if(isPlayerPremiumCallback(cid) or not parent.premium) then + if(isPremium(cid) or not parent.premium) then if(not OUTFITMODULE_FUNCTION[2](cid, parent.outfit, parent.addon)) then if(parent.addon == 0 or OUTFITMODULE_FUNCTION[2](cid, parent.outfit)) then if(parent.gender == nil or parent.gender == getPlayerSex(cid)) then @@ -741,13 +776,13 @@ if(Modules == nil) then module.npcHandler:say('I will not dress you with addon of outfit you cannot wear!', cid) end else - module.npcHandler:say('You alrady have this ' .. (parent.addon == 0 and 'outfit' or 'addon') .. '!', cid) + module.npcHandler:say('You already have this ' .. (parent.addon == 0 and 'outfit' or 'addon') .. '!', cid) end else module.npcHandler:say('Sorry, I dress only premium players.', cid) end - module.npcHandler:resetNpc() + module.npcHandler:resetNpc(cid) return true end @@ -759,7 +794,7 @@ if(Modules == nil) then end module.npcHandler:say(module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_DECLINE), {[TAG_PLAYERNAME] = getCreatureName(cid)}), cid) - module.npcHandler:resetNpc() + module.npcHandler:resetNpc(cid) return true end @@ -789,7 +824,7 @@ if(Modules == nil) then end module.npcHandler:say(msg .. ".", cid) - module.npcHandler:resetNpc() + module.npcHandler:resetNpc(cid) return true end @@ -835,43 +870,44 @@ if(Modules == nil) then function ShopModule:parseBuyable(data) for item in string.gmatch(data, '[^;]+') do local i, name, itemid, cost, subType, realName = 1, nil, nil, nil, nil, nil - for temp in string.gmatch(item, '[^,]+') do + for tmp in string.gmatch(item, '[^,]+') do if(i == 1) then - name = temp + name = tmp elseif(i == 2) then - itemid = tonumber(temp) + itemid = tonumber(tmp) elseif(i == 3) then - cost = tonumber(temp) + cost = tonumber(tmp) elseif(i == 4) then - subType = tonumber(temp) + subType = tonumber(tmp) elseif(i == 5) then - realName = temp + realName = tmp else - print('[Warning] NpcSystem:', 'Unknown parameter found in buyable items parameter.', temp, item) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Unknown parameter found in buyable items parameter.', tmp, item) end + i = i + 1 end if(SHOPMODULE_MODE == SHOPMODULE_MODE_TRADE) then if(itemid ~= nil and cost ~= nil) then - if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then - print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item) + if(isItemFluidContainer(itemid) and subType == nil) then + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'SubType missing for parameter item:', item) else self:addBuyableItem(nil, itemid, cost, subType, realName) end else - print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost) end elseif(name ~= nil and itemid ~= nil and cost ~= nil) then - if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then - print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item) + if(isItemFluidContainer(itemid) and subType == nil) then + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'SubType missing for parameter item:', item) else local names = {} table.insert(names, name) self:addBuyableItem(names, itemid, cost, subType, realName) end else - print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost) end end end @@ -890,7 +926,7 @@ if(Modules == nil) then elseif(i == 4) then realName = temp else - print('[Warning] NpcSystem:', 'Unknown parameter found in sellable items parameter.', temp, item) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Unknown parameter found in sellable items parameter.', temp, item) end i = i + 1 end @@ -899,14 +935,14 @@ if(Modules == nil) then if(itemid ~= nil and cost ~= nil) then self:addSellableItem(nil, itemid, cost, realName) else - print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Parameter(s) missing for item:', itemid, cost) end elseif(name ~= nil and itemid ~= nil and cost ~= nil) then local names = {} table.insert(names, name) self:addSellableItem(names, itemid, cost, realName) else - print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Parameter(s) missing for item:', name, itemid, cost) end end end @@ -929,21 +965,21 @@ if(Modules == nil) then elseif(i == 6) then realName = temp else - print('[Warning] NpcSystem:', 'Unknown parameter found in buyable items parameter.', temp, item) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Unknown parameter found in buyable items parameter.', temp, item) end i = i + 1 end if(name ~= nil and container ~= nil and itemid ~= nil and cost ~= nil) then - if((isItemRune(itemid) or isItemFluidContainer(itemid)) and subType == nil) then - print('[Warning] NpcSystem:', 'SubType missing for parameter item:', item) + if(isItemFluidContainer(itemid) and subType == nil) then + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'SubType missing for parameter item:', item) else local names = {} table.insert(names, name) self:addBuyableItemContainer(names, container, itemid, cost, subType, realName) end else - print('[Warning] NpcSystem:', 'Parameter(s) missing for item:', name, container, itemid, cost) + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'Parameter(s) missing for item:', name, container, itemid, cost) end end end @@ -996,20 +1032,26 @@ if(Modules == nil) then -- names = A table containing one or more strings of alternative names to this item. Used only for old buy/sell system. -- itemid = The itemid of the buyable item -- cost = The price of one single item - -- subType - The subType of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer. Default value is 1. + -- subType - The subType of each rune or fluidcontainer item. Can be left out if it is not a rune/fluidcontainer. Default value is 0 and 1 (depending on shop mode) -- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used) function ShopModule:addBuyableItem(names, itemid, cost, subType, realName) + if(type(subType) == 'string' and realName == nil) then + realName = subType + subType = nil + end + + local v = getItemInfo(itemid) if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then local item = { id = itemid, buy = cost, sell = -1, - subType = subType or 1, - name = realName or getItemNameById(itemid) + subType = tonumber(subType) or (v.charges > 0 and v.charges or 0), + name = realName or v.name } for i, shopItem in ipairs(self.npcHandler.shopItems) do - if(shopItem.id == item.id and shopItem.subType == item.subType) then + if(shopItem.id == item.id and (shopItem.subType == item.subType or shopItem.subType == 0)) then if(item.sell ~= shopItem.sell) then item.sell = shopItem.sell end @@ -1031,8 +1073,8 @@ if(Modules == nil) then cost = cost, eventType = SHOPMODULE_BUY_ITEM, module = self, - realName = realName or getItemNameById(itemid), - subType = subType or 1 + realName = realName or v.name, + subType = tonumber(subType) or (v.charges > 0 and v.charges or 1) } for i, name in pairs(names) do @@ -1056,14 +1098,15 @@ if(Modules == nil) then -- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used) function ShopModule:addBuyableItemContainer(names, container, itemid, cost, subType, realName) if(names ~= nil) then + local v = getItemInfo(itemid) local parameters = { container = container, itemid = itemid, cost = cost, eventType = SHOPMODULE_BUY_ITEM_CONTAINER, module = self, - realName = realName or getItemNameById(itemid), - subType = subType or 1 + realName = realName or v.name, + subType = tonumber(subType) or (v.charges > 0 and v.charges or 1) } for i, name in pairs(names) do @@ -1084,13 +1127,14 @@ if(Modules == nil) then -- cost = The price of one single item -- realName - The real, full name for the item. Will be used as ITEMNAME in MESSAGE_ONBUY and MESSAGE_ONSELL if defined. Default value is nil (getItemNameById will be used) function ShopModule:addSellableItem(names, itemid, cost, realName) + local v = getItemInfo(itemid) if(SHOPMODULE_MODE ~= SHOPMODULE_MODE_TALK) then local item = { id = itemid, buy = -1, sell = cost, - subType = 1, - name = realName or getItemNameById(itemid) + subType = ((v.charges > 0 and v.stackable) and v.charges or 0), + name = realName or v.name } for i, shopItem in ipairs(self.npcHandler.shopItems) do @@ -1116,7 +1160,7 @@ if(Modules == nil) then cost = cost, eventType = SHOPMODULE_SELL_ITEM, module = self, - realName = realName or getItemNameById(itemid) + realName = realName or v.name } for i, name in pairs(names) do @@ -1148,18 +1192,28 @@ if(Modules == nil) then end if(shopItem == nil) then - error("[ShopModule.onBuy]", "Item not found on shopItems list") + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'ShopModule.onBuy - Item not found on shopItems list') return false end if(shopItem.buy == -1) then - error("[ShopModule.onSell]", "Attempt to purchase an item which only sellable") + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'ShopModule.onBuy - Attempt to purchase an item which only sellable') return false end - local backpack, totalCost = 1988, amount * shopItem.buy + if(amount <= 0) then + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'ShopModule.onBuy - Attempt to purchase ' .. amount .. ' items') + return false + end + + local subType, count = shopItem.subType or 0, amount + if(inBackpacks and isItemStackable(itemid)) then + amount = amount * 100 / math.max(1, subType) + end + + local backpack, backpackPrice, totalCost = 1988, 20, amount * shopItem.buy if(inBackpacks) then - totalCost = totalCost + (math.max(1, math.floor(amount / getContainerCapById(backpack))) * 20) + totalCost = totalCost + (math.max(1, math.floor(count / getContainerCapById(backpack))) * backpackPrice) end local parseInfo = { @@ -1168,15 +1222,13 @@ if(Modules == nil) then [TAG_TOTALCOST] = totalCost, [TAG_ITEMNAME] = shopItem.name } - if(getPlayerMoney(cid) < totalCost) then local msg = self.npcHandler:getMessage(MESSAGE_NEEDMONEY) doPlayerSendCancel(cid, self.npcHandler:parseMessage(msg, parseInfo)) return false end - local subType = shopItem.subType or 1 - local a, b = doNpcSellItem(cid, itemid, amount, subType, ignoreCap, inBackpacks, backpack) + local a, b = doNpcSellItem(cid, itemid, count, subType, ignoreCap, inBackpacks, backpack) if(a < amount) then local msgId = MESSAGE_NEEDMORESPACE if(a == 0) then @@ -1194,7 +1246,7 @@ if(Modules == nil) then end if(a > 0) then - doPlayerRemoveMoney(cid, ((a * shopItem.buy) + (b * 20))) + doPlayerRemoveMoney(cid, ((a * shopItem.buy) + (b * backpackPrice))) return true end @@ -1215,7 +1267,7 @@ if(Modules == nil) then end -- Callback onSell() function. If you wish, you can change certain Npc to use your onSell(). - function ShopModule:callbackOnSell(cid, itemid, subType, amount, ignoreCap, inBackpacks) + function ShopModule:callbackOnSell(cid, itemid, subType, amount, ignoreEquipped, dummy) local shopItem = nil for _, item in ipairs(self.npcHandler.shopItems) do if(item.id == itemid and item.subType == subType) then @@ -1225,12 +1277,12 @@ if(Modules == nil) then end if(shopItem == nil) then - error("[ShopModule.onBuy]", "Item not found on shopItems list") + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'ShopModule.onSell - Item not found on shopItems list') return false end if(shopItem.sell == -1) then - error("[ShopModule.onSell]", "Attempt to sell an item which is only buyable") + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'ShopModule.onSell - Attempt to sell an item which is only buyable') return false end @@ -1245,7 +1297,7 @@ if(Modules == nil) then subType = -1 end - if(doPlayerRemoveItem(cid, itemid, amount, subType)) then + if(doPlayerRemoveItem(cid, itemid, amount, subType, ignoreEquipped)) then local msg = self.npcHandler:getMessage(MESSAGE_SOLD) doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, self.npcHandler:parseMessage(msg, parseInfo)) @@ -1277,6 +1329,11 @@ if(Modules == nil) then return false end + local shop = getShopOwner(cid) + if(shop and shop == getNpcId()) then + return true + end + if(table.maxn(module.npcHandler.shopItems) == 0) then local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) } local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_NOSHOP), parseInfo) @@ -1287,7 +1344,7 @@ if(Modules == nil) then local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) } local msg = module.npcHandler:parseMessage(module.npcHandler:getMessage(MESSAGE_SENDTRADE), parseInfo) - openShopWindow(cid, module.npcHandler.shopItems, + addEvent(openShopWindow, 500, cid, module.npcHandler.shopItems, function(cid, itemid, subType, amount, ignoreCap, inBackpacks) module.npcHandler:onBuy(cid, itemid, subType, amount, ignoreCap, inBackpacks) end, @@ -1353,7 +1410,7 @@ if(Modules == nil) then end end - module.npcHandler:resetNpc() + module.npcHandler:resetNpc(cid) return true end @@ -1374,7 +1431,7 @@ if(Modules == nil) then local msg = module.npcHandler:parseMessage(module.noText, parseInfo) module.npcHandler:say(msg, cid) - module.npcHandler:resetNpc() + module.npcHandler:resetNpc(cid) return true end diff --git a/data/npc/lib/npcsystem/npchandler.lua b/data/npc/lib/npcsystem/npchandler.lua index 0f800e0..f8d0822 100644 --- a/data/npc/lib/npcsystem/npchandler.lua +++ b/data/npc/lib/npcsystem/npchandler.lua @@ -1,5 +1,5 @@ -- Advanced NPC System (Created by Jiddo), --- Modified by Talaturen. +-- Modified by TheForgottenServer Team. if(NpcHandler == nil) then -- Constant talkdelay behaviors. @@ -28,8 +28,8 @@ if(NpcHandler == nil) then MESSAGE_ONSELL = 7 -- When the player successfully sells something via talk. MESSAGE_SOLD = 8 -- When the player sold something through the shop window. MESSAGE_MISSINGMONEY = 9 -- When the player does not have enough money. - MESSAGE_NEEDMONEY = 10 -- Same as above, used for shop window. - MESSAGE_MISSINGITEM = 11 -- When the player is trying to sell an item he does not have. + MESSAGE_MISSINGITEM = 10 -- When the player is trying to sell an item he does not have. + MESSAGE_NEEDMONEY = 11 -- Same as above, used for shop window. MESSAGE_NEEDITEM = 12 -- Same as above, used for shop window. MESSAGE_NEEDSPACE = 13 -- When the player don't have any space to buy an item MESSAGE_NEEDMORESPACE = 14 -- When the player has some space to buy an item, but not enough space @@ -70,9 +70,9 @@ if(NpcHandler == nil) then keywordHandler = nil, focuses = nil, talkStart = nil, - idleTime = 90, - talkRadius = 4, - talkDelayTime = 1, -- Seconds to delay outgoing messages. + idleTime = 300, + talkRadius = 3, + talkDelayTime = 350, -- Seconds to delay outgoing messages. queue = nil, talkDelay = nil, callbackFunctions = nil, @@ -86,11 +86,11 @@ if(NpcHandler == nil) then [MESSAGE_ONBUY] = 'It was a pleasure doing business with you.', [MESSAGE_BOUGHT] = 'Bought |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.', [MESSAGE_SELL] = 'Do you want to sell |ITEMCOUNT| |ITEMNAME| for |TOTALCOST| gold coins?', - [MESSAGE_ONSELL] = 'Thank you for this |ITEMNAME|, |PLAYERNAME| gold.', + [MESSAGE_ONSELL] = 'Thank you for this |ITEMNAME|, |PLAYERNAME|.', [MESSAGE_SOLD] = 'Sold |ITEMCOUNT|x |ITEMNAME| for |TOTALCOST| gold.', [MESSAGE_MISSINGMONEY] = 'Sorry, you don\'t have enough money.', - [MESSAGE_NEEDMONEY] = 'You do not have enough money.', [MESSAGE_MISSINGITEM] = 'You don\'t even have that item, |PLAYERNAME|!', + [MESSAGE_NEEDMONEY] = 'You do not have enough money.', [MESSAGE_NEEDITEM] = 'You do not have this object.', [MESSAGE_NEEDSPACE] = 'You do not have enough capacity.', [MESSAGE_NEEDMORESPACE] = 'You do not have enough capacity for all items.', @@ -108,8 +108,9 @@ if(NpcHandler == nil) then -- Creates a new NpcHandler with an empty callbackFunction stack. function NpcHandler:new(keywordHandler) local obj = {} - obj.callbackFunctions = {} - obj.modules = {} + obj.messages = {} + + obj.keywordHandler = keywordHandler if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then obj.focuses = {} obj.talkStart = {} @@ -118,9 +119,10 @@ if(NpcHandler == nil) then obj.focuses = 0 obj.talkStart = 0 end + + obj.callbackFunctions = {} + obj.modules = {} obj.talkDelay = {} - obj.keywordHandler = keywordHandler - obj.messages = {} obj.shopItems = {} setmetatable(obj.messages, self.messages) @@ -143,8 +145,12 @@ if(NpcHandler == nil) then -- Function used to change the focus of this npc. function NpcHandler:addFocus(newFocus) + if(not isCreature(newFocus)) then + return + end + if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then - if(self:isFocused(newFocus)) then + if(self:isFocused(newFocus, true)) then return end @@ -153,40 +159,53 @@ if(NpcHandler == nil) then self.focuses = newFocus end - self:updateFocus() + self:updateFocus(true) end - NpcHandler.changeFocus = NpcHandler.addFocus --"changeFocus" looks better for CONVERSATION_DEFAULT + NpcHandler.changeFocus = NpcHandler.addFocus -- "changeFocus" looks better for CONVERSATION_DEFAULT -- Function used to verify if npc is focused to certain player - function NpcHandler:isFocused(focus) + function NpcHandler:isFocused(focus, creatureCheck) + local creatureCheck = creatureCheck or false if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then - for k,v in pairs(self.focuses) do - if v == focus then - return true + for k, v in pairs(self.focuses) do + if(v == focus) then + if(creatureCheck or isCreature(v)) then + return true + end + + self:unsetFocus(focus, k) + return false end end return false end - return (self.focuses == focus) + if(creatureCheck or isCreature(self.focuses)) then + return self.focuses == focus + end + + self:changeFocus(0) + return false end -- This function should be called on each onThink and makes sure the npc faces the player it is talking to. -- Should also be called whenever a new player is focused. - function NpcHandler:updateFocus() + function NpcHandler:updateFocus(creatureCheck) + local creatureCheck = creatureCheck or false if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then - for pos, focus in pairs(self.focuses) do - if(focus ~= nil) then + for _, focus in pairs(self.focuses) do + if(creatureCheck or isCreature(focus)) then doNpcSetCreatureFocus(focus) return end end - - doNpcSetCreatureFocus(0) - else + elseif(creatureCheck or isCreature(self.focuses)) then doNpcSetCreatureFocus(self.focuses) + return end + + doNpcSetCreatureFocus(0) end -- Used when the npc should un-focus the player. @@ -197,21 +216,36 @@ if(NpcHandler == nil) then end local pos = nil - for k,v in pairs(self.focuses) do - if v == focus then + for k, v in pairs(self.focuses) do + if(v == focus) then pos = k end end - table.remove(self.focuses, pos) - self.talkStart[focus] = nil - closeShopWindow(focus) --Even if it can not exist, we need to prevent it. - self:updateFocus() - else - closeShopWindow(focus) + + if(pos ~= nil) then + closeShopWindow(focus) + self:unsetFocus(focus, pos) + end + elseif(self.focuses == focus) then + if(isCreature(focus)) then + closeShopWindow(focus) + end + self:changeFocus(0) end end + -- Internal un-focusing function, beware using! + function NpcHandler:unsetFocus(focus, pos) + if(type(self.focuses) ~= "table" or pos == nil or self.focuses[pos] == nil) then + return + end + + table.remove(self.focuses, pos) + self.talkStart[focus] = nil + self:updateFocus() + end + -- Returns the callback function with the specified id or nil if no such callback function exists. function NpcHandler:getCallback(id) local ret = nil @@ -247,32 +281,32 @@ if(NpcHandler == nil) then -- Calls the callback function represented by id for all modules added to this npchandler with the given arguments. function NpcHandler:processModuleCallback(id, ...) local ret = true - for i, module in pairs(self.modules) do + for _, module in pairs(self.modules) do local tmpRet = true if(id == CALLBACK_CREATURE_APPEAR and module.callbackOnCreatureAppear ~= nil) then - tmpRet = module:callbackOnCreatureAppear(unpack(arg)) + tmpRet = module:callbackOnCreatureAppear(...) elseif(id == CALLBACK_CREATURE_DISAPPEAR and module.callbackOnCreatureDisappear ~= nil) then - tmpRet = module:callbackOnCreatureDisappear(unpack(arg)) + tmpRet = module:callbackOnCreatureDisappear(...) elseif(id == CALLBACK_CREATURE_SAY and module.callbackOnCreatureSay ~= nil) then - tmpRet = module:callbackOnCreatureSay(unpack(arg)) + tmpRet = module:callbackOnCreatureSay(...) elseif(id == CALLBACK_PLAYER_ENDTRADE and module.callbackOnPlayerEndTrade ~= nil) then - tmpRet = module:callbackOnPlayerEndTrade(unpack(arg)) + tmpRet = module:callbackOnPlayerEndTrade(...) elseif(id == CALLBACK_PLAYER_CLOSECHANNEL and module.callbackOnPlayerCloseChannel ~= nil) then - tmpRet = module:callbackOnPlayerCloseChannel(unpack(arg)) + tmpRet = module:callbackOnPlayerCloseChannel(...) elseif(id == CALLBACK_ONBUY and module.callbackOnBuy ~= nil) then - tmpRet = module:callbackOnBuy(unpack(arg)) + tmpRet = module:callbackOnBuy(...) elseif(id == CALLBACK_ONSELL and module.callbackOnSell ~= nil) then - tmpRet = module:callbackOnSell(unpack(arg)) + tmpRet = module:callbackOnSell(...) elseif(id == CALLBACK_ONTHINK and module.callbackOnThink ~= nil) then - tmpRet = module:callbackOnThink(unpack(arg)) + tmpRet = module:callbackOnThink(...) elseif(id == CALLBACK_GREET and module.callbackOnGreet ~= nil) then - tmpRet = module:callbackOnGreet(unpack(arg)) + tmpRet = module:callbackOnGreet(...) elseif(id == CALLBACK_FAREWELL and module.callbackOnFarewell ~= nil) then - tmpRet = module:callbackOnFarewell(unpack(arg)) + tmpRet = module:callbackOnFarewell(...) elseif(id == CALLBACK_MESSAGE_DEFAULT and module.callbackOnMessageDefault ~= nil) then - tmpRet = module:callbackOnMessageDefault(unpack(arg)) + tmpRet = module:callbackOnMessageDefault(...) elseif(id == CALLBACK_MODULE_RESET and module.callbackOnModuleReset ~= nil) then - tmpRet = module:callbackOnModuleReset(unpack(arg)) + tmpRet = module:callbackOnModuleReset(...) end if(not tmpRet) then @@ -303,12 +337,13 @@ if(NpcHandler == nil) then -- Translates all message tags found in msg using parseInfo function NpcHandler:parseMessage(msg, parseInfo) - local ret = msg for search, replace in pairs(parseInfo) do - ret = string.gsub(ret, search, replace) + if(replace ~= nil) then + msg = msg:gsub(search, replace) + end end - return ret + return msg end -- Makes sure the npc un-focuses the currently focused player @@ -322,12 +357,29 @@ if(NpcHandler == nil) then if(self:processModuleCallback(CALLBACK_FAREWELL)) then if(self.queue == nil or not self.queue:greetNext()) then local msg = self:getMessage(MESSAGE_FAREWELL) - local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) } - msg = self:parseMessage(msg, parseInfo) + msg = self:parseMessage(msg, { [TAG_PLAYERNAME] = getPlayerName(cid) or -1 }) + + self:resetNpc(cid) + if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then + self:say(msg, cid, 0, true) + msg = msg:gsub('{', ''):gsub('}', '') + local ghost, position = isPlayerGhost(cid), getThingPosition(getNpcId()) + + local spectators, nid = getSpectators(position, 7, 7), getNpcId() + for _, pid in ipairs(spectators) do + if(isPlayer(pid) and pid ~= cid) then + if(NPCHANDLER_TALKDELAY ~= TALKDELAY_NONE) then + addEvent(doCreatureSay, self.talkDelayTime, nid, msg, TALKTYPE_SAY, ghost, pid, position) + else + doCreatureSay(nid, msg, TALKTYPE_SAY, ghost, pid, position) + end + end + end + else + self:say(msg) + end - self:say(msg, cid) self:releaseFocus(cid) - self:say(msg) end end end @@ -335,17 +387,30 @@ if(NpcHandler == nil) then -- Greets a new player. function NpcHandler:greet(cid) - if(cid ~= 0) then - local callback = self:getCallback(CALLBACK_GREET) - if(callback == nil or callback(cid)) then - if(self:processModuleCallback(CALLBACK_GREET, cid)) then - local msg = self:getMessage(MESSAGE_GREET) - local parseInfo = { [TAG_PLAYERNAME] = getCreatureName(cid) } - msg = self:parseMessage(msg, parseInfo) + local callback = self:getCallback(CALLBACK_GREET) + if(callback == nil or callback(cid)) then + if(self:processModuleCallback(CALLBACK_GREET, cid)) then + local msg = self:getMessage(MESSAGE_GREET) + msg = self:parseMessage(msg, { [TAG_PLAYERNAME] = getCreatureName(cid) }) + self:addFocus(cid) + if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then + self:say(msg, cid, 0, true) + msg = msg:gsub('{', ''):gsub('}', '') + local ghost, position = isPlayerGhost(cid), getThingPosition(getNpcId()) + + local spectators, nid = getSpectators(position, 7, 7), getNpcId() + for _, pid in ipairs(spectators) do + if(isPlayer(pid) and pid ~= cid) then + if(NPCHANDLER_TALKDELAY ~= TALKDELAY_NONE) then + addEvent(doCreatureSay, self.talkDelayTime, nid, msg, TALKTYPE_SAY, ghost, pid, position) + else + doCreatureSay(nid, msg, TALKTYPE_SAY, ghost, pid, position) + end + end + end + else self:say(msg) - self:addFocus(cid) - self:say(msg, cid) end end end @@ -394,12 +459,10 @@ if(NpcHandler == nil) then self.talkStart = os.time() end end + elseif(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then + self.talkStart[cid] = os.time() else - if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then - self.talkStart[cid] = os.time() - else - self.talkStart = os.time() - end + self.talkStart = os.time() end end end @@ -411,7 +474,7 @@ if(NpcHandler == nil) then function NpcHandler:onPlayerEndTrade(cid) local callback = self:getCallback(CALLBACK_PLAYER_ENDTRADE) if(callback == nil or callback(cid)) then - if(self:processModuleCallback(CALLBACK_PLAYER_ENDTRADE, cid, class, msg)) then + if(self:processModuleCallback(CALLBACK_PLAYER_ENDTRADE, cid)) then if(self:isFocused(cid)) then local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) } local msg = self:parseMessage(self:getMessage(MESSAGE_ONCLOSESHOP), parseInfo) @@ -425,7 +488,7 @@ if(NpcHandler == nil) then function NpcHandler:onPlayerCloseChannel(cid) local callback = self:getCallback(CALLBACK_PLAYER_CLOSECHANNEL) if(callback == nil or callback(cid)) then - if(self:processModuleCallback(CALLBACK_PLAYER_CLOSECHANNEL, cid, class, msg)) then + if(self:processModuleCallback(CALLBACK_PLAYER_CLOSECHANNEL, cid)) then if(self:isFocused(cid)) then self:unGreet(cid) end @@ -457,24 +520,33 @@ if(NpcHandler == nil) then function NpcHandler:onThink() local callback = self:getCallback(CALLBACK_ONTHINK) if(callback == nil or callback()) then - if(NPCHANDLER_TALKDELAY == TALKDELAY_ONTHINK) then - if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then - for cid, talkDelay in pairs(self.talkDelay) do - if(talkDelay.time ~= nil and talkDelay.message ~= nil and os.time() >= talkDelay.time) then - selfSay(talkDelay.message, cid) - self.talkDelay[cid] = nil + for i, speech in pairs(self.talkDelay) do + if((speech.cid == nil or speech.cid == 0) and speech.time ~= nil and speech.message ~= nil) then + if(os.mtime() >= speech.time) then + selfSay(speech.message) + self.talkDelay[i] = nil + end + elseif(isCreature(speech.cid) and speech.start ~= nil and speech.time ~= nil and speech.message ~= nil) then + if(os.mtime() >= speech.time) then + local talkStart = (NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT and self.talkStart[speech.cid] or self.talkStart) + if(speech.force or (self:isFocused(speech.cid) and talkStart == speech.start)) then + if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then + selfSay(speech.message, speech.cid) + else + selfSay(speech.message) + end end + + self.talkDelay[i] = nil end - elseif(self.talkDelay.time ~= nil and self.talkDelay.message ~= nil and os.time() >= self.talkDelay.time) then - selfSay(self.talkDelay.message) - self.talkDelay.time = nil - self.talkDelay.message = nil + else + self.talkDelay[i] = nil end end if(self:processModuleCallback(CALLBACK_ONTHINK)) then if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then - for pos, focus in pairs(self.focuses) do + for _, focus in pairs(self.focuses) do if(focus ~= nil) then if(not self:isInRange(focus)) then self:onWalkAway(focus) @@ -488,7 +560,7 @@ if(NpcHandler == nil) then elseif(self.focuses ~= 0) then if(not self:isInRange(self.focuses)) then self:onWalkAway(self.focuses) - elseif(os.time()-self.talkStart > self.idleTime) then + elseif((os.time() - self.talkStart) > self.idleTime) then self:unGreet(self.focuses) else self:updateFocus() @@ -541,12 +613,9 @@ if(NpcHandler == nil) then if(self:processModuleCallback(CALLBACK_CREATURE_DISAPPEAR, cid)) then if(self.queue == nil or not self.queue:greetNext()) then local msg = self:getMessage(MESSAGE_WALKAWAY) - local parseInfo = { [TAG_PLAYERNAME] = getPlayerName(cid) } - msg = self:parseMessage(msg, parseInfo) - - self:say(msg, cid) + self:resetNpc(cid) + self:say(self:parseMessage(msg, { [TAG_PLAYERNAME] = getPlayerName(cid) or -1 })) self:releaseFocus(cid) - self:say(msg) end end end @@ -555,48 +624,44 @@ if(NpcHandler == nil) then -- Returns true if cid is within the talkRadius of this npc. function NpcHandler:isInRange(cid) - local distance = getDistanceTo(cid) or -1 - if(distance == -1) then + if not isPlayer(cid) then return false end - return (distance <= self.talkRadius) + local distance = getNpcDistanceTo(cid) or -1 + return distance ~= -1 and distance <= self.talkRadius end -- Resets the npc into it's initial state (in regard of the keyrodhandler). -- All modules are also receiving a reset call through their callbackOnModuleReset function. - function NpcHandler:resetNpc() + function NpcHandler:resetNpc(cid) if(self:processModuleCallback(CALLBACK_MODULE_RESET)) then - self.keywordHandler:reset() + self.keywordHandler:reset(cid) end end -- Makes the npc represented by this instance of NpcHandler say something. -- This implements the currently set type of talkdelay. - -- shallDelay is a boolean value. If it is false, the message is not delayed. Default value is false. - function NpcHandler:say(message, focus, shallDelay) - local shallDelay = shallDelay or false - if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE or not shallDelay) then + function NpcHandler:say(message, focus, delay, force) + local delay = delay or 0 + if(NPCHANDLER_TALKDELAY == TALKDELAY_NONE or delay <= 0) then if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then selfSay(message, focus) - return else selfSay(message) - return end + + return end -- TODO: Add an event handling method for delayed messages - if(NPCHANDLER_CONVBEHAVIOR ~= CONVERSATION_DEFAULT) then - self.talkDelay[focus] = { - message = message, - time = os.time() + self.talkDelayTime, - } - else - self.talkDelay = { - message = message, - time = os.time() + self.talkDelayTime - } - end + table.insert(self.talkDelay, { + id = getNpcId(), + cid = focus, + message = message, + time = os.mtime() + (delay and delay or self.talkDelayTime), + start = os.time(), + force = force or false + }) end end diff --git a/data/npc/lib/npcsystem/queue.lua b/data/npc/lib/npcsystem/queue.lua index 8cbf2f9..b8cc563 100644 --- a/data/npc/lib/npcsystem/queue.lua +++ b/data/npc/lib/npcsystem/queue.lua @@ -1,5 +1,5 @@ -- Advanced NPC System (Created by Jiddo), --- Modified by Talaturen. +-- Modified by TheForgottenServer Team. if(Queue == nil) then Queue = { @@ -12,6 +12,7 @@ if(Queue == nil) then local obj = {} obj.handler = handler obj.customers = {} + setmetatable(obj, self) self.__index = self return obj @@ -58,9 +59,9 @@ if(Queue == nil) then function Queue:canGreet(cid) if(isPlayer(cid)) then return self.handler:isInRange(cid) - else - return false end + + return false end -- Greets the player with the given cid. @@ -83,6 +84,7 @@ if(Queue == nil) then end end end + return false end -end \ No newline at end of file +end diff --git a/data/npc/npcs.xml b/data/npc/npcs.xml new file mode 100644 index 0000000..a46afcc --- /dev/null +++ b/data/npc/npcs.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/npc/scripts/addons.lua b/data/npc/scripts/addons.lua deleted file mode 100644 index 722866a..0000000 --- a/data/npc/scripts/addons.lua +++ /dev/null @@ -1,45 +0,0 @@ -local keywordHandler = KeywordHandler:new() -local npcHandler = NpcHandler:new(keywordHandler) -NpcSystem.parseParameters(npcHandler) - -function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end -function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end -function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end -function onThink() npcHandler:onThink() end - -function buyAddons(cid, message, keywords, parameters, node) - --TODO: buyAddons function in modules.lua - if(not npcHandler:isFocused(cid)) then - return false - end - - local addon = parameters.addon - local cost = parameters.cost - local premium = (parameters.premium ~= nil and parameters.premium) - - if isPlayerPremiumCallback == nil or (isPlayerPremiumCallback(cid) and premium) then - if doPlayerRemoveMoney(cid, cost) then - doPlayerAddAddons(cid, addon) - npcHandler:say('There, you are now able to use all addons!', cid) - else - npcHandler:say('Sorry, you do not have enough money.', cid) - end - else - npcHandler:say('I only serve customers with premium accounts.', cid) - end - - keywordHandler:moveUp(1) - return true -end - -local node1 = keywordHandler:addKeyword({'first addon'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the first addons set for 5000 gold coins?'}) - node1:addChildKeyword({'yes'}, buyAddons, {addon = 1, cost = 5000, premium = true}) - node1:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, moveup = 1, text = 'Too expensive, eh?'}) - -local node2 = keywordHandler:addKeyword({'second addon'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Would you like to buy the second addons set for 10000 gold coins?'}) - node2:addChildKeyword({'yes'}, buyAddons, {addon = 2, cost = 10000, premium = true}) - node2:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, moveup = 1, text = 'Too expensive, eh?'}) - -keywordHandler:addKeyword({'addon'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'I sell the first addons set for 5000 gold coins and the second addons set for 10000 gold coins.'}) - -npcHandler:addModule(FocusModule:new()) \ No newline at end of file diff --git a/data/npc/scripts/aiexample.lua b/data/npc/scripts/aiexample.lua new file mode 100644 index 0000000..fd4a7fd --- /dev/null +++ b/data/npc/scripts/aiexample.lua @@ -0,0 +1,54 @@ +_G['NpcSystem'] = nil +_G['NpcHandler'] = nil +_G['KeywordHandler'] = nil +_G['KeywordNode'] = nil +_G['Queue'] = nil +_G['Modules'] = nil +_G['StdModule'] = nil +_G['FocusModule'] = nil +_G['KeywordModule'] = nil +_G['TravelModule'] = nil +_G['OutfitModule'] = nil +_G['ShopModule'] = nil + +local Npc = AI.SocialNpc:New() + +Npc.VisionRadius = 5 +Npc.HearRadius = 3 +Npc.LookAtFocus = false + +-- All of these properties have their own default values, just messing around +Npc.GreetKeywords = {"hi", "sup"} +Npc.FarewellKeywords = {"bb", "cYa"} +Npc.MaxIdleTime = 10000 +Npc.ResponseDelay = 1000 +Npc.TalkCaseSensitive = true +Npc.PlayerNameToken = 'DUDE' + + +Npc.GreetReply = "Hello |DUDE|, I'm a test npc" +Npc.FarewellReply = "Bye |DUDE|" + +Npc.Conversation = { + {["ass%"]= function(talk, match) + talk:Say("You called me an " .. match) + talk:Say("You must die") + + local m = talk:Listen() + talk:End("And now you called me a " .. m .. "! Bye |DUDE|") + end}, + + {["job|occupation"] = "My job is to be a !failure"}, + {["mission|quest"] = "I have no missions for you son"}, + {["heal"] = { + {[function(talk) + return getCreatureHealth(talk.player) < 200 + end] = function(talk) + doCreatureAddHealth(talk.player, 200) + return "You have been healed!" + end}, + {[true] = "Sorry ur not wounded enought!"} + }} +} + +Npc:Register() \ No newline at end of file diff --git a/data/npc/scripts/bless.lua b/data/npc/scripts/bless.lua index 9e76fc3..baf3788 100644 --- a/data/npc/scripts/bless.lua +++ b/data/npc/scripts/bless.lua @@ -2,11 +2,12 @@ local keywordHandler = KeywordHandler:new() local npcHandler = NpcHandler:new(keywordHandler) NpcSystem.parseParameters(npcHandler) - function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end function onThink() npcHandler:onThink() end +function onPlayerEndTrade(cid) npcHandler:onPlayerEndTrade(cid) end +function onPlayerCloseChannel(cid) npcHandler:onPlayerCloseChannel(cid) end local node1 = keywordHandler:addKeyword({'first bless'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the first blessing for 2000 (plus level depending amount) gold?'}) node1:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, number = 1, premium = true, baseCost = 2000, levelCost = 200, startLevel = 30, endLevel = 120}) @@ -28,4 +29,8 @@ local node5 = keywordHandler:addKeyword({'fifth bless'}, StdModule.say, {npcHand node5:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, number = 5, premium = true, baseCost = 2000, levelCost = 200, startLevel = 30, endLevel = 120}) node5:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, reset = true, text = 'Too expensive, eh?'}) +local node6 = keywordHandler:addKeyword({'pvp bless'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'Do you want to buy the pvp blessing for 2000 (plus level depending amount) gold?'}) + node6:addChildKeyword({'yes'}, StdModule.bless, {npcHandler = npcHandler, number = 0, premium = true, baseCost = 2000, levelCost = 200, startLevel = 30, endLevel = 270}) + node6:addChildKeyword({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, reset = true, text = 'Too expensive, eh?'}) + npcHandler:addModule(FocusModule:new()) diff --git a/data/npc/scripts/bootmaker.lua b/data/npc/scripts/bootmaker.lua index c134ea3..f0f0a2b 100644 --- a/data/npc/scripts/bootmaker.lua +++ b/data/npc/scripts/bootmaker.lua @@ -3,10 +3,12 @@ local npcHandler = NpcHandler:new(keywordHandler) NpcSystem.parseParameters(npcHandler) local talkState = {} -function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end -function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end -function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end -function onThink() npcHandler:onThink() end +function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end +function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end +function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end +function onThink() npcHandler:onThink() end +function onPlayerEndTrade(cid) npcHandler:onPlayerEndTrade(cid) end +function onPlayerCloseChannel(cid) npcHandler:onPlayerCloseChannel(cid) end function creatureSayCallback(cid, type, msg) if(not npcHandler:isFocused(cid)) then diff --git a/data/npc/scripts/default.lua b/data/npc/scripts/default.lua index 022d960..1139f78 100644 --- a/data/npc/scripts/default.lua +++ b/data/npc/scripts/default.lua @@ -2,9 +2,11 @@ local keywordHandler = KeywordHandler:new() local npcHandler = NpcHandler:new(keywordHandler) NpcSystem.parseParameters(npcHandler) -function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end -function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end -function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end -function onThink() npcHandler:onThink() end +function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end +function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end +function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end +function onThink() npcHandler:onThink() end +function onPlayerEndTrade(cid) npcHandler:onPlayerEndTrade(cid) end +function onPlayerCloseChannel(cid) npcHandler:onPlayerCloseChannel(cid) end npcHandler:addModule(FocusModule:new()) diff --git a/data/npc/scripts/example82.lua b/data/npc/scripts/example82.lua index 179d91f..ee7bb32 100644 --- a/data/npc/scripts/example82.lua +++ b/data/npc/scripts/example82.lua @@ -43,12 +43,6 @@ for _, item in ipairs(itemWindow) do items[item.id] = {buyPrice = item.buy, sellPrice = item.sell, subType = item.subType, realName = item.name} end -local function getPlayerMoney(cid) - return ((getPlayerItemCount(cid, 2160) * 10000) + - (getPlayerItemCount(cid, 2152) * 100) + - getPlayerItemCount(cid, 2148)) -end - local onBuy = function(cid, item, subType, amount, ignoreCap, inBackpacks) if(items[item] == nil) then selfSay("Ehm.. sorry... this shouldn't be there, I'm not selling it.", cid) @@ -90,6 +84,7 @@ local onSell = function(cid, item, subType, amount, ignoreCap, inBackpacks) end function onCreatureAppear(cid) + -- end function onCreatureDisappear(cid) diff --git a/data/npc/scripts/furniture.lua b/data/npc/scripts/furniture.lua index f9687e5..eb6e4a2 100644 --- a/data/npc/scripts/furniture.lua +++ b/data/npc/scripts/furniture.lua @@ -3,9 +3,11 @@ local npcHandler = NpcHandler:new(keywordHandler) NpcSystem.parseParameters(npcHandler) function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end -function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end -function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end -function onThink() npcHandler:onThink() end +function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end +function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end +function onThink() npcHandler:onThink() end +function onPlayerEndTrade(cid) npcHandler:onPlayerEndTrade(cid) end +function onPlayerCloseChannel(cid) npcHandler:onPlayerCloseChannel(cid) end -- Don't forget npcHandler = npcHandler in the parameters. It is required for all StdModule functions! keywordHandler:addKeyword({'chairs'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'I sell wooden, sofa, red cushioned, green cushioned, tusk and ivory chairs.'}) diff --git a/data/npc/scripts/loot.lua b/data/npc/scripts/loot.lua index e6215b7..898b2b7 100644 --- a/data/npc/scripts/loot.lua +++ b/data/npc/scripts/loot.lua @@ -3,9 +3,11 @@ local npcHandler = NpcHandler:new(keywordHandler) NpcSystem.parseParameters(npcHandler) function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end -function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end -function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end -function onThink() npcHandler:onThink() end +function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end +function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end +function onThink() npcHandler:onThink() end +function onPlayerEndTrade(cid) npcHandler:onPlayerEndTrade(cid) end +function onPlayerCloseChannel(cid) npcHandler:onPlayerCloseChannel(cid) end -- Don't forget npcHandler = npcHandler in the parameters. It is required for all StdModule functions! keywordHandler:addKeyword({'helmets'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'I buy royal (40k), warrior (6k), crusader (9k), crown (5k), devil (4k), chain (35gp) and iron helmets (30gp), also mystic turbans (500gp).'}) diff --git a/data/npc/scripts/oracle.lua b/data/npc/scripts/oracle.lua index 74742d6..f9e3dc6 100644 --- a/data/npc/scripts/oracle.lua +++ b/data/npc/scripts/oracle.lua @@ -2,63 +2,100 @@ local keywordHandler = KeywordHandler:new() local npcHandler = NpcHandler:new(keywordHandler) NpcSystem.parseParameters(npcHandler) -function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end -function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end -function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end -function onThink() npcHandler:onThink() end +function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end +function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end +function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end +function onThink() npcHandler:onThink() end +function onPlayerEndTrade(cid) npcHandler:onPlayerEndTrade(cid) end +function onPlayerCloseChannel(cid) npcHandler:onPlayerCloseChannel(cid) end function oracle(cid, message, keywords, parameters, node) - if(not npcHandler:isFocused(cid)) then + local npcHandler = parameters.npcHandler + if(npcHandler == nil) then + print('[Warning - ' .. getCreatureName(getNpcId()) .. '] NpcSystem:', 'oracle - Call without any npcHandler instance.') return false end - local cityNode = node:getParent():getParent() - local vocNode = node:getParent() + if(not npcHandler:isFocused(cid)) then + return false + end - local destination = cityNode:getParameters().destination - local town = cityNode:getParameters().town - local vocation = vocNode:getParameters().vocation + local cityNode, vocationNode = node:getParent():getParent(), node:getParent() + local params = { + ['vocation'] = vocationNode:getParameters().vocation, + ['town'] = cityNode:getParameters().town, + ['premium'] = cityNode:getParameters().premium or false, + ['items'] = vocationNode:getParameters().items or nil + } - if(destination ~= nil and vocation ~= nil and town ~= nil) then + if(params['town'] ~= nil and params['vocation'] ~= nil) then if(getPlayerLevel(cid) < parameters.level) then - npcHandler:say('You must first reach level ' .. parameters.level .. '!', cid) - npcHandler:resetNpc() + npcHandler:say('You must first reach level {' .. parameters.level .. '}!', cid) + elseif(getPlayerVocation(cid) > 0) then + npcHandler:say('Sorry, you already have a vocation!', cid) + elseif(params['premium'] and not isPremium(cid)) then + npcHandler:say('Sorry, this town is reserved only for premium players!', cid) else - if(getPlayerVocation(cid) > 0) then - npcHandler:say('Sorry, You already have a vocation!') - npcHandler:resetNpc() - else - doPlayerSetVocation(cid, vocation) - doPlayerSetTown(cid, town) - npcHandler:resetNpc() - - local tmp = getCreaturePosition(cid) - doTeleportThing(cid, destination) - doSendMagicEffect(tmp, CONST_ME_POFF) - doSendMagicEffect(destination, CONST_ME_TELEPORT) + doPlayerSetVocation(cid, params['vocation']) + doPlayerSetTown(cid, params['town']) + if(params['items'] ~= nil) then + local parcel, time = doCreateItemEx(2595), os.time() + local label, target = doAddContainerItem(parcel, 2599), getCreatureName(cid) + + doItemSetAttribute(label, "text", target .. "\n" .. getTownName(params['town'])) + doItemSetAttribute(label, "date", time) + doItemSetAttribute(label, "writer", getCreatureName(getNpcId())) + + for _, item in ipairs(params['items']) do + if(type(item[2]) == 'string') then + local tmp = doCreateItemEx(item[1]) + + doItemSetAttribute(tmp, "text", item[2]) + doItemSetAttribute(tmp, "date", time) + doItemSetAttribute(tmp, "writer", getCreatureName(getNpcId())) + + doAddContainerItemEx(parcel, tmp) + else + doAddContainerItem(parcel, item[1], item[2] or 1) + end + end + + doPlayerSendMailByName(target, parcel, params['town']) end + + local tmp, temple = getThingPosition(cid), getTownTemplePosition(params['town']) + npcHandler:say('SO BE IT!', cid) + doTeleportThing(cid, temple) + + doSendMagicEffect(tmp, CONST_ME_POFF) + doSendMagicEffect(temple, CONST_ME_TELEPORT) end + else + npcHandler:resetNpc(cid) + error('Player: ' .. getCreatureName(cid) .. ', Params: ' .. table.serialize(params)) end return true + end function greetCallback(cid) - if(getPlayerLevel(cid) < 8) then - npcHandler:say('COME BACK WHEN YOU GROW UP, CHILD!') - return false - else + if(getPlayerLevel(cid) >= getConfigValue('rookLevelToLeaveRook')) then return true end + + npcHandler:say('COME BACK WHEN YOU GROW UP, CHILD!') + return false end + npcHandler:setCallback(CALLBACK_GREET, greetCallback) npcHandler:setMessage(MESSAGE_GREET, 'Hello |PLAYERNAME|. Are you prepared to face your destiny?') -local yesNode = KeywordNode:new({'yes'}, oracle, {level = 8}) +local yesNode = KeywordNode:new({'yes'}, oracle, {level = getConfigValue('rookLevelToLeaveRook')}) local noNode = KeywordNode:new({'no'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, moveup = 1, text = 'Then what vocation do you want to become?'}) local node1 = keywordHandler:addKeyword({'yes'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'What city do you wish to live in? {Rhyves}, {Varak} or {Jorvik}?'}) - local node2 = node1:addChildKeyword({'varak'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, town = 1, destination = {x=242, y=429, z=12}, text = 'Varak, eh? So what vocation do you wish to become? {Sorcerer}, {druid}, {paladin} or {knight}?'}) + local node2 = node1:addChildKeyword({'varak'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, town = 1, text = 'Varak, eh? So what vocation do you wish to become? {Sorcerer}, {druid}, {paladin} or {knight}?'}) local node3 = node2:addChildKeyword({'sorcerer'}, StdModule.say, {npcHandler = npcHandler, vocation = 1, onlyFocus = true, text = 'So, you wish to be a powerful magician? Are you sure about that? This decision is irreversible!'}) node3:addChildKeywordNode(yesNode) node3:addChildKeywordNode(noNode) @@ -71,20 +108,7 @@ local node1 = keywordHandler:addKeyword({'yes'}, StdModule.say, {npcHandler = np node3 = node2:addChildKeyword({'knight'}, StdModule.say, {npcHandler = npcHandler, vocation = 4, onlyFocus = true, text = 'A mighty warrior. Is that your final decision? This decision is irreversible!'}) node3:addChildKeywordNode(yesNode) node3:addChildKeywordNode(noNode) - node2 = node1:addChildKeyword({'rhyves'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, town = 1, destination = {x=159, y=387, z=6}, text = 'Rhyves, eh? So what vocation do you wish to become? {Sorcerer}, {druid}, {paladin} or {knight}?'}) - node3 = node2:addChildKeyword({'sorcerer'}, StdModule.say, {npcHandler = npcHandler, vocation = 1, onlyFocus = true, text = 'So, you wish to be a powerful magician? Are you sure about that? This decision is irreversible!'}) - node3:addChildKeywordNode(yesNode) - node3:addChildKeywordNode(noNode) - node3 = node2:addChildKeyword({'druid'}, StdModule.say, {npcHandler = npcHandler, vocation = 2, onlyFocus = true, text = 'Are you sure that a druid is what you wish to become? This decision is irreversible!'}) - node3:addChildKeywordNode(yesNode) - node3:addChildKeywordNode(noNode) - node3 = node2:addChildKeyword({'paladin'}, StdModule.say, {npcHandler = npcHandler, vocation = 3, onlyFocus = true, text = 'A ranged marksman. Are you sure? This decision is irreversible!'}) - node3:addChildKeywordNode(yesNode) - node3:addChildKeywordNode(noNode) - node3 = node2:addChildKeyword({'knight'}, StdModule.say, {npcHandler = npcHandler, vocation = 4, onlyFocus = true, text = 'A mighty warrior. Is that your final decision? This decision is irreversible!'}) - node3:addChildKeywordNode(yesNode) - node3:addChildKeywordNode(noNode) - node2 = node1:addChildKeyword({'jorvik'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, town = 1, destination = {x=469, y=172, z=7}, text = 'Jorvik, eh? So what vocation do you wish to become? {Sorcerer}, {druid}, {paladin} or {knight}?'}) + node2 = node1:addChildKeyword({'rhyves'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, town = 2, premium = true, text = 'Rhyves, eh? So what vocation do you wish to become? {Sorcerer}, {druid}, {paladin} or {knight}?'}) node3 = node2:addChildKeyword({'sorcerer'}, StdModule.say, {npcHandler = npcHandler, vocation = 1, onlyFocus = true, text = 'So, you wish to be a powerful magician? Are you sure about that? This decision is irreversible!'}) node3:addChildKeywordNode(yesNode) node3:addChildKeywordNode(noNode) diff --git a/data/npc/scripts/promotion.lua b/data/npc/scripts/promotion.lua index d75a554..fdea829 100644 --- a/data/npc/scripts/promotion.lua +++ b/data/npc/scripts/promotion.lua @@ -2,10 +2,12 @@ local keywordHandler = KeywordHandler:new() local npcHandler = NpcHandler:new(keywordHandler) NpcSystem.parseParameters(npcHandler) -function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end -function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end -function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end -function onThink() npcHandler:onThink() end +function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end +function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end +function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end +function onThink() npcHandler:onThink() end +function onPlayerEndTrade(cid) npcHandler:onPlayerEndTrade(cid) end +function onPlayerCloseChannel(cid) npcHandler:onPlayerCloseChannel(cid) end local node1 = keywordHandler:addKeyword({'promot'}, StdModule.say, {npcHandler = npcHandler, onlyFocus = true, text = 'I can promote you for 20000 gold coins. Do you want me to promote you?'}) node1:addChildKeyword({'yes'}, StdModule.promotePlayer, {npcHandler = npcHandler, cost = 20000, level = 20, promotion = 1, text = 'Congratulations! You are now promoted.'}) diff --git a/data/npc/scripts/runes.lua b/data/npc/scripts/runes.lua index 0af854c..9f195d1 100644 --- a/data/npc/scripts/runes.lua +++ b/data/npc/scripts/runes.lua @@ -3,17 +3,23 @@ local npcHandler = NpcHandler:new(keywordHandler) NpcSystem.parseParameters(npcHandler) local talkState = {} -function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end -function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end -function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end -function onThink() npcHandler:onThink() end +function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end +function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end +function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end +function onThink() npcHandler:onThink() end +function onPlayerEndTrade(cid) npcHandler:onPlayerEndTrade(cid) end +function onPlayerCloseChannel(cid) npcHandler:onPlayerCloseChannel(cid) end local shopModule = ShopModule:new() npcHandler:addModule(shopModule) -shopModule:addBuyableItem({'spellbook'}, 2175, 150, 'spellbook') +shopModule:addBuyableItem({'spellbook'}, 2175, 150, 1, 'spellbook') shopModule:addBuyableItem({'magic lightwand'}, 2163, 400, 'magic lightwand') +shopModule:addSellableItem({'normal potion flask', 'normal flask'}, 7636, 5, 'empty small potion flask') +shopModule:addSellableItem({'strong potion flask', 'strong flask'}, 7634, 10, 'empty strong potion flask') +shopModule:addSellableItem({'great potion flask', 'great flask'}, 7635, 15, 'empty great potion flask') + shopModule:addBuyableItem({'small health'}, 8704, 20, 1, 'small health potion') shopModule:addBuyableItem({'health potion'}, 7618, 45, 1, 'health potion') shopModule:addBuyableItem({'mana potion'}, 7620, 50, 1, 'mana potion') @@ -25,72 +31,77 @@ shopModule:addBuyableItem({'great spirit'}, 8472, 190, 1, 'great spirit potion') shopModule:addBuyableItem({'ultimate health'}, 8473, 310, 1, 'ultimate health potion') shopModule:addBuyableItem({'antidote potion'}, 8474, 50, 1, 'antidote potion') -shopModule:addSellableItem({'normal potion flask', 'normal flask'}, 7636, 5, 'empty small potion flask') -shopModule:addSellableItem({'strong potion flask', 'strong flask'}, 7634, 10, 'empty strong potion flask') -shopModule:addSellableItem({'great potion flask', 'great flask'}, 7635, 15, 'empty great potion flask') - -shopModule:addBuyableItem({'instense healing'}, 2265, 95, 1, 'intense healing rune') -shopModule:addBuyableItem({'ultimate healing'}, 2273, 175, 1, 'ultimate healing rune') -shopModule:addBuyableItem({'magic wall'}, 2293, 350, 3, 'magic wall rune') -shopModule:addBuyableItem({'destroy field'}, 2261, 45, 3, 'destroy field rune') -shopModule:addBuyableItem({'light magic missile'}, 2287, 40, 10, 'light magic missile rune') -shopModule:addBuyableItem({'heavy magic missile'}, 2311, 120, 10, 'heavy magic missile rune') -shopModule:addBuyableItem({'great fireball'}, 2304, 180, 4, 'great fireball rune') -shopModule:addBuyableItem({'explosion'}, 2313, 250, 6, 'explosion rune') -shopModule:addBuyableItem({'sudden death'}, 2268, 350, 3, 'sudden death rune') -shopModule:addBuyableItem({'death arrow'}, 2263, 300, 3, 'death arrow rune') -shopModule:addBuyableItem({'paralyze'}, 2278, 700, 1, 'paralyze rune') shopModule:addBuyableItem({'animate dead'}, 2316, 375, 1, 'animate dead rune') -shopModule:addBuyableItem({'convince creature'}, 2290, 80, 1, 'convince creature rune') -shopModule:addBuyableItem({'chameleon'}, 2291, 210, 1, 'chameleon rune') -shopModule:addBuyableItem({'desintegrate'}, 2310, 80, 3, 'desintegreate rune') +shopModule:addBuyableItem({'blank rune'}, 2260, 10, 1, 'blank rune') +shopModule:addBuyableItem({'desintegrate'}, 2310, 26, 1, 'desintegrate rune') +shopModule:addBuyableItem({'energy bomb'}, 2262, 162, 1, 'energy bomb rune') +shopModule:addBuyableItem({'fireball'}, 2302, 30, 1, 'fireball rune') +shopModule:addBuyableItem({'holy missile'}, 2295, 16, 1, 'holy missile rune') +shopModule:addBuyableItem({'icicle'}, 2271, 30, 1, 'icicle rune') +shopModule:addBuyableItem({'magic wall'}, 2293, 116, 1, 'magic wall rune') +shopModule:addBuyableItem({'paralyze'}, 2278, 700, 1, 'paralyze rune') +shopModule:addBuyableItem({'poison bomb'}, 2286, 85, 1, 'poison bomb rune') +shopModule:addBuyableItem({'soulfire'}, 2308, 46, 1, 'soulfire rune') +shopModule:addBuyableItem({'stone shower'}, 2288, 37, 1, 'stone shower rune') +shopModule:addBuyableItem({'thunderstorm'}, 2315, 37, 1, 'thunderstorm rune') +shopModule:addBuyableItem({'wild growth'}, 2269, 160, 1, 'wild growth rune') -shopModule:addBuyableItemContainer({'bp slhp'}, 2000, 8704, 400, 1, 'backpack of small health potions') -shopModule:addBuyableItemContainer({'bp hp'}, 2000, 7618, 900, 1, 'backpack of health potions') -shopModule:addBuyableItemContainer({'bp mp'}, 2001, 7620, 1000, 1, 'backpack of mana potions') -shopModule:addBuyableItemContainer({'bp shp'}, 2000, 7588, 2000, 1, 'backpack of strong health potions') -shopModule:addBuyableItemContainer({'bp smp'}, 2001, 7589, 1600, 1, 'backpack of strong mana potions') -shopModule:addBuyableItemContainer({'bp ghp'}, 2000, 7591, 3800, 1, 'backpack of great health potions') -shopModule:addBuyableItemContainer({'bp gmp'}, 2001, 7590, 2400, 1, 'backpack of great mana potions') -shopModule:addBuyableItemContainer({'bp gsp'}, 1999, 8472, 3800, 1, 'backpack of great spirit potions') -shopModule:addBuyableItemContainer({'bp uhp'}, 2000, 8473, 6200, 1, 'backpack of ultimate health potions') -shopModule:addBuyableItemContainer({'bp ap'}, 2002, 8474, 2000, 1, 'backpack of antidote potions') +shopModule:addBuyableItem({'avalanche'}, 2274, 45, 1, 'avalanche rune') +shopModule:addBuyableItem({'antidote'}, 2266, 65, 1, 'antidote rune') +shopModule:addBuyableItem({'chameleon'}, 2291, 210, 1, 'chameleon rune') +shopModule:addBuyableItem({'convince creature'}, 2290, 80, 1, 'convince creature rune') +shopModule:addBuyableItem({'destroy field'}, 2261, 15, 1, 'destroy field rune') +shopModule:addBuyableItem({'energy field'}, 2277, 38, 1, 'energy field rune') +shopModule:addBuyableItem({'energy wall'}, 2279, 85, 1, 'energy wall rune') +shopModule:addBuyableItem({'explosion'}, 2313, 31, 1, 'explosion rune') +shopModule:addBuyableItem({'fire bomb'}, 2305, 117, 1, 'fire bomb rune') +shopModule:addBuyableItem({'fire field'}, 2301, 28, 1, 'fire field rune') +shopModule:addBuyableItem({'fire wall'}, 2303, 61, 1, 'fire wall rune') +shopModule:addBuyableItem({'great fireball'}, 2304, 45, 1, 'great fireball rune') +shopModule:addBuyableItem({'heavy magic missile'}, 2311, 12, 1, 'heavy magic missile rune') +shopModule:addBuyableItem({'intense healing'}, 2265, 95, 1, 'intense healing rune') +shopModule:addBuyableItem({'light magic missile'}, 2287, 4, 1, 'light magic missile rune') +shopModule:addBuyableItem({'poison field'}, 2285, 21, 1, 'poison field rune') +shopModule:addBuyableItem({'poison wall'}, 2289, 52, 1, 'poison wall rune') +shopModule:addBuyableItem({'stalagmite'}, 2292, 12, 1, 'stalagmite rune') +shopModule:addBuyableItem({'sudden death'}, 2268, 108, 1, 'sudden death rune') +shopModule:addBuyableItem({'ultimate healing'}, 2273, 175, 1, 'ultimate healing rune') -shopModule:addBuyableItem({'wand of vortex', 'vortex'}, 2190, 500, 'wand of vortex') -shopModule:addBuyableItem({'wand of dragonbreath', 'dragonbreath'}, 2191, 1000, 'wand of dragonbreath') -shopModule:addBuyableItem({'wand of decay', 'decay'}, 2188, 5000, 'wand of decay') -shopModule:addBuyableItem({'wand of draconia', 'draconia'}, 8921, 7500, 'wand of draconia') -shopModule:addBuyableItem({'wand of cosmic energy', 'cosmic energy'}, 2189, 10000, 'wand of cosmic energy') -shopModule:addBuyableItem({'wand of inferno', 'inferno'}, 2187, 15000, 'wand of inferno') -shopModule:addBuyableItem({'wand of starstorm', 'starstorm'}, 8920, 18000, 'wand of starstorm') -shopModule:addBuyableItem({'wand of voodoo', 'voodoo'}, 8922, 22000, 'wand of voodoo') +shopModule:addBuyableItem({'wand of vortex', 'vortex'}, 2190, 500, 1, 'wand of vortex') +shopModule:addBuyableItem({'wand of dragonbreath', 'dragonbreath'}, 2191, 1000, 1, 'wand of dragonbreath') +shopModule:addBuyableItem({'wand of decay', 'decay'}, 2188, 5000, 1, 'wand of decay') +shopModule:addBuyableItem({'wand of draconia', 'draconia'}, 8921, 7500, 1, 'wand of draconia') +shopModule:addBuyableItem({'wand of cosmic energy', 'cosmic energy'}, 2189, 10000, 1, 'wand of cosmic energy') +shopModule:addBuyableItem({'wand of inferno', 'inferno'}, 2187, 15000, 1, 'wand of inferno') +shopModule:addBuyableItem({'wand of starstorm', 'starstorm'}, 8920, 18000, 1, 'wand of starstorm') +shopModule:addBuyableItem({'wand of voodoo', 'voodoo'}, 8922, 22000, 1, 'wand of voodoo') -shopModule:addBuyableItem({'snakebite rod', 'snakebite'}, 2182, 500, 'snakebite rod') -shopModule:addBuyableItem({'moonlight rod', 'moonlight'}, 2186, 1000, 'moonlight rod') -shopModule:addBuyableItem({'necrotic rod', 'necrotic'}, 2185, 5000, 'necrotic rod') -shopModule:addBuyableItem({'northwind rod', 'northwind'}, 8911, 7500, 'northwind rod') -shopModule:addBuyableItem({'terra rod', 'terra'}, 2181, 10000, 'terra rod') -shopModule:addBuyableItem({'hailstorm rod', 'hailstorm'}, 2183, 15000, 'hailstorm rod') -shopModule:addBuyableItem({'springsprout rod', 'springsprout'}, 8912, 18000, 'springsprout rod') -shopModule:addBuyableItem({'underworld rod', 'underworld'}, 8910, 22000, 'underworld rod') +shopModule:addBuyableItem({'snakebite rod', 'snakebite'}, 2182, 500, 1, 'snakebite rod') +shopModule:addBuyableItem({'moonlight rod', 'moonlight'}, 2186, 1000, 1, 'moonlight rod') +shopModule:addBuyableItem({'necrotic rod', 'necrotic'}, 2185, 5000, 1, 'necrotic rod') +shopModule:addBuyableItem({'northwind rod', 'northwind'}, 8911, 7500, 1, 'northwind rod') +shopModule:addBuyableItem({'terra rod', 'terra'}, 2181, 10000, 1, 'terra rod') +shopModule:addBuyableItem({'hailstorm rod', 'hailstorm'}, 2183, 15000, 1, 'hailstorm rod') +shopModule:addBuyableItem({'springsprout rod', 'springsprout'}, 8912, 18000, 1, 'springsprout rod') +shopModule:addBuyableItem({'underworld rod', 'underworld'}, 8910, 22000, 1, 'underworld rod') -shopModule:addSellableItem({'wand of vortex', 'vortex'}, 2190, 250, 'wand of vortex') -shopModule:addSellableItem({'wand of dragonbreath', 'dragonbreath'}, 2191, 500, 'wand of dragonbreath') -shopModule:addSellableItem({'wand of decay', 'decay'}, 2188, 2500, 'wand of decay') -shopModule:addSellableItem({'wand of draconia', 'draconia'}, 8921, 3750, 'wand of draconia') -shopModule:addSellableItem({'wand of cosmic energy', 'cosmic energy'}, 2189, 5000, 'wand of cosmic energy') -shopModule:addSellableItem({'wand of inferno', 'inferno'},2187, 7500, 'wand of inferno') -shopModule:addSellableItem({'wand of starstorm', 'starstorm'}, 8920, 9000, 'wand of starstorm') -shopModule:addSellableItem({'wand of voodoo', 'voodoo'}, 8922, 11000, 'wand of voodoo') +shopModule:addSellableItem({'wand of vortex', 'vortex'}, 2190, 100, 'wand of vortex') +shopModule:addSellableItem({'wand of dragonbreath', 'dragonbreath'}, 2191, 200, 'wand of dragonbreath') +shopModule:addSellableItem({'wand of decay', 'decay'}, 2188, 1000, 'wand of decay') +shopModule:addSellableItem({'wand of draconia', 'draconia'}, 8921, 1500, 'wand of draconia') +shopModule:addSellableItem({'wand of cosmic energy', 'cosmic energy'}, 2189, 2000, 'wand of cosmic energy') +shopModule:addSellableItem({'wand of inferno', 'inferno'}, 2187, 3000, 'wand of inferno') +shopModule:addSellableItem({'wand of starstorm', 'starstorm'}, 8920, 3600, 'wand of starstorm') +shopModule:addSellableItem({'wand of voodoo', 'voodoo'}, 8922, 4400, 'wand of voodoo') -shopModule:addSellableItem({'snakebite rod', 'snakebite'}, 2182, 250,'snakebite rod') -shopModule:addSellableItem({'moonlight rod', 'moonlight'}, 2186, 500, 'moonlight rod') -shopModule:addSellableItem({'necrotic rod', 'necrotic'}, 2185, 2500, 'necrotic rod') -shopModule:addSellableItem({'northwind rod', 'northwind'}, 8911, 3750, 'northwind rod') -shopModule:addSellableItem({'terra rod', 'terra'}, 2181, 5000, 'terra rod') -shopModule:addSellableItem({'hailstorm rod', 'hailstorm'}, 2183, 7500, 'hailstorm rod') -shopModule:addSellableItem({'springsprout rod', 'springsprout'}, 8912, 9000, 'springsprout rod') -shopModule:addSellableItem({'underworld rod', 'underworld'}, 8910, 11000, 'underworld rod') +shopModule:addSellableItem({'snakebite rod', 'snakebite'}, 2182, 100, 'snakebite rod') +shopModule:addSellableItem({'moonlight rod', 'moonlight'}, 2186, 200, 'moonlight rod') +shopModule:addSellableItem({'necrotic rod', 'necrotic'}, 2185, 1000, 'necrotic rod') +shopModule:addSellableItem({'northwind rod', 'northwind'}, 8911, 1500, 'northwind rod') +shopModule:addSellableItem({'terra rod', 'terra'}, 2181, 2000, 'terra rod') +shopModule:addSellableItem({'hailstorm rod', 'hailstorm'}, 2183, 3000, 'hailstorm rod') +shopModule:addSellableItem({'springsprout rod', 'springsprout'}, 8912, 3600, 'springsprout rod') +shopModule:addSellableItem({'underworld rod', 'underworld'}, 8910, 4400, 'underworld rod') local items = {[1] = 2190, [2] = 2182, [5] = 2190, [6] = 2182} function creatureSayCallback(cid, type, msg) diff --git a/data/raids/lib/raids.lua b/data/raids/lib/raids.lua deleted file mode 100644 index e69de29..0000000 diff --git a/data/spells/lib/spells.lua b/data/spells/lib/spells.lua index cf10bae..a0595a1 100644 --- a/data/spells/lib/spells.lua +++ b/data/spells/lib/spells.lua @@ -1,4 +1,4 @@ ---Waves +-- Waves AREA_WAVE4 = { {1, 1, 1, 1, 1}, {0, 1, 1, 1, 0}, @@ -22,7 +22,13 @@ AREA_WAVE5 = { {0, 0, 3, 0, 0} } ---Diagonal waves +AREA_WAVE6 = { + {0, 0, 0, 0, 0}, + {0, 1, 3, 1, 0}, + {0, 0, 0, 0, 0} +} + +-- Diagonal waves AREADIAGONAL_WAVE4 = { {0, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 0}, @@ -49,13 +55,18 @@ AREADIAGONAL_WAVE5 = { {0, 0, 0, 0, 0, 3} } ---Beams +AREADIAGONAL_WAVE6 = { + {0, 0, 1}, + {0, 3, 0}, + {1, 0, 0} +} + +-- Beams AREA_BEAM1 = { {3} } -AREA_BEAM5 = { - {1}, +AREA_BEAM4 = { {1}, {1}, {1}, @@ -63,8 +74,7 @@ AREA_BEAM5 = { {3} } -AREA_BEAM7 = { - {1}, +AREA_BEAM6 = { {1}, {1}, {1}, @@ -74,28 +84,26 @@ AREA_BEAM7 = { {3} } ---Diagonal Beams -AREADIAGONAL_BEAM5 = { - {1, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0}, - {0, 0, 0, 1, 0, 0}, - {0, 0, 0, 0, 1, 0}, - {0, 0, 0, 0, 0, 3} +-- Diagonal Beams +AREADIAGONAL_BEAM4 = { + {1, 0, 0, 0, 0}, + {0, 1, 0, 0, 0}, + {0, 0, 1, 0, 0}, + {0, 0, 0, 1, 0}, + {0, 0, 0, 0, 3} } -AREADIAGONAL_BEAM7 = { - {1, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0}, - {0, 0, 0, 0, 0, 0, 1, 0}, - {0, 0, 0, 0, 0, 0, 0, 3} +AREADIAGONAL_BEAM6 = { + {1, 0, 0, 0, 0, 0, 0}, + {0, 1, 0, 0, 0, 0, 0}, + {0, 0, 1, 0, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 0, 1, 0, 0}, + {0, 0, 0, 0, 0, 1, 0}, + {0, 0, 0, 0, 0, 0, 3} } ---Circles +-- Circles AREA_CIRCLE2X2 = { {0, 1, 1, 1, 0}, {1, 1, 1, 1, 1}, @@ -151,7 +159,7 @@ AREA_CROSS6X6 = { {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0} } ---Squares +-- Squares AREA_SQUARE1X1 = { {1, 1, 1}, {1, 3, 1}, diff --git a/data/spells/scripts/attack/annihilation.lua b/data/spells/scripts/attack/annihilation.lua new file mode 100644 index 0000000..34cc64d --- /dev/null +++ b/data/spells/scripts/attack/annihilation.lua @@ -0,0 +1,17 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) +setCombatParam(combat, COMBAT_PARAM_BLOCKARMOR, true) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_HITAREA) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_WEAPONTYPE) +setCombatParam(combat, COMBAT_PARAM_USECHARGES, true) + +function onGetFormulaValues(cid, level, skill, attack, element, factor) + local levelTotal, formula = level / 5, 0.1606 + local normal, elemental = -(skill * attack * formula + levelTotal), math.ceil((skill * element * formula + levelTotal)) + return normal/2, normal, -math.random(elemental/2, elemental) +end + +setCombatCallback(combat, CALLBACK_PARAM_SKILLVALUE, "onGetFormulaValues") +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/berserk.lua b/data/spells/scripts/attack/berserk.lua index c401d9f..177f8f8 100644 --- a/data/spells/scripts/attack/berserk.lua +++ b/data/spells/scripts/attack/berserk.lua @@ -6,9 +6,10 @@ setCombatParam(combat, COMBAT_PARAM_USECHARGES, true) local area = createCombatArea(AREA_SQUARE1X1) setCombatArea(combat, area) -function onGetFormulaValues(cid, level, skill, attack, factor) - local skillTotal, levelTotal = skill + attack, level / 5 - return -(skillTotal * 0.5 + levelTotal), -(skillTotal * 1.5 + levelTotal) +function onGetFormulaValues(cid, level, skill, attack, element, factor) + local levelTotal, formula = level / 5, 0.0496 + local normal, elemental = -(skill * attack * formula + levelTotal), math.ceil((skill * element * formula + levelTotal)) + return normal/2, normal, -math.random(elemental/2, elemental) end setCombatCallback(combat, CALLBACK_PARAM_SKILLVALUE, "onGetFormulaValues") diff --git a/data/spells/scripts/attack/brutal strike.lua b/data/spells/scripts/attack/brutal strike.lua new file mode 100644 index 0000000..1884570 --- /dev/null +++ b/data/spells/scripts/attack/brutal strike.lua @@ -0,0 +1,17 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) +setCombatParam(combat, COMBAT_PARAM_BLOCKARMOR, true) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_HITAREA) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_WEAPONTYPE) +setCombatParam(combat, COMBAT_PARAM_USECHARGES, true) + +function onGetFormulaValues(cid, level, skill, attack, element, factor) + local levelTotal, formula = level / 5, 0.0408 + local normal, elemental = -(skill * attack * formula + levelTotal), math.ceil((skill * element * formula + levelTotal)) + return normal/2, normal, -math.random(elemental/2, elemental) +end + +setCombatCallback(combat, CALLBACK_PARAM_SKILLVALUE, "onGetFormulaValues") +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/curse.lua b/data/spells/scripts/attack/curse.lua new file mode 100644 index 0000000..559a9a2 --- /dev/null +++ b/data/spells/scripts/attack/curse.lua @@ -0,0 +1,30 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_SMALLCLOUDS) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_DEATH) + +local condition = createConditionObject(CONDITION_CURSED) +setConditionParam(condition, CONDITION_PARAM_DELAYED, 1) + +addDamageCondition(condition, 1, 3000, -45) +addDamageCondition(condition, 1, 3000, -40) +addDamageCondition(condition, 1, 3000, -35) +addDamageCondition(condition, 1, 3000, -34) +addDamageCondition(condition, 2, 3000, -33) +addDamageCondition(condition, 2, 3000, -32) +addDamageCondition(condition, 2, 3000, -31) +addDamageCondition(condition, 2, 3000, -30) +addDamageCondition(condition, 3, 3000, -29) +addDamageCondition(condition, 3, 3000, -25) +addDamageCondition(condition, 3, 3000, -24) +addDamageCondition(condition, 4, 3000, -23) +addDamageCondition(condition, 4, 3000, -20) +addDamageCondition(condition, 5, 3000, -19) +addDamageCondition(condition, 5, 3000, -15) +addDamageCondition(condition, 6, 3000, -10) +addDamageCondition(condition, 10, 3000, -5) +setCombatCondition(combat, condition) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end \ No newline at end of file diff --git a/data/spells/scripts/attack/electrify.lua b/data/spells/scripts/attack/electrify.lua new file mode 100644 index 0000000..274057c --- /dev/null +++ b/data/spells/scripts/attack/electrify.lua @@ -0,0 +1,13 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ENERGYHIT) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ENERGY) + +local condition = createConditionObject(CONDITION_ENERGY) +setConditionParam(condition, CONDITION_PARAM_DELAYED, 1) +addDamageCondition(condition, 25, 3000, -45) +setCombatCondition(combat, condition) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/energy beam.lua b/data/spells/scripts/attack/energy beam.lua index 916dc8f..d1c7c3e 100644 --- a/data/spells/scripts/attack/energy beam.lua +++ b/data/spells/scripts/attack/energy beam.lua @@ -3,7 +3,7 @@ setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE) setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ENERGYHIT) setAttackFormula(combat, COMBAT_FORMULA_LEVELMAGIC, 5, 5, 2.5, 4) -local area = createCombatArea(AREA_BEAM5, AREADIAGONAL_BEAM5) +local area = createCombatArea(AREA_BEAM4, AREADIAGONAL_BEAM4) setCombatArea(combat, area) function onCastSpell(cid, var) diff --git a/data/spells/scripts/attack/envenom.lua b/data/spells/scripts/attack/envenom.lua index 2e9b492..6a8bb42 100644 --- a/data/spells/scripts/attack/envenom.lua +++ b/data/spells/scripts/attack/envenom.lua @@ -1,17 +1,14 @@ local combat = createCombatObject() -setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_POISONDAMAGE) -setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_POISONAREA) -setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_POISON) +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_EARTHDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_SMALLPLANTS) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_EARTH) local condition = createConditionObject(CONDITION_POISON) -setConditionParam(condition, CONDITION_PARAM_DELAYED, true) -setConditionParam(condition, CONDITION_PARAM_MINVALUE, 20) -setConditionParam(condition, CONDITION_PARAM_MAXVALUE, 70) -setConditionParam(condition, CONDITION_PARAM_STARTVALUE, 5) -setConditionParam(condition, CONDITION_PARAM_TICKINTERVAL, 6000) -setConditionParam(condition, CONDITION_PARAM_FORCEUPDATE, true) +setConditionParam(condition, CONDITION_PARAM_DELAYED, 1) +addDamageCondition(condition, 25, 3000, -45) setCombatCondition(combat, condition) + function onCastSpell(cid, var) return doCombat(cid, combat, var) -end +end \ No newline at end of file diff --git a/data/spells/scripts/attack/ethereal spear.lua b/data/spells/scripts/attack/ethereal spear.lua index 9cf905a..a5576ce 100644 --- a/data/spells/scripts/attack/ethereal spear.lua +++ b/data/spells/scripts/attack/ethereal spear.lua @@ -3,8 +3,8 @@ setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) setCombatParam(combat, COMBAT_PARAM_BLOCKARMOR, true) setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ETHEREALSPEAR) -function onGetFormulaValues(cid, level, skill, attack, factor) - return -(((skill + 25) / 3) + (level / 5)), -((skill + 25) + (level / 5)) +function onGetFormulaValues(cid, level, skill) + return -(((skill + 25) / 3) + (level / 5)), -((skill + 25) + (level / 5)), 0 end setCombatCallback(combat, CALLBACK_PARAM_SKILLVALUE, "onGetFormulaValues") diff --git a/data/spells/scripts/attack/fierce berserk.lua b/data/spells/scripts/attack/fierce berserk.lua index 5bd0b4b..b81a49e 100644 --- a/data/spells/scripts/attack/fierce berserk.lua +++ b/data/spells/scripts/attack/fierce berserk.lua @@ -6,9 +6,10 @@ setCombatParam(combat, COMBAT_PARAM_USECHARGES, true) local area = createCombatArea(AREA_SQUARE1X1) setCombatArea(combat, area) -function onGetFormulaValues(cid, level, skill, attack, factor) - local skillTotal, levelTotal = skill + attack * 2, level / 5 - return -(skillTotal * 1.1 + levelTotal), -(skillTotal * 3 + levelTotal) +function onGetFormulaValues(cid, level, skill, attack, element, factor) + local levelTotal, formula = level / 5, 0.1126 + local normal, elemental = -(skill * attack * formula + levelTotal), math.ceil((skill * element * formula + levelTotal)) + return normal/2, normal, -math.random(elemental/2, elemental) end setCombatCallback(combat, CALLBACK_PARAM_SKILLVALUE, "onGetFormulaValues") diff --git a/data/spells/scripts/attack/front sweep.lua b/data/spells/scripts/attack/front sweep.lua new file mode 100644 index 0000000..9b5bfa0 --- /dev/null +++ b/data/spells/scripts/attack/front sweep.lua @@ -0,0 +1,17 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_HITAREA) + +local area = createCombatArea(AREA_WAVE6, AREADIAGONAL_WAVE6) +setCombatArea(combat, area) + +function onGetFormulaValues(cid, level, skill, attack, element, factor) + local levelTotal, formula = level / 5, 0.0854 + local normal, elemental = -(skill * attack * formula + levelTotal), math.ceil((skill * element * formula + levelTotal)) + return normal/2, normal, -math.random(elemental/2, elemental) +end + +setCombatCallback(combat, CALLBACK_PARAM_SKILLVALUE, "onGetFormulaValues") +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/great energy beam.lua b/data/spells/scripts/attack/great energy beam.lua index 2da3a6d..873c95a 100644 --- a/data/spells/scripts/attack/great energy beam.lua +++ b/data/spells/scripts/attack/great energy beam.lua @@ -1,9 +1,9 @@ local combat = createCombatObject() setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE) -setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ENERGYHIT) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ENERGYAREA) setAttackFormula(combat, COMBAT_FORMULA_LEVELMAGIC, 5, 5, 4, 7) -local area = createCombatArea(AREA_BEAM7, AREADIAGONAL_BEAM7) +local area = createCombatArea(AREA_BEAM6, AREADIAGONAL_BEAM6) setCombatArea(combat, area) function onCastSpell(cid, var) diff --git a/data/spells/scripts/attack/groundshaker.lua b/data/spells/scripts/attack/groundshaker.lua index c627f71..112f11d 100644 --- a/data/spells/scripts/attack/groundshaker.lua +++ b/data/spells/scripts/attack/groundshaker.lua @@ -6,9 +6,11 @@ setCombatParam(combat, COMBAT_PARAM_USECHARGES, true) local area = createCombatArea(AREA_CIRCLE3X3) setCombatArea(combat, area) -function onGetFormulaValues(cid, level, skill, attack, factor) - local skillTotal, levelTotal = skill + attack, level / 5 - return -(skillTotal * 0.5 + levelTotal), -(skillTotal * 1.1 + levelTotal) +function onGetFormulaValues(cid, level, skill, attack, element, factor) + + local attackTotal, elementTotal, levelTotal = skill + attack, skill + element, level / 9 + return -(attackTotal * 0.53 + levelTotal), -(attackTotal * 1.0 + levelTotal), + -math.random(math.ceil(elementTotal * 0.5), math.ceil(elementTotal * 0.9)) end setCombatCallback(combat, CALLBACK_PARAM_SKILLVALUE, "onGetFormulaValues") diff --git a/data/spells/scripts/attack/holy flash.lua b/data/spells/scripts/attack/holy flash.lua new file mode 100644 index 0000000..23f0f45 --- /dev/null +++ b/data/spells/scripts/attack/holy flash.lua @@ -0,0 +1,12 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_HOLYDAMAGE) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_SMALLHOLY) + +local condition = createConditionObject(CONDITION_DAZZLED) +setConditionParam(condition, CONDITION_PARAM_DELAYED, 1) +addDamageCondition(condition, math.random(7,11), 3000, -20) +setCombatCondition(combat, condition) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end \ No newline at end of file diff --git a/data/spells/scripts/attack/soul fire.lua b/data/spells/scripts/attack/ignite.lua similarity index 62% rename from data/spells/scripts/attack/soul fire.lua rename to data/spells/scripts/attack/ignite.lua index 5167819..651c452 100644 --- a/data/spells/scripts/attack/soul fire.lua +++ b/data/spells/scripts/attack/ignite.lua @@ -1,14 +1,12 @@ local combat = createCombatObject() -setCombatParam(combat, COMBAT_PARAM_TARGETCASTERORTOPMOST, true) setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_FIREDAMAGE) -setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_HITBYFIRE) setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_FIRE) local condition = createConditionObject(CONDITION_FIRE) setConditionParam(condition, CONDITION_PARAM_DELAYED, 1) -addDamageCondition(condition, 10, 2000, -10) +addDamageCondition(condition, 25, 3000, -45) setCombatCondition(combat, condition) function onCastSpell(cid, var) - return doCombat(cid, combat, var) -end + return doCombat(cid, combat, var) +end \ No newline at end of file diff --git a/data/spells/scripts/attack/inflict wound.lua b/data/spells/scripts/attack/inflict wound.lua new file mode 100644 index 0000000..3bd0049 --- /dev/null +++ b/data/spells/scripts/attack/inflict wound.lua @@ -0,0 +1,17 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) + +setCombatParam(combat, COMBAT_PARAM_TARGETCASTERORTOPMOST, true) +setCombatParam(combat, COMBAT_PARAM_EFFECT, 0) +setCombatParam(combat, COMBAT_PARAM_USECHARGES, true) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_WEAPONTYPE) + +local condition = createConditionObject(CONDITION_PHYSICAL) +setConditionParam(condition, CONDITION_PARAM_DELAYED, 10) +addDamageCondition(condition, 15, 2000, -50) +setCombatCondition(combat, condition) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end + diff --git a/data/spells/scripts/attack/lightning.lua b/data/spells/scripts/attack/lightning.lua new file mode 100644 index 0000000..bf710d0 --- /dev/null +++ b/data/spells/scripts/attack/lightning.lua @@ -0,0 +1,9 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ENERGYAREA) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ENERGY) +setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -20, -1, -40, 5, 10, 1.4, 4.1) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/physical strike.lua b/data/spells/scripts/attack/physical strike.lua new file mode 100644 index 0000000..a360043 --- /dev/null +++ b/data/spells/scripts/attack/physical strike.lua @@ -0,0 +1,9 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_EXPLOSIONAREA) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_EXPLOSION) +setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -10, -1, -20, 5, 5, 1.4, 2.1) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/strong energy strike.lua b/data/spells/scripts/attack/strong energy strike.lua new file mode 100644 index 0000000..7b561dc --- /dev/null +++ b/data/spells/scripts/attack/strong energy strike.lua @@ -0,0 +1,9 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ENERGYAREA) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ENERGYBALL) +setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -10, -1, -20, 10, 10, 2.4, 2.4) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/strong ethereal spear.lua b/data/spells/scripts/attack/strong ethereal spear.lua new file mode 100644 index 0000000..6c08221 --- /dev/null +++ b/data/spells/scripts/attack/strong ethereal spear.lua @@ -0,0 +1,13 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) +setCombatParam(combat, COMBAT_PARAM_BLOCKARMOR, true) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ETHEREALSPEAR) + +function onGetFormulaValues(cid, level, skill) + return -(((skill + 25) / 3) * 2 + (level / 5)) * 2, -((skill + 25) + (level / 5 * 2)) * 2, 0 +end +setCombatCallback(combat, CALLBACK_PARAM_SKILLVALUE, "onGetFormulaValues") + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/strong flame strike.lua b/data/spells/scripts/attack/strong flame strike.lua new file mode 100644 index 0000000..449bb0e --- /dev/null +++ b/data/spells/scripts/attack/strong flame strike.lua @@ -0,0 +1,9 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_FIREDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_FIREAREA) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_FIRE) +setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -10, -1, -20, 10, 10, 2.4, 2.4) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/strong ice strike.lua b/data/spells/scripts/attack/strong ice strike.lua new file mode 100644 index 0000000..9bc4305 --- /dev/null +++ b/data/spells/scripts/attack/strong ice strike.lua @@ -0,0 +1,9 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ICEDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ICEATTACK) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ICE) +setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -10, -1, -20, 10, 10, 2.4, 2.4) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/strong ice wave.lua b/data/spells/scripts/attack/strong ice wave.lua new file mode 100644 index 0000000..e7c8cc2 --- /dev/null +++ b/data/spells/scripts/attack/strong ice wave.lua @@ -0,0 +1,12 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ICEDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ICEATTACK) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ICE) +setAttackFormula(combat, COMBAT_FORMULA_LEVELMAGIC, 5, 5, 4.5, 9) + +local area = createCombatArea(AREA_WAVE4, AREADIAGONAL_WAVE4) +setCombatArea(combat, area) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/strong terra strike.lua b/data/spells/scripts/attack/strong terra strike.lua new file mode 100644 index 0000000..005f7d2 --- /dev/null +++ b/data/spells/scripts/attack/strong terra strike.lua @@ -0,0 +1,9 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_EARTHDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_SMALLPLANTS) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_EARTH) +setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -10, -1, -20, 10, 10, 2.4, 2.4) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end \ No newline at end of file diff --git a/data/spells/scripts/attack/ultimate energy strike.lua b/data/spells/scripts/attack/ultimate energy strike.lua new file mode 100644 index 0000000..1a17cca --- /dev/null +++ b/data/spells/scripts/attack/ultimate energy strike.lua @@ -0,0 +1,9 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ENERGYAREA) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ENERGYBALL) +setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -10, -1, -20, 10, 10, 3.6, 3.6) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end \ No newline at end of file diff --git a/data/spells/scripts/attack/ultimate flame strike.lua b/data/spells/scripts/attack/ultimate flame strike.lua new file mode 100644 index 0000000..46fca3d --- /dev/null +++ b/data/spells/scripts/attack/ultimate flame strike.lua @@ -0,0 +1,9 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_FIREDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_FIREAREA) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_FIRE) +setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -10, -1, -20, 10, 10, 3.6, 3.6) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end \ No newline at end of file diff --git a/data/spells/scripts/attack/ultimate ice strike.lua b/data/spells/scripts/attack/ultimate ice strike.lua new file mode 100644 index 0000000..d804eaf --- /dev/null +++ b/data/spells/scripts/attack/ultimate ice strike.lua @@ -0,0 +1,9 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ICEDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ICEATTACK) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ICE) +setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -10, -1, -20, 10, 10, 3.6, 3.6) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end \ No newline at end of file diff --git a/data/spells/scripts/attack/ultimate terra strike.lua b/data/spells/scripts/attack/ultimate terra strike.lua new file mode 100644 index 0000000..3d6a899 --- /dev/null +++ b/data/spells/scripts/attack/ultimate terra strike.lua @@ -0,0 +1,9 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_EARTHDAMAGE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_SMALLPLANTS) +setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_EARTH) +setCombatFormula(combat, COMBAT_FORMULA_LEVELMAGIC, -1, -10, -1, -20, 10, 10, 3.6, 3.6) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/attack/whirlwind throw.lua b/data/spells/scripts/attack/whirlwind throw.lua index 0f1b26d..e7d9d33 100644 --- a/data/spells/scripts/attack/whirlwind throw.lua +++ b/data/spells/scripts/attack/whirlwind throw.lua @@ -1,12 +1,14 @@ local combat = createCombatObject() setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) setCombatParam(combat, COMBAT_PARAM_BLOCKARMOR, true) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_HITAREA) setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_WEAPONTYPE) setCombatParam(combat, COMBAT_PARAM_USECHARGES, true) -function onGetFormulaValues(cid, level, skill, attack, factor) - local skillTotal, levelTotal = skill + attack, level / 5 - return -(skillTotal / 3 + levelTotal), -(skillTotal + levelTotal) +function onGetFormulaValues(cid, level, skill, attack, element, factor) + local levelTotal, formula = level / 5, 0.0332 + local normal, elemental = -(skill * attack * formula + levelTotal), math.ceil((skill * element * formula + levelTotal)) + return normal/2, normal, -math.random(elemental/2, elemental) end setCombatCallback(combat, CALLBACK_PARAM_SKILLVALUE, "onGetFormulaValues") diff --git a/data/spells/scripts/attack/wrath of nature.lua b/data/spells/scripts/attack/wrath of nature.lua index e4b21ea..33d6599 100644 --- a/data/spells/scripts/attack/wrath of nature.lua +++ b/data/spells/scripts/attack/wrath of nature.lua @@ -1,6 +1,6 @@ local combat = createCombatObject() setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_EARTHDAMAGE) -setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_PLANTATTACK) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_SMALLPLANTS) setAttackFormula(combat, COMBAT_FORMULA_LEVELMAGIC, 5, 5, 5, 10) local area = createCombatArea(AREA_CROSS6X6) diff --git a/data/spells/scripts/healing/cure bleeding.lua b/data/spells/scripts/healing/cure bleeding.lua new file mode 100644 index 0000000..6d608d0 --- /dev/null +++ b/data/spells/scripts/healing/cure bleeding.lua @@ -0,0 +1,8 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) +setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) +setCombatParam(combat, COMBAT_PARAM_DISPEL, CONDITION_PHYSICAL) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/healing/cure burning.lua b/data/spells/scripts/healing/cure burning.lua new file mode 100644 index 0000000..61e3b0c --- /dev/null +++ b/data/spells/scripts/healing/cure burning.lua @@ -0,0 +1,8 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) +setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) +setCombatParam(combat, COMBAT_PARAM_DISPEL, CONDITION_FIRE) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/healing/cure curse.lua b/data/spells/scripts/healing/cure curse.lua new file mode 100644 index 0000000..cebff34 --- /dev/null +++ b/data/spells/scripts/healing/cure curse.lua @@ -0,0 +1,8 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) +setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) +setCombatParam(combat, COMBAT_PARAM_DISPEL, CONDITION_CURSED) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/healing/cure electrification.lua b/data/spells/scripts/healing/cure electrification.lua new file mode 100644 index 0000000..bde0aa5 --- /dev/null +++ b/data/spells/scripts/healing/cure electrification.lua @@ -0,0 +1,8 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) +setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) +setCombatParam(combat, COMBAT_PARAM_DISPEL, CONDITION_ENERGY) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/healing/antidote.lua b/data/spells/scripts/healing/cure poison.lua similarity index 100% rename from data/spells/scripts/healing/antidote.lua rename to data/spells/scripts/healing/cure poison.lua diff --git a/data/spells/scripts/healing/intense recovery.lua b/data/spells/scripts/healing/intense recovery.lua new file mode 100644 index 0000000..12bf9b9 --- /dev/null +++ b/data/spells/scripts/healing/intense recovery.lua @@ -0,0 +1,15 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) +setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) + +local condition = createConditionObject(CONDITION_REGENERATION) +setConditionParam(condition, CONDITION_PARAM_SUBID, 1) +setConditionParam(condition, CONDITION_PARAM_BUFF, true) +setConditionParam(condition, CONDITION_PARAM_TICKS, 1 * 60 * 1000) +setConditionParam(condition, CONDITION_PARAM_HEALTHGAIN, 40) +setConditionParam(condition, CONDITION_PARAM_HEALTHTICKS, 3000) +setCombatCondition(combat, condition) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/healing/intense wound cleansing.lua b/data/spells/scripts/healing/intense wound cleansing.lua new file mode 100644 index 0000000..8e8dddf --- /dev/null +++ b/data/spells/scripts/healing/intense wound cleansing.lua @@ -0,0 +1,10 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_HEALING) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) +setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) +setCombatParam(combat, COMBAT_PARAM_DISPEL, CONDITION_PARALYZE) +setHealingFormula(combat, COMBAT_FORMULA_LEVELMAGIC, 10, 10, 100.5, 150.10) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/healing/recovery.lua b/data/spells/scripts/healing/recovery.lua new file mode 100644 index 0000000..b0eb3c5 --- /dev/null +++ b/data/spells/scripts/healing/recovery.lua @@ -0,0 +1,15 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) +setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) + +local condition = createConditionObject(CONDITION_REGENERATION) +setConditionParam(condition, CONDITION_PARAM_SUBID, 1) +setConditionParam(condition, CONDITION_PARAM_BUFF, true) +setConditionParam(condition, CONDITION_PARAM_TICKS, 1 * 60 * 1000) +setConditionParam(condition, CONDITION_PARAM_HEALTHGAIN, 20) +setConditionParam(condition, CONDITION_PARAM_HEALTHTICKS, 3000) +setCombatCondition(combat, condition) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/healing/salvation.lua b/data/spells/scripts/healing/salvation.lua new file mode 100644 index 0000000..11d66be --- /dev/null +++ b/data/spells/scripts/healing/salvation.lua @@ -0,0 +1,10 @@ +local combat = createCombatObject() +setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_HEALING) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) +setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) +setCombatParam(combat, COMBAT_PARAM_DISPEL, CONDITION_PARALYZE) +setHealingFormula(combat, COMBAT_FORMULA_LEVELMAGIC, 5, 5, 27.5, 35) + +function onCastSpell(cid, var) + return doCombat(cid, combat, var) +end diff --git a/data/spells/scripts/healing/wound cleasing.lua b/data/spells/scripts/healing/wound cleansing.lua similarity index 100% rename from data/spells/scripts/healing/wound cleasing.lua rename to data/spells/scripts/healing/wound cleansing.lua diff --git a/data/spells/scripts/party/enchant.lua b/data/spells/scripts/party/enchant.lua index 7525f8c..f9bb916 100644 --- a/data/spells/scripts/party/enchant.lua +++ b/data/spells/scripts/party/enchant.lua @@ -12,7 +12,7 @@ setConditionParam(condition, CONDITION_PARAM_STAT_MAGICLEVEL, 1) local config = { baseMana = 120, - pvpManaSpent = getConfigValue("addManaSpentInPvPZone") + hardcoreManaSpent = getConfigValue("addManaSpentInPvPZone") } function onCastSpell(cid, var) @@ -51,7 +51,7 @@ function onCastSpell(cid, var) end doCreatureAddMana(cid, -(mana - config.baseMana), false) - if(not getPlayerFlagValue(cid, PlayerFlag_NotGainMana) and (not getTileInfo(getThingPosition(cid)).pvp or config.pvpManaSpent)) then + if(not getPlayerFlagValue(cid, PlayerFlag_NotGainMana) and (not getTileInfo(getThingPosition(cid)).hardcore or config.hardcoreManaSpent)) then doPlayerAddSpentMana(cid, (mana - config.baseMana)) end diff --git a/data/spells/scripts/party/heal.lua b/data/spells/scripts/party/heal.lua index bfb6813..821f3b9 100644 --- a/data/spells/scripts/party/heal.lua +++ b/data/spells/scripts/party/heal.lua @@ -5,7 +5,7 @@ setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) local condition = createConditionObject(CONDITION_REGENERATION) -setConditionParam(condition, CONDITION_PARAM_SUBID, 1) +setConditionParam(condition, CONDITION_PARAM_SUBID, 2) setConditionParam(condition, CONDITION_PARAM_BUFF, true) setConditionParam(condition, CONDITION_PARAM_TICKS, 2 * 60 * 1000) setConditionParam(condition, CONDITION_PARAM_HEALTHGAIN, 20) @@ -13,7 +13,7 @@ setConditionParam(condition, CONDITION_PARAM_HEALTHTICKS, 2000) local config = { baseMana = 120, - pvpManaSpent = getConfigValue("addManaSpentInPvPZone") + hardcoreManaSpent = getConfigValue("addManaSpentInPvPZone") } function onCastSpell(cid, var) @@ -52,7 +52,7 @@ function onCastSpell(cid, var) end doCreatureAddMana(cid, -(mana - config.baseMana), false) - if(not getPlayerFlagValue(cid, PlayerFlag_NotGainMana) and (not getTileInfo(getThingPosition(cid)).pvp or config.pvpManaSpent)) then + if(not getPlayerFlagValue(cid, PlayerFlag_NotGainMana) and (not getTileInfo(getThingPosition(cid)).hardcore or config.hardcoreManaSpent)) then doPlayerAddSpentMana(cid, (mana - config.baseMana)) end diff --git a/data/spells/scripts/party/protect.lua b/data/spells/scripts/party/protect.lua index 8074385..f90af36 100644 --- a/data/spells/scripts/party/protect.lua +++ b/data/spells/scripts/party/protect.lua @@ -12,7 +12,7 @@ setConditionParam(condition, CONDITION_PARAM_SKILL_SHIELD, 2) local config = { baseMana = 90, - pvpManaSpent = getConfigValue("addManaSpentInPvPZone") + hardcoreManaSpent = getConfigValue("addManaSpentInPvPZone") } function onCastSpell(cid, var) @@ -51,7 +51,7 @@ function onCastSpell(cid, var) end doCreatureAddMana(cid, -(mana - config.baseMana), false) - if(not getPlayerFlagValue(cid, PlayerFlag_NotGainMana) and (not getTileInfo(getThingPosition(cid)).pvp or config.pvpManaSpent)) then + if(not getPlayerFlagValue(cid, PlayerFlag_NotGainMana) and (not getTileInfo(getThingPosition(cid)).hardcore or config.hardcoreManaSpent)) then doPlayerAddSpentMana(cid, (mana - config.baseMana)) end diff --git a/data/spells/scripts/party/train.lua b/data/spells/scripts/party/train.lua index 2f7ad97..f8734c3 100644 --- a/data/spells/scripts/party/train.lua +++ b/data/spells/scripts/party/train.lua @@ -13,7 +13,7 @@ setConditionParam(condition, CONDITION_PARAM_SKILL_DISTANCE, 3) local config = { baseMana = 60, - pvpManaSpent = getConfigValue("addManaSpentInPvPZone") + hardcoreManaSpent = getConfigValue("addManaSpentInPvPZone") } function onCastSpell(cid, var) @@ -52,7 +52,7 @@ function onCastSpell(cid, var) end doCreatureAddMana(cid, -(mana - config.baseMana), false) - if(not getPlayerFlagValue(cid, PlayerFlag_NotGainMana) and (not getTileInfo(getThingPosition(cid)).pvp or config.pvpManaSpent)) then + if(not getPlayerFlagValue(cid, PlayerFlag_NotGainMana) and (not getTileInfo(getThingPosition(cid)).hardcore or config.hardcoreManaSpent)) then doPlayerAddSpentMana(cid, (mana - config.baseMana)) end diff --git a/data/spells/scripts/summon/animate dead rune.lua b/data/spells/scripts/summon/animate dead rune.lua deleted file mode 100644 index 72b9834..0000000 --- a/data/spells/scripts/summon/animate dead rune.lua +++ /dev/null @@ -1,29 +0,0 @@ -local function doTargetCorpse(cid, pos) - local getPos = pos - getPos.stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE - - local corpse = getThingFromPos(getPos) - if(corpse.uid > 0 and isCorpse(corpse.uid) and isMoveable(corpse.uid) and getCreatureSkullType(cid) ~= SKULL_BLACK) then - doRemoveItem(corpse.uid) - local creature = doCreateMonster(cid, "Skeleton", pos) - doConvinceCreature(cid, creature) - - doSendMagicEffect(pos, CONST_ME_MAGIC_BLUE) - return true - end - - doSendMagicEffect(getPlayerPosition(cid), CONST_ME_POFF) - doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - return false -end - -function onCastSpell(cid, var) - local pos = variantToPosition(var) - if(pos.x ~= 0 and pos.y ~= 0) then - return doTargetCorpse(cid, pos) - end - - doSendMagicEffect(getPlayerPosition(cid), CONST_ME_POFF) - doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - return false -end diff --git a/data/spells/scripts/summon/undead legion.lua b/data/spells/scripts/summon/undead legion.lua deleted file mode 100644 index 43f26ae..0000000 --- a/data/spells/scripts/summon/undead legion.lua +++ /dev/null @@ -1,25 +0,0 @@ -function onTargetTile(cid, pos) - local getPos = pos - getPos.stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE - - local corpse = getThingFromPos(getPos) - if(corpse.uid > 0 and isCorpse(corpse.uid) and isMoveable(corpse.uid) and getCreatureSkullType(cid) ~= SKULL_BLACK) then - doRemoveItem(corpse.uid) - doConvinceCreature(cid, doCreateMonster("Skeleton", pos)) - - doSendMagicEffect(pos, CONST_ME_MAGIC_BLUE) - return true - end - - return false -end - -local area, combat = createCombatArea(AREA_CIRCLE3X3), createCombatObject() -setCombatArea(combat, area) - -setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) -setCombatCallback(combat, CALLBACK_PARAM_TARGETTILE, "onTargetTile") - -function onCastSpell(cid, var) - return doCombat(cid, combat, var) -end diff --git a/data/spells/scripts/support/animate dead rune.lua b/data/spells/scripts/support/animate dead rune.lua new file mode 100644 index 0000000..c6f4bad --- /dev/null +++ b/data/spells/scripts/support/animate dead rune.lua @@ -0,0 +1,26 @@ +local function doTargetCorpse(cid, position) + position.stackpos = 255 + local corpse = getThingFromPos(position) + if(corpse.uid > 0 and isCorpse(corpse.uid) and isMoveable(corpse.uid) and getCreatureSkullType(cid) ~= SKULL_BLACK) then + doRemoveItem(corpse.uid) + doConvinceCreature(cid, doCreateMonster("Skeleton", position)) + + doSendMagicEffect(position, CONST_ME_MAGIC_BLUE) + return true + end + + doSendMagicEffect(getThingPosition(cid), CONST_ME_POFF) + doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) + return false +end + +function onCastSpell(cid, var) + local position = variantToPosition(var) + if(position.x ~= 0 and position.y ~= 0) then + return doTargetCorpse(cid, position) + end + + doSendMagicEffect(getThingPosition(cid), CONST_ME_POFF) + doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) + return false +end diff --git a/data/spells/scripts/support/challenge.lua b/data/spells/scripts/support/challenge.lua index 0e7df83..a5be639 100644 --- a/data/spells/scripts/support/challenge.lua +++ b/data/spells/scripts/support/challenge.lua @@ -4,8 +4,8 @@ setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_RED) local area = createCombatArea(AREA_SQUARE1X1) setCombatArea(combat, area) -function onTargetCreature(cid, target) return doChallengeCreature(cid, target) end -setCombatCallback(combat, CALLBACK_PARAM_TARGETCREATURE, "onTargetCreature") +function onTarget(cid, target) return doChallengeCreature(cid, target) end +setCombatCallback(combat, CALLBACK_PARAM_TARGETCREATURE, "onTarget") function onCastSpell(cid, var) return doCombat(cid, combat, var) diff --git a/data/spells/scripts/support/conjure food.lua b/data/spells/scripts/support/conjure food.lua new file mode 100644 index 0000000..d0aef74 --- /dev/null +++ b/data/spells/scripts/support/conjure food.lua @@ -0,0 +1,25 @@ +local FOODS = { + ITEM_FOOD_MEAT, + ITEM_HAM, + ITEM_FOOD_GRAPE, + ITEM_FOOD_APLE, + ITEM_FOOD_BREAD, + ITEM_FOOD_ROOL, + ITEM_FOOD_CHEESE +} + +function onCastSpell(cid, var) + local size = table.maxn(FOODS) + if(not doPlayerAddItem(cid, FOODS[math.random(1, size)])) then + doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) + doSendMagicEffect(getThingPosition(cid), CONST_ME_POFF) + return false + end + + if(math.random(1, 100) > 50) then + doPlayerAddItem(cid, FOODS[math.random(1, size)]) + end + + doSendMagicEffect(getThingPosition(cid), CONST_ME_MAGIC_GREEN) + return true +end diff --git a/data/spells/scripts/support/destroy field rune.lua b/data/spells/scripts/support/destroy field rune.lua index 1bee290..e485748 100644 --- a/data/spells/scripts/support/destroy field rune.lua +++ b/data/spells/scripts/support/destroy field rune.lua @@ -2,21 +2,21 @@ UNREMOVABLE_FIELDS = {1497, 1498, 1499, 1505, 1506, 1507, 1508, 7465, 7466, 7467 local function doRemoveField(cid, pos) local field = getTileItemByType(pos, ITEM_TYPE_MAGICFIELD) - if(not isInArray(UNREMOVABLE_FIELDS, field.itemid)) then + if(field.uid > 0 and not isInArray(UNREMOVABLE_FIELDS, field.itemid)) then doRemoveItem(field.uid) doSendMagicEffect(pos, CONST_ME_POFF) return true end doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - doSendMagicEffect(getPlayerPosition(cid), CONST_ME_POFF) + doSendMagicEffect(getThingPosition(cid), CONST_ME_POFF) return false end function onCastSpell(cid, var) local pos = variantToPosition(var) if(pos.x == CONTAINER_POSITION) then - pos = getThingPos(cid) + pos = getThingPosition(cid) end if(pos.x ~= 0 and pos.y ~= 0) then @@ -24,6 +24,6 @@ function onCastSpell(cid, var) end doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - doSendMagicEffect(getPlayerPosition(cid), CONST_ME_POFF) + doSendMagicEffect(getThingPosition(cid), CONST_ME_POFF) return false end diff --git a/data/spells/scripts/support/haste.lua b/data/spells/scripts/support/haste.lua index 32c81fd..2f8bbf0 100644 --- a/data/spells/scripts/support/haste.lua +++ b/data/spells/scripts/support/haste.lua @@ -1,5 +1,5 @@ local combat = createCombatObject() -setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_GREEN) setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) local condition = createConditionObject(CONDITION_HASTE) diff --git a/data/spells/scripts/support/magic rope.lua b/data/spells/scripts/support/magic rope.lua index 3f7b278..f1ba49f 100644 --- a/data/spells/scripts/support/magic rope.lua +++ b/data/spells/scripts/support/magic rope.lua @@ -1,21 +1,21 @@ -local spotsId = {384, 418, 8278, 8592} +local SPOTS = {384, 418, 8278, 8592} function onCastSpell(cid, var) - local pos = getCreaturePosition(cid) - pos.stackpos = 0 + local position = getThingPosition(cid) + position.stackpos = 0 - local itemGround = getThingFromPos(pos) - if(isInArray(spotsId, itemGround.itemid)) then - local newPos = pos - newPos.y = newPos.y + 1 - newPos.z = newPos.z - 1 + local ground = getThingFromPos(position) + if(isInArray(SPOTS, ground.itemid)) then + local newPosition = position + newPosition.y = newPosition.y + 1 + newPosition.z = newPosition.z - 1 - doTeleportThing(cid, newPos, false) - doSendMagicEffect(pos, CONST_ME_TELEPORT) + doTeleportThing(cid, newPosition, false) + doSendMagicEffect(position, CONST_ME_TELEPORT) return true else doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) - doSendMagicEffect(pos, CONST_ME_POFF) + doSendMagicEffect(position, CONST_ME_POFF) return false end end diff --git a/data/spells/scripts/support/magic wall rune.lua b/data/spells/scripts/support/magic wall rune.lua index dbddd77..3049a67 100644 --- a/data/spells/scripts/support/magic wall rune.lua +++ b/data/spells/scripts/support/magic wall rune.lua @@ -1,6 +1,7 @@ local combat = createCombatObject() setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ENERGY) -setCombatParam(combat, COMBAT_PARAM_CREATEITEM, 1497) +setCombatParam(combat, COMBAT_PARAM_CREATEITEM, ITEM_MAGIC_WALL) +setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) function onCastSpell(cid, var) return doCombat(cid, combat, var) diff --git a/data/spells/scripts/support/paralyze rune.lua b/data/spells/scripts/support/paralyze rune.lua index c19a155..6e9f010 100644 --- a/data/spells/scripts/support/paralyze rune.lua +++ b/data/spells/scripts/support/paralyze rune.lua @@ -8,5 +8,10 @@ setConditionFormula(condition, -0.9, 0, -0.9, 0) setCombatCondition(combat, condition) function onCastSpell(cid, var) - return doCombat(cid, combat, var) + if(not doCombat(cid, combat, var)) then + return false + end + + doSendMagicEffect(getThingPosition(cid), CONST_ME_MAGIC_GREEN) + return true end diff --git a/data/spells/scripts/support/protector.lua b/data/spells/scripts/support/protector.lua index 7c81d26..d2afc8e 100644 --- a/data/spells/scripts/support/protector.lua +++ b/data/spells/scripts/support/protector.lua @@ -12,11 +12,6 @@ local disable = createConditionObject(CONDITION_PACIFIED) setConditionParam(disable, CONDITION_PARAM_TICKS, 10000) setCombatCondition(combat, disable) -local exhaust = createConditionObject(CONDITION_EXHAUST) -setConditionParam(exhaust, CONDITION_PARAM_SUBID, 1) -setConditionParam(exhaust, CONDITION_PARAM_TICKS, 10000) -setCombatCondition(combat, exhaust) - function onCastSpell(cid, var) return doCombat(cid, combat, var) end diff --git a/data/spells/scripts/support/sharpshooter.lua b/data/spells/scripts/support/sharpshooter.lua index 6dfdfce..6c0fe68 100644 --- a/data/spells/scripts/support/sharpshooter.lua +++ b/data/spells/scripts/support/sharpshooter.lua @@ -13,11 +13,6 @@ setConditionParam(speed, CONDITION_PARAM_TICKS, 10000) setConditionFormula(speed, -0.7, 56, -0.7, 56) setCombatCondition(combat, speed) -local exhaust = createConditionObject(CONDITION_EXHAUST) -setConditionParam(exhaust, CONDITION_PARAM_SUBID, 2) -setConditionParam(exhaust, CONDITION_PARAM_TICKS, 10000) -setCombatCondition(combat, exhaust) - function onCastSpell(cid, var) return doCombat(cid, combat, var) end diff --git a/data/spells/scripts/support/strong haste.lua b/data/spells/scripts/support/strong haste.lua index 8e7b6d3..9cbe013 100644 --- a/data/spells/scripts/support/strong haste.lua +++ b/data/spells/scripts/support/strong haste.lua @@ -1,5 +1,5 @@ local combat = createCombatObject() -setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE) +setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_GREEN) setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false) local condition = createConditionObject(CONDITION_HASTE) diff --git a/data/spells/scripts/support/swift foot.lua b/data/spells/scripts/support/swift foot.lua index 2e043ab..4903dea 100644 --- a/data/spells/scripts/support/swift foot.lua +++ b/data/spells/scripts/support/swift foot.lua @@ -11,11 +11,6 @@ local disable = createConditionObject(CONDITION_PACIFIED) setConditionParam(disable, CONDITION_PARAM_TICKS, 10000) setCombatCondition(combat, disable) -local exhaust = createConditionObject(CONDITION_EXHAUST) -setConditionParam(exhaust, CONDITION_PARAM_SUBID, 1) -setConditionParam(exhaust, CONDITION_PARAM_TICKS, 10000) -setCombatCondition(combat, exhaust) - function onCastSpell(cid, var) return doCombat(cid, combat, var) end diff --git a/data/spells/scripts/support/wild growth rune.lua b/data/spells/scripts/support/wild growth rune.lua index 4300c07..7e127c4 100644 --- a/data/spells/scripts/support/wild growth rune.lua +++ b/data/spells/scripts/support/wild growth rune.lua @@ -1,6 +1,6 @@ local combat = createCombatObject() setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_EARTH) -setCombatParam(combat, COMBAT_PARAM_CREATEITEM, 1499) +setCombatParam(combat, COMBAT_PARAM_CREATEITEM, ITEM_WILD_GROWTH) function onCastSpell(cid, var) return doCombat(cid, combat, var) diff --git a/data/spells/spells.xml b/data/spells/spells.xml index 0b26ef7..66df2e7 100644 --- a/data/spells/spells.xml +++ b/data/spells/spells.xml @@ -1,167 +1,263 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - + + - - - - - + + + + + - + - + - - - + - + + + + + + + + + - + + + + + + + + + - + - + - + + + + + - + - + + + + + - + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + - + - + - + - + + + + + - + - + - + + + + + - + - + + + + + + + + + + + + + + + + + - + + + + + + + + + - + - + - + + + + + + + + + + + - + + + + + @@ -171,7 +267,31 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -179,33 +299,28 @@ - + - + - - - + - + - - - - + - + @@ -215,7 +330,7 @@ - + @@ -225,7 +340,7 @@ - + @@ -235,7 +350,7 @@ - + @@ -245,7 +360,7 @@ - + @@ -255,15 +370,13 @@ - + - - - + @@ -273,246 +386,238 @@ - + - + - + - + - + - + - - - + + + - + - - - + - + - + - + - + - + - + - + - - - - - + - + - - - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -520,7 +625,7 @@ - + @@ -528,35 +633,35 @@ - + - + - + - + - + - + - + - + diff --git a/data/talkactions/lib/talkactions.lua b/data/talkactions/lib/talkactions.lua deleted file mode 100644 index fc54edc..0000000 --- a/data/talkactions/lib/talkactions.lua +++ /dev/null @@ -1 +0,0 @@ --- Nothing -- \ No newline at end of file diff --git a/data/talkactions/scripts/animatedtext.lua b/data/talkactions/scripts/animatedtext.lua deleted file mode 100644 index 6cd48f1..0000000 --- a/data/talkactions/scripts/animatedtext.lua +++ /dev/null @@ -1,21 +0,0 @@ -function onSay(cid, words, param, channel) - if(param == '') then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.") - return true - end - - local t = string.explode(param, ",") - local tmp = t[1] - if(t[2]) then - tmp = t[2] - end - - t[1] = tonumber(t[1]) - if(t[1] > 0 and t[1] < 256) then - doSendAnimatedText(getCreaturePosition(cid), tmp, t[1]) - else - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Typed color has to be between 0 and 256") - end - - return true -end diff --git a/data/talkactions/scripts/balance.lua b/data/talkactions/scripts/balance.lua new file mode 100644 index 0000000..897331f --- /dev/null +++ b/data/talkactions/scripts/balance.lua @@ -0,0 +1,75 @@ +local function isValidMoney(value) + if(value == nil) then + return false + end + + return (value > 0 and value <= 99999999999999) +end + +function onSay(cid, words, param, channel) + local guild = getPlayerGuildId(cid) + if(guild == 0) then + return false + end + + local t = string.explode(param, ' ', 1) + if(getPlayerGuildLevel(cid) == GUILDLEVEL_LEADER and isInArray({ 'pick' }, t[1])) then + if(t[1] == 'pick') then + local money = { tonumber(t[2]) } + if(not isValidMoney(money[1])) then + doPlayerSendChannelMessage(cid, '', 'Invalid amount of money specified.', TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + local result = db.getResult('SELECT `balance` FROM `guilds` WHERE `id` = ' .. guild) + if(result:getID() == -1) then + return false + end + + money[2] = result:getDataLong('balance') + result:free() + + if(money[1] > money[2]) then + doPlayerSendChannelMessage(cid, '', 'The balance is too low for such amount.', TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + if(not db.executeQuery('UPDATE `guilds` SET `balance` = `balance` - ' .. money[1] .. ' WHERE `id` = ' .. guild .. ' LIMIT 1;')) then + return false + end + + doPlayerAddMoney(cid, money[1]) + doPlayerSendChannelMessage(cid, '', 'You have just picked ' .. money[1] .. ' money from your guild balance.', TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + else + doPlayerSendChannelMessage(cid, '', 'Invalid sub-command.', TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + end + elseif(t[1] == 'donate') then + local money = tonumber(t[2]) + if(not isValidMoney(money)) then + doPlayerSendChannelMessage(cid, '', 'Invalid amount of money specified.', TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + if(getPlayerMoney(cid) < money) then + doPlayerSendChannelMessage(cid, '', 'You don\'t have enough money.', TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + if(not doPlayerRemoveMoney(cid, money)) then + return false + end + + db.executeQuery('UPDATE `guilds` SET `balance` = `balance` + ' .. money .. ' WHERE `id` = ' .. guild .. ' LIMIT 1;') + doPlayerSendChannelMessage(cid, '', 'You have transfered ' .. money .. ' money to your guild balance.', TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + else + local result = db.getResult('SELECT `name`, `balance` FROM `guilds` WHERE `id` = ' .. guild) + if(result:getID() == -1) then + return false + end + + doPlayerSendChannelMessage(cid, '', 'Current balance of guild ' .. result:getDataString('name') .. ' is: ' .. result:getDataLong('balance') .. ' bronze coins.', TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + result:free() + end + + return true +end \ No newline at end of file diff --git a/data/talkactions/scripts/ban.lua b/data/talkactions/scripts/ban.lua new file mode 100644 index 0000000..d16fee4 --- /dev/null +++ b/data/talkactions/scripts/ban.lua @@ -0,0 +1,16 @@ +local TYPE_ACCESS = { + [1] = { "Player" }, + [2] = { "Player" }, + [3] = { "Account", "Player" }, + [4] = { "Account", "Player" }, + [5] = { "Account", "Player", "IP" } +} + +function onSay(cid, words, param, channel) + unregisterCreatureEventType(cid, "channelrequest") + unregisterCreatureEventType(cid, "textedit") + + doPlayerSendChannels(cid, TYPE_ACCESS[getPlayerAccess(cid)]) + registerCreatureEvent(cid, "Ban_Type") + return true +end \ No newline at end of file diff --git a/data/talkactions/scripts/broadcastclass.lua b/data/talkactions/scripts/broadcastclass.lua index 06779fe..ca5f0a2 100644 --- a/data/talkactions/scripts/broadcastclass.lua +++ b/data/talkactions/scripts/broadcastclass.lua @@ -5,10 +5,14 @@ function onSay(cid, words, param, channel) end local t = string.explode(param, " ", 1) - if(not t[2]) then - doBroadcastMessage(t[1]) - elseif(not doBroadcastMessage(t[2], MESSAGE_TYPES[t[1]])) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Bad message color type.") + if(MESSAGE_TYPES[t[1]] == nil) then + t = t[1] .. " " .. t[2] + end + + if(type(t) == 'table') then + doBroadcastMessage(t[2], MESSAGE_TYPES[t[1]]) + else + doBroadcastMessage(t) end return true diff --git a/data/talkactions/scripts/clean.lua b/data/talkactions/scripts/clean.lua index 12c8676..51997c9 100644 --- a/data/talkactions/scripts/clean.lua +++ b/data/talkactions/scripts/clean.lua @@ -6,8 +6,9 @@ function onSay(cid, words, param, channel) return true end - if(param == 'tile') then - local removeLoaded, t = false, string.explode(param, ",") + local t = string.explode(param, ",") + if(t[1] == 'tile') then + local removeLoaded = false if(t[2]) then removeLoaded = getBooleanFromString(t[2]) end @@ -17,7 +18,7 @@ function onSay(cid, words, param, channel) end if(not tonumber(param)) then - doPlayerSendCancel(cid, "Command requires numeric param.") + doPlayerSendCancel(cid, "Command numeric param required.") return true end diff --git a/data/talkactions/scripts/commands.lua b/data/talkactions/scripts/commands.lua index e6d1a86..9d674e2 100644 --- a/data/talkactions/scripts/commands.lua +++ b/data/talkactions/scripts/commands.lua @@ -1,13 +1,12 @@ local config = { - guildTalksEnabled = getBooleanFromString(getConfigValue('ingameGuildManagement')) + ingameGuilds = getBooleanFromString(getConfigValue('ingameGuildManagement')) } function onSay(cid, words, param, channel) local playerAccess, t = getPlayerAccess(cid), {} for i, talk in ipairs(getTalkActionList()) do - if(not talk.hide and playerAccess >= talk.access) then - local tmp = talk.words:sub(1, 1):trim() - if((guildTalksEnabled or (talk.words ~= "!joinguild" and talk.words ~= "!createguild")) and (tmp == "!" or tmp == "/")) then + if(not talk.hidden and playerAccess >= talk.access) then + if(config.ingameGuilds or (talk.functionName ~= "guildjoin" and talk.functionName ~= "guildcreate")) then table.insert(t, talk) end end @@ -26,6 +25,6 @@ function onSay(cid, words, param, channel) str = str .. line .. talk.words .. "\n" end - doShowTextDialog(cid, 2160, str) + doShowTextDialog(cid, ITEM_ACTION_BOOK, str) return true end diff --git a/data/talkactions/scripts/configinfo.lua b/data/talkactions/scripts/configinfo.lua index 59a7bd0..679b03c 100644 --- a/data/talkactions/scripts/configinfo.lua +++ b/data/talkactions/scripts/configinfo.lua @@ -1,9 +1,9 @@ -local HIDDEN = {"passwordType", "sql"} +local HIDDEN = {"adminPassword", "managerPassword", "encryptionType", "^sql"} function onSay(cid, words, param, channel) for _, str in ipairs(HIDDEN) do if(param:find(str) ~= nil) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Config value \"" .. value .. "\" does not exists.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Config value \"" .. param .. "\" does not exists.") return true end end diff --git a/data/talkactions/scripts/createitem.lua b/data/talkactions/scripts/createitem.lua index 5396a6e..388ba6d 100644 --- a/data/talkactions/scripts/createitem.lua +++ b/data/talkactions/scripts/createitem.lua @@ -10,7 +10,10 @@ function onSay(cid, words, param, channel) local id = tonumber(t[1]) if(not id) then - id = getItemIdByName(t[1], false) + errors(false) + id = getItemIdByName(t[1]) + errors(true) + if(not id) then doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Item wich such name does not exists.") return true diff --git a/data/talkactions/scripts/creature.lua b/data/talkactions/scripts/creature.lua index b7f1675..d069f63 100644 --- a/data/talkactions/scripts/creature.lua +++ b/data/talkactions/scripts/creature.lua @@ -4,8 +4,7 @@ function onSay(cid, words, param, channel) func = doCreateNpc end - local pid = cid - local t = string.explode(param, ",") + local pid, t = cid, string.explode(param, ",") if(t[2]) then pid = getPlayerByNameWildcard(t[2]) if(not pid) then @@ -14,12 +13,14 @@ function onSay(cid, words, param, channel) end end - local position = getCreaturePosition(pid) - local effect = CONST_ME_MAGIC_RED - local ret = func(t[1], position, false) + local position, effect = getCreaturePosition(pid), CONST_ME_MAGIC_RED + errors(false) + local ret = func(t[1], position) + errors(true) + if(tonumber(ret) == nil) then effect = CONST_ME_POFF - doPlayerSendDefaultCancel(cid, (ret == false and RETURNVALUE_NOTPOSSIBLE or RETURNVALUE_NOTENOUGHROOM)) + doPlayerSendDefaultCancel(cid, (not ret and RETURNVALUE_NOTPOSSIBLE or RETURNVALUE_NOTENOUGHROOM)) end doSendMagicEffect(position, effect) diff --git a/data/talkactions/scripts/frags.lua b/data/talkactions/scripts/frags.lua index cec5aaf..0ca0540 100644 --- a/data/talkactions/scripts/frags.lua +++ b/data/talkactions/scripts/frags.lua @@ -1,17 +1,12 @@ -local config = { - useFragHandler = getBooleanFromString(getConfigValue('useFragHandler')), - advancedFragList = getBooleanFromString(getConfigValue('advancedFragList')) -} - function onSay(cid, words, param, channel) - if(not config.useFragHandler) then + if(not getBooleanFromString(getConfigValue('useFragHandler'))) then return false end local time = os.time() local times = {today = (time - 86400), week = (time - (7 * 86400))} - local contents, result = {day = {}, week = {}, month = {}}, db.getResult("SELECT `pd`.`date`, `pd`.`level`, `p`.`name` FROM `player_killers` pk LEFT JOIN `killers` k ON `pk`.`kill_id` = `k`.`id` LEFT JOIN `player_deaths` pd ON `k`.`death_id` = `pd`.`id` LEFT JOIN `players` p ON `pd`.`player_id` = `p`.`id` WHERE `pk`.`player_id` = " .. getPlayerGUID(cid) .. " AND `k`.`unjustified` = 1 AND `pd`.`date` >= " .. (time - (30 * 86400)) .. " ORDER BY `pd`.`date` DESC") + local contents, result = {day = {}, week = {}, month = {}}, db.getResult("SELECT `pd`.`date`, `pd`.`level`, `p`.`name` FROM `player_killers` pk LEFT JOIN `killers` k ON `pk`.`kill_id` = `k`.`id` LEFT JOIN `player_deaths` pd ON `k`.`death_id` = `pd`.`id` LEFT JOIN `players` p ON `pd`.`player_id` = `p`.`id` WHERE `pk`.`player_id` = " .. getPlayerGUID(cid) .. " AND `k`.`unjustified` = 1 AND `k`.`war` = 0 AND `pd`.`date` >= " .. (time - (30 * 86400)) .. " ORDER BY `pd`.`date` DESC") if(result:getID() ~= -1) then repeat local content = { @@ -35,7 +30,8 @@ function onSay(cid, words, param, channel) week = table.maxn(contents.week), month = table.maxn(contents.month) } - if(config.advancedFragList) then + + if(getBooleanFromString(getConfigValue('advancedFragList'))) then local result = "Frags gained today: " .. size.day .. "." if(size.day > 0) then for _, content in ipairs(contents.day) do diff --git a/data/talkactions/scripts/gamemaster.lua b/data/talkactions/scripts/gamemaster.lua index f289707..2c26274 100644 --- a/data/talkactions/scripts/gamemaster.lua +++ b/data/talkactions/scripts/gamemaster.lua @@ -1,22 +1,23 @@ -local ignore = createConditionObject(CONDITION_GAMEMASTER, -1, false, GAMEMASTER_IGNORE) -local teleport = createConditionObject(CONDITION_GAMEMASTER, -1, false, GAMEMASTER_TELEPORT) +local ignore = createConditionObject(CONDITION_GAMEMASTER, -1, false, GAMEMASTER_IGNORE, CONDITIONID_DEFAULT) +local teleport = createConditionObject(CONDITION_GAMEMASTER, -1, false, GAMEMASTER_TELEPORT, CONDITIONID_DEFAULT) +local light = createConditionObject(CONDITION_LIGHT, -1, false, 0, CONDITIONID_DEFAULT) + setConditionParam(light, CONDITION_PARAM_LIGHT_LEVEL, 255) + setConditionParam(light, CONDITION_PARAM_LIGHT_COLOR, 215) function onSay(cid, words, param, channel) - local condition = ignore - local subId = GAMEMASTER_IGNORE - local name = "private messages ignoring" + local condition, type, subId, name = ignore, CONDITION_GAMEMASTER, GAMEMASTER_IGNORE, "private messages ignoring" if(words:sub(2, 2) == "c") then - condition = teleport - subId = GAMEMASTER_TELEPORT - name = "map click teleport" + condition, subId, name = teleport, GAMEMASTER_TELEPORT, "map click teleport" + elseif(words:sub(2, 2) == "l") then + condition, type, subId, name = light, CONDITION_LIGHT, 0, "full light" end local action = "off" - if(not getCreatureCondition(cid, CONDITION_GAMEMASTER, subId)) then + if(not getCreatureCondition(cid, type, subId, CONDITIONID_DEFAULT)) then doAddCondition(cid, condition) action = "on" else - doRemoveCondition(cid, CONDITION_GAMEMASTER, subId) + doRemoveCondition(cid, type, subId, CONDITIONID_DEFAULT) end doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "You have turned " .. action .. " " .. name .. ".") diff --git a/data/talkactions/scripts/gethouse.lua b/data/talkactions/scripts/gethouse.lua index a38413d..e07c6af 100644 --- a/data/talkactions/scripts/gethouse.lua +++ b/data/talkactions/scripts/gethouse.lua @@ -12,12 +12,11 @@ function onSay(cid, words, param, channel) local t = string.explode(param, ",") if(t[2]) then teleport = getBooleanFromString(t[2]) - param = t[1] end - local house = getHouseByPlayerGUID(getPlayerGUIDByName(param)) + local house = getHouseByPlayerGUID(getPlayerGUIDByName(t[1])) if(not house) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. param .. " does not own house or doesn't exists.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. t[1] .. " does not own house or doesn't exists.") return true end @@ -26,6 +25,6 @@ function onSay(cid, words, param, channel) doTeleportThing(cid, houseInfo.entry) end - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, param .. " owns house: " .. houseInfo.name .. ".") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, t[1] .. " owns house: " .. houseInfo.name .. ".") return true end diff --git a/data/talkactions/scripts/masskick.lua b/data/talkactions/scripts/masskick.lua index 8ee9d32..0c1934b 100644 --- a/data/talkactions/scripts/masskick.lua +++ b/data/talkactions/scripts/masskick.lua @@ -1,22 +1,28 @@ function onSay(cid, words, param, channel) if(param == '') then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command requires param.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.") return true end - local t = string.explode(param, ",") - if(not t[2]) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Not enough params.") - return true - end + local players = {} + if(param:sub(1, 1) ~= '*') then + local t = string.explode(param, ",") + if(not t[2]) then + t[2] = t[1] + end - local multifloor = false - if(t[3]) then - multifloor = getBooleanFromString(t[3]) + local multifloor = false + if(t[3]) then + multifloor = getBooleanFromString(t[3]) + end + + players = getSpectators(getCreaturePosition(cid), t[1], t[2], multifloor) + else + players = getPlayersOnline() end local tmp = 0 - for i, tid in ipairs(getSpectators(getCreaturePosition(cid), t[1], t[2], multifloor)) do + for i, tid in ipairs(players) do if(isPlayer(tid) and tid ~= cid and getPlayerAccess(tid) < getPlayerAccess(cid)) then doRemoveCreature(tid) tmp = tmp + 1 diff --git a/data/talkactions/scripts/mode.lua b/data/talkactions/scripts/mode.lua index e31411c..2c5cf58 100644 --- a/data/talkactions/scripts/mode.lua +++ b/data/talkactions/scripts/mode.lua @@ -1,7 +1,7 @@ local config = { - nopvp = {"1", "nopvp", "nonpvp", "no-pvp", "non-pvp", "safe"}, - pvp = {"2", "pvp", "normal"}, - pvpenforced = {"3", "pvpe", "pvpenforced", "pvp-enforced", "war"} + optional = {"1", "optional", "optionalpvp"}, + open = {"2", "open", "openpvp"}, + hardcore = {"3", "hardcore", "hardcorepvp"} } function onSay(cid, words, param, channel) @@ -12,15 +12,15 @@ function onSay(cid, words, param, channel) local world = getWorldType() param = param:lower() - if(table.isStrIn(param, config.nopvp)) then - setWorldType(WORLD_TYPE_NO_PVP) - world = "No-PVP" - elseif(table.isStrIn(param, config.pvp)) then - setWorldType(WORLD_TYPE_PVP) - world = "PVP" - elseif(table.isStrIn(param, config.pvpenforced)) then - setWorldType(WORLD_TYPE_PVP_ENFORCED) - world = "PVP-Enforced" + if(table.isStrIn(param, config.optional)) then + setWorldType(WORLDTYPE_OPTIONAL) + world = "Optional PvP" + elseif(table.isStrIn(param, config.open)) then + setWorldType(WORLDTYPE_OPEN) + world = "Open PvP" + elseif(table.isStrIn(param, config.hardcore)) then + setWorldType(WORLDTYPE_HARDCORE) + world = "Hardcore PvP" else doPlayerSendCancel(cid, "Bad gameworld type.") return true diff --git a/data/talkactions/scripts/money.lua b/data/talkactions/scripts/money.lua index 9290713..acb5a72 100644 --- a/data/talkactions/scripts/money.lua +++ b/data/talkactions/scripts/money.lua @@ -1,4 +1,4 @@ function onSay(cid, words, param, channel) - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "You have " .. getPlayerMoney(cid) .. " gold.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "You have " .. doNumberFormat(getPlayerMoney(cid)) .. " gold.") return true end diff --git a/data/talkactions/scripts/multicheck.lua b/data/talkactions/scripts/multicheck.lua index bee6978..556e22e 100644 --- a/data/talkactions/scripts/multicheck.lua +++ b/data/talkactions/scripts/multicheck.lua @@ -10,6 +10,9 @@ function onSay(cid, words, param, channel) _ip = nil else _ip = getPlayerIp(tid) + if(_ip == 0) then + _ip = nil + end end else _ip = doConvertIpToInteger(revertIp) @@ -17,27 +20,30 @@ function onSay(cid, words, param, channel) end end - local list, ips = {}, {} - local players = getPlayersOnline() + local list, ips, players = {}, {}, getPlayersOnline() for i, pid in ipairs(players) do local ip = getPlayerIp(pid) - local tmp = table.find(ips, ip) - if(tmp ~= nil and (not _ip or _ip == ip)) then - if(table.countElements(list, ip) == 0) then - list[players[tmp]] = ip + if(ip ~= 0) then + local tmp = table.find(ips, ip) + if(tmp ~= nil and (not _ip or _ip == ip)) then + if(table.countElements(list, ip) == 0) then + list[players[tmp]] = ip + end + + list[pid] = ip end - list[pid] = ip + table.insert(ips, ip) end - - table.insert(ips, ip) end - if(table.maxn(list) > 0) then + local total = table.maxn(list) + if(total > 0) then doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Currently online players with same IP address(es):") for pid, ip in pairs(list) do doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, getCreatureName(pid) .. " (" .. doConvertIntegerToIp(ip) .. ")") end + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Total: " .. total .. " clients with more than one connection.") else doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Currently there aren't any players with same IP address(es).") end diff --git a/data/talkactions/scripts/mute.lua b/data/talkactions/scripts/mute.lua new file mode 100644 index 0000000..dc0daa9 --- /dev/null +++ b/data/talkactions/scripts/mute.lua @@ -0,0 +1,33 @@ +function onSay(cid, words, param, channel) + if(param == '') then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.") + return true + end + + local t = string.explode(param, ",") + local pid = getPlayerByNameWildcard(t[1]) + if(not pid or (isPlayerGhost(pid) and getPlayerGhostAccess(pid) > getPlayerGhostAccess(cid))) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. t[1] .. " is not currently online.") + return true + end + + if(getPlayerAccess(cid) <= getPlayerAccess(pid) or getPlayerFlagValue(pid, PLAYERFLAG_CANNOTBEMUTED)) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Cannot perform action.") + return true + end + + if(words:sub(2, 2) == "u") then + doRemoveCondition(pid, CONDITION_MUTED, 0) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, getCreatureName(pid) .. " has been unmuted.") + return true + end + + local time = tonumber(t[2]) + if(not time or time < 1) then + time = -1 + end + + doMutePlayer(pid, time) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, getCreatureName(pid) .. " has been muted for" .. (time < 1 and "ever" or " " .. time .. " seconds") .. ".") + return true +end diff --git a/data/talkactions/scripts/newtype.lua b/data/talkactions/scripts/newtype.lua index 0faee52..1f0c54a 100644 --- a/data/talkactions/scripts/newtype.lua +++ b/data/talkactions/scripts/newtype.lua @@ -1,17 +1,14 @@ +local function checkType(value) + return not(value <= 1 or value == 135 or (value > 160 and value < 192) or value == 411 or value == 415 or value == 424 or (value > 439 and value < 441) or (value > 466 and value < 470) or value > 484) +end + function onSay(cid, words, param, channel) if(param == '') then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command requires param.") - return true - end - - local t = string.explode(param, ",") - t[1] = tonumber(t[1]) - if(not t[1]) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command requires numeric param.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.") return true end - local pid = cid + local t, pid = string.explode(param, ","), cid if(t[2]) then pid = getPlayerByNameWildcard(t[2]) if(not pid or (isPlayerGhost(pid) and getPlayerGhostAccess(pid) > getPlayerGhostAccess(cid))) then @@ -20,7 +17,29 @@ function onSay(cid, words, param, channel) end end - if(t[1] <= 1 or t[1] == 135 or (t[1] > 160 and t[1] < 192) or t[1] > 351) then + local period, tmp = -1, tonumber(t[3]) + if(t[3] and tmp) then + period = tmp + end + + if(not isNumeric(t[1])) then + if(getMonsterInfo(t[1])) then + doSetMonsterOutfit(pid, t[1], period) + else + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Such outfit does not exist.") + end + + return true + end + + t[1] = tonumber(t[1]) + if(not checkType(t[1])) then + local item = getItemInfo(t[1]) + if(item) then + doSetItemOutfit(pid, t[1], period) + return true + end + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Such outfit does not exist.") return true end @@ -28,6 +47,13 @@ function onSay(cid, words, param, channel) local tmp = getCreatureOutfit(pid) tmp.lookType = t[1] + if(t[3]) then + t[3] = tonumber(t[3]) + if(checkType(t[3])) then + tmp.lookMount = t[3] + end + end + doCreatureChangeOutfit(pid, tmp) return true end diff --git a/data/talkactions/scripts/notations.lua b/data/talkactions/scripts/notations.lua index a051699..4956681 100644 --- a/data/talkactions/scripts/notations.lua +++ b/data/talkactions/scripts/notations.lua @@ -14,7 +14,7 @@ function onSay(cid, words, param, channel) end local list = getBanList(BAN_NOTATION, ret) - if(type(list) ~= "table" or table.maxn(list) <= 0) then + if(type(list) ~= 'table' or table.maxn(list) <= 0) then doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Such account or player does not have any notation.") return true end @@ -22,7 +22,7 @@ function onSay(cid, words, param, channel) ret = "Notations for account " .. ret .. "\n" for i, ban in ipairs(list) do local tmp = ban.adminId ~= 0 and getPlayerNameByGUID(ban.adminId) or "unknown" - ret = ret .. "\nAdded at " .. os.date("%c", ban.added) .. " by " .. tmp .. " with reason: " .. getBanReason(ban.reason) .. " and comment: " .. ban.comment .. "." + ret = ret .. "\nAdded at " .. os.date("%c", ban.added) .. " by " .. tmp .. " with comment: " .. ban.comment .. "." end doPlayerPopupFYI(cid, ret) diff --git a/data/talkactions/scripts/online.lua b/data/talkactions/scripts/online.lua index e5ae078..c363cf4 100644 --- a/data/talkactions/scripts/online.lua +++ b/data/talkactions/scripts/online.lua @@ -1,14 +1,6 @@ -local config = { - showGamemasters = getBooleanFromString(getConfigValue('displayGamemastersWithOnlineCommand')) -} - function onSay(cid, words, param, channel) - local players = getPlayersOnline() - local strings = {""} - - local i, position = 1, 1 - local added = false - for _, pid in ipairs(players) do + local strings, i, position, added, showGamemasters = {""}, 1, 1, false, getBooleanFromString(getConfigValue('displayGamemastersWithOnlineCommand')) + for _, pid in ipairs(getPlayersOnline()) do if(added) then if(i > (position * 7)) then strings[position] = strings[position] .. "," @@ -19,16 +11,15 @@ function onSay(cid, words, param, channel) end end - if((config.showGamemasters or getPlayerCustomFlagValue(cid, PLAYERCUSTOMFLAG_GAMEMASTERPRIVILEGES) or not getPlayerCustomFlagValue(pid, PLAYERCUSTOMFLAG_GAMEMASTERPRIVILEGES)) and (not isPlayerGhost(pid) or getPlayerGhostAccess(cid) >= getPlayerGhostAccess(pid))) then + added = false + if((showGamemasters or getPlayerCustomFlagValue(cid, PLAYERCUSTOMFLAG_GAMEMASTERPRIVILEGES) or not getPlayerCustomFlagValue(pid, PLAYERCUSTOMFLAG_GAMEMASTERPRIVILEGES)) and (not isPlayerGhost(pid) or getPlayerGhostAccess(cid) >= getPlayerGhostAccess(pid))) then strings[position] = strings[position] .. getCreatureName(pid) .. " [" .. getPlayerLevel(pid) .. "]" i = i + 1 added = true - else - added = false end end - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, (i - 1) .. " player(s) online:") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, (i - 1) .. " player" .. (i > 1 and "s" or "") .. " online:") for i, str in ipairs(strings) do if(str:sub(str:len()) ~= ",") then str = str .. "." diff --git a/data/talkactions/scripts/owner.lua b/data/talkactions/scripts/owner.lua index 19fb6a9..151911f 100644 --- a/data/talkactions/scripts/owner.lua +++ b/data/talkactions/scripts/owner.lua @@ -2,7 +2,7 @@ local NO_OWNER_PHRASE = {"none", "nobody", "0"} function onSay(cid, words, param, channel) if(param == '') then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command requires param.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.") return true end diff --git a/data/talkactions/scripts/playerinfo.lua b/data/talkactions/scripts/playerinfo.lua index 6a752c3..acd0ef5 100644 --- a/data/talkactions/scripts/playerinfo.lua +++ b/data/talkactions/scripts/playerinfo.lua @@ -24,10 +24,11 @@ function onSay(cid, words, param, channel) "\nSkills:" .. "\nFist - " .. getPlayerSkillLevel(pid, SKILL_FIST) .. ", Club - " .. getPlayerSkillLevel(pid, SKILL_CLUB) .. ", Sword - " .. getPlayerSkillLevel(pid, SKILL_SWORD) .. ", Axe - " .. getPlayerSkillLevel(pid, SKILL_AXE) .. "\nDistance - " .. getPlayerSkillLevel(pid, SKILL_DISTANCE) .. ", Shielding - " .. getPlayerSkillLevel(pid, SKILL_SHIELD) .. ", Fishing - " .. getPlayerSkillLevel(pid, SKILL_FISHING) .. - "\nCash:" .. - "\nCrystal - " .. getPlayerItemCount(pid, 2160) .. ", Platinum - " .. getPlayerItemCount(pid, 2152) .. ", Gold - " .. getPlayerItemCount(pid, 2148) .. - "\nBalance: " .. getPlayerBalance(pid) .. - "\nPosition: [X - " .. pos.x .. " | Y - " .. pos.y .. " | Z - " .. pos.z .. "]" .. + "\nCoins:" .. + "\nCrystal - " .. getPlayerItemCount(pid, ITEM_CRYSTAL_COIN) .. ", Platinum - " .. getPlayerItemCount(pid, ITEM_PLATINUM_COIN) .. ", Gold - " .. getPlayerItemCount(pid, ITEM_GOLD_COIN) .. + "\nOverall amount - " .. getPlayerMoney(pid) .. + "\nBalance: " .. getPlayerBalance(pid) .. + "\nPosition: [X - " .. pos.x .. " | Y - " .. pos.y .. " | Z - " .. pos.z .. "]" .. "\n\nInformation about account" .. "\nName: " .. getPlayerAccount(pid) .. "\nID: " .. tmp.accountId .. diff --git a/data/talkactions/scripts/promote.lua b/data/talkactions/scripts/promote.lua index e6f36af..cfe656d 100644 --- a/data/talkactions/scripts/promote.lua +++ b/data/talkactions/scripts/promote.lua @@ -8,9 +8,10 @@ function onSay(cid, words, param, channel) return true end - local pid = getPlayerByNameWildcard(param) + local t = string.explode(param, ",", 1) + local pid = getPlayerByNameWildcard(t[1]) if(not pid) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. param .. " not found.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. t[1] .. " not found.") return true end @@ -19,18 +20,28 @@ function onSay(cid, words, param, channel) return true end - local g = 1 + local g, group = 1, getPlayerGroupId(pid) if(words:sub(2, 2) == "d") then g = -1 end - local newId = getPlayerGroupId(pid) + g - if(newId <= 0 or not setPlayerGroupId(pid, newId)) then + local newGroup = group + g + if(t[2] ~= nil) then + for i, id in ipairs(getGroupList()) do + local tmp = getGroupInfo(id) + if(isInArray({tmp.id, tmp.name}, t[2])) then + newGroup = id + break + end + end + end + + if(newGroup <= 0 or newGroup == group or not setPlayerGroupId(pid, newGroup)) then doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Cannot perform action.") return true end - local str = "been " .. (g == 1 and "promoted" or "demoted") .. " to " .. getGroupInfo(newId).name .. "." + local str = "been " .. (group < newGroup and "promoted" or "demoted") .. " to " .. getGroupInfo(newGroup).name .. "." if(not config.broadcast) then doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, param .. " has " .. str) else diff --git a/data/talkactions/scripts/pvp.lua b/data/talkactions/scripts/pvp.lua index d934f26..88df841 100644 --- a/data/talkactions/scripts/pvp.lua +++ b/data/talkactions/scripts/pvp.lua @@ -1,7 +1,7 @@ local worlds = { - [WORLD_TYPE_NO_PVP] = "No-PVP", - [WORLD_TYPE_PVP] = "PVP", - [WORLD_TYPE_PVP_ENFORCED] = "PVP-Enforced" + [WORLDTYPE_OPTIONAL] = "Optional PvP", + [WORLDTYPE_OPEN] = "Open PvP", + [WORLDTYPE_HARDCORE] = "Hardcore PvP" } function onSay(cid, words, param, channel) diff --git a/data/talkactions/scripts/reload.lua b/data/talkactions/scripts/reload.lua index f297c40..2f0fe64 100644 --- a/data/talkactions/scripts/reload.lua +++ b/data/talkactions/scripts/reload.lua @@ -2,14 +2,14 @@ local reloadInfo = { {RELOAD_ACTIONS, "actions", "action"}, {RELOAD_CHAT, "chat", "channels"}, {RELOAD_CONFIG, "config", "configuration"}, - {RELOAD_CREATUREEVENTS, "creatureevents", "creature events", "creaturescripts", "creature scripts", "creatureevent", "creature event", "creature script", "creaturescript"}, + {RELOAD_CREATUREEVENTS, "creatureevents", "creature events", "creaturescripts", "creature scripts", "creatureevent", "creature event", "creature script", "creaturescript", "creature"}, {RELOAD_GAMESERVERS, "gameservers", "game servers", "servers"}, - {RELOAD_GLOBALEVENTS, "globalevents", "global events", "globalevent", "global event"}, + {RELOAD_GLOBALEVENTS, "globalevents", "global events", "globalevent", "global event", "global"}, {RELOAD_GROUPS, "groups", "playergroups", "group"}, {RELOAD_HIGHSCORES, "highscores", "scores", "highscore", "score"}, - {RELOAD_HOUSEPRICES, "houseprices", "house prices", "prices"}, {RELOAD_ITEMS, "items", "item"}, {RELOAD_MONSTERS, "monsters", "monster"}, + {RELOAD_MOUNTS, "mounts", "mount"}, {RELOAD_MOVEEVENTS, "moveevents", "move events", "movements", "move", "movement"}, {RELOAD_NPCS, "npcs", "npc"}, {RELOAD_OUTFITS, "outfits", "outfit"}, diff --git a/data/talkactions/scripts/remove.lua b/data/talkactions/scripts/remove.lua index eda8153..949eb54 100644 --- a/data/talkactions/scripts/remove.lua +++ b/data/talkactions/scripts/remove.lua @@ -15,7 +15,7 @@ function onSay(cid, words, param, channel) toPos.stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE local tmp = getThingFromPos(toPos) if(tmp.uid ~= 0) then - if(isCreature(tmp.uid)) then + if(isCreature(tmp.uid) and (not isPlayer(tmp.uid) or (not isPlayerGhost(tmp.uid) or getPlayerGhostAccess(cid) >= getPlayerGhostAccess(tmp.uid)))) then doRemoveCreature(tmp.uid) else doRemoveItem(tmp.uid, math.min(math.max(1, tmp.type), amount)) diff --git a/data/talkactions/scripts/reports.lua b/data/talkactions/scripts/reports.lua index 38ae752..4dadd64 100644 --- a/data/talkactions/scripts/reports.lua +++ b/data/talkactions/scripts/reports.lua @@ -1,30 +1,53 @@ local config = { - expireReportsAfterReads = getConfigValue('expireReportsAfterReads') + expiration = getConfigValue('reportsExpirationAfterReads') } function onSay(cid, words, param, channel) - local reportId = tonumber(param) + local t = { param } + if(t[1] ~= nil) then + t = string.explode(param, " ", 1) + end + + local reportId = tonumber(t[1]) if(reportId ~= nil) then local report = db.getResult("SELECT `r`.*, `p`.`name` AS `player_name` FROM `server_reports` r LEFT JOIN `players` p ON `r`.`player_id` = `p`.`id` WHERE `r`.`id` = " .. reportId) - if(report:getID() ~= -1) then - db.executeQuery("UPDATE `server_reports` SET `reads` = `reads` + 1 WHERE `id` = " .. reportId) - doPlayerPopupFYI(cid, "Report no. " .. reportId .. "\n\nName: " .. report:getDataString("player_name") .. "\nPosition: [X: " .. report:getDataInt("posx") .. " | Y: " .. report:getDataInt("posy") .. " | Z: " .. report:getDataInt("posz") .. "]\nDate: " .. os.date("%c", report:getDataInt("timestamp")) .. "\nReport:\n" .. report:getDataString("report")) - report:free() - else + if(report:getID() == -1) then doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Report with no. " .. reportId .. " does not exists.") + return true end - else - local list = db.getResult("SELECT `r`.`id`, `r`.`player_id`, `p`.`name` AS `player_name` FROM `server_reports` r LEFT JOIN `players` p ON `r`.`player_id` = `p`.`id` WHERE `r`.`reads` < " .. config.expireReportsAfterReads) - if(list:getID() ~= -1) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "New reports:") - repeat - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, list:getDataInt("id") .. ", by " .. list:getDataString("player_name") .. ".") - until not list:next() - list:free() - else - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "There are no active reports.") + + if(t[2] ~= nil and isInArray({"delete", "remove"}, t[2])) then + db.query("DELETE FROM `server_reports` WHERE `id` = " .. reportId) + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Report with no. " .. reportId .. " has been deleted.") + + result:free() + return true end + + + db.query("UPDATE `server_reports` SET `reads` = `reads` + 1 WHERE `id` = " .. reportId) + doPlayerPopupFYI(cid, "Report no. " .. reportId .. "\n\nName: " .. report:getDataString("player_name") .. "\nPosition: [X: " .. report:getDataInt("posx") .. " | Y: " .. report:getDataInt("posy") .. " | Z: " .. report:getDataInt("posz") .. "]\nDate: " .. os.date("%c", report:getDataInt("timestamp")) .. "\nReads: " .. report:getDataInt("reads") .. "\nReport:\n\n" .. report:getDataString("report")) + + report:free() + return true + end + + local tmp = ";" + if(config.expiration > 0) then + tmp = " WHERE `r`.`reads` < " .. config.expiration .. ";" end + local list = db.getResult("SELECT `r`.`id`, `r`.`player_id`, `p`.`name` AS `player_name` FROM `server_reports` r LEFT JOIN `players` p ON `r`.`player_id` = `p`.`id`" .. tmp) + if(list:getID() == -1) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "There are no active reports.") + return true + end + + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Active reports:") + repeat + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, list:getDataInt("id") .. ", by " .. list:getDataString("player_name") .. ".") + until not list:next() + + list:free() return true end diff --git a/data/talkactions/scripts/save.lua b/data/talkactions/scripts/save.lua index 0adf3b4..783101c 100644 --- a/data/talkactions/scripts/save.lua +++ b/data/talkactions/scripts/save.lua @@ -1,17 +1,26 @@ local savingEvent = 0 function onSay(cid, words, param, channel) - if(isNumber(param)) then + local tmp = tonumber(param) + if(tmp ~= nil) then stopEvent(savingEvent) - save(tonumber(param) * 60 * 1000) + save(tmp * 60 * 1000) + elseif(param:trim() == '') then + doSaveServer(13) else - doSaveServer() + local tid = getPlayerByNameWildcard(param) + if(not tid or (isPlayerGhost(tid) and getPlayerGhostAccess(tid) > getPlayerGhostAccess(cid))) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. param .. " not found.") + else + doPlayerSave(tid) + end end + return true end function save(delay) - doSaveServer() + doSaveServer(13) if(delay > 0) then savingEvent = addEvent(save, delay, delay) end diff --git a/data/talkactions/scripts/serverinfo.lua b/data/talkactions/scripts/serverinfo.lua index 244f368..263a26b 100644 --- a/data/talkactions/scripts/serverinfo.lua +++ b/data/talkactions/scripts/serverinfo.lua @@ -3,7 +3,8 @@ local config = { rateSkill = getConfigInfo('rateSkill'), rateLoot = getConfigInfo('rateLoot'), rateMagic = getConfigInfo('rateMagic'), - rateSpawn = getConfigInfo('rateSpawn'), + rateSpawnMin = getConfigInfo('rateSpawnMin'), + rateSpawnMax = getConfigInfo('rateSpawnMax'), protectionLevel = getConfigInfo('protectionLevel'), stages = getBooleanFromString(getConfigInfo('experienceStages')) } @@ -14,6 +15,6 @@ function onSay(cid, words, param, channel) exp = getExperienceStage(getPlayerLevel(cid), getVocationInfo(getPlayerVocation(cid)).experienceMultiplier) end - doPlayerPopupFYI(cid, "Server Information:\n\nExperience rate: x" .. exp .. "\nSkills rate: x" .. config.rateSkill .. "\nLoot rate: x" .. config.rateLoot .. "\nMagic rate: x" .. config.rateMagic .. "\nSpawns rate: x" .. config.rateSpawn .. "\nProtection level: " .. config.protectionLevel) + doPlayerPopupFYI(cid, "Server Information:\n\nExperience rate: x" .. exp .. "\nSkills rate: x" .. config.rateSkill .. "\nLoot rate: x" .. config.rateLoot .. "\nMagic rate: x" .. config.rateMagic .. "\nSpawns rate: x" .. config.rateSpawnMin .. " - x" .. config.rateSpawnMax .. "\nProtection level: " .. config.protectionLevel) return true end diff --git a/data/talkactions/scripts/shutdown.lua b/data/talkactions/scripts/shutdown.lua index 8ad1619..a1081d4 100644 --- a/data/talkactions/scripts/shutdown.lua +++ b/data/talkactions/scripts/shutdown.lua @@ -6,17 +6,30 @@ function onSay(cid, words, param, channel) return true end - if(param:lower() == "stop") then - stopEvent(shutdownEvent) - shutdownEvent = 0 - return true - elseif(param:lower() == "kill") then - os.exit() - return true + params = string.explode(param, ",") + local action, reason, mins = "", "", 0 + if(not tonumber(params[1])) then + action = string.trim(params[1]:lower()) + else + mins = tonumber(string.trim(params[1])) + if(table.maxn(params) > 1) then + reason = params[2] + end + end + + if(action) then + if(action == "cancel" or action == "stop") then + stopEvent(shutdownEvent) + shutdownEvent = 0 + doPlayerSendCancel(cid, "Shutdown cancelled.") + return true + elseif(action == "kill") then + os.exit() + return true + end end - param = tonumber(param) - if(not param or param < 0) then + if(not mins or mins < 0) then doPlayerSendCancel(cid, "Numeric param may not be lower than 0.") return true end @@ -25,23 +38,25 @@ function onSay(cid, words, param, channel) stopEvent(shutdownEvent) end - return prepareShutdown(math.abs(math.ceil(param))) + return prepareShutdown(math.abs(math.ceil(mins)), reason) end -function prepareShutdown(minutes) +function prepareShutdown(minutes, reason) if(minutes <= 0) then doSetGameState(GAMESTATE_SHUTDOWN) return false end + local change, r = 5, (reason and " Reason: "..reason or "") if(minutes == 1) then - doBroadcastMessage("Server is going down in " .. minutes .. " minute, please log out now!") - elseif(minutes <= 3) then - doBroadcastMessage("Server is going down in " .. minutes .. " minutes, please log out.") + doBroadcastMessage("Server is going down in " .. minutes .. " minute, please log out now!" .. r) + elseif(minutes <= 5) then + doBroadcastMessage("Server is going down in " .. minutes .. " minutes, please log out." .. r) + change = 1 else - doBroadcastMessage("Server is going down in " .. minutes .. " minutes.") + doBroadcastMessage("Server is going down in " .. minutes .. " minutes." .. r) end - shutdownEvent = addEvent(prepareShutdown, 60000, minutes - 1) + shutdownEvent = addEvent(prepareShutdown, (change * 60 * 1000), minutes - change, reason) return true end diff --git a/data/talkactions/scripts/skill.lua b/data/talkactions/scripts/skill.lua new file mode 100644 index 0000000..53fbfa8 --- /dev/null +++ b/data/talkactions/scripts/skill.lua @@ -0,0 +1,43 @@ +function onSay(cid, words, param, channel) + if(param == '') then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.") + return true + end + + local t = string.explode(param, ",") + if(not t[2]) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Not enough params.") + return true + end + + local pid = getPlayerByNameWildcard(t[1]) + if(not pid or (isPlayerGhost(pid) and getPlayerGhostAccess(pid) > getPlayerGhostAccess(cid))) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. t[1] .. " not found.") + return true + end + + t[2] = t[2]:lower() + local skill = SKILL_IDS[t[2]] + if(not skill) then + local tmp = t[2]:sub(1, 1) + if(tmp == 'l' or tmp == 'e') then + skill = SKILL__LEVEL + elseif(tmp == 'm') then + skill = SKILL__MAGLEVEL + else + skill = tonumber(t[2]) + if(not skill or skill < SKILL_FIRST or SKILL > SKILL__PRE_LAST) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Such skill does not exists.") + return true + end + end + end + + local amount = tonumber(t[3]) + if(not amount or amount == 0) then + amount = 1 + end + + doPlayerAddSkill(pid, skill, amount, true) + return true +end diff --git a/data/talkactions/scripts/storage.lua b/data/talkactions/scripts/storage.lua index 43f39da..3153b43 100644 --- a/data/talkactions/scripts/storage.lua +++ b/data/talkactions/scripts/storage.lua @@ -7,7 +7,7 @@ function onSay(cid, words, param) local tid = getPlayerByNameWildcard(t[1]) if(not tid or (isPlayerGhost(tid) and getPlayerGhostAccess(tid) > getPlayerGhostAccess(cid))) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. param .. " not found.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. t[1] .. " not found.") return true end diff --git a/data/talkactions/scripts/teleporthere.lua b/data/talkactions/scripts/teleporthere.lua index ca227f6..c837083 100644 --- a/data/talkactions/scripts/teleporthere.lua +++ b/data/talkactions/scripts/teleporthere.lua @@ -1,6 +1,6 @@ function onSay(cid, words, param, channel) if(param == '') then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command requires param.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.") return true end diff --git a/data/talkactions/scripts/teleportmaster.lua b/data/talkactions/scripts/teleportmaster.lua deleted file mode 100644 index 370f655..0000000 --- a/data/talkactions/scripts/teleportmaster.lua +++ /dev/null @@ -1,37 +0,0 @@ -function onSay(cid, words, param, channel) - local tid = cid - if(param ~= '') then - tid = getPlayerByNameWildcard(param) - if(not tid or (isPlayerGhost(tid) and getPlayerGhostAccess(tid) > getPlayerGhostAccess(cid))) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. param .. " not found.") - return true - end - end - - local pos = getPlayerTown(tid) - local tmp = getTownName(pos) - if(not tmp) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Home town does not exists.") - return true - end - - pos = getTownTemplePosition(pos) - if(not pos or isInArray({pos.x, pos.y}, 0)) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Wrong temple position for town " .. tmp .. ".") - return true - end - - pos = getClosestFreeTile(tid, pos) - if(not pos or isInArray({pos.x, pos.y}, 0)) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Destination not reachable.") - return true - end - - tmp = getCreaturePosition(tid) - if(doTeleportThing(tid, pos, true) and not isPlayerGhost(tid)) then - doSendMagicEffect(tmp, CONST_ME_POFF) - doSendMagicEffect(pos, CONST_ME_TELEPORT) - end - - return true -end diff --git a/data/talkactions/scripts/teleportsend.lua b/data/talkactions/scripts/teleportsend.lua index e711a14..7d214e2 100644 --- a/data/talkactions/scripts/teleportsend.lua +++ b/data/talkactions/scripts/teleportsend.lua @@ -1,6 +1,6 @@ function onSay(cid, words, param, channel) if(param == '') then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command requires param.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.") return true end diff --git a/data/talkactions/scripts/teleportto.lua b/data/talkactions/scripts/teleportto.lua index 7bb7c45..b05c5cb 100644 --- a/data/talkactions/scripts/teleportto.lua +++ b/data/talkactions/scripts/teleportto.lua @@ -1,6 +1,6 @@ function onSay(cid, words, param, channel) if(param == '') then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command requires param.") + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command param required.") return true end @@ -14,6 +14,8 @@ function onSay(cid, words, param, channel) pos = getCreaturePosition(player) elseif(creature ~= nil and (not isPlayer(creature) or (not isPlayerGhost(creature) or getPlayerGhostAccess(creature) <= getPlayerGhostAccess(cid)))) then pos = getCreaturePosition(creature) + elseif(isInArray({'back', 'last'}, param:lower())) then + pos = getCreatureLastPosition(cid) elseif(type(waypoint) == 'table' and waypoint.x ~= 0 and waypoint.y ~= 0) then pos = waypoint elseif(tile[2] and tile[3]) then diff --git a/data/talkactions/scripts/teleporttown.lua b/data/talkactions/scripts/teleporttown.lua index 2351ef5..5374331 100644 --- a/data/talkactions/scripts/teleporttown.lua +++ b/data/talkactions/scripts/teleporttown.lua @@ -1,42 +1,52 @@ function onSay(cid, words, param, channel) - if(param == '') then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Command requires param.") + local master = false + if(words == '/t') then + master = true + elseif(param == '') then + local str = "" + for i, town in ipairs(getTownList()) do + str = str .. town.name .. "\n" + end + + doShowTextDialog(cid, ITEM_ACTION_BOOK, str) return true end - local tid = cid - local t = string.explode(param, ",") - if(t[2]) then - tid = getPlayerByNameWildcard(t[2]) - if(not tid or (isPlayerGhost(tid) and getPlayerGhostAccess(tid) > getPlayerGhostAccess(cid))) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. t[2] .. " not found.") + local tid, t = cid, string.explode(param, ",") + if(t[(master and 1 or 2)]) then + tid = getPlayerByNameWildcard(t[(master and 1 or 2)]) + if(not tid or (isPlayerGhost(tid) and getPlayerAccess(tid) > getPlayerAccess(cid))) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Player " .. t[(master and 1 or 2)] .. " not found.") return true end end - local tmp = t[1] - if(not tonumber(tmp)) then - tmp = getTownId(tmp) - if(not tmp) then - doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Town " .. t[1] .. " does not exists.") - return true + local tmp = getPlayerTown(tid) + if(not master) then + tmp = t[1] + if(not tonumber(tmp)) then + tmp = getTownId(tmp) + if(not tmp) then + doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Town " .. t[1] .. " does not exists.") + return true + end end end - local pos = getTownTemplePosition(tmp, false) - if(not pos or isInArray({pos.x, pos.y}, 0)) then + local pos = getTownTemplePosition(tmp) + if(type(pos) ~= 'table' or isInArray({pos.x, pos.y}, 0)) then doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Town " .. t[1] .. " does not exists or has invalid temple position.") return true end pos = getClosestFreeTile(tid, pos) - if(not pos or isInArray({pos.x, pos.y}, 0)) then + if(type(pos) ~= 'table' or isInArray({pos.x, pos.y}, 0)) then doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Destination not reachable.") return true end tmp = getCreaturePosition(tid) - if(doTeleportThing(tid, pos, true) and not isPlayerGhost(tid)) then + if(doTeleportThing(tid, pos) and not isPlayerGhost(tid)) then doSendMagicEffect(tmp, CONST_ME_POFF) doSendMagicEffect(pos, CONST_ME_TELEPORT) end diff --git a/data/talkactions/scripts/unban.lua b/data/talkactions/scripts/unban.lua index cf9bc54..9ac37bd 100644 --- a/data/talkactions/scripts/unban.lua +++ b/data/talkactions/scripts/unban.lua @@ -4,7 +4,7 @@ function onSay(cid, words, param, channel) return true end - local account, tmp = getAccountIdByName(param), true + local account, tmp = getAccountIdByName(param), false if(account == 0) then account = getAccountIdByAccount(param) if(account == 0) then @@ -12,7 +12,7 @@ function onSay(cid, words, param, channel) return true end - tmp = false + tmp = true end local ban = getBanData(account, BAN_ACCOUNT) @@ -25,7 +25,7 @@ function onSay(cid, words, param, channel) doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, name .. " has been " .. (ban.expires == -1 and "undeleted" or "unbanned") .. ".") end - if(not tmp) then + if(tmp) then return true end diff --git a/data/talkactions/scripts/war.lua b/data/talkactions/scripts/war.lua new file mode 100644 index 0000000..df28a13 --- /dev/null +++ b/data/talkactions/scripts/war.lua @@ -0,0 +1,178 @@ +function onSay(cid, words, param, channel) + local guild = getPlayerGuildId(cid) + if(not guild or getPlayerGuildLevel(cid) < GUILDLEVEL_LEADER) then + doPlayerSendChannelMessage(cid, "", "You cannot execute this talkaction.", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + local t = string.explode(param, ",") + if(not t[2]) then + doPlayerSendChannelMessage(cid, "", "Not enough param(s).", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + local enemy = getGuildId(t[2]) + if(not enemy) then + doPlayerSendChannelMessage(cid, "", "Guild \"" .. t[2] .. "\" does not exists.", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + if(enemy == guild) then + doPlayerSendChannelMessage(cid, "", "You cannot perform war action on your own guild.", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + local enemyName, tmp = "", db.getResult("SELECT `name` FROM `guilds` WHERE `id` = " .. enemy) + if(tmp:getID() ~= -1) then + enemyName = tmp:getDataString("name") + tmp:free() + end + + if(isInArray({"accept", "reject", "cancel"}, t[1])) then + local query = "`guild_id` = " .. enemy .. " AND `enemy_id` = " .. guild + if(t[1] == "cancel") then + query = "`guild_id` = " .. guild .. " AND `enemy_id` = " .. enemy + end + + tmp = db.getResult("SELECT `id`, `begin`, `end`, `payment` FROM `guild_wars` WHERE " .. query .. " AND `status` = 0") + if(tmp:getID() == -1) then + doPlayerSendChannelMessage(cid, "", "Currently there's no pending invitation for a war with " .. enemyName .. ".", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + if(t[1] == "accept") then + local _tmp = db.getResult("SELECT `balance` FROM `guilds` WHERE `id` = " .. guild) + local state = _tmp:getID() < 0 or _tmp:getDataInt("balance") < tmp:getDataInt("payment") + + _tmp:free() + if(state) then + doPlayerSendChannelMessage(cid, "", "Your guild balance is too low to accept this invitation.", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + db.executeQuery("UPDATE `guilds` SET `balance` = `balance` - " .. tmp:getDataInt("payment") .. " WHERE `id` = " .. guild) + end + + query = "UPDATE `guild_wars` SET " + local msg = "accepted " .. enemyName .. " invitation to war." + if(t[1] == "reject") then + query = query .. "`end` = " .. os.time() .. ", `status` = 2" + msg = "rejected " .. enemyName .. " invitation to war." + elseif(t[1] == "cancel") then + query = query .. "`end` = " .. os.time() .. ", `status` = 3" + msg = "canceled invitation to a war with " .. enemyName .. "." + else + query = query .. "`begin` = " .. os.time() .. ", `end` = " .. (tmp:getDataInt("end") > 0 and (os.time() + ((tmp:getDataInt("begin") - tmp:getDataInt("end")) / 86400)) or 0) .. ", `status` = 1" + end + + query = query .. " WHERE `id` = " .. tmp:getDataInt("id") + if(t[1] == "accept") then + doGuildAddEnemy(guild, enemy, tmp:getDataInt("id"), WAR_GUILD) + doGuildAddEnemy(enemy, guild, tmp:getDataInt("id"), WAR_ENEMY) + end + + tmp:free() + db.executeQuery(query) + doBroadcastMessage(getPlayerGuildName(cid) .. " has " .. msg, MESSAGE_EVENT_ADVANCE) + return true + end + + if(t[1] == "invite") then + local str = "" + tmp = db.getResult("SELECT `guild_id`, `status` FROM `guild_wars` WHERE `guild_id` IN (" .. guild .. "," .. enemy .. ") AND `enemy_id` IN (" .. enemy .. "," .. guild .. ") AND `status` IN (0, 1)") + if(tmp:getID() ~= -1) then + if(tmp:getDataInt("status") == 0) then + if(tmp:getDataInt("guild_id") == guild) then + str = "You have already invited " .. enemyName .. " to war." + else + str = enemyName .. " have already invited you to war." + end + else + str = "You are already on a war with " .. enemyName .. "." + end + + tmp:free() + end + + if(str ~= "") then + doPlayerSendChannelMessage(cid, "", str, TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + local frags = tonumber(t[3]) + if(frags ~= nil) then + frags = math.max(10, math.min(1000, frags)) + else + frags = 100 + end + + local payment = tonumber(t[4]) + if(payment ~= nil) then + payment = math.max(100000, math.min(1000000000, payment)) + tmp = db.getResult("SELECT `balance` FROM `guilds` WHERE `id` = " .. guild) + + local state = tmp:getID() < 0 or tmp:getDataInt("balance") < payment + tmp:free() + if(state) then + doPlayerSendChannelMessage(cid, "", "Your guild balance is too low for such payment.", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + db.executeQuery("UPDATE `guilds` SET `balance` = `balance` - " .. payment .. " WHERE `id` = " .. guild) + else + payment = 0 + end + + local begining, ending = os.time(), tonumber(t[5]) + if(ending ~= nil and ending ~= 0) then + ending = begining + (ending * 86400) + else + ending = 0 + end + + db.executeQuery("INSERT INTO `guild_wars` (`guild_id`, `enemy_id`, `begin`, `end`, `frags`, `payment`) VALUES (" .. guild .. ", " .. enemy .. ", " .. begining .. ", " .. ending .. ", " .. frags .. ", " .. payment .. ");") + doBroadcastMessage(getPlayerGuildName(cid) .. " has invited " .. enemyName .. " to war till " .. frags .. " frags.", MESSAGE_EVENT_ADVANCE) + return true + end + + if(not isInArray({"end", "finish"}, t[1])) then + return false + end + + local status = (t[1] == "end" and 1 or 4) + tmp = db.getResult("SELECT `id` FROM `guild_wars` WHERE `guild_id` = " .. guild .. " AND `enemy_id` = " .. enemy .. " AND `status` = " .. status) + if(tmp:getID() ~= -1) then + local query = "UPDATE `guild_wars` SET `end` = " .. os.time() .. ", `status` = 5 WHERE `id` = " .. tmp:getDataInt("id") + tmp:free() + doGuildRemoveEnemy(guild, enemy) + doGuildRemoveEnemy(enemy, guild) + + db.executeQuery(query) + doBroadcastMessage(getPlayerGuildName(cid) .. " has " .. (status == 4 and "mend fences" or "ended up a war") .. " with " .. enemyName .. ".", MESSAGE_EVENT_ADVANCE) + return true + end + + if(status == 4) then + doPlayerSendChannelMessage(cid, "", "Currently there's no pending war truce from " .. enemyName .. ".", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + tmp = db.getResult("SELECT `id`, `end` FROM `guild_wars` WHERE `guild_id` = " .. enemy .. " AND `enemy_id` = " .. guild .. " AND `status` = 1") + if(tmp:getID() ~= -1) then + if(tmp:getDataInt("end") > 0) then + tmp:free() + doPlayerSendChannelMessage(cid, "", "You cannot request ending for war with " .. enemyName .. ".", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true + end + + local query = "UPDATE `guild_wars` SET `status` = 4, `end` = " .. os.time() .. " WHERE `id` = " .. tmp:getDataInt("id") + tmp:free() + + db.executeQuery(query) + doBroadcastMessage(getPlayerGuildName(cid) .. " has signed an armstice declaration on a war with " .. enemyName .. ".", MESSAGE_EVENT_ADVANCE) + return true + end + + doPlayerSendChannelMessage(cid, "", "Currently there's no active war with " .. enemyName .. ".", TALKTYPE_CHANNEL_HIGHLIGHT, CHANNEL_GUILD) + return true +end \ No newline at end of file diff --git a/data/talkactions/scripts/waypoints.lua b/data/talkactions/scripts/waypoints.lua index 0886ef8..b2daba3 100644 --- a/data/talkactions/scripts/waypoints.lua +++ b/data/talkactions/scripts/waypoints.lua @@ -10,6 +10,6 @@ function onSay(cid, words, param, channel) str = str .. waypoint.name .. "\n" end - doShowTextDialog(cid, 2160, str) + doShowTextDialog(cid, ITEM_ACTION_BOOK, str) return true end diff --git a/data/talkactions/talkactions.xml b/data/talkactions/talkactions.xml index 16d2032..d07f7e6 100644 --- a/data/talkactions/talkactions.xml +++ b/data/talkactions/talkactions.xml @@ -1,12 +1,12 @@ - + @@ -16,7 +16,7 @@ - + @@ -24,7 +24,6 @@ - @@ -32,7 +31,8 @@ - + + @@ -46,46 +46,43 @@ + - + + - - + + - - - - - + + + + + - - + + + - - + + - + - - - - - - - - - - + + + + diff --git a/data/weapons/lib/weapons.lua b/data/weapons/lib/weapons.lua deleted file mode 100644 index fc54edc..0000000 --- a/data/weapons/lib/weapons.lua +++ /dev/null @@ -1 +0,0 @@ --- Nothing -- \ No newline at end of file diff --git a/data/weapons/scripts/burst_arrow.lua b/data/weapons/scripts/burst_arrow.lua index 99fd90d..1a09703 100644 --- a/data/weapons/scripts/burst_arrow.lua +++ b/data/weapons/scripts/burst_arrow.lua @@ -6,14 +6,9 @@ setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_FIREAREA) setCombatParam(combat, COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_BURSTARROW) setCombatFormula(combat, COMBAT_FORMULA_SKILL, 1, 0, 1, 0) -local area = createCombatArea({ - {1, 1, 1}, - {1, 3, 1}, - {1, 1, 1} -}) - +local area = createCombatArea( { {1, 1, 1}, {1, 3, 1}, {1, 1, 1} } ) setCombatArea(combat, area) function onUseWeapon(cid, var) return doCombat(cid, combat, var) -end +end \ No newline at end of file diff --git a/data/weapons/scripts/poison_arrow.lua b/data/weapons/scripts/poison_arrow.lua index 20ec0f9..7fb29b5 100644 --- a/data/weapons/scripts/poison_arrow.lua +++ b/data/weapons/scripts/poison_arrow.lua @@ -11,4 +11,4 @@ setCombatCondition(combat, condition) function onUseWeapon(cid, var) return doCombat(cid, combat, var) -end +end \ No newline at end of file diff --git a/data/weapons/scripts/viper_star.lua b/data/weapons/scripts/viper_star.lua index bd1e1eb..10a7375 100644 --- a/data/weapons/scripts/viper_star.lua +++ b/data/weapons/scripts/viper_star.lua @@ -15,7 +15,7 @@ setCombatCondition(xCombat, condition) function onUseWeapon(cid, var) local ret = doCombat(cid, combat, var) - if(ret == false) then + if(not ret) then return false end @@ -27,5 +27,6 @@ function onUseWeapon(cid, var) ret = doCombat(cid, xCombat, var) end end + return ret -end +end \ No newline at end of file diff --git a/data/weapons/weapons.xml b/data/weapons/weapons.xml index eac6b42..45d6734 100644 --- a/data/weapons/weapons.xml +++ b/data/weapons/weapons.xml @@ -25,6 +25,9 @@ + + + @@ -51,67 +54,108 @@ + + + + + + + - + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + @@ -121,99 +165,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -221,9 +352,11 @@ + + @@ -231,9 +364,11 @@ + + @@ -241,70 +376,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + @@ -341,57 +517,95 @@ + + + + + - + + + + + + + + + + - + + - + + - + + - + + - + + - + + - + + - + + - + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + @@ -401,4 +615,6 @@ + + diff --git a/data/world/tfs-house.xml b/data/world/tfs-house.xml new file mode 100644 index 0000000..9d137e4 --- /dev/null +++ b/data/world/tfs-house.xml @@ -0,0 +1,2 @@ + + diff --git a/data/world/tfs-spawn.xml b/data/world/tfs-spawn.xml new file mode 100644 index 0000000..0526da5 --- /dev/null +++ b/data/world/tfs-spawn.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/world/tfs.otbm b/data/world/tfs.otbm new file mode 100644 index 0000000000000000000000000000000000000000..9e4a8f905e4598856141919dbdcff8bee42543e4 GIT binary patch literal 747636 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

F?*{0RAuUcQN6|MLp-UdVKW_wdF+_VqXHTt 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 N8apP$ADDjw`9H%WZk7N5 literal 0 HcmV?d00001 diff --git a/database.cpp b/database.cpp index b55dead..bff7093 100644 --- a/database.cpp +++ b/database.cpp @@ -24,9 +24,6 @@ #ifdef __USE_SQLITE__ #include "databasesqlite.h" #endif -#ifdef __USE_ODBC__ -#include "databaseodbc.h" -#endif #ifdef __USE_PGSQL__ #include "databasepgsql.h" #endif @@ -37,36 +34,36 @@ extern ConfigManager g_config; #endif boost::recursive_mutex DBQuery::databaseLock; -Database* _Database::_instance = NULL; +Database* _Database::m_instance = NULL; Database* _Database::getInstance() { - if(!_instance) + if(!m_instance) { #if defined MULTI_SQL_DRIVERS #ifdef __USE_MYSQL__ if(g_config.getString(ConfigManager::SQL_TYPE) == "mysql") - _instance = new DatabaseMySQL; + m_instance = new DatabaseMySQL; #endif -#ifdef __USE_ODBC__ - if(g_config.getString(ConfigManager::SQL_TYPE) == "odbc") - _instance = new DatabaseODBC; +#ifdef __USE_MYSQLPP__ + else if(g_config.getString(ConfigManager::SQL_TYPE) == "mysql++") + m_instance = new DatabaseMySQLpp; #endif #ifdef __USE_SQLITE__ - if(g_config.getString(ConfigManager::SQL_TYPE) == "sqlite") - _instance = new DatabaseSQLite; + else if(g_config.getString(ConfigManager::SQL_TYPE) == "sqlite") + m_instance = new DatabaseSQLite; #endif #ifdef __USE_PGSQL__ - if(g_config.getString(ConfigManager::SQL_TYPE) == "pgsql") - _instance = new DatabasePgSQL; + else if(g_config.getString(ConfigManager::SQL_TYPE) == "pgsql") + m_instance = new DatabasePgSQL; #endif #else - _instance = new Database; + m_instance = new Database; #endif } - _instance->use(); - return _instance; + m_instance->use(); + return m_instance; } DBResult* _Database::verifyResult(DBResult* result) @@ -75,36 +72,25 @@ DBResult* _Database::verifyResult(DBResult* result) return result; result->free(); - result = NULL; return NULL; } -DBInsert::DBInsert(Database* db) -{ - m_db = db; - m_rows = 0; - // checks if current database engine supports multiline INSERTs - m_multiLine = m_db->getParam(DBPARAM_MULTIINSERT); -} - -void DBInsert::setQuery(const std::string& query) +void DBInsert::setQuery(std::string query) { m_query = query; m_buf = ""; m_rows = 0; } -bool DBInsert::addRow(const std::string& row) +bool DBInsert::addRow(std::string row) { - if(!m_multiLine) // executes INSERT for current row - return m_db->executeQuery(m_query + "(" + row + ")"); + if(!m_db->multiLine()) + return m_db->query(m_query + "(" + row + ")"); - m_rows++; - int32_t size = m_buf.length(); - // adds new row to buffer - if(!size) + ++m_rows; + if(m_buf.empty()) m_buf = "(" + row + ")"; - else if(size > 8192) + else if(m_buf.length() > 8192) { if(!execute()) return false; @@ -126,15 +112,11 @@ bool DBInsert::addRow(std::stringstream& row) bool DBInsert::execute() { - if(!m_multiLine || m_buf.length() < 1) // INSERTs were executed on-fly - return true; - - if(!m_rows) //no rows to execute + if(!m_db->multiLine() || m_buf.empty() || !m_rows) return true; m_rows = 0; - // executes buffer - bool res = m_db->executeQuery(m_query + m_buf); + bool ret = m_db->query(m_query + m_buf); m_buf = ""; - return res; + return ret; } diff --git a/database.h b/database.h index e8fb7cd..a056480 100644 --- a/database.h +++ b/database.h @@ -25,7 +25,7 @@ #ifdef MULTI_SQL_DRIVERS #define DATABASE_VIRTUAL virtual #define DATABASE_CLASS _Database -#define DBRES_CLASS _DBResult +#define RESULT_CLASS _DBResult class _Database; class _DBResult; #else @@ -33,25 +33,25 @@ class _DBResult; #if defined(__USE_MYSQL__) #define DATABASE_CLASS DatabaseMySQL -#define DBRES_CLASS MySQLResult +#define RESULT_CLASS MySQLResult class DatabaseMySQL; class MySQLResult; +#elif defined(__USE_MYSQLPP__) +#define DATABASE_CLASS DatabaseMySQLpp +#define RESULT_CLASS MySQLppResult +class DatabaseMySQLpp; +class MySQLppResult; + #elif defined(__USE_SQLITE__) #define DATABASE_CLASS DatabaseSQLite -#define DBRES_CLASS SQLiteResult +#define RESULT_CLASS SQLiteResult class DatabaseSQLite; class SQLiteResult; -#elif defined(__USE_ODBC__) -#define DATABASE_CLASS DatabaseODBC -#define DBRES_CLASS ODBCResult -class DatabaseODBC; -class ODBCResult; - #elif defined(__USE_PGSQL__) #define DATABASE_CLASS DatabasePgSQL -#define DBRES_CLASS PgSQLResult +#define RESULT_CLASS PgSQLResult class DatabasePgSQL; class PgSQLResult; @@ -65,9 +65,8 @@ class PgSQLResult; #define Database void #else typedef DATABASE_CLASS Database; -typedef DBRES_CLASS DBResult; +typedef RESULT_CLASS DBResult; -class DBQuery; enum DBParam_t { DBPARAM_MULTIINSERT = 1 @@ -76,6 +75,8 @@ enum DBParam_t class _Database { public: + friend class DBTransaction; + /** * Singleton implementation. * @@ -93,7 +94,7 @@ class _Database * @param DBParam_t parameter to get * @return suitable for given parameter */ - DATABASE_VIRTUAL bool getParam(DBParam_t param) {return false;} + DATABASE_VIRTUAL bool multiLine() const {return false;} /** * Database connected. @@ -102,14 +103,13 @@ class _Database * * @return whether or not the database is connected. */ - DATABASE_VIRTUAL bool isConnected() {return m_connected;} + DATABASE_VIRTUAL bool isConnected() const {return m_connected;} /** * Database ... */ - DATABASE_VIRTUAL void use() {m_use = time(NULL);} + DATABASE_VIRTUAL void use() {m_use = OTSYS_TIME();} - protected: /** * Transaction related methods. * @@ -119,13 +119,11 @@ class _Database * @note * If your database system doesn't support transactions you should return true - it's not feature test, code should work without transaction, just will lack integrity. */ - friend class DBTransaction; - DATABASE_VIRTUAL bool beginTransaction() {return 0;} - DATABASE_VIRTUAL bool rollback() {return 0;} - DATABASE_VIRTUAL bool commit() {return 0;} + DATABASE_VIRTUAL bool beginTransaction() {return false;} + DATABASE_VIRTUAL bool rollback() {return false;} + DATABASE_VIRTUAL bool commit() {return false;} - public: /** * Executes command. * @@ -134,7 +132,7 @@ class _Database * @param std::string query command * @return true on success, false on error */ - DATABASE_VIRTUAL bool executeQuery(const std::string &query) {return 0;} + DATABASE_VIRTUAL bool query(std::string) {return false;} /** * Queries database. @@ -144,7 +142,7 @@ class _Database * @param std::string query * @return results object (null on error) */ - DATABASE_VIRTUAL DBResult* storeQuery(const std::string &query) {return 0;} + DATABASE_VIRTUAL DBResult* storeQuery(std::string) {return NULL;} /** * Escapes string for query. @@ -154,7 +152,7 @@ class _Database * @param std::string string to be escaped * @return quoted string */ - DATABASE_VIRTUAL std::string escapeString(const std::string &s) {return "''";} + DATABASE_VIRTUAL std::string escapeString(std::string) {return "''";} /** * Escapes binary stream for query. @@ -165,7 +163,7 @@ class _Database * @param long stream length * @return quoted string */ - DATABASE_VIRTUAL std::string escapeBlob(const char* s, uint32_t length) {return "''";} + DATABASE_VIRTUAL std::string escapeBlob(const char*, uint32_t) {return "''";} /** * Retrieve id of last inserted row @@ -179,7 +177,7 @@ class _Database * * @return the case insensitive operator */ - DATABASE_VIRTUAL std::string getStringComparison() {return "= ";} + DATABASE_VIRTUAL std::string getStringComparer() {return "= ";} DATABASE_VIRTUAL std::string getUpdateLimiter() {return " LIMIT 1;";} /** @@ -190,16 +188,16 @@ class _Database DATABASE_VIRTUAL DatabaseEngine_t getDatabaseEngine() {return DATABASE_ENGINE_NONE;} protected: - _Database() {} - DATABASE_VIRTUAL ~_Database() {} - DBResult* verifyResult(DBResult* result); + _Database(): m_connected(false) {} + DATABASE_VIRTUAL ~_Database() {m_connected = false;} + bool m_connected; - time_t m_use; + int64_t m_use; private: - static Database* _instance; + static Database* m_instance; }; class _DBResult @@ -209,29 +207,29 @@ class _DBResult *\returns The Integer value of the selected field and row *\param s The name of the field */ - DATABASE_VIRTUAL int32_t getDataInt(const std::string &s) {return 0;} + DATABASE_VIRTUAL int32_t getDataInt(const std::string&) {return 0;} /** Get the Long value of a field in database *\returns The Long value of the selected field and row *\param s The name of the field */ - DATABASE_VIRTUAL int64_t getDataLong(const std::string &s) {return 0;} + DATABASE_VIRTUAL int64_t getDataLong(const std::string&) {return 0;} /** Get the String of a field in database *\returns The String of the selected field and row *\param s The name of the field */ - DATABASE_VIRTUAL std::string getDataString(const std::string &s) {return "''";} + DATABASE_VIRTUAL std::string getDataString(const std::string&) {return "";} /** Get the blob of a field in database *\returns a PropStream that is initiated with the blob data field, if not exist it returns NULL. *\param s The name of the field */ - DATABASE_VIRTUAL const char* getDataStream(const std::string &s, uint64_t &size) {return 0;} + DATABASE_VIRTUAL const char* getDataStream(const std::string&, uint64_t&) {return "";} /** Result freeing */ - DATABASE_VIRTUAL void free() {/*delete this;*/} + DATABASE_VIRTUAL void free() {} /** Moves to next result in set *\returns true if moved, false if there are no more results. @@ -253,7 +251,7 @@ class DBQuery : public std::stringstream friend class _Database; public: DBQuery() {databaseLock.lock();} - virtual ~DBQuery() {str(""); databaseLock.unlock();} + ~DBQuery() {databaseLock.unlock();} protected: static boost::recursive_mutex databaseLock; @@ -272,15 +270,15 @@ class DBInsert * * @param Database* database wrapper */ - DBInsert(Database* db); - virtual ~DBInsert() {} + DBInsert(Database* db): m_db(db), m_rows(0) {} + ~DBInsert() {} /** * Sets query prototype. * * @param std::string& INSERT query */ - void setQuery(const std::string& query); + void setQuery(std::string query); /** * Adds new row to INSERT statement. @@ -289,7 +287,7 @@ class DBInsert * * @param std::string& row data */ - bool addRow(const std::string& row); + bool addRow(std::string row); /** * Allows to use addRow() with stringstream as parameter. */ @@ -302,7 +300,6 @@ class DBInsert protected: Database* m_db; - bool m_multiLine; uint32_t m_rows; std::string m_query, m_buf; @@ -312,10 +309,10 @@ class DBInsert #ifndef MULTI_SQL_DRIVERS #if defined(__USE_MYSQL__) #include "databasemysql.h" +#elif defined(__USE_MYSQLPP__) +#include "databasemysqlpp.h" #elif defined(__USE_SQLITE__) #include "databasesqlite.h" -#elif defined(__USE_ODBC__) -#include "databaseodbc.h" #elif defined(__USE_PGSQL__) #include "databasepgsql.h" #endif @@ -326,42 +323,39 @@ class DBTransaction public: DBTransaction(Database* database) { - m_database = database; - m_state = STATE_NO_START; + m_db = database; + m_state = STATE_FRESH; } - virtual ~DBTransaction() + ~DBTransaction() { - if(m_state == STATE_START) - m_database->rollback(); + if(m_state == STATE_READY) + m_db->rollback(); } bool begin() { - m_state = STATE_START; - return m_database->beginTransaction(); + m_state = STATE_READY; + return m_db->beginTransaction(); } bool commit() { - if(m_state != STATE_START) + if(m_state != STATE_READY) return false; - m_state = STEATE_COMMIT; - return m_database->commit(); + m_state = STATE_DONE; + return m_db->commit(); } private: - Database* m_database; - + Database* m_db; enum TransactionStates_t { - STATE_NO_START, - STATE_START, - STEATE_COMMIT - }; - - TransactionStates_t m_state; + STATE_FRESH, + STATE_READY, + STATE_DONE + } m_state; }; #endif #endif diff --git a/databasemanager.cpp b/databasemanager.cpp index 9e5f2eb..989d1ed 100644 --- a/databasemanager.cpp +++ b/databasemanager.cpp @@ -40,12 +40,12 @@ bool DatabaseManager::optimizeTables() query.str(""); do { - std::cout << "> Optimizing table: " << result->getDataString("TABLE_NAME") << "... "; + std::clog << "> Optimizing table: " << result->getDataString("TABLE_NAME") << "... "; query << "OPTIMIZE TABLE `" << result->getDataString("TABLE_NAME") << "`;"; - if(db->executeQuery(query.str())) - std::cout << "[success]" << std::endl; + if(db->query(query.str())) + std::clog << "[success]" << std::endl; else - std::cout << "[failure]" << std::endl; + std::clog << "[failure]" << std::endl; query.str(""); } @@ -57,19 +57,19 @@ bool DatabaseManager::optimizeTables() case DATABASE_ENGINE_SQLITE: { - if(!db->executeQuery("VACUUM;")) + if(!db->query("VACUUM;")) break; - std::cout << "> Optimized database." << std::endl; + std::clog << "> Optimized database." << std::endl; return true; } case DATABASE_ENGINE_POSTGRESQL: { - if(!db->executeQuery("VACUUM FULL;")) + if(!db->query("VACUUM FULL;")) break; - std::cout << "> Optimized database." << std::endl; + std::clog << "> Optimized database." << std::endl; return true; } @@ -189,12 +189,9 @@ uint32_t DatabaseManager::updateDatabase() { Database* db = Database::getInstance(); uint32_t version = getDatabaseVersion(); - if(db->getDatabaseEngine() == DATABASE_ENGINE_ODBC) - return version; - if(version < 6 && db->getDatabaseEngine() == DATABASE_ENGINE_POSTGRESQL) { - std::cout << "> WARNING: Couldn't update database - PostgreSQL support available since version 6, please use latest pgsql.sql schema." << std::endl; + std::clog << "> WARNING: Couldn't update database - PostgreSQL support available since version 6, please use latest pgsql.sql schema." << std::endl; registerDatabaseConfig("db_version", 6); return 6; } @@ -204,13 +201,13 @@ uint32_t DatabaseManager::updateDatabase() { case 0: { - std::cout << "> Updating database to version: 1..." << std::endl; + std::clog << "> Updating database to version: 1..." << std::endl; if(db->getDatabaseEngine() == DATABASE_ENGINE_SQLITE) - db->executeQuery("CREATE TABLE IF NOT EXISTS `server_config` (`config` VARCHAR(35) NOT NULL DEFAULT '', `value` INTEGER NOT NULL);"); + db->query("CREATE TABLE IF NOT EXISTS `server_config` (`config` VARCHAR(35) NOT NULL DEFAULT '', `value` INTEGER NOT NULL);"); else - db->executeQuery("CREATE TABLE IF NOT EXISTS `server_config` (`config` VARCHAR(35) NOT NULL DEFAULT '', `value` INT NOT NULL) ENGINE = InnoDB;"); + db->query("CREATE TABLE IF NOT EXISTS `server_config` (`config` VARCHAR(35) NOT NULL DEFAULT '', `value` INT NOT NULL) ENGINE = InnoDB;"); - db->executeQuery("INSERT INTO `server_config` VALUES ('db_version', 1);"); + db->query("INSERT INTO `server_config` VALUES ('db_version', 1);"); if(db->getDatabaseEngine() == DATABASE_ENGINE_SQLITE) { //TODO: 0.2 migration SQLite support @@ -221,9 +218,10 @@ uint32_t DatabaseManager::updateDatabase() if(!tableExists("server_motd")) { //update bans table - if(db->executeQuery("CREATE TABLE IF NOT EXISTS `bans2` (`id` INT UNSIGNED NOT NULL auto_increment, `type` TINYINT(1) NOT NULL COMMENT 'this field defines if its ip, account, player, or any else ban', `value` INT UNSIGNED NOT NULL COMMENT 'ip, player guid, account number', `param` INT UNSIGNED NOT NULL DEFAULT 4294967295 COMMENT 'mask', `active` TINYINT(1) NOT NULL DEFAULT TRUE, `expires` INT UNSIGNED NOT NULL, `added` INT UNSIGNED NOT NULL, `admin_id` INT UNSIGNED NOT NULL DEFAULT 0, `comment` TEXT NOT NULL, `reason` INT UNSIGNED NOT NULL DEFAULT 0, `action` INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `type` (`type`, `value`)) ENGINE = InnoDB;")) + if(db->query("CREATE TABLE IF NOT EXISTS `bans2` (`id` INT UNSIGNED NOT NULL auto_increment, `type` TINYINT(1) NOT NULL COMMENT 'this field defines if its ip, account, player, or any else ban', `value` INT UNSIGNED NOT NULL COMMENT 'ip, player guid, account number', `param` INT UNSIGNED NOT NULL DEFAULT 4294967295 COMMENT 'mask', `active` TINYINT(1) NOT NULL DEFAULT TRUE, `expires` INT UNSIGNED NOT NULL, `added` INT UNSIGNED NOT NULL, `admin_id` INT UNSIGNED NOT NULL DEFAULT 0, `comment` TEXT NOT NULL, `reason` INT UNSIGNED NOT NULL DEFAULT 0, `action` INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY `type` (`type`, `value`)) ENGINE = InnoDB;")) { - if(DBResult* result = db->storeQuery("SELECT * FROM `bans`;")) + query << "SELECT * FROM `bans`;"; + if(DBResult* result = db->storeQuery(query.str())) { do { @@ -252,7 +250,7 @@ uint32_t DatabaseManager::updateDatabase() if(query.str() != "") { - db->executeQuery(query.str()); + db->query(query.str()); query.str(""); } } @@ -260,8 +258,8 @@ uint32_t DatabaseManager::updateDatabase() result->free(); } - db->executeQuery("DROP TABLE `bans`;"); - db->executeQuery("RENAME TABLE `bans2` TO `bans`;"); + db->query("DROP TABLE `bans`;"); + db->query("RENAME TABLE `bans2` TO `bans`;"); } std::string queryList[] = { @@ -296,8 +294,8 @@ uint32_t DatabaseManager::updateDatabase() "CREATE TRIGGER `ondelete_accounts` BEFORE DELETE ON `accounts` FOR EACH ROW BEGIN DELETE FROM `bans` WHERE `type` != 1 AND `type` != 2 AND `value` = OLD.`id`; END;", "CREATE TRIGGER `ondelete_players` BEFORE DELETE ON `players` FOR EACH ROW BEGIN DELETE FROM `bans` WHERE `type` = 2 AND `value` = OLD.`id`; UPDATE `houses` SET `owner` = 0 WHERE `owner` = OLD.`id`; END;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); } return 1; @@ -305,33 +303,36 @@ uint32_t DatabaseManager::updateDatabase() case 1: { - std::cout << "> Updating database to version: 2..." << std::endl; + std::clog << "> Updating database to version: 2..." << std::endl; if(db->getDatabaseEngine() == DATABASE_ENGINE_SQLITE) - db->executeQuery("ALTER TABLE `players` ADD `promotion` INTEGER NOT NULL DEFAULT 0;"); + db->query("ALTER TABLE `players` ADD `promotion` INTEGER NOT NULL DEFAULT 0;"); else - db->executeQuery("ALTER TABLE `players` ADD `promotion` INT NOT NULL DEFAULT 0;"); + db->query("ALTER TABLE `players` ADD `promotion` INT NOT NULL DEFAULT 0;"); DBResult* result; - if((result = db->storeQuery("SELECT `player_id`, `value` FROM `player_storage` WHERE `key` = 30018 AND `value` > 0"))) + query << "SELECT `player_id`, `value` FROM `player_storage` WHERE `key` = 30018 AND `value` > 0"; + if((result = db->storeQuery(query.str()))) { do { query << "UPDATE `players` SET `promotion` = " << result->getDataLong("value") << " WHERE `id` = " << result->getDataInt("player_id") << ";"; - db->executeQuery(query.str()); + db->query(query.str()); query.str(""); } while(result->next()); result->free(); } - db->executeQuery("DELETE FROM `player_storage` WHERE `key` = 30018;"); - db->executeQuery("ALTER TABLE `accounts` ADD `name` VARCHAR(32) NOT NULL DEFAULT '';"); - if((result = db->storeQuery("SELECT `id` FROM `accounts`;"))) + db->query("DELETE FROM `player_storage` WHERE `key` = 30018;"); + db->query("ALTER TABLE `accounts` ADD `name` VARCHAR(32) NOT NULL DEFAULT '';"); + + query << "SELECT `id` FROM `accounts`;"; + if((result = db->storeQuery(query.str()))) { do { query << "UPDATE `accounts` SET `name` = '" << result->getDataInt("id") << "' WHERE `id` = " << result->getDataInt("id") << ";"; - db->executeQuery(query.str()); + db->query(query.str()); query.str(""); } while(result->next()); @@ -339,12 +340,12 @@ uint32_t DatabaseManager::updateDatabase() } if(db->getDatabaseEngine() == DATABASE_ENGINE_SQLITE) - db->executeQuery("ALTER TABLE `players` ADD `deleted` BOOLEAN NOT NULL DEFAULT 0;"); + db->query("ALTER TABLE `players` ADD `deleted` INTEGER NOT NULL DEFAULT 0;"); else - db->executeQuery("ALTER TABLE `players` ADD `deleted` TINYINT(1) NOT NULL DEFAULT 0;"); + db->query("ALTER TABLE `players` ADD `deleted` INT NOT NULL DEFAULT 0;"); if(db->getDatabaseEngine() == DATABASE_ENGINE_MYSQL) - db->executeQuery("ALTER TABLE `player_items` CHANGE `attributes` `attributes` BLOB NOT NULL;"); + db->query("ALTER TABLE `player_items` CHANGE `attributes` `attributes` BLOB NOT NULL;"); registerDatabaseConfig("db_version", 2); return 2; @@ -352,14 +353,15 @@ uint32_t DatabaseManager::updateDatabase() case 2: { - std::cout << "> Updating database to version: 3..." << std::endl; - db->executeQuery("UPDATE `players` SET `vocation` = `vocation` - 4 WHERE `vocation` >= 5 AND `vocation` <= 8;"); + std::clog << "> Updating database to version: 3..." << std::endl; + db->query("UPDATE `players` SET `vocation` = `vocation` - 4 WHERE `vocation` >= 5 AND `vocation` <= 8;"); DBResult* result; - if((result = db->storeQuery("SELECT COUNT(`id`) AS `count` FROM `players` WHERE `vocation` > 4;")) + query << "SELECT COUNT(`id`) AS `count` FROM `players` WHERE `vocation` > 4;"; + if((result = db->storeQuery(query.str())) && result->getDataInt("count")) { - std::cout << "[Warning] There are still " << result->getDataInt("count") << " players with vocation above 4, please mind to update them manually." << std::endl; + std::clog << "[Warning] There are still " << result->getDataInt("count") << " players with vocation above 4, please mind to update them manually." << std::endl; result->free(); } @@ -369,8 +371,8 @@ uint32_t DatabaseManager::updateDatabase() case 3: { - std::cout << "> Updating database to version: 4..." << std::endl; - db->executeQuery("ALTER TABLE `houses` ADD `name` VARCHAR(255) NOT NULL;"); + std::clog << "> Updating database to version: 4..." << std::endl; + db->query("ALTER TABLE `houses` ADD `name` VARCHAR(255) NOT NULL;"); if(db->getDatabaseEngine() == DATABASE_ENGINE_SQLITE) { std::string queryList[] = { @@ -379,8 +381,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `houses` ADD `price` INTEGER NOT NULL DEFAULT 0;", "ALTER TABLE `houses` ADD `rent` INTEGER NOT NULL DEFAULT 0;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); } else { @@ -390,8 +392,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `houses` ADD `price` INT UNSIGNED NOT NULL DEFAULT 0;", "ALTER TABLE `houses` ADD `rent` INT UNSIGNED NOT NULL DEFAULT 0;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); } registerDatabaseConfig("db_version", 4); @@ -400,15 +402,15 @@ uint32_t DatabaseManager::updateDatabase() case 4: { - std::cout << "> Updating database to version: 5..." << std::endl; - db->executeQuery("ALTER TABLE `player_deaths` ADD `altkilled_by` VARCHAR(255) NOT NULL;"); + std::clog << "> Updating database to version: 5..." << std::endl; + db->query("ALTER TABLE `player_deaths` ADD `altkilled_by` VARCHAR(255) NOT NULL;"); registerDatabaseConfig("db_version", 5); return 5; } case 5: { - std::cout << "> Updating database to version: 6..." << std::endl; + std::clog << "> Updating database to version: 6..." << std::endl; switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: @@ -420,8 +422,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `tiles` ADD INDEX (`x`, `y`, `z`);", "ALTER TABLE `tile_items` ADD INDEX (`sid`);" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -436,8 +438,8 @@ uint32_t DatabaseManager::updateDatabase() "CREATE TABLE `player_storage` (`player_id` INTEGER NOT NULL, `key` INTEGER NOT NULL, `value` VARCHAR(255) NOT NULL DEFAULT '0', UNIQUE (`player_id`, `key`), FOREIGN KEY (`player_id`) REFERENCES `players` (`id`));", "INSERT INTO `player_storage` SELECT * FROM `player_storage2`;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -452,15 +454,16 @@ uint32_t DatabaseManager::updateDatabase() case 6: { - std::cout << "> Updating database to version: 7..." << std::endl; + std::clog << "> Updating database to version: 7..." << std::endl; if(g_config.getBool(ConfigManager::INGAME_GUILD_MANAGEMENT)) { - if(DBResult* result = db->storeQuery("SELECT `r`.`id`, `r`.`guild_id` FROM `guild_ranks` r LEFT JOIN `guilds` g ON `r`.`guild_id` = `g`.`id` WHERE `g`.`ownerid` = `g`.`id` AND `r`.`level` = 3;")) + query << "SELECT `r`.`id`, `r`.`guild_id` FROM `guild_ranks` r LEFT JOIN `guilds` g ON `r`.`guild_id` = `g`.`id` WHERE `g`.`ownerid` = `g`.`id` AND `r`.`level` = 3;"; + if(DBResult* result = db->storeQuery(query.str())) { do { query << "UPDATE `guilds`, `players` SET `guilds`.`ownerid` = `players`.`id` WHERE `guilds`.`id` = " << result->getDataInt("guild_id") << " AND `players`.`rank_id` = " << result->getDataInt("id") << ";"; - db->executeQuery(query.str()); + db->query(query.str()); query.str(""); } while(result->next()); @@ -474,7 +477,7 @@ uint32_t DatabaseManager::updateDatabase() case 7: { - std::cout << "> Updating database version to: 8..." << std::endl; + std::clog << "> Updating database version to: 8..." << std::endl; switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: @@ -507,8 +510,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `tile_items` ADD `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0;", "ALTER TABLE `tile_items` ADD UNIQUE (`tile_id`, `world_id`, `sid`);" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -528,27 +531,27 @@ uint32_t DatabaseManager::updateDatabase() case 8: { - std::cout << "> Updating database to version: 9..." << std::endl; - db->executeQuery("ALTER TABLE `bans` ADD `statement` VARCHAR(255) NOT NULL;"); + std::clog << "> Updating database to version: 9..." << std::endl; + db->query("ALTER TABLE `bans` ADD `statement` VARCHAR(255) NOT NULL;"); registerDatabaseConfig("db_version", 9); return 9; } case 9: { - std::cout << "> Updating database to version: 10..." << std::endl; + std::clog << "> Updating database to version: 10..." << std::endl; registerDatabaseConfig("db_version", 10); return 10; } case 10: { - std::cout << "> Updating database to version: 11..." << std::endl; - db->executeQuery("ALTER TABLE `players` ADD `description` VARCHAR(255) NOT NULL DEFAULT '';"); + std::clog << "> Updating database to version: 11..." << std::endl; + db->query("ALTER TABLE `players` ADD `description` VARCHAR(255) NOT NULL DEFAULT '';"); if(tableExists("map_storage")) { - db->executeQuery("ALTER TABLE `map_storage` RENAME TO `house_data`;"); - db->executeQuery("ALTER TABLE `house_data` ADD `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0 AFTER `house_id`;"); + db->query("ALTER TABLE `map_storage` RENAME TO `house_data`;"); + db->query("ALTER TABLE `house_data` ADD `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0 AFTER `house_id`;"); } else if(!tableExists("house_storage")) { @@ -561,11 +564,11 @@ uint32_t DatabaseManager::updateDatabase() query << " ENGINE = InnoDB"; query << ";"; - db->executeQuery(query.str()); + db->query(query.str()); query.str(""); } else - db->executeQuery("ALTER TABLE `house_storage` RENAME TO `house_data`;"); + db->query("ALTER TABLE `house_storage` RENAME TO `house_data`;"); registerDatabaseConfig("db_version", 11); return 11; @@ -573,9 +576,9 @@ uint32_t DatabaseManager::updateDatabase() case 11: { - std::cout << "> Updating database to version: 12..." << std::endl; - db->executeQuery("UPDATE `players` SET `stamina` = 151200000 WHERE `stamina` > 151200000;"); - db->executeQuery("UPDATE `players` SET `loss_experience` = `loss_experience` * 10, `loss_mana` = `loss_mana` * 10, `loss_skills` = `loss_skills` * 10, `loss_items` = `loss_items` * 10;"); + std::clog << "> Updating database to version: 12..." << std::endl; + db->query("UPDATE `players` SET `stamina` = 151200000 WHERE `stamina` > 151200000;"); + db->query("UPDATE `players` SET `loss_experience` = `loss_experience` * 10, `loss_mana` = `loss_mana` * 10, `loss_skills` = `loss_skills` * 10, `loss_items` = `loss_items` * 10;"); switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: @@ -588,8 +591,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `players` CHANGE `loss_items` `loss_items` INT NOT NULL DEFAULT 100;", "ALTER TABLE `players` ADD `loss_containers` INT NOT NULL DEFAULT 100 AFTER `loss_skills`;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -609,15 +612,14 @@ uint32_t DatabaseManager::updateDatabase() case 12: { - std::cout << "> Updating database to version: 13..." << std::endl; + std::clog << "> Updating database to version: 13..." << std::endl; std::string queryList[] = { "ALTER TABLE `accounts` DROP KEY `group_id`;", "ALTER TABLE `players` DROP KEY `group_id`;", "DROP TABLE `groups`;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); - + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); registerDatabaseConfig("db_version", 13); return 13; @@ -625,7 +627,7 @@ uint32_t DatabaseManager::updateDatabase() case 13: { - std::cout << "> Updating database to version: 14..." << std::endl; + std::clog << "> Updating database to version: 14..." << std::endl; switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: @@ -635,8 +637,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `houses` ADD `beds` INT UNSIGNED NOT NULL DEFAULT 0;", "ALTER TABLE `houses` ADD `guild` TINYINT(1) UNSIGNED NOT NULL DEFAULT FALSE;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -648,8 +650,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `houses` ADD `beds` INTEGER NOT NULL DEFAULT 0;", "ALTER TABLE `houses` ADD `guild` BOOLEAN NOT NULL DEFAULT FALSE;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -668,8 +670,8 @@ uint32_t DatabaseManager::updateDatabase() case 14: { - std::cout << "> Updating database to version: 15..." << std::endl; - db->executeQuery("DROP TABLE `player_deaths`;"); //no support for moving, sorry! + std::clog << "> Updating database to version: 15..." << std::endl; + db->query("DROP TABLE `player_deaths`;"); //no support for moving, sorry! switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: @@ -707,8 +709,8 @@ uint32_t DatabaseManager::updateDatabase() FOREIGN KEY (`kill_id`) REFERENCES `killers` (`id`) ON DELETE CASCADE\ ) ENGINE = InnoDB;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -741,8 +743,8 @@ uint32_t DatabaseManager::updateDatabase() FOREIGN KEY (`kill_id`) REFERENCES `killers` (`id`)\ );" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -761,7 +763,7 @@ uint32_t DatabaseManager::updateDatabase() case 15: { - std::cout << "> Updating database to version: 16..." << std::endl; + std::clog << "> Updating database to version: 16..." << std::endl; switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: @@ -772,8 +774,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `killers` ADD `unjustified` TINYINT(1) UNSIGNED NOT NULL DEFAULT FALSE;", "UPDATE `players` SET `redskulltime` = 0;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -785,8 +787,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `killers` ADD `unjustified` BOOLEAN NOT NULL DEFAULT FALSE;", "UPDATE `players` SET `redskulltime` = 0;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -805,11 +807,11 @@ uint32_t DatabaseManager::updateDatabase() case 16: { - std::cout << "> Updating database to version: 17..." << std::endl; + std::clog << "> Updating database to version: 17..." << std::endl; switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: - db->executeQuery("CREATE TABLE IF NOT EXISTS `player_namelocks`\ + db->query("CREATE TABLE IF NOT EXISTS `player_namelocks`\ (\ `player_id` INT NOT NULL DEFAULT 0,\ `name` VARCHAR(255) NOT NULL,\ @@ -821,7 +823,7 @@ uint32_t DatabaseManager::updateDatabase() break; case DATABASE_ENGINE_SQLITE: - db->executeQuery("CREATE TABLE IF NOT EXISTS `player_namelocks` (\ + db->query("CREATE TABLE IF NOT EXISTS `player_namelocks` (\ `player_id` INTEGER NOT NULL,\ `name` VARCHAR(255) NOT NULL,\ `new_name` VARCHAR(255) NOT NULL,\ @@ -840,7 +842,7 @@ uint32_t DatabaseManager::updateDatabase() case 17: { - std::cout << "> Updating database to version: 18..." << std::endl; + std::clog << "> Updating database to version: 18..." << std::endl; switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: @@ -857,8 +859,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `tiles` ADD FOREIGN KEY (`house_id`, `world_id`) REFERENCES `houses`(`id`, `world_id`) ON DELETE CASCADE;", "ALTER TABLE `houses` ADD `clear` TINYINT(1) UNSIGNED NOT NULL DEFAULT FALSE;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -874,8 +876,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `tiles` ADD FOREIGN KEY (`house_id`, `world_id`) REFERENCES `houses`(`id`, `world_id`);", "ALTER TABLE `houses` ADD `clear` BOOLEAN NOT NULL DEFAULT FALSE;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -891,7 +893,7 @@ uint32_t DatabaseManager::updateDatabase() case 18: { - std::cout << "> Updating database to version: 19..." << std::endl; + std::clog << "> Updating database to version: 19..." << std::endl; switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: @@ -911,8 +913,8 @@ uint32_t DatabaseManager::updateDatabase() FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE\ ) ENGINE = InnoDB;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -933,8 +935,8 @@ uint32_t DatabaseManager::updateDatabase() FOREIGN KEY (`player_id`) REFERENCES `players` (`id`)\ );" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -950,7 +952,7 @@ uint32_t DatabaseManager::updateDatabase() case 19: { - std::cout << "> Updating database to version: 20..." << std::endl; + std::clog << "> Updating database to version: 20..." << std::endl; switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: @@ -959,8 +961,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `players` CHANGE `redskulltime` `skulltime` INT NOT NULL DEFAULT 0;", "ALTER TABLE `players` ADD `skull` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0 AFTER `save`;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -972,8 +974,8 @@ uint32_t DatabaseManager::updateDatabase() "ALTER TABLE `players` ADD `skull` INTEGER NOT NULL DEFAULT 0;", "UPDATE `players` SET `skulltime` = `redskulltime`, `redskulltime` = 0;" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); break; } @@ -990,14 +992,14 @@ uint32_t DatabaseManager::updateDatabase() case 20: { - std::cout << "> Updating database to version: 21..." << std::endl; + std::clog << "> Updating database to version: 21..." << std::endl; std::string queryList[] = { "UPDATE `bans` SET `type` = 3 WHERE `type` = 5;", "UPDATE `bans` SET `param` = 2 WHERE `type` = 2;", "UPDATE `bans` SET `param` = 0 WHERE `type` IN (3,4);" }; - for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); i++) - db->executeQuery(queryList[i]); + for(uint32_t i = 0; i < sizeof(queryList) / sizeof(std::string); ++i) + db->query(queryList[i]); registerDatabaseConfig("db_version", 21); return 21; @@ -1005,14 +1007,14 @@ uint32_t DatabaseManager::updateDatabase() case 21: { - std::cout << "> Updating database to version: 22..." << std::endl; + std::clog << "> Updating database to version: 22..." << std::endl; switch(db->getDatabaseEngine()) { case DATABASE_ENGINE_MYSQL: - db->executeQuery("CREATE TABLE `account_viplist` (`account_id` INT NOT NULL, `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, `player_id` INT NOT NULL, KEY (`account_id`), KEY (`player_id`), KEY (`world_id`), UNIQUE (`account_id`, `player_id`), FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`) ON DELETE CASCADE, FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE) ENGINE = InnoDB;"); + db->query("CREATE TABLE `account_viplist` (`account_id` INT NOT NULL, `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, `player_id` INT NOT NULL, KEY (`account_id`), KEY (`player_id`), KEY (`world_id`), UNIQUE (`account_id`, `player_id`), FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`) ON DELETE CASCADE, FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE) ENGINE = InnoDB;"); break; case DATABASE_ENGINE_SQLITE: - db->executeQuery("CREATE TABLE `account_viplist` (`account_id` INTEGER NOT NULL, `world_id` INTEGER NOT NULL DEFAULT 0, `player_id` INTEGER NOT NULL, UNIQUE (`account_id`, `player_id`), FOREIGN KEY `account_id` REFERENCES `accounts` (`id`), FOREIGN KEY `player_id` REFERENCES `players` (`id`));"); + db->query("CREATE TABLE `account_viplist` (`account_id` INTEGER NOT NULL, `world_id` INTEGER NOT NULL DEFAULT 0, `player_id` INTEGER NOT NULL, UNIQUE (`account_id`, `player_id`), FOREIGN KEY `account_id` REFERENCES `accounts` (`id`), FOREIGN KEY `player_id` REFERENCES `players` (`id`));"); break; default: break; @@ -1024,7 +1026,7 @@ uint32_t DatabaseManager::updateDatabase() case 22: { - std::cout << "> Updating database to version 23..." << std::endl; + std::clog << "> Updating database to version 23..." << std::endl; if(g_config.getBool(ConfigManager::ACCOUNT_MANAGER)) { query << "SELECT `id`, `key` FROM `accounts` WHERE `key` "; @@ -1036,30 +1038,338 @@ uint32_t DatabaseManager::updateDatabase() query << " '0';"; if(DBResult* result = db->storeQuery(query.str())) { - query.str(""); do { std::string key = result->getDataString("key"); _encrypt(key, false); - query << "UPDATE `accounts` SET `key` = " << db->escapeString(key) << " WHERE `id` = " << result->getDataInt("id") << db->getUpdateLimiter(); - db->executeQuery(query.str()); query.str(""); + query << "UPDATE `accounts` SET `key` = " << db->escapeString(key) << " WHERE `id` = " << result->getDataInt("id") << db->getUpdateLimiter(); + db->query(query.str()); } while(result->next()); result->free(); } } - query << "DELETE FROM `server_config` WHERE `config` " << db->getStringComparison() << "'password_type';"; - db->executeQuery(query.str()); query.str(""); + query << "DELETE FROM `server_config` WHERE `config` " << db->getStringComparer() << "'password_type';"; + db->query(query.str()); registerDatabaseConfig("encryption", g_config.getNumber(ConfigManager::ENCRYPTION)); registerDatabaseConfig("db_version", 23); return 23; } + case 23: + { + std::clog << "> Updating database to version 24..." << std::endl; + query << "ALTER TABLE `guilds` ADD `checkdata` "; + if(db->getDatabaseEngine() == DATABASE_ENGINE_SQLITE) + query << "INTEGER NOT NULL;"; + else + query << "INT NOT NULL AFTER `creationdata`;"; + + db->query(query.str()); + query.str(""); + registerDatabaseConfig("db_version", 24); + return 24; + } + + case 24: + { + std::clog << "> Updating database to version 25..." << std::endl; + switch(db->getDatabaseEngine()) + { + case DATABASE_ENGINE_SQLITE: + { + query << "DROP TABLE `server_config`;"; + db->query(query.str()); + + query << "CREATE TABLE `server_config` (`config` VARCHAR(35) NOT NULL DEFAULT '', `value` VARCHAR(255) NOT NULL DEFAULT '', UNIQUE (`config`));"; + registerDatabaseConfig("encryption", g_config.getNumber(ConfigManager::ENCRYPTION)); + break; + } + + case DATABASE_ENGINE_MYSQL: + { + query << "ALTER TABLE `server_config` CHANGE `value` `value` VARCHAR(255) NOT NULL DEFAULT '';"; + break; + } + + default: + break; + } + + db->query(query.str()); + query.str(""); + + registerDatabaseConfig("db_version", 25); + return 25; + } + + case 25: + { + std::clog << "> Updating database to version 26..." << std::endl; + switch(db->getDatabaseEngine()) + { + case DATABASE_ENGINE_SQLITE: + { + query << "ALTER TABLE `accounts` ADD `salt` VARCHAR(40) NOT NULL DEFAULT '';"; + break; + } + + case DATABASE_ENGINE_MYSQL: + { + query << "ALTER TABLE `accounts` ADD `salt` VARCHAR(40) NOT NULL DEFAULT '' AFTER `password`;"; + break; + } + + default: + break; + } + + db->query(query.str()); + query.str(""); + + registerDatabaseConfig("db_version", 26); + return 26; + } + + case 26: + { + std::clog << "> Updating database to version 27..." << std::endl; + if(db->getDatabaseEngine() == DATABASE_ENGINE_MYSQL) + { + query << "ALTER TABLE `player_storage` CHANGE `key` `key` VARCHAR(32) NOT NULL DEFAULT '0'"; + db->query(query.str()); + query.str(""); + + query << "ALTER TABLE `global_storage` CHANGE `key` `key` VARCHAR(32) NOT NULL DEFAULT '0'"; + db->query(query.str()); + query.str(""); + } + + registerDatabaseConfig("db_version", 27); + return 27; + } + + case 27: + { + std::clog << "> Updating database to version 28..." << std::endl; + if(db->getDatabaseEngine() == DATABASE_ENGINE_MYSQL) + { + query << "ALTER TABLE `players` ADD `currmount` INT NOT NULL DEFAULT 0 AFTER `lookaddons`;"; + db->query(query.str()); + query.str(""); + } + + registerDatabaseConfig("db_version", 28); + return 28; + } + + case 28: + { + std::clog << "> Updating database to version 29..." << std::endl; + switch(db->getDatabaseEngine()) + { + case DATABASE_ENGINE_SQLITE: + { + query << "ALTER TABLE `players` ADD `lookmount` INT NOT NULL DEFAULT 0;"; + break; + } + + case DATABASE_ENGINE_MYSQL: + { + query << "ALTER TABLE `players` CHANGE `currmount` `lookmount` INT NOT NULL DEFAULT 0"; + break; + } + + default: + break; + } + + db->query(query.str()); + query.str(""); + + registerDatabaseConfig("db_version", 29); + return 29; + } + + case 29: + { + std::clog << "> Updating database to version 30..." << std::endl; + switch(db->getDatabaseEngine()) + { + case DATABASE_ENGINE_SQLITE: + { + query << "CREATE TABLE `tile_store` ( `house_id` INTEGER NOT NULL, `world_id` INTEGER NOT NULL DEFAULT 0, `data` LONGBLOB NOT NULL, FOREIGN KEY (`house_id`) REFERENCES `houses` (`id`) );"; + break; + } + + case DATABASE_ENGINE_MYSQL: + { + query << "CREATE TABLE `tile_store` ( `house_id` INT UNSIGNED NOT NULL, `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `data` LONGBLOB NOT NULL, FOREIGN KEY (`house_id`) REFERENCES `houses` (`id`) ON DELETE CASCADE ) ENGINE = InnoDB;"; + break; + } + + default: + break; + } + + db->query(query.str()); + query.str(""); + + registerDatabaseConfig("db_version", 30); + return 30; + } + + case 30: + { + std::clog << "> Updating database to version 31..." << std::endl; + switch(db->getDatabaseEngine()) + { + case DATABASE_ENGINE_SQLITE: + { + query << "ALTER TABLE `players` ADD `pvp_blessing` BOOLEAN NOT NULL DEFAULT FALSE;"; + break; + } + + case DATABASE_ENGINE_MYSQL: + { + query << "ALTER TABLE `players` ADD `pvp_blessing` TINYINT(1) NOT NULL DEFAULT 0 AFTER `blessings`;"; + break; + } + + default: + break; + } + + db->query(query.str()); + query.str(""); + + registerDatabaseConfig("db_version", 31); + return 31; + } + + case 31: + { + std::clog << "> Updating database to version 32..." << std::endl; + if(db->getDatabaseEngine() == DATABASE_ENGINE_MYSQL) + { + query << "CREATE TABLE IF NOT EXISTS `player_statements`\ +(\ + `id` INT NOT NULL AUTO_INCREMENT,\ + `player_id` INT NOT NULL,\ + `channel_id` INT NOT NULL DEFAULT 0,\ + `text` VARCHAR (255) NOT NULL,\ + `date` BIGINT NOT NULL DEFAULT 0,\ + PRIMARY KEY (`id`), KEY (`player_id`), KEY (`channel_id`),\ + FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE\ +) ENGINE = InnoDB;"; + + db->query(query.str()); + query.str(""); + + query << "CREATE TABLE IF NOT EXISTS `guild_wars`\ +(\ + `id` INT NOT NULL AUTO_INCREMENT,\ + `guild_id` INT NOT NULL,\ + `enemy_id` INT NOT NULL,\ + `begin` BIGINT NOT NULL DEFAULT 0,\ + `end` BIGINT NOT NULL DEFAULT 0,\ + `frags` INT UNSIGNED NOT NULL DEFAULT 0,\ + `payment` BIGINT UNSIGNED NOT NULL DEFAULT 0,\ + `guild_kills` INT UNSIGNED NOT NULL DEFAULT 0,\ + `enemy_kills` INT UNSIGNED NOT NULL DEFAULT 0,\ + `status` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,\ + PRIMARY KEY (`id`), KEY `status` (`status`),\ + KEY `guild_id` (`guild_id`), KEY `enemy_id` (`enemy_id`),\ + FOREIGN KEY (`guild_id`) REFERENCES `guilds`(`id`) ON DELETE CASCADE,\ + FOREIGN KEY (`enemy_id`) REFERENCES `guilds`(`id`) ON DELETE CASCADE\ +) ENGINE=InnoDB;"; + + db->query(query.str()); + query.str(""); + + query << "CREATE TABLE IF NOT EXISTS `guild_kills`\ +(\ + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,\ + `guild_id` INT NOT NULL,\ + `war_id` INT NOT NULL,\ + `death_id` INT NOT NULL,\ + FOREIGN KEY (`guild_id`) REFERENCES `guilds`(`id`) ON DELETE CASCADE,\ + FOREIGN KEY (`war_id`) REFERENCES `guild_wars`(`id`) ON DELETE CASCADE,\ + FOREIGN KEY (`death_id`) REFERENCES `player_deaths`(`id`) ON DELETE CASCADE\ +) ENGINE = InnoDB;"; + + db->query(query.str()); + query.str(""); + + query << "ALTER TABLE `killers` ADD `war` INT NOT NULL DEFAULT 0;"; + db->query(query.str()); + query.str(""); + + query << "ALTER TABLE `guilds` ADD `balance` BIGINT UNSIGNED NOT NULL AFTER `motd`;"; + db->query(query.str()); + query.str(""); + } + + registerDatabaseConfig("db_version", 32); + return 32; + } + + case 32: + { + std::clog << "> Updating database to version 33..." << std::endl; + if(db->getDatabaseEngine() == DATABASE_ENGINE_MYSQL) + { + query << "ALTER TABLE `bans` DROP `reason`;"; + db->query(query.str()); + query.str(""); + + query << "ALTER TABLE `bans` DROP `action`;"; + db->query(query.str()); + query.str(""); + + query << "ALTER TABLE `bans` DROP `statement`;"; + db->query(query.str()); + query.str(""); + } + + registerDatabaseConfig("db_version", 33); + return 33; + } + + case 33: + { + std::clog << "> Updating database to version 34..." << std::endl; + switch(db->getDatabaseEngine()) + { + case DATABASE_ENGINE_MYSQL: + { + db->query("CREATE TABLE `market_offers` (`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `player_id` INT NOT NULL, `sale` TINYINT(1) NOT NULL DEFAULT 0, `itemtype` INT UNSIGNED NOT NULL, `amount` SMALLINT UNSIGNED NOT NULL, `created` BIGINT UNSIGNED NOT NULL, `anonymous` TINYINT(1) NOT NULL DEFAULT 0, `price` INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`id`), KEY(`sale`, `itemtype`), KEY(`created`), FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE) ENGINE = InnoDB;"); + db->query("CREATE TABLE `market_history` (`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `player_id` INT NOT NULL, `sale` TINYINT(1) NOT NULL DEFAULT 0, `itemtype` INT UNSIGNED NOT NULL, `amount` SMALLINT UNSIGNED NOT NULL, `price` INT UNSIGNED NOT NULL DEFAULT 0, `expires_at` BIGINT UNSIGNED NOT NULL, `inserted` BIGINT UNSIGNED NOT NULL, `state` TINYINT(1) UNSIGNED NOT NULL, PRIMARY KEY(`id`), KEY(`player_id`, `sale`), FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE) ENGINE = InnoDB;"); + break; + } + + case DATABASE_ENGINE_SQLITE: + { + db->query("CREATE TABLE `market_offers` (`id` INTEGER PRIMARY KEY NOT NULL, `player_id` INTEGER NOT NULL, `sale` BOOLEAN NOT NULL DEFAULT 0, `itemtype` UNSIGNED INTEGER NOT NULL, `amount` UNSIGNED INTEGER NOT NULL, `created` UNSIGNED INTEGER NOT NULL, `anonymous` BOOLEAN NOT NULL DEFAULT 0, `price` UNSIGNED INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE);"); + db->query("CREATE INDEX market_offers_idx ON market_offers(created);"); + db->query("CREATE INDEX market_offers_idx2 ON market_offers(sale, itemtype);"); + db->query("CREATE TABLE `market_history` (`id` INTEGER PRIMARY KEY NOT NULL, `player_id` INTEGER NOT NULL, `sale` BOOLEAN NOT NULL DEFAULT 0, `itemtype` UNSIGNED INTEGER NOT NULL, `amount` UNSIGNED INTEGER NOT NULL, `price` UNSIGNED INTEGER NOT NULL DEFAULT 0, `expires_at` UNSIGNED INTEGER NOT NULL, `inserted` UNSIGNED INTEGER NOT NULL, `state` UNSIGNED INTEGER NOT NULL, FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE);"); + db->query("CREATE INDEX market_history_idx ON market_history(player_id, sale);"); + break; + } + + default: break; + } + + registerDatabaseConfig("db_version", 34); + return 34; + } + default: break; } @@ -1079,7 +1389,24 @@ bool DatabaseManager::getDatabaseConfig(std::string config, int32_t &value) if(!(result = db->storeQuery(query.str()))) return false; - value = result->getDataInt("value"); + value = atoi(result->getDataString("value").c_str()); + result->free(); + return true; +} + +bool DatabaseManager::getDatabaseConfig(std::string config, std::string &value) +{ + value = ""; + + Database* db = Database::getInstance(); + DBResult* result; + + DBQuery query; + query << "SELECT `value` FROM `server_config` WHERE `config` = " << db->escapeString(config) << ";"; + if(!(result = db->storeQuery(query.str()))) + return false; + + value = result->getDataString("value"); result->free(); return true; } @@ -1091,11 +1418,25 @@ void DatabaseManager::registerDatabaseConfig(std::string config, int32_t value) int32_t tmp = 0; if(!getDatabaseConfig(config, tmp)) - query << "INSERT INTO `server_config` VALUES (" << db->escapeString(config) << ", " << value << ");"; + query << "INSERT INTO `server_config` VALUES (" << db->escapeString(config) << ", '" << value << "');"; else - query << "UPDATE `server_config` SET `value` = " << value << " WHERE `config` = " << db->escapeString(config) << ";"; + query << "UPDATE `server_config` SET `value` = '" << value << "' WHERE `config` = " << db->escapeString(config) << ";"; - db->executeQuery(query.str()); + db->query(query.str()); +} + +void DatabaseManager::registerDatabaseConfig(std::string config, std::string value) +{ + Database* db = Database::getInstance(); + DBQuery query; + + std::string tmp; + if(!getDatabaseConfig(config, tmp)) + query << "INSERT INTO `server_config` VALUES (" << db->escapeString(config) << ", " << db->escapeString(value) << ");"; + else + query << "UPDATE `server_config` SET `value` = " << db->escapeString(value) << " WHERE `config` = " << db->escapeString(config) << ";"; + + db->query(query.str()); } void DatabaseManager::checkEncryption() @@ -1112,7 +1453,7 @@ void DatabaseManager::checkEncryption() { if((Encryption_t)value != ENCRYPTION_PLAIN) { - std::cout << "> WARNING: You cannot change the encryption to MD5, change it back in config.lua to \"sha1\"." << std::endl; + std::clog << "> WARNING: You cannot change the encryption to MD5, change it back in config.lua." << std::endl; return; } @@ -1120,22 +1461,23 @@ void DatabaseManager::checkEncryption() DBQuery query; if(db->getDatabaseEngine() != DATABASE_ENGINE_MYSQL && db->getDatabaseEngine() != DATABASE_ENGINE_POSTGRESQL) { - if(DBResult* result = db->storeQuery("SELECT `id`, `password`, `key` FROM `accounts`;")) + query << "SELECT `id`, `password`, `key` FROM `accounts`;"; + if(DBResult* result = db->storeQuery(query.str())) { do { query << "UPDATE `accounts` SET `password` = " << db->escapeString(transformToMD5(result->getDataString("password"), false)) << ", `key` = " << db->escapeString(transformToMD5(result->getDataString("key"), false)) << " WHERE `id` = " << result->getDataInt("id") << ";"; - db->executeQuery(query.str()); + db->query(query.str()); } while(result->next()); result->free(); } } else - db->executeQuery("UPDATE `accounts` SET `password` = MD5(`password`), `key` = MD5(`key`);"); + db->query("UPDATE `accounts` SET `password` = MD5(`password`), `key` = MD5(`key`);"); registerDatabaseConfig("encryption", (int32_t)newValue); - std::cout << "> Encryption updated to MD5." << std::endl; + std::clog << "> Encryption updated to MD5." << std::endl; break; } @@ -1143,7 +1485,7 @@ void DatabaseManager::checkEncryption() { if((Encryption_t)value != ENCRYPTION_PLAIN) { - std::cout << "> WARNING: You cannot change the encryption to SHA1, change it back in config.lua to \"md5\"." << std::endl; + std::clog << "> WARNING: You cannot change the encryption to SHA1, change it back in config.lua." << std::endl; return; } @@ -1151,28 +1493,85 @@ void DatabaseManager::checkEncryption() DBQuery query; if(db->getDatabaseEngine() != DATABASE_ENGINE_MYSQL && db->getDatabaseEngine() != DATABASE_ENGINE_POSTGRESQL) { - if(DBResult* result = db->storeQuery("SELECT `id`, `password`, `key` FROM `accounts`;")) + query << "SELECT `id`, `password`, `key` FROM `accounts`;"; + if(DBResult* result = db->storeQuery(query.str())) { do { query << "UPDATE `accounts` SET `password` = " << db->escapeString(transformToSHA1(result->getDataString("password"), false)) << ", `key` = " << db->escapeString(transformToSHA1(result->getDataString("key"), false)) << " WHERE `id` = " << result->getDataInt("id") << ";"; - db->executeQuery(query.str()); + db->query(query.str()); } while(result->next()); result->free(); } } else - db->executeQuery("UPDATE `accounts` SET `password` = SHA1(`password`), `key` = SHA1(`key`);"); + db->query("UPDATE `accounts` SET `password` = SHA1(`password`), `key` = SHA1(`key`);"); + + registerDatabaseConfig("encryption", (int32_t)newValue); + std::clog << "> Encryption set to SHA1." << std::endl; + break; + } + + case ENCRYPTION_SHA256: + { + if((Encryption_t)value != ENCRYPTION_PLAIN) + { + std::clog << "> WARNING: You cannot change the encryption to SHA256, change it back in config.lua." << std::endl; + return; + } + + Database* db = Database::getInstance(); + DBQuery query; + + query << "SELECT `id`, `password`, `key` FROM `accounts`;"; + if(DBResult* result = db->storeQuery(query.str())) + { + do + { + query << "UPDATE `accounts` SET `password` = " << db->escapeString(transformToSHA256(result->getDataString("password"), false)) << ", `key` = " << db->escapeString(transformToSHA256(result->getDataString("key"), false)) << " WHERE `id` = " << result->getDataInt("id") << ";"; + db->query(query.str()); + } + while(result->next()); + result->free(); + } registerDatabaseConfig("encryption", (int32_t)newValue); - std::cout << "> Encryption set to SHA1." << std::endl; + std::clog << "> Encryption set to SHA256." << std::endl; + break; + } + + case ENCRYPTION_SHA512: + { + if((Encryption_t)value != ENCRYPTION_PLAIN) + { + std::clog << "> WARNING: You cannot change the encryption to SHA512, change it back in config.lua." << std::endl; + return; + } + + Database* db = Database::getInstance(); + DBQuery query; + + query << "SELECT `id`, `password`, `key` FROM `accounts`;"; + if(DBResult* result = db->storeQuery(query.str())) + { + do + { + query << "UPDATE `accounts` SET `password` = " << db->escapeString(transformToSHA512(result->getDataString("password"), false)) << ", `key` = " << db->escapeString(transformToSHA512(result->getDataString("key"), false)) << " WHERE `id` = " << result->getDataInt("id") << ";"; + db->query(query.str()); + } + while(result->next()); + result->free(); + } + + registerDatabaseConfig("encryption", (int32_t)newValue); + std::clog << "> Encryption set to SHA512." << std::endl; break; } default: { - std::cout << "> WARNING: You cannot switch from hashed passwords to plain text, change back the passwordType in config.lua to the passwordType you were previously using." << std::endl; + std::clog << "> WARNING: You cannot switch from hashed passwords to plain text, change back the passwordType in config.lua to the passwordType you were previously using." << std::endl; break; } } @@ -1190,7 +1589,7 @@ void DatabaseManager::checkEncryption() Database* db = Database::getInstance(); DBQuery query; query << "UPDATE `accounts` SET `password` = " << db->escapeString(transformToMD5("1", false)) << " WHERE `id` = 1 AND `password` = '1';"; - db->executeQuery(query.str()); + db->query(query.str()); break; } @@ -1199,7 +1598,25 @@ void DatabaseManager::checkEncryption() Database* db = Database::getInstance(); DBQuery query; query << "UPDATE `accounts` SET `password` = " << db->escapeString(transformToSHA1("1", false)) << " WHERE `id` = 1 AND `password` = '1';"; - db->executeQuery(query.str()); + db->query(query.str()); + break; + } + + case ENCRYPTION_SHA256: + { + Database* db = Database::getInstance(); + DBQuery query; + query << "UPDATE `accounts` SET `password` = " << db->escapeString(transformToSHA256("1", false)) << " WHERE `id` = 1 AND `password` = '1';"; + db->query(query.str()); + break; + } + + case ENCRYPTION_SHA512: + { + Database* db = Database::getInstance(); + DBQuery query; + query << "UPDATE `accounts` SET `password` = " << db->escapeString(transformToSHA512("1", false)) << " WHERE `id` = 1 AND `password` = '1';"; + db->query(query.str()); break; } @@ -1236,12 +1653,12 @@ void DatabaseManager::checkTriggers() }; DBQuery query; - for(uint32_t i = 0; i < sizeof(triggerName) / sizeof(std::string); i++) + for(uint32_t i = 0; i < sizeof(triggerName) / sizeof(std::string); ++i) { if(!triggerExists(triggerName[i])) { - std::cout << "> Trigger: " << triggerName[i] << " does not exist, creating it..." << std::endl; - db->executeQuery(triggerStatement[i]); + std::clog << "> Trigger: " << triggerName[i] << " does not exist, creating it..." << std::endl; + db->query(triggerStatement[i]); } } @@ -1401,12 +1818,12 @@ END;", }; DBQuery query; - for(uint32_t i = 0; i < sizeof(triggerName) / sizeof(std::string); i++) + for(uint32_t i = 0; i < sizeof(triggerName) / sizeof(std::string); ++i) { if(!triggerExists(triggerName[i])) { - std::cout << "> Trigger: " << triggerName[i] << " does not exist, creating it..." << std::endl; - db->executeQuery(triggerStatement[i]); + std::clog << "> Trigger: " << triggerName[i] << " does not exist, creating it..." << std::endl; + db->query(triggerStatement[i]); } } diff --git a/databasemanager.h b/databasemanager.h index aebe309..0abea7f 100644 --- a/databasemanager.h +++ b/databasemanager.h @@ -31,16 +31,21 @@ class DatabaseManager return &instance; } - bool optimizeTables(); - bool tableExists(std::string table); bool triggerExists(std::string trigger); int32_t getDatabaseVersion(); bool isDatabaseSetup(); + + bool optimizeTables(); uint32_t updateDatabase(); + bool getDatabaseConfig(std::string config, int32_t &value); void registerDatabaseConfig(std::string config, int32_t value); + + bool getDatabaseConfig(std::string config, std::string &value); + void registerDatabaseConfig(std::string config, std::string value); + void checkEncryption(); void checkTriggers(); }; diff --git a/databasemysql.cpp b/databasemysql.cpp index 1e2dff5..9bd24de 100644 --- a/databasemysql.cpp +++ b/databasemysql.cpp @@ -15,77 +15,100 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" -#include "scheduler.h" +#ifdef __USE_MYSQL__ +#include #include "database.h" #include "databasemysql.h" -#ifdef __MYSQL_ALT_INCLUDE__ -#include "errmsg.h" +#include "scheduler.h" +#include "configmanager.h" +#include "tools.h" + +#ifdef _MSC_VER +#include #else #include #endif -#include -#include "configmanager.h" extern ConfigManager g_config; -DatabaseMySQL::DatabaseMySQL() +DatabaseMySQL::DatabaseMySQL() : + m_handle(new MYSQL), m_attempts(0), m_timeoutTask(0) { - m_connected = false; - if(!mysql_init(&m_handle)) - { - std::cout << std::endl << "Failed to initialize MySQL connection handler." << std::endl; + assert(connect(false)); + int32_t timeout = g_config.getNumber(ConfigManager::SQL_KEEPALIVE) * 1000; + if(timeout) + m_timeoutTask = Scheduler::getInstance().addEvent(createSchedulerTask(timeout, + boost::bind(&DatabaseMySQL::keepAlive, this))); + + if(asLowerCaseString(g_config.getString(ConfigManager::HOUSE_STORAGE)) == "relational") return; - } - uint32_t readTimeout = g_config.getNumber(ConfigManager::MYSQL_READ_TIMEOUT); - if(readTimeout) - mysql_options(&m_handle, MYSQL_OPT_READ_TIMEOUT, (const char*)&readTimeout); + //we cannot lock mutex here :) + DBResult* result = storeQuery("SHOW variables LIKE 'max_allowed_packet';"); + assert(result); + if(result->getDataLong("Value") < 16776192) + std::clog << std::endl << "> WARNING: max_allowed_packet might be set too low for binary map storage." << std::endl + << "Use the following query to raise max_allow_packet: SET GLOBAL max_allowed_packet = 16776192;" << std::endl; - uint32_t writeTimeout = g_config.getNumber(ConfigManager::MYSQL_WRITE_TIMEOUT); - if(writeTimeout) - mysql_options(&m_handle, MYSQL_OPT_WRITE_TIMEOUT, (const char*)&writeTimeout); + result->free(); +} - connect(); - if(mysql_get_client_version() <= 50019) - { - //MySQL servers <= 5.0.19 has a bug where MYSQL_OPT_RECONNECT is (incorrectly) reset by mysql_real_connect calls - //See http://dev.mysql.com/doc/refman/5.0/en/mysql-options.html for more information. - std::cout << std::endl << "> WARNING: Outdated MySQL server detected, consider upgrading to a newer version." << std::endl; - } +DatabaseMySQL::~DatabaseMySQL() +{ + mysql_close(m_handle); + delete m_handle; + if(m_timeoutTask != 0) + Scheduler::getInstance().stopEvent(m_timeoutTask); +} - if(g_config.getBool(ConfigManager::HOUSE_STORAGE)) +bool DatabaseMySQL::connect(bool _reconnect) +{ + m_connected = false; + if(_reconnect) { - //we cannot lock mutex here :) - if(DBResult* result = storeQuery("SHOW variables LIKE 'max_allowed_packet';")) - { - if(result->getDataLong("Value") < 16776192) - { - std::cout << std::endl << "> WARNING: max_allowed_packet might be set too low for binary map storage." << std::endl; - std::cout << "Use the following query to raise max_allow_packet: SET GLOBAL max_allowed_packet = 16776192;" << std::endl; - } + uint32_t attempts = g_config.getNumber(ConfigManager::MYSQL_RECONNECTION_ATTEMPTS); + if(attempts != 0 && m_attempts > attempts) + return false; - result->free(); + std::clog << "> WARNING: MYSQL Lost connection, attempting to reconnect..." << std::endl; + if(attempts != 0 && ++m_attempts > attempts) + { + std::clog << std::endl << "Failed connection to database - maximum reconnect attempts passed." << std::endl; + return false; } } - int32_t keepAlive = g_config.getNumber(ConfigManager::SQL_KEEPALIVE); - if(keepAlive) - Scheduler::getInstance().addEvent(createSchedulerTask((keepAlive * 1000), boost::bind(&DatabaseMySQL::keepAlive, this))); -} + if(!mysql_init(m_handle)) + { + std::clog << std::endl << "Failed to initialize MySQL connection handler." << std::endl; + return false; + } -bool DatabaseMySQL::getParam(DBParam_t param) -{ - switch(param) + int32_t timeout = g_config.getNumber(ConfigManager::MYSQL_READ_TIMEOUT); + if(timeout) + mysql_options(m_handle, MYSQL_OPT_READ_TIMEOUT, (const char*)&timeout); + + timeout = g_config.getNumber(ConfigManager::MYSQL_WRITE_TIMEOUT); + if(timeout) + mysql_options(m_handle, MYSQL_OPT_WRITE_TIMEOUT, (const char*)&timeout); + + if(!mysql_real_connect(m_handle, + g_config.getString(ConfigManager::SQL_HOST).c_str(), + g_config.getString(ConfigManager::SQL_USER).c_str(), + g_config.getString(ConfigManager::SQL_PASS).c_str(), + g_config.getString(ConfigManager::SQL_DB).c_str(), + g_config.getNumber(ConfigManager::SQL_PORT), + NULL, 0)) { - case DBPARAM_MULTIINSERT: - return true; - default: - break; + std::clog << std::endl << "Failed connecting to database - MYSQL ERROR: " << mysql_error(m_handle) << " (" << mysql_errno(m_handle) << ")" << std::endl; + return false; } - return false; + m_connected = true; + m_attempts = 0; + return true; } bool DatabaseMySQL::rollback() @@ -93,9 +116,9 @@ bool DatabaseMySQL::rollback() if(!m_connected) return false; - if(mysql_rollback(&m_handle)) + if(mysql_rollback(m_handle)) { - std::cout << "mysql_rollback() - MYSQL ERROR: " << mysql_error(&m_handle) << " (" << mysql_errno(&m_handle) << ")" << std::endl; + std::clog << "mysql_rollback() - MYSQL ERROR: " << mysql_error(m_handle) << " (" << mysql_errno(m_handle) << ")" << std::endl; return false; } @@ -107,78 +130,80 @@ bool DatabaseMySQL::commit() if(!m_connected) return false; - if(mysql_commit(&m_handle)) + if(mysql_commit(m_handle)) { - std::cout << "mysql_commit() - MYSQL ERROR: " << mysql_error(&m_handle) << " (" << mysql_errno(&m_handle) << ")" << std::endl; + std::clog << "mysql_commit() - MYSQL ERROR: " << mysql_error(m_handle) << " (" << mysql_errno(m_handle) << ")" << std::endl; return false; } return true; } -bool DatabaseMySQL::executeQuery(const std::string &query) +bool DatabaseMySQL::query(std::string query) { - if(!m_connected) + if(!m_connected && !connect(true)) return false; - bool state = true; - if(mysql_real_query(&m_handle, query.c_str(), query.length())) + bool result = true; +#ifdef __SQL_QUERY_DEBUG__ + std::clog << "MYSQL DEBUG, query: " << query.c_str() << std::endl; +#endif + if(mysql_real_query(m_handle, query.c_str(), query.length())) { - int32_t error = mysql_errno(&m_handle); - if((error == CR_UNKNOWN_ERROR || error == CR_SERVER_LOST || error == CR_SERVER_GONE_ERROR) && reconnect()) - return executeQuery(query); + int32_t error = mysql_errno(m_handle); + if(error == CR_SERVER_LOST || error == CR_SERVER_GONE_ERROR) + m_connected = false; - state = false; - std::cout << "mysql_real_query(): " << query << " - MYSQL ERROR: " << mysql_error(&m_handle) << " (" << error << ")" << std::endl; + std::clog << "mysql_real_query(): " << query << " - MYSQL ERROR: " << mysql_error(m_handle) << " (" << error << ")" << std::endl; + result = false; } - if(MYSQL_RES* tmp = mysql_store_result(&m_handle)) - { + if(MYSQL_RES* tmp = mysql_store_result(m_handle)) mysql_free_result(tmp); - tmp = NULL; - } - return state; + return result; } -DBResult* DatabaseMySQL::storeQuery(const std::string &query) +DBResult* DatabaseMySQL::storeQuery(std::string query) { - if(!m_connected) + if(!m_connected && !connect(true)) return NULL; int32_t error = 0; - if(mysql_real_query(&m_handle, query.c_str(), query.length())) +#ifdef __SQL_QUERY_DEBUG__ + std::clog << "MYSQL DEBUG, storeQuery: " << query.c_str() << std::endl; +#endif + if(mysql_real_query(m_handle, query.c_str(), query.length())) { - error = mysql_errno(&m_handle); - if((error == CR_UNKNOWN_ERROR || error == CR_SERVER_LOST || error == CR_SERVER_GONE_ERROR) && reconnect()) - return storeQuery(query); + error = mysql_errno(m_handle); + if(error == CR_SERVER_LOST || error == CR_SERVER_GONE_ERROR) + m_connected = false; - std::cout << "mysql_real_query(): " << query << " - MYSQL ERROR: " << mysql_error(&m_handle) << " (" << error << ")" << std::endl; + std::clog << "mysql_real_query(): " << query << " - MYSQL ERROR: " << mysql_error(m_handle) << " (" << error << ")" << std::endl; return NULL; - } - if(MYSQL_RES* tmp = mysql_store_result(&m_handle)) + if(MYSQL_RES* _result = mysql_store_result(m_handle)) { - DBResult* res = (DBResult*)new MySQLResult(tmp); - return verifyResult(res); + DBResult* result = (DBResult*)new MySQLResult(_result); + return verifyResult(result); } - error = mysql_errno(&m_handle); - if((error == CR_UNKNOWN_ERROR || error == CR_SERVER_LOST || error == CR_SERVER_GONE_ERROR) && reconnect()) - return storeQuery(query); + error = mysql_errno(m_handle); + if(error == CR_UNKNOWN_ERROR || error == CR_SERVER_LOST) + m_connected = false; - std::cout << "mysql_store_result(): " << query << " - MYSQL ERROR: " << mysql_error(&m_handle) << " (" << error << ")" << std::endl; + std::clog << "mysql_store_result(): " << query << " - MYSQL ERROR: " << mysql_error(m_handle) << " (" << error << ")" << std::endl; return NULL; } std::string DatabaseMySQL::escapeBlob(const char* s, uint32_t length) { - if(!s) + if(!m_connected || *s == '\0') return "''"; - char* output = new char[length * 2 + 1]; - mysql_real_escape_string(&m_handle, output, s, length); + char* output = new char[(length << 1) + 1]; + mysql_real_escape_string(m_handle, output, s, length); std::string res = "'"; res += output; @@ -190,175 +215,96 @@ std::string DatabaseMySQL::escapeBlob(const char* s, uint32_t length) void DatabaseMySQL::keepAlive() { - int32_t delay = g_config.getNumber(ConfigManager::SQL_KEEPALIVE); - if(delay) - { - if(time(NULL) > (m_use + delay) && mysql_ping(&m_handle)) - reconnect(); + int32_t timeout = g_config.getNumber(ConfigManager::SQL_KEEPALIVE) * 1000; + if(timeout != 0 && OTSYS_TIME() > m_use + timeout && mysql_ping(m_handle)) + connect(true); - Scheduler::getInstance().addEvent(createSchedulerTask((delay * 1000), boost::bind(&DatabaseMySQL::keepAlive, this))); - } + Scheduler::getInstance().addEvent(createSchedulerTask(timeout, + boost::bind(&DatabaseMySQL::keepAlive, this))); } -bool DatabaseMySQL::connect() -{ - if(m_connected) - { - m_connected = false; - mysql_close(&m_handle); - } - - if(!mysql_real_connect(&m_handle, g_config.getString(ConfigManager::SQL_HOST).c_str(), g_config.getString(ConfigManager::SQL_USER).c_str(), - g_config.getString(ConfigManager::SQL_PASS).c_str(), g_config.getString(ConfigManager::SQL_DB).c_str(), g_config.getNumber( - ConfigManager::SQL_PORT), NULL, 0)) - { - std::cout << "Failed connecting to database - MYSQL ERROR: " << mysql_error(&m_handle) << " (" << mysql_errno(&m_handle) << ")" << std::endl; - return false; - } - - m_connected = true; - m_attempts = 0; - return true; -} - -bool DatabaseMySQL::reconnect() -{ - while(m_attempts <= MAX_RECONNECT_ATTEMPTS) - { - m_attempts++; - if(connect()) - return true; - } - - std::cout << "Unable to reconnect - too many attempts, limit exceeded!" << std::endl; - return false; -} - -int32_t MySQLResult::getDataInt(const std::string &s) +int32_t MySQLResult::getDataInt(const std::string& s) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end()) - { - if(!m_row[it->second]) - return 0; - - return atoi(m_row[it->second]); - } + return m_row[it->second] ? atoi(m_row[it->second]) : 0; - if(refetch()) - return getDataInt(s); - - std::cout << "Error during getDataInt(" << s << ")." << std::endl; + std::clog << "Error during getDataInt(" << s << ")." << std::endl; return 0; // Failed } -int64_t MySQLResult::getDataLong(const std::string &s) +int64_t MySQLResult::getDataLong(const std::string& s) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end()) - { - if(!m_row[it->second]) - return 0; - - return atoll(m_row[it->second]); - } - - if(refetch()) - return getDataLong(s); + return m_row[it->second] ? atoll(m_row[it->second]) : 0; - std::cout << "Error during getDataLong(" << s << ")." << std::endl; + std::clog << "Error during getDataLong(" << s << ")." << std::endl; return 0; // Failed } -std::string MySQLResult::getDataString(const std::string &s) +std::string MySQLResult::getDataString(const std::string& s) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end()) - { - if(!m_row[it->second]) - return ""; + return m_row[it->second] ? std::string(m_row[it->second]) : std::string(); - return std::string(m_row[it->second]); - } - - if(refetch()) - return getDataString(s); - - std::cout << "Error during getDataString(" << s << ")." << std::endl; - return ""; // Failed + std::clog << "Error during getDataString(" << s << ")." << std::endl; + return std::string(); // Failed } -const char* MySQLResult::getDataStream(const std::string &s, uint64_t &size) +const char* MySQLResult::getDataStream(const std::string& s, uint64_t& size) { + size = 0; listNames_t::iterator it = m_listNames.find(s); - if(it != m_listNames.end()) + if(it == m_listNames.end()) { - if(!m_row[it->second]) - { - size = 0; - return NULL; - } - - size = mysql_fetch_lengths(m_handle)[it->second]; - return m_row[it->second]; + std::clog << "Error during getDataStream(" << s << ")." << std::endl; + return NULL; // Failed } - if(refetch()) - return getDataStream(s, size); + if(!m_row[it->second]) + return NULL; - std::cout << "Error during getDataStream(" << s << ")." << std::endl; - size = 0; - return NULL; // Failed + size = mysql_fetch_lengths(m_handle)[it->second]; + return m_row[it->second]; } void MySQLResult::free() { - if(m_handle) + if(!m_handle) { - mysql_free_result(m_handle); - m_handle = NULL; - - m_listNames.clear(); - delete this; + std::clog << "[Critical - MySQLResult::free] Trying to free already freed result!!!" << std::endl; + return; } - else - std::cout << "[Warning - MySQLResult::free] Trying to free already freed result." << std::endl; + + mysql_free_result(m_handle); + m_handle = NULL; + delete this; } bool MySQLResult::next() { m_row = mysql_fetch_row(m_handle); - return m_row; + return (m_row != NULL); } -void MySQLResult::fetch() +MySQLResult::~MySQLResult() { - m_listNames.clear(); - int32_t i = 0; - - MYSQL_FIELD* field; - while((field = mysql_fetch_field(m_handle))) - m_listNames[field->name] = i++; + if(m_handle) + mysql_free_result(m_handle); } -bool MySQLResult::refetch() +MySQLResult::MySQLResult(MYSQL_RES* result) { - if(m_attempts >= MAX_REFETCH_ATTEMPTS) - return false; + if(!result) + return; - fetch(); - ++m_attempts; - return true; -} + m_handle = result; + int32_t i = 0; -MySQLResult::MySQLResult(MYSQL_RES* result) -{ - m_attempts = 0; - if(result) - { - m_handle = result; - fetch(); - } - else - delete this; + MYSQL_FIELD* field = NULL; + while((field = mysql_fetch_field(m_handle))) + m_listNames[field->name] = i++; } +#endif diff --git a/databasemysql.h b/databasemysql.h index 3c9d899..738aecb 100644 --- a/databasemysql.h +++ b/databasemysql.h @@ -18,80 +18,74 @@ #ifndef __DATABASEMYSQL__ #define __DATABASEMYSQL__ +#ifdef __USE_MYSQL__ #ifndef __DATABASE__ #error "database.h should be included first." #endif -#if defined WINDOWS -#include -#endif -#ifdef __MYSQL_ALT_INCLUDE__ +#ifdef _MSC_VER #include #else #include #endif + +#if defined WINDOWS +#include +#endif #include #include -#define MAX_RECONNECT_ATTEMPTS 10 -#define MAX_REFETCH_ATTEMPTS 3 - class DatabaseMySQL : public _Database { public: DatabaseMySQL(); - DATABASE_VIRTUAL ~DatabaseMySQL() {mysql_close(&m_handle);} + DATABASE_VIRTUAL ~DatabaseMySQL(); - DATABASE_VIRTUAL bool getParam(DBParam_t param); + DATABASE_VIRTUAL bool connect(bool _reconnect); + DATABASE_VIRTUAL bool multiLine() const {return true;} - DATABASE_VIRTUAL bool beginTransaction() {return executeQuery("BEGIN");} + DATABASE_VIRTUAL bool beginTransaction() {return query("BEGIN");} DATABASE_VIRTUAL bool rollback(); DATABASE_VIRTUAL bool commit(); - DATABASE_VIRTUAL bool executeQuery(const std::string &query); - DATABASE_VIRTUAL DBResult* storeQuery(const std::string &query); + DATABASE_VIRTUAL bool query(std::string query); + DATABASE_VIRTUAL DBResult* storeQuery(std::string query); - DATABASE_VIRTUAL std::string escapeString(const std::string &s) {return escapeBlob(s.c_str(), s.length());} + DATABASE_VIRTUAL std::string escapeString(std::string s) {return escapeBlob(s.c_str(), s.length());} DATABASE_VIRTUAL std::string escapeBlob(const char* s, uint32_t length); - DATABASE_VIRTUAL uint64_t getLastInsertId() {return (uint64_t)mysql_insert_id(&m_handle);} + DATABASE_VIRTUAL uint64_t getLastInsertId() {return (uint64_t)mysql_insert_id(m_handle);} DATABASE_VIRTUAL DatabaseEngine_t getDatabaseEngine() {return DATABASE_ENGINE_MYSQL;} protected: DATABASE_VIRTUAL void keepAlive(); - bool connect(); - bool reconnect(); - - MYSQL m_handle; - uint32_t m_attempts; + MYSQL* m_handle; + uint16_t m_attempts; + uint32_t m_timeoutTask; }; class MySQLResult : public _DBResult { friend class DatabaseMySQL; - public: - DATABASE_VIRTUAL int32_t getDataInt(const std::string &s); - DATABASE_VIRTUAL int64_t getDataLong(const std::string &s); - DATABASE_VIRTUAL std::string getDataString(const std::string &s); - DATABASE_VIRTUAL const char* getDataStream(const std::string &s, uint64_t &size); + DATABASE_VIRTUAL int32_t getDataInt(const std::string& s); + DATABASE_VIRTUAL int64_t getDataLong(const std::string& s); + DATABASE_VIRTUAL std::string getDataString(const std::string& s); + DATABASE_VIRTUAL const char* getDataStream(const std::string& s, uint64_t& size); DATABASE_VIRTUAL void free(); DATABASE_VIRTUAL bool next(); protected: MySQLResult(MYSQL_RES* result); - DATABASE_VIRTUAL ~MySQLResult() {} - - void fetch(); - bool refetch(); + DATABASE_VIRTUAL ~MySQLResult(); typedef std::map listNames_t; listNames_t m_listNames; MYSQL_RES* m_handle; MYSQL_ROW m_row; - uint32_t m_attempts; }; #endif +#endif diff --git a/databasemysqlpp.cpp b/databasemysqlpp.cpp new file mode 100644 index 0000000..eac91cd --- /dev/null +++ b/databasemysqlpp.cpp @@ -0,0 +1,261 @@ +//////////////////////////////////////////////////////////////////////// +// OpenTibia - an opensource roleplaying game +//////////////////////////////////////////////////////////////////////// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +//////////////////////////////////////////////////////////////////////// +#include "otpch.h" +#ifdef __USE_MYSQLPP__ +#include + +#include "database.h" +#include "databasemysqlpp.h" + +#include "scheduler.h" +#include "configmanager.h" +#include "tools.h" + +extern ConfigManager g_config; + +DatabaseMySQLpp::DatabaseMySQLpp() : + m_connection(NULL) +{ + m_driver = sql::mysql::get_mysql_driver_instance(); + assert(m_driver); + m_driver->threadInit(); + + connect(false); + if(asLowerCaseString(g_config.getString(ConfigManager::HOUSE_STORAGE)) == "relational") + return; + + //we cannot lock mutex here :) + DBResult* result = storeQuery("SHOW variables LIKE 'max_allowed_packet';"); + assert(result); + + if(result->getDataLong("Value") < 16776192) + std::clog << std::endl << "> WARNING: max_allowed_packet might be set too low for binary map storage." << std::endl + << "Use the following query to raise max_allow_packet: SET GLOBAL max_allowed_packet = 16776192;" << std::endl; + + result->free(); +} + +DatabaseMySQLpp::~DatabaseMySQLpp() +{ + if(m_connection) + delete m_connection; + + if(m_driver) + { + m_driver->threadEnd(); + delete m_driver; + } +} + +bool DatabaseMySQLpp::connect(bool reconnect) +{ + m_connected = false; + if(reconnect && m_connection) + { + m_connection->close(); + delete m_connection; + } + + try + { + m_connection = dynamic_cast(m_driver->connect( + "tcp://" + + + g_config.getString(ConfigManager::SQL_HOST) + + + ":" + + + asString( + g_config.getNumber(ConfigManager::SQL_PORT) + ), + g_config.getString(ConfigManager::SQL_USER), + g_config.getString(ConfigManager::SQL_PASS))); + } + catch(sql::SQLException& e) + { + std::clog << "[Exception - DatabaseMySQLpp::connect] " << e.what() << std::endl; + return false; + } + + m_connection->setSchema(g_config.getString(ConfigManager::SQL_DB)); + m_connected = true; + return true; +} + +bool DatabaseMySQLpp::beginTransaction() +{ + if(!m_connected) + return false; + + return query("BEGIN"); +} + +bool DatabaseMySQLpp::rollback() +{ + if(!m_connected) + return false; + + m_connection->rollback(); + return true; +} + +bool DatabaseMySQLpp::commit() +{ + if(!m_connected) + return false; + + m_connection->commit(); + return true; +} + +bool DatabaseMySQLpp::query(std::string query) +{ + if(!m_connected && !connect(true)) + return false; + + try + { + sql::Statement* statement = m_connection->createStatement(); + statement->execute(query); + + delete statement; + return true; + } + catch(sql::SQLException& e) + { + std::clog << "[Exception - DatabaseMySQLpp::query] " << e.what() << std::endl; + } + + return result; +} + +DBResult* DatabaseMySQLpp::storeQuery(std::string query) +{ + if(!m_connected && !connect(true)) + return false; + + try + { + sql::Statement* statement = m_connection->createStatement(); + if(sql::ResultSet* result = statement->executeQuery(query)) + { + delete statement; + DBResult* _result = (DBResult*)new MySQLppResult(result); + return verifyResult(_result); + } + + delete statement; + } + catch(sql::SQLException& e) + { + std::clog << "[Exception - DatabaseMySQLpp::storeQuery] " << e.what() << std::endl; + } + + return NULL; +} + +std::string DatabaseMySQLpp::escapeBlob(const char* s, uint32_t) +{ + if(!m_connected || *s == '\0') + return "''"; + + return "'" + m_connection->escapeString(s) + "'"; +} + +uint64_t DatabaseMySQLpp::getLastInsertId() +{ + DBQuery query; + query << "SELECT LAST_INSERT_ID() AS `t`;"; + DBResult* result = storeQuery(query.str()); + if(!result) + return 0; + + uint64_t t = result->getDataLong("t"); + result->free(); + return t; +} + +int32_t MySQLppResult::getDataInt(const std::string& s) +{ + if(!m_result) + return 0; + + return m_result->getInt(s); +} + +int64_t MySQLppResult::getDataLong(const std::string& s) +{ + if(!m_result) + return 0; + + return m_result->getInt64(s); +} + +std::string MySQLppResult::getDataString(const std::string& s) +{ + if(!m_result) + return std::string(); + + return m_result->getString(s); +} + +const char* MySQLppResult::getDataStream(const std::string& s, uint64_t& size) +{ + if(!m_result) + { + size = 0; + return ""; + } + + std::istream* result = m_result->getBlob(s); + std::string tmp; + for(char c = result->get(); c; c = result->get()) + tmp += c; + + delete result; + size = tmp.size(); + return tmp.c_str(); +} + +void MySQLppResult::free() +{ + if(!m_result) + { + std::clog << "[Critical - MySQLppResult::free] Trying to free already freed result!!!" << std::endl; + return; + } + + delete this; +} + +bool MySQLppResult::next() +{ + return m_result->next(); +} + +MySQLppResult::~MySQLppResult() +{ + if(!m_result) + return; + + delete m_result; +} + +MySQLppResult::MySQLppResult(sql::ResultSet* result) : + m_result(result) +{ } +#endif diff --git a/databaseodbc.h b/databasemysqlpp.h similarity index 57% rename from databaseodbc.h rename to databasemysqlpp.h index 21eacc8..0f92b5e 100644 --- a/databaseodbc.h +++ b/databasemysqlpp.h @@ -15,64 +15,53 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// -// -// C++ Interface: databaseodbc -// -// Description: Frontend for ODBC connections -// -// -// Author: Bruno R Ferreira , (C) 2007 -// -// -#ifndef __DATABASE_ODBC__ -#define __DATABASE_ODBC__ +#ifndef __DATABASEMYSQLPP__ +#define __DATABASEMYSQLPP__ +#ifdef __USE_MYSQLPP__ #ifndef __DATABASE__ #error "database.h should be included first." #endif -#ifdef WINDOWS -#include -#else -#include -#endif -#include -#include +#include +#include +#include +#include +#include + +#include +#include -/** - @author Bruno R Ferreira -*/ -class DatabaseODBC : public _Database +class DatabaseMySQLpp : public _Database { public: - DatabaseODBC(); - DATABASE_VIRTUAL ~DatabaseODBC(); + DatabaseMySQLpp(); + DATABASE_VIRTUAL ~DatabaseMySQLpp(); - DATABASE_VIRTUAL bool getParam(DBParam_t param); + DATABASE_VIRTUAL bool connect(bool _reconnect); + DATABASE_VIRTUAL bool multiLine() const {return true;} DATABASE_VIRTUAL bool beginTransaction(); DATABASE_VIRTUAL bool rollback(); DATABASE_VIRTUAL bool commit(); - DATABASE_VIRTUAL bool executeQuery(const std::string& query); - DATABASE_VIRTUAL DBResult* storeQuery(const std::string& query); + DATABASE_VIRTUAL bool query(std::string query); + DATABASE_VIRTUAL DBResult* storeQuery(std::string query); - DATABASE_VIRTUAL std::string escapeString(const std::string& s) {return escapeBlob(s.c_str(), s.length());} - DATABASE_VIRTUAL std::string escapeBlob(const char *s, uint32_t length); + DATABASE_VIRTUAL std::string escapeString(std::string s) {return escapeBlob(s.c_str(), s.length());} + DATABASE_VIRTUAL std::string escapeBlob(const char* s, uint32_t length); - DATABASE_VIRTUAL DatabaseEngine_t getDatabaseEngine() {return DATABASE_ENGINE_ODBC;} + DATABASE_VIRTUAL uint64_t getLastInsertId(); + DATABASE_VIRTUAL DatabaseEngine_t getDatabaseEngine() {return DATABASE_ENGINE_MYSQL;} protected: - std::string _parse(const std::string& s); - - SQLHDBC m_handle; - SQLHENV m_env; + sql::mysql::MySQL_Driver* m_driver; + sql::mysql::MySQL_Connection* m_connection; }; -class ODBCResult : public _DBResult +class MySQLppResult : public _DBResult { - friend class DatabaseODBC; - + friend class DatabaseMySQLpp; public: DATABASE_VIRTUAL int32_t getDataInt(const std::string& s); DATABASE_VIRTUAL int64_t getDataLong(const std::string& s); @@ -83,12 +72,10 @@ class ODBCResult : public _DBResult DATABASE_VIRTUAL bool next(); protected: - ODBCResult(SQLHSTMT stmt); - DATABASE_VIRTUAL ~ODBCResult() {} - - typedef std::map listNames_t; - listNames_t m_listNames; + MySQLppResult(sql::ResultSet* result); + DATABASE_VIRTUAL ~MySQLppResult(); - SQLHSTMT m_handle; + sql::ResultSet* m_result; }; #endif +#endif diff --git a/databaseodbc.cpp b/databaseodbc.cpp deleted file mode 100644 index 7590997..0000000 --- a/databaseodbc.cpp +++ /dev/null @@ -1,367 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// -// C++ Implementation: databaseodbc -// Description: Frontend for ODBC connections -// -// Author: Bruno R Ferreira , (C) 2007 -//////////////////////////////////////////////////////////////////////// -#include "otpch.h" -#include - -#include "database.h" -#include "databaseodbc.h" - -#include "configmanager.h" -extern ConfigManager g_config; - -#define RETURN_SUCCESS(ret) (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO) - -DatabaseODBC::DatabaseODBC() -{ - m_connected = false; - - char* dns = new char[SQL_MAX_DSN_LENGTH]; - char* user = new char[32]; - char* pass = new char[32]; - - strcpy((char*)dns, g_config.getString(ConfigManager::SQL_DB).c_str()); - strcpy((char*)user, g_config.getString(ConfigManager::SQL_USER).c_str()); - strcpy((char*)pass, g_config.getString(ConfigManager::SQL_PASS).c_str()); - - SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_env); - if(!RETURN_SUCCESS(ret)) - { - std::cout << "Failed to allocate ODBC SQLHENV enviroment handle." << std::endl; - m_env = NULL; - return; - } - - ret = SQLSetEnvAttr(m_env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER*)SQL_OV_ODBC3, 0); - if(!RETURN_SUCCESS(ret)) - { - std::cout << "SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION): Failed to switch to ODBC 3 version." << std::endl; - SQLFreeHandle(SQL_HANDLE_ENV, m_env); - m_env = NULL; - } - - if(m_env == NULL) - { - std::cout << "ODBC SQLHENV enviroment not initialized." << std::endl; - return; - } - - ret = SQLAllocHandle(SQL_HANDLE_DBC, m_env, &m_handle); - if(!RETURN_SUCCESS(ret)) - { - std::cout << "Failed to allocate ODBC SQLHDBC connection handle." << std::endl; - m_handle = NULL; - return; - } - - ret = SQLSetConnectAttr(m_handle, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER*)5, 0); - if(!RETURN_SUCCESS(ret)) - { - std::cout << "SQLSetConnectAttr(SQL_ATTR_CONNECTION_TIMEOUT): Failed to set connection timeout." << std::endl; - SQLFreeHandle(SQL_HANDLE_DBC, m_handle); - m_handle = NULL; - return; - } - - ret = SQLConnect(m_handle, (SQLCHAR*)dns, SQL_NTS, (SQLCHAR*)user, SQL_NTS, (SQLCHAR*)pass, SQL_NTS); - if(!RETURN_SUCCESS(ret)) - { - std::cout << "Failed to connect to ODBC via DSN: " << dns << " (user " << user << ")" << std::endl; - SQLFreeHandle(SQL_HANDLE_DBC, m_handle); - m_handle = NULL; - return; - } - - m_connected = true; -} - -DatabaseODBC::~DatabaseODBC() -{ - if(m_connected) - { - SQLDisconnect(m_handle); - SQLFreeHandle(SQL_HANDLE_DBC, m_handle); - m_handle = NULL; - m_connected = false; - } - - SQLFreeHandle(SQL_HANDLE_ENV, m_env); -} - -bool DatabaseODBC::getParam(DBParam_t param) -{ - switch(param) - { - case DBPARAM_MULTIINSERT: - default: - break; - } - - return false; -} - -bool DatabaseODBC::beginTransaction() -{ - return true; - // return executeQuery("BEGIN"); -} - -bool DatabaseODBC::rollback() -{ - return true; - // SQL_RETURN ret = SQLTransact(m_env, m_handle, SQL_ROLLBACK); - // return RETURN_SUCCESS(ret); -} - -bool DatabaseODBC::commit() -{ - return true; - // SQL_RETURN ret = SQLTransact(m_env, m_handle, SQL_COMMIT); - // return RETURN_SUCCESS(ret); -} - -bool DatabaseODBC::executeQuery(const std::string& query) -{ - if(!m_connected) - return false; - - #ifdef __SQL_QUERY_DEBUG__ - std::cout << "ODBC QUERY: " << query << std::endl; - #endif - - SQLHSTMT stmt; - SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_STMT, m_handle, &stmt); - if(!RETURN_SUCCESS(ret)) - { - std::cout << "Failed to allocate ODBC SQLHSTMT statement." << std::endl; - return false; - } - - std::string buf = _parse(query); - ret = SQLExecDirect(stmt, (SQLCHAR*)buf.c_str(), buf.length()); - if(!RETURN_SUCCESS(ret)) - { - std::cout << "SQLExecDirect(): " << query << ": ODBC ERROR." << std::endl; - return false; - } - - return true; -} - -DBResult* DatabaseODBC::storeQuery(const std::string& query) -{ - if(!m_connected) - return NULL; - - #ifdef __SQL_QUERY_DEBUG__ - std::cout << "ODBC QUERY: " << query << std::endl; - #endif - - SQLHSTMT stmt; - SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_STMT, m_handle, &stmt); - if(!RETURN_SUCCESS(ret)) - { - std::cout << "Failed to allocate ODBC SQLHSTMT statement." << std::endl; - return NULL; - } - - std::string buf = _parse(query); - ret = SQLExecDirect(stmt, (SQLCHAR*)buf.c_str(), buf.length() ); - if(!RETURN_SUCCESS(ret)) - { - std::cout << "SQLExecDirect(): " << query << ": ODBC ERROR." << std::endl; - return NULL; - } - - DBResult* results = (DBResult*)new ODBCResult(stmt); - return verifyResult(results); -} - -std::string DatabaseODBC::escapeBlob(const char *s, uint32_t length) -{ - std::string buf = "'"; - for(uint32_t i = 0; i < length; i++) - { - switch(s[i]) - { - case '\'': - buf += "\'\'"; - break; - - case '\0': - buf += "\\0"; - break; - - case '\\': - buf += "\\\\"; - break; - - case '\r': - buf += "\\r"; - break; - - case '\n': - buf += "\\n"; - break; - - default: - buf += s[i]; - } - } - - buf += "'"; - return buf; -} - -std::string DatabaseODBC::_parse(const std::string& s) -{ - std::string query = ""; - query.reserve(s.size()); - - bool inString = false; - for(uint32_t a = 0; a < s.length(); a++) - { - uint8_t ch = s[a]; - if(ch == '\'') - { - if(inString && s[a + 1] != '\'') - inString = false; - else - inString = true; - } - - if(ch == '`' && !inString) - ch = '"'; - - query += ch; - } - - return query; -} - -int32_t ODBCResult::getDataInt(const std::string& s) -{ - listNames_t::iterator it = m_listNames.find(s); - if(it != m_listNames.end()) - { - int32_t value; - SQLRETURN ret = SQLGetData(m_handle, it->second, SQL_C_SLONG, &value, 0, NULL); - - if(RETURN_SUCCESS(ret)) - return value; - else - std::cout << "Error during getDataInt(" << s << ")." << std::endl; - } - - std::cout << "Error during getDataInt(" << s << ")." << std::endl; - return 0; // Failed -} - -int64_t ODBCResult::getDataLong(const std::string& s) -{ - listNames_t::iterator it = m_listNames.find(s); - if(it != m_listNames.end()) - { - int64_t value; - SQLRETURN ret = SQLGetData(m_handle, it->second, SQL_C_SBIGINT, &value, 0, NULL); - - if(RETURN_SUCCESS(ret)) - return value; - else - std::cout << "Error during getDataLong(" << s << ")." << std::endl; - } - - std::cout << "Error during getDataLong(" << s << ")." << std::endl; - return 0; // Failed -} - -std::string ODBCResult::getDataString(const std::string& s) -{ - listNames_t::iterator it = m_listNames.find(s); - if(it != m_listNames.end()) - { - char* value = new char[1024]; - SQLRETURN ret = SQLGetData(m_handle, it->second, SQL_C_CHAR, value, 1024, NULL); - - if(RETURN_SUCCESS(ret)) - { - std::string buff = std::string(value); - return buff; - } - else - std::cout << "Error during getDataString(" << s << ")." << std::endl; - } - - std::cout << "Error during getDataString(" << s << ")." << std::endl; - return std::string(""); // Failed -} - -const char* ODBCResult::getDataStream(const std::string& s, uint64_t& size) -{ - listNames_t::iterator it = m_listNames.find(s); - if(it != m_listNames.end()) - { - char* value = new char[1024]; - if(RETURN_SUCCESS(SQLGetData(m_handle, it->second, SQL_C_BINARY, value, 1024, (SQLLEN*)&size))) - return value; - } - - std::cout << "Error during getDataStream(" << s << ")." << std::endl; - size = 0; - return 0; // Failed -} - -void ODBCResult::free() -{ - if(m_handle) - { - SQLFreeHandle(SQL_HANDLE_STMT, m_handle); - delete this; - } - else - std::cout << "[Warning - ODBCResult::free] Trying to free already freed result." << std::endl; -} - -bool ODBCResult::next() -{ - SQLRETURN ret = SQLFetch(m_handle); - return RETURN_SUCCESS(ret); -} - -ODBCResult::ODBCResult(SQLHSTMT stmt) -{ - if(!res) - { - delete this; - return; - } - - m_handle = stmt; - int16_t numCols = 0; - - SQLNumResultCols(m_handle, &numCols); - for(int32_t i = 1; i <= numCols; i++) - { - char* name = new char[129]; - SQLDescribeCol(m_handle, i, (SQLCHAR*)name, 129, NULL, NULL, NULL, NULL, NULL); - m_listNames[name] = i; - } -} diff --git a/databasepgsql.cpp b/databasepgsql.cpp index d9c2baf..f191c0c 100644 --- a/databasepgsql.cpp +++ b/databasepgsql.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" +#ifdef __USE_PGSQL__ #include #include "database.h" @@ -23,46 +24,59 @@ #include "configmanager.h" extern ConfigManager g_config; -DatabasePgSQL::DatabasePgSQL() +DatabasePgSQL::DatabasePgSQL() : + m_handle(NULL) { std::stringstream dns; dns << "host='" << g_config.getString(ConfigManager::SQL_HOST) << "' dbname='" << g_config.getString(ConfigManager::SQL_DB) << "' user='" << g_config.getString(ConfigManager::SQL_USER) << "' password='" << g_config.getString(ConfigManager::SQL_PASS) << "' port='" << g_config.getNumber(ConfigManager::SQL_PORT) << "'"; m_handle = PQconnectdb(dns.str().c_str()); - m_connected = PQstatus(m_handle) == CONNECTION_OK; - if(!m_connected) - std::cout << "Failed to estabilish PostgreSQL database connection: " << PQerrorMessage(m_handle) << std::endl; + if(PQstatus(m_handle) != CONNECTION_OK) + { + std::clog << "Failed to estabilish PostgreSQL database connection: " << PQerrorMessage(m_handle) << std::endl; + PQfinish(m_handle); + } + else + m_connected = true; } -bool DatabasePgSQL::getParam(DBParam_t param) +std::string DatabasePgSQL::_parse(const std::string& s) { - switch(param) + std::string query = ""; + query.reserve(s.size()); + + bool inString = false; + for(uint32_t i = 0; i < s.length(); ++i) { - case DBPARAM_MULTIINSERT: - return true; + uint8_t ch = s[i]; + if(ch == '\'') + { + if(inString && s[i + 1] != '\'') + inString = false; + else + inString = true; + } - default: - break; + if(ch == '`' && !inString) + ch = '"'; + + query += ch; } - return false; + return query; } -bool DatabasePgSQL::executeQuery(const std::string& query) +bool DatabasePgSQL::query(std::string query) { if(!m_connected) return false; - #ifdef __SQL_QUERY_DEBUG__ - std::cout << "PGSQL QUERY: " << query << std::endl; - #endif - // executes query PGresult* res = PQexec(m_handle, _parse(query).c_str()); ExecStatusType stat = PQresultStatus(res); if(stat != PGRES_COMMAND_OK && stat != PGRES_TUPLES_OK) { - std::cout << "PQexec(): " << query << ": " << PQresultErrorMessage(res) << std::endl; + std::clog << "PQexec(): " << query << ": " << PQresultErrorMessage(res) << std::endl; PQclear(res); return false; } @@ -72,34 +86,30 @@ bool DatabasePgSQL::executeQuery(const std::string& query) return true; } -DBResult* DatabasePgSQL::storeQuery(const std::string& query) +DBResult* DatabasePgSQL::storeQuery(std::string query) { if(!m_connected) return NULL; - #ifdef __SQL_QUERY_DEBUG__ - std::cout << "PGSQL QUERY: " << query << std::endl; - #endif - // executes query PGresult* res = PQexec(m_handle, _parse(query).c_str()); ExecStatusType stat = PQresultStatus(res); if(stat != PGRES_COMMAND_OK && stat != PGRES_TUPLES_OK) { - std::cout << "PQexec(): " << query << ": " << PQresultErrorMessage(res) << std::endl; + std::clog << "PQexec(): " << query << ": " << PQresultErrorMessage(res) << std::endl; PQclear(res); return false; } // everything went fine - DBResult* results = new PgSQLResult(res); - return verifyResult(results); + DBResult* result = new PgSQLResult(res); + return verifyResult(result); } -std::string DatabasePgSQL::escapeString(const std::string& s) +std::string DatabasePgSQL::escapeString(std::string s) { // remember to quote even empty string! - if(!s.size()) + if(!m_connected || !s.size()) return std::string("''"); // the worst case is 2n + 1 @@ -119,7 +129,7 @@ std::string DatabasePgSQL::escapeString(const std::string& s) std::string DatabasePgSQL::escapeBlob(const char *s, uint32_t length) { // remember to quote even empty stream! - if(!s) + if(!m_connected || !s) return std::string("''"); // quotes escaped string and frees temporary buffer @@ -143,41 +153,16 @@ uint64_t DatabasePgSQL::getLastInsertId() ExecStatusType stat = PQresultStatus(res); if(stat != PGRES_COMMAND_OK && stat != PGRES_TUPLES_OK) { - std::cout << "PQexec(): " << query << ": " << PQresultErrorMessage(res) << std::endl; + std::clog << "PQexec(): \"SELECT LASTVAL() as last\": " << PQresultErrorMessage(res) << std::endl; PQclear(res); return 0; } const uint64_t id = atoll(PQgetvalue(res, 0, PQfnumber(res, "last"))); - PGClear(res); + PQclear(res); return id; } -std::string DatabasePgSQL::_parse(const std::string& s) -{ - std::string query = ""; - - bool inString = false; - for(uint32_t a = 0; a < s.length(); a++) - { - uint8_t ch = s[a]; - if(ch == '\'') - { - if(inString && s[a + 1] != '\'') - inString = false; - else - inString = true; - } - - if(ch == '`' && !inString) - ch = '"'; - - query += ch; - } - - return query; -} - const char* PgSQLResult::getDataStream(const std::string& s, uint64_t& size) { std::string buf = PQgetvalue(m_handle, m_cursor, PQfnumber(m_handle, s.c_str())); @@ -192,13 +177,15 @@ const char* PgSQLResult::getDataStream(const std::string& s, uint64_t& size) void PgSQLResult::free() { - if(m_handle) + if(!m_handle) { - PQclear(m_handle); - delete this; + std::clog << "[Critical - PgSQLResult::free] Trying to free already freed result!!!" << std::endl; + return; } - else - std::cout << "[Warning - PgSQLResult::free] Trying to free already freed result." << std::endl; + + PQclear(m_handle); + m_handle = NULL; + delete this; } bool PgSQLResult::next() @@ -210,15 +197,19 @@ bool PgSQLResult::next() return true; } -PgSQLResult::PgSQLResult(PGresult* results) +PgSQLResult::~PgSQLResult() { - if(!res) - { - delete this; + if(m_handle) + PQclear(m_handle); +} + +PgSQLResult::PgSQLResult(PGresult* result) +{ + if(!result) return; - } - m_handle = results; - m_cursor = -1; + m_handle = result; m_rows = PQntuples(m_handle) - 1; + m_cursor = -1; } +#endif \ No newline at end of file diff --git a/databasepgsql.h b/databasepgsql.h index f385aae..59c2dcf 100644 --- a/databasepgsql.h +++ b/databasepgsql.h @@ -18,10 +18,11 @@ #ifndef __DATABASE_PGSQL__ #define __DATABASE_PGSQL__ +#ifdef __USE_PGSQL__ #ifndef __DATABASE__ #error "database.h should be included first." #endif -#include +#include "libpq-fe.h" class DatabasePgSQL : public _Database { @@ -29,16 +30,16 @@ class DatabasePgSQL : public _Database DatabasePgSQL(); DATABASE_VIRTUAL ~DatabasePgSQL() {PQfinish(m_handle);} - DATABASE_VIRTUAL bool getParam(DBParam_t param); + DATABASE_VIRTUAL bool multiLine() const {return true;} - DATABASE_VIRTUAL bool beginTransaction() {return executeQuery("BEGIN");} - DATABASE_VIRTUAL bool rollback() {return executeQuery("ROLLBACK");} - DATABASE_VIRTUAL bool commit() {return executeQuery("COMMIT");} + DATABASE_VIRTUAL bool beginTransaction() {return query("BEGIN");} + DATABASE_VIRTUAL bool rollback() {return query("ROLLBACK");} + DATABASE_VIRTUAL bool commit() {return query("COMMIT");} - DATABASE_VIRTUAL bool executeQuery(const std::string& query); - DATABASE_VIRTUAL DBResult* storeQuery(const std::string& query); + DATABASE_VIRTUAL bool query(std::string query); + DATABASE_VIRTUAL DBResult* storeQuery(std::string query); - DATABASE_VIRTUAL std::string escapeString(const std::string& s); + DATABASE_VIRTUAL std::string escapeString(std::string s); DATABASE_VIRTUAL std::string escapeBlob(const char *s, uint32_t length); DATABASE_VIRTUAL uint64_t getLastInsertId(); @@ -67,9 +68,10 @@ class PgSQLResult : public _DBResult protected: PgSQLResult(PGresult* results); - DATABASE_VIRTUAL ~PgSQLResult() {} + DATABASE_VIRTUAL ~PgSQLResult(); PGresult* m_handle; int32_t m_rows, m_cursor; }; #endif +#endif \ No newline at end of file diff --git a/databasesqlite.cpp b/databasesqlite.cpp index 49f048d..8ac9686 100644 --- a/databasesqlite.cpp +++ b/databasesqlite.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" +#ifdef __USE_SQLITE__ #include #include @@ -23,17 +24,16 @@ #include "tools.h" #include "configmanager.h" + extern ConfigManager g_config; #if SQLITE_VERSION_NUMBER < 3003009 -#define OTSYS_SQLITE3_PREPARE sqlite3_prepare -#else -#define OTSYS_SQLITE3_PREPARE sqlite3_prepare_v2 +#define sqlite3_prepare_v2 sqlite3_prepare #endif -DatabaseSQLite::DatabaseSQLite() +DatabaseSQLite::DatabaseSQLite() : + m_handle(NULL) { - m_connected = false; // test for existence of database file; // sqlite3_open will create a new one if it isn't there (what we don't want) if(!fileExists(g_config.getString(ConfigManager::SQL_FILE).c_str())) @@ -42,32 +42,20 @@ DatabaseSQLite::DatabaseSQLite() // Initialize sqlite if(sqlite3_open(g_config.getString(ConfigManager::SQL_FILE).c_str(), &m_handle) != SQLITE_OK) { - std::cout << "Failed to initialize SQLite connection." << std::endl; + std::clog << "Failed to initialize SQLite connection: " << sqlite3_errmsg(m_handle) << " (" << sqlite3_errcode(m_handle) << ")" << std::endl; sqlite3_close(m_handle); } else m_connected = true; } -bool DatabaseSQLite::getParam(DBParam_t param) -{ - switch(param) - { - case DBPARAM_MULTIINSERT: - default: - break; - } - - return false; -} - -std::string DatabaseSQLite::_parse(const std::string &s) +std::string DatabaseSQLite::_parse(const std::string& s) { std::string query = ""; query.reserve(s.size()); bool inString = false; - for(uint32_t i = 0; i < s.length(); i++) + for(uint32_t i = 0; i < s.length(); ++i) { uint8_t ch = s[i]; if(ch == '\'') @@ -87,23 +75,22 @@ std::string DatabaseSQLite::_parse(const std::string &s) return query; } -bool DatabaseSQLite::executeQuery(const std::string &query) +bool DatabaseSQLite::query(std::string query) { boost::recursive_mutex::scoped_lock lockClass(sqliteLock); if(!m_connected) return false; - #ifdef __SQL_QUERY_DEBUG__ - std::cout << "SQLITE QUERY: " << query << std::endl; - #endif - std::string buf = _parse(query); +#ifdef __SQL_QUERY_DEBUG__ + std::clog << "SQLLITE DEBUG, query: " << buf << std::endl; +#endif sqlite3_stmt* stmt; // prepares statement - if(OTSYS_SQLITE3_PREPARE(m_handle, buf.c_str(), buf.length(), &stmt, NULL) != SQLITE_OK) + if(sqlite3_prepare_v2(m_handle, buf.c_str(), buf.length(), &stmt, NULL) != SQLITE_OK) { sqlite3_finalize(stmt); - std::cout << "OTSYS_SQLITE3_PREPARE(): SQLITE ERROR: " << sqlite3_errmsg(m_handle) << " (" << buf << ")" << std::endl; + std::clog << "sqlite3_prepare_v2(): SQLITE ERROR: " << sqlite3_errmsg(m_handle) << " (" << buf << ")" << std::endl; return false; } @@ -112,7 +99,7 @@ bool DatabaseSQLite::executeQuery(const std::string &query) if(ret != SQLITE_OK && ret != SQLITE_DONE && ret != SQLITE_ROW) { sqlite3_finalize(stmt); - std::cout << "sqlite3_step(): SQLITE ERROR: " << sqlite3_errmsg(m_handle) << std::endl; + std::clog << "sqlite3_step(): SQLITE ERROR: " << sqlite3_errmsg(m_handle) << std::endl; return false; } @@ -122,23 +109,22 @@ bool DatabaseSQLite::executeQuery(const std::string &query) return true; } -DBResult* DatabaseSQLite::storeQuery(const std::string &query) +DBResult* DatabaseSQLite::storeQuery(std::string query) { boost::recursive_mutex::scoped_lock lockClass(sqliteLock); if(!m_connected) return NULL; - #ifdef __SQL_QUERY_DEBUG__ - std::cout << "SQLITE QUERY: " << query << std::endl; - #endif - std::string buf = _parse(query); +#ifdef __SQL_QUERY_DEBUG__ + std::clog << "SQLLITE DEBUG, storeQuery: " << buf << std::endl; +#endif sqlite3_stmt* stmt; // prepares statement - if(OTSYS_SQLITE3_PREPARE(m_handle, buf.c_str(), buf.length(), &stmt, NULL) != SQLITE_OK) + if(sqlite3_prepare_v2(m_handle, buf.c_str(), buf.length(), &stmt, NULL) != SQLITE_OK) { sqlite3_finalize(stmt); - std::cout << "OTSYS_SQLITE3_PREPARE(): SQLITE ERROR: " << sqlite3_errmsg(m_handle) << " (" << buf << ")" << std::endl; + std::clog << "sqlite3_prepare_v2(): SQLITE ERROR: " << sqlite3_errmsg(m_handle) << " (" << buf << ")" << std::endl; return NULL; } @@ -146,16 +132,16 @@ DBResult* DatabaseSQLite::storeQuery(const std::string &query) return verifyResult(result); } -std::string DatabaseSQLite::escapeString(const std::string &s) +std::string DatabaseSQLite::escapeString(std::string s) { // remember about quoiting even an empty string! if(!s.size()) return std::string("''"); // the worst case is 2n + 3 - char* output = new char[s.length() * 2 + 3]; + char* output = new char[(s.length() << 1) + 3]; // quotes escaped string and frees temporary buffer - sqlite3_snprintf(s.length() * 2 + 1, output, "%Q", s.c_str()); + sqlite3_snprintf((s.length() << 1) + 1, output, "%Q", s.c_str()); std::string r(output); delete[] output; @@ -173,7 +159,7 @@ std::string DatabaseSQLite::escapeBlob(const char* s, uint32_t length) { std::string buf = "x'"; char* hex = new char[2 + 1]; //need one extra byte for null-character - for(uint32_t i = 0; i < length; i++) + for(uint32_t i = 0; i < length; ++i) { sprintf(hex, "%02x", ((uint8_t)s[i])); buf += hex; @@ -184,27 +170,27 @@ std::string DatabaseSQLite::escapeBlob(const char* s, uint32_t length) return buf; } -int32_t SQLiteResult::getDataInt(const std::string &s) +int32_t SQLiteResult::getDataInt(const std::string& s) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end()) return sqlite3_column_int(m_handle, it->second); - std::cout << "Error during getDataInt(" << s << ")." << std::endl; + std::clog << "Error during getDataInt(" << s << ")." << std::endl; return 0; // Failed } -int64_t SQLiteResult::getDataLong(const std::string &s) +int64_t SQLiteResult::getDataLong(const std::string& s) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end()) return sqlite3_column_int64(m_handle, it->second); - std::cout << "Error during getDataLong(" << s << ")." << std::endl; + std::clog << "Error during getDataLong(" << s << ")." << std::endl; return 0; // Failed } -std::string SQLiteResult::getDataString(const std::string &s) +std::string SQLiteResult::getDataString(const std::string& s) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end() ) @@ -213,11 +199,11 @@ std::string SQLiteResult::getDataString(const std::string &s) return value; } - std::cout << "Error during getDataString(" << s << ")." << std::endl; + std::clog << "Error during getDataString(" << s << ")." << std::endl; return std::string(""); // Failed } -const char* SQLiteResult::getDataStream(const std::string &s, uint64_t &size) +const char* SQLiteResult::getDataStream(const std::string& s, uint64_t& size) { listNames_t::iterator it = m_listNames.find(s); if(it != m_listNames.end()) @@ -227,33 +213,42 @@ const char* SQLiteResult::getDataStream(const std::string &s, uint64_t &size) return value; } - std::cout << "Error during getDataStream(" << s << ")." << std::endl; + std::clog << "Error during getDataStream(" << s << ")." << std::endl; return NULL; // Failed } void SQLiteResult::free() { - if(m_handle) + if(!m_handle) { - sqlite3_finalize(m_handle); - delete this; + std::clog << "[Critical - SQLiteResult::free] Trying to free already freed result!!!" << std::endl; + return; } - else - std::cout << "[Warning - SQLiteResult::free] Trying to free already freed result." << std::endl; + + sqlite3_finalize(m_handle); + m_handle = NULL; + + m_listNames.clear(); + delete this; +} + +SQLiteResult::~SQLiteResult() +{ + if(!m_handle) + return; + + sqlite3_finalize(m_handle); + m_listNames.clear(); } SQLiteResult::SQLiteResult(sqlite3_stmt* stmt) { if(!stmt) - { - delete this; return; - } m_handle = stmt; - m_listNames.clear(); - int32_t fields = sqlite3_column_count(m_handle); - for(int32_t i = 0; i < fields; i++) + for(int32_t i = 0; i < fields; ++i) m_listNames[sqlite3_column_name(m_handle, i)] = i; } +#endif diff --git a/databasesqlite.h b/databasesqlite.h index 0480da3..7357f3e 100644 --- a/databasesqlite.h +++ b/databasesqlite.h @@ -18,6 +18,7 @@ #ifndef __DATABASE_SQLITE__ #define __DATABASE_SQLITE__ +#ifdef __USE_SQLITE__ #ifndef __DATABASE__ #error "database.h should be included first." #endif @@ -32,28 +33,28 @@ class DatabaseSQLite : public _Database DatabaseSQLite(); DATABASE_VIRTUAL ~DatabaseSQLite() {sqlite3_close(m_handle);} - DATABASE_VIRTUAL bool getParam(DBParam_t param); + DATABASE_VIRTUAL bool multiLine() const {return false;} - DATABASE_VIRTUAL bool beginTransaction() {return executeQuery("BEGIN");} - DATABASE_VIRTUAL bool rollback() {return executeQuery("ROLLBACK");} - DATABASE_VIRTUAL bool commit() {return executeQuery("COMMIT");} + DATABASE_VIRTUAL bool beginTransaction() {return query("BEGIN");} + DATABASE_VIRTUAL bool rollback() {return query("ROLLBACK");} + DATABASE_VIRTUAL bool commit() {return query("COMMIT");} - DATABASE_VIRTUAL bool executeQuery(const std::string &query); - DATABASE_VIRTUAL DBResult* storeQuery(const std::string &query); + DATABASE_VIRTUAL bool query(std::string query); + DATABASE_VIRTUAL DBResult* storeQuery(std::string query); - DATABASE_VIRTUAL std::string escapeString(const std::string &s); + DATABASE_VIRTUAL std::string escapeString(std::string s); DATABASE_VIRTUAL std::string escapeBlob(const char* s, uint32_t length); DATABASE_VIRTUAL uint64_t getLastInsertId() {return (uint64_t)sqlite3_last_insert_rowid(m_handle);} - DATABASE_VIRTUAL std::string getStringComparison() {return "LIKE ";} + DATABASE_VIRTUAL std::string getStringComparer() {return "LIKE ";} DATABASE_VIRTUAL std::string getUpdateLimiter() {return ";";} DATABASE_VIRTUAL DatabaseEngine_t getDatabaseEngine() {return DATABASE_ENGINE_SQLITE;} protected: - boost::recursive_mutex sqliteLock; + std::string _parse(const std::string& s); - std::string _parse(const std::string &s); + boost::recursive_mutex sqliteLock; sqlite3* m_handle; }; @@ -62,17 +63,17 @@ class SQLiteResult : public _DBResult friend class DatabaseSQLite; public: - DATABASE_VIRTUAL int32_t getDataInt(const std::string &s); - DATABASE_VIRTUAL int64_t getDataLong(const std::string &s); - DATABASE_VIRTUAL std::string getDataString(const std::string &s); - DATABASE_VIRTUAL const char* getDataStream(const std::string &s, uint64_t &size); + DATABASE_VIRTUAL int32_t getDataInt(const std::string& s); + DATABASE_VIRTUAL int64_t getDataLong(const std::string& s); + DATABASE_VIRTUAL std::string getDataString(const std::string& s); + DATABASE_VIRTUAL const char* getDataStream(const std::string& s, uint64_t& size); DATABASE_VIRTUAL void free(); DATABASE_VIRTUAL bool next() {return sqlite3_step(m_handle) == SQLITE_ROW;} protected: SQLiteResult(sqlite3_stmt* stmt); - DATABASE_VIRTUAL ~SQLiteResult() {} + DATABASE_VIRTUAL ~SQLiteResult(); typedef std::map listNames_t; listNames_t m_listNames; @@ -80,3 +81,4 @@ class SQLiteResult : public _DBResult sqlite3_stmt* m_handle; }; #endif +#endif diff --git a/debianfix.sh b/debianfix.sh deleted file mode 100644 index 01722ea..0000000 --- a/debianfix.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# -# Debian Lenny fix for OTServ 1.0 -# Written by Ivan Vucica -# This file is released into public domain. -# -# Only in areas where above statement is not legally applicable: -# (c) 2007-2009 Ivan Vucica -# This is free software: you are free to change and redistribute it. -# There is NO WARRANTY, to the extent permitted by law. -# - -echo "Debian fix for TheForgottenServer" -echo "---------------------" - -echo "Replacing boost::system..." -find \( -name '*.cpp' -or -name '*.h' \) -exec sed -i 's/boost::system::/asio::/g' {} \; -print -echo "Replacing boost::asio..." -find \( -name '*.cpp' -or -name '*.h' \) -exec sed -i 's/boost::asio::/asio::/g' {} \; -print -echo "Replacing boost/asio.hpp..." -find \( -name '*.cpp' -or -name '*.h' \) -exec sed -i 's/boost\/asio.hpp/asio.hpp/g' {} \; -print - -echo "Replacing autoconf stuff..." -sed -i 's/AC_MSG_ERROR("Linking against boost::system library failed.")/AC_MSG_WARN("Linking against boost::system library failed.")/g' configure.ac -echo "Replacing autoconf boost/asio.hpp..." -sed -i 's/boost\/asio.hpp/asio.hpp/g' configure.ac - diff --git a/definitions.h b/definitions.h index 8fe4bec..2e93ac9 100644 --- a/definitions.h +++ b/definitions.h @@ -17,136 +17,138 @@ #ifndef __DEFINITIONS__ #define __DEFINITIONS__ -#undef MULTI_SQL_DRIVERS -#define SQL_DRIVERS __USE_SQLITE__+__USE_MYSQL__+__USE_ODBC__+__USE_PGSQL__ -#if SQL_DRIVERS > 1 -#define MULTI_SQL_DRIVERS -#endif -#ifdef __MINGW32__ - #define XML_GCC_FREE - #ifndef __WINDOWS__ - #define __WINDOWS__ - #endif -#endif - -#if defined _WIN32 || defined WIN32 || defined _WIN64 || defined WIN64 || defined __WINDOWS__ || defined WINDOWS - #if defined _WIN64 || defined WIN64 - #ifndef _WIN64 - #define _WIN64 - #endif - #ifndef WIN64 - #define WIN64 - #endif - #else - #ifndef _WIN32 - #define _WIN32 - #endif - #ifndef WIN32 - #define WIN32 - #endif - #endif - #ifndef __WINDOWS__ - #define __WINDOWS__ - #endif - #ifndef WINDOWS - #define WINDOWS - #endif -#endif +#define CLIENT_VERSION_MIN 944 +#define CLIENT_VERSION_MAX 946 +#define CLIENT_VERSION_STRING "Only clients with protocol 9.44 allowed!" -#ifdef __CYGWIN__ - #undef WIN32 - #undef WIN64 - #undef _WIN32 - #undef _WIN64 - #undef WINDOWS - #undef __WINDOWS__ - #define HAVE_ERRNO_AS_DEFINE -#endif +#define CLIENT_VERSION_DAT 0 +#define CLIENT_VERSION_SPR 0 +#define CLIENT_VERSION_PIC 0 +//#define CLIENT_VERSION_DATA -#ifdef XML_GCC_FREE - #define xmlFree(s) free(s) -#endif +#define SOFTWARE_NAME "The Forgotten Server" +#define SOFTWARE_VERSION "0.4_SVN" +#define SOFTWARE_CODENAME "" +#define SOFTWARE_DEVELOPERS "Elf, Talaturen, Dalkon, BeniS, Tryller and Kornholijo" +#define SOFTWARE_PROTOCOL "9.46" -#ifdef __USE_MINIDUMP__ - #ifndef __EXCEPTION_TRACER__ - #define __EXCEPTION_TRACER__ - #endif -#endif +#define VERSION_CHECK "http://forgottenserver.otland.net/version.xml" +#define VERSION_PATCH 0 +#define VERSION_TIMESTAMP 0 +#define VERSION_BUILD 0 +#define VERSION_DATABASE 34 -#ifdef __DEBUG_EXCEPTION_REPORT__ - #define DEBUG_REPORT int *a = NULL; *a = 1; -#else - #ifdef __EXCEPTION_TRACER__ - #include "exception.h" - #define DEBUG_REPORT ExceptionHandler::dumpStack(); - #else - #define DEBUG_REPORT - #endif -#endif +#undef MULTI_SQL_DRIVERS +#define SQL_DRIVERS __USE_SQLITE__+__USE_MYSQL__+__USE_PGSQL__ -#define BOOST_ASIO_ENABLE_CANCELIO 1 -#if defined WINDOWS -#if defined _MSC_VER && defined NDEBUG - #define _SECURE_SCL 0 - #define HAS_ITERATOR_DEBUGGING 0 +#if SQL_DRIVERS > 1 + #define MULTI_SQL_DRIVERS #endif +#define MAX_RAND_RANGE 10000000 #ifndef __FUNCTION__ #define __FUNCTION__ __func__ #endif -#ifdef _WIN32_WINNT -#undef _WIN32_WINNT -#endif - -#ifdef _WIN64_WINNT -#undef _WIN64_WINNT -#endif - -//Windows 2000 0x0500 -//Windows XP 0x0501 -//Windows 2003 0x0502 -//Windows Vista 0x0600 -//Windows Seven 0x0601 - -#define _WIN32_WINNT 0x0501 -#define _WIN64_WINNT 0x0501 - -#ifndef __GNUC__ +#define BOOST_FILESYSTEM_VERSION 2 +#define BOOST_ASIO_ENABLE_CANCELIO 1 +#ifdef _MSC_VER + #define __PRETTY_FUNCTION__ __FUNCDNAME__ #ifndef NOMINMAX #define NOMINMAX #endif - #include - inline int strcasecmp(const char *s1, const char *s2) - { - return ::_stricmp(s1, s2); - } - - inline int strncasecmp(const char *s1, const char *s2, size_t n) - { - return ::_strnicmp(s1, s2, n); - } - - typedef unsigned long long uint64_t; - typedef signed long long int64_t; - typedef unsigned long uint32_t; - typedef signed long int32_t; - typedef unsigned short uint16_t; - typedef signed short int16_t; - typedef unsigned char uint8_t; - typedef signed char int8_t; + #ifdef NDEBUG + #define _SECURE_SCL 0 + #define HAS_ITERATOR_DEBUGGING 0 + #endif + #include #define atoll _atoi64 + #if VISUALC_VERSION < 10 + typedef unsigned long long uint64_t; + typedef signed long long int64_t; + typedef unsigned int uint32_t; + typedef signed int int32_t; + typedef unsigned short uint16_t; + typedef signed short int16_t; + typedef unsigned char uint8_t; + typedef signed char int8_t; + #endif #pragma warning(disable:4786) // msvc too long debug names in stl #pragma warning(disable:4250) // 'class1' : inherits 'class2::member' via dominance #pragma warning(disable:4244) #pragma warning(disable:4267) #pragma warning(disable:4018) + #pragma warning(disable:4309) + #pragma warning(disable:4996) // '_ftime64' : this function or variable may be unsafe + + #ifndef _WIN32 + #define _WIN32 + #endif + #ifndef WIN32 + #define WIN32 + #endif + + #ifndef __WINDOWS__ + #define __WINDOWS__ + #endif + #ifndef WINDOWS + #define WINDOWS + #endif +#else + #if defined _WIN32 || defined WIN32 || defined __WINDOWS__ || defined WINDOWS + #ifndef _WIN32 + #define _WIN32 + #endif + #ifndef WIN32 + #define WIN32 + #endif + #ifndef __WINDOWS__ + #define __WINDOWS__ + #endif + #ifndef WINDOWS + #define WINDOWS + #endif + #endif + + #ifdef __CYGWIN__ + #undef WIN32 + #undef _WIN32 + #undef WINDOWS + #undef __WINDOWS__ + #define HAVE_ERRNO_AS_DEFINE + #endif #endif + +#ifdef __WINDOWS__ + #ifdef _WIN32_WINNT + #undef _WIN32_WINNT + #endif + + //Windows 2000 0x0500 + //Windows XP 0x0501 + //Windows 2003 0x0502 + //Windows Vista 0x0600 + //Windows Seven 0x0601 + + #define _WIN32_WINNT 0x0501 +#elif defined __GNUC__ + #define __USE_ZLIB__ +#endif + +#ifdef __MINGW32__ + #define XML_GCC_FREE #endif + +#ifdef XML_GCC_FREE + #define xmlFree(s) free(s) #endif +#ifndef __EXCEPTION_TRACER__ + #define DEBUG_REPORT +#endif +#endif diff --git a/depot.cpp b/depot.cpp index 54c6c40..9c99166 100644 --- a/depot.cpp +++ b/depot.cpp @@ -19,11 +19,8 @@ #include "tools.h" Depot::Depot(uint16_t type): - Container(type) -{ - maxSize = 30; - depotLimit = 1000; -} + Container(type), inbox(NULL), locker(NULL), depotLimit(1000) +{} Attr_ReadValue Depot::readAttr(AttrTypes_t attr, PropStream& propStream) { @@ -31,7 +28,7 @@ Attr_ReadValue Depot::readAttr(AttrTypes_t attr, PropStream& propStream) return Item::readAttr(attr, propStream); uint16_t depotId; - if(!propStream.GET_USHORT(depotId)) + if(!propStream.getShort(depotId)) return ATTR_READ_ERROR; setAttribute("depotid", depotId); @@ -39,14 +36,14 @@ Attr_ReadValue Depot::readAttr(AttrTypes_t attr, PropStream& propStream) } ReturnValue Depot::__queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const + uint32_t flags, Creature* actor/* = NULL*/) const { const Item* item = thing->getItem(); if(!item) return RET_NOTPOSSIBLE; if((flags & FLAG_NOLIMIT) == FLAG_NOLIMIT) - return Container::__queryAdd(index, thing, count, flags); + return Container::__queryAdd(index, thing, count, flags, actor); int32_t addCount = 0; if((item->isStackable() && item->getItemCount() != count)) @@ -63,7 +60,7 @@ ReturnValue Depot::__queryAdd(int32_t index, const Thing* thing, uint32_t count, if(getItemHoldingCount() + addCount > depotLimit) return RET_DEPOTISFULL; - return Container::__queryAdd(index, thing, count, flags); + return Container::__queryAdd(index, thing, count, flags, actor); } ReturnValue Depot::__queryMaxCount(int32_t index, const Thing* thing, uint32_t count, @@ -72,17 +69,25 @@ ReturnValue Depot::__queryMaxCount(int32_t index, const Thing* thing, uint32_t c return Container::__queryMaxCount(index, thing, count, maxQueryCount, flags); } +std::map& Depot::__getAllItemTypeCount(std::map& countMap) const +{ + for(ContainerIterator it = locker->begin(); it != locker->end(); ++it) + countMap[(*it)->getID()] += (*it)->getItemCount(); + + return countMap; +} + void Depot::postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link /*= LINK_OWNER*/) + int32_t index, CylinderLink_t /*link = LINK_OWNER*/) { if(getParent()) getParent()->postAddNotification(actor, thing, oldParent, index, LINK_PARENT); } void Depot::postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link /*= LINK_OWNER*/) + int32_t index, bool isCompleteRemoval, CylinderLink_t /*link = LINK_OWNER*/) { if(getParent()) - getParent()->postRemoveNotification(actor, thing, newParent, - index, isCompleteRemoval, LINK_PARENT); + getParent()->postRemoveNotification(actor, thing, newParent, index, isCompleteRemoval, LINK_PARENT); } diff --git a/depot.h b/depot.h index 79692f0..e97636f 100644 --- a/depot.h +++ b/depot.h @@ -30,8 +30,16 @@ class Depot : public Container //serialization virtual Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream); + virtual uint32_t getItemHoldingCount() const {return Container::getItemHoldingCount() - 3;} uint32_t getDepotId() const; + void setDepotId(int32_t depotId) {setAttribute("depotid", depotId);} + + void setInbox(Container* container) {inbox = container;} + Container* getInbox() const {return inbox;} + + void setLocker(Container* container) {locker = container;} + Container* getLocker() const {return locker;} void setMaxDepotLimit(uint32_t count) {depotLimit = count;} @@ -48,20 +56,23 @@ class Depot : public Container virtual const Creature* getCreature() const {return NULL;} virtual ReturnValue __queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const; + uint32_t flags, Creature* actor = NULL) const; virtual ReturnValue __queryMaxCount(int32_t index, const Thing* thing, uint32_t count, uint32_t& maxQueryCount, uint32_t flags) const; + virtual std::map& __getAllItemTypeCount(std::map& countMap) const; + virtual void postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link = LINK_OWNER); + int32_t index, CylinderLink_t link = LINK_OWNER); virtual void postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link = LINK_OWNER); + int32_t index, bool isCompleteRemoval, CylinderLink_t link = LINK_OWNER); //overrides virtual bool canRemove() const {return false;} private: + Container *inbox, *locker; uint32_t depotLimit; }; diff --git a/dev-cpp/TheForgottenServer-console-debug.dev b/dev-cpp/TheForgottenServer-console-debug.dev deleted file mode 100644 index e3cf440..0000000 --- a/dev-cpp/TheForgottenServer-console-debug.dev +++ /dev/null @@ -1,1759 +0,0 @@ -[Project] -FileName=TheForgottenServer-console-debug.dev -Name=TheForgottenServer -UnitCount=171 -Type=1 -Ver=1 -ObjFiles= -Includes= -Libs= -PrivateResource=TheForgottenServer_private.rc -ResourceIncludes= -MakeIncludes= -Compiler= -CppCompiler=-D__USE_MYSQL___@@_-D__USE_SQLITE___@@_-D__ENABLE_SERVER_DIAGNOSTIC___@@_-D__CONSOLE___@@_-D__EXCEPTION_TRACER___@@_ -Linker=-O1_@@_-lboost_system_@@_-lgmp_@@_-llua5.1_@@_-lmysql_@@_-lboost_regex_@@_-lsqlite3_@@_-lwsock32_@@_-lxml2_@@_-lmysql_@@_-lws2_32_@@_-s_@@_-lboost_filesystem_@@_-lboost_thread_@@_-Wl,-Map=forgottenserver.map_@@_ -IsCpp=1 -Icon=TheForgottenServer.ico -ExeOutput= -ObjectOutput=obj-console-debug/ -OverrideOutput=0 -OverrideOutputName=TheForgottenServer.exe -HostApplication= -Folders= -CommandLine= -UseCustomMakefile=0 -CustomMakefile= -IncludeVersionInfo=1 -SupportXPThemes=0 -CompilerSet=0 -CompilerSettings=0000000001100000000000 - -[Unit2] -FileName=..\account.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit3] -FileName=..\actions.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CPP) -c actions.cpp -o actions.o $(CXXFLAGS) - -[Unit4] -FileName=..\actions.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit7] -FileName=..\baseevents.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit8] -FileName=..\baseevents.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit9] -FileName=..\beds.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit10] -FileName=..\beds.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit11] -FileName=..\chat.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit12] -FileName=..\chat.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit13] -FileName=..\combat.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit14] -FileName=..\combat.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit15] -FileName=..\protocolold.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit16] -FileName=..\protocolold.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit17] -FileName=..\condition.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit18] -FileName=..\condition.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit19] -FileName=..\configmanager.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit20] -FileName=..\configmanager.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit21] -FileName=..\connection.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit22] -FileName=..\connection.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit23] -FileName=..\const.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit24] -FileName=..\container.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit25] -FileName=..\container.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit26] -FileName=..\creature.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit27] -FileName=..\creature.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit28] -FileName=..\creatureevent.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit29] -FileName=..\creatureevent.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit30] -FileName=..\cylinder.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit31] -FileName=..\cylinder.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit32] -FileName=..\database.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit33] -FileName=..\database.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit34] -FileName=..\databasemysql.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit35] -FileName=..\databasemysql.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit36] -FileName=..\databasesqlite.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit37] -FileName=..\databasesqlite.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit38] -FileName=..\definitions.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit39] -FileName=..\depot.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit40] -FileName=..\depot.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit41] -FileName=..\enums.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit42] -FileName=..\exception.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit43] -FileName=..\exception.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit44] -FileName=..\fileloader.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit45] -FileName=..\fileloader.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit46] -FileName=..\game.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit47] -FileName=..\game.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit48] -FileName=..\gui.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit49] -FileName=..\gui.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit50] -FileName=..\gui_resources.rc -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit51] -FileName=..\house.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit52] -FileName=..\house.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit53] -FileName=..\housetile.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit54] -FileName=..\housetile.h -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= -CompileCpp=1 - -[Unit55] -FileName=..\inputbox.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit56] -FileName=..\inputbox.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit57] -FileName=..\ioguild.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit58] -FileName=..\ioguild.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit59] -FileName=..\iologindata.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit60] -FileName=..\iologindata.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit61] -FileName=..\iomap.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit62] -FileName=..\iomap.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit63] -FileName=..\iomapserialize.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit64] -FileName=..\iomapserialize.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit65] -FileName=..\item.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit66] -FileName=..\item.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit67] -FileName=..\itemloader.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit68] -FileName=..\items.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit69] -FileName=..\items.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit70] -FileName=..\luascript.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit71] -FileName=..\luascript.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit74] -FileName=..\map.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit75] -FileName=..\map.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit76] -FileName=..\md5.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit77] -FileName=..\md5.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit78] -FileName=..\monster.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit79] -FileName=..\monster.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit80] -FileName=..\monsters.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit81] -FileName=..\monsters.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit82] -FileName=..\movement.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit83] -FileName=..\movement.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit84] -FileName=..\networkmessage.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit85] -FileName=..\networkmessage.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit86] -FileName=..\npc.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit87] -FileName=..\npc.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit88] -FileName=..\otpch.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit89] -FileName=..\otserv.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit90] -FileName=..\otsystem.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit91] -FileName=..\outfit.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit92] -FileName=..\outfit.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit93] -FileName=..\outputmessage.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit94] -FileName=..\outputmessage.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit95] -FileName=..\party.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit96] -FileName=..\party.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit97] -FileName=..\player.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit98] -FileName=..\player.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit99] -FileName=..\playerbox.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit100] -FileName=..\playerbox.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit101] -FileName=..\position.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit102] -FileName=..\position.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit103] -FileName=..\protocol.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit104] -FileName=..\protocol.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit105] -FileName=..\protocolgame.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit106] -FileName=..\protocolgame.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit107] -FileName=..\protocollogin.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit108] -FileName=..\protocollogin.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit109] -FileName=..\quests.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit110] -FileName=..\quests.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit111] -FileName=..\raids.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit112] -FileName=..\raids.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit113] -FileName=..\resources.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit114] -FileName=..\rsa.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit115] -FileName=..\rsa.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit116] -FileName=..\scheduler.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit117] -FileName=..\scheduler.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit118] -FileName=..\scriptmanager.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit119] -FileName=..\scriptmanager.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit120] -FileName=..\server.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit121] -FileName=..\server.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit122] -FileName=..\sha1.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit123] -FileName=..\sha1.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit124] -FileName=..\spawn.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit125] -FileName=..\spawn.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit126] -FileName=..\spells.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit127] -FileName=..\spells.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit128] -FileName=..\status.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit129] -FileName=..\status.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit130] -FileName=..\talkaction.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit131] -FileName=..\talkaction.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit132] -FileName=..\tasks.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit133] -FileName=..\tasks.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit134] -FileName=..\teleport.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit135] -FileName=..\teleport.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit136] -FileName=..\templates.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit137] -FileName=..\textlogger.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit138] -FileName=..\textlogger.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit139] -FileName=..\thing.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit140] -FileName=..\thing.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit141] -FileName=..\tile.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit142] -FileName=..\tile.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit143] -FileName=..\tools.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit144] -FileName=..\tools.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit145] -FileName=..\town.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit146] -FileName=..\trashholder.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit147] -FileName=..\trashholder.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit148] -FileName=..\vocation.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit149] -FileName=..\vocation.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit150] -FileName=..\waitlist.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit151] -FileName=..\waitlist.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit152] -FileName=..\weapons.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit153] -FileName=..\weapons.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit154] -FileName=..\ioban.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit155] -FileName=..\ioban.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit156] -FileName=..\gameservers.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit157] -FileName=..\globalevent.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit158] -FileName=..\globalevent.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit159] -FileName=..\gameservers.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit160] -FileName=..\databasemanager.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit161] -FileName=..\databasemanager.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit162] -FileName=..\admin.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit163] -FileName=..\admin.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit164] -FileName=..\databaseodbc.cpp -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit165] -FileName=..\databaseodbc.h -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit5] -FileName=..\allocator.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit6] -FileName=..\allocator.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit72] -FileName=..\mailbox.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit73] -FileName=..\mailbox.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit166] -FileName=..\databasepgsql.cpp -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit167] -FileName=..\databasepgsql.h -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit1] -FileName=..\waypoints.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit170] -FileName=..\group.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit171] -FileName=..\group.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit168] -FileName=..\itemattributes.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=0 - -[Unit169] -FileName=..\itemattributes.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=0 - -[VersionInfo] -Major=0 -Minor=3 -Release=6 -Build=3429 -LanguageID=1033 -CharsetID=1250 -CompanyName=OtLand.net -FileVersion=0.3.6 -FileDescription=The Forgotten Server -InternalName=Crying Damson -LegalCopyright= -LegalTrademarks= -OriginalFilename=Crying Damson.exe -ProductName=The Forgotten Server -ProductVersion=0.3.6 -AutoIncBuildNr=0 - diff --git a/dev-cpp/TheForgottenServer-console.dev b/dev-cpp/TheForgottenServer-console.dev deleted file mode 100644 index fba7a62..0000000 --- a/dev-cpp/TheForgottenServer-console.dev +++ /dev/null @@ -1,1759 +0,0 @@ -[Project] -FileName=TheForgottenServer-console.dev -Name=TheForgottenServer -UnitCount=171 -Type=1 -Ver=1 -ObjFiles= -Includes= -Libs= -PrivateResource=TheForgottenServer_private.rc -ResourceIncludes= -MakeIncludes= -Compiler= -CppCompiler=-D__USE_MYSQL___@@_-D__USE_SQLITE___@@_-D__ENABLE_SERVER_DIAGNOSTIC___@@_-D__CONSOLE___@@_ -Linker=-O1_@@_-lboost_system_@@_-lgmp_@@_-llua5.1_@@_-lmysql_@@_-lboost_regex_@@_-lsqlite3_@@_-lwsock32_@@_-lxml2_@@_-lmysql_@@_-lws2_32_@@_-s_@@_-lboost_filesystem_@@_-lboost_thread_@@_ -IsCpp=1 -Icon=TheForgottenServer.ico -ExeOutput= -ObjectOutput=obj-console/ -OverrideOutput=0 -OverrideOutputName=TheForgottenServer.exe -HostApplication= -Folders= -CommandLine= -UseCustomMakefile=0 -CustomMakefile= -IncludeVersionInfo=1 -SupportXPThemes=0 -CompilerSet=0 -CompilerSettings=0000000001100000000000 - -[Unit2] -FileName=..\account.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit3] -FileName=..\actions.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CPP) -c actions.cpp -o actions.o $(CXXFLAGS) - -[Unit4] -FileName=..\actions.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit7] -FileName=..\baseevents.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit8] -FileName=..\baseevents.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit9] -FileName=..\beds.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit10] -FileName=..\beds.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit11] -FileName=..\chat.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit12] -FileName=..\chat.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit13] -FileName=..\combat.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit14] -FileName=..\combat.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit15] -FileName=..\protocolold.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit16] -FileName=..\protocolold.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit17] -FileName=..\condition.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit18] -FileName=..\condition.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit19] -FileName=..\configmanager.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit20] -FileName=..\configmanager.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit21] -FileName=..\connection.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit22] -FileName=..\connection.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit23] -FileName=..\const.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit24] -FileName=..\container.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit25] -FileName=..\container.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit26] -FileName=..\creature.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit27] -FileName=..\creature.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit28] -FileName=..\creatureevent.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit29] -FileName=..\creatureevent.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit30] -FileName=..\cylinder.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit31] -FileName=..\cylinder.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit32] -FileName=..\database.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit33] -FileName=..\database.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit34] -FileName=..\databasemysql.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit35] -FileName=..\databasemysql.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit36] -FileName=..\databasesqlite.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit37] -FileName=..\databasesqlite.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit38] -FileName=..\definitions.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit39] -FileName=..\depot.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit40] -FileName=..\depot.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit41] -FileName=..\enums.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit42] -FileName=..\exception.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit43] -FileName=..\exception.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit44] -FileName=..\fileloader.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit45] -FileName=..\fileloader.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit46] -FileName=..\game.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit47] -FileName=..\game.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit48] -FileName=..\gui.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit49] -FileName=..\gui.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit50] -FileName=..\gui_resources.rc -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit51] -FileName=..\house.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit52] -FileName=..\house.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit53] -FileName=..\housetile.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit54] -FileName=..\housetile.h -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= -CompileCpp=1 - -[Unit55] -FileName=..\inputbox.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit56] -FileName=..\inputbox.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit57] -FileName=..\ioguild.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit58] -FileName=..\ioguild.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit59] -FileName=..\iologindata.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit60] -FileName=..\iologindata.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit61] -FileName=..\iomap.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit62] -FileName=..\iomap.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit63] -FileName=..\iomapserialize.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit64] -FileName=..\iomapserialize.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit65] -FileName=..\item.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit66] -FileName=..\item.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit67] -FileName=..\itemloader.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit68] -FileName=..\items.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit69] -FileName=..\items.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit70] -FileName=..\luascript.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit71] -FileName=..\luascript.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit74] -FileName=..\map.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit75] -FileName=..\map.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit76] -FileName=..\md5.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit77] -FileName=..\md5.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit78] -FileName=..\monster.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit79] -FileName=..\monster.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit80] -FileName=..\monsters.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit81] -FileName=..\monsters.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit82] -FileName=..\movement.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit83] -FileName=..\movement.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit84] -FileName=..\networkmessage.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit85] -FileName=..\networkmessage.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit86] -FileName=..\npc.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit87] -FileName=..\npc.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit88] -FileName=..\otpch.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit89] -FileName=..\otserv.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit90] -FileName=..\otsystem.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit91] -FileName=..\outfit.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit92] -FileName=..\outfit.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit93] -FileName=..\outputmessage.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit94] -FileName=..\outputmessage.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit95] -FileName=..\party.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit96] -FileName=..\party.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit97] -FileName=..\player.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit98] -FileName=..\player.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit99] -FileName=..\playerbox.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit100] -FileName=..\playerbox.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit101] -FileName=..\position.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit102] -FileName=..\position.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit103] -FileName=..\protocol.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit104] -FileName=..\protocol.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit105] -FileName=..\protocolgame.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit106] -FileName=..\protocolgame.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit107] -FileName=..\protocollogin.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit108] -FileName=..\protocollogin.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit109] -FileName=..\quests.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit110] -FileName=..\quests.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit111] -FileName=..\raids.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit112] -FileName=..\raids.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit113] -FileName=..\resources.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit114] -FileName=..\rsa.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit115] -FileName=..\rsa.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit116] -FileName=..\scheduler.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit117] -FileName=..\scheduler.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit118] -FileName=..\scriptmanager.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit119] -FileName=..\scriptmanager.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit120] -FileName=..\server.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit121] -FileName=..\server.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit122] -FileName=..\sha1.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit123] -FileName=..\sha1.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit124] -FileName=..\spawn.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit125] -FileName=..\spawn.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit126] -FileName=..\spells.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit127] -FileName=..\spells.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit128] -FileName=..\status.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit129] -FileName=..\status.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit130] -FileName=..\talkaction.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit131] -FileName=..\talkaction.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit132] -FileName=..\tasks.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit133] -FileName=..\tasks.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit134] -FileName=..\teleport.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit135] -FileName=..\teleport.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit136] -FileName=..\templates.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit137] -FileName=..\textlogger.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit138] -FileName=..\textlogger.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit139] -FileName=..\thing.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit140] -FileName=..\thing.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit141] -FileName=..\tile.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit142] -FileName=..\tile.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit143] -FileName=..\tools.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit144] -FileName=..\tools.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit145] -FileName=..\town.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit146] -FileName=..\trashholder.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit147] -FileName=..\trashholder.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit148] -FileName=..\vocation.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit149] -FileName=..\vocation.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit150] -FileName=..\waitlist.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit151] -FileName=..\waitlist.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit152] -FileName=..\weapons.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit153] -FileName=..\weapons.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit154] -FileName=..\ioban.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit155] -FileName=..\ioban.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit156] -FileName=..\gameservers.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit157] -FileName=..\globalevent.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit158] -FileName=..\globalevent.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit159] -FileName=..\gameservers.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit160] -FileName=..\databasemanager.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit161] -FileName=..\databasemanager.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit162] -FileName=..\admin.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit163] -FileName=..\admin.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit164] -FileName=..\databaseodbc.cpp -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit165] -FileName=..\databaseodbc.h -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit5] -FileName=..\allocator.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit6] -FileName=..\allocator.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit72] -FileName=..\mailbox.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit73] -FileName=..\mailbox.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit166] -FileName=..\databasepgsql.cpp -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit167] -FileName=..\databasepgsql.h -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit1] -FileName=..\waypoints.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit170] -FileName=..\group.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit171] -FileName=..\group.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit168] -FileName=..\itemattributes.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=0 - -[Unit169] -FileName=..\itemattributes.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=0 - -[VersionInfo] -Major=0 -Minor=3 -Release=6 -Build=3429 -LanguageID=1033 -CharsetID=1250 -CompanyName=OtLand.net -FileVersion=0.3.6 -FileDescription=The Forgotten Server -InternalName=Crying Damson -LegalCopyright= -LegalTrademarks= -OriginalFilename=Crying Damson.exe -ProductName=The Forgotten Server -ProductVersion=0.3.6 -AutoIncBuildNr=0 - diff --git a/dev-cpp/TheForgottenServer-debug.dev b/dev-cpp/TheForgottenServer-debug.dev deleted file mode 100644 index 5883e53..0000000 --- a/dev-cpp/TheForgottenServer-debug.dev +++ /dev/null @@ -1,1759 +0,0 @@ -[Project] -FileName=TheForgottenServer-debug.dev -Name=TheForgottenServer -UnitCount=171 -Type=0 -Ver=1 -ObjFiles= -Includes= -Libs= -PrivateResource=TheForgottenServer_private.rc -ResourceIncludes= -MakeIncludes= -Compiler= -CppCompiler=-D__USE_MYSQL___@@_-D__USE_SQLITE___@@_-D__ENABLE_SERVER_DIAGNOSTIC___@@_-D__EXCEPTION_TRACER___@@_ -Linker=-O1_@@_-lboost_system_@@_-lgmp_@@_-llua5.1_@@_-lmysql_@@_-lboost_regex_@@_-lsqlite3_@@_-lwsock32_@@_-lxml2_@@_-lmysql_@@_-lws2_32_@@_-s_@@_-lboost_filesystem_@@_-Wl,-Map=forgottenserver.map_@@_-lboost_thread_@@_ -IsCpp=1 -Icon=TheForgottenServer.ico -ExeOutput= -ObjectOutput=obj-debug/ -OverrideOutput=0 -OverrideOutputName=TheForgottenServer.exe -HostApplication= -Folders= -CommandLine= -UseCustomMakefile=0 -CustomMakefile= -IncludeVersionInfo=1 -SupportXPThemes=0 -CompilerSet=0 -CompilerSettings=0000000001100000000000 - -[Unit2] -FileName=..\account.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit3] -FileName=..\actions.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=$(CPP) -c actions.cpp -o actions.o $(CXXFLAGS) - -[Unit4] -FileName=..\actions.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit7] -FileName=..\baseevents.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit8] -FileName=..\baseevents.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit9] -FileName=..\beds.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit10] -FileName=..\beds.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit11] -FileName=..\chat.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit12] -FileName=..\chat.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit13] -FileName=..\combat.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit14] -FileName=..\combat.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit15] -FileName=..\protocolold.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit16] -FileName=..\protocolold.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit17] -FileName=..\condition.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit18] -FileName=..\condition.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit19] -FileName=..\configmanager.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit20] -FileName=..\configmanager.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit21] -FileName=..\connection.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit22] -FileName=..\connection.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit23] -FileName=..\const.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit24] -FileName=..\container.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit25] -FileName=..\container.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit26] -FileName=..\creature.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit27] -FileName=..\creature.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit28] -FileName=..\creatureevent.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit29] -FileName=..\creatureevent.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit30] -FileName=..\cylinder.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit31] -FileName=..\cylinder.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit32] -FileName=..\database.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit33] -FileName=..\database.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit34] -FileName=..\databasemysql.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit35] -FileName=..\databasemysql.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit36] -FileName=..\databasesqlite.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit37] -FileName=..\databasesqlite.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit38] -FileName=..\definitions.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit39] -FileName=..\depot.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit40] -FileName=..\depot.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit41] -FileName=..\enums.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit42] -FileName=..\exception.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit43] -FileName=..\exception.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit44] -FileName=..\fileloader.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit45] -FileName=..\fileloader.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit46] -FileName=..\game.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit47] -FileName=..\game.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit48] -FileName=..\gui.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit49] -FileName=..\gui.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit50] -FileName=..\gui_resources.rc -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit51] -FileName=..\house.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit52] -FileName=..\house.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit53] -FileName=..\housetile.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit54] -FileName=..\housetile.h -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= -CompileCpp=1 - -[Unit55] -FileName=..\inputbox.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit56] -FileName=..\inputbox.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit57] -FileName=..\ioguild.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit58] -FileName=..\ioguild.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit59] -FileName=..\iologindata.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit60] -FileName=..\iologindata.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit61] -FileName=..\iomap.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit62] -FileName=..\iomap.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit63] -FileName=..\iomapserialize.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit64] -FileName=..\iomapserialize.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit65] -FileName=..\item.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit66] -FileName=..\item.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit67] -FileName=..\itemloader.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit68] -FileName=..\items.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit69] -FileName=..\items.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit70] -FileName=..\luascript.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit71] -FileName=..\luascript.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit74] -FileName=..\map.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit75] -FileName=..\map.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit76] -FileName=..\md5.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit77] -FileName=..\md5.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit78] -FileName=..\monster.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit79] -FileName=..\monster.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit80] -FileName=..\monsters.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit81] -FileName=..\monsters.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit82] -FileName=..\movement.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit83] -FileName=..\movement.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit84] -FileName=..\networkmessage.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit85] -FileName=..\networkmessage.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit86] -FileName=..\npc.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit87] -FileName=..\npc.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit88] -FileName=..\otpch.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit89] -FileName=..\otserv.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit90] -FileName=..\otsystem.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit91] -FileName=..\outfit.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit92] -FileName=..\outfit.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit93] -FileName=..\outputmessage.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit94] -FileName=..\outputmessage.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit95] -FileName=..\party.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit96] -FileName=..\party.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit97] -FileName=..\player.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit98] -FileName=..\player.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit99] -FileName=..\playerbox.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit100] -FileName=..\playerbox.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit101] -FileName=..\position.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit102] -FileName=..\position.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit103] -FileName=..\protocol.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit104] -FileName=..\protocol.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit105] -FileName=..\protocolgame.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit106] -FileName=..\protocolgame.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit107] -FileName=..\protocollogin.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit108] -FileName=..\protocollogin.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit109] -FileName=..\quests.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit110] -FileName=..\quests.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit111] -FileName=..\raids.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit112] -FileName=..\raids.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit113] -FileName=..\resources.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit114] -FileName=..\rsa.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit115] -FileName=..\rsa.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit116] -FileName=..\scheduler.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit117] -FileName=..\scheduler.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit118] -FileName=..\scriptmanager.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit119] -FileName=..\scriptmanager.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit120] -FileName=..\server.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit121] -FileName=..\server.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit122] -FileName=..\sha1.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit123] -FileName=..\sha1.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit124] -FileName=..\spawn.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit125] -FileName=..\spawn.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit126] -FileName=..\spells.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit127] -FileName=..\spells.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit128] -FileName=..\status.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit129] -FileName=..\status.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit130] -FileName=..\talkaction.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit131] -FileName=..\talkaction.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit132] -FileName=..\tasks.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit133] -FileName=..\tasks.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit134] -FileName=..\teleport.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit135] -FileName=..\teleport.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit136] -FileName=..\templates.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit137] -FileName=..\textlogger.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit138] -FileName=..\textlogger.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit139] -FileName=..\thing.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit140] -FileName=..\thing.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit141] -FileName=..\tile.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit142] -FileName=..\tile.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit143] -FileName=..\tools.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit144] -FileName=..\tools.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit145] -FileName=..\town.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit146] -FileName=..\trashholder.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit147] -FileName=..\trashholder.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit148] -FileName=..\vocation.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit149] -FileName=..\vocation.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit150] -FileName=..\waitlist.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit151] -FileName=..\waitlist.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit152] -FileName=..\weapons.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit153] -FileName=..\weapons.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit154] -FileName=..\ioban.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit155] -FileName=..\ioban.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit156] -FileName=..\gameservers.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit157] -FileName=..\globalevent.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit158] -FileName=..\globalevent.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit159] -FileName=..\gameservers.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit160] -FileName=..\databasemanager.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit161] -FileName=..\databasemanager.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit162] -FileName=..\admin.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit163] -FileName=..\admin.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit164] -FileName=..\databaseodbc.cpp -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit165] -FileName=..\databaseodbc.h -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit5] -FileName=..\allocator.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit6] -FileName=..\allocator.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit72] -FileName=..\mailbox.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit73] -FileName=..\mailbox.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit166] -FileName=..\databasepgsql.cpp -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit167] -FileName=..\databasepgsql.h -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit1] -FileName=..\waypoints.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit170] -FileName=..\group.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit171] -FileName=..\group.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit168] -FileName=..\itemattributes.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=0 - -[Unit169] -FileName=..\itemattributes.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=0 - -[VersionInfo] -Major=0 -Minor=3 -Release=6 -Build=3429 -LanguageID=1033 -CharsetID=1250 -CompanyName=OtLand.net -FileVersion=0.3.6 -FileDescription=The Forgotten Server -InternalName=Crying Damson -LegalCopyright= -LegalTrademarks= -OriginalFilename=Crying Damson.exe -ProductName=The Forgotten Server -ProductVersion=0.3.6 -AutoIncBuildNr=0 - diff --git a/dev-cpp/TheForgottenServer.dev b/dev-cpp/TheForgottenServer.dev index 2a48dcc..28bf1ac 100644 --- a/dev-cpp/TheForgottenServer.dev +++ b/dev-cpp/TheForgottenServer.dev @@ -1,8 +1,8 @@ [Project] FileName=TheForgottenServer.dev Name=TheForgottenServer -UnitCount=171 -Type=0 +UnitCount=161 +Type=1 Ver=1 ObjFiles= Includes= @@ -11,8 +11,8 @@ PrivateResource=TheForgottenServer_private.rc ResourceIncludes= MakeIncludes= Compiler= -CppCompiler=-D__USE_MYSQL___@@_-D__USE_SQLITE___@@_-D__ENABLE_SERVER_DIAGNOSTIC___@@_ -Linker=-O1_@@_-lboost_system_@@_-lgmp_@@_-llua5.1_@@_-lmysql_@@_-lboost_regex_@@_-lsqlite3_@@_-lwsock32_@@_-lxml2_@@_-lmysql_@@_-lws2_32_@@_-s_@@_-lboost_filesystem_@@_-lboost_thread_@@_ +CppCompiler=-D__USE_MYSQL___@@_-D__USE_SQLITE___@@_-D__ENABLE_SERVER_DIAGNOSTIC___@@_-O2_@@_ +Linker=-lboost_system_@@_-llua5.1_@@_-lmysql_@@_-lboost_regex_@@_-lsqlite3_@@_-lwsock32_@@_-lxml2_@@_-lmysql_@@_-lws2_32_@@_-s_@@_-lboost_filesystem_@@_-lboost_thread_@@_-lz_@@_-lcryptopp_@@_-leay32_@@_ IsCpp=1 Icon=TheForgottenServer.ico ExeOutput= @@ -29,8 +29,36 @@ SupportXPThemes=0 CompilerSet=0 CompilerSettings=0000000001100000000000 +[VersionInfo] +Major=0 +Minor=4 +Release=0 +Build=0 +LanguageID=1033 +CharsetID=1250 +CompanyName=OtLand.net +FileVersion=0.4 +FileDescription=The Forgotten Server +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename=The Forgotten Server.exe +ProductName=The Forgotten Server +ProductVersion=0.4 +AutoIncBuildNr=0 + +[Unit1] +FileName=..\actions.cpp +CompileCpp=1 +Folder=TheForgottenServer +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + [Unit2] -FileName=..\account.h +FileName=..\admin.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -40,17 +68,37 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\actions.cpp +FileName=..\allocator.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 Link=1 Priority=1000 OverrideBuildCmd=0 -BuildCmd=$(CPP) -c actions.cpp -o actions.o $(CXXFLAGS) +BuildCmd= [Unit4] -FileName=..\actions.h +FileName=..\baseevents.cpp +CompileCpp=1 +Folder=TheForgottenServer +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit5] +FileName=..\beds.cpp +CompileCpp=1 +Folder=TheForgottenServer +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit6] +FileName=..\chat.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -60,7 +108,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\baseevents.cpp +FileName=..\combat.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -70,7 +118,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\baseevents.h +FileName=..\condition.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -80,7 +128,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\beds.cpp +FileName=..\configmanager.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -90,7 +138,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\beds.h +FileName=..\connection.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -100,7 +148,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\chat.cpp +FileName=..\container.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -110,7 +158,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\chat.h +FileName=..\creature.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -120,7 +168,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\combat.cpp +FileName=..\creatureevent.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -130,7 +178,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\combat.h +FileName=..\cylinder.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -140,7 +188,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\protocolold.cpp +FileName=..\database.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -150,7 +198,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\protocolold.h +FileName=..\databasemanager.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -160,17 +208,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\condition.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit18] -FileName=..\condition.h +FileName=..\databasemysql.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -180,7 +218,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\configmanager.cpp +FileName=..\depot.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -190,7 +228,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\configmanager.h +FileName=..\dispatcher.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -200,7 +238,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\connection.cpp +FileName=..\exception.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -210,7 +248,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\connection.h +FileName=..\fileloader.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -220,7 +258,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\const.h +FileName=..\game.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -230,7 +268,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\container.cpp +FileName=..\gameservers.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -240,7 +278,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\container.h +FileName=..\globalevent.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -250,7 +288,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\creature.cpp +FileName=..\group.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -260,7 +298,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\creature.h +FileName=..\house.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -270,7 +308,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\creatureevent.cpp +FileName=..\housetile.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -280,7 +318,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\creatureevent.h +FileName=..\ioban.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -290,7 +328,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\cylinder.cpp +FileName=..\ioguild.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -300,7 +338,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\cylinder.h +FileName=..\iologindata.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -310,7 +348,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\database.cpp +FileName=..\iomap.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -320,7 +358,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\database.h +FileName=..\iomapserialize.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -330,7 +368,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\databasemysql.cpp +FileName=..\item.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -340,7 +378,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\databasemysql.h +FileName=..\itemattributes.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -350,7 +388,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\databasesqlite.cpp +FileName=..\items.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -360,7 +398,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\databasesqlite.h +FileName=..\luascript.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -370,7 +408,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\definitions.h +FileName=..\mailbox.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -380,7 +418,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\depot.cpp +FileName=..\manager.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -390,7 +428,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\depot.h +FileName=..\map.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -400,7 +438,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\enums.h +FileName=..\monster.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -410,7 +448,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\exception.cpp +FileName=..\monsters.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -420,7 +458,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\exception.h +FileName=..\movement.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -430,7 +468,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\fileloader.cpp +FileName=..\networkmessage.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -440,7 +478,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\fileloader.h +FileName=..\npc.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -450,7 +488,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\game.cpp +FileName=..\otserv.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -460,7 +498,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\game.h +FileName=..\outfit.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -470,7 +508,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\gui.cpp +FileName=..\outputmessage.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -480,7 +518,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\gui.h +FileName=..\party.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -490,17 +528,17 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\gui_resources.rc +FileName=..\player.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 -Link=0 +Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit51] -FileName=..\house.cpp +FileName=..\position.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -510,7 +548,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit52] -FileName=..\house.h +FileName=..\protocol.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -520,7 +558,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit53] -FileName=..\housetile.cpp +FileName=..\protocolgame.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -530,17 +568,17 @@ OverrideBuildCmd=0 BuildCmd= [Unit54] -FileName=..\housetile.h +FileName=..\protocolhttp.cpp +CompileCpp=1 Folder=TheForgottenServer Compile=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= -CompileCpp=1 [Unit55] -FileName=..\inputbox.cpp +FileName=..\protocollogin.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -550,7 +588,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit56] -FileName=..\inputbox.h +FileName=..\protocolold.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -560,7 +598,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit57] -FileName=..\ioguild.cpp +FileName=..\quests.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -570,7 +608,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit58] -FileName=..\ioguild.h +FileName=..\raids.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -580,7 +618,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit59] -FileName=..\iologindata.cpp +FileName=..\scheduler.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -590,7 +628,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit60] -FileName=..\iologindata.h +FileName=..\scriptmanager.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -600,7 +638,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit61] -FileName=..\iomap.cpp +FileName=..\server.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -610,7 +648,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit62] -FileName=..\iomap.h +FileName=..\spawn.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -620,7 +658,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit63] -FileName=..\iomapserialize.cpp +FileName=..\spells.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -630,7 +668,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit64] -FileName=..\iomapserialize.h +FileName=..\status.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -640,7 +678,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit65] -FileName=..\item.cpp +FileName=..\talkaction.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -650,7 +688,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit66] -FileName=..\item.h +FileName=..\teleport.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -660,7 +698,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit67] -FileName=..\itemloader.h +FileName=..\textlogger.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -670,7 +708,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit68] -FileName=..\items.cpp +FileName=..\thing.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -680,7 +718,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit69] -FileName=..\items.h +FileName=..\tile.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -690,7 +728,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit70] -FileName=..\luascript.cpp +FileName=..\tools.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -700,7 +738,27 @@ OverrideBuildCmd=0 BuildCmd= [Unit71] -FileName=..\luascript.h +FileName=..\trashholder.cpp +CompileCpp=1 +Folder=TheForgottenServer +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit72] +FileName=..\vocation.cpp +CompileCpp=1 +Folder=TheForgottenServer +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit73] +FileName=..\waitlist.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -710,7 +768,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit74] -FileName=..\map.cpp +FileName=..\weapons.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -720,7 +778,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit75] -FileName=..\map.h +FileName=..\account.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -730,7 +788,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit76] -FileName=..\md5.cpp +FileName=..\actions.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -740,7 +798,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit77] -FileName=..\md5.h +FileName=..\admin.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -750,7 +808,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit78] -FileName=..\monster.cpp +FileName=..\allocator.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -760,7 +818,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit79] -FileName=..\monster.h +FileName=..\baseevents.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -770,7 +828,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit80] -FileName=..\monsters.cpp +FileName=..\beds.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -780,7 +838,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit81] -FileName=..\monsters.h +FileName=..\chat.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -790,7 +848,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit82] -FileName=..\movement.cpp +FileName=..\combat.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -800,7 +858,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit83] -FileName=..\movement.h +FileName=..\condition.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -810,7 +868,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit84] -FileName=..\networkmessage.cpp +FileName=..\configmanager.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -820,7 +878,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit85] -FileName=..\networkmessage.h +FileName=..\connection.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -830,7 +888,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit86] -FileName=..\npc.cpp +FileName=..\const.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -840,7 +898,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit87] -FileName=..\npc.h +FileName=..\container.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -850,7 +908,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit88] -FileName=..\otpch.h +FileName=..\creature.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -860,7 +918,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit89] -FileName=..\otserv.cpp +FileName=..\creatureevent.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -870,7 +928,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit90] -FileName=..\otsystem.h +FileName=..\cylinder.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -880,7 +938,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit91] -FileName=..\outfit.cpp +FileName=..\database.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -890,7 +948,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit92] -FileName=..\outfit.h +FileName=..\databasemanager.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -900,7 +958,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit93] -FileName=..\outputmessage.cpp +FileName=..\databasemysql.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -910,7 +968,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit94] -FileName=..\outputmessage.h +FileName=..\databasesqlite.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -920,7 +978,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit95] -FileName=..\party.cpp +FileName=..\definitions.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -930,17 +988,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit96] -FileName=..\party.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit97] -FileName=..\player.cpp +FileName=..\depot.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -950,17 +998,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit98] -FileName=..\player.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit99] -FileName=..\playerbox.cpp +FileName=..\enums.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -970,7 +1008,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit100] -FileName=..\playerbox.h +FileName=..\fileloader.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -980,7 +1018,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit101] -FileName=..\position.cpp +FileName=..\game.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -990,7 +1028,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit102] -FileName=..\position.h +FileName=..\gameservers.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1000,7 +1038,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit103] -FileName=..\protocol.cpp +FileName=..\globalevent.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1010,7 +1048,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit104] -FileName=..\protocol.h +FileName=..\group.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1020,7 +1058,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit105] -FileName=..\protocolgame.cpp +FileName=..\house.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1030,7 +1068,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit106] -FileName=..\protocolgame.h +FileName=..\housetile.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1040,7 +1078,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit107] -FileName=..\protocollogin.cpp +FileName=..\ioban.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1050,7 +1088,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit108] -FileName=..\protocollogin.h +FileName=..\ioguild.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1060,7 +1098,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit109] -FileName=..\quests.cpp +FileName=..\iologindata.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1070,7 +1108,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit110] -FileName=..\quests.h +FileName=..\iomap.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1080,7 +1118,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit111] -FileName=..\raids.cpp +FileName=..\iomapserialize.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1090,7 +1128,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit112] -FileName=..\raids.h +FileName=..\item.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1100,7 +1138,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit113] -FileName=..\resources.h +FileName=..\itemattributes.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1110,7 +1148,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit114] -FileName=..\rsa.cpp +FileName=..\itemloader.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1120,7 +1158,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit115] -FileName=..\rsa.h +FileName=..\items.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1130,7 +1168,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit116] -FileName=..\scheduler.cpp +FileName=..\luascript.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1140,7 +1178,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit117] -FileName=..\scheduler.h +FileName=..\mailbox.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1150,7 +1188,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit118] -FileName=..\scriptmanager.cpp +FileName=..\manager.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1160,7 +1198,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit119] -FileName=..\scriptmanager.h +FileName=..\map.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1170,7 +1208,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit120] -FileName=..\server.cpp +FileName=..\monster.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1180,7 +1218,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit121] -FileName=..\server.h +FileName=..\monsters.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1190,7 +1228,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit122] -FileName=..\sha1.cpp +FileName=..\movement.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1200,7 +1238,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit123] -FileName=..\sha1.h +FileName=..\networkmessage.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1210,7 +1248,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit124] -FileName=..\spawn.cpp +FileName=..\npc.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1220,7 +1258,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit125] -FileName=..\spawn.h +FileName=..\otpch.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1230,7 +1268,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit126] -FileName=..\spells.cpp +FileName=..\otsystem.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1240,7 +1278,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit127] -FileName=..\spells.h +FileName=..\outfit.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1250,7 +1288,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit128] -FileName=..\status.cpp +FileName=..\outputmessage.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1260,7 +1298,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit129] -FileName=..\status.h +FileName=..\party.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1270,7 +1308,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit130] -FileName=..\talkaction.cpp +FileName=..\player.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1280,7 +1318,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit131] -FileName=..\talkaction.h +FileName=..\position.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1290,7 +1328,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit132] -FileName=..\tasks.cpp +FileName=..\protocol.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1300,7 +1338,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit133] -FileName=..\tasks.h +FileName=..\protocolgame.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1310,7 +1348,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit134] -FileName=..\teleport.cpp +FileName=..\protocolhttp.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1320,7 +1358,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit135] -FileName=..\teleport.h +FileName=..\protocollogin.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1330,7 +1368,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit136] -FileName=..\templates.h +FileName=..\protocolold.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1340,7 +1378,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit137] -FileName=..\textlogger.cpp +FileName=..\quests.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1350,7 +1388,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit138] -FileName=..\textlogger.h +FileName=..\raids.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1360,7 +1398,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit139] -FileName=..\thing.cpp +FileName=..\scheduler.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1370,7 +1408,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit140] -FileName=..\thing.h +FileName=..\scriptmanager.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1380,7 +1418,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit141] -FileName=..\tile.cpp +FileName=..\server.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1390,7 +1428,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit142] -FileName=..\tile.h +FileName=..\spawn.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1400,7 +1438,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit143] -FileName=..\tools.cpp +FileName=..\spells.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1410,7 +1448,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit144] -FileName=..\tools.h +FileName=..\status.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1420,7 +1458,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit145] -FileName=..\town.h +FileName=..\talkaction.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1430,7 +1468,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit146] -FileName=..\trashholder.cpp +FileName=..\teleport.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1440,7 +1478,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit147] -FileName=..\trashholder.h +FileName=..\templates.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1450,7 +1488,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit148] -FileName=..\vocation.cpp +FileName=..\textlogger.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1460,7 +1498,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit149] -FileName=..\vocation.h +FileName=..\thing.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1470,7 +1508,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit150] -FileName=..\waitlist.cpp +FileName=..\tile.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1480,7 +1518,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit151] -FileName=..\waitlist.h +FileName=..\tools.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1490,7 +1528,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit152] -FileName=..\weapons.cpp +FileName=..\town.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1500,7 +1538,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit153] -FileName=..\weapons.h +FileName=..\trashholder.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1510,7 +1548,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit154] -FileName=..\ioban.cpp +FileName=..\vocation.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1520,7 +1558,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit155] -FileName=..\ioban.h +FileName=..\waitlist.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1530,7 +1568,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit156] -FileName=..\gameservers.h +FileName=..\waypoints.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1540,67 +1578,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit157] -FileName=..\globalevent.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit158] -FileName=..\globalevent.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit159] -FileName=..\gameservers.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit160] -FileName=..\databasemanager.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit161] -FileName=..\databasemanager.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit162] -FileName=..\admin.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit163] -FileName=..\admin.h +FileName=..\weapons.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1609,28 +1587,8 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit164] -FileName=..\databaseodbc.cpp -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit165] -FileName=..\databaseodbc.h -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit5] -FileName=..\allocator.cpp +[Unit18] +FileName=..\databasesqlite.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1639,8 +1597,8 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit6] -FileName=..\allocator.h +[Unit97] +FileName=..\dispatcher.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1649,8 +1607,8 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] -FileName=..\mailbox.cpp +[Unit99] +FileName=..\exception.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1659,8 +1617,8 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] -FileName=..\mailbox.h +[Unit158] +FileName=..\mounts.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1669,28 +1627,8 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit166] -FileName=..\databasepgsql.cpp -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit167] -FileName=..\databasepgsql.h -CompileCpp=0 -Folder=TheForgottenServer -Compile=0 -Link=0 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd= - -[Unit1] -FileName=..\waypoints.h +[Unit159] +FileName=..\mounts.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1699,8 +1637,8 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit170] -FileName=..\group.cpp +[Unit160] +FileName=..\iomarket.cpp CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1709,8 +1647,8 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit171] -FileName=..\group.h +[Unit161] +FileName=..\iomarket.h CompileCpp=1 Folder=TheForgottenServer Compile=1 @@ -1718,42 +1656,3 @@ Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= - -[Unit168] -FileName=..\itemattributes.cpp -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=0 - -[Unit169] -FileName=..\itemattributes.h -CompileCpp=1 -Folder=TheForgottenServer -Compile=1 -Link=1 -Priority=1000 -OverrideBuildCmd=0 -BuildCmd=0 - -[VersionInfo] -Major=0 -Minor=3 -Release=6 -Build=3429 -LanguageID=1033 -CharsetID=1250 -CompanyName=OtLand.net -FileVersion=0.3.6 -FileDescription=The Forgotten Server -InternalName=Crying Damson -LegalCopyright= -LegalTrademarks= -OriginalFilename=Crying Damson.exe -ProductName=The Forgotten Server -ProductVersion=0.3.6 -AutoIncBuildNr=0 - diff --git a/dev-cpp/TheForgottenServer_private.h b/dev-cpp/TheForgottenServer_private.h index 4faa146..783fc1e 100644 --- a/dev-cpp/TheForgottenServer_private.h +++ b/dev-cpp/TheForgottenServer_private.h @@ -5,19 +5,19 @@ #define THEFORGOTTENSERVER_PRIVATE_H /* VERSION DEFINITIONS */ -#define VER_STRING "0.3.6.3293" +#define VER_STRING "0.4.0.0" #define VER_MAJOR 0 -#define VER_MINOR 3 -#define VER_RELEASE 6 -#define VER_BUILD 3293 +#define VER_MINOR 4 +#define VER_RELEASE 0 +#define VER_BUILD 0 #define COMPANY_NAME "OtLand.net" -#define FILE_VERSION "0.3.6" +#define FILE_VERSION "0.4" #define FILE_DESCRIPTION "The Forgotten Server" -#define INTERNAL_NAME "Crying Damson" +#define INTERNAL_NAME "" #define LEGAL_COPYRIGHT "" #define LEGAL_TRADEMARKS "" -#define ORIGINAL_FILENAME "Crying Damson.exe" +#define ORIGINAL_FILENAME "The Forgotten Server.exe" #define PRODUCT_NAME "The Forgotten Server" -#define PRODUCT_VERSION "0.3.6" +#define PRODUCT_VERSION "0.4" #endif /*THEFORGOTTENSERVER_PRIVATE_H*/ diff --git a/dev-cpp/TheForgottenServer_private.rc b/dev-cpp/TheForgottenServer_private.rc index ce5e12f..f4776d6 100644 --- a/dev-cpp/TheForgottenServer_private.rc +++ b/dev-cpp/TheForgottenServer_private.rc @@ -2,7 +2,7 @@ /* DO NOT EDIT! */ #include // include for version info constants -#include "../gui_resources.rc" + A ICON MOVEABLE PURE LOADONCALL DISCARDABLE "TheForgottenServer.ico" @@ -10,23 +10,23 @@ A ICON MOVEABLE PURE LOADONCALL DISCARDABLE "TheForgottenServer.ico" // TO CHANGE VERSION INFORMATION, EDIT PROJECT OPTIONS... // 1 VERSIONINFO -FILEVERSION 0,3,6,3293 -PRODUCTVERSION 0,3,6,3293 +FILEVERSION 0,4,0,0 +PRODUCTVERSION 0,4,0,0 FILETYPE VFT_APP { BLOCK "StringFileInfo" { - BLOCK "040904E4" + BLOCK "040904E2" { VALUE "CompanyName", "OtLand.net" - VALUE "FileVersion", "0.3.6" + VALUE "FileVersion", "0.4" VALUE "FileDescription", "The Forgotten Server" - VALUE "InternalName", "Crying Damson" + VALUE "InternalName", "" VALUE "LegalCopyright", "" VALUE "LegalTrademarks", "" - VALUE "OriginalFilename", "Crying Damson.exe" + VALUE "OriginalFilename", "The Forgotten Server.exe" VALUE "ProductName", "The Forgotten Server" - VALUE "ProductVersion", "0.3.6" + VALUE "ProductVersion", "0.4" } } BLOCK "VarFileInfo" diff --git a/tasks.cpp b/dispatcher.cpp similarity index 94% rename from tasks.cpp rename to dispatcher.cpp index 0a3013c..5341396 100644 --- a/tasks.cpp +++ b/dispatcher.cpp @@ -15,7 +15,7 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" -#include "tasks.h" +#include "dispatcher.h" #include "outputmessage.h" #if defined __EXCEPTION_TRACER__ @@ -23,15 +23,15 @@ #endif #include "game.h" + extern Game g_game; Dispatcher::DispatcherState Dispatcher::m_threadState = Dispatcher::STATE_TERMINATED; Dispatcher::Dispatcher() { - m_taskList.clear(); Dispatcher::m_threadState = Dispatcher::STATE_RUNNING; - boost::thread(boost::bind(&Dispatcher::dispatcherThread, (void*)this)); + m_thread = boost::thread(boost::bind(&Dispatcher::dispatcherThread, (void*)this)); } void Dispatcher::dispatcherThread(void* p) @@ -41,7 +41,6 @@ void Dispatcher::dispatcherThread(void* p) ExceptionHandler dispatcherExceptionHandler; dispatcherExceptionHandler.InstallHandler(); #endif - srand((uint32_t)OTSYS_TIME()); OutputMessagePool* outputPool = NULL; boost::unique_lock taskLockUnique(dispatcher->m_taskLock, boost::defer_lock); @@ -99,7 +98,7 @@ void Dispatcher::addTask(Task* task, bool front/* = false*/) } #ifdef __DEBUG_SCHEDULER__ else - std::cout << "[Error - Dispatcher::addTask] Dispatcher thread is terminated." << std::endl; + std::clog << "[Error - Dispatcher::addTask] Dispatcher thread is terminated." << std::endl; #endif m_taskLock.unlock(); diff --git a/tasks.h b/dispatcher.h similarity index 97% rename from tasks.h rename to dispatcher.h index fcba679..628f6b9 100644 --- a/tasks.h +++ b/dispatcher.h @@ -70,6 +70,7 @@ class Dispatcher void stop(); void shutdown(); + void exit() {m_thread.join();} static void dispatcherThread(void* p); @@ -84,6 +85,7 @@ class Dispatcher STATE_TERMINATED }; + boost::thread m_thread; boost::mutex m_taskLock; boost::condition_variable m_taskSignal; diff --git a/doc/ACTION_IDS b/doc/ACTION_IDS index 86b9721..5dad19b 100644 --- a/doc/ACTION_IDS +++ b/doc/ACTION_IDS @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 32468fe..ecd7c54 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -705,6 +705,14 @@ // ] ] + [ 0.3.7 + [ Note + Almost all files were changed, so there is + no point of listing them in here. + + You are advised to update all your data files. + ] + } ] [ Milestones @@ -1126,4 +1134,84 @@ and much more ... (IT IS really stable & bug free!) ] -] + [ 0.3.7 + Protocol 9.2 support (Talaturen, Dalkon, Elf) + Mounts system with mounts.xml (Talaturen, Elf) + New spell cooldown system (Elf, Dalkon, Talaturen) + Orange skull (Elf) + MD5, SHA1 and RSA implementations improved using OpenSSL (Dalkon) + LuaJIT support, up to 130 times faster Lua scripts (Talaturen, Elf) + A lot of Lua stack errors fixed (Elf, Talaturen) + Lua function doRelocate is now approximately 100 times faster (Elf) + New configurable variables in config.lua (Dalkon, Elf, slawkens, Talaturen, Don Daniello): + separateVipListPerCharacter + vipListDefaultLimit + vipListDefaultPremiumLimit + globalSaveMinute + staminaLootLimit + fistBaseAttack + logsDirectory + tileLimit + protectionTileLimit (previously maxItemsPerPZTile) + houseTileLimit (previously maxItemsPerHouseTile) + bindOnlyGlobalAddress (previously bindOnlyConfiguredIpAddress) + rsaModulus + truncateLogOnStartup (previously truncateLogsOnStartup) + generateAccountSalt + tradeLimit + unifiedSpells + savePlayerData + enableCooldowns + useMounts + houseDataStorage (previously related to useHouseDataStorage) + mountCooldown + unmountPlayerInPz + pvpBlessingThreshold + fairFightTimeRange + monsterSpawnWalkback + useCapacity + defaultDepotSizePremium + defaultDepotSize + daemonize + skipItemsVersionCheck. + New Lua functions (Elf, slawkens, Dalkon, Talaturen): + std.sha256(string[, upperCase]) + std.sha512(string[, upperCase]) + unregisterCreatureEvent(uid, eventName) + errors(var) + getTownList() + db.query(query) (previously known as db.executeQuery) + getPlayerTradeState(cid) + getPlayerModes(cid) + doPlayerFollowCreature(cid) + doPlayerLeaveParty(cid[, forced = false]) + getItemParent(uid) + db.stringComparer() (previously known as stringComparison) + os.mtime() + getChannelList() + doSteerCreature(cid, position[, maxNodes]) + getVocationList() + getGroupList() + doPlayerOpenChannel(cid, channelId) + getContainerItems(uid) + doPlayerChangeName(guid, oldName, newName) + db.connected() + getCreatureStorageList(cid) + getStorageList() + doPlayerAddMount(cid, mountId) + doPlayerRemoveMount(cid, mountId) + getPlayerMount(cid, mountId) + doPlayerSetMount(cid, mountId) + doPlayerSetMountStatus(cid, mounted) + getMountInfo([mountId]) + doPlayerSetWalkthrough(cid, uid, walkthrough) + getPlayerPVPBlessing(cid) + doPlayerSetPVPBlessing(cid[, value]) + doAddAccountWarnings(accountId[, warnings]) + getAccountWarnings(accountId) + Improved autobuild scripts for Linux (Elf, Fallen) + Fixed compiling in MSVC and added project files (Dalkon) + PVP Blessings (Elf) + A lot of miscellaneous cleanups, fixes and optimizations (Dalkon, Elf, slawkens, Talaturen) + } +] \ No newline at end of file diff --git a/doc/COMMANDS_HELP b/doc/COMMANDS_HELP index 2cc5f40..cd1ebbd 100644 --- a/doc/COMMANDS_HELP +++ b/doc/COMMANDS_HELP @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -37,16 +37,49 @@ /attr Changes thing properties. Parameters - type, value + [command[, type, ]]attribute, value Values - type - * Items - set - erase + command + * Items only + set/add/new + erase/remove/delete action/actionid/aid unique/uniqueid/uid - destination/position/pos/destpos + destination/position/location/pos/dest/loc + type + * Items only - set/add/new & erase/remove/delete + bool/boolean + int/integer/num/number + float/double + string/text/* + attribute + * Items - set/add/new & erase/remove/delete + duration - number + description - string + text - string + date - number + writer - string + charges - number + fluidtype - number + owner - number + corpseowner - number + decaying - number + name - string + pluralname - string + article - string + scriptprotected - bool + attack - number + extraattack - number + defense - number + extradefense - number + armor - number + attackspeed - number + hitchance - number + shootrange - number + dualwield - bool + depotid - number + sleepstart - number * Creatures health maxhealth @@ -57,27 +90,31 @@ lossskill cannotmove skull + shield + emblem speaktype * Players fyi tutorial + guildlevel guildrank guildnick group vocation - sex - stamina - town + sex/gender + town/temple + marriage/partner balance - marriage rates - resetidle + idle + stamina + capacity/cap execute - saving + saving/save Examples /attr aid 300 - /attr set text "Hello guys + /attr set string text "Hello guys" /attr skull 2 /serverdiag @@ -94,11 +131,11 @@ /shutdown Saves & Shutdowns server. Parameters - time/kill/stop + time/kill/cancel Examples /shutdown 3 (Shutdown after 3 minutes) - /shutdown stop (Stop shutdown event) + /shutdown cancel (Cancel shutdown event) /shutdown kill (Exit without save) /shutdown (Immediately) @@ -106,10 +143,10 @@ Changes world's pvp type. Parameters pvpType - nopvp/pvp/pvpenforced + optional/open/hardcore Example - /mode nopvp + /mode hardcode /promote & /demote Promote or demote specified player to higher/lower group. @@ -157,7 +194,7 @@ actions/channels/config/creatureevents/gameservers/globalevents groups/highscores/houseprices/items/monsters/moveevents/npcs outfits/quests/raids/spells/stages/talkactions/vocations/weapons - all + mods, all Examples /reload spells @@ -175,16 +212,17 @@ /newtype Outfit changer. Parameters - lookType, playerName + lookType/monster/item, playerName Examples /newtype 266 + /newtype Demon + /newtype 2160 (Changes outfit to crystal coin) /newtype 13, Eternal Oblivion /r Removes an item or a creature from a tile. - Notice: Player is a creature so you may use it - with players. + NOTE: Player is also a creature. (Monster, npc too!) Parameters amount/all @@ -267,7 +305,7 @@ Examples !y Hello - !y 50, Hi! Colored text ;-) + !y 50 Hi! Colored text ;-) /bc Broadcast message in color. @@ -277,8 +315,8 @@ message Examples: - /bc green;Hello! - /bc Hiho + /bc Hiho + /bc green Hello! (This one will be green) /mkick Mass kick. @@ -312,8 +350,7 @@ /c Teleports a creature to you. - Notice: Player is a creature so you may use it - with players + NOTE: Player is also a creature. (Monster, npc too!) Example /c Demon @@ -325,8 +362,8 @@ Examples /goto 123,60,7 - /goto eryn - /goto dragon + /goto Eryn + /goto Dragon /goto GM Tester /a @@ -393,6 +430,12 @@ /save Saves server. + Parameters + playerName + + Examples + /save (saves whole server, including map) + /save GM Tester (saves only single character) /clean Cleans map or tile. @@ -407,7 +450,7 @@ /clean tile (Clean tile you're standing on) /reports - Displays server reports. + Displays server reports (these reported by CTRL + Z). Parameters reportId @@ -425,6 +468,16 @@ /mc 127.0.0.1 (Display all players with IP = 127.0.0.1) /mc Tester (Display all players with same IP as Tester) + /mute + Mute player. + Parameters + playerName + time (in seconds) + + Examples + /mute Tester (mute forever) + /mute Tester, 30 (mute for 30 seconds) + /wp Displays map waypoints. @@ -446,7 +499,7 @@ Examples /gethouse GM Tester (get house name) - /gethouse GM Tester, yes (get house name and teleport) + /gethouse GM Tester, yes (get house name and teleport there) /b Broadcast message in red color. @@ -492,7 +545,7 @@ !q Checks money value in your inventory. - Notice: It isn't counting the money in depots/houses. + NOTE: It isn't counting the money in depots/houses. !serverinfo Displays server information such as experience rate and loot rate. diff --git a/doc/CONFIG_HELP b/doc/CONFIG_HELP index 0fe4ce5..16dd8a2 100644 --- a/doc/CONFIG_HELP +++ b/doc/CONFIG_HELP @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -16,55 +16,65 @@ ] [ ABOUT - List with all, possible to use in config.lua file, config values. - It contains info, possible and default value. + List of all possible config options, containing default values and additional informations. + Config file is loaded at server startup and can be reloaded while server is running. + However, there are few options that cannot be reloaded. ] [ LIST *ACCOUNT MANAGER accountManager Defines if account manager should be enabled. - Disable it if you're using website and don't need such feature (it's a part of AAC). - Default: yes + Should be disabled if you're using website to manage accounts. + Default: true namelockManager Defines if namelock manager should be enabled. If you enable this feature, player who has been namelocked will be able to login on account manager and change their names. - Default: no + Default: true newPlayerChooseVoc - Ignore if your account manager is disabled. - Default: no + Ignored if account manager is disabled. + Default: false newPlayerSpawnPosX - Ignore if your account manager is disabled. - Default: 100 + Ignored if account manager is disabled. + Default: 95 newPlayerSpawnPosY - Ignore if your account manager is disabled. - Default: 100 + Ignored if account manager is disabled. + Default: 117 newPlayerSpawnPosZ - Ignore if your account manager is disabled. + Ignored if account manager is disabled. Default: 7 newPlayerTownId - Ignore if your account manager is disabled. + Ignored if account manager is disabled. Default: 1 newPlayerLevel - Ignore if your account manager is disabled. + Ignored if account manager is disabled. Default: 1 newPlayerMagicLevel - Ignore if your account manager is disabled. + Ignored if account manager is disabled. Default: 0 generateAccountNumber - Default: yes + Ignored if account manager is disabled. + Default: false + + generateAccountSalt + Ignored if account manager is disabled. + Default: false *UNJUSTIFIED KILLS + useFragHandler + If not active, unjustified kills are not recorded. + Default: true + redSkullLength Default: 30 * 24 * 60 * 60 @@ -106,14 +116,10 @@ useBlackSkull New feature from 8.5. If you'll use it, player won't be banished but instead they receive black skull which block some actions. - Default: yes + Default: true advancedFragList - Default: no - - useFragHandler - Enable or disable hardcoded frag handler. - Default: yes + Default: false *BANISHMENTS notationsToBan @@ -121,9 +127,11 @@ Default: 3 warningsToFinalBan + Warnings until a final banishment. Default: 4 warningsToDeletion + Warnings until character deletion. Default: 5 banLength @@ -136,17 +144,16 @@ Default: 7 * 24 * 60 * 60 (7 days) finalBanLength + Final banishment length, pretty obvious. Default: 30 * 24 * 60 * 60 (30 days) ipBanishmentLength + IP Banishment length. Default: 1 * 24 * 60 * 60 (1 day) broadcastBanishments Specify if every banishment should be broadcasted (red message). - Default: yes - - maxViolationCommentSize - Default: 200 + Default: true violationNameReportActionType 1 = just a report, 2 = name lock, 3 = player banishment @@ -155,12 +162,16 @@ autoBanishUnknownBytes Specify if player should be banned automatically in case his client send unknown byte to server. It happens mostly when player use unofficial software to play. - Default: no + Default: false + + allowedMaxSizedPackets + How many times a packet can be sent at full size before the connection is dropped + Default: 3 *BATTLE worldType - Values: pvp, no-pvp, pvp-enforced - Default: pvp + Values: open, optional, hardcore + Default: open protectionLevel To this level players are protected. @@ -172,7 +183,7 @@ If it's enabled and players are fighting on pvpTiles: Some of them have lower level than protectionLevel - they're able to fight. Some of them have protected vocation (E.q. none vocation) - they're able to fight. - Default: yes + Default: true pzLocked Time needed to wait after attacking a player. @@ -193,53 +204,52 @@ displayCriticalHitNotify Display text effect when critical hit occurs? ("CRITICAL!") - Default: no + Default: false removeWeaponAmmunition Should player ammunition be removed? Ex. bolts - should remove their charges when used? - Default: yes + Default: true removeWeaponCharges - Default: yes + Should enchanted weapons' charges be removed? + Default: true removeRuneCharges Should rune charges be removed when using? No, means that they will have infinite charges. - Default: yes + Default: true whiteSkullTime - In miliseconds. + When gaining white skull status, how long should it stay? + (In milliseconds.) Default: 15 * 60 * 1000 (15 minutes) noDamageToSameLookfeet If enabled, players with same outfit feet color will be unable to attack themselfes. It works only for melee attacks. - Default: no + Default: false showHealingDamage Display effect with health points gained when creature was healed? - Default: no + Default: false showHealingDamageForMonsters Should healing effect was also displayed for monsters? - Default: yes + Default: false fieldOwnershipDuration How long a creature is an owner of a field item created by him. - In miliseconds. + (In milliseconds.) Default: 5 * 1000 stopAttackingAtExit If enabled, when player exits the game without logout, he'll stop attacking already attacked creatures. Good feature when you have trainers and want to disable a possibilty to train after closing Tibia by exit. - Default: no - - oldConditionAccuracy - Default: no + Default: false loginProtectionPeriod The famous Tibia anti-magebomb system Amount of time during which player is unable to attack other players after his login. - In miliseconds. + (In milliseconds.) Default: 10 * 1000 (10 seconds) deathLostPercent @@ -249,12 +259,12 @@ stairhopDelay Feature introduced in 8.41 global tibia update. Specify time, which player need to wait after changing floor/teleport etc, before he is able to attack monster. - In miliseconds. + (In milliseconds.) Default: 2 * 1000 pushCreatureDelay How fast creatures are pushed. - In miliseconds. + (In milliseconds.) Default: 2 * 1000 deathContainerId @@ -270,7 +280,7 @@ addManaSpentInPVPZone Should player be able to train magic level in PVP zones? Protection against mana training in PVP Arena, where mana is re-newed when player dies. - Default: yes + Default: true squareColor Color of square visible when attacking creature. @@ -279,7 +289,11 @@ allowFightback System introduced in 8.5, if you enable it then players who defend themselves from the attacks of other players do not get a protection zone block. - Default: yes + Default: true + + fistBaseAttack + Like attack on a weapon, except you are using your fists (No weapon) + Default: 7 *CONNECTION worldId @@ -291,7 +305,9 @@ Default: 127.0.0.1 bindOnlyConfiguredIpAddress - Default: no + Defines if a server should bind to all available network interfaces on the machine. + Useful for ip failover configurations, otherwise default option is alright. + Default: false loginPort Port where login server will listen. @@ -305,25 +321,17 @@ so please keep in mind the default configuration that is easy and works. Default: 7172 - adminPort - Port where admin protocol will listen. - Default: 7171 - - statusPort - Port where status protocol will listen. - Default: 7171 - loginTries How much tries specified IP address has to login. - E.g. after 10 wrong passwords, IP will be blocked until next server restart. - Default: 10 + E.g. after 3 wrong passwords, IP will be blocked until next server restart. + Default: 3 retryTimeout - In miliseconds. + (In milliseconds.) Default: 5 * 1000 loginTimeout - In miliseconds. + (In milliseconds.) Default: 60 * 1000 maxPlayers @@ -338,18 +346,19 @@ displayOnOrOffAtCharlist Should character status be specified when displaying characters list? (Online/Offline) - If enabled, serverName won't be displayed on list. - Default: no + If enabled, serverName won't be displayed on the list. + Default: false onePlayerOnlinePerAccount If enabled, only one player per account will be able to be online. - Default: yes + Default: true allowClones - Allow multiple client logins on one character. - Mostly used on pvp-enfo servers where you have a few characters + How many different clients can be logged on one character simultaneously. + Mostly used on pvp-enfo servers where you have a few common characters. and all of them are able to login on one character, e.g 'Master Sorcerer' - Default: no + 0 to disable (means 0 additional clients can login simultaneously) + Default: 0 serverName Default: Forgotten @@ -361,82 +370,126 @@ statusTimeout Minimal interval for specified IP address to request server status again. Use lower value, if you use website which is requesting status frequently. - In miliseconds. - Default: 5 * 60 * 1000 (5 hours) + (In milliseconds.) + Default: 5 * 60 * 1000 (5 minutes) replaceKickOnLogin - If player is online and other player tries to connect, - should player in game be kicked and new client be able to connect and replace him? - Default: yes + If character is online and other client tries to connect, + should client in game be kicked and new client be able to connect replacing old one? + Default: true forceSlowConnectionsToDisconnect Clients with slow connection will be disconnected automatically. - Default: no + Default: false loginOnlyWithLoginServer Enable it only if you're using multi world system. This is just a game server and you won't enable to get characters list. - Default: no + Default: false premiumPlayerSkipWaitList - Default: no + When there are too many players online, + this determines if premium players can login when the players online count is max. + Default: false - *DATABASE - NOTE: sqlFile is used only by sqlite database, and sqlKeepAlive by mysql database. - To disable sqlKeepAlive such as mysqlReadTimeout use 0 value. + *RSA + NOTE: These should not be changed unless you know what your doing! + Prime1 - known as p; Prime2 - known as q; Public - known as e; + Modulus - known as n; Private - known as d. + + rsaPrime1 + Default: 14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113 + + rsaPrime2 + Default: 7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101 + + rsaPublic + Default: 65537 + rsaModulus + Default: 109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413 + + rsaPrivate + Default: 46730330223584118622160180015036832148732986808519344675210555262940258739805766860224610646919605860206328024326703361630109888417839241959507572247284807035235569619173792292786907845791904955103601652822519121908367187885509270025388641700821735345222087940578381210879116823013776808975766851829020659073 + + *DATABASE sqlType Database storage engine. Values: mysql, odbc, sqlite, pgsql Default: sqlite sqlHost + If you have problems connecting to a local database server, try using "127.0.0.1" as a host. + Default value should work on in most cases, however it was reported by our users + that they had problems connecting and this solution worked, + so you may save some time trying this one. + Not used by sqlite database. Default: localhost sqlPort + Port used to connect to the database server. + Not used by sqlite database. Default: 3306 sqlUser + Not used by sqlite database. Default: root sqlPass - Default: "" + Not used by sqlite database. + Default: "" (means there is no password at all!) sqlDatabase + Not used by sqlite database. Default: theforgottenserver sqlFile - Used only by sqlite database. + The only one configurable required for sqlite database storage. + Whole data used by the server is stored there (if using sqlite storage method). Default: forgottenserver.s3db sqlKeepAlive Used only by mysql database. - Use 0 to disable. + How often server should connect to the mysql database and send dummy packet (ping) to refresh its connection timeout. + Useful when there are not a lot of queries send to the server, otherwise it is better to keep this option disabled. + In seconds, 0 to disable. Default: 0 mysqlReadTimeout - Use 0 to disable. + "The timeout in seconds for attempts to read from the server. + Each attempt uses this timeout value and there are retries if necessary, so the total effective timeout value is three times the option value. + You can set the value so that a lost connection can be detected earlier than the TCP/IP Close_Wait_Timeout value of 10 minutes" + (from mysql manual, original source at: http://dev.mysql.com/doc/refman/5.5/en/mysql-options.html) + 0 to disable. Default: 10 mysqlWriteTimeout - Use 0 to disable. + "The timeout in seconds for attempts to write to the server. Each attempt uses this timeout value [...]" + (from mysql manual, original source at: http://dev.mysql.com/doc/refman/5.5/en/mysql-options.html) + 0 to disable. Default: 10 + + mysqlReconnectionAttempts + How many times mysql is allowed to attempt and reconnect to the server. + 0 for unlimited attempts. + Default: 3 encryptionType - Defines how passwords should be stored in database. - *WARNING: Using plain passwords is not recommented!* - Values: plain, md5, sha1 - Default: plain + Encryption method used to store passwords (and other sensitive data) safely in the database. + WARNING: Using plain passwords is not recommended and may lead to serious data leakage. + Values: md5, sha1, sha256, sha512, plain + Default: sha256 *DEATHLIST deathListEnabled - Should players' deaths be storaged in database? - Default: yes + Should players' deaths be stored in database? + Default: true deathListRequiredTime + (In milliseconds.) Default: 1 * 60 * 1000 deathAssistCount - Default: 20 + Default: 19 maxDeathRecords Player deathlist limit. @@ -445,7 +498,7 @@ *GUILDS ingameGuildManagement - Default: "yes" + Default: true levelToFormGuild Level required to form a guild. @@ -459,7 +512,7 @@ Minimum length of a guild name. Default: 4 - guildNameMaxLength = 20 + guildNameMaxLength Maximum length of a guild name. Default: 20 @@ -470,23 +523,23 @@ updateHighscoresAfterMinutes How often highscores list will be updated. - In minutes. + (In minutes.) Default: 60 *HOUSES buyableAndSellableHouses Ingame house system enabled? - Default: yes + Default: true houseNeedPremium Is premium required to buy a house? NOTE: Houses are cleaned everytime server start, so if this option is enabled and player don't have premium - house will be removed. (If houseRentPeriod != never) - Default: yes + Default: true bedsRequirePremium Do players need premium account to use beds? - Default: yes + Default: true levelToBuyHouse Level needed to buy a house. @@ -497,13 +550,14 @@ Default: 0 (means no limit!) houseRentAsPrice - Default: no + Default: false housePriceAsRent - Default: no + Default: false housePriceEachSquare - House price + House price for each square. + Final price for a house is: number of squares * price Default: 1000 houseRentPeriod @@ -512,39 +566,41 @@ houseCleanOld Defines if houses should be auto cleaned when player is inactive (Not logging in specified amount of time) - In seconds, 0 to disable. + (In seconds, 0 to disable.) Default: 0 guildHalls - Use guild halls system, allowing to buy guild hall only bu guild leader. All members will be auto invited. + Use guild halls system, allowing to buy guild hall only by guild leader. All members will be auto invited. You can mark house as guild hall in map editor. - Default: no + Default: false *ITEM USAGE timeBetweenActions + How long you have to wait from your recent action to the next one. + (In milliseconds.) Default: 200 timeBetweenExActions Time player need to wait before using next item. + (In milliseconds.) Default: 1000 - checkCorpseOwner - Defines if corpse should be checked for owner before opening. - So for first x seconds, only creature killer is able to open it. - Default: yes - hotkeyAimbotEnabled Should system with attacking via hotkeys and battle window be enabled? (Cipsoft antibot features) - Default: yes - - maximumDoorLevel - Default: 500 + Default: true + + tibiaClassicSlots + Should system use the classical Tibia slots allowing global use of the 'arrow' slot? + Default: true + + canOnlyRopePlayers + Should players only be allowed to rope players? (false: allow players to rope monsters, items, and players) + Default: true *MAP mapName - Map name which server will load. - NOTE: if map file is named 'somemap.otbm' then just paste here 'somemap'. - Default: forgotten + Map file name. Have to be located in world/ directory. + Default: forgotten.otbm mapAuthor Map author. Used by status protocol (ots lists) @@ -553,36 +609,36 @@ randomizeTiles Specify if all items should be randomized while creating (like grass). You can configure this in data/XML/randomization.xml file. - Default: yes - - useHouseDataStorage - New house data storage system. Performs much faster map saving. (up to 60x faster!) - If enabled - items will be saved binary in `house_data` table, otherwise `tiles` and `tile_items` will be used. - Default: no + Default: true storeTrash Define if trash items should be stored in memory. It costs more memory but will perform a lot faster cleaning. Otherwise, if you disable this option, cleaning will be performed by going through all the tiles and looking for trash. (which of course consumes more CPU resources) - Default: yes + Default: true cleanProtectedZones Should protection zone tiles be cleaned at map cleaning? - Default: yes + Default: true mailboxDisabledTowns Town IDs where player can't send parcel/letters. Example "1,5,9" - Default: -1 (Means, no towns disabled) + Default: "" (Means, no towns disabled) + + *PROCESS + daemonize + Works only on *nix. + Default: false - *STARTUP defaultPriority - Works only on Windows + Works only on Windows. Values: high, higher, realtime Default: high niceLevel + Works only on *nix. Default: 5 coresUsed @@ -592,81 +648,172 @@ and want TFS to use all of its cores) Default: -1 - optimizeDatabaseAtStartup + *STARTUP + startupDatabaseOptimization Should be database optimized at server startup? - Recommended - yes! - Default: yes + Recommended - true + Default: true - removePremiumOnInit + updatePremiumStateAtStartup Should accounts premium time be updated at server start-up? - Default: yes + Default: true confirmOutdatedVersion Decides if server should display a box (GUI) or a message (console) while starting up when current version is not up-to-date - and wait for user interaction (yes/no) until continuing start-up. - Default: yes + and wait for user interaction (true/false) until continuing start-up. + Default: false - *MUTED BUFFER - maxMessageBuffer - Default: 4 + skipItemsVersionCheck + Should be items.otb version check skipped ? + If set to true, accepts all minor versions of items.otb. + Default: false + + *SPELLS + formulaLevel + Level formula for spells if not defined. + Default: 5.0 + + formulaMagic + Magic level formula for spells if not defined. + Default: 1.0 bufferMutedOnSpellFailure - Default: no + Default: false - *MISCELLANEOUS - dataDirectory - Directory, from where all data used be server is loaded. - Default: data/ + spellNameInsteadOfWords + E.g if you use 'exura', it will show 'Light Healing'. + Default: false + emoteSpells + If enabled, spells wont be visible in 'Default' channel, but as orange effect. + Anti spam feature. + Default: false + + unifiedSpells + In the screen and the default channel it will show only the name of the spell. + For example: "Ex Ura" or "exura ""im the best sorcer" will be shown as exura. + In global Tibia it's enabled. (it has been added in 8.6 update) + Default: true + + enableCooldown + New for 8.7 update, if active then cooldown icons are shown when using a spell. + Default: true + + *OUTFIT allowChangeOutfit Should player's be able to change their outfits? - Default: yes + Default: true allowChangeColors Should player's be able to change their outfit colors? - Default: yes + Default: true allowChangeAddons Should player's be able to change their outfit addons? - Default: yes + Default: true disableOutfitsForPrivilegedPlayers Are privileged players able to change their outfits? E.g gamemasters have default outfits and shouldn't be able to change them. - Default: no + Default: false + + addonsOnlyPremium + Are addons enabled for only premium players? + Default: true + + *MISCELLANEOUS + dataDirectory + Directory where server should lookup for all its files. + Default: data/ + + logsDirectory + Directory, where server should save its log files. + Default: data/logs/ bankSystem Use bank system? E.g if enabled, rent for a house will be taken from player's bank account. - Default: yes + Default: true - saveGlobalStorage - - Global storages are stored in `global_storage` table. - Default: yes + promptExceptionTracerErrorBox + Works only on Windows with precompiled support feature, + "exception tracer" (__EXCEPTION_TRACER__ flag) + Default: true - displaySkillLevelOnAdvance - Default: no + maximumDoorLevel + Default: 500 - spellNameInsteadOfWords - E.g if you use 'exura', it will show 'Light Healing'. - Default: no + maxMessageBuffer + Default: 4 - emoteSpells - If enabled, spells wont be visible in 'Default' channel, but as orange effect. - Anti spam feature. - Default: no + tradeLimit + The max amount of items that can be traded at the same time. + Default: 100 - promptExceptionTracerErrorBox - Works only on Windows with precompiled support feature, - "exception tracer" (__EXCEPTION_TRACER__ flag) - Default: "yes" + useCapacity + If disabled players can carry all they want. + Default: true + + *DEPOT + defaultDepotSizePremium + How many items should a premium player be able to store in their depot? + Default: 2000 + + defaultDepotSize + How many items can a player without premium store in their depot? + Default: 1000 + + *MOUNTS + useMounts + New in 8.7 determined if mounts are enabled or not. + Default: true + + mountCooldown + How long you have to wait until you can mount again. + (In milliseconds.) + Default: 2000 + + unmountPlayerInPz + When entering a protection zone, will the player get unmounted? + Default: true + + *VIP LIST + separateVipListPerCharacter + If enabled, each character on your account has a separate vip list. + Default: false + + vipListDefaultLimit + How many can a player without premium have on their vip list? + Default: 20 + + vipListDefaultPremiumLimit + How many can a premium player have on their vip list? + Default: 100 + + *SAVING-RELATED + houseDataStorage + House data storage system. Binary and binary-tilestore performs much faster map saving than relational (up to 60x faster!). + binary - uses house_data table to store house data in giant blob for each house. + binary-tilebased - uses tile_store table to store house data in blob for each tile (benefit: if blob becomes corrupted, only specific tile will be wiped, not entire house). + relational - uses `tiles` and `tile_items` tables, doesn't store data in blob, easier to manage but is up to 60 times slower and uses up more space in database. + Default: binary + + saveGlobalStorage + Save global storage in database? Otherwise, they will be cleared everytime server starts. + Global storages are stored in `global_storage` table. + Default: true storePlayerDirection Should players look direction be saved and restored while logging? - In global tibia it's disabled. - Default: no + In global Tibia it's disabled. + Default: false + + *LOOT + checkCorpseOwner + Defines if corpse should be checked for owner before opening. + So for first x seconds, only creature killer is able to open it. + Default: true monsterLootMessage Defines how green message with loot in the middle of screen should be displayed. @@ -679,38 +826,46 @@ monsterLootMessageType Type/id(color) of loot message. - You can use values from 18 to 27. - Default: 25 (green) + Valid range: 13-22. + Default: 19 (green) *GHOST MODE ghostModeInvisibleEffect Should invisible gamemaster be visible to other gamemasters with invisible effect? (Like, utana vid spell) If you disable this, you'll see all ghost gamemasters in normal outfits. - Default: no + Default: false ghostModeSpellEffects - Display effects while casting spells in ghost mode? - If 'no', then no one will see that ghost GM casted a spell. - Default: yes + Display effects while casting spells in a ghost mode? + If false, then no one will see that character in a ghost mode casted a spell. + Default: true *LIMITS idleWarningTime + After being idle for this time limit you're given a warning about getting kicked soon. + (In milliseconds.) Default: 14 * 60 * 1000 idleKickTime - Default:15 * 60 * 1000 + After being idle for this time limit you're kicked. + (In milliseconds.) + Default: 15 * 60 * 1000 expireReportsAfterReads Default: 1 playerQueryDeepness - Default: 2 + Default: -1 + + tileLimit + Items limit on any other tile. + Default: 0 (means no limit!) - maxItemsPerPZTile + protectionTileLimit Maximum items which can be placed on 1 protection zone tile. Default: 0 (means no limit!) - maxItemsPerHouseTile + houseTileLimit Maximum items which can be placed on 1 house tile. Default: 0 (means no limit!) @@ -718,16 +873,22 @@ freePremium Should all players have free premium accounts? NOTE: It's just enabling it in game without any changes in a database. - Default: no + Default: false premiumForPromotion Do players need premium to buy promotion? - Default: yes + Default: true *BLESSINGS + NOTE: blessingReduction* regards items/containers loss. + + blessings + Are blessings enabled? + Default: true + blessingsOnlyPremium Should only premium players be able to use blessings? - Default: yes + Default: true blessingReductionBase Default: 30 @@ -736,14 +897,22 @@ Default: 5 eachBlessReduction - NOTE: blessingReduction* regards items/containers loss. eachBlessReduction determine by how many percentage points does each bless decrease an experience/magic/skills loss. Default: 8 + pvpBlessingThreshold + pvpBlessingThreshold is damage percent received from PvP that is required to enable pvpBlessing. + Default: 40 + + fairFightTimeRange + Last X seconds from which damage to player counts. + (In seconds.) + Default: 60 + *RATES experienceStages If you enable this option, you can edit experience stages in file data/XML/stages.xml - Default: no + Default: false rateExperience How much multiplied will be experience gained from monsters. @@ -770,6 +939,7 @@ rateSpawn Default: 1 + *MONSTER RATES rateMonsterHealth Monster health multiplier. For example, if monster have 1000 health points by default, @@ -800,10 +970,11 @@ NOTE: Stamina is stored in miliseconds so you must multiply seconds by 1000. rateStaminaHits multiplies every hit a creature does which is later multiplied by player's attack speed. - rateStaminaGain is divider of every logged out second, eg: - 60000 / 3 = 20000 milliseconds, what gives 20 stamina seconds for 1 minute being logged off. - rateStaminaThresholdGain is divider for the premium stamina. - staminaRatingLimit* is in minutes. + rateStaminaGain is multiplying every second of logged out time, eg: + 60 * 1000 / 3 = 20 seconds, what gives 1 stamina minute for 3 being logged off. + rateStaminaThresholdGain is dividing in case the normal gain (that is + multiplied by rateStaminaGain btw.) passed above threshold, eg: + 60 * 1000 / 3 = 20 / 4 = 5 seconds (3 * 4 = 12 minutes for 1 stamina minute). rateStaminaLoss Default: 1 @@ -815,11 +986,16 @@ Default: 12 staminaRatingLimitTop - In minutes. - Default: 41 * 60 + (In minutes.) + Default: 40 * 60 staminaRatingLimitBottom - In minutes. + (In minutes.) + Default: 14 * 60 + + staminaLootLimit + If your stamina is under this, loot is destroyed. + (In minutes.) Default: 14 * 60 rateStaminaAboveNormal @@ -829,7 +1005,7 @@ Default: 0.5 staminaThresholdOnlyPremium - Default: yes + Default: true *PARTY experienceShareRadiusX @@ -855,6 +1031,7 @@ Default: 5 experienceShareActivity + (In milliseconds.) Default: 2 * 60 * 1000 *GLOBAL SAVE @@ -863,7 +1040,7 @@ globalSaveEnabled Should daily saving system aka global Tibia be enabled? - Default: no + Default: false globalSaveHour Hour when server will be closed. @@ -871,12 +1048,12 @@ shutdownAtGlobalSave Defines if server should be closed at global save. - Default: yes + Default: true cleanMapAtGlobalSave Defines if map should be cleaned at global save. NOTE: Its ignored when shutdownAtGlobalSave is enabled. - Default: no + Default: false *SPAWNS deSpawnRange @@ -885,6 +1062,10 @@ deSpawnRadius Default: 50 + monsterSpawnWalkback + Should monsters walk back to their spawn after losing target? + Default: true + *SUMMONS maxPlayerSummons Amount of summons players are able to have. @@ -892,13 +1073,17 @@ teleportAllSummons Should summons be teleported to monsters? - Default: no + Default: false teleportPlayerSummons Should summons be teleported to thir owners? (E.g. if owner changes a floor level) - Default: no + Default: false *STATUS + statusPort + Port where status protocol will listen. + Default: 7171 + ownerName Default: "" @@ -913,25 +1098,18 @@ displayGamemastersWithOnlineCommand Should online gamemasters be displayed when using !online command? - Default: no + Default: false *LOGS displayPlayersLogging Should message in console be displayed everytime player logs in/out? - Default: yes + Default: true prefixChannelLogs Defines channel logs file prefix. Usable if you have multiworld, and want to have channels from all worlds in different files. Default: "" - adminLogsEnabled - Create logs from remote control tool. - Default: no - - NOTE: A kind of logging listed below does not work in GUI version. - For such, please compile the software with __GUI_LOGS__ flag. - runFile It will save process pid to this file if specified. Default: "" @@ -939,9 +1117,49 @@ outLogName Default: "" - errorLogName + truncateLogOnStartup + Default: false + + *MANAGER + managerPort + Default: 7171 + + managerLogs + Default: true + + managerPassword + Left blank disables manager. + Default: "" + + managerLocalhostOnly + Default: true + + managerConnectionsLimit + Default: 1 + + *ADMIN + adminPort + Default: 7171 + + adminLogs + Default: true + + adminPassword + Left blank disables manager. Default: "" - truncateLogsOnStartup - Default: no + adminLocalhostOnly + Default: true + + adminConnectionsLimit + Default: 1 + + adminRequireLogin + Default: true + + adminEncryption + Default: "" + + adminEncryptionData + Default: "" ] diff --git a/doc/GROUPS b/doc/GROUPS index ea71cd6..c009985 100644 --- a/doc/GROUPS +++ b/doc/GROUPS @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -24,25 +24,3 @@ All player custom flags with a generator may be found at address: http://fightingelf.net/customflags/ ] - -[ Violation Reasons - * All name reasons (4) - * All name & statement reasons (6) - * All name, statement & cheating reasons (16) - * All name, statement, cheating & gamemaster (19) - * All name, statement, cheating - * All reasons (23) -] - -[ Violation Flags - * None = 0 - * Notation = 1 - * Name Report = 2 - * Banishment = 4 - * Name Report + Banishment = 8 - * Banishment + Final Warning = 16 - * Name Report + Banishment + Final Warning = 32 - * Statement Report = 64 - * IP Banishment = 128 - * Deletion = 256 -] diff --git a/doc/LICENSE b/doc/LICENSE index 3947619..95e7065 100644 --- a/doc/LICENSE +++ b/doc/LICENSE @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson diff --git a/doc/LIQUIDS b/doc/LIQUIDS index e922e5c..2b001ad 100644 --- a/doc/LIQUIDS +++ b/doc/LIQUIDS @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -15,7 +15,7 @@ http://otland.net/ ] -[ FLUIDTYPE NAME COLOR +[ FLUIDTYPE NAME COLOR 2 blood RED 10 lifefluid RED 26 lava RED @@ -25,7 +25,6 @@ 19 mud BROWN 27 rum BROWN 35 tea BROWN - 43 mead BROWN 4 slime GREEN 28 swamp GREEN diff --git a/doc/LUA_FUNCTIONS b/doc/LUA_FUNCTIONS index edec8b8..85dc3f0 100644 --- a/doc/LUA_FUNCTIONS +++ b/doc/LUA_FUNCTIONS @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -450,9 +450,9 @@ Example if getPlayerFlagValue(cid, PlayerFlag_CanSummonAll) then --if can - doSummonCreature("Demon", fromPosition.x + 1) + doSummonCreature("Demon", fromPosition) else --if cant - doSummonCreature("Rat", fromPosition.x + 1) + doSummonCreature("Rat", fromPosition) end getPlayerCustomFlagValue(cid, flag) @@ -582,7 +582,7 @@ Example if getPlayerLevel(cid) >= 10 then --checking level - doSummonCreature("Chicken", fromPosition.x + 1) + doSummonCreature("Chicken", fromPosition) else doPlayerSendCancel(cid, "Sorry, your level isnt enought to summon this monster.") end @@ -590,7 +590,7 @@ [ LIST [ Note - This list seems to be strongly outdated! Please come back on Gold release ;) + There are few latest missing... ] //get* @@ -604,58 +604,56 @@ getCreatureSummons(cid) getCreatureOutfit(cid) getCreaturePosition(cid) + getCreatureLastPosition(cid) getCreatureLookDirection(cid) getCreatureName(cid) - getCreatureSpeed(cid) //TODO - getCreatureBaseSpeed(cid) //TODO - getCreatureTarget(cid) //TODO + getCreatureSpeed(cid) + getCreatureBaseSpeed(cid) + getCreatureTarget(cid) getCreatureByName(name) getCreatureSkullType(cid) - getCreatureCondition(cid, condition[, subId]) //TODO - getCreatureNoMove(cid) //TODO + getCreatureCondition(cid, condition[, subId = 0]) + getCreatureNoMove(cid) + getCreatureStorage(cid, key) getMonsterInfo(name) - getMonsterHealingSpells(name) //TODO - getMonsterAttackSpells(name) //TODO - getMonsterLootList(name) //TODO - getMonsterSummonList(name) //TODO - getMonsterTargetList(cid) //TODO - getMonsterFriendList(cid) //TODO - getPlayerByNameWildcard(name~[, ret = false]) //TODO - getPlayerLossSkill(cid) //TODO - getPlayerLossPercent(cid, lossType) //TODO - getPlayerGUIDByName(name[, multiworld = false]) //TODO - getPlayerNameByGUID(guid[, multiworld = false[, displayError = true]]) //TODO + getMonsterSummonList(name) + getMonsterTargetList(cid) + getMonsterFriendList(cid) + getPlayerByNameWildcard(name~[, ret = false]) + getPlayerLossSkill(cid) + getPlayerLossPercent(cid, lossType) + getPlayerGUIDByName(name[, multiworld = false]) + getPlayerNameByGUID(guid[, multiworld = false]) getPlayerFood(cid) getPlayerLevel(cid) getPlayerExperience(cid) - getPlayerMagLevel(cid[, ignoreBuffs = false]) //TODO - getPlayerSpentMana(cid) //TODO + getPlayerMagLevel(cid[, ignoreBuffs = false]) + getPlayerSpentMana(cid) getPlayerAccess(cid) getPlayerGhostAccess(cid) getPlayerSkillLevel(cid, skillId) - getPlayerSkillTries(cid, skillId) //TODO + getPlayerSkillTries(cid, skillId) getPlayerTown(cid) getPlayerVocation(cid) - getPlayerRequiredMana(cid, magicLevel) //TODO - getPlayerRequiredSkillTries(cid, skillId, skillLevel) //TODO + getPlayerRequiredMana(cid, magicLevel) + getPlayerRequiredSkillTries(cid, skillId, skillLevel) getPlayerItemCount(cid, itemid[, subType = -1]) getPlayerSoul(cid) - getPlayerAccountId(cid) //TODO - getPlayerAccount(cid) //TODO - getPlayerIp(cid) //TODO + getPlayerAccountId(cid) + getPlayerAccount(cid) + getPlayerIp(cid) getPlayerFreeCap(cid) getPlayerLight(cid) getPlayerSlotItem(cid, slot) - getPlayerWeapon(cid[, ignoreAmmo = false]) //TODO - getPlayerItemById(cid, deepSearch, itemId[, subType = -1]) //TODO + getPlayerWeapon(cid[, ignoreAmmo = false]) + getPlayerItemById(cid, deepSearch, itemId[, subType = -1]) getPlayerDepotItems(cid, depotid) getPlayerGuildId(cid) getPlayerGuildName(cid) getPlayerGuildRank(cid) getPlayerGuildNick(cid) - getPlayerGuildLevel(cid) //TODO: From here, all bottoms + getPlayerGuildLevel(cid) getPlayerSex(cid) - getPlayerStorageValue(uid, key) getPlayerGUID(cid) getPlayerFlagValue(cid, flag) getPlayerCustomFlagValue(cid, flag) @@ -673,10 +671,12 @@ getPlayerBalance(cid) getPlayerMoney(cid) getPlayerSkullTicks(cid, type) + getPlayerModes(cid) getPlayerRates(cid) getPlayerLastLogin(cid) getPlayerLastLoginSaved(cid) getPlayerAccountManager(cid) + getPlayerTradeState(cid) getInstantSpellInfo(cid, name) getPlayersByAccountId(accountNumber) getPlayersByIp(ip[, mask = 0xFFFFFFFF]) @@ -694,6 +694,7 @@ getItemWeightById(itemid, count[, precise]) getItemDescriptions(uid) getItemWeight(uid[, precise]) + getItemParent(uid) getItemAttack(uid) getItemExtraAttack(uid) getItemDefense(uid) @@ -702,24 +703,22 @@ getItemAttackSpeed(uid) getItemHitChance(uid) getItemShootRange(uid) - getItemIdByName(name[, displayError = true]) + getItemIdByName(name) getItemLevelDoor(itemid) getItemWeaponType(uid) getFluidSourceType(type) getContainerSize(uid) getContainerCap(uid) - getContainerCapById(itemid) getContainerItem(uid, slot) - getDepotId(uid) getTileItemById(pos, itemId[, subType = -1]) - getTileItemByType(pos, type) + getTileItemsByType(pos, type) getTileThingByPos(pos) getTileInfo(pos) getTopCreature(pos) getClosestFreeTile(cid, targetpos[, extended = false[, ignoreHouse = true]]) getThingFromPos(pos) getThing(uid) - getThingPos(uid) + getThingPosition(uid) getHouseInfo(id) getHouseAccessList(houseid, listid) getHouseByPlayerGUID(playerGUID) @@ -741,20 +740,28 @@ getSearchString(fromPosition, toPosition[, fromIsCreature = false[, toIsCreature = false]]) getWaypointPosition(name) getGameState() - getNotationsCount(accId) - getBanData(value) - getBanList(type[, value]) + getStorage(key) + getNotationsCount(accountId[, playerId]) + getStatementsCount(name/guid[, channelId]) + getBanData(value[, type[, param]]) + getBanList(type[, value[, param]]) getBanReason(id) - getBanAction(id[, ipBanishment]) - getGlobalStorageValue(valueid) + getBanAction(id[, ipBanishment = false]) getExperienceStage(level) getConfigFile() getConfigValue(key) getModList() - loadmodlib(libName) - domodlib(libName) - getLogsDir() + doUpdateHouseAuctions() + loadmodlib(lib) + domodlib(lib) + dodirectory(dir[, recursively = false]) + errors(var) getDataDir() + getLogsDir() + getVocationList() + getGroupList() + getChannelList() + getTownList() getWaypointList() getTalkActionList() getExperienceStageList() @@ -782,7 +789,6 @@ setCombatCallBack(combat, key, function_name) setCombatFormula(combat, type, mina, minb, maxa, maxb) setConditionFormula(combat, mina, minb, maxa, maxb) - setGlobalStorageValue(key, newValue) setWorldType(type) //do* @@ -796,48 +802,53 @@ doCreatureChangeOutfit(cid, outfit) doCreatureSay(uid, text, type[, ghost = false[, cid = 0[, pos]]]) doCreatureSetNoMove(cid, cannotMove) + doCreatureSetStorage(cid, key, value) doSetCreatureLight(cid, lightLevel, lightColor, time) - doSetCreatureOutfit(cid, outfit, time) - doRemoveCreature(cid[, executeLogout = true]) - doMoveCreature(cid, direction) + doSetCreatureOutfit(cid, outfit[, time = -1]) + doRemoveCreature(cid[, forceLogout = true]) + doMoveCreature(cid, direction[, flag = FLAG_NOLIMIT]) + doSteerCreature(cid, position) doConvinceCreature(cid, target) doChallengeCreature(cid, target) doChangeSpeed(cid, delta) doSummonMonster(name, pos) - doCreateMonster(name, pos) + doCreateMonster(name, pos[, extend = false[, force = false]]) doMonsterChangeTarget(cid) doMonsterSetTarget(cid, target) doCreateNpc(name, pos) - doSetMonsterOutfit(cid, name, time) + doSetMonsterOutfit(cid, name[, time = -1]) doPlayerBroadcastMessage(cid, message[, type]) doPlayerSetSex(cid, newSex) doPlayerSetTown(cid, townid) doPlayerSetVocation(cid, voc) - doPlayerSetStorageValue(uid, key, newValue) doPlayerSetGroupId(cid, newGroupId) doPlayerSetPromotionLevel(cid, level) doPlayerSetStamina(cid, minutes) doPlayerSetBalance(cid, balance) doPlayerSetExtraExpRate(cid, value) doPlayerSetPartner(cid, guid) + doPlayerFollowCreature(cid, target) doPlayerRemoveItem(cid, itemid, count[, subtype]) doPlayerAddExperience(cid, amount) doPlayerSetGuildId(cid, id) doPlayerSetGuildRank(cid, rank) doPlayerSetGuildNick(cid, nick) - doPlayerAddOutfit(cid,looktype, addons) - doPlayerRemoveOutfit(cid,looktype, addons) + doPlayerAddOutfit(cid,looktype, addon) + doPlayerRemoveOutfit(cid,looktype[, addon = 0]) + doPlayerAddOutfitId(cid, outfitId, addon) + doPlayerRemoveOutfitId(cid, outfitId[, addon = 0]) doPlayerSetRedSkullTicks(cid, amount) doPlayerSetLossPercent(cid, lossType, newPercent) doPlayerSetLossSkill(cid, doLose) doPlayerAddSkillTry(cid, skillid, n) doPlayerAddSpentMana(cid, amount) doPlayerAddSoul(cid, soul) - doPlayerAddItem(uid, itemid[, count/subtype[, canDropOnMap = true]]) - doPlayerAddItemEx(cid, uid[, canDropOnMap = false]) - doPlayerSendTextMessage(cid, MessageClasses, message) + doPlayerAddItem(uid, itemid[, count/subtype = 1[, canDropOnMap = true[, slot = 0]]]) + doPlayerAddItemEx(cid, uid[, canDropOnMap = false[, slot = 0]]) + doPlayerSendTextMessage(cid, MessageClasses, message[, animatedText[, color[, pos]]]) doPlayerSendChannelMessage(cid, author, message, SpeakClasses, channel) doPlayerSendToChannel(cid, targetId, SpeakClasses, message, channel[, time]) + doPlayerOpenChannel(cid, channelId) doPlayerAddMoney(cid, money) doPlayerRemoveMoney(cid, money) doPlayerTransferMoneyTo(cid, target, money) @@ -846,7 +857,6 @@ doPlayerAddMapMark(cid, pos, type[, description]) doPlayerAddPremiumDays(cid, days) doPlayerAddBlessing(cid, blessing) - doPlayerAddStamina(cid, minutes) doPlayerResetIdleTime(cid) doPlayerLearnInstantSpell(cid, name) doPlayerUnlearnInstantSpell(cid, name) @@ -855,23 +865,26 @@ doPlayerSendDefaultCancel(cid, ReturnValue) doPlayerSetRate(cid, type, value) doPlayerJoinParty(cid, lid) + doPlayerLeaveParty(cid[, forced = false]) doPlayerSendOutfitWindow(cid) + doPlayerSwitchSaving(cid) doPlayerSave(cid[, shallow = false]) - doCreateItem(itemid, type/count, pos) + doCreateItem(itemid[, type/count = 1], pos) doCreateItemEx(itemid[, count/subtype]) doAddContainerItemEx(uid, virtuid) - doAddContainerItem(uid, itemid[, count/subtype]) + doAddContainerItem(uid, itemid[, count/subtype = 1]) doChangeTypeItem(uid, newtype) doDecayItem(uid) - doRemoveItem(uid[, count]) + doRemoveItem(uid[, count = -1]) doTransformItem(uid, toitemid[, count/subtype]) doSetItemActionId(uid, actionid) doSetItemText(uid, text[, writer[, date]]) doSetItemSpecialDescription(uid, desc) - doSetItemOutfit(cid, item, time) + doSetItemOutfit(cid, item[, time = -1]) doSetItemProtection(uid, value) doTileAddItemEx(pos, uid) doTileQueryAdd(uid, pos[, flags]) + doItemRaidUnref(uid) doAddCondition(cid, condition) doRemoveCondition(cid, type[, subId]) doRemoveConditions(cid[, onlyPersistent]) @@ -886,25 +899,28 @@ doCombat(cid, combat, param) doTeleportThing(cid, newpos[, pushmove = true]) doCreateTeleport(itemid, topos, createpos) + doSendCreatureSquare(cid, color[, player]) doSendMagicEffect(pos, type[, player]) doSendDistanceShoot(frompos, topos, type[, player]) doSendAnimatedText(pos, text, color[, player]) doShowTextDialog(cid, itemid, text) - doRelocate(pos, toPos[, creatures = true]) + doRelocate(pos, toPos[, creatures = true[, unmovable = true]]) + doCleanTile(pos[, forceMapLoaded = false]) doBroadcastMessage(message, type) doWaypointAddTemporial(name, pos) doSetGameState(stateId) - doAddIpBanishment(ip[, length[, comment[, admin]]]) - doAddNamelock(name[, reason[, action[, comment[, admin]]]]) - doAddBanishment(accId[, length[, reason[, action[, comment[, admin]]]]]) - doAddDeletion(accId[, reason[, action[, comment[, admin]]]]]) - doAddNotation(accId[, reason[, action[, comment[, admin]]]]]) + doSetStorage(key, value) + doAddIpBanishment(ip[, mask[, length[, reason[, comment[, admin[, statement]]]]]]) + doAddPlayerBanishment(name/guid[, type[, length[, reason[, action[, comment[, admin[, statement]]]]]]]) + doAddAccountBanishment(accountId[, playerId[, length[, reason[, action[, comment[, admin[, statement]]]]]]]) + doAddNotation(accountId[, playerId[, reason[, comment[, admin[, statement]]]]]]) + doAddStatement(name/guid[, channelId[, reason[, comment[, admin[, statement]]]]]]) doRemoveIpBanishment(ip[, mask]) - doRemoveNamelock(name) - doRemoveBanisment(accId) - doRemoveDeletion(accId) - doRemoveNotations(accId) - doSaveServer() + doRemovePlayerBanishment(name/guid, type) + doRemoveAccountBanishment(accountId[, playerId]) + doRemoveNotations(accountId[, playerId]) + doRemoveStatements(name/guid[, channelId]) + doSaveServer([shallow = false]) doReloadInfo(id[, cid]) doCleanHouse(houseId) doCleanMap() @@ -916,6 +932,8 @@ isNpc(uid) isPlayer(cid) isPlayerPzLocked(cid) + isPlayerSaving(cid) + isPlayerProtected(cid) isItemStackable(itemid) isItemRune(itemid) isItemMovable(itemid) @@ -927,13 +945,16 @@ isMovable(uid) isSightClear(fromPos, toPos, floorCheck) isIpBanished(ip[, mask]) + isPlayerBanished(cid, type) isPlayerNamelocked(name) - isAccountBanished(accId) - isAccountDeleted(accId) + isAccountBanished(accountId[, playerId]) isInArray({array}, value[, lower = true]) + isSummon(uid) + isUnderWater(uid) //others registerCreatureEvent(uid, eventName) + unregisterCreatureEvent(uid, eventName) createCombatArea({area}[, {exArea}]) createConditionObject(type[, ticks[, buff[, subId]]]) addDamageCondition(condition, rounds, time, value) @@ -946,22 +967,23 @@ variantToNumber(var) variantToString(var) variantToPosition(var) - canPlayerWearOutfit(cid, lookType, addons) + canPlayerWearOutfit(cid, lookType[, addon = 0]) + canPlayerWearOutfitId(cid, outfitId[, addon = 0]) executeRaid(name) addEvent(callback, delay, ...) stopEvent(eventid) - hasProperty(uid) - - md5(str) - sha1(str) + hasPlayerClient(cid) + hasItemProperty(uid, prop) //db table - db.executeQuery(query) + db.query(query) db.storeQuery(query) db.escapeString(str) db.escapeBlob(s, length) - db.stringComparisonOperator() db.lastInsertId() + db.stringComparison() + db.updateLimiter() + db.connected() //result table result.getDataInt(resId, s) @@ -989,6 +1011,20 @@ bit.urshift(type, n) #bit.uarshift + //std table + std.cout(...) + std.clog(...) + std.cerr(...) + std.md5(string[, upperCase = false]) + std.sha1(string[, upperCase = false]) + std.sha256(string[, upperCase = false]) + std.sha512(string[, upperCase = false]) + std.vahash(string[, upperCase = false]) + + //os table + os.time() + os.mtime() + //compats table.getPos = table.find doSetCreatureDropLoot = doCreatureSetDropLoot @@ -1045,28 +1081,51 @@ setPlayerPromotionLevel = doPlayerSetPromotionLevel setPlayerGroupId = doPlayerSetGroupId setPlayerPartner = doPlayerSetPartner + doPlayerSetStorageValue = doCreatureSetStorage setPlayerStorageValue = doPlayerSetStorageValue + getPlayerStorageValue = getCreatureStorage + getGlobalStorageValue = getStorage + setGlobalStorageValue = doSetStorage setPlayerBalance = doPlayerSetBalance doAddMapMark = doPlayerAddMapMark doSendTutorial = doPlayerSendTutorial + getWaypointsList = getWaypointList + getPlayerLastLoginSaved = getPlayerLastLogin + getThingPos = getThingPosition + doAreaCombatHealth = doCombatAreaHealth + doAreaCombatMana = doCombatAreaMana + doAreaCombatCondition = doCombatAreaCondition + doAreaCombatDispel = doCombatAreaDispel + getItemDescriptionsById = getItemInfo + hasProperty = hasItemProperty + hasClient = hasPlayerClient + print = std.cout + getPosByDir = getPositionByDirection + db.updateQueryLimitOperator = db.updateLimiter + db.stringComparisonOperator = db.stringComparer + db.stringComparison = db.stringComparer + db.executeQuery = db.query + isNumber = isNumeric //lua-made functions doPlayerGiveItem(cid, itemid, amount, subType) + doPlayerGiveItemContainer(cid, containerid, itemid, amount, subType) doPlayerTakeItem(cid, itemid, amount) doPlayerBuyItem(cid, itemid, count, cost, charges) doPlayerBuyItemContainer(cid, containerid, itemid, count, cost, charges) doPlayerSellItem(cid, itemid, count, cost) - doPlayerWithdrawMoney(cid, money) - doPlayerDepositMoney(cid, money) - comparePos(pos, posEx) - isInRange(pos, fromPos, toPos) - getArea(pos, rangeX, rangeY) + doPlayerWithdrawMoney(cid, amount) + doPlayerDepositMoney(cid, amount) + doPlayerAddStamina(cid, minutes) + doComparePositions(pos, posEx) + isInRange(position, fromPosition, toPosition) + getArea(pos, x, y) isPremium(cid) getMonthDayEnding(day) getMonthString(m) getArticle(str) - isNumber(str) - getDistanceBetween(firstPosition, secondPosition) + isNumeric(str) + getDistanceBetween(fromPosition, toPosition) doPlayerAddAddons(cid, addon) isSorcerer(cid) isDruid(cid) @@ -1075,13 +1134,13 @@ isRookie(cid) getDirectionTo(pos, posEx) getCreatureLookPosition(cid) - getPosByDir(fromPosition, direction, size) + getPositionByDirection(fromPosition, direction, size) doPlayerWithdrawAllMoney(cid) doPlayerDepositAllMoney(cid) doPlayerTransferAllMoneyTo(cid, target) - doPlayerAddLevel(cid, amount, round) - doPlayerAddMagLevel(cid, amount) - doPlayerAddSkill(cid, amount) + doPlayerAddLevel(cid[, amount = 1[, round = false]]) + doPlayerAddMagLevel(cid[, amount = 1]) + doPlayerAddSkill(cid, skill[, amount = 1[, round = false]]) playerExists(name) getTibiaTime() doWriteLogFile(file, text) @@ -1114,7 +1173,7 @@ getTileZoneInfo(pos) debugPrint(text) doShutdown() - doSummonCreature(name, pos) + doSummonCreature(name, pos[, extend = false[, force = false]]) getOnlinePlayers() getPlayerByName(name) isPlayerGhost(cid) @@ -1123,9 +1182,16 @@ isInParty(cid) isPrivateChannel(channelId) doConvertIntegerToIp(int, mask) - doConvertIpToInteger(int, mask) + doConvertIpToInteger(str) + doRevertIp(ip) getBooleanFromString(str) doCopyItem(item, attributes) + getContainerCapById(itemid) + getDepotId(uid) + getMonsterAttackSpells(name) + getMonsterHealingSpells(name) + getMonsterLootList(name) + getMonsterSummonList(name) exhaustion.check(cid, storage) exhaustion.get(cid, storage) exhaustion.set(cid, storage, time) @@ -1137,4 +1203,5 @@ string.split(str) string.trim(str) string.explode(str, sep) + choose(...) ] diff --git a/doc/MYSQL_HELP b/doc/MYSQL_HELP index d4e8d03..9852923 100644 --- a/doc/MYSQL_HELP +++ b/doc/MYSQL_HELP @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -94,7 +94,7 @@ #1 Download XAMPP from: - http://heanet.dl.sourceforge.net/sourceforge/xampp/xampp-win32-1.6.6a-installer.exe + http://heanet.dl.sourceforge.net/sourceforge/xampp/xampp-win32-1.7.4-VC6-installer.exe #2 Go to http://localhost/security/xamppsecurity.php and change your root password. @@ -123,6 +123,17 @@ again find the '-- SQL' part and change sqlType to "mysql". You may now save and close config.lua. + #9 + VERY IMPORTANT! DON'T SKIP THIS PART!!! + Once you are done with that, view these threads + and make sure your XAMPP installation is not + vulnerable to security holes: + http://otland.net/f14/warning-everyone-whos-running-xampp-15321/ + http://otland.net/f14/serious-vulnerability-xampp-everyone-using-xampp-please-read-140244/ + + If there is something you don't understand, don't hesitate + to reply in the threads and ask for help. + Trouble Did you find a part confusing, or just failed somewhere? Explain your troubles to us at http://otland.net/, and diff --git a/doc/OUTFIT_STATS b/doc/OUTFIT_STATS new file mode 100644 index 0000000..8654f41 --- /dev/null +++ b/doc/OUTFIT_STATS @@ -0,0 +1,215 @@ +[ OUTFIT STATS + Project Name + The Forgotten Server + + Version + 0.3.7 + + Codenamed + Crying Damson + + License + GNU GPLv3 + + Forum + http://otland.net/ +] + +[ ABOUT + List with all the special outfit attributes/stats. + These are used in the outfit.xml file: + + e.g. + + + + + + + + + + + + - This will increase the players health/mana by 100 when + they have the full citizen outfit. + + The features are self explainitory and will give players + the attributes you have set when they have both addons + of the outfit. +] + +[ "list" attributes + + manaShield + + + invisible + + + healthGain + + + healthTicks + + + manaGain + + + manaTicks + + + speed + + + attackSpeed + + +] + +[ "reflect" attributes + + reflect percentAll + -e.g. + + - reflect 100% of all damage types + + + More: + reflect percentElements + reflect percentMagic + reflect percentEnergy + reflect percentFire + reflect percentPoison + reflect percentIce + reflect percentHoly + reflect percentDeath + reflect percentLifeDrain + reflect percentManaDrain + reflect percentDrown + reflect percentPhysical + reflect percentHealing + reflect percentUndefined + + reflect chanceAll + -e.g. + + - 100% chance to reflect all damage types + + + More: + reflect chanceElements + reflect chanceMagic + reflect chanceEnergy + reflect chanceFire + reflect chancePoison + reflect chanceIce + reflect chanceHoly + reflect chanceDeath + reflect chanceLifeDrain + reflect chanceManaDrain + reflect chanceDrown + reflect chancePhysical + reflect chanceHealing + reflect chanceUndefined + +] + +[ "absorb" attributes + + absorb percentAll + -e.g. + + - absorb 100% of all damage types + + + More: + absorb percentElements + absorb percentMagic + absorb percentEnergy + absorb percentFire + absorb percentPoison + absorb percentIce + absorb percentHoly + absorb percentDeath + absorb percentLifeDrain + absorb percentManaDrain + absorb percentDrown + absorb percentPhysical + absorb percentHealing + absorb percentUndefined + +] + +[ "skills" attributes + + skills fist + -e.g. + + - increase players fist fighting by 10 + + + More: + skills club + skills axe + skills sword + skills distance + skills shielding + skills fishing + skills melee (fist, club, sword, axe) + skills weapon (club, sword, axe, dist) + skills fistPercent + skills clubPercent + skills axePercent + skills swordPercent + skills distancePercent + skills shieldingPercent + skills fishingPercent + skills meleePercent (fist, club, sword, axe) + skills weaponPercent (club, sword, axe, dist) + +] + +[ "stats" attributes + + stats maxHealth + -e.g. + + - increase players max health by 100 + + + More: + stats maxMana + stats soul + stats level + stats magLevel + stats maxHealthPercent + stats maxManaPercent + stats soulPercent + stats levelPercent + stats magLevelPercent + +] + +[ "supress" attributes + + supress poison + -e.g. + + - suppress all poison damage + + + More: + supress fire + supress energy + supress physical + supress haste + supress paralyze + supress invisible + supress light + supress manaShield + supress drunk + supress drown + supress muted + +] \ No newline at end of file diff --git a/doc/README b/doc/README index f92594b..25331d1 100644 --- a/doc/README +++ b/doc/README @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -23,10 +23,11 @@ http://sourceforge.net/project/memberlist.php?group_id=32523 .. also a thanks to the following people who has helped this - project by contributing something: Cayan, TheMask, slawkens - Rafael Hamdan, Fare-Fray, Kiwi Dan, Gentleman Riko, - Piotrek1447, Hermes, Gesior.pl, levandi, Bennet, - Casa, Winghawk, Nexoz, Godely, buzzbuzz2, koob. + project by contributing something: + Cayan, TheMask, King Komic, Rafael Hamdan, Fare, Kiwi Dan, + Piotrek1447, Hermes, Gesior.pl, levandi, Casa, Winghawk, + Nexoz, Chojrak, Pietia, Lithium, Jonern, KaczooH, slawkens, + BeniS, Shinmaru, Leftwing, Siramix. ] [ LICENSE @@ -36,26 +37,36 @@ [ DEVELOPERS Project Manager(s) - Talaturen Elf + Talaturen Founder(s) Talaturen - Kiper C++ Programmer(s) Elf + Dalkon + Fallen Talaturen - LUA Scripter(s) + Lua Scripter(s) + Dalkon Elf + BeniS - DATA Directory Manager(s) - Lithium - KaczooH + Data Directory Manager(s) + Elf + Dalkon + Fallen + BeniS Documentation Manager(s) - KaczooH + Shinmaru + BeniS + + Mapper(s) + Leftwing + Siramix ] [ FORUM @@ -75,45 +86,46 @@ ] [ COMPILING - GNU\Linux - Debian (...and forks, eg. Ubuntu) - A detailed and always up-to-date tutorial may be found here: + GNU\Linux + Automatically(Apt-get and Yum Based OS) + Launch terminal and type: + cd 0.3.7 && sudo ./compile.sh + It will ask you some simple questions along the compilation/intallation process, wait till it is done, then you may continue to CONFIGURING and EXECUTING. + + Debian http://otland.net/f137/linux-ultimate-compile-guide-debian-ubuntu-included-2868/ - Root - You need to get on root user or use sudo for almost all below operations. - - Download required packages - Launch a terminal, and paste in the following commands: - apt-get install libboost1.38-dev libboost-system1.38-dev libboost-filesystem1.38-dev libboost-date-time1.38-dev libboost-regex1.38-dev libboost-thread1.38-dev libgmp3-dev liblua5.1-0 liblua5.1-0-dev liblua50 liblua50-dev liblualib50 liblualib50-dev lua50 lua5.1 libsqlite0-dev libsqlite3-dev sqlite3 libmysql++-dev libmysqlclient15-dev mysql-client-5.0 mysql-server-5.0 mysql-common libxml2-dev libxml++2.6-dev cpp gcc g++ make automake autoconf pkg-config subversion liblua5.1-sql-mysql-dev liblua5.1-sql-sqlite3-dev zlib1g-dev zlib1g - - Download sources - Launch a terminal, and paste in this command: - svn co http://svn.otland.net/public/forgottenserver/tags/0.3.6pl1 - - Compiling - Launch a terminal, and type this: - cd 0.3.6pl1 && ./autogen.sh && ./configure --enable-sqlite --enable-server-diag && make - - Linker error - If you receive a linker error saying something with 'libboost', try pasting the following commands to terminal and then move back to compiling again: - wget http://garr.dl.sourceforge.net/sourceforge/asio/boost_asio_1_0_0.tar.gz - tar -xzf boost_asio_1_0_0.tar.gz - cp -ar boost_asio_1_0_0/boost/* /usr/include/boost/ - g++ boost_asio_1_0_0/libs/system/src/error_code.cpp -c -o /usr/lib/libboost_system.a - ln /usr/lib/libboost_regex-mt.so /usr/lib/libboost_regex.so - ln /usr/lib/libboost_regex-mt.a /usr/lib/libboost_regex.a - ln /usr/lib/libboost_thread-mt.so /usr/lib/libboost_thread.so - ln /usr/lib/libboost_thread-mt.a /usr/lib/libboost_thread.a - ln /usr/lib/libboost_filesystem-mt.so /usr/lib/libboost_filesystem.so - ln /usr/lib/libboost_filesystem-mt.a /usr/lib/libboost_filesystem.a - ln /usr/lib/libboost_date_time-mt.so /usr/lib/libboost_date_time.so - ln /usr/lib/libboost_date_time-mt.a /usr/lib/libboost_date_time.a - rm -rf boost_asio_1_0_0 boost_asio_1_0_0.tar.gz + Ubuntu + http://otland.net/f479/ubuntu-nothing-complete-server-forgotten-server-modernaac-mysql-lighttpd-89001/ + + In short... + Root + You need to get on root user or use sudo for almost all below operations. + + Download required packages + Launch a terminal, and paste in the following commands: + apt-get install libboost1.38-dev liblua5.1-0 liblua5.1-0-dev liblua50 liblua50-dev liblualib50 liblualib50-dev lua50 libsqlite0 libsqlite0-dev libsqlite3-0 libsqlite3-dev sqlite3 lua5.1 libmysql++-dev libgmp3-dev subversion make g++ libxml2 libxml2-dev libxml++2.6-dev libmysqlclient15-dev mysql-client-5.0 mysql-common mysql-server-5.0 gcc cpp automake autoconf pkg-config zlib1g-dev ccache libssl-dev + + Download sources + Launch a terminal, and paste in this command: + svn co http://svn.otland.net/public/forgottenserver/tags/0.3.7 + + Compiling + Launch a terminal, and type this: + cd 0.3.7 && ./autogen.sh && ./configure --enable-sqlite --enable-server-diag && ./build.sh Windows + Microsoft Visual C++ (MSVC) + http://otland.net/f479/compiling-msvc-compiling-forgotten-server-under-windows-easy-way-125178/ + Dev-Cpp - Download TheForgottenDev-Cpp and the sources from: - http://otland.net/showthread.php?t=1024 + [ NOTE + This tutorial part is marked as obsolete, + use at own risk + ] + + Download The Forgotten Dev-Cpp and the sources from: + http://otland.net/f19/stians-dev-cpp-repack-0-2-crypto-64bit-62455/ Extract them anywhere and then run devcpp.exe, press on the menuitem "File -> Open Project or File" and choose C:\path\to\sources\dev-cpp\TheForgottenServer.dev. @@ -127,9 +139,10 @@ [ CONFIGURING Launch config.lua with your favorite text editor (NOTE: Windows built-in Notepad - may not display line break!) and start configuring it (if you would need help or - you wouldn't understand something, please check doc/CONFIG_HELP), in case there - are other things you would like to configure- go over to the data directory. + may not display line break, use Wordpad instead!) and start configuring it + (if you would need help or you wouldn't understand something, please check + doc/CONFIG_HELP), in case there are other things you would like to configure- + go over to the data directory. ] [ EXECUTING @@ -138,16 +151,22 @@ Read MYSQL_HELP then launch a terminal, and paste this command: cd ~/forgottenserver/forgottenserver/ && ./theforgottenserver - SqLite + SQLite Launch a terminal, and paste this command: cd ~/forgottenserver/forgottenserver/ && ./theforgottenserver + PostgreSQL + PgSQL is currently not available. + Windows MySQL Read MYSQL_HELP and then move the exe file to the directory where you have config.lua and data directory and execute it. - SqLite + SQLite Move the exe file to the directory where you have config.lua and data directory and execute it. + + PostgreSQL + PgSQL is currently not available. ] diff --git a/doc/RESERVED_IDS b/doc/RESERVED_IDS index b5d281b..ec71c3e 100644 --- a/doc/RESERVED_IDS +++ b/doc/RESERVED_IDS @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson diff --git a/doc/SCRIPTSYSTEM_HELP b/doc/SCRIPTSYSTEM_HELP index 92913ee..01aa188 100644 --- a/doc/SCRIPTSYSTEM_HELP +++ b/doc/SCRIPTSYSTEM_HELP @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -27,6 +27,8 @@ uniqueid itemid fromid, toid + fromaid, toaid + fromuid, touid Defined in which situations action should be triggered. Execution order: unique, action, id, runeid allowfaruse @@ -47,43 +49,52 @@ type Event type Values: - login, logout + login, logout, + spawn-single, spawn-global, advance, statschange, - direction, outfit - sendmail, receivemail - traderequest, tradeaccept - joinchannel, leavechannel - look, think, textedit, reportbug - push, target, follow + direction, outfit, + mailsend, mailchange, + traderequest, tradeaccept, + channeljoin, channelleave, + look, think, textedit, reportbug, + push, target, follow, attack, combat, areacombat, cast - kill, death, preparedeath + kill, death, preparedeath, + channelrequest, reportviolation, + thankyou, houseedit, throw Functions: onLogin(cid) - onLogout(cid) + onLogout(cid, forceLogout) + onSpawn(cid) onAdvance(cid, skill, oldLevel, newLevel) onStatsChange(cid, attacker, type, combat, value) onDirection(cid, old, current) onOutfit(cid, old, current) - onSendMail(cid, receiver, item, openBox) - onReceiveMail(cid, sender, item, openBox) + onMailSend(cid, receiver, item, openBox) + onMailReceive(cid, sender, item, openBox) onTradeRequest(cid, target, item) onTradeAccept(cid, target, item, targetItem) - onJoinChannel(cid, channel, users) - onLeaveChannel (cid, channel, users) + onChannelJoin(cid, channel, users) + onChannelLeave(cid, channel, users) + onChannelRequest(cid, channel, custom) onLook(cid, thing, position, lookDistance) onThink(cid, interval) onTextEdit(cid, item, newText) + onHouseEdit(cid, house, list, text) onReportBug(cid, comment) - onAreaCombat(cid, tileItem, tilePosition, isAggressive) - onPush(cid, target) + onReportViolation(cid, type, reason, name, comment, translation, statementId) + onThankYou(cid, statementId) + onAreaCombat(cid, ground, position, aggressive) + onPush(cid, target, ground, position) onTarget(cid, target) onFollow(cid, target) - onCombat(cid, target) + onCombat(cid, target, aggressive) onAttack(cid, target) onCast(cid, target) - onKill(cid, target, lastHit) + onKill(cid, target, damage, flags, war) onDeath(cid, corpse, deathList) onPrepareDeath(cid, deathList) + onThrow(cid, item, fromPosition, toPosition) *GLOBALEVENTS File: globalevents/globalevents.xml @@ -91,21 +102,23 @@ type Server events, script can be executed at server start, shutdown or players record. Values: - start + startup shutdown + globalsave record interval - Script will be executed every x seconds. + Script will be executed every x miliseconds. Ignored if 'type' specified. time Script will be executed at specified time. - For example: '12:00' + For example: '12:00:55' Functions - onThink(interval, lastExecution, thinkInterval) + onThink(interval) onStartup() onShutdown() + onGlobalSave onRecord(current, old, cid) - onTimer() + onTime() *MOVEMENTS File: movements/movements.xml @@ -156,7 +169,7 @@ *SPELLS File: spells/spells.xml Attributes - //TODO + //TODO :( Function: onCastSpell(cid, var) *TALKACTIONS @@ -170,6 +183,8 @@ Read "words". access Access required. + groups + Groups allowed to use. channel Will work only on specified channel. filter @@ -179,17 +194,17 @@ If you use 'word' - you will need to write /goto PlayerName If you use 'word-spaced' - you are able to make seperate commands with space between word(s) Values: quotation/word/word-spaced - log + logged Defines if talkaction should be logged. Default: no - hide + hidden Defines if talkaction should be hidden from displaying it when using /commands case-sensitive Defines if talkaction should be case sensitive. Is 'no' - then /command will be same as /CoMmAnD Default: yes exception List of players, who can't use this talkaction. - Example: "GM John;GM 2;Elf" + Example: "GM John;GM 2;Maatt" Function: onSay(cid, words, param, channel) *WEAPONS diff --git a/doc/SIGNALS b/doc/SIGNALS index 222e582..0829241 100644 --- a/doc/SIGNALS +++ b/doc/SIGNALS @@ -1,9 +1,9 @@ -[ CUSTOM FLAGS +[ SIGNALS Project Name The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson diff --git a/doc/SQL_QUERIES b/doc/SQL_QUERIES index 5e455a0..17372f3 100644 --- a/doc/SQL_QUERIES +++ b/doc/SQL_QUERIES @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -26,7 +26,7 @@ Use only while server is OFFLINE! Oh, and be careful while using them! - Credits for few queries, taken from otland + Credits for few queries, taken from OtLand Jester, Jonern ] @@ -38,6 +38,10 @@ In this example 7 days UPDATE `accounts` SET `premdays` = `premdays` + 7; + Give premium days to players who have logged in last 24 hours + In this example, 3 days + UPDATE `accounts` SET `premdays` = `premdays` + 3 WHERE `id` IN (SELECT `account_id` FROM `players` WHERE `lastlogin` > (UNIX_TIMESTAMP() - 86400) GROUP BY `account_id`); + Change all players position UPDATE `players` SET `posx` = 111, `posy` = 222, `posz` = 7; @@ -45,7 +49,7 @@ UPDATE `players` SET `blessings` = 0; Clean all players deaths - TRUNCATE TABLE `player_deaths`; + DELETE FROM `player_deaths`, `killers`, `player_killers`, `environment_killers`; Delete inactive players In this example below level 50, 20 days of inactivity diff --git a/doc/TODO b/doc/TODO index 46b1976..65e25ec 100644 --- a/doc/TODO +++ b/doc/TODO @@ -3,7 +3,7 @@ The Forgotten Server Version - 0.3.6 + 0.3.7 Codenamed Crying Damson @@ -15,10 +15,10 @@ http://otland.net/ ] -[ BUGS - Ghosts stacking may cause debug to the stacker (8.53+) - Assigned to: Elf +[ LIST + Auction (Market) System from Tibia 9.4 + Assigned to: Talaturen, Elf - RVW control through the Comment not working - Assigned to: Elf + Tibia's position swapping: + Assigned to: ? ] diff --git a/doc/TODO_04 b/doc/TODO_04 index 9620d13..4b64405 100644 --- a/doc/TODO_04 +++ b/doc/TODO_04 @@ -1,35 +1,25 @@ [ 0.4 - Pozwolę sobie napisac po polsku, bo z angielskim u mnie słabo, a i tak prawie sami tu jesteśmy... - Piszcie swoje propozycje co do 0.4- narazie wypiszę wszystko co przyszło mi do glowy, albo ustaliliśmy z Elfem. - TODO: - * Finish boost_thread support - * GUI w QT/wxWidgets - * Multilang (szczególnie RET_* i możliwie wszystko pozostałe) - * Manual do komend- przykładowe użycie, możliwe wartości, używane w grze, możliwość deaktywacji w configu- przykład: /man serverinfo, /man /goto, /a man, itd. - * Stabilność :D! - * Nowy podział plików: - * configs/ - * database/main.lua - * config.lua/.xml - * Przeniesione data/XML/ - * logs/ - * tfadmin.log - * crash.log - * client_assertions.log - * bots/ - * talkactions/ - * chat/ - * server/ - * world/ - data/world/ - łatwiejsza podmiana data/ - * langs/ - Tłumaczenia: polish.lua, english.lua, etc + * Multi-language (especially RET_* and most likely other stuff) + * New files structure: + * config/ + * config.lua + * Stuff from data/XML/ + * lang/ + Translations: polish.lua, english.lua, and so on * data/ - Po staremu prócz world/, XML/ i logs/ - * docs/ - * TheForgottenServer.exe - * forgottenserver.s3db + As it was, but without XML/ and world/ + * maps/ + * Stuff from data/world + * mods/ ] + +[ + Optimize packets to only one-per-tile after cleaning map or house + Assigned to: Elf + + Deprecate several hardcoded systems (guild management, etc) and rescript them in Lua + Assigned to: jeybi, Elf (?) +] \ No newline at end of file diff --git a/enums.h b/enums.h index 1db49b0..5bac19b 100644 --- a/enums.h +++ b/enums.h @@ -21,20 +21,54 @@ #include #include +#define PLAYER_ID_RANGE 0x10000000 +#define MONSTER_ID_RANGE 0x40000000 +#define NPC_ID_RANGE 0x80000000 + +enum MarketAction_t +{ + MARKETACTION_BUY = 0, + MARKETACTION_SELL = 1 +}; + +enum MarketRequest_t +{ + MARKETREQUEST_OWN_OFFERS = 0xFFFE, + MARKETREQUEST_OWN_HISTORY = 0xFFFF +}; + +enum MarketOfferState_t +{ + OFFERSTATE_ACTIVE = 0, + OFFERSTATE_CANCELLED = 1, + OFFERSTATE_EXPIRED = 2, + OFFERSTATE_ACCEPTED = 3, + + OFFERSTATE_ACCEPTEDEX = 255 +}; + +enum CreatureType_t +{ + CREATURETYPE_PLAYER = 0, + CREATURETYPE_MONSTER = 1, + CREATURETYPE_NPC = 2 +}; + enum DatabaseEngine_t { DATABASE_ENGINE_NONE = 0, DATABASE_ENGINE_MYSQL, DATABASE_ENGINE_SQLITE, - DATABASE_ENGINE_POSTGRESQL, - DATABASE_ENGINE_ODBC + DATABASE_ENGINE_POSTGRESQL }; enum Encryption_t { ENCRYPTION_PLAIN = 0, ENCRYPTION_MD5, - ENCRYPTION_SHA1 + ENCRYPTION_SHA1, + ENCRYPTION_SHA256, + ENCRYPTION_SHA512 }; enum GuildLevel_t @@ -45,39 +79,15 @@ enum GuildLevel_t GUILDLEVEL_LEADER }; -enum OperatingSystem_t -{ - CLIENTOS_LINUX = 0x01, - CLIENTOS_WINDOWS = 0x02 -}; - enum Channels_t { CHANNEL_GUILD = 0x00, CHANNEL_PARTY = 0x01, - CHANNEL_RVR = 0x03, - CHANNEL_HELP = 0x09, + CHANNEL_HELP = 0x07, CHANNEL_DEFAULT = 0xFFFE, //internal usage only, there is no such channel CHANNEL_PRIVATE = 0xFFFF }; -enum ViolationAction_t -{ - ACTION_NOTATION = 0, - ACTION_NAMEREPORT, - ACTION_BANISHMENT, - ACTION_BANREPORT, - ACTION_BANFINAL, - ACTION_BANREPORTFINAL, - ACTION_STATEMENT, - //internal use - ACTION_DELETION, - ACTION_NAMELOCK, - ACTION_BANLOCK, - ACTION_BANLOCKFINAL, - ACTION_PLACEHOLDER -}; - enum RaceType_t { RACE_NONE = 0, @@ -90,21 +100,24 @@ enum RaceType_t enum CombatType_t { - COMBAT_FIRST = 0, - COMBAT_NONE = COMBAT_FIRST, + COMBAT_NONE = 0x00, + COMBAT_ALL = COMBAT_NONE, /* for internal use only.*/ + COMBAT_PHYSICALDAMAGE = 1 << 0, - COMBAT_ENERGYDAMAGE = 1 << 1, - COMBAT_EARTHDAMAGE = 1 << 2, - COMBAT_FIREDAMAGE = 1 << 3, + COMBAT_ENERGYDAMAGE = 1 << 1, + COMBAT_EARTHDAMAGE = 1 << 2, + COMBAT_FIREDAMAGE = 1 << 3, COMBAT_UNDEFINEDDAMAGE = 1 << 4, - COMBAT_LIFEDRAIN = 1 << 5, - COMBAT_MANADRAIN = 1 << 6, - COMBAT_HEALING = 1 << 7, - COMBAT_DROWNDAMAGE = 1 << 8, - COMBAT_ICEDAMAGE = 1 << 9, - COMBAT_HOLYDAMAGE = 1 << 10, - COMBAT_DEATHDAMAGE = 1 << 11, - COMBAT_LAST = COMBAT_DEATHDAMAGE + COMBAT_LIFEDRAIN = 1 << 5, + COMBAT_MANADRAIN = 1 << 6, + COMBAT_HEALING = 1 << 7, + COMBAT_DROWNDAMAGE = 1 << 8, + COMBAT_ICEDAMAGE = 1 << 9, + COMBAT_HOLYDAMAGE = 1 << 10, + COMBAT_DEATHDAMAGE = 1 << 11, + + COMBAT_FIRST = COMBAT_NONE, + COMBAT_LAST = COMBAT_DEATHDAMAGE }; enum CombatParam_t @@ -123,7 +136,9 @@ enum CombatParam_t COMBATPARAM_TARGETPLAYERSORSUMMONS, COMBATPARAM_DIFFERENTAREADAMAGE, COMBATPARAM_HITEFFECT, - COMBATPARAM_HITCOLOR + COMBATPARAM_HITCOLOR, + COMBATPARAM_ELEMENTTYPE, + COMBATPARAM_ELEMENTDAMAGE }; enum CallBackParam_t @@ -138,50 +153,62 @@ enum CallBackParam_t enum ConditionParam_t { CONDITIONPARAM_OWNER = 1, - CONDITIONPARAM_TICKS = 2, - CONDITIONPARAM_OUTFIT = 3, - CONDITIONPARAM_HEALTHGAIN = 4, - CONDITIONPARAM_HEALTHTICKS = 5, - CONDITIONPARAM_MANAGAIN = 6, - CONDITIONPARAM_MANATICKS = 7, - CONDITIONPARAM_DELAYED = 8, - CONDITIONPARAM_SPEED = 9, - CONDITIONPARAM_LIGHT_LEVEL = 10, - CONDITIONPARAM_LIGHT_COLOR = 11, - CONDITIONPARAM_SOULGAIN = 12, - CONDITIONPARAM_SOULTICKS = 13, - CONDITIONPARAM_MINVALUE = 14, - CONDITIONPARAM_MAXVALUE = 15, - CONDITIONPARAM_STARTVALUE = 16, - CONDITIONPARAM_TICKINTERVAL = 17, - CONDITIONPARAM_FORCEUPDATE = 18, - CONDITIONPARAM_SKILL_MELEE = 19, - CONDITIONPARAM_SKILL_FIST = 20, - CONDITIONPARAM_SKILL_CLUB = 21, - CONDITIONPARAM_SKILL_SWORD = 22, - CONDITIONPARAM_SKILL_AXE = 23, - CONDITIONPARAM_SKILL_DISTANCE = 24, - CONDITIONPARAM_SKILL_SHIELD = 25, - CONDITIONPARAM_SKILL_FISHING = 26, - CONDITIONPARAM_STAT_MAXHEALTH = 27, - CONDITIONPARAM_STAT_MAXMANA = 28, - CONDITIONPARAM_STAT_SOUL = 29, - CONDITIONPARAM_STAT_MAGICLEVEL = 30, - CONDITIONPARAM_STAT_MAXHEALTHPERCENT = 31, - CONDITIONPARAM_STAT_MAXMANAPERCENT = 32, - CONDITIONPARAM_STAT_SOULPERCENT = 33, - CONDITIONPARAM_STAT_MAGICLEVELPERCENT = 34, - CONDITIONPARAM_SKILL_MELEEPERCENT = 35, - CONDITIONPARAM_SKILL_FISTPERCENT = 36, - CONDITIONPARAM_SKILL_CLUBPERCENT = 37, - CONDITIONPARAM_SKILL_SWORDPERCENT = 38, - CONDITIONPARAM_SKILL_AXEPERCENT = 39, - CONDITIONPARAM_SKILL_DISTANCEPERCENT = 40, - CONDITIONPARAM_SKILL_SHIELDPERCENT = 41, - CONDITIONPARAM_SKILL_FISHINGPERCENT = 42, - CONDITIONPARAM_PERIODICDAMAGE = 43, - CONDITIONPARAM_BUFF = 44, - CONDITIONPARAM_SUBID = 45 + CONDITIONPARAM_TICKS, + CONDITIONPARAM_OUTFIT, + CONDITIONPARAM_HEALTHGAIN, + CONDITIONPARAM_HEALTHTICKS, + CONDITIONPARAM_MANAGAIN, + CONDITIONPARAM_MANATICKS, + CONDITIONPARAM_DELAYED, + CONDITIONPARAM_SPEED, + CONDITIONPARAM_LIGHT_LEVEL, + CONDITIONPARAM_LIGHT_COLOR, + CONDITIONPARAM_SOULGAIN, + CONDITIONPARAM_SOULTICKS, + CONDITIONPARAM_MINVALUE, + CONDITIONPARAM_MAXVALUE, + CONDITIONPARAM_STARTVALUE, + CONDITIONPARAM_TICKINTERVAL, + CONDITIONPARAM_FORCEUPDATE, + CONDITIONPARAM_SKILL_MELEE, + CONDITIONPARAM_SKILL_FIST, + CONDITIONPARAM_SKILL_CLUB, + CONDITIONPARAM_SKILL_SWORD, + CONDITIONPARAM_SKILL_AXE, + CONDITIONPARAM_SKILL_DISTANCE, + CONDITIONPARAM_SKILL_SHIELD, + CONDITIONPARAM_SKILL_FISHING, + CONDITIONPARAM_STAT_MAXHEALTH, + CONDITIONPARAM_STAT_MAXMANA, + CONDITIONPARAM_STAT_SOUL, + CONDITIONPARAM_STAT_MAGICLEVEL, + CONDITIONPARAM_STAT_MAXHEALTHPERCENT, + CONDITIONPARAM_STAT_MAXMANAPERCENT, + CONDITIONPARAM_STAT_SOULPERCENT, + CONDITIONPARAM_STAT_MAGICLEVELPERCENT, + CONDITIONPARAM_SKILL_MELEEPERCENT, + CONDITIONPARAM_SKILL_FISTPERCENT, + CONDITIONPARAM_SKILL_CLUBPERCENT, + CONDITIONPARAM_SKILL_SWORDPERCENT, + CONDITIONPARAM_SKILL_AXEPERCENT, + CONDITIONPARAM_SKILL_DISTANCEPERCENT, + CONDITIONPARAM_SKILL_SHIELDPERCENT, + CONDITIONPARAM_SKILL_FISHINGPERCENT, + CONDITIONPARAM_PERIODICDAMAGE, + CONDITIONPARAM_BUFF, + CONDITIONPARAM_SUBID, + CONDITIONPARAM_FIELD +}; + +enum Exhaust_t +{ + EXHAUST_OTHER = 0, + EXHAUST_SPELLGROUP_NONE = 1, + EXHAUST_SPELLGROUP_ATTACK = 2, + EXHAUST_SPELLGROUP_HEALING = 3, + EXHAUST_SPELLGROUP_SUPPORT = 4, + EXHAUST_SPELLGROUP_SPECIAL = 5, + EXHAUST_MELEE = 6 }; enum BlockType_t @@ -210,22 +237,6 @@ enum Increment_t INCREMENT_LAST = MAGIC_PERCENT }; -enum skills_t -{ - SKILL_FIRST = 0, - SKILL_FIST = SKILL_FIRST, - SKILL_CLUB, - SKILL_SWORD, - SKILL_AXE, - SKILL_DIST, - SKILL_SHIELD, - SKILL_FISH, - SKILL__MAGLEVEL, - SKILL__LEVEL, - SKILL_LAST = SKILL_FISH, - SKILL__LAST = SKILL__LEVEL -}; - enum stats_t { STAT_FIRST = 0, @@ -281,21 +292,65 @@ enum PlayerSex_t // own use- each female should be even and male odd. }; +enum WarType_t +{ + WAR_FIRST = 0, + WAR_GUILD = WAR_FIRST, + WAR_ENEMY, + WAR_LAST = WAR_ENEMY +}; + +struct War_t +{ + War_t() + { + war = 0; + type = WAR_FIRST; + + memset(ids, 0, sizeof(ids)); + memset(frags, 0, sizeof(frags)); + + limit = 0; + payment = 0; + } + + uint32_t war; + WarType_t type; + + uint32_t ids[WAR_LAST + 1]; + std::string names[WAR_LAST + 1]; + uint16_t frags[WAR_LAST + 1]; + + uint16_t limit; + uint64_t payment; +}; + struct Outfit_t { - Outfit_t() {lookHead = lookBody = lookLegs = lookFeet = lookType = lookTypeEx = lookAddons = 0;} - uint16_t lookType, lookTypeEx; + Outfit_t() + { + lookType = lookTypeEx = lookMount = 0; + lookHead = lookBody = lookLegs = lookFeet = lookAddons = 0; + } + Outfit_t(uint16_t _lookType) + { + lookType = _lookType; + lookTypeEx = lookMount = 0; + lookHead = lookBody = lookLegs = lookFeet = lookAddons = 0; + } + + uint16_t lookType, lookMount, lookTypeEx; uint8_t lookHead, lookBody, lookLegs, lookFeet, lookAddons; - bool operator==(const Outfit_t o) const + bool operator==(const Outfit_t& o) const { - return (o.lookAddons == lookAddons + return (o.lookAddons == lookAddons && o.lookMount == lookMount && o.lookType == lookType && o.lookTypeEx == lookTypeEx && o.lookHead == lookHead && o.lookBody == lookBody && o.lookLegs == lookLegs && o.lookFeet == lookFeet); } - bool operator!=(const Outfit_t o) const + bool operator!=(const Outfit_t& o) const { return !(*this == o); } @@ -328,5 +383,64 @@ struct ShopInfo sellPrice(_sellPrice), itemName(_itemName) {} }; +struct MarketOffer +{ + uint32_t price; + uint32_t timestamp; + uint16_t amount; + uint16_t counter; + uint16_t itemId; + std::string playerName; +}; + +struct MarketOfferEx +{ + uint32_t playerId; + uint32_t timestamp; + uint32_t price; + uint16_t amount; + uint16_t counter; + uint16_t itemId; + MarketAction_t type; + std::string playerName; +}; + +struct ExpiredMarketOffer +{ + uint32_t id; + uint32_t price; + uint16_t amount; + uint16_t itemId; + uint32_t playerId; +}; + +struct HistoryMarketOffer +{ + uint32_t timestamp; + uint32_t price; + uint16_t itemId; + uint16_t amount; + MarketOfferState_t state; +}; + +struct MarketStatistics +{ + MarketStatistics() + { + numTransactions = 0; + highestPrice = 0; + totalPrice = 0; + lowestPrice = 0; + } + + uint32_t numTransactions; + uint32_t highestPrice; + uint64_t totalPrice; + uint32_t lowestPrice; +}; + +typedef std::list MarketOfferList; +typedef std::list ExpiredMarketOfferList; +typedef std::list HistoryMarketOfferList; typedef std::list ShopInfoList; #endif diff --git a/exception.cpp b/exception.cpp index 07c2a3c..b05be06 100644 --- a/exception.cpp +++ b/exception.cpp @@ -14,134 +14,181 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// -#ifdef __EXCEPTION_TRACER__ #include "otpch.h" -#include "exception.h" - +#ifdef __EXCEPTION_TRACER__ +#include #include -#include #include -#ifdef WINDOWS -#include -#include -#endif - -#include -#include "tools.h" - -#include "configmanager.h" -extern ConfigManager g_config; - -typedef std::map FunctionMap; -FunctionMap functionMap; +#include +#include +#include +#include +#include "exception.h" -uint32_t offMax, offMin; -bool mapLoaded = false; -boost::recursive_mutex mapLock; +#ifdef DEBUG_REPORT + #undef DEBUG_REPORT +#endif +#define DEBUG_REPORT ExceptionHandler::dumpStack(); #ifdef WINDOWS -void printPointer(std::ostream* output, uint32_t p); -EXCEPTION_DISPOSITION __cdecl _SEHHandler(struct _EXCEPTION_RECORD *ExceptionRecord, void* EstablisherFrame, - struct _CONTEXT *ContextRecord, void* DispatcherContext); + int ExceptionHandler::ref_counter = 0; +#else //Unix/Linux + #include + #include + #include + #include + #include /* POSIX.1-2001 */ + + extern time_t start_time; + void _SigHandler(int signum, siginfo_t *info, void* secret); + #ifndef COMPILER_STRING + #define COMPILER_STRING "" + #endif + + #define COMPILATION_DATE __DATE__ " " __TIME__ #endif ExceptionHandler::ExceptionHandler() { - installed = false; + isInstalled = false; } ExceptionHandler::~ExceptionHandler() { - if(installed) + if(isInstalled) RemoveHandler(); } bool ExceptionHandler::InstallHandler() { - #ifdef WINDOWS - boost::recursive_mutex::scoped_lock lockObj(mapLock); - if(!mapLoaded) - LoadMap(); - - if(installed) - return false; - - /* - mov eax,fs:[0] - mov [prevSEH],eax - mov [chain].prev,eax - mov [chain].SEHfunction,_SEHHandler - lea eax,[chain] - mov fs:[0],eax - */ - - #ifdef __GNUC__ - SEHChain *prevSEH; - __asm__ ("movl %%fs:0,%%eax;movl %%eax,%0;":"=r"(prevSEH)::"%eax"); - chain.prev = prevSEH; - chain.SEHfunction = (void*)&_SEHHandler; - __asm__("movl %0,%%eax;movl %%eax,%%fs:0;": : "g" (&chain):"%eax"); - #endif - #endif +#ifdef WINDOWS + ++ref_counter; + if(ref_counter == 1) + SetUnhandledExceptionFilter(ExceptionHandler::MiniDumpExceptionHandler); + + //Unix/Linux +#else + struct sigaction sa; + sa.sa_sigaction = &_SigHandler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_SIGINFO; + + sigaction(SIGILL, &sa, NULL); // illegal instruction + sigaction(SIGSEGV, &sa, NULL); // segmentation fault + sigaction(SIGFPE, &sa, NULL); // floating-point exception +#endif - installed = true; + isInstalled = true; return true; } - bool ExceptionHandler::RemoveHandler() { - if(!installed) + if(!isInstalled) return false; - #ifdef WINDOWS - #ifdef __GNUC__ - __asm__ ("movl %0,%%eax;movl %%eax,%%fs:0;"::"r"(chain.prev):"%eax" ); - #endif - #endif +#ifdef WINDOWS + --ref_counter; + if(ref_counter == 0) + SetUnhandledExceptionFilter(NULL); + +//Unix/Linux +#else + signal(SIGILL, SIG_DFL); // illegal instruction + signal(SIGSEGV, SIG_DFL); // segmentation fault + signal(SIGFPE, SIG_DFL); // floating-point exception +#endif - installed = false; + isInstalled = false; return true; } -char* getFunctionName(unsigned long addr, unsigned long& start) +#ifdef WINDOWS +long ExceptionHandler::MiniDumpExceptionHandler(EXCEPTION_POINTERS* exceptionPointers /*= NULL*/) { - FunctionMap::iterator functions; - if(addr < offMin || addr > offMax) - return NULL; + // Alerts the user about what is happening + std::clog << "Unhandled exception, generating minidump..." << std::endl; + + // Get system time + SYSTEMTIME systemTime; + GetSystemTime(&systemTime); + + // Format file name + // "theforgottenserver_DD-MM-YYYY_HH-MM-SS.mdmp" + char fileName[64] = {"\0"}; + sprintf(fileName, "theforgottenserver_%02u-%02u-%04u_%02u-%02u-%02u.mdmp", + systemTime.wDay, systemTime.wMonth, systemTime.wYear, + systemTime.wHour, systemTime.wMinute, systemTime.wSecond); + + // Create the dump file + HANDLE hFile = CreateFileA(fileName, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + // If we cannot create the file, then we cannot dump the memory + if(!hFile || hFile == INVALID_HANDLE_VALUE) + { + std::clog << "Cannot create dump file, error: " << GetLastError() << std::endl; + return EXCEPTION_CONTINUE_SEARCH; + } + + // Collect the exception information + MINIDUMP_EXCEPTION_INFORMATION exceptionInformation; + exceptionInformation.ClientPointers = FALSE; + exceptionInformation.ExceptionPointers = exceptionPointers; + exceptionInformation.ThreadId = GetCurrentThreadId(); + + // Get the process and it's Id + HANDLE hProcess = GetCurrentProcess(); + DWORD ProcessId = GetProcessId(hProcess); + + // Dump flags + MINIDUMP_TYPE flags = (MINIDUMP_TYPE)(MiniDumpNormal); + + // Write dump to file + BOOL dumpResult = MiniDumpWriteDump(hProcess, ProcessId, hFile, flags, + &exceptionInformation, NULL, NULL); - for(FunctionMap::iterator functions = functionMap.begin(); functions != functionMap.end(); ++functions) + // Delete the dump file if we cannot generate the crash trace + if(!dumpResult) { - if(functions->first <= addr || functions == functionMap.begin()) - continue; + std::clog << "Cannot generate minidump, error: " << GetLastError() << std::endl; - functions--; - start = functions->first; - return functions->second; + //Close file and delete it + CloseHandle(hFile); + DeleteFileA(fileName); + + return EXCEPTION_CONTINUE_SEARCH; } - return NULL; -} + // Close dump file + CloseHandle(hFile); -#ifdef WINDOWS -EXCEPTION_DISPOSITION __cdecl _SEHHandler(struct _EXCEPTION_RECORD *ExceptionRecord, void* EstablisherFrame, - struct _CONTEXT *ContextRecord, void* DispatcherContext) + return EXCEPTION_EXECUTE_HANDLER; +} +#else //Unix/Linux +#define BACKTRACE_DEPTH 128 +void _SigHandler(int signum, siginfo_t *info, void* secret) { - uint32_t *esp; - uint32_t *next_ret; - uint32_t stack_val; - uint32_t *stacklimit; - uint32_t *stackstart; - uint32_t nparameters = 0; - uint32_t file,foundRetAddress = 0; - _MEMORY_BASIC_INFORMATION mbi; + bool file; + + int addrs; + void* buffer[BACKTRACE_DEPTH]; + char** symbols; + + ucontext_t context = *(ucontext_t*)secret; + rusage resources; + rlimit resourcelimit; + greg_t esp = 0; + tm *ts; + char date_buff[80]; std::ostream *outdriver; - std::cout << ">> CRASH: Writing report file..." << std::endl; - std::ofstream output(getFilePath(FILE_TYPE_LOG, "server/exceptions.log").c_str(), std::ios_base::app); + std::clog << "Error: generating report file..." <flags(std::ios::hex | std::ios::showbase); - *outdriver << "Exception: " << (uint32_t)ExceptionRecord->ExceptionCode << " at eip = " << (uint32_t)ExceptionRecord->ExceptionAddress; - FunctionMap::iterator functions; - unsigned long functionAddr; - char* functionName = getFunctionName((unsigned long)ExceptionRecord->ExceptionAddress, functionAddr); - if(functionName) - *outdriver << "(" << functionName << " - " << functionAddr << ")"; - - //registers - *outdriver << std::endl; - *outdriver << "eax = ";printPointer(outdriver,ContextRecord->Eax);*outdriver << std::endl; - *outdriver << "ebx = ";printPointer(outdriver,ContextRecord->Ebx);*outdriver << std::endl; - *outdriver << "ecx = ";printPointer(outdriver,ContextRecord->Ecx);*outdriver << std::endl; - *outdriver << "edx = ";printPointer(outdriver,ContextRecord->Edx);*outdriver << std::endl; - *outdriver << "esi = ";printPointer(outdriver,ContextRecord->Esi);*outdriver << std::endl; - *outdriver << "edi = ";printPointer(outdriver,ContextRecord->Edi);*outdriver << std::endl; - *outdriver << "ebp = ";printPointer(outdriver,ContextRecord->Ebp);*outdriver << std::endl; - *outdriver << "esp = ";printPointer(outdriver,ContextRecord->Esp);*outdriver << std::endl; - *outdriver << "efl = " << ContextRecord->EFlags << std::endl; - - //stack dump - esp = (uint32_t *)(ContextRecord->Esp); - VirtualQuery(esp, &mbi, sizeof(mbi)); - stacklimit = (uint32_t*)((uint32_t)(mbi.BaseAddress) + mbi.RegionSize); + *outdriver << "Signal: " << signum; - *outdriver << std::endl; - *outdriver << "---Stack Trace---" << std::endl; - *outdriver << "From: " << (uint32_t)esp << - " to: " << (uint32_t)stacklimit << std::endl; - - stackstart = esp; - next_ret = (uint32_t*)(ContextRecord->Ebp); - uint32_t frame_param_counter; - frame_param_counter = 0; - while(esp < stacklimit) { - stack_val = *esp; - if(foundRetAddress) - nparameters++; + #if __WORDSIZE == 32 + *outdriver << " at eip = " << context.uc_mcontext.gregs[REG_EIP] << std::endl; + *outdriver << "eax = " << context.uc_mcontext.gregs[REG_EAX] << std::endl; + *outdriver << "ebx = " << context.uc_mcontext.gregs[REG_EBX] << std::endl; + *outdriver << "ecx = " << context.uc_mcontext.gregs[REG_ECX] << std::endl; + *outdriver << "edx = " << context.uc_mcontext.gregs[REG_EDX] << std::endl; + *outdriver << "esi = " << context.uc_mcontext.gregs[REG_ESI] << std::endl; + *outdriver << "edi = " << context.uc_mcontext.gregs[REG_EDI] << std::endl; + *outdriver << "ebp = " << context.uc_mcontext.gregs[REG_EBP] << std::endl; + *outdriver << "esp = " << context.uc_mcontext.gregs[REG_ESP] << std::endl; + *outdriver << "efl = " << context.uc_mcontext.gregs[REG_EFL] << std::endl; + esp = context.uc_mcontext.gregs[REG_ESP]; + #else // 64-bit + *outdriver << " at rip = " << context.uc_mcontext.gregs[REG_RIP] << std::endl; + *outdriver << "rax = " << context.uc_mcontext.gregs[REG_RAX] << std::endl; + *outdriver << "rbx = " << context.uc_mcontext.gregs[REG_RBX] << std::endl; + *outdriver << "rcx = " << context.uc_mcontext.gregs[REG_RCX] << std::endl; + *outdriver << "rdx = " << context.uc_mcontext.gregs[REG_RDX] << std::endl; + *outdriver << "rsi = " << context.uc_mcontext.gregs[REG_RSI] << std::endl; + *outdriver << "rdi = " << context.uc_mcontext.gregs[REG_RDI] << std::endl; + *outdriver << "rbp = " << context.uc_mcontext.gregs[REG_RBP] << std::endl; + *outdriver << "rsp = " << context.uc_mcontext.gregs[REG_RSP] << std::endl; + *outdriver << "efl = " << context.uc_mcontext.gregs[REG_EFL] << std::endl; + esp = context.uc_mcontext.gregs[REG_RSP]; + #endif + } + outdriver->flush(); + *outdriver << std::endl; - if(esp - stackstart < 20 || nparameters < 10 || std::abs(esp - next_ret) < 10 || frame_param_counter < 8) - { - *outdriver << (uint32_t)esp << " | "; - printPointer(outdriver,stack_val); - if(esp == next_ret) - *outdriver << " \\\\\\\\\\\\ stack frame //////"; - else if(esp - next_ret == 1) - *outdriver << " <-- ret" ; - else if(esp - next_ret == 2) - { - next_ret = (uint32_t*)*(esp - 2); - frame_param_counter = 0; - } - - frame_param_counter++; - *outdriver<< std::endl; + // stack backtrace + addrs = backtrace(buffer, BACKTRACE_DEPTH); + symbols = backtrace_symbols(buffer, addrs); + if(symbols != NULL && addrs != 0) { + *outdriver << "---Stack Trace---" << std::endl; + if(esp != 0) { + *outdriver << "From: " << (unsigned long)esp << + " to: " << (unsigned long)(esp+addrs) << std::endl; } - - if(stack_val >= offMin && stack_val <= offMax) + for(int i = 0; i != addrs; ++i) { - foundRetAddress++; - // - unsigned long functionAddr; - char* functionName = getFunctionName(stack_val, functionAddr); - output << (unsigned long)esp << " " << functionName << "(" << - functionAddr << ")" << std::endl; + *outdriver << symbols[i] << std::endl; } - - esp++; } + outdriver->flush(); - *outdriver << "*****************************************************" << std::endl; - if(file) + if(file) { ((std::ofstream*)outdriver)->close(); - - if(g_config.getBool(ConfigManager::TRACER_BOX)) - { - std::stringstream ss; - ss << "If you want developers review this crash log, please open a tracker ticket for the software at OtLand.net and attach the " << getFilePath(FILE_TYPE_LOG, "server/exceptions.log") << " file."; - MessageBoxA(NULL, ss.str().c_str(), "Error", MB_OK | MB_ICONERROR); } - std::cout << "> Crash report generated, killing server." << std::endl; - exit(1); - return ExceptionContinueSearch; -} - -void printPointer(std::ostream* output, uint32_t p) -{ - *output << p; - if(!IsBadReadPtr((void*)p, 4)) - *output << " -> " << *(uint32_t*)p; + _exit(1); } #endif - -bool ExceptionHandler::LoadMap() -{ - #ifdef __GNUC__ - if(mapLoaded) - return false; - - functionMap.clear(); - installed = false; - //load map file if exists - char line[1024]; - FILE* input = fopen("forgottenserver.map", "r"); - offMin = 0xFFFFFF; - offMax = 0; - int32_t n = 0; - if(!input) - { - MessageBoxA(NULL, "Failed loading symbols, forgottenserver.map file not found.", "Error", MB_OK | MB_ICONERROR); - std::cout << "Failed loading symbols, forgottenserver.map file not found. " << std::endl; - exit(1); - return false; - } - - //read until found .text 0x00401000 - while(fgets(line, 1024, input)) - { - if(!memcmp(line, ".text",5)) - break; - } - - if(feof(input)) - return false; - - char tofind[] = "0x"; - char lib[] = ".a("; - while(fgets(line, 1024, input)) - { - char* pos = strstr(line, lib); - if(pos) - break; //not load libs - - pos = strstr(line, tofind); - if(pos) - { - //read hex offset - char hexnumber[12]; - strncpy(hexnumber, pos, 10); - hexnumber[10] = 0; - char* pEnd; - uint32_t offset = strtol(hexnumber, &pEnd, 0); - if(offset) - { - //read function name - char* pos2 = pos + 12; - while(*pos2 != 0) - { - if(*pos2 != ' ') - break; - - pos2++; - } - - if(*pos2 == 0 || (*pos2 == '0' && *(pos2+1) == 'x')) - continue; - - char* name = new char[strlen(pos2)+1]; - strcpy(name, pos2); - name[strlen(pos2) - 1] = 0; - functionMap[offset] = name; - if(offset > offMax) - offMax = offset; - if(offset < offMin) - offMin = offset; - n++; - } - } - } - - //close file - fclose(input); - mapLoaded = true; - #endif - return true; -} - -void ExceptionHandler::dumpStack() -{ - #ifndef __GNUC__ - return; - #endif - - uint32_t *esp; - uint32_t *next_ret; - uint32_t stack_val; - uint32_t *stacklimit; - uint32_t *stackstart; - uint32_t nparameters = 0; - uint32_t foundRetAddress = 0; - _MEMORY_BASIC_INFORMATION mbi; - - std::cout << ">> CRASH: Writing report file..." << std::endl; - std::ofstream output(getFilePath(FILE_TYPE_LOG, "server/exceptions.log").c_str(), std::ios_base::app); - output.flags(std::ios::hex | std::ios::showbase); - time_t rawtime; - time(&rawtime); - output << "*****************************************************" << std::endl; - output << "Stack dump - " << std::ctime(&rawtime) << std::endl; - output << "Compiler Info - " << BOOST_COMPILER << std::endl; - output << "Compilation Date - " << __DATE__ << " " << __TIME__ << std::endl << std::endl; - - #ifdef __GNUC__ - __asm__ ("movl %%esp, %0;":"=r"(esp)::); - #else - // - #endif - - VirtualQuery(esp, &mbi, sizeof(mbi)); - stacklimit = (uint32_t*)((uint32_t)(mbi.BaseAddress) + mbi.RegionSize); - - output << "---Stack Trace---" << std::endl; - output << "From: " << (uint32_t)esp << - " to: " << (uint32_t)stacklimit << std::endl; - - stackstart = esp; - #ifdef __GNUC__ - __asm__ ("movl %%ebp, %0;":"=r"(next_ret)::); - #else - // - #endif - uint32_t frame_param_counter; - frame_param_counter = 0; - while(esp < stacklimit) - { - stack_val = *esp; - if(foundRetAddress) - nparameters++; - - if(esp - stackstart < 20 || nparameters < 10 || std::abs(esp - next_ret) < 10 || frame_param_counter < 8) - { - output << (uint32_t)esp << " | "; - printPointer(&output, stack_val); - if(esp == next_ret) - output << " \\\\\\\\\\\\ stack frame //////"; - else if(esp - next_ret == 1) - output << " <-- ret" ; - else if(esp - next_ret == 2) - { - next_ret = (uint32_t*)*(esp - 2); - frame_param_counter = 0; - } - - frame_param_counter++; - output << std::endl; - } - - if(stack_val >= offMin && stack_val <= offMax) - { - foundRetAddress++; - // - unsigned long functionAddr; - char* functionName = getFunctionName(stack_val, functionAddr); - output << (unsigned long)esp << " " << functionName << "(" << - functionAddr << ")" << std::endl; - } - - esp++; - } - - output << "*****************************************************" << std::endl; - output.close(); - std::cout << "> Crash report generated, killing server." << std::endl; -} #endif diff --git a/exception.h b/exception.h index b5ef0f6..555261d 100644 --- a/exception.h +++ b/exception.h @@ -18,30 +18,31 @@ #ifdef __EXCEPTION_TRACER__ #ifndef __EXCEPTION_H__ #define __EXCEPTION_H__ + #include "otsystem.h" +#ifdef WINDOWS + #include + #include + #include +#endif class ExceptionHandler { public: ExceptionHandler(); - virtual ~ExceptionHandler(); + ~ExceptionHandler(); bool InstallHandler(); bool RemoveHandler(); - static void dumpStack(); - private: - bool LoadMap(); - - struct SEHChain - { - SEHChain *prev; - void *SEHfunction; - }; + bool isInstalled; +#ifdef WINDOWS - SEHChain chain; - bool installed; -}; + static long WINAPI MiniDumpExceptionHandler(EXCEPTION_POINTERS* exceptionPointers = NULL); + static int ref_counter; #endif +}; + #endif +#endif \ No newline at end of file diff --git a/fileloader.cpp b/fileloader.cpp index 44251a6..4736cab 100644 --- a/fileloader.cpp +++ b/fileloader.cpp @@ -28,8 +28,7 @@ FileLoader::FileLoader() //cache m_use_cache = false; m_cache_size = 0; - m_cache_index = NO_VALID_CACHE; - m_cache_offset = NO_VALID_CACHE; + m_cache_index = m_cache_offset = NO_VALID_CACHE; memset(m_cached_data, 0, sizeof(m_cached_data)); } @@ -37,25 +36,41 @@ FileLoader::~FileLoader() { if(m_file) { +#ifdef __USE_ZLIB__ + gzclose(m_file); +#else fclose(m_file); +#endif m_file = NULL; } NodeStruct::clearNet(m_root); delete[] m_buffer; - for(int32_t i = 0; i < CACHE_BLOCKS; i++) + for(int32_t i = 0; i < CACHE_BLOCKS; ++i) { if(m_cached_data[i].data) delete[] m_cached_data[i].data; } } -bool FileLoader::openFile(const char* filename, bool write, bool caching /*= false*/) +bool FileLoader::openFile(std::string name, bool write, bool caching/* = false*/) { uint32_t version = 0; if(write) { - m_file = fopen(filename, "wb"); +#ifdef __USE_ZLIB__ + m_file = gzopen(name.c_str(), "wb"); +#else + m_file = fopen(name.c_str(), "wb"); +#endif + if(!m_file) + name += ".gz"; + +#ifdef __USE_ZLIB__ + m_file = gzopen(name.c_str(), "wb"); +#else + m_file = fopen(name.c_str(), "wb"); +#endif if(m_file) { writeData(&version, sizeof(version), false); @@ -68,14 +83,34 @@ bool FileLoader::openFile(const char* filename, bool write, bool caching /*= fal } } - m_file = fopen(filename, "rb"); +#ifdef __USE_ZLIB__ + m_file = gzopen(name.c_str(), "rb"); +#else + m_file = fopen(name.c_str(), "rb"); +#endif + if(!m_file) + name += ".gz"; + +#ifdef __USE_ZLIB__ + m_file = gzopen(name.c_str(), "rb"); +#else + m_file = fopen(name.c_str(), "rb"); +#endif if(m_file) { +#ifdef __USE_ZLIB__ + if(gzread(m_file, &version, sizeof(version))) +#else if(fread(&version, sizeof(version), 1, m_file)) +#endif { if(version > 0) { +#ifdef __USE_ZLIB__ + gzclose(m_file); +#else fclose(m_file); +#endif m_file = NULL; m_lastError = ERROR_INVALID_FILE_VERSION; return false; @@ -84,9 +119,15 @@ bool FileLoader::openFile(const char* filename, bool write, bool caching /*= fal if(caching) { m_use_cache = true; +#ifdef __USE_ZLIB__ + gzseek(m_file, 0, SEEK_END); + int32_t fileSize = gztell(m_file); +#else fseek(m_file, 0, SEEK_END); - int32_t file_size = ftell(m_file); - m_cache_size = std::min(32768, std::max(file_size/20, 8192)) & ~0x1FFF; + int32_t fileSize = ftell(m_file); +#endif + m_use_cache = true; + m_cache_size = std::min(32768, std::max(fileSize / 20, 8192)) & ~0x1FFF; } if(safeSeek(4)) @@ -217,44 +258,41 @@ bool FileLoader::parseNode(NODE node) const uint8_t* FileLoader::getProps(const NODE node, uint32_t &size) { - if(node) + if(!node) + return NULL; + + if(node->propsSize >= m_buffer_size) { - if(node->propsSize >= m_buffer_size) - { - delete[] m_buffer; - m_buffer = new uint8_t[m_buffer_size + 1024]; - m_buffer_size = m_buffer_size + 1024; - } + delete[] m_buffer; + while(node->propsSize >= m_buffer_size) + m_buffer_size *= 2; - //get buffer - if(readBytes(m_buffer, node->propsSize, node->start + 2)) - { - //unscape buffer - uint32_t j = 0; - bool escaped = false; - for(uint32_t i = 0; i < node->propsSize; ++i, ++j) - { - if(m_buffer[i] == ESCAPE_CHAR) - { - //escape char found, skip it and write next - ++i; - m_buffer[j] = m_buffer[i]; - //is neede a displacement for next bytes - escaped = true; - } - else if(escaped) - { - //perform that displacement - m_buffer[j] = m_buffer[i]; - } - } + m_buffer = new uint8_t[m_buffer_size]; + } + + //get buffer + if(!readBytes(m_buffer, node->propsSize, node->start + 2)) + return NULL; - size = j; - return m_buffer; + //unscape buffer + uint32_t j = 0; + bool escaped = false; + for(uint32_t i = 0; i < node->propsSize; ++i, ++j) + { + if(m_buffer[i] == ESCAPE_CHAR) + { + //escape char found, skip it and write next + ++i; + m_buffer[j] = m_buffer[i]; + //is neede a displacement for next bytes + escaped = true; } + else if(escaped) //perform that displacement + m_buffer[j] = m_buffer[i]; } - return NULL; + size = j; + return m_buffer; } bool FileLoader::getProps(const NODE node, PropStream &props) @@ -292,33 +330,31 @@ void FileLoader::endNode() writeData(&nodeEnd, sizeof(nodeEnd), false); } -const NODE FileLoader::getChildNode(const NODE parent, uint32_t &type) +NODE FileLoader::getChildNode(const NODE& parent, uint32_t &type) const { - if(parent) + if(!parent) { - NODE child = parent->child; - if(child) - type = child->type; - - return child; + type = m_root->type; + return m_root; } - type = m_root->type; - return m_root; + NODE child = parent->child; + if(child) + type = child->type; + + return child; } -const NODE FileLoader::getNextNode(const NODE prev, uint32_t &type) +NODE FileLoader::getNextNode(const NODE& prev, uint32_t &type) const { - if(prev) - { - NODE next = prev->next; - if(next) - type = next->type; + if(!prev) + return NO_NODE; - return next; - } + NODE next = prev->next; + if(next) + type = next->type; - return NO_NODE; + return next; } inline bool FileLoader::readByte(int32_t &value) @@ -333,8 +369,7 @@ inline bool FileLoader::readByte(int32_t &value) if(m_cache_offset >= m_cached_data[m_cache_index].size) { - int32_t pos = m_cache_offset + m_cached_data[m_cache_index].base; - int32_t tmp = getCacheBlock(pos); + int32_t pos = m_cache_offset + m_cached_data[m_cache_index].base, tmp = getCacheBlock(pos); if(tmp < 0) return false; @@ -349,7 +384,11 @@ inline bool FileLoader::readByte(int32_t &value) return true; } +#ifdef __USE_ZLIB__ + value = gzgetc(m_file); +#else value = fgetc(m_file); +#endif if(value != EOF) return true; @@ -389,20 +428,28 @@ inline bool FileLoader::readBytes(uint8_t* buffer, int32_t size, int32_t pos) return true; } +#ifdef __USE_ZLIB__ + if(gzseek(m_file, pos, SEEK_SET) < 0) +#else if(fseek(m_file, pos, SEEK_SET)) +#endif { m_lastError = ERROR_SEEK_ERROR; return false; } +#ifdef __USE_ZLIB__ + if(gzread(m_file, buffer, size) == size) +#else if(fread(buffer, 1, size, m_file) == (uint32_t)size) +#endif return true; m_lastError = ERROR_EOF; return false; } -inline bool FileLoader::checks(const NODE node) +inline bool FileLoader::checks(const NODE& node) { if(!m_file) { @@ -430,7 +477,11 @@ inline bool FileLoader::safeSeek(uint32_t pos) m_cache_index = i; m_cache_offset = pos - m_cached_data[i].base; } +#ifdef __USE_ZLIB__ + else if(gzseek(m_file, pos, SEEK_SET) < 0) +#else else if(fseek(m_file, pos, SEEK_SET)) +#endif { m_lastError = ERROR_SEEK_ERROR; return false; @@ -453,7 +504,11 @@ inline bool FileLoader::safeTell(int32_t &pos) return true; } +#ifdef __USE_ZLIB__ + pos = gztell(m_file); +#else pos = ftell(m_file); +#endif if(pos == -1) { m_lastError = ERROR_TELL_ERROR; @@ -468,16 +523,13 @@ inline uint32_t FileLoader::getCacheBlock(uint32_t pos) { bool found = false; uint32_t i, base_pos = pos & ~(m_cache_size - 1); - for(i = 0; i < CACHE_BLOCKS; i++) + for(i = 0; i < CACHE_BLOCKS; ++i) { - if(m_cached_data[i].loaded) - { - if(m_cached_data[i].base == base_pos) - { - found = true; - break; - } - } + if(!m_cached_data[i].loaded || m_cached_data[i].base != base_pos) + continue; + + found = true; + break; } if(!found) @@ -489,24 +541,24 @@ inline uint32_t FileLoader::getCacheBlock(uint32_t pos) int32_t FileLoader::loadCacheBlock(uint32_t pos) { int32_t i, loading_cache = -1, base_pos = pos & ~(m_cache_size - 1); - for(i = 0; i < CACHE_BLOCKS; i++) + for(i = 0; i < CACHE_BLOCKS; ++i) { - if(!m_cached_data[i].loaded) - { - loading_cache = i; - break; - } + if(m_cached_data[i].loaded) + continue; + + loading_cache = i; + break; } if(loading_cache == -1) { - for(i = 0; i < CACHE_BLOCKS; i++) + for(i = 0; i < CACHE_BLOCKS; ++i) { - if((long)(std::abs((long)m_cached_data[i].base - base_pos)) > (long)(2 * m_cache_size)) - { - loading_cache = i; - break; - } + if((long)(std::abs((long)m_cached_data[i].base - base_pos)) <= (long)(2 * m_cache_size)) + continue; + + loading_cache = i; + break; } if(loading_cache == -1) @@ -517,16 +569,22 @@ int32_t FileLoader::loadCacheBlock(uint32_t pos) m_cached_data[loading_cache].data = new uint8_t[m_cache_size]; m_cached_data[loading_cache].base = base_pos; - +#ifdef __USE_ZLIB__ + if(gzseek(m_file, m_cached_data[loading_cache].base, SEEK_SET) < 0) +#else if(fseek(m_file, m_cached_data[loading_cache].base, SEEK_SET)) +#endif { m_lastError = ERROR_SEEK_ERROR; return -1; } +#ifdef __USE_ZLIB__ + uint32_t size = gzread(m_file, m_cached_data[loading_cache].data, m_cache_size); +#else uint32_t size = fread(m_cached_data[loading_cache].data, 1, m_cache_size, m_file); +#endif m_cached_data[loading_cache].size = size; - if(size < (pos - m_cached_data[loading_cache].base)) { m_lastError = ERROR_SEEK_ERROR; diff --git a/fileloader.h b/fileloader.h index 34412a6..0aaec33 100644 --- a/fileloader.h +++ b/fileloader.h @@ -19,6 +19,10 @@ #define __FILELOADER__ #include "otsystem.h" +#ifdef __USE_ZLIB__ +#include +#endif + struct NodeStruct; typedef NodeStruct* NODE; @@ -35,7 +39,7 @@ struct NodeStruct NodeStruct* next; NodeStruct* child; - static void clearNet(NodeStruct* root) {if(root) clearChild(root); } + static void clearNet(NodeStruct* root) {if(root) clearChild(root);} private: static void clearNext(NodeStruct* node) { @@ -78,7 +82,7 @@ enum FILELOADER_ERRORS ERROR_INVALID_FORMAT, ERROR_TELL_ERROR, ERROR_COULDNOTWRITE, - ERROR_CACHE_ERROR, + ERROR_CACHE_ERROR }; class PropStream; @@ -88,17 +92,17 @@ class FileLoader FileLoader(); virtual ~FileLoader(); - bool openFile(const char* filename, bool write, bool caching = false); + bool openFile(std::string name, bool write, bool caching = false); const uint8_t* getProps(const NODE, uint32_t &size); bool getProps(const NODE, PropStream& props); - const NODE getChildNode(const NODE parent, uint32_t &type); - const NODE getNextNode(const NODE prev, uint32_t &type); + NODE getChildNode(const NODE& parent, uint32_t &type) const; + NODE getNextNode(const NODE& prev, uint32_t &type) const; void startNode(uint8_t type); void endNode(); int32_t setProps(void* data, uint16_t size); - int32_t getError() {return m_lastError;} + int32_t getError() const {return m_lastError;} void clearError() {m_lastError = ERROR_NONE;} protected: @@ -112,7 +116,7 @@ class FileLoader inline bool readByte(int32_t &value); inline bool readBytes(unsigned char* buffer, int32_t size, int32_t pos); - inline bool checks(const NODE node); + inline bool checks(const NODE& node); inline bool safeSeek(uint32_t pos); inline bool safeTell(int32_t &pos); @@ -124,8 +128,12 @@ class FileLoader uint8_t c = *(((uint8_t*)data) + i); if(unescape && (c == NODE_START || c == NODE_END || c == ESCAPE_CHAR)) { - uint8_t escape = ESCAPE_CHAR; - size_t value = fwrite(&escape, 1, 1, m_file); + uint8_t tmp = ESCAPE_CHAR; +#ifdef __USE_ZLIB__ + size_t value = gzwrite(m_file, &tmp, 1); +#else + size_t value = fwrite(&tmp, 1, 1, m_file); +#endif if(value != 1) { m_lastError = ERROR_COULDNOTWRITE; @@ -133,7 +141,11 @@ class FileLoader } } +#ifdef __USE_ZLIB__ + size_t value = gzwrite(m_file, &c, 1); +#else size_t value = fwrite(&c, 1, 1, m_file); +#endif if(value != 1) { m_lastError = ERROR_COULDNOTWRITE; @@ -145,8 +157,13 @@ class FileLoader } protected: - FILE* m_file; FILELOADER_ERRORS m_lastError; +#ifdef __USE_ZLIB__ + gzFile m_file; +#else + FILE* m_file; +#endif + NODE m_root; uint32_t m_buffer_size; uint8_t* m_buffer; @@ -162,8 +179,10 @@ class FileLoader #define CACHE_BLOCKS 3 uint32_t m_cache_size; _cache m_cached_data[CACHE_BLOCKS]; + #define NO_VALID_CACHE 0xFFFFFFFF uint32_t m_cache_index, m_cache_offset; + inline uint32_t getCacheBlock(uint32_t pos); int32_t loadCacheBlock(uint32_t pos); }; @@ -182,60 +201,57 @@ class PropStream int32_t size() const {return end - p;} template - inline bool GET_STRUCT(T* &ret) + inline bool getType(T& ret) { if(size() < (int32_t)sizeof(T)) - { - ret = NULL; return false; - } - ret = (T*)p; + ret = *((T*)p); p += sizeof(T); return true; } template - inline bool GET_VALUE(T &ret) + inline bool getStruct(T* &ret) { if(size() < (int32_t)sizeof(T)) + { + ret = NULL; return false; + } - ret = *((T*)p); + ret = (T*)p; p += sizeof(T); return true; } - inline bool GET_TIME(time_t &ret) {return GET_VALUE(ret);} - inline bool GET_ULONG(uint32_t &ret) {return GET_VALUE(ret);} - inline bool GET_USHORT(uint16_t &ret) {return GET_VALUE(ret);} - inline bool GET_UCHAR(uint8_t &ret) {return GET_VALUE(ret);} + inline bool getByte(uint8_t& ret) {return getType(ret);} + inline bool getShort(uint16_t& ret) {return getType(ret);} + inline bool getTime(time_t& ret) {return getType(ret);} + inline bool getLong(uint32_t& ret) {return getType(ret);} - inline bool GET_STRING(std::string& ret) + inline bool getFloat(float& ret) { - uint16_t strLen; - if(!GET_USHORT(strLen)) + // ugly hack, but it makes reading not depending on arch + if(size() < (int32_t)sizeof(uint32_t)) return false; - if(size() < (int32_t)strLen) - return false; - - char* str = new char[strLen + 1]; - memcpy(str, p, strLen); - str[strLen] = 0; + float f; + memcpy(&f, (uint32_t*)p, sizeof(uint32_t)); - ret.assign(str, strLen); - delete[] str; - p = p + strLen; + ret = f; + p += sizeof(uint32_t); return true; } - inline bool GET_LSTRING(std::string& ret) + inline bool getString(std::string& ret) { - uint32_t strLen; - if(!GET_ULONG(strLen)) - return false; + uint16_t strLen; + return getShort(strLen) && getString(ret, strLen); + } + inline bool getString(std::string& ret, uint16_t strLen) + { if(size() < (int32_t)strLen) return false; @@ -249,8 +265,12 @@ class PropStream return true; } - inline bool GET_NSTRING(uint16_t strLen, std::string& ret) + inline bool getLongString(std::string& ret) { + uint32_t strLen; + if(!getLong(strLen)) + return false; + if(size() < (int32_t)strLen) return false; @@ -260,11 +280,11 @@ class PropStream ret.assign(str, strLen); delete[] str; - p += strLen; + p = p + strLen; return true; } - inline bool SKIP_N(int16_t n) + inline bool skip(int16_t n) { if(size() < n) return false; @@ -296,59 +316,76 @@ class PropWriteStream return buffer; } - //TODO: might need temp buffer and zero fill the memory chunk allocated by realloc template - inline void ADD_TYPE(T* add) + inline void addType(T add) { if((bufferSize - size) < sizeof(T)) { bufferSize += ((sizeof(T) + 0x1F) & 0xFFFFFFE0); - buffer = (char*)realloc(buffer, bufferSize); + char* tmp = (char*)realloc(buffer, bufferSize); + if(tmp != NULL) + buffer = tmp; + else + std::clog << "[Error - PropWriteStream::addType] Failed to allocate memory" << std::endl; } - memcpy(&buffer[size], (char*)add, sizeof(T)); + memcpy(&buffer[size], &add, sizeof(T)); size += sizeof(T); } + //TODO: might need tmp buffer and zero fill the memory chunk allocated by realloc template - inline void ADD_VALUE(T add) + inline void addStruct(T* add) { if((bufferSize - size) < sizeof(T)) { bufferSize += ((sizeof(T) + 0x1F) & 0xFFFFFFE0); - buffer = (char*)realloc(buffer, bufferSize); + char* tmp = (char*)realloc(buffer, bufferSize); + if(tmp != NULL) + buffer = tmp; + else + std::clog << "[Error - PropWriteStream::addStruct] Failed to allocate memory" << std::endl; } - memcpy(&buffer[size], &add, sizeof(T)); + memcpy(&buffer[size], (char*)add, sizeof(T)); size += sizeof(T); } - inline void ADD_ULONG(uint32_t ret) {ADD_VALUE(ret);} - inline void ADD_USHORT(uint16_t ret) {ADD_VALUE(ret);} - inline void ADD_UCHAR(uint8_t ret) {ADD_VALUE(ret);} + inline void addByte(uint8_t ret) {addType(ret);} + inline void addShort(uint16_t ret) {addType(ret);} + inline void addTime(time_t ret) {addType(ret);} + inline void addLong(uint32_t ret) {addType(ret);} - inline void ADD_STRING(const std::string& add) + inline void addString(const std::string& add) { uint16_t strLen = add.size(); - ADD_USHORT(strLen); + addShort(strLen); if((bufferSize - size) < strLen) { bufferSize += ((strLen + 0x1F) & 0xFFFFFFE0); - buffer = (char*)realloc(buffer, bufferSize); + char* tmp = (char*)realloc(buffer, bufferSize); + if(tmp != NULL) + buffer = tmp; + else + std::clog << "[Error - PropWriteStream::addString] Failed to allocate memory" << std::endl; } memcpy(&buffer[size], add.c_str(), strLen); size += strLen; } - inline void ADD_LSTRING(const std::string& add) + inline void addLongString(const std::string& add) { uint16_t strLen = add.size(); - ADD_ULONG(strLen); + addLong(strLen); if((bufferSize - size) < strLen) { bufferSize += ((strLen + 0x1F) & 0xFFFFFFE0); - buffer = (char*)realloc(buffer, bufferSize); + char* tmp = (char*)realloc(buffer, bufferSize); + if(tmp != NULL) + buffer = tmp; + else + std::clog << "[Error - PropWriteStream::addLongString] Failed to allocate memory" << std::endl; } memcpy(&buffer[size], add.c_str(), strLen); diff --git a/game.cpp b/game.cpp index 33199ea..29bab6a 100644 --- a/game.cpp +++ b/game.cpp @@ -33,8 +33,10 @@ #include "iologindata.h" #include "ioban.h" #include "ioguild.h" +#include "iomarket.h" #include "items.h" +#include "trashholder.h" #include "container.h" #include "monsters.h" @@ -49,7 +51,9 @@ #include "spells.h" #include "talkaction.h" #include "weapons.h" +#include "mounts.h" +#include "textlogger.h" #include "vocation.h" #include "group.h" @@ -71,12 +75,10 @@ extern GlobalEvents* g_globalEvents; Game::Game() { - gameState = GAME_STATE_NORMAL; - worldType = WORLD_TYPE_PVP; + gameState = GAMESTATE_NORMAL; + worldType = WORLDTYPE_OPEN; map = NULL; playersRecord = lastStageLevel = 0; - for(int32_t i = 0; i < 3; i++) - globalSaveMessage[i] = false; //(1440 minutes/day) * 10 seconds event interval / (3600 seconds/day) lightHourDelta = 1440 * 10 / 3600; @@ -85,13 +87,14 @@ Game::Game() lightState = LIGHT_STATE_DAY; lastBucket = checkCreatureLastIndex = checkLightEvent = checkCreatureEvent = checkDecayEvent = saveEvent = 0; + checkWarsEvent = 0; } Game::~Game() { blacklist.clear(); - if(map) - delete map; + whitelist.clear(); + delete map; } void Game::start(ServiceManager* servicer) @@ -102,67 +105,105 @@ void Game::start(ServiceManager* servicer) boost::bind(&Game::checkCreatures, this))); checkLightEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL, boost::bind(&Game::checkLight, this))); + checkWarsEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_WARSINTERVAL, + boost::bind(&Game::checkWars, this))); services = servicer; - if(g_config.getBool(ConfigManager::GLOBALSAVE_ENABLED) && g_config.getNumber(ConfigManager::GLOBALSAVE_H) >= 1 - && g_config.getNumber(ConfigManager::GLOBALSAVE_H) <= 24) + if(!g_config.getBool(ConfigManager::GLOBALSAVE_ENABLED)) + return; + + int32_t prepareHour = g_config.getNumber(ConfigManager::GLOBALSAVE_H), + prepareMinute = g_config.getNumber(ConfigManager::GLOBALSAVE_M); + + if(prepareHour < 0 || prepareHour > 24) + { + std::clog << "> WARNING: No valid hour (" << prepareHour << ") for a global save, should be between 0-23. Global save disabled." << std::endl; + return; + } + + if(prepareMinute < 0 || prepareMinute > 59) { - int32_t prepareGlobalSaveHour = g_config.getNumber(ConfigManager::GLOBALSAVE_H) - 1, hoursLeft = 0, minutesLeft = 0, minutesToRemove = 0; - bool ignoreEvent = false; + std::clog << "> WARNING: No valid minute (" << prepareMinute << ") for a global save, should be between 0-59. Global save disabled." << std::endl; + return; + } + + time_t timeNow = time(NULL); + const tm* theTime = localtime(&timeNow); + + int32_t hour = theTime->tm_hour, minute = theTime->tm_min, second = theTime->tm_sec, + hoursLeft = 0, minutesLeft = 0, broadcast = 5; + + if(prepareHour == 0) + prepareHour = 24; - time_t timeNow = time(NULL); - const tm* theTime = localtime(&timeNow); - if(theTime->tm_hour > prepareGlobalSaveHour) + if(hour != prepareHour) + { + if(prepareMinute >= 5) + prepareMinute -= 5; + else { - hoursLeft = 24 - (theTime->tm_hour - prepareGlobalSaveHour); - if(theTime->tm_min > 55 && theTime->tm_min <= 59) - minutesToRemove = theTime->tm_min - 55; - else - minutesLeft = 55 - theTime->tm_min; + prepareHour--; + prepareMinute = 55 + prepareMinute; } - else if(theTime->tm_hour == prepareGlobalSaveHour) - { - if(theTime->tm_min >= 55 && theTime->tm_min <= 59) - { - if(theTime->tm_min >= 57) - setGlobalSaveMessage(0, true); - if(theTime->tm_min == 59) - setGlobalSaveMessage(1, true); + if(hour > prepareHour) + hoursLeft = 24 - (hour - prepareHour); + else + hoursLeft = prepareHour - hour; - prepareGlobalSave(); - ignoreEvent = true; - } - else - minutesLeft = 55 - theTime->tm_min; + if(minute > prepareMinute) + { + minutesLeft = 60 - (minute - prepareMinute); + hoursLeft--; + } + else if(minute != prepareMinute) + minutesLeft = prepareMinute - minute; + } + else + { + if(minute > prepareMinute) + { + minutesLeft = 55 - (minute - prepareMinute); + hoursLeft = 23; } else { - hoursLeft = prepareGlobalSaveHour - theTime->tm_hour; - if(theTime->tm_min > 55 && theTime->tm_min <= 59) - minutesToRemove = theTime->tm_min - 55; - else - minutesLeft = 55 - theTime->tm_min; + minutesLeft = prepareMinute - minute; + if(minutesLeft >= 5) + minutesLeft = minutesLeft - 5; + else if(minutesLeft == 3 || minutesLeft == 1) + { + prepareGlobalSave(minutesLeft); + return; + } + else if(minutesLeft > 0) + { + broadcast = (minutesLeft == 2 ? 1 : 3); + minutesLeft = 1; + } } + } - uint32_t hoursLeftInMs = 60000 * 60 * hoursLeft, minutesLeftInMs = 60000 * (minutesLeft - minutesToRemove); - if(!ignoreEvent && (hoursLeftInMs + minutesLeftInMs) > 0) - saveEvent = Scheduler::getInstance().addEvent(createSchedulerTask(hoursLeftInMs + minutesLeftInMs, - boost::bind(&Game::prepareGlobalSave, this))); + uint32_t timeLeft = (hoursLeft * 60 * 60 * 1000) + (minutesLeft * 60 * 1000); + if(timeLeft > 0) + { + timeLeft -= second * 1000; + saveEvent = Scheduler::getInstance().addEvent(createSchedulerTask(timeLeft, + boost::bind(&Game::prepareGlobalSave, this, broadcast))); } } void Game::loadGameState() { ScriptEnviroment::loadGameState(); - loadMotd(); loadPlayersRecord(); + loadMotd(); checkHighscores(); } void Game::setGameState(GameState_t newState) { - if(gameState == GAME_STATE_SHUTDOWN) + if(gameState == GAMESTATE_SHUTDOWN) return; //this cannot be stopped if(gameState != newState) @@ -170,34 +211,40 @@ void Game::setGameState(GameState_t newState) gameState = newState; switch(newState) { - case GAME_STATE_INIT: + case GAMESTATE_INIT: { Spawns::getInstance()->startup(); - Raids::getInstance()->loadFromXml(); Raids::getInstance()->startup(); - Quests::getInstance()->loadFromXml(); + loadStatuslist(); loadGameState(); - g_globalEvents->startup(); - IOBan::getInstance()->clearTemporials(); - if(g_config.getBool(ConfigManager::REMOVE_PREMIUM_ON_INIT)) + g_globalEvents->startup(); + if(g_config.getBool(ConfigManager::INIT_PREMIUM_UPDATE)) IOLoginData::getInstance()->updatePremiumDays(); + + IOGuild::getInstance()->checkWars(); + + if(g_config.getBool(ConfigManager::MARKET_ENABLED)) + { + checkExpiredMarketOffers(); + IOMarket::getInstance()->updateStatistics(); + } break; } - case GAME_STATE_SHUTDOWN: + case GAMESTATE_SHUTDOWN: { - g_globalEvents->execute(GLOBAL_EVENT_SHUTDOWN); + g_globalEvents->execute(GLOBALEVENT_SHUTDOWN); AutoList::iterator it = Player::autoList.begin(); while(it != Player::autoList.end()) //kick all players that are still online { - it->second->kickPlayer(true, true); + it->second->kick(true, true); it = Player::autoList.begin(); } - Houses::getInstance()->payHouses(); - saveGameState(false); + Houses::getInstance()->check(); + saveGameState((uint8_t)SAVE_PLAYERS | (uint8_t)SAVE_MAP | (uint8_t)SAVE_STATE); Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::shutdown, this))); Scheduler::getInstance().stop(); @@ -205,58 +252,64 @@ void Game::setGameState(GameState_t newState) break; } - case GAME_STATE_CLOSED: + case GAMESTATE_CLOSED: { AutoList::iterator it = Player::autoList.begin(); while(it != Player::autoList.end()) //kick all players who not allowed to stay { if(!it->second->hasFlag(PlayerFlag_CanAlwaysLogin)) { - it->second->kickPlayer(true, true); + it->second->kick(true, true); it = Player::autoList.begin(); } else ++it; } - saveGameState(false); + map->updateAuctions(); + saveGameState((uint8_t)SAVE_PLAYERS | (uint8_t)SAVE_MAP | (uint8_t)SAVE_STATE); break; } - case GAME_STATE_NORMAL: - case GAME_STATE_MAINTAIN: - case GAME_STATE_STARTUP: - case GAME_STATE_CLOSING: + case GAMESTATE_NORMAL: + case GAMESTATE_MAINTAIN: + case GAMESTATE_STARTUP: + case GAMESTATE_CLOSING: default: break; } } } -void Game::saveGameState(bool shallow) +void Game::saveGameState(uint8_t flags) { - std::cout << "> Saving server..." << std::endl; + std::clog << "> Saving server..." << std::endl; uint64_t start = OTSYS_TIME(); - if(gameState == GAME_STATE_NORMAL) - setGameState(GAME_STATE_MAINTAIN); + if(gameState == GAMESTATE_NORMAL) + setGameState(GAMESTATE_MAINTAIN); - IOLoginData* io = IOLoginData::getInstance(); - for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) + if(hasBitSet(SAVE_PLAYERS, flags)) { - it->second->loginPosition = it->second->getPosition(); - io->savePlayer(it->second, false, shallow); + IOLoginData* io = IOLoginData::getInstance(); + for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) + { + it->second->loginPosition = it->second->getPosition(); + io->savePlayer(it->second, false, hasBitSet(SAVE_PLAYERS_SHALLOW, flags)); + } } - std::string storage = "relational"; - if(g_config.getBool(ConfigManager::HOUSE_STORAGE)) - storage = "binary"; + if(hasBitSet(SAVE_MAP, flags)) + map->saveMap(); + + if(hasBitSet(SAVE_STATE, flags)) + ScriptEnviroment::saveGameState(); - map->saveMap(); - ScriptEnviroment::saveGameState(); - if(gameState == GAME_STATE_MAINTAIN) - setGameState(GAME_STATE_NORMAL); + if(gameState == GAMESTATE_MAINTAIN) + setGameState(GAMESTATE_NORMAL); - std::cout << "> SAVE: Complete in " << (OTSYS_TIME() - start) / (1000.) << " seconds using " << storage << " house storage." << std::endl; + std::clog << "> SAVE: Complete in " << (OTSYS_TIME() - start) / (1000.) << " seconds using " + << asLowerCaseString(g_config.getString(ConfigManager::HOUSE_STORAGE)) + << " house storage." << std::endl; } int32_t Game::loadMap(std::string filename) @@ -264,17 +317,21 @@ int32_t Game::loadMap(std::string filename) if(!map) map = new Map; - return map->loadMap(getFilePath(FILE_TYPE_OTHER, std::string("world/" + filename + ".otbm"))); + std::string file = getFilePath(FILE_TYPE_CONFIG, "world/" + filename); + if(!fileExists(file.c_str())) + file = getFilePath(FILE_TYPE_OTHER, "world/" + filename); + + return map->loadMap(file); } -void Game::cleanMap(uint32_t& count) +void Game::cleanMapEx(uint32_t& count) { uint64_t start = OTSYS_TIME(); uint32_t tiles = 0; count = 0; int32_t marked = -1; - if(gameState == GAME_STATE_NORMAL) - setGameState(GAME_STATE_MAINTAIN); + if(gameState == GAMESTATE_NORMAL) + setGameState(GAMESTATE_MAINTAIN); Tile* tile = NULL; ItemVector::iterator tit; @@ -297,7 +354,7 @@ void Game::cleanMap(uint32_t& count) tit = tile->getItemList()->begin(); while(tile->getItemList() && tit != tile->getItemList()->end()) { - if((*tit)->isMoveable() && !(*tit)->isLoadedFromMap() + if((*tit)->isMovable() && !(*tit)->isLoadedFromMap() && !(*tit)->isScriptProtected()) { internalRemoveItem(NULL, *tit); @@ -310,8 +367,6 @@ void Game::cleanMap(uint32_t& count) ++tit; } } - - trash.clear(); } else { @@ -328,7 +383,7 @@ void Game::cleanMap(uint32_t& count) tit = tile->getItemList()->begin(); while(tile->getItemList() && tit != tile->getItemList()->end()) { - if((*tit)->isMoveable() && !(*tit)->isLoadedFromMap() + if((*tit)->isMovable() && !(*tit)->isLoadedFromMap() && !(*tit)->isScriptProtected()) { internalRemoveItem(NULL, *tit); @@ -341,9 +396,9 @@ void Game::cleanMap(uint32_t& count) ++tit; } } - - trash.clear(); } + + trash.clear(); } else if(g_config.getBool(ConfigManager::CLEAN_PROTECTED_ZONES)) { @@ -360,7 +415,7 @@ void Game::cleanMap(uint32_t& count) tit = tile->getItemList()->begin(); while(tile->getItemList() && tit != tile->getItemList()->end()) { - if((*tit)->isMoveable() && !(*tit)->isLoadedFromMap() + if((*tit)->isMovable() && !(*tit)->isLoadedFromMap() && !(*tit)->isScriptProtected()) { internalRemoveItem(NULL, *tit); @@ -391,7 +446,7 @@ void Game::cleanMap(uint32_t& count) tit = tile->getItemList()->begin(); while(tile->getItemList() && tit != tile->getItemList()->end()) { - if((*tit)->isMoveable() && !(*tit)->isLoadedFromMap() + if((*tit)->isMovable() && !(*tit)->isLoadedFromMap() && !(*tit)->isScriptProtected()) { internalRemoveItem(NULL, *tit); @@ -408,15 +463,21 @@ void Game::cleanMap(uint32_t& count) } } - if(gameState == GAME_STATE_MAINTAIN) - setGameState(GAME_STATE_NORMAL); + if(gameState == GAMESTATE_MAINTAIN) + setGameState(GAMESTATE_NORMAL); - std::cout << "> CLEAN: Removed " << count << " item" << (count != 1 ? "s" : "") + std::clog << "> CLEAN: Removed " << count << " item" << (count != 1 ? "s" : "") << " from " << tiles << " tile" << (tiles != 1 ? "s" : ""); if(marked >= 0) - std::cout << " (" << marked << " were marked)"; + std::clog << " (" << marked << " were marked)"; + + std::clog << " in " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; +} - std::cout << " in " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; +void Game::cleanMap() +{ + uint32_t dummy; + cleanMapEx(dummy); } void Game::proceduralRefresh(RefreshTiles::iterator* it/* = NULL*/) @@ -434,7 +495,7 @@ void Game::proceduralRefresh(RefreshTiles::iterator* it/* = NULL*/) // Refresh some items every 100 ms until all tiles has been checked // For 100k tiles, this would take 100000/2500 = 40s = half a minute - Scheduler::getInstance().addEvent(createSchedulerTask(100, + Scheduler::getInstance().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, boost::bind(&Game::proceduralRefresh, this, it))); } @@ -460,7 +521,7 @@ void Game::refreshMap(RefreshTiles::iterator* it/* = NULL*/, uint32_t limit/* = if((items = tile->getItemList())) { downItemsSize = tile->getDownItemCount(); - for(uint32_t i = downItemsSize - 1; i >= 0; --i) + for(uint32_t i = downItemsSize - 1; i; --i) { if((item = items->at(i))) { @@ -469,8 +530,8 @@ void Game::refreshMap(RefreshTiles::iterator* it/* = NULL*/, uint32_t limit/* = #else if(internalRemoveItem(NULL, item) != RET_NOERROR) { - std::cout << "> WARNING: Could not refresh item: " << item->getID(); - std::cout << " at position: " << tile->getPosition() << std::endl; + std::clog << "> WARNING: Could not refresh item: " << item->getID(); + std::clog << " at position: " << tile->getPosition() << std::endl; } #endif } @@ -491,7 +552,7 @@ void Game::refreshMap(RefreshTiles::iterator* it/* = NULL*/, uint32_t limit/* = } else { - std::cout << "> WARNING: Could not refresh item: " << item->getID() + std::clog << "> WARNING: Could not refresh item: " << item->getID() << " at position: " << tile->getPosition() << std::endl; delete item; } @@ -499,6 +560,21 @@ void Game::refreshMap(RefreshTiles::iterator* it/* = NULL*/, uint32_t limit/* = } } +bool Game::isSwimmingPool(Item* item, const Tile* tile, bool checkProtection) const +{ + if(!tile) + return false; + + TrashHolder* trashHolder = NULL; + if(!item) + trashHolder = tile->getTrashHolder(); + else + trashHolder = item->getTrashHolder(); + + return trashHolder && trashHolder->getEffect() == MAGIC_EFFECT_LOSE_ENERGY && (!checkProtection + || tile->getZone() == ZONE_PROTECTION || tile->getZone() == ZONE_OPTIONAL); +} + Cylinder* Game::internalGetCylinder(Player* player, const Position& pos) { if(pos.x != 0xFFFF) @@ -506,10 +582,7 @@ Cylinder* Game::internalGetCylinder(Player* player, const Position& pos) //container if(pos.y & 0x40) - { - uint8_t fromCid = pos.y & 0x0F; - return player->getContainer(fromCid); - } + return player->getContainer((uint8_t)(pos.y & 0x0F)); return player; } @@ -532,7 +605,7 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index case STACKPOS_MOVE: { Item* item = tile->getTopDownItem(); - if(item && item->isMoveable()) + if(item && item->isMovable()) thing = item; else thing = tile->getTopVisibleCreature(player); @@ -548,18 +621,15 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index case STACKPOS_USEITEM: { - Item* downItem = tile->getTopDownItem(); + thing = tile->getTopDownItem(); Item* item = tile->getItemByTopOrder(2); if(item && g_actions->hasAction(item)) { const ItemType& it = Item::items[item->getID()]; - if(!downItem || (!it.hasHeight && !it.allowPickupable)) + if(!thing || (!it.hasHeight && !it.allowPickupable)) thing = item; } - if(!thing) - thing = downItem; - if(!thing) thing = tile->getTopTopItem(); @@ -611,6 +681,9 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index void Game::internalGetPosition(Item* item, Position& pos, int16_t& stackpos) { + if(!item) + return; + pos.x = pos.y = pos.z = stackpos = 0; if(Cylinder* topParent = item->getTopParent()) { @@ -663,6 +736,23 @@ Player* Game::getPlayerByID(uint32_t id) return NULL; //just in case the player doesnt exist } +Player* Game::getPlayerByGUID(const uint32_t& guid) +{ + if(guid == 0) + return NULL; + + for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) + { + Player* player = (*it).second; + if(!player->isRemoved()) + { + if(guid == player->getGUID()) + return player; + } + } + return NULL; +} + Creature* Game::getCreatureByName(std::string s) { if(s.empty()) @@ -708,7 +798,7 @@ Player* Game::getPlayerByNameEx(const std::string& s) return player; #ifdef __DEBUG__ - std::cout << "[Failure - Game::getPlayerByNameEx] Cannot load player: " << name << std::endl; + std::clog << "[Failure - Game::getPlayerByNameEx] Cannot load player: " << name << std::endl; #endif delete player; return NULL; @@ -743,7 +833,7 @@ Player* Game::getPlayerByGuidEx(uint32_t guid) return player; #ifdef __DEBUG__ - std::cout << "[Failure - Game::getPlayerByGuidEx] Cannot load player: " << name << std::endl; + std::clog << "[Failure - Game::getPlayerByGuidEx] Cannot load player: " << name << std::endl; #endif delete player; return NULL; @@ -857,13 +947,16 @@ bool Game::internalPlaceCreature(Creature* creature, const Position& pos, bool e bool Game::placeCreature(Creature* creature, const Position& pos, bool extendedPos /*= false*/, bool forced /*= false*/) { + if(!internalPlaceCreature(creature, pos, extendedPos, forced)) + return false; + Player* tmpPlayer = NULL; if((tmpPlayer = creature->getPlayer()) && !tmpPlayer->storedConditionList.empty()) { for(ConditionList::iterator it = tmpPlayer->storedConditionList.begin(); it != tmpPlayer->storedConditionList.end(); ++it) { - if((*it)->getType() == CONDITION_MUTED && ((*it)->getTicks() - ( - (time(NULL) - tmpPlayer->getLastLogout()) * 1000)) <= 0) + if((*it)->getType() == CONDITION_MUTED && (*it)->getTicks() != -1 && + (*it)->getTicks() - ((time(NULL) - tmpPlayer->getLastLogout()) * 1000) <= 0) continue; tmpPlayer->addCondition(*it); @@ -872,9 +965,6 @@ bool Game::placeCreature(Creature* creature, const Position& pos, bool extendedP tmpPlayer->storedConditionList.clear(); } - if(!internalPlaceCreature(creature, pos, extendedPos, forced)) - return false; - SpectatorVec::iterator it; SpectatorVec list; @@ -905,7 +995,10 @@ ReturnValue Game::placeSummon(Creature* creature, const std::string& name) // Place the monster creature->addSummon(monster); if(placeCreature(monster, creature->getPosition(), true)) + { + addMagicEffect(monster->getPosition(), MAGIC_EFFECT_TELEPORT); return RET_NOERROR; + } creature->removeSummon(monster); return RET_NOTENOUGHROOM; @@ -918,9 +1011,18 @@ bool Game::removeCreature(Creature* creature, bool isLogout /*= true*/) Tile* tile = creature->getTile(); SpectatorVec list; + getSpectators(list, tile->getPosition(), false, true); SpectatorVec::iterator it; - getSpectators(list, tile->getPosition(), false, true); + for(it = list.begin(); it != list.end(); ++it) + (*it)->onCreatureDisappear(creature, isLogout); + + if(tile != creature->getTile()) + { + list.clear(); + tile = creature->getTile(); + getSpectators(list, tile->getPosition(), false, true); + } Player* player = NULL; std::vector oldStackPosVector; @@ -938,17 +1040,17 @@ bool Game::removeCreature(Creature* creature, bool isLogout /*= true*/) uint32_t i = 0; for(it = list.begin(); it != list.end(); ++it) { + if(creature != (*it)) + (*it)->updateTileCache(tile); + if(!(player = (*it)->getPlayer()) || !player->canSeeCreature(creature)) continue; + player->setWalkthrough(creature, false); player->sendCreatureDisappear(creature, oldStackPosVector[i]); ++i; } - //event method - for(it = list.begin(); it != list.end(); ++it) - (*it)->onCreatureDisappear(creature, isLogout); - creature->getParent()->postRemoveNotification(NULL, creature, NULL, oldIndex, true); creature->onRemovedCreature(); @@ -969,6 +1071,12 @@ bool Game::playerMoveThing(uint32_t playerId, const Position& fromPos, if(!player || player->isRemoved()) return false; + if(player->getNoMove()) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + return false; + } + uint8_t fromIndex = 0; if(fromPos.x == 0xFFFF) { @@ -989,17 +1097,7 @@ bool Game::playerMoveThing(uint32_t playerId, const Position& fromPos, } if(Creature* movingCreature = thing->getCreature()) - { - uint32_t delay = g_config.getNumber(ConfigManager::PUSH_CREATURE_DELAY); - if(Position::areInRange<1,1,0>(movingCreature->getPosition(), player->getPosition()) && delay > 0) - { - SchedulerTask* task = createSchedulerTask(delay, boost::bind(&Game::playerMoveCreature, this, - player->getID(), movingCreature->getID(), movingCreature->getPosition(), toCylinder->getPosition())); - player->setNextActionTask(task); - } - else - playerMoveCreature(playerId, movingCreature->getID(), movingCreature->getPosition(), toCylinder->getPosition()); - } + playerMoveCreature(playerId, movingCreature->getID(), movingCreature->getPosition(), toCylinder->getPosition(), true); else if(thing->getItem()) playerMoveItem(playerId, fromPos, spriteId, fromStackpos, toPos, count); @@ -1007,7 +1105,7 @@ bool Game::playerMoveThing(uint32_t playerId, const Position& fromPos, } bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId, - const Position& movingCreaturePos, const Position& toPos) + const Position& movingCreaturePos, const Position& toPos, bool delay) { Player* player = getPlayerByID(playerId); if(!player || player->isRemoved() || player->hasFlag(PlayerFlag_CannotMoveCreatures)) @@ -1015,16 +1113,14 @@ bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId, if(!player->canDoAction()) { - uint32_t delay = player->getNextActionTime(); - SchedulerTask* task = createSchedulerTask(delay, boost::bind(&Game::playerMoveCreature, - this, playerId, movingCreatureId, movingCreaturePos, toPos)); - + SchedulerTask* task = createSchedulerTask(player->getNextActionTime(), + boost::bind(&Game::playerMoveCreature, this, playerId, movingCreatureId, movingCreaturePos, toPos, true)); player->setNextActionTask(task); return false; } Creature* movingCreature = getCreatureByID(movingCreatureId); - if(!movingCreature || movingCreature->isRemoved() || movingCreature->getNoMove()) + if(!movingCreature || movingCreature->isRemoved() || !player->canSeeCreature(movingCreature)) return false; player->setNextActionTask(NULL); @@ -1036,8 +1132,8 @@ bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId, { Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(player->getStepDuration(), boost::bind(&Game::playerMoveCreature, this, - playerId, movingCreatureId, movingCreaturePos, toPos)); + SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()), + boost::bind(&Game::playerMoveCreature, this, playerId, movingCreatureId, movingCreaturePos, toPos, true)); player->setNextWalkActionTask(task); return true; @@ -1046,6 +1142,17 @@ bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId, player->sendCancelMessage(RET_THEREISNOWAY); return false; } + else if(delay) + { + uint32_t delayTime = g_config.getNumber(ConfigManager::PUSH_CREATURE_DELAY); + if(delayTime > 0) + { + SchedulerTask* task = createSchedulerTask(delayTime, + boost::bind(&Game::playerMoveCreature, this, playerId, movingCreatureId, movingCreaturePos, toPos, false)); + player->setNextActionTask(task); + return true; + } + } Tile* toTile = map->getTile(toPos); if(!toTile) @@ -1054,17 +1161,31 @@ bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId, return false; } - if((!movingCreature->isPushable() && !player->hasFlag(PlayerFlag_CanPushAllCreatures)) || !player->canSeeCreature(movingCreature)) + if(!player->hasFlag(PlayerFlag_CanPushAllCreatures)) { - player->sendCancelMessage(RET_NOTMOVEABLE); - return false; + if(!movingCreature->isPushable()) + { + player->sendCancelMessage(RET_NOTMOVABLE); + return false; + } + + if(movingCreature->getNoMove()) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + return false; + } + + if(toTile->hasProperty(BLOCKPATH)) + { + player->sendCancelMessage(RET_NOTENOUGHROOM); + return false; + } } //check throw distance const Position& pos = movingCreature->getPosition(); - if(!player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere) && ((std::abs(pos.x - toPos.x) > movingCreature->getThrowRange()) || (std::abs( - pos.y - toPos.y) > movingCreature->getThrowRange()) || (std::abs( - pos.z - toPos.z) * 4 > movingCreature->getThrowRange()))) + if(!player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere) && ((std::abs(pos.x - toPos.x) > movingCreature->getThrowRange()) || + (std::abs(pos.y - toPos.y) > movingCreature->getThrowRange()) || (std::abs(pos.z - toPos.z) * 4 > movingCreature->getThrowRange()))) { player->sendCancelMessage(RET_DESTINATIONOUTOFREACH); return false; @@ -1072,25 +1193,42 @@ bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId, if(player != movingCreature) { - if(toTile->hasProperty(BLOCKPATH)) + if(!player->hasFlag(PlayerFlag_IgnoreProtectionZone) && (movingCreature->getZone() == ZONE_PROTECTION + || movingCreature->getZone() == ZONE_OPTIONAL) && !toTile->hasFlag(TILESTATE_OPTIONALZONE) + && !toTile->hasFlag(TILESTATE_PROTECTIONZONE)) { - player->sendCancelMessage(RET_NOTENOUGHROOM); + player->sendCancelMessage(RET_ACTIONNOTPERMITTEDINANOPVPZONE); return false; } - if((movingCreature->getZone() == ZONE_PROTECTION || movingCreature->getZone() == ZONE_NOPVP) - && !toTile->hasFlag(TILESTATE_NOPVPZONE) && !toTile->hasFlag(TILESTATE_PROTECTIONZONE) - && !player->hasFlag(PlayerFlag_IgnoreProtectionZone)) + if(!player->hasFlag(PlayerFlag_CanPushAllCreatures)) { - player->sendCancelMessage(RET_NOTPOSSIBLE); - return false; - } + if(toTile->getCreatureCount() && !Item::items[ + movingCreature->getTile()->ground->getID()].walkStack) + { + player->sendCancelMessage(RET_NOTENOUGHROOM); + return false; + } - if(toTile->getCreatures() && !toTile->getCreatures()->empty() - && !player->hasFlag(PlayerFlag_CanPushAllCreatures)) - { - player->sendCancelMessage(RET_NOTPOSSIBLE); - return false; + if(MagicField* field = toTile->getFieldItem()) + { + if(field->isUnstepable() || field->isBlocking(movingCreature) + || !movingCreature->isImmune(field->getCombatType())) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + return false; + } + } + + if(player->isProtected()) + { + Player* movingPlayer = movingCreature->getPlayer(); + if(movingPlayer && !movingPlayer->isProtected()) + { + player->sendCancelMessage(RET_NOTMOVABLE); + return false; + } + } } } @@ -1098,7 +1236,7 @@ bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId, CreatureEventList pushEvents = player->getCreatureEvents(CREATURE_EVENT_PUSH); for(CreatureEventList::iterator it = pushEvents.begin(); it != pushEvents.end(); ++it) { - if(!(*it)->executePush(player, movingCreature) && !deny) + if(!(*it)->executePush(player, movingCreature, toTile) && !deny) deny = true; } @@ -1108,7 +1246,7 @@ bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId, ReturnValue ret = internalMoveCreature(player, movingCreature, movingCreature->getTile(), toTile); if(ret != RET_NOERROR) { - if(!player->hasCustomFlag(PlayerCustomFlag_CanMoveFromFar) || !player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere)) + if(!player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere)) { player->sendCancelMessage(ret); return false; @@ -1117,17 +1255,15 @@ bool Game::playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId, if(!toTile->ground) { player->sendCancelMessage(RET_NOTPOSSIBLE); - return true; + return false; } - internalTeleport(movingCreature, toTile->getPosition(), true); - return true; + internalTeleport(movingCreature, toTile->getPosition(), false); } - - if(Player* movingPlayer = movingCreature->getPlayer()) + else if(Player* movingPlayer = movingCreature->getPlayer()) { uint64_t delay = OTSYS_TIME() + movingPlayer->getStepDuration(); - if(delay > movingPlayer->getNextActionTime()) + if(delay > movingPlayer->getNextActionTime(false)) movingPlayer->setNextAction(delay); } @@ -1149,7 +1285,7 @@ ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction, if((!(tmpTile = map->getTile(Position(currentPos.x, currentPos.y, currentPos.z - 1))) || (!tmpTile->ground && !tmpTile->hasProperty(BLOCKSOLID))) && (tmpTile = map->getTile(Position(destPos.x, destPos.y, destPos.z - 1))) - && tmpTile->ground && !tmpTile->hasProperty(BLOCKSOLID)) + && tmpTile->ground && !tmpTile->hasProperty(BLOCKSOLID) && !tmpTile->hasProperty(FLOORCHANGEDOWN)) { flags = flags | FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE; destPos.z--; @@ -1168,26 +1304,27 @@ ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction, if((toTile = map->getTile(destPos))) ret = internalMoveCreature(NULL, creature, fromTile, toTile, flags); - if(ret != RET_NOERROR) - { - if(Player* player = creature->getPlayer()) - { - player->sendCancelMessage(ret); - player->sendCancelWalk(); - } - } + if(ret == RET_NOERROR) + return RET_NOERROR; + + Player* player = creature->getPlayer(); + if(!player) + return ret; + player->sendCancelMessage(ret); + player->sendCancelWalk(); return ret; } -ReturnValue Game::internalMoveCreature(Creature* actor, Creature* creature, Cylinder* fromCylinder, Cylinder* toCylinder, uint32_t flags/* = 0*/) +ReturnValue Game::internalMoveCreature(Creature* actor, Creature* creature, Cylinder* fromCylinder, + Cylinder* toCylinder, uint32_t flags/* = 0*/, bool forceTeleport/* = false*/) { //check if we can move the creature to the destination - ReturnValue ret = toCylinder->__queryAdd(0, creature, 1, flags); + ReturnValue ret = toCylinder->__queryAdd(0, creature, 1, flags, actor); if(ret != RET_NOERROR) return ret; - fromCylinder->getTile()->moveCreature(actor, creature, toCylinder); + fromCylinder->getTile()->moveCreature(actor, creature, toCylinder, forceTeleport); if(creature->getParent() != toCylinder) return RET_NOERROR; @@ -1199,7 +1336,7 @@ ReturnValue Game::internalMoveCreature(Creature* actor, Creature* creature, Cyli { toCylinder->getTile()->moveCreature(actor, creature, subCylinder); if(creature->getParent() != subCylinder) //could happen if a script move the creature - break; + break; toCylinder = subCylinder; flags = 0; @@ -1219,10 +1356,8 @@ bool Game::playerMoveItem(uint32_t playerId, const Position& fromPos, if(!player->canDoAction()) { - uint32_t delay = player->getNextActionTime(); - SchedulerTask* task = createSchedulerTask(delay, boost::bind(&Game::playerMoveItem, this, - playerId, fromPos, spriteId, fromStackpos, toPos, count)); - + SchedulerTask* task = createSchedulerTask(player->getNextActionTime(), + boost::bind(&Game::playerMoveItem, this, playerId, fromPos, spriteId, fromStackpos, toPos, count)); player->setNextActionTask(task); return false; } @@ -1269,14 +1404,12 @@ bool Game::playerMoveItem(uint32_t playerId, const Position& fromPos, if(!player->hasCustomFlag(PlayerCustomFlag_CanPushAllItems) && (!item->isPushable() || (item->isLoadedFromMap() && (item->getUniqueId() != 0 || (item->getActionId() != 0 && item->getContainer()))))) { - player->sendCancelMessage(RET_NOTMOVEABLE); + player->sendCancelMessage(RET_NOTMOVABLE); return false; } - const Position& mapFromPos = fromCylinder->getTile()->getPosition(); - const Position& mapToPos = toCylinder->getTile()->getPosition(); - - const Position& playerPos = player->getPosition(); + const Position &mapToPos = toCylinder->getTile()->getPosition(), &playerPos = player->getPosition(), + &mapFromPos = fromCylinder->getTile()->getPosition(); if(playerPos.z > mapFromPos.z && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere)) { player->sendCancelMessage(RET_FIRSTGOUPSTAIRS); @@ -1297,8 +1430,8 @@ bool Game::playerMoveItem(uint32_t playerId, const Position& fromPos, { Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(player->getStepDuration(), boost::bind(&Game::playerMoveItem, this, - playerId, fromPos, spriteId, fromStackpos, toPos, count)); + SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()), + boost::bind(&Game::playerMoveItem, this, playerId, fromPos, spriteId, fromStackpos, toPos, count)); player->setNextWalkActionTask(task); return true; @@ -1361,8 +1494,8 @@ bool Game::playerMoveItem(uint32_t playerId, const Position& fromPos, { Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(player->getStepDuration(), boost::bind(&Game::playerMoveItem, this, - playerId, itemPos, spriteId, itemStackpos, toPos, count)); + SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()), + boost::bind(&Game::playerMoveItem, this, playerId, itemPos, spriteId, itemStackpos, toPos, count)); player->setNextWalkActionTask(task); return true; @@ -1390,12 +1523,26 @@ bool Game::playerMoveItem(uint32_t playerId, const Position& fromPos, return false; } + bool deny = false; + CreatureEventList throwEvents = player->getCreatureEvents(CREATURE_EVENT_THROW); + for(CreatureEventList::iterator it = throwEvents.begin(); it != throwEvents.end(); ++it) + { + if(!(*it)->executeThrow(player, item, fromPos, toPos) && !deny) + deny = true; + } + + if(deny) + return false; + ReturnValue ret = internalMoveItem(player, fromCylinder, toCylinder, toIndex, item, count, NULL); - if(ret == RET_NOERROR) - return true; + if(ret != RET_NOERROR) + { + player->sendCancelMessage(ret); + return false; + } - player->sendCancelMessage(ret); - return false; + player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL) - 10); + return true; } ReturnValue Game::internalMoveItem(Creature* actor, Cylinder* fromCylinder, Cylinder* toCylinder, @@ -1416,8 +1563,8 @@ ReturnValue Game::internalMoveItem(Creature* actor, Cylinder* fromCylinder, Cyli int32_t floor = 0; while((subCylinder = toCylinder->__queryDestination(index, item, &toItem, flags)) != toCylinder) { - toCylinder = subCylinder; flags = 0; + toCylinder = subCylinder; //to prevent infinite loop if(++floor >= MAP_MAX_LAYERS) break; @@ -1428,23 +1575,20 @@ ReturnValue Game::internalMoveItem(Creature* actor, Cylinder* fromCylinder, Cyli return RET_NOERROR; //silently ignore move //check if we can add this item - ReturnValue ret = toCylinder->__queryAdd(index, item, count, flags); + ReturnValue ret = toCylinder->__queryAdd(index, item, count, flags, actor); if(ret == RET_NEEDEXCHANGE) { //check if we can add it to source cylinder int32_t fromIndex = fromCylinder->__getIndexOfThing(item); - - ret = fromCylinder->__queryAdd(fromIndex, toItem, toItem->getItemCount(), 0); - if(ret == RET_NOERROR) + if((ret = fromCylinder->__queryAdd(fromIndex, toItem, toItem->getItemCount(), 0, actor)) == RET_NOERROR) { //check how much we can move uint32_t maxExchangeQueryCount = 0; - ReturnValue retExchangeMaxCount = fromCylinder->__queryMaxCount(-1, toItem, toItem->getItemCount(), maxExchangeQueryCount, 0); - + ReturnValue retExchangeMaxCount = fromCylinder->__queryMaxCount(INDEX_WHEREEVER, toItem, toItem->getItemCount(), maxExchangeQueryCount, 0); if(retExchangeMaxCount != RET_NOERROR && maxExchangeQueryCount == 0) return retExchangeMaxCount; - if((toCylinder->__queryRemove(toItem, toItem->getItemCount(), flags) == RET_NOERROR) && ret == RET_NOERROR) + if((toCylinder->__queryRemove(toItem, toItem->getItemCount(), flags, actor) == RET_NOERROR) && ret == RET_NOERROR) { int32_t oldToItemIndex = toCylinder->__getIndexOfThing(toItem); toCylinder->__removeThing(toItem, toItem->getItemCount()); @@ -1457,7 +1601,7 @@ ReturnValue Game::internalMoveItem(Creature* actor, Cylinder* fromCylinder, Cyli if(newToItemIndex != -1) fromCylinder->postAddNotification(actor, toItem, toCylinder, newToItemIndex); - ret = toCylinder->__queryAdd(index, item, count, flags); + ret = toCylinder->__queryAdd(index, item, count, flags, actor); toItem = NULL; } } @@ -1470,27 +1614,26 @@ ReturnValue Game::internalMoveItem(Creature* actor, Cylinder* fromCylinder, Cyli uint32_t maxQueryCount = 0; ReturnValue retMaxCount = toCylinder->__queryMaxCount(index, item, count, maxQueryCount, flags); if(retMaxCount != RET_NOERROR && !maxQueryCount) - return retMaxCount; + return ret; - uint32_t m = maxQueryCount, n = 0; + uint32_t m = maxQueryCount; if(item->isStackable()) m = std::min((uint32_t)count, m); Item* moveItem = item; //check if we can remove this item - ret = fromCylinder->__queryRemove(item, m, flags); - if(ret != RET_NOERROR) + if((ret = fromCylinder->__queryRemove(item, m, flags, actor)) != RET_NOERROR) return ret; //remove the item int32_t itemIndex = fromCylinder->__getIndexOfThing(item); fromCylinder->__removeThing(item, m); - bool isCompleteRemoval = item->isRemoved(); + Item* updateItem = NULL; - //update item(s) if(item->isStackable()) { + uint8_t n = 0; if(toItem && toItem->getID() == item->getID()) { n = std::min((uint32_t)100 - toItem->getItemCount(), m); @@ -1507,8 +1650,7 @@ ReturnValue Game::internalMoveItem(Creature* actor, Cylinder* fromCylinder, Cyli freeThing(item); } - //add item - if(moveItem /*m - n > 0*/) + if(moveItem) toCylinder->__addThing(actor, index, moveItem); if(itemIndex != -1) @@ -1544,69 +1686,96 @@ ReturnValue Game::internalMoveItem(Creature* actor, Cylinder* fromCylinder, Cyli } ReturnValue Game::internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index /*= INDEX_WHEREEVER*/, - uint32_t flags /*= 0*/, bool test /*= false*/) + uint32_t flags/* = 0*/, bool test/* = false*/) +{ + uint32_t remainderCount = 0; + return internalAddItem(actor, toCylinder, item, index, flags, test, remainderCount); +} + +ReturnValue Game::internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index, + uint32_t flags, bool test, uint32_t& remainderCount) +{ + Item* stackItem = NULL; + return internalAddItem(actor, toCylinder, item, index, flags, test, remainderCount, &stackItem); +} + +ReturnValue Game::internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index, + uint32_t flags, bool test, uint32_t& remainderCount, Item** stackItem) { + *stackItem = NULL; + remainderCount = 0; if(!toCylinder || !item) return RET_NOTPOSSIBLE; - Item* toItem = NULL; - toCylinder = toCylinder->__queryDestination(index, item, &toItem, flags); + Cylinder* destCylinder = toCylinder; + toCylinder = toCylinder->__queryDestination(index, item, stackItem, flags); - ReturnValue ret = toCylinder->__queryAdd(index, item, item->getItemCount(), flags); + //check if we can add this item + ReturnValue ret = toCylinder->__queryAdd(index, item, item->getItemCount(), flags, actor); if(ret != RET_NOERROR) return ret; uint32_t maxQueryCount = 0; - ret = toCylinder->__queryMaxCount(index, item, item->getItemCount(), maxQueryCount, flags); + ret = destCylinder->__queryMaxCount(INDEX_WHEREEVER, item, item->getItemCount(), maxQueryCount, flags); if(ret != RET_NOERROR) return ret; - if(!test) - { - uint32_t m = maxQueryCount; - if(item->isStackable()) - m = std::min((uint32_t)item->getItemCount(), maxQueryCount); + if(test) + return RET_NOERROR; - Item* moveItem = item; - if(item->isStackable() && toItem && toItem->getID() == item->getID()) + Item* toItem = *stackItem; + if(item->isStackable() && toItem) + { + uint32_t m = std::min((uint32_t)item->getItemCount(), maxQueryCount), n = 0; + if(toItem->getID() == item->getID()) { - uint32_t n = std::min((uint32_t)100 - toItem->getItemCount(), m); + n = std::min((uint32_t)100 - toItem->getItemCount(), m); toCylinder->__updateThing(toItem, toItem->getID(), toItem->getItemCount() + n); - if(m - n > 0) - { - if(m - n != item->getItemCount()) - moveItem = Item::CreateItem(item->getID(), m - n); - } - else + } + + uint32_t count = m - n; + if(count > 0) + { + if(item->getItemCount() != count) { - moveItem = NULL; - if(item->getParent() != VirtualCylinder::virtualCylinder) + Item* remainderItem = Item::CreateItem(item->getID(), count); + if((ret = internalAddItem(NULL, toCylinder, remainderItem, INDEX_WHEREEVER, flags, false)) == RET_NOERROR) { - item->onRemoved(); - freeThing(item); + if(item->getParent() != VirtualCylinder::virtualCylinder) + { + item->onRemoved(); + freeThing(item); + } + + return RET_NOERROR; } - } - } - if(moveItem) - { - toCylinder->__addThing(actor, index, moveItem); - int32_t moveItemIndex = toCylinder->__getIndexOfThing(moveItem); - if(moveItemIndex != -1) - toCylinder->postAddNotification(actor, moveItem, NULL, moveItemIndex); + delete remainderItem; + remainderCount = count; + return ret; + } } else { - int32_t itemIndex = toCylinder->__getIndexOfThing(item); - if(itemIndex != -1) - toCylinder->postAddNotification(actor, item, NULL, itemIndex); + if(item->getParent() != VirtualCylinder::virtualCylinder) + { + item->onRemoved(); + freeThing(item); + } + + return RET_NOERROR; } } + toCylinder->__addThing(NULL, index, item); + int32_t itemIndex = toCylinder->__getIndexOfThing(item); + if(itemIndex != -1) + toCylinder->postAddNotification(actor, item, NULL, itemIndex); + return RET_NOERROR; } -ReturnValue Game::internalRemoveItem(Creature* actor, Item* item, int32_t count /*= -1*/, bool test /*= false*/, uint32_t flags /*= 0*/) +ReturnValue Game::internalRemoveItem(Creature* actor, Item* item, int32_t count/* = -1*/, bool test/* = false*/, uint32_t flags/* = 0*/) { Cylinder* cylinder = item->getParent(); if(!cylinder) @@ -1616,7 +1785,7 @@ ReturnValue Game::internalRemoveItem(Creature* actor, Item* item, int32_t count count = item->getItemCount(); //check if we can remove this item - ReturnValue ret = cylinder->__queryRemove(item, count, flags | FLAG_IGNORENOTMOVEABLE); + ReturnValue ret = cylinder->__queryRemove(item, count, flags | FLAG_IGNORENOTMOVABLE, actor); if(ret != RET_NOERROR) return ret; @@ -1629,25 +1798,44 @@ ReturnValue Game::internalRemoveItem(Creature* actor, Item* item, int32_t count int32_t index = cylinder->__getIndexOfThing(item); cylinder->__removeThing(item, count); - bool isCompleteRemoval = false; + cylinder->postRemoveNotification(actor, item, NULL, index, item->isRemoved()); if(item->isRemoved()) - { - isCompleteRemoval = true; freeThing(item); - } - - cylinder->postRemoveNotification(actor, item, NULL, index, isCompleteRemoval); } item->onRemoved(); return RET_NOERROR; } -ReturnValue Game::internalPlayerAddItem(Creature* actor, Player* player, Item* item, bool dropOnMap /*= true*/) +ReturnValue Game::internalPlayerAddItem(Creature* actor, Player* player, Item* item, + bool dropOnMap/* = true*/, slots_t slot/* = SLOT_WHEREEVER*/) +{ + Item* toItem = NULL; + return internalPlayerAddItem(actor, player, item, dropOnMap, slot, &toItem); +} + +ReturnValue Game::internalPlayerAddItem(Creature* actor, Player* player, Item* item, + bool dropOnMap, slots_t slot, Item** toItem) { - ReturnValue ret = internalAddItem(actor, player, item); - if(ret != RET_NOERROR && dropOnMap) - ret = internalAddItem(actor, player->getTile(), item, INDEX_WHEREEVER, FLAG_NOLIMIT); + uint32_t remainderCount = 0, count = item->getItemCount(); + ReturnValue ret = internalAddItem(actor, player, item, (int32_t)slot, 0, false, remainderCount, toItem); + if(ret == RET_NOERROR) + return RET_NOERROR; + + if(dropOnMap) + { + if(!remainderCount) + return internalAddItem(actor, player->getTile(), item, (int32_t)slot, FLAG_NOLIMIT); + + Item* remainderItem = Item::CreateItem(item->getID(), remainderCount); + if(internalAddItem(actor, player->getTile(), remainderItem, INDEX_WHEREEVER, FLAG_NOLIMIT) == RET_NOERROR) + return RET_NOERROR; + + delete remainderItem; + } + + if(remainderCount && toItem) + transformItem(*toItem, (*toItem)->getID(), ((*toItem)->getItemCount() - (count - remainderCount))); return ret; } @@ -1660,48 +1848,49 @@ Item* Game::findItemOfType(Cylinder* cylinder, uint16_t itemId, std::list listContainer; Container* tmpContainer = NULL; + Item* item = NULL; Thing* thing = NULL; - Item* item = NULL; for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex();) { if((thing = cylinder->__getThing(i)) && (item = thing->getItem())) { if(item->getID() == itemId && (subType == -1 || subType == item->getSubType())) return item; - else - { - ++i; - if(depthSearch && (tmpContainer = item->getContainer())) - listContainer.push_back(tmpContainer); - } + + ++i; + if(depthSearch && (tmpContainer = item->getContainer())) + listContainer.push_back(tmpContainer); } else ++i; } + Container* container = NULL; while(listContainer.size() > 0) { - Container* container = listContainer.front(); + container = listContainer.front(); listContainer.pop_front(); - for(int32_t i = 0; i < (int32_t)container->size();) + for(int32_t i = 0; i < (int32_t)container->size(); ) { - Item* item = container->getItem(i); - if(item->getID() == itemId && (subType == -1 || subType == item->getSubType())) - return item; - else + if((item = container->getItem(i))) { + if(item->getID() == itemId && (subType == -1 || subType == item->getSubType())) + return item; + ++i; if((tmpContainer = item->getContainer())) listContainer.push_back(tmpContainer); } + else + ++i; } } return NULL; } -bool Game::removeItemOfType(Cylinder* cylinder, uint16_t itemId, int32_t count, int32_t subType /*= -1*/) +bool Game::removeItemOfType(Cylinder* cylinder, uint16_t itemId, int32_t count, int32_t subType/* = -1*/, bool onlyContainers/* = false*/) { if(!cylinder || ((int32_t)cylinder->__getItemTypeCount(itemId, subType) < count)) return false; @@ -1711,14 +1900,14 @@ bool Game::removeItemOfType(Cylinder* cylinder, uint16_t itemId, int32_t count, std::list listContainer; Container* tmpContainer = NULL; + Item* item = NULL; Thing* thing = NULL; - Item* item = NULL; - for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex() && count > 0;) + for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex() && count > 0; ) { if((thing = cylinder->__getThing(i)) && (item = thing->getItem())) { - if(item->getID() == itemId) + if(!onlyContainers && item->getID() == itemId) { if(item->isStackable()) { @@ -1738,6 +1927,8 @@ bool Game::removeItemOfType(Cylinder* cylinder, uint16_t itemId, int32_t count, --count; internalRemoveItem(NULL, item); } + else + ++i; } else { @@ -1750,47 +1941,54 @@ bool Game::removeItemOfType(Cylinder* cylinder, uint16_t itemId, int32_t count, ++i; } + Container* container = NULL; while(listContainer.size() > 0 && count > 0) { - Container* container = listContainer.front(); + container = listContainer.front(); listContainer.pop_front(); - for(int32_t i = 0; i < (int32_t)container->size() && count > 0;) + for(int32_t i = 0; i < (int32_t)container->size() && count > 0; ) { - Item* item = container->getItem(i); - if(item->getID() == itemId) + if((item = container->getItem(i))) { - if(item->isStackable()) + if(item->getID() == itemId) { - if(item->getItemCount() > count) + if(item->isStackable()) { - internalRemoveItem(NULL, item, count); - count = 0; + if(item->getItemCount() > count) + { + internalRemoveItem(NULL, item, count); + count = 0; + } + else + { + count-= item->getItemCount(); + internalRemoveItem(NULL, item); + } } - else + else if(subType == -1 || subType == item->getSubType()) { - count-= item->getItemCount(); + --count; internalRemoveItem(NULL, item); } + else + ++i; } - else if(subType == -1 || subType == item->getSubType()) + else { - --count; - internalRemoveItem(NULL, item); + ++i; + if((tmpContainer = item->getContainer())) + listContainer.push_back(tmpContainer); } } else - { ++i; - if((tmpContainer = item->getContainer())) - listContainer.push_back(tmpContainer); - } } } - return (count == 0); + return !count; } -uint32_t Game::getMoney(const Cylinder* cylinder) +uint64_t Game::getMoney(const Cylinder* cylinder) { if(!cylinder) return 0; @@ -1801,31 +1999,29 @@ uint32_t Game::getMoney(const Cylinder* cylinder) Thing* thing = NULL; Item* item = NULL; - uint32_t moneyCount = 0; + uint64_t moneyCount = 0; for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex(); ++i) { - if(!(thing = cylinder->__getThing(i))) - continue; - - if(!(item = thing->getItem())) + if(!(thing = cylinder->__getThing(i)) || !(item = thing->getItem())) continue; if((tmpContainer = item->getContainer())) listContainer.push_back(tmpContainer); - else if(item->getWorth() != 0) + else if(item->getWorth()) moneyCount += item->getWorth(); } + Container* container = NULL; while(listContainer.size() > 0) { - Container* container = listContainer.front(); + container = listContainer.front(); listContainer.pop_front(); for(ItemList::const_iterator it = container->getItems(); it != container->getEnd(); ++it) { item = *it; if((tmpContainer = item->getContainer())) listContainer.push_back(tmpContainer); - else if(item->getWorth() != 0) + else if(item->getWorth()) moneyCount += item->getWorth(); } } @@ -1833,7 +2029,7 @@ uint32_t Game::getMoney(const Cylinder* cylinder) return moneyCount; } -bool Game::removeMoney(Cylinder* cylinder, int32_t money, uint32_t flags /*= 0*/) +bool Game::removeMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/) { if(!cylinder) return false; @@ -1850,29 +2046,26 @@ bool Game::removeMoney(Cylinder* cylinder, int32_t money, uint32_t flags /*= 0*/ Thing* thing = NULL; Item* item = NULL; - int32_t moneyCount = 0; - for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex() && money > 0; ++i) + int64_t moneyCount = 0; + for(int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex(); ++i) { - if(!(thing = cylinder->__getThing(i))) - continue; - - if(!(item = thing->getItem())) + if(!(thing = cylinder->__getThing(i)) || !(item = thing->getItem())) continue; if((tmpContainer = item->getContainer())) listContainer.push_back(tmpContainer); - else if(item->getWorth() != 0) + else if(item->getWorth()) { moneyCount += item->getWorth(); moneyMap.insert(std::make_pair(item->getWorth(), item)); } } - while(listContainer.size() > 0 && money > 0) + while(listContainer.size() > 0) { Container* container = listContainer.front(); listContainer.pop_front(); - for(int32_t i = 0; i < (int32_t)container->size() && money > 0; i++) + for(int32_t i = 0; i < (int32_t)container->size(); ++i) { Item* item = container->getItem(i); if((tmpContainer = item->getContainer())) @@ -1891,16 +2084,14 @@ bool Game::removeMoney(Cylinder* cylinder, int32_t money, uint32_t flags /*= 0*/ for(MoneyMultiMap::iterator mit = moneyMap.begin(); mit != moneyMap.end() && money > 0; ++mit) { - Item* item = mit->second; - if(!item) + if(!(item = mit->second)) continue; internalRemoveItem(NULL, item); if(mit->first > money) { // Remove a monetary value from an item - int32_t remaind = item->getWorth() - money; - addMoney(cylinder, remaind, flags); + addMoney(cylinder, (int64_t)(item->getWorth() - money), flags); money = 0; } else @@ -1910,33 +2101,42 @@ bool Game::removeMoney(Cylinder* cylinder, int32_t money, uint32_t flags /*= 0*/ } moneyMap.clear(); - return money == 0; + return !money; } -void Game::addMoney(Cylinder* cylinder, int32_t money, uint32_t flags /*= 0*/) +void Game::addMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/) { IntegerMap moneyMap = Item::items.getMoneyMap(); - int32_t tmp = 0; for(IntegerMap::reverse_iterator it = moneyMap.rbegin(); it != moneyMap.rend(); ++it) { - tmp = money / it->first; + int64_t tmp = money / it->first; money -= tmp * it->first; - if(tmp != 0) + if(!tmp) + continue; + + do { - do + uint32_t remainderCount = 0; + Item* item = Item::CreateItem(it->second, std::min(100, tmp)); + if(internalAddItem(NULL, cylinder, item, INDEX_WHEREEVER, flags, false, remainderCount) != RET_NOERROR) { - Item* remaindItem = Item::CreateItem(it->second, std::min(100, tmp)); - if(internalAddItem(NULL, cylinder, remaindItem, INDEX_WHEREEVER, flags) != RET_NOERROR) - internalAddItem(NULL, cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT); + if(remainderCount) + { + delete item; + item = Item::CreateItem(it->second, remainderCount); + } - tmp -= std::min(100, tmp); + if(internalAddItem(NULL, cylinder->getTile(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + delete item; } - while(tmp > 0); + + tmp -= std::min((int64_t)100, tmp); } + while(tmp > 0); } } -Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) +Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount/* = -1*/) { if(item->getID() == newId && (newCount == -1 || (newCount == item->getSubType() && newCount != 0))) return item; @@ -1949,7 +2149,7 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) if(itemIndex == -1) { #ifdef __DEBUG__ - std::cout << "Error: transformItem, itemIndex == -1" << std::endl; + std::clog << "Error: transformItem, itemIndex == -1" << std::endl; #endif return item; } @@ -1957,12 +2157,11 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) if(!item->canTransform()) return item; - const ItemType& curType = Item::items[item->getID()]; - const ItemType& newType = Item::items[newId]; + const ItemType &curType = Item::items[item->getID()], &newType = Item::items[newId]; if(curType.alwaysOnTop != newType.alwaysOnTop) { - //This only occurs when you transform items on tiles from a downItem to a topItem (or vice versa) - //Remove the old, and add the new + // This only occurs when you transform items on tiles from a downItem to a topItem (or vice versa) + // Remove the old, and add the new ReturnValue ret = internalRemoveItem(NULL, item); if(ret != RET_NOERROR) return item; @@ -1977,11 +2176,14 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) return NULL; newItem->copyAttributes(item); - if(internalAddItem(NULL, cylinder, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT) == RET_NOERROR) - return newItem; + if(internalAddItem(NULL, cylinder, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + { + delete newItem; + return NULL; + } - delete newItem; - return NULL; + newItem->makeUnique(item); + return newItem; } if(curType.type == newType.type) @@ -1991,36 +2193,38 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) { if(!item->isStackable() && (!item->getDefaultDuration() || item->getDuration() <= 0)) { - int32_t tmpId = newId; - if(curType.id == newType.id) - tmpId = curType.decayTo; + int16_t tmp = newId; + if(curType.id == newId) + tmp = curType.decayTo; - if(tmpId != -1) - { - item = transformItem(item, tmpId); - return item; - } + if(tmp != -1) + return transformItem(item, tmp); } internalRemoveItem(NULL, item); return NULL; } - uint16_t itemId = item->getID(); - int32_t count = item->getSubType(); - cylinder->postRemoveNotification(NULL, item, cylinder, itemIndex, false); - if(curType.id != newType.id) + uint16_t tmp = item->getID(); + if(curType.id != newId) { - itemId = newId; + tmp = newId; if(newType.group != curType.group) item->setDefaultSubtype(); + + if(curType.hasSubType() && !newType.hasSubType()) + { + item->resetFluidType(); + item->resetCharges(); + } } + int32_t count = item->getSubType(); if(newCount != -1 && newType.hasSubType()) count = newCount; - cylinder->__updateThing(item, itemId, count); + cylinder->__updateThing(item, tmp, count); cylinder->postAddNotification(NULL, item, cylinder, itemIndex); return item; } @@ -2033,16 +2237,13 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) newItem = Item::CreateItem(newId, newCount); if(!newItem) - { - #ifdef __DEBUG__ - std::cout << "Error: [Game::transformItem] Item of type " << item->getID() << " transforming into invalid type " << newId << std::endl; - #endif return NULL; - } + newItem->copyAttributes(item); + newItem->makeUnique(item); cylinder->__replaceThing(itemIndex, newItem); - cylinder->postAddNotification(NULL, newItem, cylinder, itemIndex); + cylinder->postAddNotification(NULL, newItem, cylinder, itemIndex); item->setParent(NULL); cylinder->postRemoveNotification(NULL, item, cylinder, itemIndex, true); @@ -2050,7 +2251,7 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) return newItem; } -ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool pushMove, uint32_t flags /*= 0*/) +ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool forceTeleport, uint32_t flags/* = 0*/, bool fullTeleport/* = true*/) { if(newPos == thing->getPosition()) return RET_NOERROR; @@ -2062,11 +2263,10 @@ ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool pu { if(Creature* creature = thing->getCreature()) { - if(Position::areInRange<1,1,0>(creature->getPosition(), newPos) && pushMove) - creature->getTile()->moveCreature(NULL, creature, toTile, false); - else - creature->getTile()->moveCreature(NULL, creature, toTile, true); + if(fullTeleport) + return internalMoveCreature(NULL, creature, creature->getParent(), toTile, flags, forceTeleport); + creature->getTile()->moveCreature(NULL, creature, toTile, forceTeleport); return RET_NOERROR; } @@ -2077,48 +2277,38 @@ ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool pu return RET_NOTPOSSIBLE; } -//Implementation of player invoked events bool Game::playerMove(uint32_t playerId, Direction dir) { Player* player = getPlayerByID(playerId); if(!player || player->isRemoved()) return false; + player->setIdleTime(0); if(player->getNoMove()) { player->sendCancelWalk(); return false; } - player->stopWalk(); - int32_t delay = player->getWalkDelay(dir); - if(delay > 0) - { - player->setNextAction(OTSYS_TIME() + player->getStepDuration(dir) - SCHEDULER_MINTICKS); - if(SchedulerTask* task = createSchedulerTask(((uint32_t)delay), - boost::bind(&Game::playerMove, this, playerId, dir))) - player->setNextWalkTask(task); - - return false; - } - - player->setFollowCreature(NULL); - player->onWalk(dir); + std::list dirs; + dirs.push_back(dir); - player->setIdleTime(0); - return internalMoveCreature(player, dir) == RET_NOERROR; + player->setNextWalkActionTask(NULL); + return player->startAutoWalk(dirs); } -bool Game::playerBroadcastMessage(Player* player, SpeakClasses type, const std::string& text) +bool Game::playerBroadcastMessage(Player* player, MessageClasses type, const std::string& text, uint32_t statementId) { - if(!player->hasFlag(PlayerFlag_CanBroadcast) || type < SPEAK_CLASS_FIRST || type > SPEAK_CLASS_LAST) + if(!player->hasFlag(PlayerFlag_CanBroadcast) || !((type >= MSG_SPEAK_FIRST && type <= MSG_SPEAK_LAST) || + (type >= MSG_SPEAK_MONSTER_FIRST && type <= MSG_SPEAK_MONSTER_LAST))) return false; + Logger::getInstance()->eFile("talkactions/" + player->getName() + ".log", "#b " + text, true); for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) - it->second->sendCreatureSay(player, type, text); + it->second->sendCreatureSay(player, type, text, NULL, statementId); - //TODO: event handling - onCreatureSay - std::cout << "> " << player->getName() << " broadcasted: \"" << text << "\"." << std::endl; + //TODO: event handling - onCreatureSay (???) + std::clog << "> " << player->getName() << " broadcasted: \"" << text << "\"." << std::endl; return true; } @@ -2178,7 +2368,8 @@ bool Game::playerRequestChannels(uint32_t playerId) if(!player || player->isRemoved()) return false; - player->sendChannelsDialog(); + player->sendChannelsDialog(g_chat.getChannelList(player)); + player->setSentChat(true); return true; } @@ -2188,20 +2379,28 @@ bool Game::playerOpenChannel(uint32_t playerId, uint16_t channelId) if(!player || player->isRemoved()) return false; + bool deny = false; + CreatureEventList openEvents = player->getCreatureEvents(CREATURE_EVENT_CHANNEL_REQUEST); + for(CreatureEventList::iterator it = openEvents.begin(); it != openEvents.end(); ++it) + { + if(!(*it)->executeChannelRequest(player, asString(channelId), false, !player->hasSentChat()) && !deny) + deny = true; + } + + player->setSentChat(false); + if(deny) + return false; + ChatChannel* channel = g_chat.addUserToChannel(player, channelId); if(!channel) { #ifdef __DEBUG_CHAT__ - std::cout << "Game::playerOpenChannel - failed adding user to channel." << std::endl; + std::clog << "Game::playerOpenChannel - failed adding user to channel." << std::endl; #endif return false; } - if(channel->getId() != CHANNEL_RVR) - player->sendChannel(channel->getId(), channel->getName()); - else - player->sendRuleViolationsChannel(channel->getId()); - + player->sendChannel(channel->getId(), channel->getName()); return true; } @@ -2221,67 +2420,24 @@ bool Game::playerOpenPrivateChannel(uint32_t playerId, std::string& receiver) if(!player || player->isRemoved()) return false; - if(IOLoginData::getInstance()->playerExists(receiver)) - player->sendOpenPrivateChannel(receiver); - else - player->sendCancel("A player with this name does not exist."); - - return true; -} - -bool Game::playerProcessRuleViolation(uint32_t playerId, const std::string& name) -{ - Player* player = getPlayerByID(playerId); - if(!player || player->isRemoved()) - return false; - - if(!player->hasFlag(PlayerFlag_CanAnswerRuleViolations)) - return false; - - Player* reporter = getPlayerByName(name); - if(!reporter) - return false; - - RuleViolationsMap::iterator it = ruleViolations.find(reporter->getID()); - if(it == ruleViolations.end()) - return false; - - RuleViolation& rvr = *it->second; - if(!rvr.isOpen) - return false; - - rvr.isOpen = false; - rvr.gamemaster = player; - if(ChatChannel* channel = g_chat.getChannelById(CHANNEL_RVR)) + bool deny = false; + CreatureEventList openEvents = player->getCreatureEvents(CREATURE_EVENT_CHANNEL_REQUEST); + for(CreatureEventList::iterator it = openEvents.begin(); it != openEvents.end(); ++it) { - UsersMap tmpMap = channel->getUsers(); - for(UsersMap::iterator tit = tmpMap.begin(); tit != tmpMap.end(); ++tit) - tit->second->sendRemoveReport(reporter->getName()); + if(!(*it)->executeChannelRequest(player, receiver, true, !player->hasSentChat()) && !deny) + deny = true; } - return true; -} - -bool Game::playerCloseRuleViolation(uint32_t playerId, const std::string& name) -{ - Player* player = getPlayerByID(playerId); - if(!player || player->isRemoved()) - return false; - - Player* reporter = getPlayerByName(name); - if(!reporter) + player->setSentChat(false); + if(deny) return false; - return closeRuleViolation(reporter); -} - -bool Game::playerCancelRuleViolation(uint32_t playerId) -{ - Player* player = getPlayerByID(playerId); - if(!player || player->isRemoved()) - return false; + if(IOLoginData::getInstance()->playerExists(receiver)) + player->sendOpenPrivateChannel(receiver); + else + player->sendCancel("A player with this name does not exist."); - return cancelRuleViolation(player); + return true; } bool Game::playerCloseNpcChannel(uint32_t playerId) @@ -2321,6 +2477,12 @@ bool Game::playerAutoWalk(uint32_t playerId, std::list& listDir) return false; player->setIdleTime(0); + if(player->getNoMove()) + { + player->sendCancelWalk(); + return false; + } + if(player->hasCondition(CONDITION_GAMEMASTER, GAMEMASTER_TELEPORT)) { Position pos = player->getPosition(); @@ -2335,7 +2497,7 @@ bool Game::playerAutoWalk(uint32_t playerId, std::list& listDir) } internalCreatureTurn(player, getDirectionTo(player->getPosition(), pos, false)); - internalTeleport(player, pos, false); + internalTeleport(player, pos, true); return true; } @@ -2371,7 +2533,7 @@ bool Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, int16_t f } Item* item = thing->getItem(); - if(!item || !item->isUseable()) + if(!item || !item->isUsable()) { player->sendCancelMessage(RET_CANNOTUSETHISOBJECT); return false; @@ -2381,9 +2543,14 @@ bool Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, int16_t f ReturnValue ret = g_actions->canUse(player, fromPos); if(ret == RET_NOERROR) { - ret = g_actions->canUse(player, toPos, item); + ret = g_actions->canUseEx(player, toPos, item); if(ret == RET_TOOFARAWAY) - walkToPos = toPos; + { + if(!player->hasCustomFlag(PlayerCustomFlag_CanUseFar)) + walkToPos = toPos; + else + ret = RET_NOERROR; + } } if(ret != RET_NOERROR) @@ -2391,7 +2558,6 @@ bool Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, int16_t f if(ret == RET_TOOFARAWAY) { Position itemPos = fromPos; - int16_t itemStackpos = fromStackpos; if(fromPos.x != 0xFFFF && toPos.x != 0xFFFF && Position::areInRange<1,1,0>(fromPos, player->getPosition()) && !Position::areInRange<1,1,0>(fromPos, toPos)) { @@ -2405,7 +2571,13 @@ bool Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, int16_t f } //changing the position since its now in the inventory of the player - internalGetPosition(moveItem, itemPos, itemStackpos); + internalGetPosition(moveItem, itemPos, fromStackpos); + } + + if(player->getNoMove()) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + return false; } std::list listDir; @@ -2414,8 +2586,8 @@ bool Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, int16_t f Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(400, boost::bind(&Game::playerUseItemEx, this, - playerId, itemPos, itemStackpos, fromSpriteId, toPos, toStackpos, toSpriteId, isHotkey)); + SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()), + boost::bind(&Game::playerUseItemEx, this, playerId, itemPos, fromStackpos, fromSpriteId, toPos, toStackpos, toSpriteId, isHotkey)); player->setNextWalkActionTask(task); return true; @@ -2433,15 +2605,12 @@ bool Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, int16_t f if(!player->canDoAction()) { - uint32_t delay = player->getNextActionTime(); - SchedulerTask* task = createSchedulerTask(delay, boost::bind(&Game::playerUseItemEx, this, - playerId, fromPos, fromStackpos, fromSpriteId, toPos, toStackpos, toSpriteId, isHotkey)); - + SchedulerTask* task = createSchedulerTask(player->getNextActionTime(), + boost::bind(&Game::playerUseItemEx, this, playerId, fromPos, fromStackpos, fromSpriteId, toPos, toStackpos, toSpriteId, isHotkey)); player->setNextActionTask(task); return false; } - player->setIdleTime(0); player->setNextActionTask(NULL); return g_actions->useItemEx(player, fromPos, toPos, toStackpos, item, isHotkey); } @@ -2464,25 +2633,34 @@ bool Game::playerUseItem(uint32_t playerId, const Position& pos, int16_t stackpo } Item* item = thing->getItem(); - if(!item || item->isUseable()) + if(!item || item->isUsable()) { player->sendCancelMessage(RET_CANNOTUSETHISOBJECT); return false; } ReturnValue ret = g_actions->canUse(player, pos); + if(ret == RET_TOOFARAWAY && player->hasCustomFlag(PlayerCustomFlag_CanUseFar)) + ret = RET_NOERROR; + if(ret != RET_NOERROR) { if(ret == RET_TOOFARAWAY) { + if(player->getNoMove()) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + return false; + } + std::list listDir; if(getPathToEx(player, pos, listDir, 0, 1, true, true)) { Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(400, boost::bind(&Game::playerUseItem, this, - playerId, pos, stackpos, index, spriteId, isHotkey)); + SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()), + boost::bind(&Game::playerUseItem, this, playerId, pos, stackpos, index, spriteId, isHotkey)); player->setNextWalkActionTask(task); return true; @@ -2500,20 +2678,17 @@ bool Game::playerUseItem(uint32_t playerId, const Position& pos, int16_t stackpo if(!player->canDoAction()) { - uint32_t delay = player->getNextActionTime(); - SchedulerTask* task = createSchedulerTask(delay, boost::bind(&Game::playerUseItem, this, - playerId, pos, stackpos, index, spriteId, isHotkey)); - + SchedulerTask* task = createSchedulerTask(player->getNextActionTime(), + boost::bind(&Game::playerUseItem, this, playerId, pos, stackpos, index, spriteId, isHotkey)); player->setNextActionTask(task); return false; } - player->setIdleTime(0); player->setNextActionTask(NULL); return g_actions->useItem(player, pos, index, item); } -bool Game::playerUseBattleWindow(uint32_t playerId, const Position& fromPos, int16_t fromStackpos, +bool Game::playerUseBattleWindow(uint32_t playerId, const Position& pos, int16_t stackpos, uint32_t creatureId, uint16_t spriteId, bool isHotkey) { Player* player = getPlayerByID(playerId); @@ -2533,7 +2708,7 @@ bool Game::playerUseBattleWindow(uint32_t playerId, const Position& fromPos, int return false; } - Thing* thing = internalGetThing(player, fromPos, fromStackpos, spriteId, STACKPOS_USE); + Thing* thing = internalGetThing(player, pos, stackpos, spriteId, STACKPOS_USE); if(!thing) { player->sendCancelMessage(RET_NOTPOSSIBLE); @@ -2547,19 +2722,25 @@ bool Game::playerUseBattleWindow(uint32_t playerId, const Position& fromPos, int return false; } - ReturnValue ret = g_actions->canUse(player, fromPos); + ReturnValue ret = g_actions->canUse(player, pos); if(ret != RET_NOERROR) { if(ret == RET_TOOFARAWAY) { + if(player->getNoMove()) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + return false; + } + std::list listDir; if(getPathToEx(player, item->getPosition(), listDir, 0, 1, true, true)) { Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(400, boost::bind(&Game::playerUseBattleWindow, this, - playerId, fromPos, fromStackpos, creatureId, spriteId, isHotkey)); + SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()), + boost::bind(&Game::playerUseBattleWindow, this, playerId, pos, stackpos, creatureId, spriteId, isHotkey)); player->setNextWalkActionTask(task); return true; @@ -2577,17 +2758,14 @@ bool Game::playerUseBattleWindow(uint32_t playerId, const Position& fromPos, int if(!player->canDoAction()) { - uint32_t delay = player->getNextActionTime(); - SchedulerTask* task = createSchedulerTask(delay, boost::bind(&Game::playerUseBattleWindow, this, - playerId, fromPos, fromStackpos, creatureId, spriteId, isHotkey)); - + SchedulerTask* task = createSchedulerTask(player->getNextActionTime(), + boost::bind(&Game::playerUseBattleWindow, this, playerId, pos, stackpos, creatureId, spriteId, isHotkey)); player->setNextActionTask(task); return false; } - player->setIdleTime(0); player->setNextActionTask(NULL); - return g_actions->useItemEx(player, fromPos, creature->getPosition(), + return g_actions->useItemEx(player, pos, creature->getPosition(), creature->getParent()->__getIndexOfThing(creature), item, isHotkey, creatureId); } @@ -2670,14 +2848,20 @@ bool Game::playerRotateItem(uint32_t playerId, const Position& pos, int16_t stac if(pos.x != 0xFFFF && !Position::areInRange<1,1,0>(pos, player->getPosition())) { + if(player->getNoMove()) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + return false; + } + std::list listDir; if(getPathToEx(player, pos, listDir, 0, 1, true, true)) { Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(400, boost::bind(&Game::playerRotateItem, this, - playerId, pos, stackpos, spriteId)); + SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()), + boost::bind(&Game::playerRotateItem, this, playerId, pos, stackpos, spriteId)); player->setNextWalkActionTask(task); return true; @@ -2691,7 +2875,6 @@ bool Game::playerRotateItem(uint32_t playerId, const Position& pos, int16_t stac if(newId != 0) transformItem(item, newId); - player->setIdleTime(0); return true; } @@ -2736,6 +2919,16 @@ bool Game::playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std:: deny = true; } + player->setWriteItem(NULL); + if((Container*)writeItem->getParent() == &player->transferContainer) + { + player->transferContainer.setParent(NULL); + player->transferContainer.__removeThing(writeItem, writeItem->getItemCount()); + + freeThing(writeItem); + return true; + } + if(deny) return false; @@ -2759,7 +2952,6 @@ bool Game::playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std:: if(newId != 0) transformItem(writeItem, newId); - player->setWriteItem(NULL); return true; } @@ -2769,16 +2961,24 @@ bool Game::playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t w if(!player || player->isRemoved()) return false; - uint32_t internalWindowTextId = 0; - uint32_t internalListId = 0; - + uint32_t internalWindowTextId = 0, internalListId = 0; House* house = player->getEditHouse(internalWindowTextId, internalListId); - if(house && internalWindowTextId == windowTextId && listId == 0) + if(!house || internalWindowTextId != windowTextId || listId) + return true; + + bool deny = false; + CreatureEventList houseEditEvents = player->getCreatureEvents(CREATURE_EVENT_HOUSEEDIT); + for(CreatureEventList::iterator it = houseEditEvents.begin(); it != houseEditEvents.end(); ++it) { - house->setAccessList(internalListId, text); - player->setEditHouse(NULL); + if(!(*it)->executeHouseEdit(player, house->getId(), internalListId, text)) + deny = true; } + player->setEditHouse(NULL); + if(deny) + return false; + + house->setAccessList(internalListId, text); return true; } @@ -2792,7 +2992,7 @@ bool Game::playerRequestTrade(uint32_t playerId, const Position& pos, int16_t st Player* tradePartner = getPlayerByID(tradePlayerId); if(!tradePartner || tradePartner == player) { - player->sendTextMessage(MSG_INFO_DESCR, "Sorry, not possible."); + player->sendCancelMessage(RET_NOTPOSSIBLE); return false; } @@ -2804,9 +3004,16 @@ bool Game::playerRequestTrade(uint32_t playerId, const Position& pos, int16_t st return false; } + if(!canThrowObjectTo(tradePartner->getPosition(), player->getPosition()) + && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere)) + { + player->sendCancelMessage(RET_CREATUREISNOTREACHABLE); + return false; + } + Item* tradeItem = dynamic_cast(internalGetThing(player, pos, stackpos, spriteId, STACKPOS_USE)); - if(!tradeItem || tradeItem->getClientID() != spriteId || !tradeItem->isPickupable() || (tradeItem->isLoadedFromMap() && - (tradeItem->getUniqueId() != 0 || (tradeItem->getActionId() != 0 && tradeItem->getContainer())))) + if(!tradeItem || tradeItem->getClientID() != spriteId || !tradeItem->isPickupable() || (tradeItem->isLoadedFromMap() + && (tradeItem->getUniqueId() != 0 || (tradeItem->getActionId() != 0 && tradeItem->getContainer())))) { player->sendCancelMessage(RET_NOTPOSSIBLE); return false; @@ -2824,16 +3031,29 @@ bool Game::playerRequestTrade(uint32_t playerId, const Position& pos, int16_t st return false; } + HouseTile* houseTile = dynamic_cast(tradeItem->getParent()); + if(houseTile && houseTile->getHouse() && !houseTile->getHouse()->isInvited(player)) + { + player->sendCancelMessage(RET_PLAYERISNOTINVITED); + return false; + } + if(!Position::areInRange<1,1,0>(tradeItem->getPosition(), player->getPosition())) { + if(player->getNoMove()) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + return false; + } + std::list listDir; if(getPathToEx(player, pos, listDir, 0, 1, true, true)) { Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::playerAutoWalk, this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(400, boost::bind(&Game::playerRequestTrade, this, - playerId, pos, stackpos, tradePlayerId, spriteId)); + SchedulerTask* task = createSchedulerTask(std::max((int32_t)SCHEDULER_MINTICKS, player->getStepDuration()), + boost::bind(&Game::playerRequestTrade, this, playerId, pos, stackpos, tradePlayerId, spriteId)); player->setNextWalkActionTask(task); return true; @@ -2844,7 +3064,7 @@ bool Game::playerRequestTrade(uint32_t playerId, const Position& pos, int16_t st } const Container* container = NULL; - for(std::map::const_iterator it = tradeItems.begin(); it != tradeItems.end(); it++) + for(std::map::const_iterator it = tradeItems.begin(); it != tradeItems.end(); ++it) { if(tradeItem == it->first || ((container = dynamic_cast(tradeItem)) && container->isHoldingItem(it->first)) || @@ -2856,9 +3076,11 @@ bool Game::playerRequestTrade(uint32_t playerId, const Position& pos, int16_t st } Container* tradeContainer = tradeItem->getContainer(); - if(tradeContainer && tradeContainer->getItemHoldingCount() + 1 > 100) + if(tradeContainer && tradeContainer->getItemHoldingCount() + 1 > (uint32_t)g_config.getNumber(ConfigManager::TRADE_LIMIT)) { - player->sendTextMessage(MSG_INFO_DESCR, "You cannot trade more than 100 items."); + std::stringstream s; + s << "You cannot trade more than " << g_config.getNumber(ConfigManager::TRADE_LIMIT) << " items."; + player->sendTextMessage(MSG_INFO_DESCR, s.str()); return false; } @@ -2902,13 +3124,14 @@ bool Game::internalStartTrade(Player* player, Player* tradePartner, Item* tradeI char buffer[100]; sprintf(buffer, "%s wants to trade with you", player->getName().c_str()); tradePartner->sendTextMessage(MSG_INFO_DESCR, buffer); + tradePartner->tradeState = TRADE_ACKNOWLEDGE; tradePartner->tradePartner = player; } else { - Item* counterOfferItem = tradePartner->tradeItem; - player->sendTradeItemRequest(tradePartner, counterOfferItem, false); + Item* counterItem = tradePartner->tradeItem; + player->sendTradeItemRequest(tradePartner, counterItem, false); tradePartner->sendTradeItemRequest(player, tradeItem, false); } @@ -2918,15 +3141,23 @@ bool Game::internalStartTrade(Player* player, Player* tradePartner, Item* tradeI bool Game::playerAcceptTrade(uint32_t playerId) { Player* player = getPlayerByID(playerId); - if(!player || player->isRemoved()) + if(!player || player->isRemoved() || (player->getTradeState() != TRADE_ACKNOWLEDGE + && player->getTradeState() != TRADE_INITIATED)) + return false; + + Player* tradePartner = player->tradePartner; + if(!tradePartner) return false; - if(!(player->getTradeState() == TRADE_ACKNOWLEDGE || player->getTradeState() == TRADE_INITIATED)) + if(!canThrowObjectTo(tradePartner->getPosition(), player->getPosition()) + && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere)) + { + player->sendCancelMessage(RET_CREATUREISNOTREACHABLE); return false; + } player->setTradeState(TRADE_ACCEPT); - Player* tradePartner = player->tradePartner; - if(!tradePartner || tradePartner->getTradeState() != TRADE_ACCEPT) + if(tradePartner->getTradeState() != TRADE_ACCEPT) return false; Item* tradeItem1 = player->tradeItem; @@ -2960,54 +3191,63 @@ bool Game::playerAcceptTrade(uint32_t playerId) tradeItems.erase(it); } - ReturnValue ret1 = internalAddItem(player, tradePartner, tradeItem1, INDEX_WHEREEVER, 0, true); - ReturnValue ret2 = internalAddItem(tradePartner, player, tradeItem2, INDEX_WHEREEVER, 0, true); + ReturnValue ret1 = internalAddItem(player, tradePartner, tradeItem1, INDEX_WHEREEVER, FLAG_IGNOREAUTOSTACK, true); + ReturnValue ret2 = internalAddItem(tradePartner, player, tradeItem2, INDEX_WHEREEVER, FLAG_IGNOREAUTOSTACK, true); - bool isSuccess = false; + bool success = false; if(ret1 == RET_NOERROR && ret2 == RET_NOERROR) { ret1 = internalRemoveItem(tradePartner, tradeItem1, tradeItem1->getItemCount(), true); ret2 = internalRemoveItem(player, tradeItem2, tradeItem2->getItemCount(), true); if(ret1 == RET_NOERROR && ret2 == RET_NOERROR) { - Cylinder* cylinder1 = tradeItem1->getParent(); - Cylinder* cylinder2 = tradeItem2->getParent(); - - internalMoveItem(player, cylinder1, tradePartner, INDEX_WHEREEVER, tradeItem1, tradeItem1->getItemCount(), NULL); - internalMoveItem(tradePartner, cylinder2, player, INDEX_WHEREEVER, tradeItem2, tradeItem2->getItemCount(), NULL); + internalMoveItem(player, tradeItem1->getParent(), tradePartner, INDEX_WHEREEVER, + tradeItem1, tradeItem1->getItemCount(), NULL, FLAG_IGNOREAUTOSTACK); + internalMoveItem(tradePartner, tradeItem2->getParent(), player, INDEX_WHEREEVER, + tradeItem2, tradeItem2->getItemCount(), NULL, FLAG_IGNOREAUTOSTACK); tradeItem1->onTradeEvent(ON_TRADE_TRANSFER, tradePartner, player); tradeItem2->onTradeEvent(ON_TRADE_TRANSFER, player, tradePartner); - - isSuccess = true; + success = true; } } - if(!isSuccess) + if(!success) { - std::string errorDescription = getTradeErrorDescription(ret1, tradeItem1); - tradePartner->sendTextMessage(MSG_INFO_DESCR, errorDescription); - tradeItem2->onTradeEvent(ON_TRADE_CANCEL, tradePartner, NULL); + std::string error; + if(tradeItem2) + { + error = getTradeErrorDescription(ret1, tradeItem1); + tradePartner->sendTextMessage(MSG_INFO_DESCR, error); + tradeItem2->onTradeEvent(ON_TRADE_CANCEL, tradePartner, player); + } - errorDescription = getTradeErrorDescription(ret2, tradeItem2); - player->sendTextMessage(MSG_INFO_DESCR, errorDescription); - tradeItem1->onTradeEvent(ON_TRADE_CANCEL, player, NULL); + if(tradeItem1) + { + error = getTradeErrorDescription(ret2, tradeItem2); + player->sendTextMessage(MSG_INFO_DESCR, error); + tradeItem1->onTradeEvent(ON_TRADE_CANCEL, player, tradePartner); + } } player->setTradeState(TRADE_NONE); player->tradeItem = NULL; player->tradePartner = NULL; - player->sendTradeClose(); tradePartner->setTradeState(TRADE_NONE); tradePartner->tradeItem = NULL; tradePartner->tradePartner = NULL; + + player->sendTradeClose(); tradePartner->sendTradeClose(); - return isSuccess; + return success; } std::string Game::getTradeErrorDescription(ReturnValue ret, Item* item) { + if(!item) + return std::string(); + std::stringstream ss; if(ret == RET_NOTENOUGHCAPACITY) { @@ -3147,7 +3387,7 @@ bool Game::internalCloseTrade(Player* player) Player* tradePartner = player->tradePartner; if((tradePartner && tradePartner->getTradeState() == TRADE_TRANSFER) || player->getTradeState() == TRADE_TRANSFER) { - std::cout << "[Warning - Game::internalCloseTrade] TradeState == TRADE_TRANSFER, " << + std::clog << "[Warning - Game::internalCloseTrade] TradeState == TRADE_TRANSFER, " << player->getName() << " " << player->getTradeState() << ", " << tradePartner->getName() << " " << tradePartner->getTradeState() << std::endl; return true; @@ -3163,7 +3403,7 @@ bool Game::internalCloseTrade(Player* player) tradeItems.erase(it); } - player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player, NULL); + player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player, tradePartner); player->tradeItem = NULL; } @@ -3183,7 +3423,7 @@ bool Game::internalCloseTrade(Player* player) tradeItems.erase(it); } - tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner, NULL); + tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner, player); tradePartner->tradeItem = NULL; } @@ -3216,6 +3456,8 @@ bool Game::playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t coun uint8_t subType = count; if(it.isFluidContainer() && count < uint8_t(sizeof(reverseFluidMap) / sizeof(int8_t))) subType = reverseFluidMap[count]; + else if(!it.hasSubType()) + subType = 0; if(!player->canShopItem(it.id, subType, SHOPEVENT_BUY)) return false; @@ -3224,7 +3466,7 @@ bool Game::playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t coun return true; } -bool Game::playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount) +bool Game::playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, bool ignoreEquipped) { Player* player = getPlayerByID(playerId); if(!player || player->isRemoved()) @@ -3242,11 +3484,13 @@ bool Game::playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, u uint8_t subType = count; if(it.isFluidContainer() && count < uint8_t(sizeof(reverseFluidMap) / sizeof(int8_t))) subType = reverseFluidMap[count]; + else if(!it.hasSubType()) + subType = 0; if(!player->canShopItem(it.id, subType, SHOPEVENT_SELL)) return false; - merchant->onPlayerTrade(player, SHOPEVENT_SELL, onSell, it.id, subType, amount); + merchant->onPlayerTrade(player, SHOPEVENT_SELL, onSell, it.id, subType, amount, ignoreEquipped); return true; } @@ -3271,8 +3515,13 @@ bool Game::playerLookInShop(uint32_t playerId, uint16_t spriteId, uint8_t count) return false; int32_t subType = count; - if(it.isFluidContainer() && count < uint8_t(sizeof(reverseFluidMap) / sizeof(int8_t))) - subType = reverseFluidMap[count]; + if(it.isSplash() || it.isFluidContainer()) + { + if(subType == 3) // FIXME: hack... + subType = 11; + else if(count < uint8_t(sizeof(reverseFluidMap) / sizeof(int8_t))) + subType = reverseFluidMap[count]; + } std::stringstream ss; ss << "You see " << Item::getDescription(it, 1, NULL, subType); @@ -3345,7 +3594,11 @@ bool Game::playerLookAt(uint32_t playerId, const Position& pos, uint16_t spriteI ss << std::endl << "TransformTo: [" << it.transformEquipTo << "] (onEquip)."; else if(it.transformDeEquipTo) ss << std::endl << "TransformTo: [" << it.transformDeEquipTo << "] (onDeEquip)."; - else if(it.decayTo != -1) + + if(it.transformUseTo) + ss << std::endl << "TransformTo: [" << it.transformUseTo << "] (onUse)."; + + if(it.decayTo != -1) ss << std::endl << "DecayTo: [" << it.decayTo << "]."; } } @@ -3359,17 +3612,31 @@ bool Game::playerLookAt(uint32_t playerId, const Position& pos, uint16_t spriteI ss << ", Mana: [" << creature->getMana() << " / " << creature->getMaxMana() << "]"; ss << "."; - if(const Player* destPlayer = creature->getPlayer()) + if(const Player* target = creature->getPlayer()) { - ss << std::endl << "IP: " << convertIPAddress(destPlayer->getIP()) << ", Client: " << destPlayer->getClientVersion() << "."; - if(destPlayer->isGhost()) - ss << std::endl << "* Ghost mode *"; + ss << std::endl << "IP: " << convertIPAddress(target->getIP()); +#if CLIENT_VERSION_MIN != CLIENT_VERSION_MAX + ss << ", Client: " << target->getClientVersion(); +#endif + ss << "."; } + + if(creature->isGhost()) + ss << std::endl << "* Ghost mode *"; } } if(player->hasCustomFlag(PlayerCustomFlag_CanSeePosition)) - ss << std::endl << "Position: [X: " << thingPos.x << "] [Y: " << thingPos.y << "] [Z: " << thingPos.z << "]."; + { + ss << std::endl << "Position: [X: " << thingPos.x << "] [Y: " << thingPos.y << "] [Z: " << thingPos.z << "]"; + if(Tile* tile = getTile(thingPos)) + { + if(House* house = tile->getHouse()) + ss << " [House: " << house->getId() << "]"; + } + + ss << "."; + } player->sendTextMessage(MSG_INFO_DESCR, ss.str()); return true; @@ -3436,13 +3703,17 @@ bool Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId) ReturnValue ret = Combat::canTargetCreature(player, attackCreature); if(ret != RET_NOERROR) { - player->sendCancelMessage(ret); + if(ret != RET_NEEDEXCHANGE) + player->sendCancelMessage(ret); + player->sendCancelTarget(); player->setAttackedCreature(NULL); return false; } player->setAttackedCreature(attackCreature); + Dispatcher::getInstance().addTask(createTask(boost::bind( + &Game::updateCreatureWalk, this, player->getID()))); return true; } @@ -3454,9 +3725,20 @@ bool Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId) Creature* followCreature = NULL; if(creatureId) + { + if(player->getNoMove()) + { + playerCancelAttackAndFollow(playerId); + player->sendCancelMessage(RET_NOTPOSSIBLE); + return false; + } + followCreature = getCreatureByID(creatureId); + } player->setAttackedCreature(NULL); + Dispatcher::getInstance().addTask(createTask(boost::bind( + &Game::updateCreatureWalk, this, player->getID()))); return player->setFollowCreature(followCreature); } @@ -3468,21 +3750,23 @@ bool Game::playerSetFightModes(uint32_t playerId, fightMode_t fightMode, chaseMo player->setFightMode(fightMode); player->setChaseMode(chaseMode); - player->setSecureMode(secureMode); + + player->setLastAttack(OTSYS_TIME()); return true; } bool Game::playerRequestAddVip(uint32_t playerId, const std::string& vipName) { Player* player = getPlayerByID(playerId); - if(!player || player->isRemoved()) + if(!player || player->isRemoved() || !player->canDoExAction()) return false; uint32_t guid; bool specialVip; - std::string name = vipName; + + player->setNextExAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::CUSTOM_ACTIONS_DELAY_INTERVAL) - 10); if(!IOLoginData::getInstance()->getGuidByNameEx(guid, specialVip, name)) { player->sendTextMessage(MSG_STATUS_SMALL, "A player with that name does not exist."); @@ -3534,9 +3818,8 @@ bool Game::playerTurn(uint32_t playerId, Direction dir) player->setIdleTime(0); ReturnValue ret = tile->__queryAdd(0, player, 1, FLAG_IGNOREBLOCKITEM); - if(ret != RET_NOTENOUGHROOM && (ret != RET_NOTPOSSIBLE || player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere)) - && (ret != RET_PLAYERISNOTINVITED || player->hasFlag(PlayerFlag_CanEditHouses))) - return internalTeleport(player, pos, true); + if(ret != RET_NOTENOUGHROOM && (ret != RET_NOTPOSSIBLE || player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere))) + return (internalTeleport(player, pos, false, FLAG_NOLIMIT, false) != RET_NOERROR); player->sendCancelMessage(ret); return false; @@ -3568,77 +3851,96 @@ bool Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit) return true; } -bool Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, const std::string& receiver, const std::string& text) +bool Game::playerChangeMountStatus(uint32_t playerId, bool status) { Player* player = getPlayerByID(playerId); if(!player || player->isRemoved()) return false; - uint32_t muteTime = 0; - bool muted = player->isMuted(channelId, type, muteTime); - if(muted) + if(!player->canDoAction() || player->hasCondition(CONDITION_INVISIBLE)) { - char buffer[75]; - sprintf(buffer, "You are still muted for %d seconds.", muteTime); - player->sendTextMessage(MSG_STATUS_SMALL, buffer); + player->sendCancelMessage(RET_NOTPOSSIBLE); return false; } - if(player->isAccountManager()) + player->setMounted(status); + player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::EX_ACTIONS_DELAY_INTERVAL) - 10); + return true; +} + +bool Game::playerSay(uint32_t playerId, uint16_t channelId, MessageClasses type, const std::string& receiver, const std::string& text) +{ + Player* player = getPlayerByID(playerId); + if(!player || player->isRemoved()) + return false; + + int32_t muted = 0; + bool mute = player->isMuted(channelId, type, muted); + if(muted && mute) { - player->removeMessageBuffer(); - return internalCreatureSay(player, SPEAK_SAY, text, false); + if(muted > 0) + { + char buffer[75]; + sprintf(buffer, "You are still muted for %d seconds.", muted); + player->sendTextMessage(MSG_STATUS_SMALL, buffer); + } + else + player->sendTextMessage(MSG_STATUS_SMALL, "You are muted permanently."); + + return false; + } + + if(player->isAccountManager()) + { + if(mute) + player->removeMessageBuffer(); + + return internalCreatureSay(player, MSG_SPEAK_SAY, text, false); } - if(g_talkActions->onPlayerSay(player, type == SPEAK_SAY ? CHANNEL_DEFAULT : channelId, text, false)) + if(g_talkActions->onPlayerSay(player, type == MSG_SPEAK_SAY ? (unsigned)CHANNEL_DEFAULT : channelId, text, false)) return true; + ReturnValue ret = RET_NOERROR; if(!muted) { - ReturnValue ret = RET_NOERROR; - if(!muteTime) - { - ret = g_spells->onPlayerSay(player, text); - if(ret == RET_NOERROR || (ret == RET_NEEDEXCHANGE && !g_config.getBool(ConfigManager::BUFFER_SPELL_FAILURE))) - return true; - } - - player->removeMessageBuffer(); - if(ret == RET_NEEDEXCHANGE) + ret = g_spells->onPlayerSay(player, text); + if(ret == RET_NOERROR || (ret == RET_NEEDEXCHANGE && + !g_config.getBool(ConfigManager::BUFFER_SPELL_FAILURE))) return true; } + if(mute) + player->removeMessageBuffer(); + + if(ret == RET_NEEDEXCHANGE) + return true; + + uint32_t statementId = 0; + IOLoginData::getInstance()->playerStatement(player, channelId, text, statementId); switch(type) { - case SPEAK_SAY: - return internalCreatureSay(player, SPEAK_SAY, text, false); - case SPEAK_WHISPER: - return playerWhisper(player, text); - case SPEAK_YELL: - return playerYell(player, text); - case SPEAK_PRIVATE: - case SPEAK_PRIVATE_RED: - case SPEAK_RVR_ANSWER: - return playerSpeakTo(player, type, receiver, text); - case SPEAK_CHANNEL_O: - case SPEAK_CHANNEL_Y: - case SPEAK_CHANNEL_RN: - case SPEAK_CHANNEL_RA: - case SPEAK_CHANNEL_W: - { - if(playerTalkToChannel(player, type, text, channelId)) + case MSG_SPEAK_SAY: + return internalCreatureSay(player, MSG_SPEAK_SAY, text, false, NULL, NULL, statementId); + case MSG_SPEAK_WHISPER: + return playerWhisper(player, text, statementId); + case MSG_SPEAK_YELL: + return playerYell(player, text, statementId); + case MSG_PRIVATE_TO: + case MSG_GAMEMASTER_PRIVATE_TO: + return playerSpeakTo(player, type, receiver, text, statementId); + case MSG_CHANNEL: + case MSG_GAMEMASTER_CHANNEL: + { + if(playerSpeakToChannel(player, type, text, channelId, statementId)) return true; - return playerSay(playerId, 0, SPEAK_SAY, receiver, text); + return internalCreatureSay(player, MSG_SPEAK_SAY, text, false, NULL, NULL, statementId); } - case SPEAK_PRIVATE_PN: + case MSG_NPC_TO: return playerSpeakToNpc(player, text); - case SPEAK_BROADCAST: - return playerBroadcastMessage(player, SPEAK_BROADCAST, text); - case SPEAK_RVR_CHANNEL: - return playerReportRuleViolation(player, text); - case SPEAK_RVR_CONTINUE: - return playerContinueReport(player, text); + case MSG_GAMEMASTER_BROADCAST: + return playerBroadcastMessage(player, MSG_GAMEMASTER_BROADCAST, text, statementId); default: break; @@ -3647,32 +3949,17 @@ bool Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, c return false; } -bool Game::playerWhisper(Player* player, const std::string& text) +bool Game::playerWhisper(Player* player, const std::string& text, uint32_t statementId) { SpectatorVec list; - SpectatorVec::const_iterator it; - getSpectators(list, player->getPosition(), false, false, - Map::maxClientViewportX, Map::maxClientViewportX, - Map::maxClientViewportY, Map::maxClientViewportY); - - //send to client - Player* tmpPlayer = NULL; - for(it = list.begin(); it != list.end(); ++it) - { - if((tmpPlayer = (*it)->getPlayer())) - tmpPlayer->sendCreatureSay(player, SPEAK_WHISPER, text); - } - - //event method - for(it = list.begin(); it != list.end(); ++it) - (*it)->onCreatureSay(player, SPEAK_WHISPER, text); - + getSpectators(list, player->getPosition(), false, false, 1, 1); + internalCreatureSay(player, MSG_SPEAK_WHISPER, text, false, &list, NULL, statementId); return true; } -bool Game::playerYell(Player* player, const std::string& text) +bool Game::playerYell(Player* player, const std::string& text, uint32_t statementId) { - if(player->getLevel() <= 1) + if(player->getLevel() <= 1 && !player->hasFlag(PlayerFlag_CannotBeMuted)) { player->sendTextMessage(MSG_STATUS_SMALL, "You may not yell as long as you are on level 1."); return true; @@ -3690,12 +3977,12 @@ bool Game::playerYell(Player* player, const std::string& text) player->addCondition(condition); } - internalCreatureSay(player, SPEAK_YELL, asUpperCaseString(text), false); + internalCreatureSay(player, MSG_SPEAK_YELL, asUpperCaseString(text), false, NULL, NULL, statementId); return true; } -bool Game::playerSpeakTo(Player* player, SpeakClasses type, const std::string& receiver, - const std::string& text) +bool Game::playerSpeakTo(Player* player, MessageClasses type, const std::string& receiver, + const std::string& text, uint32_t statementId) { Player* toPlayer = getPlayerByName(receiver); if(!toPlayer || toPlayer->isRemoved()) @@ -3718,10 +4005,12 @@ bool Game::playerSpeakTo(Player* player, SpeakClasses type, const std::string& r return false; } - if(type == SPEAK_PRIVATE_RED && !player->hasFlag(PlayerFlag_CanTalkRedPrivate)) - type = SPEAK_PRIVATE; + if(type == MSG_GAMEMASTER_PRIVATE_TO && player->hasFlag(PlayerFlag_CanTalkRedPrivate)) + type = MSG_GAMEMASTER_PRIVATE_FROM; + else + type = MSG_PRIVATE_FROM; - toPlayer->sendCreatureSay(player, type, text); + toPlayer->sendCreatureSay(player, type, text, NULL, statementId); toPlayer->onCreatureSay(player, type, text); if(!canSee) { @@ -3735,35 +4024,40 @@ bool Game::playerSpeakTo(Player* player, SpeakClasses type, const std::string& r return true; } -bool Game::playerTalkToChannel(Player* player, SpeakClasses type, const std::string& text, uint16_t channelId) +bool Game::playerSpeakToChannel(Player* player, MessageClasses type, const std::string& text, uint16_t channelId, uint32_t statementId) { switch(type) { - case SPEAK_CHANNEL_Y: + case MSG_CHANNEL: { - if(channelId == CHANNEL_HELP && player->hasFlag(PlayerFlag_TalkOrangeHelpChannel)) - type = SPEAK_CHANNEL_O; - break; - } + if(channelId == CHANNEL_HELP) + { + if(player->hasFlag(PlayerFlag_TalkOrangeHelpChannel)) + type = MSG_CHANNEL_HIGHLIGHT; + + if(player->hasFlag(PlayerFlag_CanTalkRedChannel)) + type = MSG_GAMEMASTER_CHANNEL; + } - case SPEAK_CHANNEL_O: - { - if(channelId != CHANNEL_HELP || !player->hasFlag(PlayerFlag_TalkOrangeHelpChannel)) - type = SPEAK_CHANNEL_Y; break; } - case SPEAK_CHANNEL_RN: + case MSG_GAMEMASTER_CHANNEL: { - if(!player->hasFlag(PlayerFlag_CanTalkRedChannel)) - type = SPEAK_CHANNEL_Y; - break; + if(player->hasFlag(PlayerFlag_CanTalkRedChannelAnonymous)) + { + if(text.length() < 251) + return g_chat.talk(player, type, text, channelId, statementId, true); + } + else + type = MSG_CHANNEL; } - case SPEAK_CHANNEL_RA: + + case MSG_GAMEMASTER_BROADCAST: { - if(!player->hasFlag(PlayerFlag_CanTalkRedChannelAnonymous)) - type = SPEAK_CHANNEL_Y; + if(!player->hasFlag(PlayerFlag_CanBroadcast)) + type = MSG_CHANNEL; break; } @@ -3771,7 +4065,11 @@ bool Game::playerTalkToChannel(Player* player, SpeakClasses type, const std::str break; } - return g_chat.talkToChannel(player, type, text, channelId); + if(text.length() < 251) + return g_chat.talk(player, type, text, channelId, statementId, false); + + player->sendCancelMessage(RET_NOTPOSSIBLE); + return false; } bool Game::playerSpeakToNpc(Player* player, const std::string& text) @@ -3781,57 +4079,17 @@ bool Game::playerSpeakToNpc(Player* player, const std::string& text) getSpectators(list, player->getPosition()); //send to npcs only - Npc* tmpNpc = NULL; + Npc* tmpNpc; for(it = list.begin(); it != list.end(); ++it) { if((tmpNpc = (*it)->getNpc())) - (*it)->onCreatureSay(player, SPEAK_PRIVATE_PN, text); - } - return true; -} - -bool Game::playerReportRuleViolation(Player* player, const std::string& text) -{ - //Do not allow reports on multiclones worlds since reports are name-based - if(g_config.getNumber(ConfigManager::ALLOW_CLONES)) - { - player->sendTextMessage(MSG_INFO_DESCR, "Rule violation reports are disabled."); - return false; + (*it)->onCreatureSay(player, MSG_NPC_TO, text); } - - cancelRuleViolation(player); - boost::shared_ptr rvr(new RuleViolation(player, text, time(NULL))); - ruleViolations[player->getID()] = rvr; - - ChatChannel* channel = g_chat.getChannelById(CHANNEL_RVR); - if(!channel) - return false; - - for(UsersMap::const_iterator it = channel->getUsers().begin(); it != channel->getUsers().end(); ++it) - it->second->sendToChannel(player, SPEAK_RVR_CHANNEL, text, CHANNEL_RVR, rvr->time); - - return true; -} - -bool Game::playerContinueReport(Player* player, const std::string& text) -{ - RuleViolationsMap::iterator it = ruleViolations.find(player->getID()); - if(it == ruleViolations.end()) - return false; - - RuleViolation& rvr = *it->second; - Player* toPlayer = rvr.gamemaster; - if(!toPlayer) - return false; - - toPlayer->sendCreatureSay(player, SPEAK_RVR_CONTINUE, text); - player->sendTextMessage(MSG_STATUS_SMALL, "Message sent to Gamemaster."); return true; } -//-- bool Game::canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight /*= true*/, - int32_t rangex /*= Map::maxClientViewportX*/, int32_t rangey /*= Map::maxClientViewportY*/) + int32_t rangex/* = Map::maxClientViewportX*/, int32_t rangey/* = Map::maxClientViewportY*/) { return map->canThrowObjectTo(fromPos, toPos, checkLineOfSight, rangex, rangey); } @@ -3873,11 +4131,11 @@ bool Game::internalCreatureTurn(Creature* creature, Direction dir) return true; } -bool Game::internalCreatureSay(Creature* creature, SpeakClasses type, const std::string& text, - bool ghostMode, SpectatorVec* spectators/* = NULL*/, Position* pos/* = NULL*/) +bool Game::internalCreatureSay(Creature* creature, MessageClasses type, const std::string& text, + bool ghostMode, SpectatorVec* spectators/* = NULL*/, Position* pos/* = NULL*/, uint32_t statementId/* = 0*/) { Player* player = creature->getPlayer(); - if(player && player->isAccountManager()) + if(player && player->isAccountManager() && !ghostMode) { player->manageAccount(text); return true; @@ -3895,7 +4153,7 @@ bool Game::internalCreatureSay(Creature* creature, SpeakClasses type, const std: // is used if available and if it can be used, else a local vector is // used (hopefully the compiler will optimize away the construction of // the temporary when it's not used). - if(type != SPEAK_YELL && type != SPEAK_MONSTER_YELL) + if(type != MSG_SPEAK_YELL && type != MSG_SPEAK_MONSTER_YELL) getSpectators(list, destPos, false, false, Map::maxClientViewportX, Map::maxClientViewportX, Map::maxClientViewportY, Map::maxClientViewportY); @@ -3913,7 +4171,7 @@ bool Game::internalCreatureSay(Creature* creature, SpeakClasses type, const std: continue; if(!ghostMode || tmpPlayer->canSeeCreature(creature)) - tmpPlayer->sendCreatureSay(creature, type, text, &destPos); + tmpPlayer->sendCreatureSay(creature, type, text, &destPos, statementId); } //event method @@ -3936,8 +4194,8 @@ bool Game::getPathToEx(const Creature* creature, const Position& targetPos, } bool Game::getPathToEx(const Creature* creature, const Position& targetPos, std::list& dirList, - uint32_t minTargetDist, uint32_t maxTargetDist, bool fullPathSearch /*= true*/, - bool clearSight /*= true*/, int32_t maxSearchDist /*= -1*/) + uint32_t minTargetDist, uint32_t maxTargetDist, bool fullPathSearch/* = true*/, + bool clearSight/* = true*/, int32_t maxSearchDist/* = -1*/) { FindPathParams fpp; fpp.fullPathSearch = fullPathSearch; @@ -3948,21 +4206,42 @@ bool Game::getPathToEx(const Creature* creature, const Position& targetPos, std: return getPathToEx(creature, targetPos, dirList, fpp); } +bool Game::steerCreature(Creature* creature, const Position& position, uint16_t maxNodes/* = 100*/) +{ + FindPathParams fpp; + fpp.maxClosedNodes = maxNodes; + + fpp.fullPathSearch = true; + fpp.maxSearchDist = -1; + fpp.minTargetDist = 0; + fpp.maxTargetDist = 1; + + std::list dirList; + if(!getPathToEx(creature, position, dirList, fpp)) + return false; + + if(Player* player = creature->getPlayer()) + player->setNextWalkTask(NULL); + + creature->startAutoWalk(dirList); + return true; +} + void Game::checkCreatureWalk(uint32_t creatureId) { Creature* creature = getCreatureByID(creatureId); - if(creature && creature->getHealth() > 0) - { - creature->onWalk(); - cleanup(); - } + if(!creature || creature->getHealth() < 1) + return; + + creature->onWalk(); + cleanup(); } void Game::updateCreatureWalk(uint32_t creatureId) { Creature* creature = getCreatureByID(creatureId); if(creature && creature->getHealth() > 0) - creature->getPathToFollowCreature(); + creature->goToFollowCreature(); } void Game::checkCreatureAttack(uint32_t creatureId) @@ -3996,19 +4275,34 @@ void Game::removeCreatureCheck(Creature* creature) void Game::checkCreatures() { - Scheduler::getInstance().addEvent(createSchedulerTask( + checkCreatureEvent = Scheduler::getInstance().addEvent(createSchedulerTask( EVENT_CHECK_CREATURE_INTERVAL, boost::bind(&Game::checkCreatures, this))); - checkCreatureLastIndex++; - if(checkCreatureLastIndex == EVENT_CREATURECOUNT) + if(++checkCreatureLastIndex == EVENT_CREATURECOUNT) checkCreatureLastIndex = 0; std::vector::iterator it; +#ifndef __GROUPED_ATTACKS__ + std::vector creatureVector; + for(uint16_t i = 0; i < EVENT_CREATURECOUNT; ++i) + { + if(i == checkCreatureLastIndex) + continue; + + creatureVector = checkCreatureVectors[i]; + for(it = creatureVector.begin(); it != creatureVector.end(); ++it) + { + if((*it)->checked && (*it)->getHealth() > 0) + (*it)->onAttacking(EVENT_CHECK_CREATURE_INTERVAL); + } + } +#endif + for(it = toAddCheckCreatureVector.begin(); it != toAddCheckCreatureVector.end(); ++it) checkCreatureVectors[(*it)->checkVector].push_back(*it); toAddCheckCreatureVector.clear(); std::vector& checkCreatureVector = checkCreatureVectors[checkCreatureLastIndex]; - for(it = checkCreatureVector.begin(); it != checkCreatureVector.end();) + for(it = checkCreatureVector.begin(); it != checkCreatureVector.end(); ) { if((*it)->checked) { @@ -4028,12 +4322,12 @@ void Game::checkCreatures() cleanup(); } -void Game::changeSpeed(Creature* creature, int32_t varSpeedDelta) +void Game::changeSpeed(Creature* creature, int32_t varSpeed) { - int32_t varSpeed = creature->getSpeed() - creature->getBaseSpeed(); - varSpeed += varSpeedDelta; - creature->setSpeed(varSpeed); + if(!creature) + return; + creature->setSpeed(creature->getSpeed() - creature->getBaseSpeed() + varSpeed); const SpectatorVec& list = getSpectators(creature->getPosition()); SpectatorVec::const_iterator it; @@ -4041,7 +4335,7 @@ void Game::changeSpeed(Creature* creature, int32_t varSpeedDelta) Player* tmpPlayer = NULL; for(it = list.begin(); it != list.end(); ++it) { - if((tmpPlayer = (*it)->getPlayer())) + if((*it) && (tmpPlayer = (*it)->getPlayer())) tmpPlayer->sendChangeSpeed(creature, creature->getStepSpeed()); } } @@ -4106,42 +4400,54 @@ void Game::changeLight(const Creature* creature) Player* tmpPlayer = NULL; for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) { - if((tmpPlayer = (*it)->getPlayer())) + if((tmpPlayer = (*it)->getPlayer()) && !tmpPlayer->hasCustomFlag(PlayerCustomFlag_HasFullLight)) tmpPlayer->sendCreatureLight(creature); } } bool Game::combatBlockHit(CombatType_t combatType, Creature* attacker, Creature* target, - int32_t& healthChange, bool checkDefense, bool checkArmor) + int32_t& healthChange, bool checkDefense, bool checkArmor, bool field/* = false*/, bool element/* = false*/) { if(healthChange > 0) return false; const Position& targetPos = target->getPosition(); const SpectatorVec& list = getSpectators(targetPos); - if(!target->isAttackable() || Combat::canDoCombat(attacker, target) != RET_NOERROR) + if(Combat::canDoCombat(attacker, target, true) != RET_NOERROR) { - addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF, target->isGhost()); + if(!element) + addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF, target->isGhost()); + return true; } int32_t damage = -healthChange; - BlockType_t blockType = target->blockHit(attacker, combatType, damage, checkDefense, checkArmor); + BlockType_t blockType = target->blockHit(attacker, combatType, + damage, checkDefense, checkArmor, !field, field, element); healthChange = -damage; if(blockType == BLOCK_DEFENSE) { - addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF); + if(!element) + addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF); + return true; } - else if(blockType == BLOCK_ARMOR) + + if(blockType == BLOCK_ARMOR) { - addMagicEffect(list, targetPos, MAGIC_EFFECT_BLOCKHIT); + if(!element) + addMagicEffect(list, targetPos, MAGIC_EFFECT_BLOCKHIT); + return true; } - else if(blockType != BLOCK_IMMUNITY) + + if(blockType != BLOCK_IMMUNITY) return false; + if(element) + return true; + MagicEffect_t effect = MAGIC_EFFECT_NONE; switch(combatType) { @@ -4172,7 +4478,17 @@ bool Game::combatBlockHit(CombatType_t combatType, Creature* attacker, Creature* } bool Game::combatChangeHealth(CombatType_t combatType, Creature* attacker, Creature* target, int32_t healthChange, - MagicEffect_t hitEffect/* = MAGIC_EFFECT_UNKNOWN*/, TextColor_t hitColor/* = TEXTCOLOR_UNKNOWN*/, bool force/* = false*/) + MagicEffect_t hitEffect/* = MAGIC_EFFECT_UNKNOWN*/, Color_t hitColor/* = COLOR_UNKNOWN*/, bool force/* = false*/) +{ + CombatParams params; + params.effects.hit = hitEffect; + params.effects.color = hitColor; + + params.combatType = combatType; + return combatChangeHealth(params, attacker, target, healthChange, force); +} + +bool Game::combatChangeHealth(const CombatParams& params, Creature* attacker, Creature* target, int32_t healthChange, bool force) { const Position& targetPos = target->getPosition(); if(healthChange > 0) @@ -4184,63 +4500,105 @@ bool Game::combatChangeHealth(CombatType_t combatType, Creature* attacker, Creat CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE); for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it) { - if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_HEALTHGAIN, combatType, healthChange)) + if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_HEALTHGAIN, params.combatType, healthChange)) deny = true; } if(deny) return false; + int32_t oldHealth = target->getHealth(); target->gainHealth(attacker, healthChange); - if(g_config.getBool(ConfigManager::SHOW_HEALING_DAMAGE) && !target->isGhost() && - (g_config.getBool(ConfigManager::SHOW_HEALING_DAMAGE_MONSTER) || !target->getMonster())) + if(oldHealth != target->getHealth() && g_config.getBool(ConfigManager::SHOW_HEALTH_CHANGE) && !target->isGhost() && + (g_config.getBool(ConfigManager::SHOW_HEALTH_CHANGE_MONSTER) || !target->getMonster())) { - char buffer[20]; - sprintf(buffer, "+%d", healthChange); - const SpectatorVec& list = getSpectators(targetPos); - if(combatType != COMBAT_HEALING) + if(params.combatType != COMBAT_HEALING) addMagicEffect(list, targetPos, MAGIC_EFFECT_WRAPS_BLUE); - addAnimatedText(list, targetPos, TEXTCOLOR_GREEN, buffer); + SpectatorVec textList; + for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) + { + if(!(*it)->getPlayer()) + continue; + + if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z) + textList.push_back(*it); + } + + healthChange = (target->getHealth() - oldHealth); + std::string plural = (healthChange != 1 ? "s." : "."); + + std::stringstream ss; + MessageDetails* details = new MessageDetails(healthChange, COLOR_MAYABLUE); + if(!textList.empty()) + { + if(!attacker) + ss << ucfirst(target->getNameDescription()) << " is healed for " << healthChange << " hitpoint" << plural; + else if(attacker != target) + ss << ucfirst(attacker->getNameDescription()) << " heals " << target->getNameDescription() << " for " << healthChange << " hitpoint" << plural; + else + { + ss << ucfirst(attacker->getNameDescription()) << " heals "; + if(Player* attackerPlayer = attacker->getPlayer()) + ss << (attackerPlayer->getSex(false) == PLAYERSEX_FEMALE ? "herself" : "himself") << " for " << healthChange << " hitpoint" << plural; + else + ss << "itself for " << healthChange << " hitpoint" << plural; + } + + addStatsMessage(textList, MSG_HEALED_OTHERS, ss.str(), targetPos, details); + ss.str(""); + } + + Player* player = NULL; + if(attacker && (player = attacker->getPlayer())) + { + if(attacker != target) + ss << "You healed " << target->getNameDescription() << " for " << healthChange << " hitpoint" << plural; + else + ss << "You healed yourself for " << healthChange << " hitpoint" << plural; + + player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos, details); + ss.str(""); + } + + if((player = target->getPlayer()) && attacker != target) + { + if(attacker) + ss << ucfirst(attacker->getNameDescription()) << " heals you for " << healthChange << " hitpoint" << plural; + else + ss << "You are healed for " << healthChange << " hitpoint" << plural; + + player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos, details); + } + + delete details; } } else { const SpectatorVec& list = getSpectators(targetPos); - if(!target->isAttackable() || Combat::canDoCombat(attacker, target) != RET_NOERROR) + if(target->getHealth() < 1 || Combat::canDoCombat(attacker, target, true) != RET_NOERROR) { addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF); return true; } + int32_t elementDamage = 0; + if(params.element.damage && params.element.type != COMBAT_NONE) + elementDamage = -params.element.damage; + int32_t damage = -healthChange; - if(damage != 0) + if(damage > 0) { - if(target->hasCondition(CONDITION_MANASHIELD) && combatType != COMBAT_UNDEFINEDDAMAGE) + if(target->hasCondition(CONDITION_MANASHIELD) && params.combatType != COMBAT_UNDEFINEDDAMAGE) { - int32_t manaDamage = std::min(target->getMana(), damage); - damage = std::max((int32_t)0, damage - manaDamage); - if(manaDamage != 0) - { - bool deny = false; - CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE); - for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it) - { - if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_MANALOSS, combatType, manaDamage)) - deny = true; - } - - if(deny) - return false; - - target->drainMana(attacker, combatType, manaDamage); - char buffer[20]; - sprintf(buffer, "%d", manaDamage); + int32_t manaDamage = std::min(target->getMana(), damage + elementDamage); + damage = std::max((int32_t)0, damage + elementDamage - manaDamage); + elementDamage = 0; // TODO: I don't know how it works ;( + if(manaDamage && combatChangeMana(attacker, target, -manaDamage, params.combatType, true)) addMagicEffect(list, targetPos, MAGIC_EFFECT_LOSE_ENERGY); - addAnimatedText(list, targetPos, TEXTCOLOR_BLUE, buffer); - } } damage = std::min(target->getHealth(), damage); @@ -4250,137 +4608,136 @@ bool Game::combatChangeHealth(CombatType_t combatType, Creature* attacker, Creat CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE); for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it) { - if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_HEALTHLOSS, combatType, damage)) + if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_HEALTHLOSS, params.combatType, damage)) deny = true; } if(deny) return false; - target->drainHealth(attacker, combatType, damage); - addCreatureHealth(list, target); + target->drainHealth(attacker, params.combatType, damage); + if(elementDamage) + target->drainHealth(attacker, params.element.type, elementDamage); - TextColor_t textColor = TEXTCOLOR_NONE; + Color_t textColor = COLOR_NONE; MagicEffect_t magicEffect = MAGIC_EFFECT_NONE; - switch(combatType) + + addCreatureHealth(list, target); + if(params.combatType == COMBAT_PHYSICALDAMAGE) { - case COMBAT_PHYSICALDAMAGE: + Item* splash = NULL; + switch(target->getRace()) { - Item* splash = NULL; - switch(target->getRace()) - { - case RACE_VENOM: - textColor = TEXTCOLOR_LIGHTGREEN; - magicEffect = MAGIC_EFFECT_POISON; - splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_GREEN); - break; - - case RACE_BLOOD: - textColor = TEXTCOLOR_RED; - magicEffect = MAGIC_EFFECT_DRAW_BLOOD; - splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_BLOOD); - break; - - case RACE_UNDEAD: - textColor = TEXTCOLOR_GREY; - magicEffect = MAGIC_EFFECT_HIT_AREA; - break; - - case RACE_FIRE: - textColor = TEXTCOLOR_ORANGE; - magicEffect = MAGIC_EFFECT_DRAW_BLOOD; - break; - - case RACE_ENERGY: - textColor = TEXTCOLOR_PURPLE; - magicEffect = MAGIC_EFFECT_PURPLEENERGY; - break; - - default: - break; - } - - if(splash) - { - internalAddItem(NULL, target->getTile(), splash, INDEX_WHEREEVER, FLAG_NOLIMIT); - startDecay(splash); - } - break; + case RACE_VENOM: + textColor = COLOR_LIGHTGREEN; + magicEffect = MAGIC_EFFECT_POISON; + splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_GREEN); + break; + + case RACE_BLOOD: + textColor = COLOR_RED; + magicEffect = MAGIC_EFFECT_DRAW_BLOOD; + splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_BLOOD); + break; + + case RACE_UNDEAD: + textColor = COLOR_GREY; + magicEffect = MAGIC_EFFECT_HIT_AREA; + break; + + case RACE_FIRE: + textColor = COLOR_ORANGE; + magicEffect = MAGIC_EFFECT_DRAW_BLOOD; + break; + + case RACE_ENERGY: + textColor = COLOR_PURPLE; + magicEffect = MAGIC_EFFECT_PURPLEENERGY; + break; + + default: + break; } - case COMBAT_ENERGYDAMAGE: + if(splash) { - textColor = TEXTCOLOR_PURPLE; - magicEffect = MAGIC_EFFECT_ENERGY_DAMAGE; - break; + internalAddItem(NULL, target->getTile(), splash, INDEX_WHEREEVER, FLAG_NOLIMIT); + startDecay(splash); } + } + else + getCombatDetails(params.combatType, magicEffect, textColor); - case COMBAT_EARTHDAMAGE: - { - textColor = TEXTCOLOR_LIGHTGREEN; - magicEffect = MAGIC_EFFECT_POISON_RINGS; - break; - } + if(params.effects.hit != MAGIC_EFFECT_UNKNOWN) + magicEffect = params.effects.hit; - case COMBAT_DROWNDAMAGE: - { - textColor = TEXTCOLOR_LIGHTBLUE; - magicEffect = MAGIC_EFFECT_LOSE_ENERGY; - break; - } + if(params.effects.color != COLOR_UNKNOWN) + textColor = params.effects.color; - case COMBAT_FIREDAMAGE: + if(textColor < COLOR_NONE && magicEffect < MAGIC_EFFECT_NONE) + { + addMagicEffect(list, targetPos, magicEffect); + SpectatorVec textList; + for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) { - textColor = TEXTCOLOR_ORANGE; - magicEffect = MAGIC_EFFECT_HITBY_FIRE; - break; - } + if(!(*it)->getPlayer()) + continue; - case COMBAT_ICEDAMAGE: - { - textColor = TEXTCOLOR_TEAL; - magicEffect = MAGIC_EFFECT_ICEATTACK; - break; + if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z) + textList.push_back(*it); } - case COMBAT_HOLYDAMAGE: + MessageDetails* details = new MessageDetails(damage, textColor); + if(elementDamage) { - textColor = TEXTCOLOR_YELLOW; - magicEffect = MAGIC_EFFECT_HOLYDAMAGE; - break; + getCombatDetails(params.element.type, magicEffect, textColor); + details->sub = new MessageDetails(elementDamage, textColor); + addMagicEffect(list, targetPos, magicEffect); } - case COMBAT_DEATHDAMAGE: + std::stringstream ss; + int32_t totalDamage = damage + elementDamage; + + std::string plural = (totalDamage != 1 ? "s" : ""); + if(!textList.empty()) { - textColor = TEXTCOLOR_DARKRED; - magicEffect = MAGIC_EFFECT_SMALLCLOUDS; - break; + if(!attacker) + ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << "."; + else if(attacker != target) + ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << " due to an attack by " << attacker->getNameDescription() << "."; + else + ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << " due to a self attack."; + + addStatsMessage(textList, MSG_DAMAGE_OTHERS, ss.str(), targetPos, details); + ss.str(""); } - case COMBAT_LIFEDRAIN: + Player* player = NULL; + if(attacker && (player = attacker->getPlayer())) { - textColor = TEXTCOLOR_RED; - magicEffect = MAGIC_EFFECT_WRAPS_RED; - break; - } + if(attacker != target) + ss << ucfirst(target->getNameDescription()) << " loses " << totalDamage << " hitpoint" << plural << " due to your attack."; + else + ss << "You lose " << totalDamage << " hitpoint" << plural << " due to your attack."; - default: - break; - } + player->sendStatsMessage(MSG_DAMAGE_DEALT, ss.str(), targetPos, details); + ss.str(""); + } - if(hitEffect != MAGIC_EFFECT_UNKNOWN) - magicEffect = hitEffect; + if((player = target->getPlayer()) && attacker != target) + { + if(attacker) + ss << "You lose " << totalDamage << " hitpoint" << plural << " due to an attack by " << attacker->getNameDescription() << "."; + else + ss << "You lose " << totalDamage << " hitpoint" << plural << "."; - if(hitColor != TEXTCOLOR_UNKNOWN) - textColor = hitColor; + player->sendStatsMessage(MSG_DAMAGE_RECEIVED, ss.str(), targetPos, details); + } - if(textColor < TEXTCOLOR_NONE && magicEffect < MAGIC_EFFECT_NONE) - { - char buffer[20]; - sprintf(buffer, "%d", damage); + if(details->sub) + delete details->sub; - addMagicEffect(list, targetPos, magicEffect); - addAnimatedText(list, targetPos, textColor, buffer); + delete details; } } } @@ -4389,7 +4746,8 @@ bool Game::combatChangeHealth(CombatType_t combatType, Creature* attacker, Creat return true; } -bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange) +bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange, + CombatType_t combatType/* = COMBAT_MANADRAIN*/, bool inherited/* = false*/) { const Position& targetPos = target->getPosition(); if(manaChange > 0) @@ -4406,51 +4764,134 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaCh return false; target->changeMana(manaChange); - if(g_config.getBool(ConfigManager::SHOW_HEALING_DAMAGE) && !target->isGhost() && - (g_config.getBool(ConfigManager::SHOW_HEALING_DAMAGE_MONSTER) || !target->getMonster())) + if(g_config.getBool(ConfigManager::SHOW_MANA_CHANGE) && !target->isGhost() && + (g_config.getBool(ConfigManager::SHOW_MANA_CHANGE_MONSTER) || !target->getMonster())) { - char buffer[20]; - sprintf(buffer, "+%d", manaChange); - const SpectatorVec& list = getSpectators(targetPos); - addAnimatedText(list, targetPos, TEXTCOLOR_DARKPURPLE, buffer); + + SpectatorVec textList; + for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) + { + if(!(*it)->getPlayer()) + continue; + + if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z) + textList.push_back(*it); + } + + std::stringstream ss; + MessageDetails* details = new MessageDetails(manaChange, COLOR_DARKPURPLE); + if(!textList.empty()) + { + if(!attacker) + ss << ucfirst(target->getNameDescription()) << " is regenerated with " << manaChange << " mana."; + else if(attacker != target) + ss << ucfirst(attacker->getNameDescription()) << " regenerates " << target->getNameDescription() << " with " << manaChange << " mana."; + else + ss << ucfirst(attacker->getNameDescription()) << " regenerates himself with " << manaChange << " mana."; + + addStatsMessage(textList, MSG_HEALED_OTHERS, ss.str(), targetPos, details); + ss.str(""); + } + + Player* player = NULL; + if(attacker && (player = attacker->getPlayer())) + { + if(attacker != target) + ss << "You regenerate " << target->getNameDescription() << " with " << manaChange << " mana."; + else + ss << "You regenerate yourself with " << manaChange << " mana."; + + player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos, details); + ss.str(""); + } + + if((player = target->getPlayer()) && attacker != target) + { + if(attacker) + ss << ucfirst(attacker->getNameDescription()) << " regenerates you with " << manaChange << " mana."; + else + ss << "You are regenerated with " << manaChange << " mana."; + + player->sendStatsMessage(MSG_HEALED, ss.str(), targetPos, details); + } + + delete details; } } - else + else if(!inherited && Combat::canDoCombat(attacker, target, true) != RET_NOERROR) { const SpectatorVec& list = getSpectators(targetPos); - if(!target->isAttackable() || Combat::canDoCombat(attacker, target) != RET_NOERROR) - { - addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF); - return false; - } - + addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF); + return false; + } + else + { int32_t manaLoss = std::min(target->getMana(), -manaChange); - BlockType_t blockType = target->blockHit(attacker, COMBAT_MANADRAIN, manaLoss); - if(blockType != BLOCK_NONE) - { - addMagicEffect(list, targetPos, MAGIC_EFFECT_POFF); - return false; - } - if(manaLoss > 0) { bool deny = false; CreatureEventList statsChangeEvents = target->getCreatureEvents(CREATURE_EVENT_STATSCHANGE); for(CreatureEventList::iterator it = statsChangeEvents.begin(); it != statsChangeEvents.end(); ++it) { - if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_MANALOSS, COMBAT_UNDEFINEDDAMAGE, manaChange)) + if(!(*it)->executeStatsChange(target, attacker, STATSCHANGE_MANALOSS, combatType, manaLoss)) deny = true; } if(deny) return false; - target->drainMana(attacker, COMBAT_MANADRAIN, manaLoss); - char buffer[20]; - sprintf(buffer, "%d", manaLoss); + target->drainMana(attacker, combatType, manaLoss); + const SpectatorVec& list = getSpectators(targetPos); + + SpectatorVec textList; + for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) + { + if(!(*it)->getPlayer()) + continue; + + if((*it) != attacker && (*it) != target && (*it)->getPosition().z == target->getPosition().z) + textList.push_back(*it); + } + + std::stringstream ss; + MessageDetails* details = new MessageDetails(manaLoss, COLOR_BLUE); + if(!textList.empty()) + { + if(!attacker) + ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana."; + else if(attacker != target) + ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana due to an attack by " << attacker->getNameDescription(); + else + ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana due to a self attack."; + + addStatsMessage(textList, MSG_DAMAGE_OTHERS, ss.str(), targetPos, details); + ss.str(""); + } + + Player* player; + if(attacker && (player = attacker->getPlayer())) + { + if(attacker != target) + ss << ucfirst(target->getNameDescription()) << " loses " << manaLoss << " mana due to your attack."; + else + ss << "You lose " << manaLoss << " mana due to your attack."; + + player->sendStatsMessage(MSG_DAMAGE_DEALT, ss.str(), targetPos, details); + ss.str(""); + } + + if((player = target->getPlayer()) && attacker != target) + { + if(attacker) + ss << "You lose " << manaLoss << " mana due to an attack by " << attacker->getNameDescription(); + else + ss << "You lose " << manaLoss << " mana."; + + player->sendStatsMessage(MSG_DAMAGE_RECEIVED, ss.str(), targetPos, details); + } - addAnimatedText(list, targetPos, TEXTCOLOR_BLUE, buffer); + delete details; } } @@ -4473,25 +4914,23 @@ void Game::addCreatureHealth(const SpectatorVec& list, const Creature* target) } } -void Game::addAnimatedText(const Position& pos, uint8_t textColor, - const std::string& text) +void Game::addCreatureSquare(const Creature* target, uint8_t squareColor) { - const SpectatorVec& list = getSpectators(pos); - addAnimatedText(list, pos, textColor, text); + const SpectatorVec& list = getSpectators(target->getPosition()); + addCreatureSquare(list, target, squareColor); } -void Game::addAnimatedText(const SpectatorVec& list, const Position& pos, uint8_t textColor, - const std::string& text) +void Game::addCreatureSquare(const SpectatorVec& list, const Creature* target, uint8_t squareColor) { Player* player = NULL; for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) { if((player = (*it)->getPlayer())) - player->sendAnimatedText(pos, textColor, text); + player->sendCreatureSquare(target, squareColor); } } -void Game::addMagicEffect(const Position& pos, uint8_t effect, bool ghostMode /* = false */) +void Game::addMagicEffect(const Position& pos, uint8_t effect, bool ghostMode/* = false*/) { if(ghostMode) return; @@ -4500,7 +4939,8 @@ void Game::addMagicEffect(const Position& pos, uint8_t effect, bool ghostMode /* addMagicEffect(list, pos, effect); } -void Game::addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect, bool ghostMode/* = false*/) +void Game::addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect, + bool ghostMode/* = false*/) { if(ghostMode) return; @@ -4521,7 +4961,8 @@ void Game::addDistanceEffect(const Position& fromPos, const Position& toPos, uin addDistanceEffect(list, fromPos, toPos, effect); } -void Game::addDistanceEffect(const SpectatorVec& list, const Position& fromPos, const Position& toPos, uint8_t effect) +void Game::addDistanceEffect(const SpectatorVec& list, const Position& fromPos, + const Position& toPos, uint8_t effect) { Player* player = NULL; for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) @@ -4531,6 +4972,17 @@ void Game::addDistanceEffect(const SpectatorVec& list, const Position& fromPos, } } +void Game::addStatsMessage(const SpectatorVec& list, MessageClasses mClass, const std::string& message, + const Position& pos, MessageDetails* details/* = NULL*/) +{ + Player* player = NULL; + for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) + { + if((player = (*it)->getPlayer())) + player->sendStatsMessage(mClass, message, pos, details); + } +} + void Game::startDecay(Item* item) { if(!item || !item->canDecay() || item->getDecaying() == DECAYING_TRUE) @@ -4558,13 +5010,13 @@ void Game::internalDecayItem(Item* item) { ReturnValue ret = internalRemoveItem(NULL, item); if(ret != RET_NOERROR) - std::cout << "> DEBUG: internalDecayItem failed, error code: " << (int32_t)ret << ", item id: " << item->getID() << std::endl; + std::clog << "> DEBUG: internalDecayItem failed, error code: " << (int32_t)ret << ", item id: " << item->getID() << std::endl; } } void Game::checkDecay() { - Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, + checkDecayEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, boost::bind(&Game::checkDecay, this))); size_t bucket = (lastBucket + 1) % EVENT_DECAYBUCKETS; @@ -4613,12 +5065,12 @@ void Game::checkDecay() void Game::checkLight() { - Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL, + checkLightEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL, boost::bind(&Game::checkLight, this))); lightHour = lightHour + lightHourDelta; if(lightHour > 1440) - lightHour = lightHour - 1440; + lightHour -= 1440; if(std::abs(lightHour - SUNRISE) < 2 * lightHourDelta) lightState = LIGHT_STATE_SUNRISE; @@ -4663,56 +5115,66 @@ void Game::checkLight() LightInfo lightInfo; getWorldLightInfo(lightInfo); for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) - it->second->sendWorldLight(lightInfo); + { + if(!it->second->hasCustomFlag(PlayerCustomFlag_HasFullLight)) + it->second->sendWorldLight(lightInfo); + } } } -void Game::getWorldLightInfo(LightInfo& lightInfo) +void Game::checkWars() { - lightInfo.level = lightLevel; + IOGuild::getInstance()->checkWars(); + checkWarsEvent = Scheduler::getInstance().addEvent(createSchedulerTask(EVENT_WARSINTERVAL, + boost::bind(&Game::checkWars, this))); +} + +void Game::getWorldLightInfo(LightInfo& lightInfo) +{ + lightInfo.level = lightLevel; lightInfo.color = 0xD7; } -bool Game::cancelRuleViolation(Player* player) +void Game::updateCreatureSkull(Creature* creature) { - RuleViolationsMap::iterator it = ruleViolations.find(player->getID()); - if(it == ruleViolations.end()) - return false; + const SpectatorVec& list = getSpectators(creature->getPosition()); - Player* gamemaster = it->second->gamemaster; - if(!it->second->isOpen && gamemaster) //Send to the responser - gamemaster->sendRuleViolationCancel(player->getName()); - else if(ChatChannel* channel = g_chat.getChannelById(CHANNEL_RVR)) + //send to client + Player* tmpPlayer = NULL; + for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) { - UsersMap tmpMap = channel->getUsers(); - for(UsersMap::iterator tit = tmpMap.begin(); tit != tmpMap.end(); ++tit) - tit->second->sendRemoveReport(player->getName()); + if((tmpPlayer = (*it)->getPlayer())) + tmpPlayer->sendCreatureSkull(creature); } - - //Now erase it - ruleViolations.erase(it); - return true; } -bool Game::closeRuleViolation(Player* player) +void Game::updateCreatureShield(Creature* creature) { - RuleViolationsMap::iterator it = ruleViolations.find(player->getID()); - if(it == ruleViolations.end()) - return false; + const SpectatorVec& list = getSpectators(creature->getPosition()); - ruleViolations.erase(it); - player->sendLockRuleViolation(); - if(ChatChannel* channel = g_chat.getChannelById(CHANNEL_RVR)) + //send to client + Player* tmpPlayer = NULL; + for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) { - UsersMap tmpMap = channel->getUsers(); - for(UsersMap::iterator tit = tmpMap.begin(); tit != tmpMap.end(); ++tit) - tit->second->sendRemoveReport(player->getName()); + if((tmpPlayer = (*it)->getPlayer())) + tmpPlayer->sendCreatureShield(creature); } +} - return true; +void Game::updateCreatureEmblem(Creature* creature) +{ + const SpectatorVec& list = getSpectators(creature->getPosition()); + + //send to client + Player* tmpPlayer = NULL; + for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) + { + if((tmpPlayer = (*it)->getPlayer())) + tmpPlayer->sendCreatureEmblem(creature); + } } -void Game::updateCreatureSkull(Creature* creature) +void Game::updateCreatureWalkthrough(Creature* creature) { const SpectatorVec& list = getSpectators(creature->getPosition()); @@ -4720,8 +5182,8 @@ void Game::updateCreatureSkull(Creature* creature) Player* tmpPlayer = NULL; for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) { - if((tmpPlayer = (*it)->getPlayer())) - tmpPlayer->sendCreatureSkull(creature); + if((tmpPlayer = (*it)->getPlayer())) + tmpPlayer->sendCreatureWalkthrough(creature, (*it)->canWalkthrough(creature)); } } @@ -4739,17 +5201,15 @@ bool Game::playerInviteToParty(uint32_t playerId, uint32_t invitedId) { char buffer[90]; sprintf(buffer, "%s is already in a party.", invitedPlayer->getName().c_str()); - player->sendTextMessage(MSG_INFO_DESCR, buffer); + player->sendTextMessage(MSG_PARTY, buffer); return false; } Party* party = player->getParty(); if(!party) party = new Party(player); - else if(party->getLeader() != player) - return false; - return party->invitePlayer(invitedPlayer); + return party->getLeader() == player && party->invitePlayer(invitedPlayer); } bool Game::playerJoinParty(uint32_t playerId, uint32_t leaderId) @@ -4765,7 +5225,7 @@ bool Game::playerJoinParty(uint32_t playerId, uint32_t leaderId) if(!player->getParty()) return leader->getParty()->join(player); - player->sendTextMessage(MSG_INFO_DESCR, "You are already in a party."); + player->sendTextMessage(MSG_PARTY, "You are already in a party."); return false; } @@ -4790,25 +5250,22 @@ bool Game::playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId) return false; Player* newLeader = getPlayerByID(newLeaderId); - if(!newLeader || newLeader->isRemoved() || !player->isPartner(newLeader)) + if(!newLeader || newLeader->isRemoved() || !newLeader->getParty() || newLeader->getParty() != player->getParty()) return false; return player->getParty()->passLeadership(newLeader); } -bool Game::playerLeaveParty(uint32_t playerId) +bool Game::playerLeaveParty(uint32_t playerId, bool forced/* = false*/) { Player* player = getPlayerByID(playerId); - if(!player || player->isRemoved()) - return false; - - if(!player->getParty() || player->hasCondition(CONDITION_INFIGHT)) + if(!player || player->isRemoved() || !player->getParty() || (player->hasCondition(CONDITION_INFIGHT) && !forced)) return false; return player->getParty()->leave(player); } -bool Game::playerSharePartyExperience(uint32_t playerId, bool activate, uint8_t unknown) +bool Game::playerSharePartyExperience(uint32_t playerId, bool activate) { Player* player = getPlayerByID(playerId); if(!player || player->isRemoved()) @@ -4837,331 +5294,30 @@ bool Game::playerReportBug(uint32_t playerId, std::string comment) return true; } -bool Game::playerViolationWindow(uint32_t playerId, std::string name, uint8_t reason, ViolationAction_t action, - std::string comment, std::string statement, uint32_t statementId, bool ipBanishment) +bool Game::playerReportViolation(uint32_t playerId, ReportType_t type, uint8_t reason, const std::string& name, + const std::string& comment, const std::string& translation, uint32_t statementId) { Player* player = getPlayerByID(playerId); if(!player || player->isRemoved()) return false; - Group* group = player->getGroup(); - if(!group) - return false; - - time_t length[3] = {0, 0, 0}; - int32_t pos = 0, start = comment.find("{"); - while((start = comment.find("{")) > 0 && pos < 4) - { - std::string::size_type end = comment.find("}", start); - if(end == std::string::npos) - break; - - std::string data = comment.substr(start + 1, end - 1); - comment = comment.substr(end + 1); - - ++pos; - if(data.empty()) - continue; - - if(data == "delete") - { - action = ACTION_DELETION; - continue; - } - - time_t banTime = time(NULL); - StringVec vec = explodeString(";", data); - for(StringVec::iterator it = vec.begin(); it != vec.end(); ++it) - { - StringVec tmp = explodeString(",", *it); - uint32_t count = 1; - if(tmp.size() > 1) - { - count = atoi(tmp[1].c_str()); - if(!count) - count = 1; - } - - if(tmp[0][0] == 's') - banTime += count; - if(tmp[0][0] == 'm') - banTime += count * 60; - if(tmp[0][0] == 'h') - banTime += count * 3600; - if(tmp[0][0] == 'd') - banTime += count * 86400; - if(tmp[0][0] == 'w') - banTime += count * 604800; - if(tmp[0][0] == 'm') - banTime += count * 2592000; - if(tmp[0][0] == 'y') - banTime += count * 31536000; - } - - if(action == ACTION_DELETION) - length[pos - 2] = banTime; - else - length[pos - 1] = banTime; - } - - int16_t nameFlags = group->getNameViolationFlags(), statementFlags = group->getStatementViolationFlags(); - if((ipBanishment && ((nameFlags & IPBAN_FLAG) != IPBAN_FLAG || (statementFlags & IPBAN_FLAG) != IPBAN_FLAG)) || - !(nameFlags & (1 << action) || statementFlags & (1 << action)) || reason > group->getViolationReasons()) - { - player->sendCancel("You do not have authorization for this action."); - return false; - } - - uint32_t commentSize = g_config.getNumber(ConfigManager::MAX_VIOLATIONCOMMENT_SIZE); - if(comment.size() > commentSize) - { - char buffer[90]; - sprintf(buffer, "The comment may not exceed limit of %d characters.", commentSize); - - player->sendCancel(buffer); - return false; - } + CreatureEventList reportViolationEvents = player->getCreatureEvents(CREATURE_EVENT_REPORTVIOLATION); + for(CreatureEventList::iterator it = reportViolationEvents.begin(); it != reportViolationEvents.end(); ++it) + (*it)->executeReportViolation(player, type, reason, name, comment, translation, statementId); - toLowerCaseString(name); - Player* target = getPlayerByNameEx(name); - if(!target || name == "account manager") - { - player->sendCancel("A player with this name does not exist."); - return false; - } + return true; +} - if(target->hasFlag(PlayerFlag_CannotBeBanned)) - { - player->sendCancel("You do not have authorization for this action."); +bool Game::playerThankYou(uint32_t playerId, uint32_t statementId) +{ + Player* player = getPlayerByID(playerId); + if(!player || player->isRemoved()) return false; - } - - Account account = IOLoginData::getInstance()->loadAccount(target->getAccount(), true); - enum KickAction { - NONE = 1, - KICK = 2, - FULL_KICK = 3, - } kickAction = FULL_KICK; - - pos = 1; - switch(action) - { - case ACTION_STATEMENT: - { - StatementMap::iterator it = g_chat.statementMap.find(statementId); - if(it == g_chat.statementMap.end()) - { - player->sendCancel("Statement has been already reported."); - return false; - } - - IOBan::getInstance()->addStatement(target->getGUID(), reason, comment, - player->getGUID(), -1, statement); - g_chat.statementMap.erase(it); - - kickAction = NONE; - break; - } - - case ACTION_NAMEREPORT: - { - int64_t banTime = -1; - PlayerBan_t tmp = (PlayerBan_t)g_config.getNumber(ConfigManager::NAME_REPORT_TYPE); - if(tmp == PLAYERBAN_BANISHMENT) - { - if(!length[0]) - banTime = time(NULL) + g_config.getNumber(ConfigManager::BAN_LENGTH); - else - banTime = length[0]; - } - - if(!IOBan::getInstance()->addPlayerBanishment(target->getGUID(), banTime, reason, action, - comment, player->getGUID(), tmp)) - { - player->sendCancel("Player has been already reported."); - return false; - } - else if(tmp == PLAYERBAN_BANISHMENT) - account.warnings++; - - kickAction = (KickAction)tmp; - break; - } - - case ACTION_NOTATION: - { - if(!IOBan::getInstance()->addNotation(account.number, reason, - comment, player->getGUID(), target->getGUID())) - { - player->sendCancel("Unable to perform action."); - return false; - } - - if(IOBan::getInstance()->getNotationsCount(account.number) < (uint32_t) - g_config.getNumber(ConfigManager::NOTATIONS_TO_BAN)) - { - kickAction = NONE; - break; - } - - action = ACTION_BANISHMENT; - } - - case ACTION_BANISHMENT: - case ACTION_BANREPORT: - { - bool deny = action != ACTION_BANREPORT; - int64_t banTime = -1; - pos = 2; - - account.warnings++; - if(account.warnings >= g_config.getNumber(ConfigManager::WARNINGS_TO_DELETION)) - action = ACTION_DELETION; - else if(length[0]) - banTime = length[0]; - else if(account.warnings >= g_config.getNumber(ConfigManager::WARNINGS_TO_FINALBAN)) - banTime = time(NULL) + g_config.getNumber(ConfigManager::FINALBAN_LENGTH); - else - banTime = time(NULL) + g_config.getNumber(ConfigManager::BAN_LENGTH); - - if(!IOBan::getInstance()->addAccountBanishment(account.number, banTime, reason, action, - comment, player->getGUID(), target->getGUID())) - { - account.warnings--; - player->sendCancel("Account is already banned."); - return false; - } - - if(deny) - break; - - banTime = -1; - PlayerBan_t tmp = (PlayerBan_t)g_config.getNumber(ConfigManager::NAME_REPORT_TYPE); - if(tmp == PLAYERBAN_BANISHMENT) - { - if(!length[1]) - banTime = time(NULL) + g_config.getNumber(ConfigManager::FINALBAN_LENGTH); - else - banTime = length[1]; - } - IOBan::getInstance()->addPlayerBanishment(target->getGUID(), banTime, reason, action, comment, - player->getGUID(), tmp); - break; - } - - case ACTION_BANFINAL: - case ACTION_BANREPORTFINAL: - { - bool allow = action == ACTION_BANREPORTFINAL; - int64_t banTime = -1; - - account.warnings++; - if(account.warnings >= g_config.getNumber(ConfigManager::WARNINGS_TO_DELETION)) - action = ACTION_DELETION; - else if(length[0]) - banTime = length[0]; - else - banTime = time(NULL) + g_config.getNumber(ConfigManager::FINALBAN_LENGTH); - - if(!IOBan::getInstance()->addAccountBanishment(account.number, banTime, reason, action, - comment, player->getGUID(), target->getGUID())) - { - account.warnings--; - player->sendCancel("Account is already banned."); - return false; - } - - if(action != ACTION_DELETION) - account.warnings += (g_config.getNumber(ConfigManager::WARNINGS_TO_FINALBAN) - 1); - - if(allow) - IOBan::getInstance()->addPlayerBanishment(target->getGUID(), -1, reason, action, comment, - player->getGUID(), (PlayerBan_t)g_config.getNumber( - ConfigManager::NAME_REPORT_TYPE)); - - break; - } - - case ACTION_DELETION: - { - //completely internal - account.warnings++; - if(!IOBan::getInstance()->addAccountBanishment(account.number, -1, reason, ACTION_DELETION, - comment, player->getGUID(), target->getGUID())) - { - account.warnings--; - player->sendCancel("Account is currently banned or already deleted."); - return false; - } - - break; - } - - default: - // these just shouldn't occur in rvw - return false; - } - - if(ipBanishment && target->getIP()) - { - if(!length[pos]) - length[pos] = time(NULL) + g_config.getNumber(ConfigManager::IPBANISHMENT_LENGTH); - - IOBan::getInstance()->addIpBanishment(target->getIP(), length[pos], reason, comment, player->getGUID(), 0xFFFFFFFF); - } - - if(kickAction == FULL_KICK) - IOBan::getInstance()->removeNotations(account.number); + CreatureEventList thankYouEvents = player->getCreatureEvents(CREATURE_EVENT_THANKYOU); + for(CreatureEventList::iterator it = thankYouEvents.begin(); it != thankYouEvents.end(); ++it) + (*it)->executeThankYou(player, statementId); - std::stringstream ss; - if(g_config.getBool(ConfigManager::BROADCAST_BANISHMENTS)) - ss << player->getName() << " has"; - else - ss << "You have"; - - ss << " taken the action \"" << getAction(action, ipBanishment) << "\""; - switch(action) - { - case ACTION_NOTATION: - { - ss << " (" << (g_config.getNumber(ConfigManager::NOTATIONS_TO_BAN) - IOBan::getInstance()->getNotationsCount( - account.number)) << " left to banishment)"; - break; - } - case ACTION_STATEMENT: - { - ss << " for the statement: \"" << statement << "\""; - break; - } - default: - break; - } - - ss << " against: " << name << " (Warnings: " << account.warnings << "), with reason: \"" << getReason( - reason) << "\", and comment: \"" << comment << "\"."; - if(g_config.getBool(ConfigManager::BROADCAST_BANISHMENTS)) - broadcastMessage(ss.str(), MSG_STATUS_WARNING); - else - player->sendTextMessage(MSG_STATUS_CONSOLE_RED, ss.str()); - - if(target->isVirtual()) - { - delete target; - target = NULL; - } - else if(kickAction > NONE) - { - char buffer[30]; - sprintf(buffer, "You have been %s.", (kickAction > KICK ? "banished" : "namelocked")); - target->sendTextMessage(MSG_INFO_DESCR, buffer); - - addMagicEffect(target->getPosition(), MAGIC_EFFECT_WRAPS_GREEN); - Scheduler::getInstance().addEvent(createSchedulerTask(1000, boost::bind( - &Game::kickPlayer, this, target->getID(), false))); - } - - IOLoginData::getInstance()->saveAccount(account); return true; } @@ -5171,15 +5327,12 @@ void Game::kickPlayer(uint32_t playerId, bool displayEffect) if(!player || player->isRemoved()) return; - player->kickPlayer(displayEffect, true); + player->kick(displayEffect, true); } bool Game::broadcastMessage(const std::string& text, MessageClasses type) { - if(type < MSG_CLASS_FIRST || type > MSG_CLASS_LAST) - return false; - - std::cout << "> Broadcasted message: \"" << text << "\"." << std::endl; + std::clog << "> Broadcasted message: \"" << text << "\"." << std::endl; for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) it->second->sendTextMessage(type, text); @@ -5217,8 +5370,8 @@ Position Game::getClosestFreeTile(Creature* creature, Position pos, bool extende continue; ReturnValue ret = tile->__queryAdd(0, player, 1, FLAG_IGNOREBLOCKITEM); - if(ret == RET_NOTENOUGHROOM || (ret == RET_NOTPOSSIBLE && !player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere)) - || (ret == RET_PLAYERISNOTINVITED && !ignoreHouse && !player->hasFlag(PlayerFlag_CanEditHouses))) + if(ret == RET_NOTENOUGHROOM || (ret == RET_NOTPOSSIBLE && !player->hasCustomFlag( + PlayerCustomFlag_CanMoveAnywhere)) || (ret == RET_PLAYERISNOTINVITED && !ignoreHouse)) continue; return tile->getPosition(); @@ -5238,7 +5391,7 @@ Position Game::getClosestFreeTile(Creature* creature, Position pos, bool extende return Position(0, 0, 0); } -std::string Game::getSearchString(const Position fromPos, const Position toPos, bool fromIsCreature/* = false*/, bool toIsCreature/* = false*/) +std::string Game::getSearchString(const Position& fromPos, const Position& toPos, bool fromIsCreature/* = false*/, bool toIsCreature/* = false*/) { /* * When the position is on same level and 0 to 4 squares away, they are "[toIsCreature: standing] next to you" @@ -5250,6 +5403,12 @@ std::string Game::getSearchString(const Position fromPos, const Position toPos, * When the position is on a lower or higher level and 0 to 4 squares away they are "below (or) above you." */ + enum direction_t + { + DIR_N, DIR_S, DIR_E, DIR_W, + DIR_NE, DIR_NW, DIR_SE, DIR_SW + }; + enum distance_t { DISTANCE_BESIDE, @@ -5258,12 +5417,6 @@ std::string Game::getSearchString(const Position fromPos, const Position toPos, DISTANCE_VERYFAR }; - enum direction_t - { - DIR_N, DIR_S, DIR_E, DIR_W, - DIR_NE, DIR_NW, DIR_SE, DIR_SW - }; - enum level_t { LEVEL_HIGHER, @@ -5271,8 +5424,8 @@ std::string Game::getSearchString(const Position fromPos, const Position toPos, LEVEL_SAME }; - distance_t distance; direction_t direction; + distance_t distance; level_t level; int32_t dx = fromPos.x - toPos.x, dy = fromPos.y - toPos.y, dz = fromPos.z - toPos.z; @@ -5464,50 +5617,28 @@ double Game::getExperienceStage(uint32_t level, double divider/* = 1.*/) return stages[level] * divider; } -bool Game::fetchBlacklist() -{ - xmlDocPtr doc = xmlParseFile("http://forgottenserver.otland.net/blacklist.xml"); - if(!doc) - return false; - - xmlNodePtr p, root = xmlDocGetRootElement(doc); - if(!xmlStrcmp(root->name, (const xmlChar*)"blacklist")) - { - p = root->children; - while(p) - { - if(!xmlStrcmp(p->name, (const xmlChar*)"entry")) - { - std::string ip; - if(readXMLString(p, "ip", ip)) - blacklist.push_back(ip); - } - - p = p->next; - } - } - - xmlFreeDoc(doc); - return true; -} - bool Game::loadExperienceStages() { if(!g_config.getBool(ConfigManager::EXPERIENCE_STAGES)) + { + if(!stages.empty()) + stages.clear(); + return true; + } xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "stages.xml").c_str()); if(!doc) { - std::cout << "[Warning - Game::loadExperienceStages] Cannot load stages file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Game::loadExperienceStages] Cannot load stages file." + << std::endl << getLastXMLError() << std::endl; return false; } xmlNodePtr q, p, root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name, (const xmlChar*)"stages")) { - std::cout << "[Error - Game::loadExperienceStages] Malformed stages file" << std::endl; + std::clog << "[Error - Game::loadExperienceStages] Malformed stages file" << std::endl; xmlFreeDoc(doc); return false; } @@ -5563,7 +5694,7 @@ bool Game::loadExperienceStages() stages[lastStageLevel] = mul; else { - for(int32_t i = low; i <= high; i++) + for(int32_t i = low; i <= high; ++i) stages[i] = mul; } } @@ -5593,7 +5724,7 @@ bool Game::loadExperienceStages() stages[lastStageLevel] = mul; else { - for(int32_t i = low; i <= high; i++) + for(int32_t i = low; i <= high; ++i) stages[i] = mul; } } @@ -5629,7 +5760,7 @@ std::string Game::getHighscoreString(uint16_t skill) Highscore hs = highscoreStorage[skill]; std::stringstream ss; ss << "Highscore for " << getSkillName(skill) << "\n\nRank Level - Player Name"; - for(uint32_t i = 0; i < hs.size(); i++) + for(uint32_t i = 0; i < hs.size(); ++i) ss << "\n" << (i + 1) << ". " << hs[i].second << " - " << hs[i].first; ss << "\n\nLast updated on:\n" << std::ctime(&lastHighscoreCheck); @@ -5638,12 +5769,11 @@ std::string Game::getHighscoreString(uint16_t skill) Highscore Game::getHighscore(uint16_t skill) { - Highscore hs; - Database* db = Database::getInstance(); DBResult* result; - DBQuery query; + + Highscore hs; if(skill >= SKILL__MAGLEVEL) { if(skill == SKILL__MAGLEVEL) @@ -5690,18 +5820,21 @@ Highscore Game::getHighscore(uint16_t skill) int32_t Game::getMotdId() { - if(lastMotd == g_config.getString(ConfigManager::MOTD)) - return lastMotdId; + if(lastMotd.length() == g_config.getString(ConfigManager::MOTD).length()) + { + if(lastMotd == g_config.getString(ConfigManager::MOTD)) + return lastMotdId; + } lastMotd = g_config.getString(ConfigManager::MOTD); Database* db = Database::getInstance(); DBQuery query; - query << "INSERT INTO `server_motd` (`id`, `world_id`, `text`) VALUES (" << ++lastMotdId << ", " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " << db->escapeString(lastMotd) << ")"; - if(db->executeQuery(query.str())) - return lastMotdId; + query << "INSERT INTO `server_motd` (`id`, `world_id`, `text`) VALUES (" << lastMotdId + 1 << ", " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " << db->escapeString(lastMotd) << ")"; + if(db->query(query.str())) + ++lastMotdId; - return --lastMotdId; + return lastMotdId; } void Game::loadMotd() @@ -5713,7 +5846,7 @@ void Game::loadMotd() DBResult* result; if(!(result = db->storeQuery(query.str()))) { - std::cout << "> ERROR: Failed to load motd!" << std::endl; + std::clog << "> ERROR: Failed to load motd!" << std::endl; lastMotdId = random_range(5, 500); return; } @@ -5729,7 +5862,7 @@ void Game::checkPlayersRecord(Player* player) if(count <= playersRecord) return; - GlobalEventMap recordEvents = g_globalEvents->getEventMap(GLOBAL_EVENT_RECORD); + GlobalEventMap recordEvents = g_globalEvents->getEventMap(GLOBALEVENT_RECORD); for(GlobalEventMap::iterator it = recordEvents.begin(); it != recordEvents.end(); ++it) it->second->executeRecord(count, playersRecord, player); @@ -5745,7 +5878,7 @@ void Game::loadPlayersRecord() DBResult* result; if(!(result = db->storeQuery(query.str()))) { - std::cout << "> ERROR: Failed to load players record!" << std::endl; + std::clog << "> ERROR: Failed to load players record!" << std::endl; return; } @@ -5753,7 +5886,7 @@ void Game::loadPlayersRecord() result->free(); } -bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) +bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/, bool completeReload/* = false*/) { bool done = false; switch(reload) @@ -5763,7 +5896,7 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(g_actions->reload()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload actions." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload actions." << std::endl; break; } @@ -5773,7 +5906,7 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(g_chat.reload()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload chat." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload chat." << std::endl; break; } @@ -5783,7 +5916,7 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(g_config.reload()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload config." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload config." << std::endl; break; } @@ -5793,7 +5926,7 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(g_creatureEvents->reload()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload creature events." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload creature events." << std::endl; break; } @@ -5804,7 +5937,7 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(GameServers::getInstance()->reload()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload game servers." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload game servers." << std::endl; #endif break; @@ -5815,17 +5948,18 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(g_globalEvents->reload()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload global events." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload global events." << std::endl; break; } case RELOAD_GROUPS: { - //if(Groups::getInstance()->reload()) + //TODO- this way needs a secure 'back', so players won't stay with empty pointers to new groups + /*if(Groups::getInstance()->reload()) done = true; - //else - // std::cout << "[Error - Game::reloadInfo] Failed to reload groups." << std::endl; + else + std::clog << "[Error - Game::reloadInfo] Failed to reload groups." << std::endl;*/ break; } @@ -5835,36 +5969,25 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(reloadHighscores()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload highscores." << std::endl; - - break; - } - - case RELOAD_HOUSEPRICES: - { - if(Houses::getInstance()->reloadPrices()) - done = true; - else - std::cout << "[Error - Game::reloadInfo] Failed to reload house prices." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload highscores." << std::endl; break; } case RELOAD_ITEMS: { - //TODO - std::cout << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl; + //TODO? + std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl; done = true; break; } case RELOAD_MODS: { - std::cout << ">> Reloading mods..." << std::endl; - if(ScriptingManager::getInstance()->reloadMods()) + if(ScriptManager::getInstance()->reloadMods()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload mods." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload mods." << std::endl; break; } @@ -5874,7 +5997,18 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(g_monsters.reload()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload monsters." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload monsters." << std::endl; + + break; + } + + case RELOAD_MOUNTS: + { + //TODO? + if(Mounts::getInstance()->reload()) + done = true; + else + std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl; break; } @@ -5884,7 +6018,7 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(g_moveEvents->reload()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload move events." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload move events." << std::endl; break; } @@ -5899,7 +6033,7 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) case RELOAD_OUTFITS: { //TODO - std::cout << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl; + std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl; done = true; break; } @@ -5909,7 +6043,7 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(Quests::getInstance()->reload()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload quests." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload quests." << std::endl; break; } @@ -5917,9 +6051,9 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) case RELOAD_RAIDS: { if(!Raids::getInstance()->reload()) - std::cout << "[Error - Game::reloadInfo] Failed to reload raids." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload raids." << std::endl; else if(!Raids::getInstance()->startup()) - std::cout << "[Error - Game::reloadInfo] Failed to startup raids when reloading." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to startup raids when reloading." << std::endl; else done = true; @@ -5929,9 +6063,9 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) case RELOAD_SPELLS: { if(!g_spells->reload()) - std::cout << "[Error - Game::reloadInfo] Failed to reload spells." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload spells." << std::endl; else if(!g_monsters.reload()) - std::cout << "[Error - Game::reloadInfo] Failed to reload monsters when reloading spells." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload monsters when reloading spells." << std::endl; else done = true; @@ -5943,7 +6077,7 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(loadExperienceStages()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload stages." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload stages." << std::endl; break; } @@ -5953,17 +6087,18 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) if(g_talkActions->reload()) done = true; else - std::cout << "[Error - Game::reloadInfo] Failed to reload talk actions." << std::endl; + std::clog << "[Error - Game::reloadInfo] Failed to reload talk actions." << std::endl; break; } case RELOAD_VOCATIONS: { - //if(Vocations::getInstance()->reload()) + //TODO- this way needs a secure 'back', so players won't stay with empty pointers to new groups + /*if(Vocations::getInstance()->reload()) done = true; - //else - // std::cout << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl; + else + std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl;*/ break; } @@ -5971,7 +6106,7 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) case RELOAD_WEAPONS: { //TODO - std::cout << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl; + std::clog << "[Notice - Game::reloadInfo] Reload type does not work." << std::endl; done = true; break; } @@ -5979,22 +6114,28 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) case RELOAD_ALL: { done = true; - for(uint8_t i = RELOAD_FIRST; i <= RELOAD_LAST; i++) + for(int32_t i = RELOAD_FIRST; i <= RELOAD_LAST; ++i) { - if(!reloadInfo((ReloadInfo_t)i) && done) + if(!reloadInfo((ReloadInfo_t)i, 0, true) && done) done = false; } + if(!ScriptManager::getInstance()->reloadMods() && done) + done = false; + break; } default: { - std::cout << "[Warning - Game::reloadInfo] Reload type not found." << std::endl; + std::clog << "[Warning - Game::reloadInfo] Reload type not found." << std::endl; break; } } + if(reload != RELOAD_CONFIG && reload != RELOAD_MODS && !completeReload && !ScriptManager::getInstance()->reloadMods()) + std::clog << "[Error - Game::reloadInfo] Failed to reload mods." << std::endl; + if(!playerId) return done; @@ -6008,93 +6149,94 @@ bool Game::reloadInfo(ReloadInfo_t reload, uint32_t playerId/* = 0*/) return true; } - player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Failed to reload."); + if(reload == RELOAD_ALL) + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Failed to reload some parts."); + else + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Failed to reload."); + return false; } -void Game::prepareGlobalSave() +void Game::prepareGlobalSave(uint8_t minutes) { - if(!globalSaveMessage[0]) + std::clog << "Game::prepareGlobalSave in " << (uint32_t)minutes << " minutes" << std::endl; + switch(minutes) { - setGameState(GAME_STATE_CLOSING); - globalSaveMessage[0] = true; + case 5: + setGameState(GAMESTATE_CLOSING); + broadcastMessage("Server is going down for a global save within 5 minutes. Please logout.", MSG_STATUS_WARNING); + Scheduler::getInstance().addEvent(createSchedulerTask(2 * 60000, boost::bind(&Game::prepareGlobalSave, this, 3))); + break; - broadcastMessage("Server is going down for a global save within 5 minutes. Please logout.", MSG_STATUS_WARNING); - Scheduler::getInstance().addEvent(createSchedulerTask(120000, boost::bind(&Game::prepareGlobalSave, this))); - } - else if(!globalSaveMessage[1]) - { - globalSaveMessage[1] = true; - broadcastMessage("Server is going down for a global save within 3 minutes. Please logout.", MSG_STATUS_WARNING); - Scheduler::getInstance().addEvent(createSchedulerTask(120000, boost::bind(&Game::prepareGlobalSave, this))); - } - else if(!globalSaveMessage[2]) - { - globalSaveMessage[2] = true; - broadcastMessage("Server is going down for a global save in one minute, please logout!", MSG_STATUS_WARNING); - Scheduler::getInstance().addEvent(createSchedulerTask(60000, boost::bind(&Game::prepareGlobalSave, this))); + case 3: + broadcastMessage("Server is going down for a global save within 3 minutes. Please logout.", MSG_STATUS_WARNING); + Scheduler::getInstance().addEvent(createSchedulerTask(2 * 60000, boost::bind(&Game::prepareGlobalSave, this, 1))); + break; + + case 1: + broadcastMessage("Server is going down for a global save in one minute, please logout!", MSG_STATUS_WARNING); + Scheduler::getInstance().addEvent(createSchedulerTask(60000, boost::bind(&Game::prepareGlobalSave, this, 0))); + break; + + case 0: + globalSave(); + break; + + default: + if(minutes > 5) + Scheduler::getInstance().addEvent(createSchedulerTask((minutes - 5) * 1000, boost::bind(&Game::prepareGlobalSave, this, 5))); + break; } - else - globalSave(); } void Game::globalSave() { - if(g_config.getBool(ConfigManager::SHUTDOWN_AT_GLOBALSAVE)) + bool close = g_config.getBool(ConfigManager::SHUTDOWN_AT_GLOBALSAVE); + if(!close) // check are we're going to close the server + Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAMESTATE_CLOSED))); + + // call the global event + g_globalEvents->execute(GLOBALEVENT_GLOBALSAVE); + if(close) { //shutdown server - Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAME_STATE_SHUTDOWN))); + Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAMESTATE_SHUTDOWN))); return; } - //close server - Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAME_STATE_CLOSED))); + //pay houses + Houses::getInstance()->check(); //clean map if configured to if(g_config.getBool(ConfigManager::CLEAN_MAP_AT_GLOBALSAVE)) - { - uint32_t dummy; - cleanMap(dummy); - } + cleanMap(); - //pay houses - Houses::getInstance()->payHouses(); - //clear temporial and expired bans - IOBan::getInstance()->clearTemporials(); //remove premium days globally if configured to - if(g_config.getBool(ConfigManager::REMOVE_PREMIUM_ON_INIT)) + if(g_config.getBool(ConfigManager::INIT_PREMIUM_UPDATE)) IOLoginData::getInstance()->updatePremiumDays(); //reload everything reloadInfo(RELOAD_ALL); - //reset variables - for(int16_t i = 0; i < 3; i++) - setGlobalSaveMessage(i, false); - //prepare for next global save after 24 hours - Scheduler::getInstance().addEvent(createSchedulerTask(86100000, boost::bind(&Game::prepareGlobalSave, this))); + Scheduler::getInstance().addEvent(createSchedulerTask(((24 * 60 * 60) - (5 * 60)) * 1000, boost::bind(&Game::prepareGlobalSave, this, 5))); //open server - Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAME_STATE_NORMAL))); + Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, this, GAMESTATE_NORMAL))); } void Game::shutdown() { - std::cout << "Preparing"; + std::clog << "Preparing"; Scheduler::getInstance().shutdown(); - std::cout << " to"; + std::clog << " to"; Dispatcher::getInstance().shutdown(); - std::cout << " shutdown"; + std::clog << " shutdown"; Spawns::getInstance()->clear(); - std::cout << " the"; + std::clog << " the"; Raids::getInstance()->clear(); - std::cout << " server"; + std::clog << " server"; cleanup(); - std::cout << "- done." << std::endl; + std::clog << "- done." << std::endl; if(services) services->stop(); -#if defined(WINDOWS) && !defined(__CONSOLE__) - - exit(1); -#endif } void Game::cleanup() @@ -6123,18 +6265,625 @@ void Game::freeThing(Thing* thing) void Game::showHotkeyUseMessage(Player* player, Item* item) { - int32_t subType = -1; - if(item->hasSubType() && !item->hasCharges()) - subType = item->getSubType(); - const ItemType& it = Item::items[item->getID()]; - uint32_t count = player->__getItemTypeCount(item->getID(), subType, false); + uint32_t count = player->__getItemTypeCount(item->getID(), item->isFluidContainer() ? item->getFluidType() : -1); + + std::stringstream stream; + if(!it.showCount) + stream << "Using one of " << it.name << "..."; + else if(count == 1) + stream << "Using the last " << it.name.c_str() << "..."; + else + stream << "Using one of " << count << " " << it.pluralName.c_str() << "..."; + + player->sendTextMessage(MSG_HOTKEY_USE, stream.str().c_str()); +} + +bool Game::loadStatuslist() +{ + xmlDocPtr doc = xmlParseFile("http://forgottenserver.otland.net/statuslist.xml"); + if(!doc) + return false; + + xmlNodePtr p, root = xmlDocGetRootElement(doc); + if(!xmlStrcmp(root->name, (const xmlChar*)"statuslist")) + { + p = root->children; + while(p) + { + if(!xmlStrcmp(p->name, (const xmlChar*)"blacklist")) + { + std::string ip; + if(readXMLString(p, "ip", ip)) + blacklist.push_back(ip); + } + else if(!xmlStrcmp(p->name, (const xmlChar*)"whitelist")) + { + std::string ip; + if(readXMLString(p, "ip", ip)) + whitelist.push_back(ip); + } + + p = p->next; + } + } + + xmlFreeDoc(doc); + return true; +} + +bool Game::playerLeaveMarket(uint32_t playerId) +{ + Player* player = getPlayerByID(playerId); + if(!player || player->isRemoved()) + return false; + + player->setMarketDepotId(-1); + return true; +} + +bool Game::playerBrowseMarket(uint32_t playerId, uint16_t spriteId) +{ + Player* player = getPlayerByID(playerId); + if(!player || player->isRemoved()) + return false; + + if(player->getMarketDepotId() == -1) + return false; + + const ItemType& it = Item::items.getItemIdByClientId(spriteId); + if(it.id == 0) + return false; + + if(it.wareId == 0) + return false; + + const MarketOfferList& buyOffers = IOMarket::getInstance()->getActiveOffers(MARKETACTION_BUY, it.id); + const MarketOfferList& sellOffers = IOMarket::getInstance()->getActiveOffers(MARKETACTION_SELL, it.id); + player->sendMarketBrowseItem(it.id, buyOffers, sellOffers); + player->sendMarketDetail(it.id); + return true; +} + +bool Game::playerBrowseMarketOwnOffers(uint32_t playerId) +{ + Player* player = getPlayerByID(playerId); + if(!player || player->isRemoved()) + return false; + + if(player->getMarketDepotId() == -1) + return false; + + const MarketOfferList& buyOffers = IOMarket::getInstance()->getOwnOffers(MARKETACTION_BUY, player->getGUID()); + const MarketOfferList& sellOffers = IOMarket::getInstance()->getOwnOffers(MARKETACTION_SELL, player->getGUID()); + player->sendMarketBrowseOwnOffers(buyOffers, sellOffers); + return true; +} + +bool Game::playerBrowseMarketOwnHistory(uint32_t playerId) +{ + Player* player = getPlayerByID(playerId); + if(!player || player->isRemoved()) + return false; + + if(player->getMarketDepotId() == -1) + return false; - char buffer[40 + it.name.size()]; - if(count == 1) - sprintf(buffer, "Using the last %s...", it.name.c_str()); + const HistoryMarketOfferList& buyOffers = IOMarket::getInstance()->getOwnHistory(MARKETACTION_BUY, player->getGUID()); + const HistoryMarketOfferList& sellOffers = IOMarket::getInstance()->getOwnHistory(MARKETACTION_SELL, player->getGUID()); + player->sendMarketBrowseOwnHistory(buyOffers, sellOffers); + return true; +} + +bool Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t spriteId, uint16_t amount, uint32_t price, bool anonymous) +{ + if(amount == 0 || amount > 64000) + return false; + + if(price == 0 || price > 999999999) + return false; + + if(type != MARKETACTION_BUY && type != MARKETACTION_SELL) + return false; + + Player* player = getPlayerByID(playerId); + if(!player || player->isRemoved()) + return false; + + if(player->getMarketDepotId() == -1) + return false; + + if(g_config.getBool(ConfigManager::MARKET_PREMIUM) && !player->isPremium()) + { + player->sendMarketLeave(); + return false; + } + + const ItemType& it = Item::items.getItemIdByClientId(spriteId); + if(it.id == 0) + return false; + + if(it.wareId == 0) + return false; + + const int32_t maxOfferCount = g_config.getNumber(ConfigManager::MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER); + if(maxOfferCount > 0) + { + const int32_t offerCount = IOMarket::getInstance()->getPlayerOfferCount(player->getGUID()); + if(offerCount == -1 || offerCount >= maxOfferCount) + return false; + } + + uint64_t fee = (price / 100.) * amount; + if(fee < 20) + fee = 20; + else if(fee > 1000) + fee = 1000; + + if(type == MARKETACTION_SELL) + { + if(fee > player->balance) + return false; + + Depot* depot = player->getDepot(player->getMarketDepotId(), false); + if(!depot) + return false; + + ItemList itemList; + uint32_t count = 0; + std::list containerList; + containerList.push_back(depot->getLocker()); + bool enough = false; + do + { + Container* container = containerList.front(); + containerList.pop_front(); + for(ItemList::const_iterator iter = container->getItems(), end = container->getEnd(); iter != end; ++iter) + { + Item* item = (*iter); + Container* c = item->getContainer(); + if(c && !c->empty()) + { + containerList.push_back(c); + continue; + } + + if(item->getID() != it.id) + continue; + + const ItemType& itemType = Item::items[item->getID()]; + if(!itemType.isRune() && item->getCharges() != itemType.charges) + continue; + + if(item->getDuration() != (int32_t)itemType.decayTime) + continue; + + itemList.push_back(item); + count += Item::countByType(item, -1); + if(count >= amount) + { + enough = true; + break; + } + } + + if(enough) + break; + } + while(!containerList.empty()); + + if(!enough) + return false; + + if(it.stackable) + { + uint16_t tmpAmount = amount; + for(ItemList::const_iterator iter = itemList.begin(), end = itemList.end(); iter != end; ++iter) + { + uint16_t removeCount = std::min(tmpAmount, (*iter)->getItemCount()); + tmpAmount -= removeCount; + internalRemoveItem(NULL, *iter, removeCount); + if(tmpAmount == 0) + break; + } + } + else + { + for(ItemList::const_iterator iter = itemList.begin(), end = itemList.end(); iter != end; ++iter) + internalRemoveItem(NULL, *iter); + } + player->balance -= fee; + } + else + { + uint64_t totalPrice = (uint64_t)price * amount; + totalPrice += fee; + if(totalPrice > player->balance) + return false; + + player->balance -= totalPrice; + } + + IOMarket::getInstance()->createOffer(player->getGUID(), (MarketAction_t)type, it.id, amount, price, anonymous); + + player->sendMarketEnter(player->getMarketDepotId()); + const MarketOfferList& buyOffers = IOMarket::getInstance()->getActiveOffers(MARKETACTION_BUY, it.id); + const MarketOfferList& sellOffers = IOMarket::getInstance()->getActiveOffers(MARKETACTION_SELL, it.id); + player->sendMarketBrowseItem(it.id, buyOffers, sellOffers); + return true; +} + +bool Game::playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter) +{ + Player* player = getPlayerByID(playerId); + if(!player || player->isRemoved()) + return false; + + if(player->getMarketDepotId() == -1) + return false; + + uint32_t offerId = IOMarket::getInstance()->getOfferIdByCounter(timestamp, counter); + if(offerId == 0) + return false; + + MarketOfferEx offer = IOMarket::getInstance()->getOfferById(offerId); + if(offer.playerId != player->getGUID()) + return false; + + if(offer.type == MARKETACTION_BUY) + { + player->balance += (uint64_t)offer.price * offer.amount; + player->sendMarketEnter(player->getMarketDepotId()); + } else - sprintf(buffer, "Using one of %d %s...", count, it.pluralName.c_str()); + { + const ItemType& it = Item::items[offer.itemId]; + if(it.id == 0) + return false; + + Depot* depot = player->getDepot(player->getMarketDepotId(), true); + if(it.stackable) + { + uint16_t tmpAmount = offer.amount; + while(tmpAmount > 0) + { + int32_t stackCount = std::min((int32_t)100, (int32_t)tmpAmount); + Item* item = Item::CreateItem(it.id, stackCount); + if(internalAddItem(NULL, depot->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + { + delete item; + break; + } + + tmpAmount -= stackCount; + } + } + else + { + int32_t subType = -1; + if(it.charges != 0) + subType = it.charges; + + for(uint16_t i = 0; i < offer.amount; ++i) + { + Item* item = Item::CreateItem(it.id, subType); + if(internalAddItem(NULL, depot->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + { + delete item; + break; + } + } + } + } + + IOMarket::getInstance()->moveOfferToHistory(offerId, OFFERSTATE_CANCELLED); + offer.amount = 0; + offer.timestamp += g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); + player->sendMarketCancelOffer(offer); + return true; +} + +bool Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter, uint16_t amount) +{ + if(amount == 0 || amount > 64000) + return false; + + Player* player = getPlayerByID(playerId); + if(!player || player->isRemoved()) + return false; + + if(player->getMarketDepotId() == -1) + return false; + + uint32_t offerId = IOMarket::getInstance()->getOfferIdByCounter(timestamp, counter); + if(offerId == 0) + return false; + + MarketOfferEx offer = IOMarket::getInstance()->getOfferById(offerId); + if(amount > offer.amount) + return false; + + const ItemType& it = Item::items[offer.itemId]; + if(it.id == 0) + return false; + + uint64_t totalPrice = (uint64_t)offer.price * amount; + if(offer.type == MARKETACTION_BUY) + { + Depot* depot = player->getDepot(player->getMarketDepotId(), false); + if(!depot) + return false; + + ItemList itemList; + uint32_t count = 0; + std::list containerList; + containerList.push_back(depot->getLocker()); + bool enough = false; + do + { + Container* container = containerList.front(); + containerList.pop_front(); + for(ItemList::const_iterator iter = container->getItems(), end = container->getEnd(); iter != end; ++iter) + { + Item* item = (*iter); + Container* c = item->getContainer(); + if(c && !c->empty()) + { + containerList.push_back(c); + continue; + } + + if(item->getID() != it.id) + continue; + + const ItemType& itemType = Item::items[item->getID()]; + if(!itemType.isRune() && item->getCharges() != itemType.charges) + continue; + + if(item->getDuration() != (int32_t)itemType.decayTime) + continue; + + itemList.push_back(item); + count += Item::countByType(item, -1); + if(count >= amount) + { + enough = true; + break; + } + } + + if(enough) + break; + } + while(!containerList.empty()); + + if(!enough) + return false; + + if(it.stackable) + { + uint16_t tmpAmount = amount; + for(ItemList::const_iterator iter = itemList.begin(), end = itemList.end(); iter != end; ++iter) + { + uint16_t removeCount = std::min(tmpAmount, (*iter)->getItemCount()); + tmpAmount -= removeCount; + internalRemoveItem(NULL, *iter, removeCount); + if(tmpAmount == 0) + break; + } + } + else + { + for(ItemList::const_iterator iter = itemList.begin(), end = itemList.end(); iter != end; ++iter) + internalRemoveItem(NULL, *iter); + } + + player->balance += totalPrice; + + Player* buyerPlayer = getPlayerByGUID(offer.playerId); + if(!buyerPlayer) + { + std::string buyerName; + if(!IOLoginData::getInstance()->getNameByGuid(offer.playerId, buyerName)) + return false; + + buyerPlayer = new Player(buyerName, NULL); + if(!IOLoginData::getInstance()->loadPlayer(buyerPlayer, buyerName)) + { + delete buyerPlayer; + return false; + } + } + + Depot* buyerDepot = buyerPlayer->getDepot(buyerPlayer->getTown(), true); + if(it.stackable) + { + uint16_t tmpAmount = amount; + while(tmpAmount > 0) + { + uint16_t stackCount = std::min((uint16_t)100, tmpAmount); + Item* item = Item::CreateItem(it.id, stackCount); + if(internalAddItem(NULL, buyerDepot->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + { + delete item; + break; + } + + tmpAmount -= stackCount; + } + } + else + { + int32_t subType = -1; + if(it.charges != 0) + subType = it.charges; + + for(uint16_t i = 0; i < amount; ++i) + { + Item* item = Item::CreateItem(it.id, subType); + if(internalAddItem(NULL, buyerDepot->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + { + delete item; + break; + } + } + } + + if(buyerPlayer->isVirtual()) + { + IOLoginData::getInstance()->savePlayer(buyerPlayer, true); + delete buyerPlayer; + } + } + else + { + if(totalPrice > player->balance) + return false; + + player->balance -= totalPrice; + + Depot* depot = player->getDepot(player->getMarketDepotId(), true); + if(it.stackable) + { + uint16_t tmpAmount = amount; + while(tmpAmount > 0) + { + uint16_t stackCount = std::min((uint16_t)100, tmpAmount); + Item* item = Item::CreateItem(it.id, stackCount); + if(internalAddItem(NULL, depot->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + { + delete item; + break; + } + + tmpAmount -= stackCount; + } + } + else + { + int32_t subType = -1; + if(it.charges != 0) + subType = it.charges; + + for(uint16_t i = 0; i < amount; ++i) + { + Item* item = Item::CreateItem(it.id, subType); + if(internalAddItem(NULL, depot->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + { + delete item; + break; + } + } + } + + Player* sellerPlayer = getPlayerByGUID(offer.playerId); + if(sellerPlayer) + sellerPlayer->balance += totalPrice; + else + IOLoginData::getInstance()->increaseBankBalance(offer.playerId, totalPrice); + } + + const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); + IOMarket::getInstance()->appendHistory(player->getGUID(), (offer.type == MARKETACTION_BUY ? MARKETACTION_SELL : MARKETACTION_BUY), offer.itemId, amount, offer.price, offer.timestamp + marketOfferDuration, OFFERSTATE_ACCEPTEDEX); + IOMarket::getInstance()->appendHistory(offer.playerId, offer.type, offer.itemId, amount, offer.price, offer.timestamp + marketOfferDuration, OFFERSTATE_ACCEPTED); + IOMarket::getInstance()->acceptOffer(offerId, amount); + + player->sendMarketEnter(player->getMarketDepotId()); + offer.amount -= amount; + offer.timestamp += marketOfferDuration; + player->sendMarketAcceptOffer(offer); + return true; +} + +void Game::checkExpiredMarketOffers() +{ + IOMarket::getInstance()->clearOldHistory(); + + const ExpiredMarketOfferList& expiredBuyOffers = IOMarket::getInstance()->getExpiredOffers(MARKETACTION_BUY); + for(ExpiredMarketOfferList::const_iterator it = expiredBuyOffers.begin(), end = expiredBuyOffers.end(); it != end; ++it) + { + ExpiredMarketOffer offer = *it; + + Player* player = getPlayerByGUID(offer.playerId); + uint64_t totalPrice = (uint64_t)offer.price * offer.amount; + if(player) + player->balance += totalPrice; + else + IOLoginData::getInstance()->increaseBankBalance(offer.playerId, totalPrice); + + IOMarket::getInstance()->moveOfferToHistory(offer.id, OFFERSTATE_EXPIRED); + } + + const ExpiredMarketOfferList& expiredSellOffers = IOMarket::getInstance()->getExpiredOffers(MARKETACTION_SELL); + for(ExpiredMarketOfferList::const_iterator it = expiredSellOffers.begin(), end = expiredSellOffers.end(); it != end; ++it) + { + ExpiredMarketOffer offer = *it; + + Player* player = getPlayerByGUID(offer.playerId); + if(!player) + { + std::string name; + if(!IOLoginData::getInstance()->getNameByGuid(offer.playerId, name)) + continue; + + player = new Player(name, NULL); + if(!IOLoginData::getInstance()->loadPlayer(player, name)) + { + delete player; + continue; + } + } + + const ItemType& itemType = Item::items[offer.itemId]; + if(itemType.id == 0) + continue; + + Depot* depot = player->getDepot(player->getTown(), true); + if(itemType.stackable) + { + uint16_t tmpAmount = offer.amount; + while(tmpAmount > 0) + { + uint16_t stackCount = std::min((uint16_t)100, tmpAmount); + Item* item = Item::CreateItem(itemType.id, stackCount); + if(internalAddItem(NULL, depot->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + { + delete item; + break; + } + + tmpAmount -= stackCount; + } + } + else + { + int32_t subType = -1; + if(itemType.charges != 0) + subType = itemType.charges; + + for(uint16_t i = 0; i < offer.amount; ++i) + { + Item* item = Item::CreateItem(itemType.id, subType); + if(internalAddItem(NULL, depot->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + { + delete item; + break; + } + } + } + + if(player->isVirtual()) + { + IOLoginData::getInstance()->savePlayer(player, true); + delete player; + } + + IOMarket::getInstance()->moveOfferToHistory(offer.id, OFFERSTATE_EXPIRED); + } + + int32_t checkExpiredMarketOffersEachMinutes = g_config.getNumber(ConfigManager::CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES); + if(checkExpiredMarketOffersEachMinutes <= 0) + return; - player->sendTextMessage(MSG_INFO_DESCR, buffer); + Scheduler::getInstance().addEvent(createSchedulerTask(checkExpiredMarketOffersEachMinutes * 60 * 1000, boost::bind(&Game::checkExpiredMarketOffers, this))); } diff --git a/game.h b/game.h index 51016e0..99d8f70 100644 --- a/game.h +++ b/game.h @@ -21,6 +21,7 @@ #include "enums.h" #include "templates.h" +#include "server.h" #include "scheduler.h" #include "map.h" @@ -31,12 +32,12 @@ #include "npc.h" #include "monster.h" -class ServiceManager; class Creature; class Player; class Monster; class Npc; class CombatInfo; +struct CombatParams; enum stackposType_t { @@ -49,24 +50,24 @@ enum stackposType_t enum WorldType_t { - WORLD_TYPE_FIRST = 1, - WORLD_TYPE_NO_PVP = WORLD_TYPE_FIRST, - WORLD_TYPE_PVP = 2, - WORLD_TYPE_PVP_ENFORCED = 3, - WORLD_TYPE_LAST = WORLD_TYPE_PVP_ENFORCED + WORLDTYPE_FIRST = 1, + WORLDTYPE_OPTIONAL = WORLDTYPE_FIRST, + WORLDTYPE_OPEN = 2, + WORLDTYPE_HARDCORE = 3, + WORLDTYPE_LAST = WORLDTYPE_HARDCORE }; enum GameState_t { - GAME_STATE_FIRST = 1, - GAME_STATE_STARTUP = GAME_STATE_FIRST, - GAME_STATE_INIT = 2, - GAME_STATE_NORMAL = 3, - GAME_STATE_MAINTAIN = 4, - GAME_STATE_CLOSED = 5, - GAME_STATE_CLOSING = 6, - GAME_STATE_SHUTDOWN = 7, - GAME_STATE_LAST = GAME_STATE_SHUTDOWN + GAMESTATE_FIRST = 1, + GAMESTATE_STARTUP = GAMESTATE_FIRST, + GAMESTATE_INIT = 2, + GAMESTATE_NORMAL = 3, + GAMESTATE_MAINTAIN = 4, + GAMESTATE_CLOSED = 5, + GAMESTATE_CLOSING = 6, + GAMESTATE_SHUTDOWN = 7, + GAMESTATE_LAST = GAMESTATE_SHUTDOWN }; enum LightState_t @@ -88,37 +89,31 @@ enum ReloadInfo_t RELOAD_GLOBALEVENTS = 6, RELOAD_GROUPS = 7, RELOAD_HIGHSCORES = 8, - RELOAD_HOUSEPRICES = 9, + //RELOAD_UNUSED = 9, RELOAD_ITEMS = 10, RELOAD_MONSTERS = 11, - RELOAD_MOVEEVENTS = 12, - RELOAD_NPCS = 13, - RELOAD_OUTFITS = 14, - RELOAD_QUESTS = 15, - RELOAD_RAIDS = 16, - RELOAD_SPELLS = 17, - RELOAD_STAGES = 18, - RELOAD_TALKACTIONS = 19, - RELOAD_VOCATIONS = 20, - RELOAD_WEAPONS = 21, - RELOAD_MODS = 22, - RELOAD_ALL = 23, - RELOAD_LAST = RELOAD_MODS + RELOAD_MOUNTS = 12, + RELOAD_MOVEEVENTS = 13, + RELOAD_NPCS = 14, + RELOAD_OUTFITS = 15, + RELOAD_QUESTS = 16, + RELOAD_RAIDS = 17, + RELOAD_SPELLS = 18, + RELOAD_STAGES = 19, + RELOAD_TALKACTIONS = 20, + RELOAD_VOCATIONS = 21, + RELOAD_WEAPONS = 22, + RELOAD_MODS = 23, + RELOAD_ALL = 24, + RELOAD_LAST = RELOAD_WEAPONS }; -struct RuleViolation +enum SaveFlag_t { - RuleViolation(Player* _reporter, const std::string& _text, uint32_t _time): - reporter(_reporter), gamemaster(NULL), text(_text), time(_time), isOpen(true) {} - - Player* reporter; - Player* gamemaster; - std::string text; - uint32_t time; - bool isOpen; - - private: - RuleViolation(const RuleViolation&); + SAVE_PLAYERS = 1 << 0, + SAVE_PLAYERS_SHALLOW = 1 << 1, + SAVE_MAP = 1 << 2, + SAVE_STATE = 1 << 3 }; struct RefreshBlock_t @@ -127,16 +122,17 @@ struct RefreshBlock_t uint64_t lastRefresh; }; -typedef std::map > RuleViolationsMap; typedef std::map RefreshTiles; typedef std::vector< std::pair > Highscore; typedef std::list Trash; typedef std::map StageList; +typedef std::vector StatusList; #define EVENT_LIGHTINTERVAL 10000 -#define EVENT_DECAYINTERVAL 1000 -#define EVENT_DECAYBUCKETS 16 +#define EVENT_DECAYINTERVAL 250 +#define EVENT_DECAYBUCKETS 4 #define STATE_DELAY 1000 +#define EVENT_WARSINTERVAL 900000 /** * Main Game class. @@ -155,7 +151,9 @@ class Game void checkHighscores(); bool reloadHighscores(); - void prepareGlobalSave(); + bool isSwimmingPool(Item* item, const Tile* tile, bool checkProtection) const; + + void prepareGlobalSave(uint8_t minutes); void globalSave(); /** @@ -170,11 +168,10 @@ class Game * \param width width of the map * \param height height of the map */ - void getMapDimensions(uint32_t& width, uint32_t& height) + inline void getMapDimensions(uint32_t& width, uint32_t& height) const { width = map->mapWidth; height = map->mapHeight; - return; } void setWorldType(WorldType_t type) {worldType = type;} @@ -219,6 +216,13 @@ class Game */ Player* getPlayerByID(uint32_t id); + /** + * Returns a player based on guid + * \param guid + * \returns A Pointer to the player + */ + Player* getPlayerByGUID(const uint32_t& guid); + /** * Returns a creature based on a string name identifier * \param s is the name identifier @@ -331,7 +335,7 @@ class Game uint32_t getNpcsOnline() {return (uint32_t)Npc::autoList.size();} uint32_t getCreaturesOnline() {return (uint32_t)autoList.size();} - uint32_t getPlayersRecord() {return playersRecord;} + uint32_t getPlayersRecord() const {return playersRecord;} void getWorldLightInfo(LightInfo& lightInfo); void getSpectators(SpectatorVec& list, const Position& centerPos, bool checkforduplicate = false, bool multifloor = false, @@ -342,16 +346,24 @@ class Game void clearSpectatorCache() {if(map) map->clearSpectatorCache();} ReturnValue internalMoveCreature(Creature* creature, Direction direction, uint32_t flags = 0); - ReturnValue internalMoveCreature(Creature* actor, Creature* creature, Cylinder* fromCylinder, Cylinder* toCylinder, uint32_t flags = 0); + ReturnValue internalMoveCreature(Creature* actor, Creature* creature, Cylinder* fromCylinder, + Cylinder* toCylinder, uint32_t flags = 0, bool forceTeleport = false); ReturnValue internalMoveItem(Creature* actor, Cylinder* fromCylinder, Cylinder* toCylinder, int32_t index, Item* item, uint32_t count, Item** _moveItem, uint32_t flags = 0); ReturnValue internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index = INDEX_WHEREEVER, uint32_t flags = 0, bool test = false); - ReturnValue internalRemoveItem(Creature* actor, Item* item, int32_t count = -1, bool test = false, uint32_t flags = 0); + ReturnValue internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index, + uint32_t flags, bool test, uint32_t& remainderCount); + ReturnValue internalAddItem(Creature* actor, Cylinder* toCylinder, Item* item, int32_t index, + uint32_t flags, bool test, uint32_t& remainderCount, Item** stackItem); + ReturnValue internalRemoveItem(Creature* actor, Item* item, int32_t count = -1, bool test = false, uint32_t flags = 0); - ReturnValue internalPlayerAddItem(Creature* actor, Player* player, Item* item, bool dropOnMap = true); + ReturnValue internalPlayerAddItem(Creature* actor, Player* player, Item* item, + bool dropOnMap = true, slots_t slot = SLOT_WHEREEVER); + ReturnValue internalPlayerAddItem(Creature* actor, Player* player, Item* item, + bool dropOnMap, slots_t slot, Item** toItem); /** * Find an item of a certain type @@ -372,15 +384,17 @@ class Game * \param count is the amount to remove * \param subType is the extra type an item can have such as charges/fluidtype, default is -1 * meaning it's not used + * \param onlyContainers if true it will remove only items from containers in cylinder, default is false + * meaning it's disabled * \returns true if the removal was successful */ - bool removeItemOfType(Cylinder* cylinder, uint16_t itemId, int32_t count, int32_t subType = -1); + bool removeItemOfType(Cylinder* cylinder, uint16_t itemId, int32_t count, int32_t subType = -1, bool onlyContainers = false); /** * Get the amount of money in a a cylinder * \returns the amount of money found */ - uint32_t getMoney(const Cylinder* cylinder); + uint64_t getMoney(const Cylinder* cylinder); /** * Remove/Add item(s) with a monetary value @@ -389,7 +403,7 @@ class Game * \param flags optional flags to modifiy the default behaviour * \returns true if the removal was successful */ - bool removeMoney(Cylinder* cylinder, int32_t money, uint32_t flags = 0); + bool removeMoney(Cylinder* cylinder, int64_t money, uint32_t flags = 0); /** * Add item(s) with monetary value @@ -397,7 +411,7 @@ class Game * \param money the amount to give * \param flags optional flags to modify default behavior */ - void addMoney(Cylinder* cylinder, int32_t money, uint32_t flags = 0); + void addMoney(Cylinder* cylinder, int64_t money, uint32_t flags = 0); /** * Transform one item to another type/count @@ -415,7 +429,7 @@ class Game * \param flags optional flags to modify default behavior * \returns true if the teleportation was successful */ - ReturnValue internalTeleport(Thing* thing, const Position& newPos, bool pushMove, uint32_t flags = 0); + ReturnValue internalTeleport(Thing* thing, const Position& newPos, bool forceTeleport, uint32_t flags = FLAG_NOLIMIT, bool fullTeleport = true); /** * Turn a creature to a different direction. @@ -433,22 +447,22 @@ class Game * \param spectators Send message only to creatures pointed in vector * \param pos Appear as sent from different position */ - bool internalCreatureSay(Creature* creature, SpeakClasses type, const std::string& text, - bool ghostMode, SpectatorVec* spectators = NULL, Position* pos = NULL); + bool internalCreatureSay(Creature* creature, MessageClasses type, const std::string& text, + bool ghostMode, SpectatorVec* spectators = NULL, Position* pos = NULL, uint32_t statementId = 0); bool internalStartTrade(Player* player, Player* partner, Item* tradeItem); bool internalCloseTrade(Player* player); //Implementation of player invoked events - bool playerBroadcastMessage(Player* player, SpeakClasses type, const std::string& text); - bool playerReportBug(uint32_t playerId, std::string bug); - bool playerViolationWindow(uint32_t playerId, std::string name, uint8_t reason, - ViolationAction_t action, std::string comment, std::string statement, - uint32_t statementId, bool ipBanishment); + bool playerBroadcastMessage(Player* player, MessageClasses type, const std::string& text, uint32_t statementId); + bool playerReportBug(uint32_t playerId, std::string comment); + bool playerReportViolation(uint32_t playerId, ReportType_t type, uint8_t reason, const std::string& name, + const std::string& comment, const std::string& translation, uint32_t statementId); + bool playerThankYou(uint32_t playerId, uint32_t statementId); bool playerMoveThing(uint32_t playerId, const Position& fromPos, uint16_t spriteId, int16_t fromStackpos, const Position& toPos, uint8_t count); bool playerMoveCreature(uint32_t playerId, uint32_t movingCreatureId, - const Position& movingCreatureOrigPos, const Position& toPos); + const Position& movingCreatureOrigPos, const Position& toPos, bool delay); bool playerMoveItem(uint32_t playerId, const Position& fromPos, uint16_t spriteId, int16_t fromStackpos, const Position& toPos, uint8_t count); bool playerMove(uint32_t playerId, Direction dir); @@ -460,9 +474,6 @@ class Game bool playerCloseChannel(uint32_t playerId, uint16_t channelId); bool playerOpenPrivateChannel(uint32_t playerId, std::string& receiver); bool playerCloseNpcChannel(uint32_t playerId); - bool playerProcessRuleViolation(uint32_t playerId, const std::string& name); - bool playerCloseRuleViolation(uint32_t playerId, const std::string& name); - bool playerCancelRuleViolation(uint32_t playerId); bool playerReceivePing(uint32_t playerId); bool playerAutoWalk(uint32_t playerId, std::list& listDir); bool playerStopAutoWalk(uint32_t playerId); @@ -485,7 +496,8 @@ class Game bool playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, int index); bool playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, bool ignoreCap = false, bool inBackpacks = false); - bool playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount); + bool playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, + bool ignoreEquipped = false); bool playerCloseShop(uint32_t playerId); bool playerLookInShop(uint32_t playerId, uint16_t spriteId, uint8_t count); bool playerCloseTrade(uint32_t playerId); @@ -500,15 +512,24 @@ class Game bool playerRequestRemoveVip(uint32_t playerId, uint32_t guid); bool playerTurn(uint32_t playerId, Direction dir); bool playerRequestOutfit(uint32_t playerId); - bool playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, + bool playerSay(uint32_t playerId, uint16_t channelId, MessageClasses type, const std::string& receiver, const std::string& text); bool playerChangeOutfit(uint32_t playerId, Outfit_t outfit); + bool playerChangeMountStatus(uint32_t playerId, bool status); bool playerInviteToParty(uint32_t playerId, uint32_t invitedId); bool playerJoinParty(uint32_t playerId, uint32_t leaderId); bool playerRevokePartyInvitation(uint32_t playerId, uint32_t invitedId); bool playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId); - bool playerLeaveParty(uint32_t playerId); - bool playerSharePartyExperience(uint32_t playerId, bool activate, uint8_t unknown); + bool playerLeaveParty(uint32_t playerId, bool forced = false); + bool playerSharePartyExperience(uint32_t playerId, bool activate); + bool playerLeaveMarket(uint32_t playerId); + bool playerBrowseMarket(uint32_t playerId, uint16_t spriteId); + bool playerBrowseMarketOwnOffers(uint32_t playerId); + bool playerBrowseMarketOwnHistory(uint32_t playerId); + bool playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t spriteId, uint16_t amount, uint32_t price, bool anonymous); + bool playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter); + bool playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter, uint16_t amount); + void checkExpiredMarketOffers(); void kickPlayer(uint32_t playerId, bool displayEffect); bool broadcastMessage(const std::string& text, MessageClasses type); @@ -519,7 +540,7 @@ class Game void loadPlayersRecord(); void checkPlayersRecord(Player* player); - bool reloadInfo(ReloadInfo_t reload, uint32_t playerId = 0); + bool reloadInfo(ReloadInfo_t reload, uint32_t playerId = 0, bool completeReload = false); void cleanup(); void shutdown(); void freeThing(Thing* thing); @@ -538,23 +559,31 @@ class Game uint32_t minTargetDist, uint32_t maxTargetDist, bool fullPathSearch = true, bool clearSight = true, int32_t maxSearchDist = -1); + bool steerCreature(Creature* creature, const Position& position, uint16_t maxNodes/* = 100*/); + Position getClosestFreeTile(Creature* creature, Position pos, bool extended = false, bool ignoreHouse = true); - std::string getSearchString(const Position fromPos, const Position toPos, bool fromIsCreature = false, bool toIsCreature = false); + std::string getSearchString(const Position& fromPos, const Position& toPos, bool fromIsCreature = false, bool toIsCreature = false); void changeLight(const Creature* creature); void changeSpeed(Creature* creature, int32_t varSpeedDelta); + void internalCreatureChangeOutfit(Creature* creature, const Outfit_t& oufit, bool forced = false); void internalCreatureChangeVisible(Creature* creature, Visible_t visible); + void updateCreatureSkull(Creature* creature); - void sendPublicSquare(Player* sender, SquareColor_t color); + void updateCreatureShield(Creature* creature); + void updateCreatureEmblem(Creature* creature); + void updateCreatureWalkthrough(Creature* creature); GameState_t getGameState() const {return gameState;} void setGameState(GameState_t newState); - void saveGameState(bool shallow); + void saveGameState(uint8_t flags); void loadGameState(); - void cleanMap(uint32_t& count); + void cleanMapEx(uint32_t& count); + void cleanMap(); + void refreshMap(RefreshTiles::iterator* it = NULL, uint32_t limit = 0); void proceduralRefresh(RefreshTiles::iterator* it = NULL); @@ -567,30 +596,30 @@ class Game void checkCreatureAttack(uint32_t creatureId); void checkCreatures(); void checkLight(); + void checkWars(); bool combatBlockHit(CombatType_t combatType, Creature* attacker, Creature* target, - int32_t& healthChange, bool checkDefense, bool checkArmor); + int32_t& healthChange, bool checkDefense, bool checkArmor, bool field = false, bool element = false); bool combatChangeHealth(CombatType_t combatType, Creature* attacker, Creature* target, int32_t healthChange, - MagicEffect_t hitEffect = MAGIC_EFFECT_UNKNOWN, TextColor_t hitColor = TEXTCOLOR_UNKNOWN, bool force = false); - bool combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange); + MagicEffect_t hitEffect = MAGIC_EFFECT_UNKNOWN, Color_t hitColor = COLOR_UNKNOWN, bool force = false); + bool combatChangeHealth(const CombatParams& params, Creature* attacker, Creature* target, int32_t healthChange, bool force); + bool combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange, + CombatType_t combatType = COMBAT_MANADRAIN, bool inherited = false); //animation help functions void addCreatureHealth(const Creature* target); void addCreatureHealth(const SpectatorVec& list, const Creature* target); - void addAnimatedText(const Position& pos, uint8_t textColor, const std::string& text); - void addAnimatedText(const SpectatorVec& list, const Position& pos, uint8_t textColor, const std::string& text); + void addCreatureSquare(const Creature* target, uint8_t squareColor); + void addCreatureSquare(const SpectatorVec& list, const Creature* target, uint8_t squareColor); + void addMagicEffect(const Position& pos, uint8_t effect, bool ghostMode = false); void addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect, bool ghostMode = false); void addDistanceEffect(const SpectatorVec& list, const Position& fromPos, const Position& toPos, uint8_t effect); void addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect); - const RuleViolationsMap& getRuleViolations() const {return ruleViolations;} - bool cancelRuleViolation(Player* player); - bool closeRuleViolation(Player* player); - - std::vector blacklist; - bool fetchBlacklist(); + void addStatsMessage(const SpectatorVec& list, MessageClasses mClass, const std::string& message, + const Position& pos, MessageDetails* details = NULL); bool loadExperienceStages(); double getExperienceStage(uint32_t level, double divider = 1.); @@ -599,23 +628,28 @@ class Game inline StageList::const_iterator getLastStage() const {return stages.end();} size_t getStagesCount() const {return stages.size();} - void setGlobalSaveMessage(int16_t key, bool value) {globalSaveMessage[key] = value;} - bool getGlobalSaveMessage(int16_t key) const {return globalSaveMessage[key];} - Map* getMap() {return map;} const Map* getMap() const {return map;} - int32_t getLightHour() {return lightHour;} + bool isRunning() const {return services && services->isRunning();} + int32_t getLightHour() const {return lightHour;} void startDecay(Item* item); + bool loadStatuslist(); + + bool isInBlacklist(std::string ip) const {return std::find(blacklist.begin(), blacklist.end(), ip) != blacklist.end();} + bool isInWhitelist(std::string ip) const {return std::find(whitelist.begin(), whitelist.end(), ip) != whitelist.end();} +#ifdef __GROUND_CACHE__ + + std::map grounds; +#endif + protected: - bool playerWhisper(Player* player, const std::string& text); - bool playerYell(Player* player, const std::string& text); - bool playerSpeakTo(Player* player, SpeakClasses type, const std::string& receiver, const std::string& text); - bool playerTalkToChannel(Player* player, SpeakClasses type, const std::string& text, uint16_t channelId); + bool playerWhisper(Player* player, const std::string& text, uint32_t statementId); + bool playerYell(Player* player, const std::string& text, uint32_t statementId); + bool playerSpeakTo(Player* player, MessageClasses type, const std::string& receiver, const std::string& text, uint32_t statementId); + bool playerSpeakToChannel(Player* player, MessageClasses type, const std::string& text, uint16_t channelId, uint32_t statementId); bool playerSpeakToNpc(Player* player, const std::string& text); - bool playerReportRuleViolation(Player* player, const std::string& text); - bool playerContinueReport(Player* player, const std::string& text); struct GameEvent { @@ -627,7 +661,6 @@ class Game std::vector releaseThings; std::map tradeItems; AutoList autoList; - RuleViolationsMap ruleViolations; size_t checkCreatureLastIndex; std::vector checkCreatureVectors[EVENT_CREATURECOUNT]; @@ -658,7 +691,7 @@ class Game int32_t lastMotdId; uint32_t playersRecord; uint32_t checkLightEvent, checkCreatureEvent, checkDecayEvent, saveEvent; - bool globalSaveMessage[2]; + uint32_t checkWarsEvent; RefreshTiles refreshTiles; Trash trash; @@ -668,5 +701,8 @@ class Game Highscore highscoreStorage[9]; time_t lastHighscoreCheck; + + StatusList blacklist; + StatusList whitelist; }; #endif diff --git a/gameservers.cpp b/gameservers.cpp index 752314b..19ef28c 100644 --- a/gameservers.cpp +++ b/gameservers.cpp @@ -31,56 +31,52 @@ void GameServers::clear() serverList.clear(); } -bool GameServers::reload(bool showResult/* = true*/) +bool GameServers::reload() { clear(); - return loadFromXml(showResult); + return loadFromXml(false); } -bool GameServers::loadFromXml(bool showResult/* = true*/) +bool GameServers::loadFromXml(bool result) { - xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "servers.xml").c_str()); + xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_CONFIG, "servers.xml").c_str()); if(!doc) { - std::cout << "[Warning - GameServers::loadFromXml] Cannot load servers file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - GameServers::loadFromXml] Cannot load servers file." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr p, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"servers")) { - std::cout << "[Error - GameServers::loadFromXml] Malformed servers file." << std::endl; + std::clog << "[Error - GameServers::loadFromXml] Malformed servers file." << std::endl; xmlFreeDoc(doc); return false; } std::string strValue; int32_t intValue; - p = root->children; - while(p) + for(xmlNodePtr p = root->children; p; p = p->next) { if(xmlStrcmp(p->name, (const xmlChar*)"server")) - { - p = p->next; continue; - } std::string name, address; - uint32_t id, versionMin, versionMax, port; + uint32_t id, versionMin, versionMax; + + IntegerVec ports; if(readXMLInteger(p, "id", intValue)) id = intValue; else { - std::cout << "[Error - GameServers::loadFromXml] Missing id, skipping" << std::endl; - p = p->next; + std::clog << "[Error - GameServers::loadFromXml] Missing id, skipping" << std::endl; continue; } - if(getServerById(id)) + if(serverList.find(id) != serverList.end()) { - std::cout << "[Error - GameServers::loadFromXml] Duplicate server id " << id << ", skipping" << std::endl; - p = p->next; + std::clog << "[Error - GameServers::loadFromXml] Duplicate server id " << id << ", skipping" << std::endl; continue; } @@ -89,7 +85,7 @@ bool GameServers::loadFromXml(bool showResult/* = true*/) else { name = "Server #" + id; - std::cout << "[Warning - GameServers::loadFromXml] Missing name for server " << id << ", using default" << std::endl; + std::clog << "[Warning - GameServers::loadFromXml] Missing name for server " << id << ", using default" << std::endl; } if(readXMLInteger(p, "versionMin", intValue)) @@ -97,7 +93,7 @@ bool GameServers::loadFromXml(bool showResult/* = true*/) else { versionMin = CLIENT_VERSION_MIN; - std::cout << "[Warning - GameServers::loadFromXml] Missing versionMin for server " << id << ", using default" << std::endl; + std::clog << "[Warning - GameServers::loadFromXml] Missing versionMin for server " << id << ", using default" << std::endl; } if(readXMLInteger(p, "versionMax", intValue)) @@ -105,7 +101,7 @@ bool GameServers::loadFromXml(bool showResult/* = true*/) else { versionMax = CLIENT_VERSION_MAX; - std::cout << "[Warning - GameServers::loadFromXml] Missing versionMax for server " << id << ", using default" << std::endl; + std::clog << "[Warning - GameServers::loadFromXml] Missing versionMax for server " << id << ", using default" << std::endl; } if(readXMLString(p, "address", strValue) || readXMLString(p, "ip", strValue)) @@ -113,33 +109,35 @@ bool GameServers::loadFromXml(bool showResult/* = true*/) else { address = "localhost"; - std::cout << "[Warning - GameServers::loadFromXml] Missing address for server " << id << ", using default" << std::endl; + std::clog << "[Warning - GameServers::loadFromXml] Missing address for server " << id << ", using default" << std::endl; } - if(readXMLInteger(p, "port", intValue)) - port = intValue; + if(readXMLString(p, "port", strValue)) + ports = vectorAtoi(explodeString(strValue, ",")); else { - port = 7171; - std::cout << "[Warning - GameServers::loadFromXml] Missing port for server " << id << ", using default" << std::endl; + ports.push_back(7181); + std::clog << "[Warning - GameServers::loadFromXml] Missing port for server " << id << ", using default" << std::endl; } - if(GameServer* server = new GameServer(name, versionMin, versionMax, inet_addr(address.c_str()), port)) + if(GameServer* server = new GameServer(name, versionMin, versionMax, inet_addr(address.c_str()), ports)) serverList[id] = server; else - std::cout << "[Error - GameServers::loadFromXml] Couldn't add server " << name << std::endl; - - p = p->next; + std::clog << "[Error - GameServers::loadFromXml] Couldn't add server " << name << std::endl; } - xmlFreeDoc(doc); - if(showResult) + if(result) { - std::cout << "> Servers loaded:" << std::endl; - for(GameServersMap::iterator it = serverList.begin(); it != serverList.end(); it++) - std::cout << it->second->getName() << " (" << it->second->getAddress() << ":" << it->second->getPort() << ")" << std::endl; + std::clog << "> Servers loaded:" << std::endl; + for(GameServersMap::iterator it = serverList.begin(); it != serverList.end(); ++it) + { + IntegerVec games = it->second->getPorts(); + for(IntegerVec::const_iterator tit = games.begin(); tit != games.end(); ++tit) + std::clog << it->second->getName() << " (" << it->second->getAddress() << ":" << *tit << ")" << std::endl; + } } + xmlFreeDoc(doc); return true; } @@ -151,36 +149,3 @@ GameServer* GameServers::getServerById(uint32_t id) const return NULL; } - -GameServer* GameServers::getServerByName(std::string name) const -{ - for(GameServersMap::const_iterator it = serverList.begin(); it != serverList.end(); ++it) - { - if(it->second->getName() == name) - return it->second; - } - - return NULL; -} - -GameServer* GameServers::getServerByAddress(uint32_t address) const -{ - for(GameServersMap::const_iterator it = serverList.begin(); it != serverList.end(); ++it) - { - if(it->second->getAddress() == address) - return it->second; - } - - return NULL; -} - -GameServer* GameServers::getServerByPort(uint32_t port) const -{ - for(GameServersMap::const_iterator it = serverList.begin(); it != serverList.end(); ++it) - { - if(it->second->getPort() == port) - return it->second; - } - - return NULL; -} diff --git a/gameservers.h b/gameservers.h index c631927..b5f46a0 100644 --- a/gameservers.h +++ b/gameservers.h @@ -18,17 +18,15 @@ #ifndef __GAMESERVER__ #define __GAMESERVER__ #include "otsystem.h" - -#include "resources.h" #include "const.h" class GameServer { public: - GameServer(): name("TheForgottenServer"), address(LOCALHOST), port(7172), + GameServer(): name("TheForgottenServer"), address(LOCALHOST), versionMin(CLIENT_VERSION_MIN), versionMax(CLIENT_VERSION_MAX) {} - GameServer(std::string _name, uint32_t _versionMin, uint32_t _versionMax, uint32_t _address, uint32_t _port): - name(_name), address(_address), port(_port), versionMin(_versionMin), versionMax(_versionMax) {} + GameServer(std::string _name, uint32_t _versionMin, uint32_t _versionMax, uint32_t _address, std::vector _ports): + name(_name), address(_address), versionMin(_versionMin), versionMax(_versionMax), ports(_ports) {} virtual ~GameServer() {} std::string getName() const {return name;} @@ -36,11 +34,12 @@ class GameServer uint32_t getVersionMax() const {return versionMax;} uint32_t getAddress() const {return address;} - uint32_t getPort() const {return port;} + std::vector getPorts() const {return ports;} protected: std::string name; - uint32_t address, port, versionMin, versionMax; + uint32_t address, versionMin, versionMax; + std::vector ports; }; typedef std::map GameServersMap; @@ -56,13 +55,13 @@ class GameServers return &instance; } - bool loadFromXml(bool showResult = false); - bool reload(bool showResult = false); + bool loadFromXml(bool result); + bool reload(); GameServer* getServerById(uint32_t id) const; - GameServer* getServerByName(std::string name) const; - GameServer* getServerByAddress(uint32_t address) const; - GameServer* getServerByPort(uint32_t port) const; + + GameServersMap::const_iterator getFirstServer() const {return serverList.begin();} + GameServersMap::const_iterator getLastServer() const {return serverList.end();} protected: void clear(); diff --git a/globalevent.cpp b/globalevent.cpp index 7ca476e..52fbb53 100644 --- a/globalevent.cpp +++ b/globalevent.cpp @@ -59,16 +59,16 @@ Event* GlobalEvents::getEvent(const std::string& nodeName) return NULL; } -bool GlobalEvents::registerEvent(Event* event, xmlNodePtr p, bool override) +bool GlobalEvents::registerEvent(Event* event, xmlNodePtr, bool override) { GlobalEvent* globalEvent = dynamic_cast(event); if(!globalEvent) return false; GlobalEventMap* map = &thinkMap; - if(globalEvent->getEventType() == GLOBAL_EVENT_TIMER) + if(globalEvent->getEventType() == GLOBALEVENT_TIMER) map = &timerMap; - else if(globalEvent->getEventType() != GLOBAL_EVENT_NONE) + else if(globalEvent->getEventType() != GLOBALEVENT_NONE) map = &serverMap; GlobalEventMap::iterator it = map->find(globalEvent->getName()); @@ -85,60 +85,54 @@ bool GlobalEvents::registerEvent(Event* event, xmlNodePtr p, bool override) return true; } - std::cout << "[Warning - GlobalEvents::configureEvent] Duplicate registered globalevent with name: " << globalEvent->getName() << std::endl; + std::clog << "[Warning - GlobalEvents::configureEvent] Duplicate registered globalevent with name: " << globalEvent->getName() << std::endl; return false; } void GlobalEvents::startup() { - time_t now = time(NULL); - tm* ts = localtime(&now); - now = std::max(1, (int32_t)(60 - ts->tm_sec)) * 1000; - - execute(GLOBAL_EVENT_STARTUP); - Scheduler::getInstance().addEvent(createSchedulerTask((int32_t)now, + execute(GLOBALEVENT_STARTUP); + Scheduler::getInstance().addEvent(createSchedulerTask(TIMER_INTERVAL, boost::bind(&GlobalEvents::timer, this))); - Scheduler::getInstance().addEvent(createSchedulerTask(GLOBAL_THINK_INTERVAL, - boost::bind(&GlobalEvents::think, this, GLOBAL_THINK_INTERVAL))); + Scheduler::getInstance().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, + boost::bind(&GlobalEvents::think, this))); } void GlobalEvents::timer() { time_t now = time(NULL); tm* ts = localtime(&now); - - uint32_t hour = (uint32_t)ts->tm_hour, minute = (uint32_t)ts->tm_min; for(GlobalEventMap::iterator it = timerMap.begin(); it != timerMap.end(); ++it) { - if(hour != it->second->getHour() || minute != it->second->getMinute()) + int32_t tmp = it->second->getInterval(), h = tmp >> 16, m = (tmp >> 8) & 0xFF, s = tmp & 0xFF; + if(h != ts->tm_hour || m != ts->tm_min || s != ts->tm_sec) continue; if(!it->second->executeEvent()) - std::cout << "[Error - GlobalEvents::timer] Couldn't execute event: " + std::clog << "[Error - GlobalEvents::timer] Couldn't execute event: " << it->second->getName() << std::endl; } - now = std::max(1, (int32_t)(60 - ts->tm_sec)) * 1000; - Scheduler::getInstance().addEvent(createSchedulerTask((int32_t)now, + Scheduler::getInstance().addEvent(createSchedulerTask(TIMER_INTERVAL, boost::bind(&GlobalEvents::timer, this))); } -void GlobalEvents::think(uint32_t interval) +void GlobalEvents::think() { - time_t now = time(NULL); + int64_t now = OTSYS_TIME(); for(GlobalEventMap::iterator it = thinkMap.begin(); it != thinkMap.end(); ++it) { - if(now <= (it->second->getLastExecution() + it->second->getInterval())) + if((it->second->getLastExecution() + it->second->getInterval()) > now) continue; it->second->setLastExecution(now); - if(!it->second->executeThink(it->second->getInterval(), now, interval)) - std::cout << "[Error - GlobalEvents::think] Couldn't execute event: " + if(!it->second->executeEvent()) + std::clog << "[Error - GlobalEvents::think] Couldn't execute event: " << it->second->getName() << std::endl; } - Scheduler::getInstance().addEvent(createSchedulerTask(interval, - boost::bind(&GlobalEvents::think, this, interval))); + Scheduler::getInstance().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, + boost::bind(&GlobalEvents::think, this))); } void GlobalEvents::execute(GlobalEvent_t type) @@ -154,15 +148,16 @@ GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type) { switch(type) { - case GLOBAL_EVENT_NONE: + case GLOBALEVENT_NONE: return thinkMap; - case GLOBAL_EVENT_TIMER: + case GLOBALEVENT_TIMER: return timerMap; - case GLOBAL_EVENT_STARTUP: - case GLOBAL_EVENT_SHUTDOWN: - case GLOBAL_EVENT_RECORD: + case GLOBALEVENT_STARTUP: + case GLOBALEVENT_SHUTDOWN: + case GLOBALEVENT_GLOBALSAVE: + case GLOBALEVENT_RECORD: { GlobalEventMap retMap; for(GlobalEventMap::iterator it = serverMap.begin(); it != serverMap.end(); ++it) @@ -181,11 +176,11 @@ GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type) return GlobalEventMap(); } -GlobalEvent::GlobalEvent(LuaScriptInterface* _interface): +GlobalEvent::GlobalEvent(LuaInterface* _interface): Event(_interface) { - m_lastExecution = time(NULL); - m_hour = m_minute = 0; + m_lastExecution = OTSYS_TIME(); + m_interval = 0; } bool GlobalEvent::configureEvent(xmlNodePtr p) @@ -193,24 +188,26 @@ bool GlobalEvent::configureEvent(xmlNodePtr p) std::string strValue; if(!readXMLString(p, "name", strValue)) { - std::cout << "[Error - GlobalEvent::configureEvent] No name for a globalevent." << std::endl; + std::clog << "[Error - GlobalEvent::configureEvent] No name for a globalevent." << std::endl; return false; } m_name = strValue; - m_eventType = GLOBAL_EVENT_NONE; + m_eventType = GLOBALEVENT_NONE; if(readXMLString(p, "type", strValue)) { std::string tmpStrValue = asLowerCaseString(strValue); if(tmpStrValue == "startup" || tmpStrValue == "start" || tmpStrValue == "load") - m_eventType = GLOBAL_EVENT_STARTUP; + m_eventType = GLOBALEVENT_STARTUP; else if(tmpStrValue == "shutdown" || tmpStrValue == "quit" || tmpStrValue == "exit") - m_eventType = GLOBAL_EVENT_SHUTDOWN; + m_eventType = GLOBALEVENT_SHUTDOWN; + else if(tmpStrValue == "globalsave" || tmpStrValue == "global") + m_eventType = GLOBALEVENT_GLOBALSAVE; else if(tmpStrValue == "record" || tmpStrValue == "playersrecord") - m_eventType = GLOBAL_EVENT_RECORD; + m_eventType = GLOBALEVENT_RECORD; else { - std::cout << "[Error - GlobalEvent::configureEvent] No valid type \"" << strValue << "\" for globalevent with name " << m_name << std::endl; + std::clog << "[Error - GlobalEvent::configureEvent] No valid type \"" << strValue << "\" for globalevent with name " << m_name << std::endl; return false; } @@ -219,27 +216,46 @@ bool GlobalEvent::configureEvent(xmlNodePtr p) else if(readXMLString(p, "time", strValue) || readXMLString(p, "at", strValue)) { IntegerVec params = vectorAtoi(explodeString(strValue, ":")); - if(params.size() < 2 || params[0] > 23 || params[0] < 0 || params[1] > 59 || params[1] < 0) + if(params[0] < 0 || params[0] > 23) { - std::cout << "[Error - GlobalEvent::configureEvent] No valid time \"" << strValue << "\" for globalevent with name " << m_name << std::endl; + std::clog << "[Error - GlobalEvent::configureEvent] No valid hour \"" << strValue << "\" for globalevent with name " << m_name << std::endl; return false; } - m_hour = params[0], m_minute = params[1]; - m_eventType = GLOBAL_EVENT_TIMER; + m_interval |= params[0] << 16; + if(params.size() > 1) + { + if(params[1] < 0 || params[1] > 59) + { + std::clog << "[Error - GlobalEvent::configureEvent] No valid minute \"" << strValue << "\" for globalevent with name " << m_name << std::endl; + return false; + } + + m_interval |= params[1] << 8; + if(params.size() > 2) + { + if(params[2] < 0 || params[2] > 59) + { + std::clog << "[Error - GlobalEvent::configureEvent] No valid second \"" << strValue << "\" for globalevent with name " << m_name << std::endl; + return false; + } + + m_interval |= params[2]; + } + } + + m_eventType = GLOBALEVENT_TIMER; return true; } - else + + int32_t intValue; + if(readXMLInteger(p, "interval", intValue)) { - int32_t intValue; - if(readXMLInteger(p, "interval", intValue)) - { - m_interval = intValue; - return true; - } + m_interval = std::max((int32_t)SCHEDULER_MINTICKS, intValue); + return true; } - std::cout << "[Error - GlobalEvent::configureEvent] No interval for globalevent with name " << m_name << std::endl; + std::clog << "[Error - GlobalEvent::configureEvent] No interval for globalevent with name " << m_name << std::endl; return false; } @@ -247,86 +263,38 @@ std::string GlobalEvent::getScriptEventName() const { switch(m_eventType) { - case GLOBAL_EVENT_STARTUP: + case GLOBALEVENT_STARTUP: return "onStartup"; - case GLOBAL_EVENT_SHUTDOWN: + case GLOBALEVENT_SHUTDOWN: return "onShutdown"; - case GLOBAL_EVENT_RECORD: + case GLOBALEVENT_GLOBALSAVE: + return "onGlobalSave"; + case GLOBALEVENT_RECORD: return "onRecord"; - case GLOBAL_EVENT_TIMER: - return "onTimer"; + case GLOBALEVENT_TIMER: + return "onTime"; default: - return "onThink"; + break; } + + return "onThink"; } std::string GlobalEvent::getScriptEventParams() const { switch(m_eventType) { - case GLOBAL_EVENT_RECORD: + case GLOBALEVENT_RECORD: return "current, old, cid"; - - case GLOBAL_EVENT_NONE: - return "interval, lastExecution, thinkInterval"; - + case GLOBALEVENT_NONE: + return "interval"; + case GLOBALEVENT_TIMER: + return "time"; default: - return ""; + break; } -} -int32_t GlobalEvent::executeThink(uint32_t interval, uint32_t lastExecution, uint32_t thinkInterval) -{ - //onThink(interval, lastExecution, thinkInterval) - if(m_interface->reserveEnv()) - { - ScriptEnviroment* env = m_interface->getEnv(); - if(m_scripted == EVENT_SCRIPT_BUFFER) - { - std::stringstream scriptstream; - scriptstream << "local interval = " << interval << std::endl; - - scriptstream << "local lastExecution = " << lastExecution << std::endl; - scriptstream << "local thinkInterval = " << thinkInterval << std::endl; - - scriptstream << m_scriptData; - bool result = true; - if(m_interface->loadBuffer(scriptstream.str())) - { - lua_State* L = m_interface->getState(); - result = m_interface->getGlobalBool(L, "_result", true); - } - - m_interface->releaseEnv(); - return result; - } - else - { - #ifdef __DEBUG_LUASCRIPTS__ - char desc[125]; - sprintf(desc, "%s - %i (%i)", getName().c_str(), interval, lastExecution); - env->setEventDesc(desc); - #endif - - env->setScriptId(m_scriptId, m_interface); - lua_State* L = m_interface->getState(); - - m_interface->pushFunction(m_scriptId); - lua_pushnumber(L, interval); - - lua_pushnumber(L, lastExecution); - lua_pushnumber(L, thinkInterval); - - bool result = m_interface->callFunction(3); - m_interface->releaseEnv(); - return result; - } - } - else - { - std::cout << "[Error - GlobalEvent::executeThink] Call stack overflow." << std::endl; - return 0; - } + return ""; } int32_t GlobalEvent::executeRecord(uint32_t current, uint32_t old, Player* player) @@ -342,7 +310,9 @@ int32_t GlobalEvent::executeRecord(uint32_t current, uint32_t old, Player* playe scriptstream << "local old = " << old << std::endl; scriptstream << "local cid = " << env->addThing(player) << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -357,8 +327,8 @@ int32_t GlobalEvent::executeRecord(uint32_t current, uint32_t old, Player* playe { #ifdef __DEBUG_LUASCRIPTS__ char desc[125]; - sprintf(desc, "%s - %i to %i (%i)", getName().c_str(), old, current, player->getName().c_str()); - env->setEventDesc(desc); + sprintf(desc, "%s - %i to %i (%s)", getName().c_str(), old, current, player->getName().c_str()); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -376,7 +346,7 @@ int32_t GlobalEvent::executeRecord(uint32_t current, uint32_t old, Player* playe } else { - std::cout << "[Error - GlobalEvent::executeRecord] Call stack overflow." << std::endl; + std::clog << "[Error - GlobalEvent::executeRecord] Call stack overflow." << std::endl; return 0; } } @@ -388,8 +358,17 @@ int32_t GlobalEvent::executeEvent() ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { + std::stringstream scriptstream; + if(m_eventType == GLOBALEVENT_NONE) + scriptstream << "local interval = " << m_interval << std::endl; + else if(m_eventType == GLOBALEVENT_TIMER) + scriptstream << "local time = " << m_interval << std::endl; + + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; - if(m_interface->loadBuffer(m_scriptData)) + if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); @@ -401,16 +380,24 @@ int32_t GlobalEvent::executeEvent() else { env->setScriptId(m_scriptId, m_interface); + lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); - bool result = m_interface->callFunction(0); + int32_t params = 0; + if(m_eventType == GLOBALEVENT_NONE || m_eventType == GLOBALEVENT_TIMER) + { + lua_pushnumber(L, m_interval); + params = 1; + } + + bool result = m_interface->callFunction(params); m_interface->releaseEnv(); return result; } } else { - std::cout << "[Error - GlobalEvent::executeEvent] Call stack overflow." << std::endl; + std::clog << "[Error - GlobalEvent::executeEvent] Call stack overflow." << std::endl; return 0; } } diff --git a/globalevent.h b/globalevent.h index e656844..1ccbb3a 100644 --- a/globalevent.h +++ b/globalevent.h @@ -22,16 +22,17 @@ #include "const.h" #include "scheduler.h" -#define GLOBAL_THINK_INTERVAL 1000 +#define TIMER_INTERVAL 1000 enum GlobalEvent_t { - GLOBAL_EVENT_NONE, - GLOBAL_EVENT_TIMER, + GLOBALEVENT_NONE, + GLOBALEVENT_TIMER, - GLOBAL_EVENT_STARTUP, - GLOBAL_EVENT_SHUTDOWN, - GLOBAL_EVENT_RECORD + GLOBALEVENT_STARTUP, + GLOBALEVENT_SHUTDOWN, + GLOBALEVENT_GLOBALSAVE, + GLOBALEVENT_RECORD }; class GlobalEvent; @@ -42,10 +43,10 @@ class GlobalEvents : public BaseEvents public: GlobalEvents(); virtual ~GlobalEvents(); - void startup(); + void timer(); - void think(uint32_t interval); + void think(); void execute(GlobalEvent_t type); GlobalEventMap getEventMap(GlobalEvent_t type); @@ -58,8 +59,8 @@ class GlobalEvents : public BaseEvents virtual Event* getEvent(const std::string& nodeName); virtual bool registerEvent(Event* event, xmlNodePtr p, bool override); - virtual LuaScriptInterface& getInterface() {return m_interface;} - LuaScriptInterface m_interface; + virtual LuaInterface& getInterface() {return m_interface;} + LuaInterface m_interface; GlobalEventMap thinkMap, serverMap, timerMap; }; @@ -67,31 +68,30 @@ class GlobalEvents : public BaseEvents class GlobalEvent : public Event { public: - GlobalEvent(LuaScriptInterface* _interface); + GlobalEvent(LuaInterface* _interface); virtual ~GlobalEvent() {} virtual bool configureEvent(xmlNodePtr p); - int32_t executeThink(uint32_t interval, uint32_t lastExecution, uint32_t thinkInterval); + int32_t executeRecord(uint32_t current, uint32_t old, Player* player); int32_t executeEvent(); GlobalEvent_t getEventType() const {return m_eventType;} std::string getName() const {return m_name;} - uint32_t getInterval() const {return m_interval;} - uint32_t getHour() const {return m_hour;} - uint32_t getMinute() const {return m_minute;} + uint32_t getInterval() const {return m_interval;} - uint32_t getLastExecution() const {return m_lastExecution;} - void setLastExecution(uint32_t time) {m_lastExecution = time;} + int64_t getLastExecution() const {return m_lastExecution;} + void setLastExecution(int64_t time) {m_lastExecution = time;} protected: + GlobalEvent_t m_eventType; + virtual std::string getScriptEventName() const; virtual std::string getScriptEventParams() const; std::string m_name; - time_t m_lastExecution; - uint32_t m_interval, m_hour, m_minute; - GlobalEvent_t m_eventType; + int64_t m_lastExecution; + uint32_t m_interval; }; #endif diff --git a/group.cpp b/group.cpp index 7650563..4a7fb35 100644 --- a/group.cpp +++ b/group.cpp @@ -22,6 +22,9 @@ #include "group.h" #include "tools.h" +#include "configmanager.h" + +extern ConfigManager g_config; Group Groups::defGroup = Group(); @@ -44,25 +47,21 @@ bool Groups::loadFromXml() xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "groups.xml").c_str()); if(!doc) { - std::cout << "[Warning - Groups::loadFromXml] Cannot load groups file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Groups::loadFromXml] Cannot load groups file." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr p, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"groups")) { - std::cout << "[Error - Groups::loadFromXml] Malformed groups file." << std::endl; + std::clog << "[Error - Groups::loadFromXml] Malformed groups file." << std::endl; xmlFreeDoc(doc); return false; } - p = root->children; - while(p) - { + for(xmlNodePtr p = root->children; p; p = p->next) parseGroupNode(p); - p = p->next; - } xmlFreeDoc(doc); return true; @@ -76,7 +75,7 @@ bool Groups::parseGroupNode(xmlNodePtr p) int32_t intValue; if(!readXMLInteger(p, "id", intValue)) { - std::cout << "[Warning - Groups::parseGroupNode] Missing group id." << std::endl; + std::clog << "[Warning - Groups::parseGroupNode] Missing group id." << std::endl; return false; } @@ -104,15 +103,6 @@ bool Groups::parseGroupNode(xmlNodePtr p) else group->setGhostAccess(group->getAccess()); - if(readXMLInteger(p, "violationReasons", intValue)) - group->setViolationReasons(intValue); - - if(readXMLInteger(p, "nameViolationFlags", intValue)) - group->setNameViolationFlags(intValue); - - if(readXMLInteger(p, "statementViolationFlags", intValue)) - group->setStatementViolationFlags(intValue); - if(readXMLInteger(p, "depotLimit", intValue)) group->setDepotLimit(intValue); @@ -132,7 +122,7 @@ Group* Groups::getGroup(uint32_t groupId) if(it != groupsMap.end()) return it->second; - std::cout << "[Warning - Groups::getGroup] Group " << groupId << " not found." << std::endl; + std::clog << "[Warning - Groups::getGroup] Group " << groupId << " not found." << std::endl; return &defGroup; } @@ -140,7 +130,7 @@ int32_t Groups::getGroupId(const std::string& name) { for(GroupsMap::iterator it = groupsMap.begin(); it != groupsMap.end(); ++it) { - if(!strcasecmp(it->second->getName().c_str(), name.c_str())) + if(boost::algorithm::iequals(it->second->getName(), name)) return it->first; } @@ -152,7 +142,8 @@ uint32_t Group::getDepotLimit(bool premium) const if(m_depotLimit > 0) return m_depotLimit; - return (premium ? 2000 : 1000); + return (premium ? g_config.getNumber(ConfigManager::DEFAULT_DEPOT_SIZE_PREMIUM) + : g_config.getNumber(ConfigManager::DEFAULT_DEPOT_SIZE)); } uint32_t Group::getMaxVips(bool premium) const @@ -160,5 +151,5 @@ uint32_t Group::getMaxVips(bool premium) const if(m_maxVips > 0) return m_maxVips; - return (premium ? 100 : 20); + return (premium ? g_config.getNumber(ConfigManager::VIPLIST_DEFAULT_PREMIUM_LIMIT) : g_config.getNumber(ConfigManager::VIPLIST_DEFAULT_LIMIT)); } diff --git a/group.h b/group.h index b135267..c13890f 100644 --- a/group.h +++ b/group.h @@ -28,13 +28,11 @@ class Group { m_name = m_fullName = ""; m_access = m_ghostAccess = m_outfit = m_depotLimit = m_maxVips = m_flags = m_customFlags = 0; - m_violationReasons = m_nameViolationFlags = m_statementViolationFlags = 0; } Group(uint32_t id): m_id(id) { m_name = m_fullName = ""; m_access = m_ghostAccess = m_outfit = m_depotLimit = m_maxVips = m_flags = m_customFlags = 0; - m_violationReasons = m_nameViolationFlags = m_statementViolationFlags = 0; } std::string getName() const {return m_name;} @@ -47,12 +45,6 @@ class Group void setAccess(uint16_t v) {m_access = v;} uint16_t getGhostAccess() const {return m_ghostAccess;} void setGhostAccess(uint16_t v) {m_ghostAccess = v;} - uint8_t getViolationReasons() const {return m_violationReasons;} - void setViolationReasons(uint8_t v) {m_violationReasons = v;} - int16_t getStatementViolationFlags() const {return m_statementViolationFlags;} - void setStatementViolationFlags(uint8_t v) {m_statementViolationFlags = v;} - int16_t getNameViolationFlags() const {return m_nameViolationFlags;} - void setNameViolationFlags(uint8_t v) {m_nameViolationFlags = v;} uint16_t getOutfit() const {return m_outfit;} void setOutfit(uint16_t v) {m_outfit = v;} @@ -68,13 +60,11 @@ class Group uint64_t getCustomFlags() const {return m_customFlags;} void setCustomFlags(uint64_t v) {m_customFlags = v;} - bool hasFlag(uint64_t value) const {return (m_flags & ((uint64_t)1 << value));} - bool hasCustomFlag(uint64_t value) const {return (m_customFlags & ((uint64_t)1 << value));} + bool hasFlag(uint64_t value) const {return (m_flags & ((uint64_t)1 << value)) != 0;} + bool hasCustomFlag(uint64_t value) const {return (m_customFlags & ((uint64_t)1 << value)) != 0;} private: std::string m_name, m_fullName; - uint8_t m_violationReasons; - int16_t m_nameViolationFlags, m_statementViolationFlags; uint16_t m_access, m_ghostAccess, m_outfit; uint32_t m_id, m_depotLimit, m_maxVips; uint64_t m_flags, m_customFlags; diff --git a/gui.cpp b/gui.cpp deleted file mode 100644 index bde7b73..0000000 --- a/gui.cpp +++ /dev/null @@ -1,47 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// -#include "gui.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) -#include - -void GUI::initTrayMenu() -{ - m_trayMenu = CreateMenu(); - HMENU subFile = CreatePopupMenu(); - AppendMenu(subFile, MF_STRING, ID_TRAY_HIDE, "&Hide window"); - AppendMenu(subFile, MF_SEPARATOR, 0, 0); - AppendMenu(subFile, MF_STRING, ID_TRAY_SHUTDOWN, "&Shutdown"); - AppendMenu(m_trayMenu, MF_STRING | MF_POPUP, (UINT)subFile, "&File"); -} - -void GUI::initFont() -{ - LOGFONT lFont; - memset(&lFont, 0, sizeof(lFont)); - lstrcpy(lFont.lfFaceName, _T("Tahoma")); - lFont.lfHeight = 14; - lFont.lfWeight = FW_NORMAL; - lFont.lfItalic = FALSE; - lFont.lfCharSet = DEFAULT_CHARSET; - lFont.lfOutPrecision = OUT_DEFAULT_PRECIS; - lFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - lFont.lfQuality = DEFAULT_QUALITY; - lFont.lfPitchAndFamily = DEFAULT_PITCH; - m_font = CreateFontIndirect(&lFont); -} - -#endif diff --git a/gui.h b/gui.h deleted file mode 100644 index 7ea3636..0000000 --- a/gui.h +++ /dev/null @@ -1,49 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// - -#ifndef __GUI__ -#define __GUI__ -#include "definitions.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) - -#include "resources.h" -#include "playerbox.h" - -class GUI -{ - public: - static GUI* getInstance() - { - static GUI instance; - return &instance; - } - - void initTrayMenu(); - void initFont(); - - uint64_t m_lineCount; - std::string m_logText; - - bool m_connections, m_minimized; - HWND m_mainWindow, m_statusBar, m_logWindow; - - HFONT m_font; - PlayerBox m_pBox; - HMENU m_trayMenu; -}; -#endif -#endif diff --git a/gui_resources.rc b/gui_resources.rc deleted file mode 100644 index 7875ed3..0000000 --- a/gui_resources.rc +++ /dev/null @@ -1,65 +0,0 @@ -#include "resources.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) -#include - -ID_ICON ICON "TheForgottenServer.ico" - -ID_MENU MENU -BEGIN - POPUP "&Main" - BEGIN - MENUITEM "&Reject connections", ID_MENU_MAIN_REJECT - MENUITEM "&Clear log", ID_MENU_MAIN_CLEARLOG - MENUITEM SEPARATOR - MENUITEM "&Shutdown", ID_MENU_MAIN_SHUTDOWN - END - - POPUP "&Server" - BEGIN - POPUP "&World Type" - BEGIN - MENUITEM "&Non-PVP", ID_MENU_SERVER_WORLDTYPE_NOPVP - MENUITEM "&PVP", ID_MENU_SERVER_WORLDTYPE_PVP - MENUITEM "PVP-&Enforced", ID_MENU_SERVER_WORLDTYPE_PVPENFORCED - END - MENUITEM SEPARATOR - MENUITEM "&Broadcast message", ID_MENU_SERVER_BROADCAST - MENUITEM "&Save server", ID_MENU_SERVER_SAVE - MENUITEM "Clean &map", ID_MENU_SERVER_CLEAN - MENUITEM "&Refresh map", ID_MENU_SERVER_REFRESH - MENUITEM "&Close server", ID_MENU_SERVER_CLOSE - MENUITEM SEPARATOR - MENUITEM "&Players management", ID_MENU_SERVER_PLAYERBOX - END - POPUP "&Reload" - BEGIN - MENUITEM "Acti&ons", ID_MENU_RELOAD_ACTIONS - MENUITEM "&Chat", ID_MENU_RELOAD_CHAT - MENUITEM "&Config", ID_MENU_RELOAD_CONFIG - MENUITEM "Creature &Events", ID_MENU_RELOAD_CREATUREEVENTS - #ifdef __LOGIN_SERVER__ - MENUITEM "&Game Servers", ID_MENU_RELOAD_GAMESERVERS - #endif - MENUITEM "G&lobal Events", ID_MENU_RELOAD_GLOBALEVENTS - MENUITEM "Gro&ups", ID_MENU_RELOAD_GROUPS - MENUITEM "&Highscores", ID_MENU_RELOAD_HIGHSCORES - MENUITEM "Ho&use Prices", ID_MENU_RELOAD_HOUSEPRICES - MENUITEM "&Items", ID_MENU_RELOAD_ITEMS - MENUITEM "Mo&ds", ID_MENU_RELOAD_MODS - MENUITEM "&Monsters", ID_MENU_RELOAD_MONSTERS - MENUITEM "Mov&ements", ID_MENU_RELOAD_MOVEMENTS - MENUITEM "&Npcs", ID_MENU_RELOAD_NPCS - MENUITEM "Out&fits", ID_MENU_RELOAD_OUTFITS - MENUITEM "&Quests", ID_MENU_RELOAD_QUESTS - MENUITEM "&Raids", ID_MENU_RELOAD_RAIDS - MENUITEM "S&pells", ID_MENU_RELOAD_SPELLS - MENUITEM "&Stages", ID_MENU_RELOAD_STAGES - MENUITEM "&Talkactions", ID_MENU_RELOAD_TALKACTIONS - MENUITEM "&Vocations", ID_MENU_RELOAD_VOCATIONS - MENUITEM "&Weapons", ID_MENU_RELOAD_WEAPONS - MENUITEM SEPARATOR - MENUITEM "Reload &All", ID_MENU_RELOAD_ALL - END - -END -#endif diff --git a/house.cpp b/house.cpp index 7fa72ce..568e432 100644 --- a/house.cpp +++ b/house.cpp @@ -60,7 +60,7 @@ void House::addDoor(Door* door) doorList.push_back(door); door->setHouse(this); - updateDoorDescription(); + updateDoorDescription("", door); } void House::removeDoor(Door* door) @@ -73,7 +73,7 @@ void House::removeDoor(Door* door) } } -Door* House::getDoorByNumber(uint32_t doorId) const +Door* House::getDoorByNumber(uint8_t doorId) const { for(HouseDoorList::const_iterator it = doorList.begin(); it != doorList.end(); ++it) { @@ -95,13 +95,6 @@ Door* House::getDoorByPosition(const Position& pos) return NULL; } -void House::setPrice(uint32_t _price, bool update/* = false*/) -{ - price = _price; - if(update && !owner) - updateDoorDescription(); -} - void House::setOwner(uint32_t guid) { owner = guid; @@ -120,6 +113,8 @@ bool House::setOwnerEx(uint32_t guid, bool transfer) return false; guid = player->getGuildId(); + if(player->isVirtual()) + delete player; } if(owner) @@ -135,10 +130,7 @@ bool House::setOwnerEx(uint32_t guid, bool transfer) } setOwner(guid); - if(guid) - lastWarning = time(NULL); - else - lastWarning = 0; + lastWarning = guid ? time(NULL) : 0; Database* db = Database::getInstance(); DBTransaction trans(db); @@ -154,7 +146,21 @@ bool House::isGuild() const return g_config.getBool(ConfigManager::GUILD_HALLS) && guild; } -void House::updateDoorDescription(std::string _name/* = ""*/) +bool House::isBidded() const +{ + Database* db = Database::getInstance(); + DBResult* result; + + DBQuery query; + query << "SELECT `house_id` FROM `house_auctions` WHERE `house_id` = " << id << " LIMIT 1"; + if(!(result = db->storeQuery(query.str()))) + return false; + + result->free(); + return true; +} + +void House::updateDoorDescription(std::string _name/* = ""*/, Door* door/* = NULL*/) { std::string tmp = "house"; if(isGuild()) @@ -173,8 +179,13 @@ void House::updateDoorDescription(std::string _name/* = ""*/) else sprintf(houseDescription, "It belongs to %s '%s'. Nobody owns this %s. It costs %d gold coins.", tmp.c_str(), name.c_str(), tmp.c_str(), price); - for(HouseDoorList::iterator it = doorList.begin(); it != doorList.end(); ++it) - (*it)->setSpecialDescription(houseDescription); + if(!door) + { + for(HouseDoorList::iterator it = doorList.begin(); it != doorList.end(); ++it) + (*it)->setSpecialDescription(houseDescription); + } + else + door->setSpecialDescription(houseDescription); } void House::removePlayer(Player* player, bool ignoreRights) @@ -183,7 +194,7 @@ void House::removePlayer(Player* player, bool ignoreRights) return; Position curPos = player->getPosition(), newPos = g_game.getClosestFreeTile(player, entry, false, false); - if(g_game.internalTeleport(player, newPos, true) == RET_NOERROR && !player->isGhost()) + if(g_game.internalTeleport(player, newPos, false) == RET_NOERROR && !player->isGhost()) { g_game.addMagicEffect(curPos, MAGIC_EFFECT_POFF); g_game.addMagicEffect(newPos, MAGIC_EFFECT_TELEPORT); @@ -224,19 +235,12 @@ bool House::kickPlayer(Player* player, Player* target) if(!houseTile || houseTile->getHouse() != this) return false; - if(player == target) - { - removePlayer(target, true); - return true; - } - - if(getHouseAccessLevel(player) >= getHouseAccessLevel(target)) - { - removePlayer(target, false); - return true; - } + bool self = player == target; + if(getHouseAccessLevel(player) < getHouseAccessLevel(target) && !self) + return false; - return false; + removePlayer(target, self); + return true; } void House::clean() @@ -267,23 +271,23 @@ bool House::transferToDepot() player = g_game.getPlayerByGuidEx(tmp); } - Item* item = NULL; Container* tmpContainer = NULL; + TileItemVector* items = NULL; ItemList moveList; for(HouseTileList::iterator it = houseTiles.begin(); it != houseTiles.end(); ++it) { - for(uint32_t i = 0; i < (*it)->getThingCount(); ++i) - { - if(!(item = (*it)->__getThing(i)->getItem())) - continue; + if(!(items = (*it)->getItemList())) + continue; - if(item->isPickupable()) - moveList.push_back(item); - else if((tmpContainer = item->getContainer())) + for(ItemVector::iterator iit = items->begin(); iit != items->end(); ++iit) + { + if((*iit)->isPickupable()) + moveList.push_back(*iit); + else if((tmpContainer = (*iit)->getContainer())) { - for(ItemList::const_iterator it = tmpContainer->getItems(); it != tmpContainer->getEnd(); ++it) - moveList.push_back(*it); + for(ItemList::const_iterator cit = tmpContainer->getItems(); cit != tmpContainer->getEnd(); ++cit) + moveList.push_back(*cit); } } } @@ -292,7 +296,7 @@ bool House::transferToDepot() { Depot* depot = player->getDepot(townId, true); for(ItemList::iterator it = moveList.begin(); it != moveList.end(); ++it) - g_game.internalMoveItem(NULL, (*it)->getParent(), depot, INDEX_WHEREEVER, (*it), (*it)->getItemCount(), NULL, FLAG_NOLIMIT); + g_game.internalMoveItem(NULL, (*it)->getParent(), depot->getInbox(), INDEX_WHEREEVER, (*it), (*it)->getItemCount(), NULL, FLAG_NOLIMIT); if(player->isVirtual()) { @@ -325,6 +329,7 @@ AccessHouseLevel_t House::getHouseAccessLevel(const Player* player) if(!owner) return HOUSE_NO_INVITED; + AccessHouseLevel_t tmp = HOUSE_NO_INVITED; if(isGuild()) { if(player->getGuildId() == owner) @@ -336,11 +341,11 @@ AccessHouseLevel_t House::getHouseAccessLevel(const Player* player) case GUILDLEVEL_VICE: return HOUSE_SUBOWNER; default: - return HOUSE_GUEST; + tmp = HOUSE_GUEST; } } } - else if(player->getGUID() == owner) + else if(player->getGUID() == owner || player->marriage == owner) return HOUSE_OWNER; if(subOwnerList.isInList(player)) @@ -349,7 +354,7 @@ AccessHouseLevel_t House::getHouseAccessLevel(const Player* player) if(guestList.isInList(player)) return HOUSE_GUEST; - return HOUSE_NO_INVITED; + return tmp; } bool House::canEditAccessList(uint32_t listId, const Player* player) @@ -385,7 +390,7 @@ bool House::getAccessList(uint32_t listId, std::string& list) const return door->getAccessList(list); #ifdef __DEBUG_HOUSES__ - std::cout << "[Failure - House::getAccessList] door == NULL, listId = " << listId < 0) + if(out.length() > 0) { - expressionList.push_back(outExp); - if(outExp.substr(0, 1) == "!") + expressionList.push_back(out); + if(out.substr(0, 1) == "!") { - if(outExp.length() > 1) - regExList.push_front(std::make_pair(boost::regex(outExp.substr(1)), false)); + if(out.length() > 1) + regexList.push_front(std::make_pair(boost::regex(out.substr(1)), false)); } else - regExList.push_back(std::make_pair(boost::regex(outExp), true)); + regexList.push_back(std::make_pair(boost::regex(out), true)); } } catch(...) {} @@ -603,8 +607,7 @@ bool AccessList::addExpression(const std::string& expression) Door::~Door() { - if(accessList) - delete accessList; + delete accessList; } Attr_ReadValue Door::readAttr(AttrTypes_t attr, PropStream& propStream) @@ -612,11 +615,11 @@ Attr_ReadValue Door::readAttr(AttrTypes_t attr, PropStream& propStream) if(attr != ATTR_HOUSEDOORID) return Item::readAttr(attr, propStream); - uint8_t doorId = 0; - if(!propStream.GET_UCHAR(doorId)) + uint8_t _doorId = 0; + if(!propStream.getByte(_doorId)) return ATTR_READ_ERROR; - setDoorId(doorId); + doorId = _doorId; return ATTR_READ_CONTINUE; } @@ -625,6 +628,7 @@ void Door::copyAttributes(Item* item) Item::copyAttributes(item); if(Door* door = item->getDoor()) { + doorId = door->getDoorId(); std::string list; if(door->getAccessList(list)) setAccessList(list); @@ -692,15 +696,15 @@ bool Houses::loadFromXml(std::string filename) xmlDocPtr doc = xmlParseFile(filename.c_str()); if(!doc) { - std::cout << "[Warning - Houses::loadFromXml] Cannot load houses file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Houses::loadFromXml] Cannot load houses file." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } xmlNodePtr houseNode, root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"houses")) { - std::cout << "[Error - Houses::loadFromXml] Malformed houses file." << std::endl; + std::clog << "[Error - Houses::loadFromXml] Malformed houses file." << std::endl; xmlFreeDoc(doc); return false; } @@ -720,7 +724,7 @@ bool Houses::loadFromXml(std::string filename) int32_t houseId = 0; if(!readXMLInteger(houseNode, "houseid", houseId)) { - std::cout << "[Error - Houses::loadFromXml] Could not read houseId" << std::endl; + std::clog << "[Error - Houses::loadFromXml] Could not read houseId" << std::endl; xmlFreeDoc(doc); return false; } @@ -728,7 +732,7 @@ bool Houses::loadFromXml(std::string filename) House* house = Houses::getInstance()->getHouse(houseId); if(!house) { - std::cout << "[Error - Houses::loadFromXml] Unknown house with id: " << houseId << std::endl; + std::clog << "[Error - Houses::loadFromXml] Unknown house with id: " << houseId << std::endl; xmlFreeDoc(doc); return false; } @@ -743,18 +747,18 @@ bool Houses::loadFromXml(std::string filename) if(readXMLInteger(houseNode, "entryz", intValue)) entry.z = intValue; - house->setEntry(entry); - if(!entry.x || !entry.y) - { - std::cout << "[Warning - Houses::loadFromXml] House entry not set for: "; - std::cout << house->getName() << " (" << houseId << ")" << std::endl; - } - if(readXMLString(houseNode, "name", strValue)) house->setName(strValue); else house->resetSyncFlag(House::HOUSE_SYNC_NAME); + house->setEntry(entry); + if(!entry.x || !entry.y) + { + std::clog << "[Warning - Houses::loadFromXml] House entry not set for: " + << house->getName() << " (" << houseId << ")" << std::endl; + } + if(readXMLInteger(houseNode, "townid", intValue)) house->setTownId(intValue); else @@ -774,18 +778,12 @@ bool Houses::loadFromXml(std::string filename) if(readXMLInteger(houseNode, "rent", intValue)) rent = intValue; - uint32_t price = house->getTilesCount() * g_config.getNumber(ConfigManager::HOUSE_PRICE); - if(g_config.getBool(ConfigManager::HOUSE_RENTASPRICE)) - { - uint32_t tmp = rent; - if(!tmp) - tmp = price; - - house->setPrice(tmp); - } - else - house->setPrice(price); + uint32_t price = (house->getSize() + house->getBedsCount()) * g_config.getNumber(ConfigManager::HOUSE_PRICE); + // we should let players to pay only for walkable tiles + beds as single units not two items. + if(g_config.getBool(ConfigManager::HOUSE_RENTASPRICE) && rent) + price = rent; + house->setPrice(price); if(g_config.getBool(ConfigManager::HOUSE_PRICEASRENT)) house->setRent(price); else @@ -799,31 +797,16 @@ bool Houses::loadFromXml(std::string filename) return true; } -bool Houses::reloadPrices() -{ - if(g_config.getBool(ConfigManager::HOUSE_RENTASPRICE)) - return true; - - const uint32_t tilePrice = g_config.getNumber(ConfigManager::HOUSE_PRICE); - for(HouseMap::iterator it = houseMap.begin(); it != houseMap.end(); ++it) - it->second->setPrice(tilePrice * it->second->getTilesCount(), true); - - return true; -} - -void Houses::payHouses() +void Houses::check() { - if(rentPeriod == RENTPERIOD_NEVER) - return; - uint64_t start = OTSYS_TIME(); - std::cout << "> Paying houses..." << std::endl; + std::clog << "> Checking houses..." << std::endl; time_t currentTime = time(NULL); for(HouseMap::iterator it = houseMap.begin(); it != houseMap.end(); ++it) payHouse(it->second, currentTime, 0); - std::cout << "> Houses paid in " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; + std::clog << "Houses checked in " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; } bool Houses::payRent(Player* player, House* house, uint32_t bid, time_t _time/* = 0*/) @@ -872,6 +855,9 @@ bool Houses::payRent(Player* player, House* house, uint32_t bid, time_t _time/* break; } + house->setLastWarning(0); + house->setRentWarnings(0); + house->setPaidUntil(paidUntil); return true; } @@ -907,93 +893,109 @@ bool Houses::payHouse(House* house, time_t _time, uint32_t bid) if(!player->isPremium() && g_config.getBool(ConfigManager::HOUSE_NEED_PREMIUM)) { house->setOwnerEx(0, true); + if(player->isVirtual()) + delete player; + return false; } - uint32_t loginClean = g_config.getNumber(ConfigManager::HOUSE_CLEAN_OLD); - if(loginClean && (_time - loginClean) >= player->getLastLogin()) + int32_t loginClean = g_config.getNumber(ConfigManager::HOUSE_CLEAN_OLD); + if(loginClean && _time >= (player->getLastLogin() + loginClean)) { house->setOwnerEx(0, true); + if(player->isVirtual()) + delete player; + return false; } - bool paid = payRent(player, house, bid, _time), savePlayer = false; - if(!paid && _time >= (house->getLastWarning() + 86400)) + if(payRent(player, house, bid, _time) || _time < (house->getLastWarning() + 86400)) { - uint32_t warningsLimit = 7; - switch(rentPeriod) + if(player->isVirtual()) { - case RENTPERIOD_DAILY: - warningsLimit = 1; - break; - case RENTPERIOD_WEEKLY: - warningsLimit = 3; - break; - case RENTPERIOD_MONTHLY: - warningsLimit = 7; - break; - case RENTPERIOD_YEARLY: - warningsLimit = 14; - break; - default: - break; + IOLoginData::getInstance()->savePlayer(player); + delete player; } - uint32_t warnings = house->getRentWarnings(); - if(warnings < warningsLimit) + return true; + } + + uint32_t warningsLimit = 7; + switch(rentPeriod) + { + case RENTPERIOD_DAILY: + warningsLimit = 1; + break; + case RENTPERIOD_WEEKLY: + warningsLimit = 3; + break; + case RENTPERIOD_YEARLY: + warningsLimit = 14; + break; + default: + break; + } + + uint32_t warnings = house->getRentWarnings(); + if(warnings >= warningsLimit) + { + house->setOwnerEx(0, true); + if(player->isVirtual()) + delete player; + + return false; + } + + if(Depot* depot = player->getDepot(town->getID(), true)) + { + if(Item* letter = Item::CreateItem(ITEM_LETTER_STAMPED)) { - Depot* depot = player->getDepot(town->getID(), true); - Item* letter = Item::CreateItem(ITEM_LETTER_STAMPED); - if(depot && letter) + if(g_game.internalAddItem(NULL, depot->getInbox(), letter, INDEX_WHEREEVER, FLAG_NOLIMIT) == RET_NOERROR) { - std::string period; + letter->setWriter(g_config.getString(ConfigManager::SERVER_NAME)); + letter->setDate(std::time(NULL)); + std::stringstream s; + + s << "Warning!\nThe "; switch(rentPeriod) { case RENTPERIOD_DAILY: - period = "daily"; + s << "daily"; break; case RENTPERIOD_WEEKLY: - period = "weekly"; + s << "weekly"; break; case RENTPERIOD_MONTHLY: - period = "monthly"; + s << "monthly"; break; case RENTPERIOD_YEARLY: - period = "annual"; + s << "annual"; break; default: break; } - std::stringstream s; - s << "Warning!\nThe " << period << " rent of " << house->getRent() << " gold for your " - << (house->isGuild() ? "guild hall" : "house") << " \"" << house->getName() - << "\" has to be paid. Have it within " << (warningsLimit - warnings) - << " days or you will lose your " << (house->isGuild() ? "guild hall" : "house") << "."; + s << " rent of " << house->getRent() << " gold for your " + << (house->isGuild() ? "guild hall" : "house") << " \"" << house->getName() + << "\" has to be paid. Have it within " << (warningsLimit - warnings) + << " days or you will lose your " << (house->isGuild() ? "guild hall" : "house") << "."; letter->setText(s.str().c_str()); - if(g_game.internalAddItem(NULL, depot, letter, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) - g_game.freeThing(letter); - else - savePlayer = true; + if(player->isVirtual()) + IOLoginData::getInstance()->savePlayer(player); } - house->setRentWarnings(++warnings); - house->setLastWarning(_time); + else + g_game.freeThing(letter); } - else - house->setOwnerEx(0, true); } + house->setLastWarning(_time); + house->setRentWarnings(++warnings); if(player->isVirtual()) - { - if(savePlayer) - IOLoginData::getInstance()->savePlayer(player); - delete player; - } - return paid; + return false; } House* Houses::getHouse(uint32_t houseId, bool add/*= false*/) diff --git a/house.h b/house.h index 1129634..4f89095 100644 --- a/house.h +++ b/house.h @@ -20,7 +20,11 @@ #include "otsystem.h" #include +#if defined __GNUC__ && __GNUC__ >= 4 +#include +#else #include +#endif #include "position.h" #include "housetile.h" @@ -76,19 +80,19 @@ class AccessList typedef std::tr1::unordered_set PlayerList; typedef std::list > GuildList; typedef std::list ExpressionList; - typedef std::list > RegExList; + typedef std::list > RegexList; std::string list; PlayerList playerList; GuildList guildList; ExpressionList expressionList; - RegExList regExList; + RegexList regexList; }; class Door : public Item { public: - Door(uint16_t type): Item(type), house(NULL), accessList(NULL) {} + Door(uint16_t type): Item(type), doorId(0), house(NULL), accessList(NULL) {} virtual ~Door(); virtual Door* getDoor() {return this;} @@ -97,8 +101,8 @@ class Door : public Item //serialization virtual Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream); - void setDoorId(uint32_t doorId) {setAttribute("doorid", (int32_t)doorId);} - uint32_t getDoorId() const; + void setDoorId(uint8_t _doorId) {doorId = _doorId;} + uint8_t getDoorId() const {return doorId;} House* getHouse() {return house;} void setHouse(House* _house); @@ -113,19 +117,12 @@ class Door : public Item virtual void copyAttributes(Item* item); private: + uint8_t doorId; + House* house; AccessList* accessList; }; -inline uint32_t Door::getDoorId() const -{ - const int32_t* v = getIntegerAttribute("doorid"); - if(v) - return (uint32_t)*v; - - return 0; -} - class TransferItem : public Item { public: @@ -155,7 +152,8 @@ class House HOUSE_SYNC_SIZE = 1 << 2, HOUSE_SYNC_GUILD = 1 << 3, HOUSE_SYNC_PRICE = 1 << 4, - HOUSE_SYNC_RENT = 1 << 5 + HOUSE_SYNC_RENT = 1 << 5, + HOUSE_SYNC_UPDATE = 1 << 6 }; House(uint32_t houseId); @@ -177,11 +175,11 @@ class House void setRent(uint32_t _rent) {rent = _rent;} uint32_t getRent() const {return rent;} - void setPrice(uint32_t _price, bool update = false); + void setPrice(uint32_t _price) {price = _price;} uint32_t getPrice() const {return price;} void setLastWarning(time_t _lastWarning) {lastWarning = _lastWarning;} - time_t getLastWarning() {return lastWarning;} + time_t getLastWarning() const {return lastWarning;} void setRentWarnings(uint32_t warnings) {rentWarnings = warnings;} uint32_t getRentWarnings() const {return rentWarnings;} @@ -199,21 +197,23 @@ class House bool isGuild() const; uint32_t getDoorsCount() const {return doorList.size();} - uint32_t getBedsCount() const {return bedsList.size();} + uint32_t getBedsCount() const {return (uint32_t)std::ceil((double)bedsList.size() / 2);} uint32_t getTilesCount() const {return houseTiles.size();} bool hasSyncFlag(syncflags_t flag) const {return ((syncFlags & (uint32_t)flag) == (uint32_t)flag);} + void setSyncFlag(syncflags_t flag) {syncFlags |= (uint32_t)flag;} void resetSyncFlag(syncflags_t flag) {syncFlags &= ~(uint32_t)flag;} bool canEditAccessList(uint32_t listId, const Player* player); void setAccessList(uint32_t listId, const std::string& textlist, bool teleport = true); bool getAccessList(uint32_t listId, std::string& list) const; + bool isBidded() const; bool isInvited(const Player* player); AccessHouseLevel_t getHouseAccessLevel(const Player* player); bool kickPlayer(Player* player, Player* target); - void updateDoorDescription(std::string _name = ""); + void updateDoorDescription(std::string _name = "", Door* door = NULL); void clean(); void addDoor(Door* door); @@ -229,7 +229,7 @@ class House HouseTileList::iterator getHouseTileBegin() {return houseTiles.begin();} HouseTileList::iterator getHouseTileEnd() {return houseTiles.end();} - Door* getDoorByNumber(uint32_t doorId) const; + Door* getDoorByNumber(uint8_t doorId) const; Door* getDoorByPosition(const Position& pos); private: @@ -261,9 +261,8 @@ class Houses } bool loadFromXml(std::string filename); - bool reloadPrices(); - void payHouses(); + void check(); bool payHouse(House* house, time_t _time, uint32_t bid); bool payRent(Player* player, House* house, uint32_t bid, time_t _time = 0); @@ -277,6 +276,7 @@ class Houses House* getHouseByGuildId(uint32_t guildId); uint32_t getHousesCount(uint32_t accId); + RentPeriod_t getRentPeriod() const {return rentPeriod;} private: Houses(); diff --git a/housetile.cpp b/housetile.cpp index 4b79f50..afcd71b 100644 --- a/housetile.cpp +++ b/housetile.cpp @@ -56,20 +56,22 @@ void HouseTile::updateHouse(Item* item) if(item->getTile() != this) return; - Door* door = item->getDoor(); - if(door && door->getDoorId()) - house->addDoor(door); + if(Door* door = item->getDoor()) + { + if(door->getDoorId() != 0) + house->addDoor(door); + } else if(BedItem* bed = item->getBed()) house->addBed(bed); } -ReturnValue HouseTile::__queryAdd(int32_t index, const Thing* thing, uint32_t count, uint32_t flags) const +ReturnValue HouseTile::__queryAdd(int32_t index, const Thing* thing, uint32_t count, uint32_t flags, Creature* actor/* = NULL*/) const { if(const Creature* creature = thing->getCreature()) { if(const Player* player = creature->getPlayer()) { - if(!house->isInvited(player)) + if(!house->isInvited(player) && !player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere)) return RET_PLAYERISNOTINVITED; } else @@ -77,12 +79,35 @@ ReturnValue HouseTile::__queryAdd(int32_t index, const Thing* thing, uint32_t co } else if(thing->getItem()) { - const uint32_t itemLimit = g_config.getNumber(ConfigManager::ITEMLIMIT_HOUSETILE); - if(itemLimit && getThingCount() > itemLimit) + const uint32_t itemLimit = g_config.getNumber(ConfigManager::HOUSE_TILE_LIMIT); + if(itemLimit && getItemCount() > itemLimit) return RET_TILEISFULL; + + if(actor && g_config.getBool(ConfigManager::HOUSE_PROTECTION)) + { + if(const Player* player = actor->getPlayer()) + { + if(!house->isInvited(player) && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere)) + return RET_PLAYERISNOTINVITED; + } + } + } + + return Tile::__queryAdd(index, thing, count, flags, actor); +} + +ReturnValue HouseTile::__queryRemove(const Thing* thing, uint32_t count, uint32_t flags, Creature* actor/* = NULL*/) const +{ + if(thing->getItem() && actor && g_config.getBool(ConfigManager::HOUSE_PROTECTION)) + { + if(const Player* player = actor->getPlayer()) + { + if(!house->isInvited(player) && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere)) + return RET_PLAYERISNOTINVITED; + } } - return Tile::__queryAdd(index, thing, count, flags); + return Tile::__queryRemove(thing, count, flags, actor); } Cylinder* HouseTile::__queryDestination(int32_t& index, const Thing* thing, Item** destItem, uint32_t& flags) @@ -91,12 +116,12 @@ Cylinder* HouseTile::__queryDestination(int32_t& index, const Thing* thing, Item { if(const Player* player = creature->getPlayer()) { - if(!house->isInvited(player) && !player->hasFlag(PlayerFlag_CanEditHouses)) + if(!house->isInvited(player) && !player->hasCustomFlag(PlayerCustomFlag_CanMoveAnywhere)) { Tile* destTile = g_game.getTile(house->getEntry()); if(!destTile) { - std::cout << "[Error - HouseTile::__queryDestination] Tile at house entry position for house: " + std::clog << "[Error - HouseTile::__queryDestination] Tile at house entry position for house: " << house->getName() << " (" << house->getId() << ") does not exist." << std::endl; destTile = g_game.getTile(player->getMasterPosition()); if(!destTile) diff --git a/housetile.h b/housetile.h index 8a0ae3e..c699c19 100644 --- a/housetile.h +++ b/housetile.h @@ -26,17 +26,21 @@ class HouseTile : public DynamicTile HouseTile(int32_t x, int32_t y, int32_t z, House* _house); virtual ~HouseTile() {} + virtual HouseTile* getHouseTile() {return this;} + virtual const HouseTile* getHouseTile() const {return this;} + virtual House* getHouse() {return house;} + virtual const House* getHouse() const {return house;} + //cylinder implementations virtual ReturnValue __queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const; + uint32_t flags, Creature* actor = NULL) const; virtual Cylinder* __queryDestination(int32_t& index, const Thing* thing, Item** destItem, uint32_t& flags); + virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, uint32_t flags, Creature* actor = NULL) const; virtual void __addThing(Creature* actor, int32_t index, Thing* thing); virtual void __internalAddThing(uint32_t index, Thing* thing); - House* getHouse() {return house;} - private: void updateHouse(Item* item); House* house; diff --git a/inputbox.cpp b/inputbox.cpp deleted file mode 100644 index 6351fd4..0000000 --- a/inputbox.cpp +++ /dev/null @@ -1,167 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// -#include "inputbox.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) - -HFONT CInputBox::m_hFont = NULL; -HWND CInputBox::m_hWndInputBox = NULL; -HWND CInputBox::m_hWndParent = NULL; -HWND CInputBox::m_hWndEdit = NULL; -HWND CInputBox::m_hWndOK = NULL; -HWND CInputBox::m_hWndCancel = NULL; -HWND CInputBox::m_hWndPrompt = NULL; - -HINSTANCE CInputBox::m_hInst = NULL; - -CInputBox::CInputBox(HWND hWndParent) -{ - HINSTANCE hInst = GetModuleHandle(NULL); - WNDCLASSEX wcex; - if (!GetClassInfoEx(hInst, "InputBox", &wcex)) - { - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = (WNDPROC)WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = hInst; - wcex.hIcon = NULL;//LoadIcon(hInst, (LPCTSTR)IDI_MYINPUTBOX); - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); - wcex.lpszMenuName = NULL; - wcex.lpszClassName = "InputBox"; - wcex.hIconSm = NULL; - if(RegisterClassEx(&wcex) == 0) - MessageBoxA(NULL, "Cannot create InputBox!", "Error", MB_OK); - } - m_hWndParent = hWndParent; - Text = NULL; -} - -CInputBox::~CInputBox() -{ - if(Text) - delete[] Text; -} - -LRESULT CALLBACK CInputBox::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - LOGFONT lfont; - switch(message) - { - case WM_CREATE: - memset(&lfont, 0, sizeof(lfont)); - lstrcpy(lfont.lfFaceName, _T("Arial")); - lfont.lfHeight = 16; - lfont.lfWeight = FW_NORMAL; - lfont.lfItalic = FALSE; - lfont.lfCharSet = DEFAULT_CHARSET; - lfont.lfOutPrecision = OUT_DEFAULT_PRECIS; - lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; - lfont.lfQuality = DEFAULT_QUALITY; - lfont.lfPitchAndFamily = DEFAULT_PITCH; - m_hFont = CreateFontIndirect(&lfont); - m_hInst = GetModuleHandle(NULL); - m_hWndEdit = CreateWindowEx(WS_EX_STATICEDGE, "edit", "", WS_VISIBLE | WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL, 5, INPUTBOX_HEIGHT - 50, INPUTBOX_WIDTH - 16, 20, hWnd, NULL, m_hInst, NULL); - SendMessage(m_hWndEdit, WM_SETFONT, (WPARAM)m_hFont, 0); - m_hWndOK = CreateWindowEx(0, "button", "OK", WS_VISIBLE | WS_CHILD | WS_TABSTOP, INPUTBOX_WIDTH - 100, 10, 90, 25, hWnd, NULL, m_hInst, NULL); - SendMessage(m_hWndOK, WM_SETFONT, (WPARAM)m_hFont, 0); - m_hWndCancel = CreateWindowEx(0, "button", "Cancel", WS_VISIBLE | WS_CHILD | WS_TABSTOP, INPUTBOX_WIDTH - 100, 40, 90, 25, hWnd, NULL, m_hInst, NULL); - SendMessage(m_hWndCancel, WM_SETFONT, (WPARAM)m_hFont, 0); - m_hWndPrompt = CreateWindowEx(WS_EX_STATICEDGE, "static", "", WS_VISIBLE | WS_CHILD, 5, 10, INPUTBOX_WIDTH - 110, INPUTBOX_HEIGHT - 70, hWnd, NULL, m_hInst, NULL); - SendMessage(m_hWndPrompt, WM_SETFONT, (WPARAM)m_hFont, 0); - SetFocus(m_hWndEdit); - break; - case WM_DESTROY: - DeleteObject(m_hFont); - EnableWindow(m_hWndParent, TRUE); - SetForegroundWindow(m_hWndParent); - DestroyWindow(hWnd); - PostQuitMessage(0); - break; - case WM_COMMAND: - switch(HIWORD(wParam)) - { - case BN_CLICKED: - if((HWND)lParam == m_hWndOK) - PostMessage(m_hWndInputBox, WM_KEYDOWN, VK_RETURN, 0); - if((HWND)lParam == m_hWndCancel) - PostMessage(m_hWndInputBox, WM_KEYDOWN, VK_ESCAPE, 0); - break; - } - break; - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - return 0; -} - -BOOL CInputBox::DoModal(LPCTSTR szCaption, LPCTSTR szPrompt) -{ - RECT r; - GetWindowRect(GetDesktopWindow(), &r); - m_hWndInputBox = CreateWindowEx(WS_EX_TOOLWINDOW, "InputBox", szCaption, WS_POPUPWINDOW | WS_CAPTION | WS_TABSTOP, (r.right - INPUTBOX_WIDTH) / 2, (r.bottom - INPUTBOX_HEIGHT) / 2, INPUTBOX_WIDTH, INPUTBOX_HEIGHT, m_hWndParent, NULL, m_hInst, NULL); - if(m_hWndInputBox == NULL) - return FALSE; - SetWindowText(m_hWndPrompt, szPrompt); - SetForegroundWindow(m_hWndInputBox); - EnableWindow(m_hWndParent, FALSE); - ShowWindow(m_hWndInputBox, SW_SHOW); - UpdateWindow(m_hWndInputBox); - BOOL ret = 0; - MSG msg; - HWND hWndFocused; - while(GetMessage(&msg, NULL, 0, 0)) - { - if(msg.message == WM_KEYDOWN) - { - if(msg.wParam == VK_ESCAPE) - { - SendMessage(m_hWndInputBox, WM_DESTROY, 0, 0); - ret = 0; - } - if(msg.wParam == VK_RETURN) - { - int nCount = GetWindowTextLength(m_hWndEdit); - nCount++; - if(Text) - { - delete[] Text; - Text = NULL; - } - Text = new TCHAR[nCount]; - GetWindowText(m_hWndEdit, Text, nCount); - SendMessage(m_hWndInputBox, WM_DESTROY, 0, 0); - ret = 1; - } - if(msg.wParam == VK_TAB) - { - hWndFocused = GetFocus(); - if(hWndFocused == m_hWndEdit) - SetFocus(m_hWndOK); - if(hWndFocused == m_hWndOK) - SetFocus(m_hWndCancel); - if(hWndFocused == m_hWndCancel) - SetFocus(m_hWndEdit); - } - } - TranslateMessage(&msg); - DispatchMessage(&msg); - } - return ret; -} -#endif diff --git a/inputbox.h b/inputbox.h deleted file mode 100644 index 824a29e..0000000 --- a/inputbox.h +++ /dev/null @@ -1,50 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// - -#ifndef __INPUTBOX__ -#define __INPUTBOX__ -#include "otsystem.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) - -#include -#define INPUTBOX_WIDTH 400 -#define INPUTBOX_HEIGHT 125 - -class CInputBox -{ - static HFONT m_hFont; - static HWND m_hWndInputBox; - static HWND m_hWndParent; - static HWND m_hWndEdit; - static HWND m_hWndOK; - static HWND m_hWndCancel; - static HWND m_hWndPrompt; - - static HINSTANCE m_hInst; - - static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - -public: - LPTSTR Text; - BOOL DoModal(LPCTSTR szCaption, LPCTSTR szPrompt); - - CInputBox(HWND hWndParent); - virtual ~CInputBox(); - -}; -#endif -#endif diff --git a/ioban.cpp b/ioban.cpp index 1c610f6..15806a9 100644 --- a/ioban.cpp +++ b/ioban.cpp @@ -17,10 +17,15 @@ #include "otpch.h" #include "ioban.h" +#include "game.h" +#include "player.h" + #include "tools.h" #include "database.h" #include "iologindata.h" +extern Game g_game; + bool IOBan::isIpBanished(uint32_t ip, uint32_t mask/* = 0xFFFFFFFF*/) const { if(!ip) @@ -96,23 +101,25 @@ bool IOBan::isAccountBanished(uint32_t account, uint32_t playerId/* = 0*/) const return false; } -bool IOBan::addIpBanishment(uint32_t ip, int64_t banTime, uint32_t reasonId, - std::string comment, uint32_t gamemaster, uint32_t mask/* = 0xFFFFFFFF*/, std::string statement/* = ""*/) const +bool IOBan::addIpBanishment(uint32_t ip, int64_t banTime, std::string comment, uint32_t gamemaster, uint32_t mask/* = 0xFFFFFFFF*/) const { if(isIpBanished(ip)) return false; + PlayerVector players = g_game.getPlayersByIP(ip, mask); + for(PlayerVector::iterator it = players.begin(); it != players.end(); ++it) + (*it)->kick(true, true); + Database* db = Database::getInstance(); DBQuery query; - query << "INSERT INTO `bans` (`id`, `type`, `value`, `param`, `expires`, `added`, `admin_id`, `comment`, `reason`, `statement`) "; - query << "VALUES (NULL, " << BAN_IP << ", " << ip << ", " << mask << ", " << banTime << ", " << time(NULL) << ", " << gamemaster; - query << ", " << db->escapeString(comment.c_str()) << ", " << reasonId << ", " << db->escapeString(statement.c_str()) << ")"; - return db->executeQuery(query.str()); + query << "INSERT INTO `bans` (`id`, `type`, `value`, `param`, `expires`, `added`, `admin_id`, `comment`) " + << "VALUES (NULL, " << BAN_IP << ", " << ip << ", " << mask << ", " << banTime << ", " + << time(NULL) << ", " << gamemaster << ", " << db->escapeString(comment.c_str()) << ")"; + return db->query(query.str()); } -bool IOBan::addPlayerBanishment(uint32_t playerId, int64_t banTime, uint32_t reasonId, ViolationAction_t actionId, - std::string comment, uint32_t gamemaster, PlayerBan_t type, std::string statement/* = ""*/) const +bool IOBan::addPlayerBanishment(uint32_t playerId, int64_t banTime, std::string comment, uint32_t gamemaster, PlayerBan_t type) const { if(isPlayerBanished(playerId, type)) return false; @@ -120,22 +127,20 @@ bool IOBan::addPlayerBanishment(uint32_t playerId, int64_t banTime, uint32_t rea Database* db = Database::getInstance(); DBQuery query; - query << "INSERT INTO `bans` (`id`, `type`, `value`, `param`, `expires`, `added`, `admin_id`, `comment`, `reason`, `action`, `statement`) "; - query << "VALUES (NULL, " << BAN_PLAYER << ", " << playerId << ", " << type << ", " << banTime << ", " << time(NULL) << ", " << gamemaster; - query << ", " << db->escapeString(comment.c_str()) << ", " << reasonId << ", " << actionId << ", " << db->escapeString(statement.c_str()) << ")"; - return db->executeQuery(query.str()); + query << "INSERT INTO `bans` (`id`, `type`, `value`, `param`, `expires`, `added`, `admin_id`, `comment`) " + << "VALUES (NULL, " << BAN_PLAYER << ", " << playerId << ", " << type << ", " << banTime << ", " + << time(NULL) << ", " << gamemaster << ", " << db->escapeString(comment.c_str()) << ")"; + return db->query(query.str()); } -bool IOBan::addPlayerBanishment(std::string name, int64_t banTime, uint32_t reasonId, ViolationAction_t actionId, - std::string comment, uint32_t gamemaster, PlayerBan_t type, std::string statement/* = ""*/) const +bool IOBan::addPlayerBanishment(std::string name, int64_t banTime, std::string comment, uint32_t gamemaster, PlayerBan_t type) const { uint32_t _guid; return IOLoginData::getInstance()->getGuidByName(_guid, name) && - addPlayerBanishment(_guid, banTime, reasonId, actionId, comment, gamemaster, type, statement); + addPlayerBanishment(_guid, banTime, comment, gamemaster, type); } -bool IOBan::addAccountBanishment(uint32_t account, int64_t banTime, uint32_t reasonId, ViolationAction_t actionId, - std::string comment, uint32_t gamemaster, uint32_t playerId, std::string statement/* = ""*/) const +bool IOBan::addAccountBanishment(uint32_t account, int64_t banTime, std::string comment, uint32_t gamemaster, uint32_t playerId) const { if(isAccountBanished(account)) return false; @@ -143,49 +148,21 @@ bool IOBan::addAccountBanishment(uint32_t account, int64_t banTime, uint32_t rea Database* db = Database::getInstance(); DBQuery query; - query << "INSERT INTO `bans` (`id`, `type`, `value`, `param`, `expires`, `added`, `admin_id`, `comment`, `reason`, `action`, `statement`) "; - query << "VALUES (NULL, " << BAN_ACCOUNT << ", " << account << ", " << playerId << ", " << banTime << ", " << time(NULL) << ", " << gamemaster; - query << ", " << db->escapeString(comment.c_str()) << ", " << reasonId << ", " << actionId << ", " << db->escapeString(statement.c_str()) << ")"; - return db->executeQuery(query.str()); -} - -bool IOBan::addNotation(uint32_t account, uint32_t reasonId, - std::string comment, uint32_t gamemaster, uint32_t playerId, std::string statement/* = ""*/) const -{ - Database* db = Database::getInstance(); - DBQuery query; - - query << "INSERT INTO `bans` (`id`, `type`, `value`, `param`, `expires`, `added`, `admin_id`, `comment`, `reason`, `statement`) "; - query << "VALUES (NULL, " << BAN_NOTATION << ", " << account << ", " << playerId << ", '-1', " << time(NULL) << ", " << gamemaster; - query << ", " << db->escapeString(comment.c_str()) << ", " << reasonId << ", " << db->escapeString(statement.c_str()) << ")"; - return db->executeQuery(query.str()); + query << "INSERT INTO `bans` (`id`, `type`, `value`, `param`, `expires`, `added`, `admin_id`, `comment`) " + << "VALUES (NULL, " << BAN_ACCOUNT << ", " << account << ", " << playerId << ", " << banTime << ", " + << time(NULL) << ", " << gamemaster << ", " << db->escapeString(comment.c_str()) << ")"; + return db->query(query.str()); } -bool IOBan::addStatement(uint32_t playerId, uint32_t reasonId, - std::string comment, uint32_t gamemaster, int16_t channelId/* = -1*/, std::string statement/* = ""*/) const +bool IOBan::addNotation(uint32_t account, std::string comment, uint32_t gamemaster, uint32_t playerId) const { Database* db = Database::getInstance(); DBQuery query; - query << "INSERT INTO `bans` (`id`, `type`, `value`, "; - if(channelId >= 0) - query << "`param`, "; - - query << "`expires`, `added`, `admin_id`, `comment`, `reason`, `statement`) VALUES (NULL, " << BAN_STATEMENT << ", " << playerId; - if(channelId >= 0) - query << ", " << channelId; - - query << ", '-1', " << time(NULL) << ", " << gamemaster << ", " << db->escapeString(comment.c_str()); - query << ", " << reasonId << ", " << db->escapeString(statement.c_str()) << ")"; - return db->executeQuery(query.str()); -} - -bool IOBan::addStatement(std::string name, uint32_t reasonId, - std::string comment, uint32_t gamemaster, int16_t channelId/* = -1*/, std::string statement/* = ""*/) const -{ - uint32_t _guid; - return IOLoginData::getInstance()->getGuidByName(_guid, name) && - addStatement(_guid, reasonId, comment, gamemaster, channelId, statement); + query << "INSERT INTO `bans` (`id`, `type`, `value`, `param`, `expires`, `added`, `admin_id`, `comment`) " + << "VALUES (NULL, " << BAN_NOTATION << ", " << account << ", " << playerId << ", '-1', " + << time(NULL) << ", " << gamemaster << ", " << db->escapeString(comment.c_str()) << ")"; + return db->query(query.str()); } bool IOBan::removeIpBanishment(uint32_t ip, uint32_t mask/* = 0xFFFFFFFF*/) const @@ -195,7 +172,7 @@ bool IOBan::removeIpBanishment(uint32_t ip, uint32_t mask/* = 0xFFFFFFFF*/) cons query << "UPDATE `bans` SET `active` = 0 WHERE `value` = " << ip << " AND `param` = " << mask << " AND `type` = " << BAN_IP << " AND `active` = 1" << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + return db->query(query.str()); } bool IOBan::removePlayerBanishment(uint32_t guid, PlayerBan_t type) const @@ -205,7 +182,7 @@ bool IOBan::removePlayerBanishment(uint32_t guid, PlayerBan_t type) const query << "UPDATE `bans` SET `active` = 0 WHERE `value` = " << guid << " AND `param` = " << type << " AND `type` = " << BAN_PLAYER << " AND `active` = 1" << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + return db->query(query.str()); } bool IOBan::removePlayerBanishment(std::string name, PlayerBan_t type) const @@ -225,7 +202,7 @@ bool IOBan::removeAccountBanishment(uint32_t account, uint32_t playerId/* = 0*/) query << " AND `param` = " << playerId; query << " AND `type` = " << BAN_ACCOUNT << " AND `active` = 1" << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + return db->query(query.str()); } bool IOBan::removeNotations(uint32_t account, uint32_t playerId/* = 0*/) const @@ -238,27 +215,7 @@ bool IOBan::removeNotations(uint32_t account, uint32_t playerId/* = 0*/) const query << " AND `param` = " << playerId; query << " AND `type` = " << BAN_NOTATION << " AND `active` = 1"; - return db->executeQuery(query.str()); -} - -bool IOBan::removeStatements(uint32_t playerId, int16_t channelId/* = -1*/) const -{ - Database* db = Database::getInstance(); - DBQuery query; - - query << "UPDATE `bans` SET `active` = 0 WHERE `value` = " << playerId; - if(channelId >= 0) - query << " AND `param` = " << channelId; - - query << " AND `type` = " << BAN_STATEMENT << " AND `active` = 1"; - return db->executeQuery(query.str()); -} - -bool IOBan::removeStatements(std::string name, int16_t channelId/* = -1*/) const -{ - uint32_t _guid; - return IOLoginData::getInstance()->getGuidByName(_guid, name) - && removeStatements(_guid, channelId); + return db->query(query.str()); } uint32_t IOBan::getNotationsCount(uint32_t account, uint32_t playerId/* = 0*/) const @@ -280,34 +237,6 @@ uint32_t IOBan::getNotationsCount(uint32_t account, uint32_t playerId/* = 0*/) c return count; } -uint32_t IOBan::getStatementsCount(uint32_t playerId, int16_t channelId/* = -1*/) const -{ - Database* db = Database::getInstance(); - DBResult* result; - - DBQuery query; - query << "SELECT COUNT(`id`) AS `count` FROM `bans` WHERE `value` = " << playerId; - if(channelId >= 0) - query << " AND `param` = " << channelId; - - query << " AND `type` = " << BAN_STATEMENT << " AND `active` = 1"; - if(!(result = db->storeQuery(query.str()))) - return 0; - - const uint32_t count = result->getDataInt("count"); - result->free(); - return count; -} - -uint32_t IOBan::getStatementsCount(std::string name, int16_t channelId/* = -1*/) const -{ - uint32_t _guid; - if(!IOLoginData::getInstance()->getGuidByName(_guid, name)) - return 0; - - return getStatementsCount(_guid, channelId); -} - bool IOBan::getData(Ban& ban) const { Database* db = Database::getInstance(); @@ -333,9 +262,6 @@ bool IOBan::getData(Ban& ban) const ban.added = result->getDataLong("added"); ban.adminId = result->getDataInt("admin_id"); ban.comment = result->getDataString("comment"); - ban.reason = result->getDataInt("reason"); - ban.action = (ViolationAction_t)result->getDataInt("action"); - ban.statement = result->getDataString("statement"); result->free(); return true; @@ -369,9 +295,7 @@ BansVec IOBan::getList(Ban_t type, uint32_t value/* = 0*/, uint32_t param/* = 0* tmp.added = result->getDataLong("added"); tmp.adminId = result->getDataInt("admin_id"); tmp.comment = result->getDataString("comment"); - tmp.reason = result->getDataInt("reason"); - tmp.action = (ViolationAction_t)result->getDataInt("action"); - tmp.statement = result->getDataString("statement"); + data.push_back(tmp); } while(result->next()); @@ -380,12 +304,3 @@ BansVec IOBan::getList(Ban_t type, uint32_t value/* = 0*/, uint32_t param/* = 0* return data; } - -bool IOBan::clearTemporials() const -{ - Database* db = Database::getInstance(); - DBQuery query; - - query << "UPDATE `bans` SET `active` = 0 WHERE `expires` <= " << time(NULL) << " AND `expires` >= 0 AND `active` = 1" << db->getUpdateLimiter(); - return db->executeQuery(query.str()); -} diff --git a/ioban.h b/ioban.h index ab81e1b..d0ab8b8 100644 --- a/ioban.h +++ b/ioban.h @@ -27,8 +27,7 @@ enum Ban_t BAN_IP = 1, BAN_PLAYER = 2, BAN_ACCOUNT = 3, - BAN_NOTATION = 4, - BAN_STATEMENT = 5 + BAN_NOTATION = 4 }; enum PlayerBan_t @@ -42,16 +41,14 @@ enum PlayerBan_t struct Ban { Ban_t type; - ViolationAction_t action; - uint32_t id, value, param, added, adminId, reason; + uint32_t id, value, param, added, adminId; int32_t expires; - std::string comment, statement; + std::string comment; Ban() { type = BAN_NONE; - action = ACTION_PLACEHOLDER; - id = value = param = added = adminId = reason = expires = 0; + id = value = param = added = adminId = expires = 0; } }; @@ -63,7 +60,7 @@ class IOBan IOBan() {} public: - virtual ~IOBan() {} + ~IOBan() {} static IOBan* getInstance() { static IOBan instance; @@ -75,36 +72,21 @@ class IOBan bool isPlayerBanished(std::string name, PlayerBan_t type) const; bool isAccountBanished(uint32_t account, uint32_t playerId = 0) const; - bool addIpBanishment(uint32_t ip, int64_t banTime, uint32_t reasonId, - std::string comment, uint32_t gamemaster, uint32_t mask = 0xFFFFFFFF, std::string statement = "") const; - bool addPlayerBanishment(uint32_t playerId, int64_t banTime, uint32_t reasonId, ViolationAction_t actionId, - std::string comment, uint32_t gamemaster, PlayerBan_t type, std::string statement = "") const; - bool addPlayerBanishment(std::string name, int64_t banTime, uint32_t reasonId, ViolationAction_t actionId, - std::string comment, uint32_t gamemaster, PlayerBan_t type, std::string statement = "") const; - bool addAccountBanishment(uint32_t account, int64_t banTime, uint32_t reasonId, ViolationAction_t actionId, - std::string comment, uint32_t gamemaster, uint32_t playerId, std::string statement = "") const; - bool addNotation(uint32_t account, uint32_t reasonId, - std::string comment, uint32_t gamemaster, uint32_t playerId, std::string statement = "") const; - bool addStatement(uint32_t playerId, uint32_t reasonId, - std::string comment, uint32_t gamemaster, int16_t channelId = -1, std::string statement = "") const; - bool addStatement(std::string name, uint32_t reasonId, - std::string comment, uint32_t gamemaster, int16_t channelId = -1, std::string statement = "") const; + bool addIpBanishment(uint32_t ip, int64_t banTime, std::string comment, uint32_t gamemaster, uint32_t mask = 0xFFFFFFFF) const; + bool addPlayerBanishment(uint32_t playerId, int64_t banTime, std::string comment, uint32_t gamemaster, PlayerBan_t type) const; + bool addPlayerBanishment(std::string name, int64_t banTime, std::string comment, uint32_t gamemaster, PlayerBan_t type) const; + bool addAccountBanishment(uint32_t account, int64_t banTime, std::string comment, uint32_t gamemaster, uint32_t playerId) const; + bool addNotation(uint32_t account, std::string comment, uint32_t gamemaster, uint32_t playerId) const; bool removeIpBanishment(uint32_t ip, uint32_t mask = 0xFFFFFFFF) const; bool removePlayerBanishment(uint32_t guid, PlayerBan_t type) const; bool removePlayerBanishment(std::string name, PlayerBan_t type) const; bool removeAccountBanishment(uint32_t account, uint32_t playerId = 0) const; bool removeNotations(uint32_t account, uint32_t playerId = 0) const; - bool removeStatements(uint32_t playerId, int16_t channelId = -1) const; - bool removeStatements(std::string name, int16_t channelId = -1) const; bool getData(Ban& ban) const; std::vector getList(Ban_t type, uint32_t value = 0, uint32_t param = 0); uint32_t getNotationsCount(uint32_t account, uint32_t playerId = 0) const; - uint32_t getStatementsCount(uint32_t playerId, int16_t channelId = -1) const; - uint32_t getStatementsCount(std::string name, int16_t channelId = -1) const; - - bool clearTemporials() const; }; #endif diff --git a/ioguild.cpp b/ioguild.cpp index 0d0bf3d..4f7d49d 100644 --- a/ioguild.cpp +++ b/ioguild.cpp @@ -18,10 +18,13 @@ #include "ioguild.h" #include "database.h" +#include "player.h" #include "configmanager.h" #include "game.h" +#include "chat.h" +extern Chat g_chat; extern Game g_game; extern ConfigManager g_config; @@ -31,7 +34,7 @@ bool IOGuild::getGuildId(uint32_t& id, const std::string& name) DBResult* result; DBQuery query; - query << "SELECT `id` FROM `guilds` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " LIMIT 1"; + query << "SELECT `id` FROM `guilds` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " LIMIT 1"; if(!(result = db->storeQuery(query.str()))) return false; @@ -90,7 +93,7 @@ uint32_t IOGuild::getRankIdByName(uint32_t guild, const std::string& name) DBResult* result; DBQuery query; - query << "SELECT `id` FROM `guild_ranks` WHERE `guild_id` = " << guild << " AND `name` " << db->getStringComparison() << db->escapeString(name) << " LIMIT 1"; + query << "SELECT `id` FROM `guild_ranks` WHERE `guild_id` = " << guild << " AND `name` " << db->getStringComparer() << db->escapeString(name) << " LIMIT 1"; if(!(result = db->storeQuery(query.str()))) return 0; @@ -157,7 +160,7 @@ bool IOGuild::changeRank(uint32_t guild, const std::string& oldName, const std:: DBResult* result; DBQuery query; - query << "SELECT `id` FROM `guild_ranks` WHERE `guild_id` = " << guild << " AND `name` " << db->getStringComparison() << db->escapeString(oldName) << " LIMIT 1"; + query << "SELECT `id` FROM `guild_ranks` WHERE `guild_id` = " << guild << " AND `name` " << db->getStringComparer() << db->escapeString(oldName) << " LIMIT 1"; if(!(result = db->storeQuery(query.str()))) return false; @@ -166,7 +169,7 @@ bool IOGuild::changeRank(uint32_t guild, const std::string& oldName, const std:: query.str(""); query << "UPDATE `guild_ranks` SET `name` = " << db->escapeString(newName) << " WHERE `id` = " << id << db->getUpdateLimiter(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) @@ -184,8 +187,8 @@ bool IOGuild::createGuild(Player* player) DBResult* result; DBQuery query; - query << "INSERT INTO `guilds` (`id`, `world_id`, `name`, `ownerid`, `creationdata`, `motd`) VALUES (NULL, " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " << db->escapeString(player->getGuildName()) << ", " << player->getGUID() << ", " << time(NULL) << ", 'Your guild has been successfully created, to view all available commands type: !commands. If you would like to remove this message use !cleanmotd and to set new motd use !setmotd text.')"; - if(!db->executeQuery(query.str())) + query << "INSERT INTO `guilds` (`id`, `world_id`, `name`, `ownerid`, `creationdata`, `motd`, `checkdata`) VALUES (NULL, " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " << db->escapeString(player->getGuildName()) << ", " << player->getGUID() << ", " << time(NULL) << ", 'Your guild has been successfully created, to view all available commands type: !commands. If you would like to remove this message use !cleanmotd and to set new motd use !setmotd text.', 0)"; + if(!db->query(query.str())) return false; query.str(""); @@ -225,7 +228,7 @@ bool IOGuild::joinGuild(Player* player, uint32_t guildId, bool creation/* = fals query.str(""); query << "UPDATE `players` SET `rank_id` = " << rankId << " WHERE `id` = " << player->getGUID() << db->getUpdateLimiter(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; player->setGuildId(guildId); @@ -236,7 +239,7 @@ bool IOGuild::joinGuild(Player* player, uint32_t guildId, bool creation/* = fals level = GUILDLEVEL_LEADER; player->setGuildLevel(level, rankId); - player->invitedToGuildsList.clear(); + player->invitationsList.clear(); return true; } @@ -246,28 +249,32 @@ bool IOGuild::disbandGuild(uint32_t guildId) DBQuery query; query << "UPDATE `players` SET `rank_id` = '' AND `guildnick` = '' WHERE `rank_id` = " << getRankIdByLevel(guildId, GUILDLEVEL_LEADER) << " OR rank_id = " << getRankIdByLevel(guildId, GUILDLEVEL_VICE) << " OR rank_id = " << getRankIdByLevel(guildId, GUILDLEVEL_MEMBER); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; + InvitationsList::iterator iit; for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) { if(it->second->getGuildId() == guildId) it->second->leaveGuild(); + else if((iit = std::find(it->second->invitationsList.begin(), it->second->invitationsList.end(), + guildId)) != it->second->invitationsList.end()) + it->second->invitationsList.erase(iit); } query.str(""); - query << "DELETE FROM `guilds` WHERE `id` = " << guildId << " LIMIT 1"; - if(!db->executeQuery(query.str())) + query << "DELETE FROM `guilds` WHERE `id` = " << guildId << db->getUpdateLimiter(); + if(!db->query(query.str())) return false; query.str(""); query << "DELETE FROM `guild_invites` WHERE `guild_id` = " << guildId; - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; query.str(""); query << "DELETE FROM `guild_ranks` WHERE `guild_id` = " << guildId; - return db->executeQuery(query.str()); + return db->query(query.str()); } bool IOGuild::hasGuild(uint32_t guid) @@ -291,7 +298,7 @@ bool IOGuild::isInvited(uint32_t guild, uint32_t guid) DBResult* result; DBQuery query; - query << "SELECT `id` FROM `guild_invites` WHERE `player_id` = " << guid << " AND `guild_id`= " << guild << " LIMIT 1"; + query << "SELECT `guild_id` FROM `guild_invites` WHERE `player_id` = " << guid << " AND `guild_id`= " << guild << " LIMIT 1"; if(!(result = db->storeQuery(query.str()))) return false; @@ -304,7 +311,7 @@ bool IOGuild::invitePlayer(uint32_t guild, uint32_t guid) Database* db = Database::getInstance(); DBQuery query; query << "INSERT INTO `guild_invites` (`player_id`, `guild_id`) VALUES ('" << guid << "', '" << guild << "')"; - return db->executeQuery(query.str()); + return db->query(query.str()); } bool IOGuild::revokeInvite(uint32_t guild, uint32_t guid) @@ -312,7 +319,7 @@ bool IOGuild::revokeInvite(uint32_t guild, uint32_t guid) Database* db = Database::getInstance(); DBQuery query; query << "DELETE FROM `guild_invites` WHERE `player_id` = " << guid << " AND `guild_id` = " << guild; - return db->executeQuery(query.str()); + return db->query(query.str()); } uint32_t IOGuild::getGuildId(uint32_t guid) @@ -358,7 +365,7 @@ bool IOGuild::setGuildLevel(uint32_t guid, GuildLevel_t level) query.str(""); query << "UPDATE `players` SET `rank_id` = " << result->getDataInt("id") << " WHERE `id` = " << guid << db->getUpdateLimiter(); result->free(); - return db->executeQuery(query.str()); + return db->query(query.str()); } bool IOGuild::updateOwnerId(uint32_t guild, uint32_t guid) @@ -366,7 +373,7 @@ bool IOGuild::updateOwnerId(uint32_t guild, uint32_t guid) Database* db = Database::getInstance(); DBQuery query; query << "UPDATE `guilds` SET `ownerid` = " << guid << " WHERE `id` = " << guild << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + return db->query(query.str()); } bool IOGuild::setGuildNick(uint32_t guid, const std::string& nick) @@ -374,7 +381,7 @@ bool IOGuild::setGuildNick(uint32_t guid, const std::string& nick) Database* db = Database::getInstance(); DBQuery query; query << "UPDATE `players` SET `guildnick` = " << db->escapeString(nick) << " WHERE `id` = " << guid << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + return db->query(query.str()); } bool IOGuild::setMotd(uint32_t guild, const std::string& newMessage) @@ -382,7 +389,7 @@ bool IOGuild::setMotd(uint32_t guild, const std::string& newMessage) Database* db = Database::getInstance(); DBQuery query; query << "UPDATE `guilds` SET `motd` = " << db->escapeString(newMessage) << " WHERE `id` = " << guild << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + return db->query(query.str()); } std::string IOGuild::getMotd(uint32_t guild) @@ -399,3 +406,180 @@ std::string IOGuild::getMotd(uint32_t guild) result->free(); return motd; } + +void IOGuild::checkWars() +{ + Database* db = Database::getInstance(); + DBResult* result; + + DBQuery query; + query << "SELECT `id`, `guild_id`, `enemy_id` FROM `guild_wars` WHERE `status` IN (1,4) AND `end` > 0 AND `end` < " << time(NULL); + if(!(result = db->storeQuery(query.str()))) + return; + + War_t tmp; + do + { + tmp.war = result->getDataInt("id"); + tmp.ids[WAR_GUILD] = result->getDataInt("guild_id"); + tmp.ids[WAR_ENEMY] = result->getDataInt("enemy_id"); + finishWar(tmp, false); + } + while(result->next()); + result->free(); +} + +bool IOGuild::updateWar(War_t& war) +{ + Database* db = Database::getInstance(); + DBResult* result; + + DBQuery query; + query << "SELECT `g`.`name` AS `guild_name`, `e`.`name` AS `enemy_name`, `w`.* FROM `guild_wars` w LEFT JOIN `guilds` g ON `w`.`guild_id` = `g`.`id` LEFT JOIN `guilds` e ON `w`.`enemy_id` = `e`.`id` WHERE `w`.`id` = " << war.war; + if(!(result = db->storeQuery(query.str()))) + return false; + + war.ids[WAR_GUILD] = result->getDataInt("guild_id"); + war.ids[WAR_ENEMY] = result->getDataInt("enemy_id"); + war.names[WAR_GUILD] = result->getDataString("guild_name"); + war.names[WAR_ENEMY] = result->getDataString("enemy_name"); + + war.frags[WAR_GUILD] = result->getDataInt("guild_kills"); + war.frags[WAR_ENEMY] = result->getDataInt("enemy_kills"); + war.frags[war.type]++; + + war.limit = result->getDataInt("frags"); + war.payment = result->getDataInt("payment"); + + result->free(); + if(war.frags[WAR_GUILD] >= war.limit || war.frags[WAR_ENEMY] >= war.limit) + { + Scheduler::getInstance().addEvent(createSchedulerTask(1000, + boost::bind(&IOGuild::finishWar, this, war, true))); + return true; + } + + query.str(""); + query << "UPDATE `guild_wars` SET `guild_kills` = " << war.frags[WAR_GUILD] << ", `enemy_kills` = " << war.frags[WAR_ENEMY] << " WHERE `id` = " << war.war; + return db->query(query.str()); +} + +void IOGuild::finishWar(War_t war, bool finished) +{ + Database* db = Database::getInstance(); + DBQuery query; + if(finished) + { + query << "UPDATE `guilds` SET `balance` = `balance` + " << (war.payment << 1) << " WHERE `id` = " << war.ids[war.type]; + if(!db->query(query.str())) + return; + + query.str(""); + } + + query << "UPDATE `guild_wars` SET "; + if(finished) + query << "`guild_kills` = " << war.frags[WAR_GUILD] << ", `enemy_kills` = " << war.frags[WAR_ENEMY] << ","; + + query << "`end` = " << time(NULL) << ", `status` = 5 WHERE `id` = " << war.war; + if(!db->query(query.str())) + return; + + for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) + { + if(it->second->isRemoved()) + continue; + + bool update = false; + if(it->second->getGuildId() == war.ids[WAR_GUILD]) + { + it->second->removeEnemy(war.ids[WAR_ENEMY]); + update = true; + } + else if(it->second->getGuildId() == war.ids[WAR_ENEMY]) + { + it->second->removeEnemy(war.ids[WAR_GUILD]); + update = true; + } + + if(update) + g_game.updateCreatureEmblem(it->second); + } + + if(finished) + { + std::stringstream s; + s << war.names[war.type] << " has just won the war against " << war.names[war.type == WAR_GUILD] << "."; + g_game.broadcastMessage(s.str().c_str(), MSG_EVENT_ADVANCE); + } +} + +void IOGuild::frag(Player* player, uint64_t deathId, const DeathList& list, bool score) +{ + War_t war; + std::stringstream s; + for(DeathList::const_iterator it = list.begin(); it != list.end(); ) + { + if(score) + { + if(it->isLast()) + war = it->getWar(); + } + else if(!war.war) + war = it->getWar(); + + Creature* creature = it->getKillerCreature(); + if(it != list.begin()) + { + ++it; + if(it == list.end()) + s << " and "; + else + s << ", "; + } + else + ++it; + + s << creature->getName(); + } + + if(!war.ids[war.type]) + { + #ifdef __DEBUG__ + std::clog << "[Notice - IOGuild::frag] Unable to attach war frag to player " << player->getName() << "." << std::endl; + #endif + return; + } + + std::string killers = s.str(); + s.str(""); + + ChatChannel* channel = NULL; + if((channel = g_chat.getChannel(player, CHANNEL_GUILD))) + { + s << "Guild member " << player->getName() << " was killed by " << killers << "."; + if(score) + s << " The new score is " << war.frags[war.type == WAR_GUILD] << ":" + << war.frags[war.type] << " frags (limit " << war.limit << ")."; + + channel->talk("", MSG_GAMEMASTER_CHANNEL, s.str()); + } + + s.str(""); + if((channel = g_chat.getChannel(list[0].getKillerCreature()->getPlayer(), CHANNEL_GUILD))) + { + s << "Opponent " << player->getName() << " was killed by " << killers << "."; + if(score) + s << " The new score is " << war.frags[war.type] << ":" + << war.frags[war.type == WAR_GUILD] << " frags (limit " << war.limit << ")."; + + channel->talk("", MSG_CHANNEL_HIGHLIGHT, s.str()); + } + + Database* db = Database::getInstance(); + DBQuery query; + + query << "INSERT INTO `guild_kills` (`guild_id`, `war_id`, `death_id`) VALUES (" + << war.ids[war.type] << ", " << war.war << ", " << deathId << ");"; + db->query(query.str()); +} diff --git a/ioguild.h b/ioguild.h index 5b057ae..a9010a1 100644 --- a/ioguild.h +++ b/ioguild.h @@ -18,8 +18,12 @@ #ifndef __IOGUILD__ #define __IOGUILD__ #include "otsystem.h" +#include "enums.h" -#include "player.h" +struct DeathEntry; +typedef std::vector DeathList; + +class Player; class IOGuild { public: @@ -63,6 +67,11 @@ class IOGuild bool swapGuildIdToOwner(uint32_t& value); bool updateOwnerId(uint32_t guild, uint32_t guid); + void checkWars(); + bool updateWar(War_t& enemy); + void finishWar(War_t enemy, bool finished); + void frag(Player* player, uint64_t deathId, const DeathList& list, bool score); + private: IOGuild() {} }; diff --git a/iologindata.cpp b/iologindata.cpp index fbbaf89..fc9da2a 100644 --- a/iologindata.cpp +++ b/iologindata.cpp @@ -43,55 +43,92 @@ extern Game g_game; Account IOLoginData::loadAccount(uint32_t accountId, bool preLoad/* = false*/) { + Database* db = Database::getInstance(); + DBQuery query; + + query << "SELECT `name`, `password`, `salt`, `premdays`, `lastday`, `key`, `warnings` FROM `accounts` WHERE `id` = " << accountId << " LIMIT 1"; + DBResult* result; + if(!(result = db->storeQuery(query.str()))) + return Account(); + Account account; + account.number = accountId; + account.name = result->getDataString("name"); + account.password = result->getDataString("password"); + account.salt = result->getDataString("salt"); + account.premiumDays = std::max((int32_t)0, std::min((int32_t)GRATIS_PREMIUM, result->getDataInt("premdays"))); + account.lastDay = result->getDataInt("lastday"); + account.recoveryKey = result->getDataString("key"); + account.warnings = result->getDataInt("warnings"); + + result->free(); + if(!preLoad) + loadCharacters(account); + + return account; +} + +bool IOLoginData::loadAccount(Account& account, const std::string& name) +{ Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `id`, `name`, `password`, `premdays`, `lastday`, `key`, `warnings` FROM `accounts` WHERE `id` = " << accountId << " LIMIT 1"; + query << "SELECT `id`, `password`, `salt`, `premdays`, `lastday`, `key`, `warnings` FROM `accounts` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) - return account; + return false; account.number = result->getDataInt("id"); - account.name = result->getDataString("name"); + account.name = name; account.password = result->getDataString("password"); - account.premiumDays = result->getDataInt("premdays"); + account.salt = result->getDataString("salt"); + account.premiumDays = std::max((int32_t)0, std::min((int32_t)GRATIS_PREMIUM, result->getDataInt("premdays"))); account.lastDay = result->getDataInt("lastday"); account.recoveryKey = result->getDataString("key"); account.warnings = result->getDataInt("warnings"); - query.str(""); result->free(); - if(preLoad) - return account; + loadCharacters(account); + return true; +} + +void IOLoginData::loadCharacters(Account& account) +{ + Database* db = Database::getInstance(); + DBQuery query; #ifndef __LOGIN_SERVER__ - query << "SELECT `name` FROM `players` WHERE `account_id` = " << accountId << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " AND `deleted` = 0"; + query << "SELECT `name` FROM `players` WHERE `account_id` = " << account.number << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " AND `deleted` = 0"; #else - query << "SELECT `name`, `world_id` FROM `players` WHERE `account_id` = " << accountId << " AND `deleted` = 0"; + query << "SELECT `id`, `name`, `world_id`, `online` FROM `players` WHERE `account_id` = " << account.number << " AND `deleted` = 0"; #endif + DBResult* result; if(!(result = db->storeQuery(query.str()))) - return account; + return; do { - std::string ss = result->getDataString("name"); #ifndef __LOGIN_SERVER__ - account.charList.push_back(ss.c_str()); + account.charList.push_back(result->getDataString("name")); #else - if(GameServer* server = GameServers::getInstance()->getServerById(result->getDataInt("world_id"))) - account.charList[ss] = server; + std::string name = result->getDataString("name"); + if(hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges, result->getDataInt("id"))) + { + uint16_t hax = 0; + for(GameServersMap::const_iterator it = GameServers::getInstance()->getFirstServer(); it != GameServers::getInstance()->getLastServer(); ++it, ++hax) + account.charList[name + asString(hax)] = Character(name, it->second, -1); + } + else if(GameServer* srv = GameServers::getInstance()->getServerById(result->getDataInt("world_id"))) + account.charList[name] = Character(name, srv, result->getDataInt("online")); else - std::cout << "[Warning - IOLoginData::loadAccount] Invalid server for player '" << ss << "'." << std::endl; + std::clog << "[Warning - IOLoginData::loadAccount] Invalid server for player '" << name << "'." << std::endl; #endif } while(result->next()); result->free(); #ifndef __LOGIN_SERVER__ - account.charList.sort(); #endif - return account; } bool IOLoginData::saveAccount(Account account) @@ -99,7 +136,7 @@ bool IOLoginData::saveAccount(Account account) Database* db = Database::getInstance(); DBQuery query; query << "UPDATE `accounts` SET `premdays` = " << account.premiumDays << ", `warnings` = " << account.warnings << ", `lastday` = " << account.lastDay << " WHERE `id` = " << account.number << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + return db->query(query.str()); } bool IOLoginData::getAccountId(const std::string& name, uint32_t& number) @@ -109,7 +146,7 @@ bool IOLoginData::getAccountId(const std::string& name, uint32_t& number) Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `id` FROM `accounts` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " LIMIT 1"; + query << "SELECT `id` FROM `accounts` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -175,7 +212,7 @@ bool IOLoginData::hasFlag(PlayerFlags value, const std::string& accName) { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `group_id` FROM `accounts` WHERE `name` " << db->getStringComparison() << db->escapeString(accName) << " LIMIT 1"; + query << "SELECT `group_id` FROM `accounts` WHERE `name` " << db->getStringComparer() << db->escapeString(accName) << " LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -190,7 +227,7 @@ bool IOLoginData::hasCustomFlag(PlayerCustomFlags value, const std::string& accN { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `group_id` FROM `accounts` WHERE `name` " << db->getStringComparison() << db->escapeString(accName) << " LIMIT 1"; + query << "SELECT `group_id` FROM `accounts` WHERE `name` " << db->getStringComparer() << db->escapeString(accName) << " LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -205,7 +242,7 @@ bool IOLoginData::accountIdExists(uint32_t accountId) { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `id` FROM `accounts` WHERE `id` = " << accountId << " LIMIT 1"; + query << "SELECT 1 FROM `accounts` WHERE `id` = " << accountId << " LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -219,7 +256,7 @@ bool IOLoginData::accountNameExists(const std::string& name) { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `id` FROM `accounts` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " LIMIT 1"; + query << "SELECT 1 FROM `accounts` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -229,27 +266,26 @@ bool IOLoginData::accountNameExists(const std::string& name) return true; } -bool IOLoginData::getPassword(uint32_t accountId, std::string& password, std::string name/* = ""*/) +bool IOLoginData::getPassword(uint32_t accountId, std::string& password, std::string& salt, std::string name/* = ""*/) { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `password` FROM `accounts` WHERE `id` = " << accountId << " LIMIT 1"; + query << "SELECT `password`, `salt` FROM `accounts` WHERE `id` = " << accountId << " LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) return false; + std::string tmpPassword = result->getDataString("password"), tmpSalt = result->getDataString("salt"); + result->free(); if(name.empty() || name == "Account Manager") { - password = result->getDataString("password"); - result->free(); + password = tmpPassword; + salt = tmpSalt; return true; } - std::string tmpPassword = result->getDataString("password"); - result->free(); query.str(""); - query << "SELECT `name` FROM `players` WHERE `account_id` = " << accountId; if(!(result = db->storeQuery(query.str()))) return false; @@ -260,6 +296,8 @@ bool IOLoginData::getPassword(uint32_t accountId, std::string& password, std::st continue; password = tmpPassword; + salt = tmpSalt; + result->free(); return true; } @@ -270,12 +308,19 @@ bool IOLoginData::getPassword(uint32_t accountId, std::string& password, std::st bool IOLoginData::setPassword(uint32_t accountId, std::string newPassword) { - _encrypt(newPassword, false); - Database* db = Database::getInstance(); + std::string salt; + if(g_config.getBool(ConfigManager::GENERATE_ACCOUNT_SALT)) + { + salt = generateRecoveryKey(2, 19, true); + newPassword = salt + newPassword; + } + Database* db = Database::getInstance(); DBQuery query; - query << "UPDATE `accounts` SET `password` = " << db->escapeString(newPassword) << " WHERE `id` = " << accountId << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + + _encrypt(newPassword, false); + query << "UPDATE `accounts` SET `password` = " << db->escapeString(newPassword) << ", `salt` = "<< db->escapeString(salt) << " WHERE `id` = " << accountId << db->getUpdateLimiter(); + return db->query(query.str()); } bool IOLoginData::validRecoveryKey(uint32_t accountId, std::string recoveryKey) @@ -284,8 +329,8 @@ bool IOLoginData::validRecoveryKey(uint32_t accountId, std::string recoveryKey) Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `id` FROM `accounts` WHERE `id` = " << accountId << " AND `key` "; - query << db->getStringComparison() << db->escapeString(recoveryKey) << " LIMIT 1"; + query << "SELECT `id` FROM `accounts` WHERE `id` = " << accountId << " AND `key` " + << db->getStringComparer() << db->escapeString(recoveryKey) << " LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -302,43 +347,64 @@ bool IOLoginData::setRecoveryKey(uint32_t accountId, std::string newRecoveryKey) DBQuery query; query << "UPDATE `accounts` SET `key` = " << db->escapeString(newRecoveryKey) << " WHERE `id` = " << accountId << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + return db->query(query.str()); } uint64_t IOLoginData::createAccount(std::string name, std::string password) { + std::string salt = generateRecoveryKey(2, 19, true); + password = salt + password; _encrypt(password, false); - Database* db = Database::getInstance(); + Database* db = Database::getInstance(); DBQuery query; - query << "INSERT INTO `accounts` (`id`, `name`, `password`) VALUES (NULL, " << db->escapeString(name) << ", " << db->escapeString(password) << ")"; - if(!db->executeQuery(query.str())) + + query << "INSERT INTO `accounts` (`id`, `name`, `password`, `salt`) VALUES (NULL, " << db->escapeString(name) << ", " << db->escapeString(password) << ", " << db->escapeString(salt) << ")"; + if(!db->query(query.str())) return 0; return db->getLastInsertId(); } -void IOLoginData::removePremium(Account account) +void IOLoginData::removePremium(Account& account) { + bool save = false; uint64_t timeNow = time(NULL); - if(account.premiumDays > 0 && account.premiumDays < 65535) + if(account.premiumDays != 0 && account.premiumDays != (uint16_t)GRATIS_PREMIUM) { - uint32_t days = (uint32_t)std::ceil((timeNow - account.lastDay) / 86400); - if(days > 0) + if(account.lastDay == 0) { - if(account.premiumDays >= days) - account.premiumDays -= days; - else - account.premiumDays = 0; - account.lastDay = timeNow; + save = true; + } + else + { + uint32_t days = (timeNow - account.lastDay) / 86400; + if(days > 0) + { + if(account.premiumDays >= days) + { + account.premiumDays -= days; + uint32_t remainder = (timeNow - account.lastDay) % 86400; + account.lastDay = timeNow - remainder; + } + else + { + account.premiumDays = 0; + account.lastDay = 0; + } + save = true; + } } } - else - account.lastDay = timeNow; + else if(account.lastDay != 0) + { + account.lastDay = 0; + save = true; + } - if(!saveAccount(account)) - std::cout << "> ERROR: Failed to save account: " << account.name << "!" << std::endl; + if(save && !saveAccount(account)) + std::clog << "> ERROR: Failed to save account: " << account.name << "!" << std::endl; } const Group* IOLoginData::getPlayerGroupByAccount(uint32_t accountId) @@ -360,14 +426,13 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `id`, `account_id`, `group_id`, `world_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, "; - query << "`health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, "; - query << "`lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, "; - query << "`lastlogout`, `lastip`, `conditions`, `skull`, `skulltime`, `guildnick`, `rank_id`, `town_id`, "; - query << "`balance`, `stamina`, `direction`, `loss_experience`, `loss_mana`, `loss_skills`, `loss_containers`, "; - query << "`loss_items`, `marriage`, `promotion`, `description` FROM `players` WHERE `name` "; - query << db->getStringComparison() << db->escapeString(name) << " AND `world_id` = "; - query << g_config.getNumber(ConfigManager::WORLD_ID) << " AND `deleted` = 0 LIMIT 1"; + query << "SELECT `id`, `account_id`, `group_id`, `world_id`, `sex`, `vocation`, `experience`, `level`, " + << "`maglevel`, `health`, `healthmax`, `blessings`, `pvp_blessing`, `mana`, `manamax`, `manaspent`, `soul`, " + << "`lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `lookmount`, `posx`, `posy`, " + << "`posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skull`, `skulltime`, `guildnick`, " + << "`rank_id`, `town_id`, `balance`, `stamina`, `direction`, `loss_experience`, `loss_mana`, `loss_skills`, " + << "`loss_containers`, `loss_items`, `marriage`, `promotion`, `description`, `save` FROM `players` WHERE " + << "`name` " << db->getStringComparer() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -380,13 +445,19 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo return false; } + Group* group = Groups::getInstance()->getGroup(result->getDataInt("group_id")); + if(!group->hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges) && + result->getDataInt("world_id") != g_config.getNumber(ConfigManager::WORLD_ID)) + { + result->free(); + return false; + } + Account account = loadAccount(accountId, true); - player->accountId = accountId; player->account = account.name; + player->accountId = accountId; - Group* group = Groups::getInstance()->getGroup(result->getDataInt("group_id")); player->setGroup(group); - player->setGUID(result->getDataInt("id")); player->premiumDays = account.premiumDays; @@ -405,25 +476,28 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo player->setDirection((Direction)result->getDataInt("direction")); player->level = std::max((uint32_t)1, (uint32_t)result->getDataInt("level")); - uint64_t currExpCount = Player::getExpForLevel(player->level); - uint64_t nextExpCount = Player::getExpForLevel(player->level + 1); - uint64_t experience = (uint64_t)result->getDataLong("experience"); + uint64_t currExpCount = Player::getExpForLevel(player->level), nextExpCount = Player::getExpForLevel( + player->level + 1), experience = (uint64_t)result->getDataLong("experience"); if(experience < currExpCount || experience > nextExpCount) experience = currExpCount; player->experience = experience; + player->levelPercent = 0; if(currExpCount < nextExpCount) player->levelPercent = Player::getPercentLevel(player->experience - currExpCount, nextExpCount - currExpCount); - else - player->levelPercent = 0; player->soul = result->getDataInt("soul"); player->capacity = result->getDataInt("cap"); player->setStamina(result->getDataLong("stamina")); - player->balance = result->getDataLong("balance"); player->marriage = result->getDataInt("marriage"); - if(player->isPremium() || !g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM)) + + player->balance = result->getDataLong("balance"); + if(g_config.getBool(ConfigManager::BLESSINGS) && (player->isPremium() + || !g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM))) + { player->blessings = result->getDataInt("blessings"); + player->setPVPBlessing(result->getDataInt("pvp_blessing") != 0); + } uint64_t conditionsSize = 0; const char* conditions = result->getDataStream("conditions", conditionsSize); @@ -440,7 +514,7 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo delete condition; } - player->vocation_id = result->getDataInt("vocation"); + player->vocationId = result->getDataInt("vocation"); player->setPromotionLevel(result->getDataInt("promotion")); player->health = result->getDataInt("health"); @@ -449,8 +523,7 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo player->manaMax = result->getDataInt("manamax"); player->magLevel = result->getDataInt("maglevel"); - uint64_t nextManaCount = player->vocation->getReqMana(player->magLevel + 1); - uint64_t manaSpent = result->getDataLong("manaspent"); + uint64_t nextManaCount = player->vocation->getReqMana(player->magLevel + 1), manaSpent = result->getDataLong("manaspent"); if(manaSpent > nextManaCount) manaSpent = 0; @@ -470,7 +543,7 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo player->defaultOutfit.lookType = outfit.lookType; } - if(!wearable) //Just pick the first default outfit we can find + if(!wearable) //just pick the first default outfit we can find { const OutfitMap& defaultOutfits = Outfits::getInstance()->getOutfits(player->getSex(true)); if(!defaultOutfits.empty()) @@ -489,12 +562,18 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo player->defaultOutfit.lookFeet = result->getDataInt("lookfeet"); player->defaultOutfit.lookAddons = result->getDataInt("lookaddons"); + int32_t lookMount = result->getDataInt("lookmount"); + player->defaultOutfit.lookMount = (lookMount & 0xFFFF); + player->mounted = ((lookMount & 0xFFFF0000) != 0); + player->currentOutfit = player->defaultOutfit; Skulls_t skull = SKULL_RED; if(g_config.getBool(ConfigManager::USE_BLACK_SKULL)) skull = (Skulls_t)result->getDataInt("skull"); player->setSkullEnd((time_t)result->getDataInt("skulltime"), true, skull); + player->saving = result->getDataInt("save") != 0; + player->town = result->getDataInt("town_id"); if(Town* town = Towns::getInstance()->getTown(player->town)) player->setMasterPosition(town->getPosition()); @@ -505,13 +584,12 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo player->setLossPercent(LOSS_CONTAINERS, result->getDataInt("loss_containers")); player->setLossPercent(LOSS_ITEMS, result->getDataInt("loss_items")); - player->loginPosition = Position(result->getDataInt("posx"), result->getDataInt("posy"), result->getDataInt("posz")); player->lastLogin = result->getDataLong("lastlogin"); player->lastLogout = result->getDataLong("lastlogout"); player->lastIP = result->getDataInt("lastip"); - Position loginPos = player->loginPosition; - if(!loginPos.x || !loginPos.y) + player->loginPosition = Position(result->getDataInt("posx"), result->getDataInt("posy"), result->getDataInt("posz")); + if(!player->loginPosition.x || !player->loginPosition.y) player->loginPosition = player->getMasterPosition(); const uint32_t rankId = result->getDataInt("rank_id"); @@ -524,14 +602,40 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo query << "SELECT `guild_ranks`.`name` AS `rank`, `guild_ranks`.`guild_id` AS `guildid`, `guild_ranks`.`level` AS `level`, `guilds`.`name` AS `guildname` FROM `guild_ranks`, `guilds` WHERE `guild_ranks`.`id` = " << rankId << " AND `guild_ranks`.`guild_id` = `guilds`.`id` LIMIT 1"; if((result = db->storeQuery(query.str()))) { + player->guildId = result->getDataInt("guildid"); player->guildName = result->getDataString("guildname"); player->guildLevel = (GuildLevel_t)result->getDataInt("level"); - player->guildId = result->getDataInt("guildid"); - player->rankName = result->getDataString("rank"); + player->rankId = rankId; + player->rankName = result->getDataString("rank"); player->guildNick = nick; - result->free(); + + query.str(""); + query << "SELECT `id`, `guild_id`, `enemy_id` FROM `guild_wars` WHERE (`guild_id` = " + << player->guildId << " OR `enemy_id` = " << player->guildId << ") AND `status` IN (1,4)"; + if((result = db->storeQuery(query.str()))) + { + War_t war; + do + { + uint32_t guild = result->getDataInt("guild_id"); + if(player->guildId == guild) + { + war.type = WAR_ENEMY; + war.war = result->getDataInt("id"); + player->addEnemy(result->getDataInt("enemy_id"), war); + } + else + { + war.type = WAR_GUILD; + war.war = result->getDataInt("id"); + player->addEnemy(guild, war); + } + } + while(result->next()); + result->free(); + } } } else if(g_config.getBool(ConfigManager::INGAME_GUILD_MANAGEMENT)) @@ -541,7 +645,7 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo if((result = db->storeQuery(query.str()))) { do - player->invitedToGuildsList.push_back((uint32_t)result->getDataInt("guild_id")); + player->invitationsList.push_back((uint32_t)result->getDataInt("guild_id")); while(result->next()); result->free(); } @@ -618,8 +722,8 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo } } - result->free(); itemMap.clear(); + result->free(); } //load depot items @@ -639,24 +743,21 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo if(Depot* depot = c->getDepot()) player->addDepot(depot, pid); else - std::cout << "[Error - IOLoginData::loadPlayer] Cannot load depot " << pid << " for player " << name << std::endl; + std::clog << "[Error - IOLoginData::loadPlayer] Cannot load depot " << pid << " for player " << name << std::endl; } else - std::cout << "[Error - IOLoginData::loadPlayer] Cannot load depot " << pid << " for player " << name << std::endl; + std::clog << "[Error - IOLoginData::loadPlayer] Cannot load depot " << pid << " for player " << name << std::endl; } - else + else if((it = itemMap.find(pid)) != itemMap.end()) { - it = itemMap.find(pid); - if(it != itemMap.end()) - { - if(Container* container = it->second.first->getContainer()) - container->__internalAddThing(item); - } + if(Container* container = it->second.first->getContainer()) + container->__internalAddThing(item); } } - result->free(); + player->updateDepots(); itemMap.clear(); + result->free(); } //load storage map @@ -665,7 +766,7 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo if((result = db->storeQuery(query.str()))) { do - player->setStorage((uint32_t)result->getDataInt("key"), result->getDataString("value")); + player->setStorage(result->getDataString("key"), result->getDataString("value")); while(result->next()); result->free(); } @@ -679,17 +780,55 @@ bool IOLoginData::loadPlayer(Player* player, const std::string& name, bool preLo if((result = db->storeQuery(query.str()))) { - std::string dummy; do { uint32_t vid = result->getDataInt("vip"); if(storeNameByGuid(vid)) - player->addVIP(vid, dummy, false, true); + player->addVIP(vid, "", false, true); } while(result->next()); result->free(); } + query.str(""); + query << "SELECT `pk`.`player_id`, `pd`.`date` FROM `player_killers` pk LEFT JOIN `killers` k" + << " ON `pk`.`kill_id` = `k`.`id` LEFT JOIN `player_deaths` pd ON `k`.`death_id` = `pd`.`id`" + << " WHERE `pd`.`player_id` = " << player->getGUID() << " AND `k`.`unjustified` = 1 AND " + << "`pd`.`date` >= " << (time(NULL) - (7 * 86400)) << " AND `k`.`war` = 0"; // TODO: configurable + + std::map deaths; + if((result = db->storeQuery(query.str()))) + { + do + { + if(!deaths[result->getDataInt("player_id")] || deaths[result->getDataInt("player_id")] + < (time_t)result->getDataInt("date")) // pick up the latest date + deaths[result->getDataInt("player_id")] = (time_t)result->getDataInt("date"); + } + while(result->next()); + result->free(); + } + + if(!deaths.empty()) + { + query.str(""); + query << "SELECT `pd`.`player_id`, `pd`.`date` FROM `player_killers` pk LEFT JOIN `killers` k" + << " ON `pk`.`kill_id` = `k`.`id` LEFT JOIN `player_deaths` pd ON `k`.`death_id` = `pd`.`id`" + << " WHERE `pk`.`player_id` = " << player->getGUID() << " AND `k`.`unjustified` = 0 AND " + << "`pd`.`date` >= " << (time(NULL) - (7 * 86400)) << " AND `k`.`war` = 0"; // TODO: configurable, same as up + if((result = db->storeQuery(query.str()))) + { + do + { + if(!deaths[result->getDataInt("player_id")] || deaths[result->getDataInt("player_id")] + >= (time_t)result->getDataInt("date")) + player->addRevenge(result->getDataInt("player_id")); + } + while(result->next()); + result->free(); + } + } + player->updateInventoryWeight(); player->updateItemsLight(true); player->updateBaseSpeed(); @@ -708,7 +847,7 @@ void IOLoginData::loadItems(ItemMap& itemMap, DBResult* result) if(Item* item = Item::CreateItem(result->getDataInt("itemtype"), result->getDataInt("count"))) { if(!item->unserializeAttr(propStream)) - std::cout << "[Warning - IOLoginData::loadItems] Unserialize error for item with id " << item->getID() << std::endl; + std::clog << "[Warning - IOLoginData::loadItems] Unserialize error for item with id " << item->getID() << std::endl; itemMap[result->getDataInt("sid")] = std::make_pair(item, result->getDataInt("pid")); } @@ -731,17 +870,8 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall player->mana = player->manaMax; } } - Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `save` FROM `players` WHERE `id` = " << player->getGUID() << " LIMIT 1"; - - DBResult* result; - if(!(result = db->storeQuery(query.str()))) - return false; - - const bool save = result->getDataInt("save"); - result->free(); DBTransaction trans(db); if(!trans.begin()) @@ -749,10 +879,10 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall query.str(""); query << "UPDATE `players` SET `lastlogin` = " << player->lastLogin << ", `lastip` = " << player->lastIP; - if(!save || !player->isSaving()) + if(!player->isSaving() || !g_config.getBool(ConfigManager::SAVE_PLAYER_DATA)) { query << " WHERE `id` = " << player->getGUID() << db->getUpdateLimiter(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; return trans.commit(); @@ -770,6 +900,7 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall query << "`looklegs` = " << (uint32_t)player->defaultOutfit.lookLegs << ", "; query << "`looktype` = " << (uint32_t)player->defaultOutfit.lookType << ", "; query << "`lookaddons` = " << (uint32_t)player->defaultOutfit.lookAddons << ", "; + query << "`lookmount` = " << (uint32_t)(player->defaultOutfit.lookMount | ((uint32_t)(player->isMounted()) << 16)) << ", "; query << "`maglevel` = " << player->magLevel << ", "; query << "`mana` = " << player->mana << ", "; query << "`manamax` = " << player->manaMax << ", "; @@ -783,16 +914,13 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall query << "`sex` = " << player->sex << ", "; query << "`balance` = " << player->balance << ", "; query << "`stamina` = " << player->getStamina() << ", "; - if(g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) - { - Skulls_t skull = SKULL_RED; - if(g_config.getBool(ConfigManager::USE_BLACK_SKULL)) - skull = player->getSkull(); - query << "`skull` = " << skull << ", "; - query << "`skulltime` = " << player->getSkullEnd() << ", "; - } + Skulls_t skull = SKULL_RED; + if(g_config.getBool(ConfigManager::USE_BLACK_SKULL)) + skull = player->getSkull(); + query << "`skull` = " << skull << ", "; + query << "`skulltime` = " << player->getSkullEnd() << ", "; query << "`promotion` = " << player->promotionLevel << ", "; if(g_config.getBool(ConfigManager::STORE_DIRECTION)) query << "`direction` = " << (uint32_t)player->getDirection() << ", "; @@ -813,7 +941,7 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall if(!(*it)->serialize(propWriteStream)) return false; - propWriteStream.ADD_UCHAR(CONDITIONATTR_END); + propWriteStream.addByte(CONDITIONATTR_END); } } @@ -828,8 +956,12 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall query << "`loss_items` = " << (uint32_t)player->getLossPercent(LOSS_ITEMS) << ", "; query << "`lastlogout` = " << player->getLastLogout() << ", "; - if(player->isPremium() || !g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM)) + if(g_config.getBool(ConfigManager::BLESSINGS) && (player->isPremium() + || !g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM))) + { query << "`blessings` = " << player->blessings << ", "; + query << "`pvp_blessing` = " << (player->hasPVPBlessing() ? "1" : "0") << ", "; + } query << "`marriage` = " << player->marriage << ", "; if(g_config.getBool(ConfigManager::INGAME_GUILD_MANAGEMENT)) @@ -843,7 +975,7 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall tmpVoc = Vocations::getInstance()->getVocation(tmpVoc->getFromVocation()); query << "`vocation` = " << tmpVoc->getId() << " WHERE `id` = " << player->getGUID() << db->getUpdateLimiter(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; // skills @@ -851,7 +983,7 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall { query.str(""); query << "UPDATE `player_skills` SET `value` = " << player->skills[i][SKILL_LEVEL] << ", `count` = " << player->skills[i][SKILL_TRIES] << " WHERE `player_id` = " << player->getGUID() << " AND `skillid` = " << i << db->getUpdateLimiter(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; } @@ -861,27 +993,29 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall // learned spells query.str(""); query << "DELETE FROM `player_spells` WHERE `player_id` = " << player->getGUID(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; - char buffer[280]; - DBInsert query_insert(db); - - query_insert.setQuery("INSERT INTO `player_spells` (`player_id`, `name`) VALUES "); - for(LearnedInstantSpellList::const_iterator it = player->learnedInstantSpellList.begin(); it != player->learnedInstantSpellList.end(); ++it) + DBInsert stmt(db); + if(player->learnedInstantSpellList.size()) { - sprintf(buffer, "%d, %s", player->getGUID(), db->escapeString(*it).c_str()); - if(!query_insert.addRow(buffer)) + query.str(""); + stmt.setQuery("INSERT INTO `player_spells` (`player_id`, `name`) VALUES "); + for(LearnedInstantSpellList::const_iterator it = player->learnedInstantSpellList.begin(); it != player->learnedInstantSpellList.end(); ++it) + { + query << player->getGUID() << "," << db->escapeString(*it); + if(!stmt.addRow(query)) + return false; + } + + if(!stmt.execute()) return false; } - if(!query_insert.execute()) - return false; - //item saving query.str(""); query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; ItemBlockList itemList; @@ -891,8 +1025,8 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall itemList.push_back(itemBlock(slotId, item)); } - query_insert.setQuery("INSERT INTO `player_items` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); - if(!saveItems(player, itemList, query_insert)) + stmt.setQuery("INSERT INTO `player_items` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); + if(!saveItems(player, itemList, stmt)) return false; itemList.clear(); @@ -912,92 +1046,106 @@ bool IOLoginData::savePlayer(Player* player, bool preSave/* = true*/, bool shall size_t size = s.length(); if(size > 0) {*/ + query.str(""); query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID();// << " AND `pid` IN (" << s.substr(0, --size) << ")"; - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; - query_insert.setQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); - if(!saveItems(player, itemList, query_insert)) - return false; + if(itemList.size()) + { + stmt.setQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); + if(!saveItems(player, itemList, stmt)) + return false; - itemList.clear(); + itemList.clear(); + } //} query.str(""); query << "DELETE FROM `player_storage` WHERE `player_id` = " << player->getGUID(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; player->generateReservedStorage(); - query_insert.setQuery("INSERT INTO `player_storage` (`player_id`, `key`, `value`) VALUES "); + query.str(""); + + stmt.setQuery("INSERT INTO `player_storage` (`player_id`, `key`, `value`) VALUES "); for(StorageMap::const_iterator cit = player->getStorageBegin(); cit != player->getStorageEnd(); ++cit) { - sprintf(buffer, "%u, %u, %s", player->getGUID(), cit->first, db->escapeString(cit->second).c_str()); - if(!query_insert.addRow(buffer)) + query << player->getGUID() << "," << db->escapeString(cit->first) << "," << db->escapeString(cit->second); + if(!stmt.addRow(query)) return false; } - if(!query_insert.execute()) + if(!stmt.execute()) return false; if(g_config.getBool(ConfigManager::INGAME_GUILD_MANAGEMENT)) { - //save guild invites query.str(""); + //save guild invites query << "DELETE FROM `guild_invites` WHERE player_id = " << player->getGUID(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; - query_insert.setQuery("INSERT INTO `guild_invites` (`player_id`, `guild_id`) VALUES "); - for(InvitedToGuildsList::const_iterator it = player->invitedToGuildsList.begin(); it != player->invitedToGuildsList.end(); ++it) + query.str(""); + stmt.setQuery("INSERT INTO `guild_invites` (`player_id`, `guild_id`) VALUES "); + for(InvitationsList::iterator it = player->invitationsList.begin(); it != player->invitationsList.end(); ++it) { - sprintf(buffer, "%d, %d", player->getGUID(), *it); - if(!query_insert.addRow(buffer)) + if(!IOGuild::getInstance()->guildExists(*it)) + { + it = player->invitationsList.erase(it); + continue; + } + + query << player->getGUID() << "," << (*it); + if(!stmt.addRow(query)) return false; } - if(!query_insert.execute()) + if(!stmt.execute()) return false; } - //save vip list- FIXME: merge it to one config query? query.str(""); + //save vip list if(!g_config.getBool(ConfigManager::VIPLIST_PER_PLAYER)) query << "DELETE FROM `account_viplist` WHERE `account_id` = " << player->getAccount() << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); else query << "DELETE FROM `player_viplist` WHERE `player_id` = " << player->getGUID(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; if(!g_config.getBool(ConfigManager::VIPLIST_PER_PLAYER)) - query_insert.setQuery("INSERT INTO `account_viplist` (`account_id`, `world_id`, `player_id`) VALUES "); + stmt.setQuery("INSERT INTO `account_viplist` (`account_id`, `world_id`, `player_id`) VALUES "); else - query_insert.setQuery("INSERT INTO `player_viplist` (`player_id`, `vip_id`) VALUES "); + stmt.setQuery("INSERT INTO `player_viplist` (`player_id`, `vip_id`) VALUES "); - for(VIPListSet::iterator it = player->VIPList.begin(); it != player->VIPList.end(); it++) + query.str(""); + for(VIPSet::iterator it = player->VIPList.begin(); it != player->VIPList.end(); ++it) { if(!playerExists(*it, false, false)) continue; if(!g_config.getBool(ConfigManager::VIPLIST_PER_PLAYER)) - sprintf(buffer, "%d, %d, %d", player->getAccount(), g_config.getNumber(ConfigManager::WORLD_ID), *it); + query << player->getAccount() << "," << g_config.getNumber(ConfigManager::WORLD_ID) << "," << (*it); else - sprintf(buffer, "%d, %d", player->getGUID(), *it); + query << player->getGUID() << "," << (*it); - if(!query_insert.addRow(buffer)) + if(!stmt.addRow(query)) return false; } - if(!query_insert.execute()) + if(!stmt.execute()) return false; //End the transaction return trans.commit(); } -bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert) +bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& stmt) { Database* db = Database::getInstance(); typedef std::pair Stack; @@ -1008,24 +1156,23 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, for(ItemBlockList::const_iterator it = itemList.begin(); it != itemList.end(); ++it, ++runningId) { item = it->second; - PropWriteStream propWriteStream; item->serializeAttr(propWriteStream); uint32_t attributesSize = 0; const char* attributes = propWriteStream.getStream(attributesSize); - char buffer[attributesSize * 3 + 100]; //MUST be (size * 2), else people can crash server when filling writable with native characters - sprintf(buffer, "%d, %d, %d, %d, %d, %s", player->getGUID(), it->first, runningId, item->getID(), - (int32_t)item->getSubType(), db->escapeBlob(attributes, attributesSize).c_str()); - if(!query_insert.addRow(buffer)) + std::stringstream buffer; + buffer << player->getGUID() << "," << it->first << "," << runningId << "," << item->getID() << "," + << (int32_t)item->getSubType() << "," << db->escapeBlob(attributes, attributesSize); + if(!stmt.addRow(buffer)) return false; if(Container* container = item->getContainer()) stackList.push_back(Stack(container, runningId)); } - while(stackList.size() > 0) + while(stackList.size()) { Stack stack = stackList.front(); stackList.pop_front(); @@ -1042,19 +1189,33 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, uint32_t attributesSize = 0; const char* attributes = propWriteStream.getStream(attributesSize); - char buffer[attributesSize * 3 + 100]; //MUST be (size * 2), else people can crash server when filling writable with native characters - sprintf(buffer, "%d, %d, %d, %d, %d, %s", player->getGUID(), stack.second, runningId, item->getID(), - (int32_t)item->getSubType(), db->escapeBlob(attributes, attributesSize).c_str()); - if(!query_insert.addRow(buffer)) + std::stringstream buffer; + buffer << player->getGUID() << "," << stack.second << "," << runningId << "," << item->getID() << "," + << (int32_t)item->getSubType() << "," << db->escapeBlob(attributes, attributesSize); + if(!stmt.addRow(buffer)) return false; } } - return query_insert.execute(); + return stmt.execute(); +} + +bool IOLoginData::playerStatement(Player* _player, uint16_t channelId, const std::string& text, uint32_t& statementId) +{ + Database* db = Database::getInstance(); + DBQuery query; + + query << "INSERT INTO `player_statements` (`player_id`, `channel_id`, `text`, `date`) VALUES (" << _player->getGUID() + << ", " << channelId << ", " << db->escapeString(text) << ", " << time(NULL) << ")"; + if(!db->query(query.str())) + return false; + + statementId = db->getLastInsertId(); + return true; } -bool IOLoginData::playerDeath(Player* player, const DeathList& dl) +bool IOLoginData::playerDeath(Player* _player, const DeathList& dl) { Database* db = Database::getInstance(); DBQuery query; @@ -1063,22 +1224,25 @@ bool IOLoginData::playerDeath(Player* player, const DeathList& dl) if(!trans.begin()) return false; - query << "INSERT INTO `player_deaths` (`player_id`, `date`, `level`) VALUES (" << player->getGUID() - << ", " << time(NULL) << ", " << player->getLevel() << ")"; - if(!db->executeQuery(query.str())) + query << "INSERT INTO `player_deaths` (`player_id`, `date`, `level`) VALUES (" << _player->getGUID() + << ", " << time(NULL) << ", " << _player->getLevel() << ")"; + if(!db->query(query.str())) return false; - int32_t i = 0, size = dl.size(), tmp = g_config.getNumber(ConfigManager::DEATH_ASSISTS) + 1; - if(tmp > 0 && size > tmp) + uint32_t i = 0, size = dl.size(), tmp = g_config.getNumber(ConfigManager::DEATH_ASSISTS) + 1; + if(size > tmp) size = tmp; + DeathList wl; + bool war = false; + uint64_t deathId = db->getLastInsertId(); for(DeathList::const_iterator it = dl.begin(); i < size && it != dl.end(); ++it, ++i) { query.str(""); - query << "INSERT INTO `killers` (`death_id`, `final_hit`, `unjustified`) VALUES (" - << deathId << ", " << (it == dl.begin()) << ", " << it->isUnjustified() << ")"; - if(!db->executeQuery(query.str())) + query << "INSERT INTO `killers` (`death_id`, `final_hit`, `unjustified`, `war`) VALUES (" + << deathId << ", " << it->isLast() << ", " << it->isUnjustified() << ", " << it->getWar().war << ")"; + if(!db->query(query.str())) return false; std::string name; @@ -1095,10 +1259,13 @@ bool IOLoginData::playerDeath(Player* player, const DeathList& dl) if(player) { + if(_player->isEnemy(player, false)) + wl.push_back(*it); + query.str(""); query << "INSERT INTO `player_killers` (`kill_id`, `player_id`) VALUES (" << killId << ", " << player->getGUID() << ")"; - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; } else @@ -1112,11 +1279,14 @@ bool IOLoginData::playerDeath(Player* player, const DeathList& dl) query.str(""); query << "INSERT INTO `environment_killers` (`kill_id`, `name`) VALUES (" << killId << ", " << db->escapeString(name) << ")"; - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; } } + if(!wl.empty()) + IOGuild::getInstance()->frag(_player, deathId, wl, war); + return trans.commit(); } @@ -1130,7 +1300,11 @@ bool IOLoginData::playerMail(Creature* actor, std::string name, uint32_t townId, townId = player->getTown(); Depot* depot = player->getDepot(townId, true); - if(!depot || g_game.internalMoveItem(actor, item->getParent(), depot, INDEX_WHEREEVER, + Container* inbox = NULL; + if(!(inbox = depot->getInbox())) + inbox = depot; + + if(!inbox || g_game.internalMoveItem(actor, item->getParent(), inbox, INDEX_WHEREEVER, item, item->getItemCount(), NULL, FLAG_NOLIMIT) != RET_NOERROR) { if(player->isVirtual()) @@ -1149,7 +1323,7 @@ bool IOLoginData::playerMail(Creature* actor, std::string name, uint32_t townId, CreatureEventList mailEvents = player->getCreatureEvents(CREATURE_EVENT_MAIL_RECEIVE); for(CreatureEventList::iterator it = mailEvents.begin(); it != mailEvents.end(); ++it) { - if(!(*it)->executeMailReceive(player, tmp, item, opened) && result) + if(!(*it)->executeMail(player, tmp, item, opened) && result) result = false; } @@ -1158,14 +1332,14 @@ bool IOLoginData::playerMail(Creature* actor, std::string name, uint32_t townId, mailEvents = tmp->getCreatureEvents(CREATURE_EVENT_MAIL_SEND); for(CreatureEventList::iterator it = mailEvents.begin(); it != mailEvents.end(); ++it) { - if(!(*it)->executeMailSend(tmp, player, item, opened) && result) + if(!(*it)->executeMail(tmp, player, item, opened) && result) result = false; } } if(player->isVirtual()) { - IOLoginData::getInstance()->savePlayer(player); + savePlayer(player); delete player; } @@ -1176,7 +1350,7 @@ bool IOLoginData::hasFlag(const std::string& name, PlayerFlags value) { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `group_id` FROM `players` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; + query << "SELECT `group_id` FROM `players` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -1191,7 +1365,7 @@ bool IOLoginData::hasCustomFlag(const std::string& name, PlayerCustomFlags value { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `group_id` FROM `players` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; + query << "SELECT `group_id` FROM `players` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -1249,7 +1423,7 @@ bool IOLoginData::isPremium(uint32_t guid) const uint32_t account = result->getDataInt("account_id"); result->free(); - if(group && group->hasCustomFlag(PlayerFlag_IsAlwaysPremium)) + if(group && group->hasFlag(PlayerFlag_IsAlwaysPremium)) return true; query.str(""); @@ -1259,7 +1433,7 @@ bool IOLoginData::isPremium(uint32_t guid) const uint32_t premium = result->getDataInt("premdays"); result->free(); - return premium; + return premium > 0; } bool IOLoginData::playerExists(uint32_t guid, bool multiworld /*= false*/, bool checkCache /*= true*/) @@ -1305,7 +1479,7 @@ bool IOLoginData::playerExists(std::string& name, bool multiworld /*= false*/, b Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `id`, `name` FROM `players` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " AND `deleted` = 0"; + query << "SELECT `id`, `name` FROM `players` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " AND `deleted` = 0"; if(!multiworld) query << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); @@ -1381,7 +1555,7 @@ bool IOLoginData::getGuidByName(uint32_t& guid, std::string& name, bool multiwor Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `name`, `id` FROM `players` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " AND `deleted` = 0"; + query << "SELECT `name`, `id` FROM `players` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " AND `deleted` = 0"; if(!multiworld) query << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); @@ -1402,7 +1576,7 @@ bool IOLoginData::getGuidByNameEx(uint32_t& guid, bool &specialVip, std::string& { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `id`, `name`, `group_id` FROM `players` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " AND `deleted` = 0 AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " LIMIT 1"; + query << "SELECT `id`, `name`, `group_id` FROM `players` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " AND `deleted` = 0 AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -1424,12 +1598,12 @@ bool IOLoginData::changeName(uint32_t guid, std::string newName, std::string old query << "INSERT INTO `player_namelocks` (`player_id`, `name`, `new_name`, `date`) VALUES ("<< guid << ", " << db->escapeString(oldName) << ", " << db->escapeString(newName) << ", " << time(NULL) << ")"; - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; query.str(""); query << "UPDATE `players` SET `name` = " << db->escapeString(newName) << " WHERE `id` = " << guid << db->getUpdateLimiter(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; GuidCacheMap::iterator it = guidCacheMap.find(oldName); @@ -1445,8 +1619,15 @@ bool IOLoginData::changeName(uint32_t guid, std::string newName, std::string old bool IOLoginData::createCharacter(uint32_t accountId, std::string characterName, int32_t vocationId, uint16_t sex) { - if(playerExists(characterName)) + Database* db = Database::getInstance(); + DBQuery query; + query << "SELECT `id` FROM `players` WHERE `name` " << db->getStringComparer() << db->escapeString(characterName) << ";"; + DBResult* result = db->storeQuery(query.str()); + if(result) + { + result->free(); return false; + } Vocation* vocation = Vocations::getInstance()->getVocation(vocationId); Vocation* rookVoc = Vocations::getInstance()->getVocation(0); @@ -1474,21 +1655,19 @@ bool IOLoginData::createCharacter(uint32_t accountId, std::string characterName, } } - Database* db = Database::getInstance(); - DBQuery query; - + query.str(""); query << "INSERT INTO `players` (`id`, `name`, `world_id`, `group_id`, `account_id`, `level`, `vocation`, `health`, `healthmax`, `experience`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `maglevel`, `mana`, `manamax`, `manaspent`, `soul`, `town_id`, `posx`, `posy`, `posz`, `conditions`, `cap`, `sex`, `lastlogin`, `lastip`, `skull`, `skulltime`, `save`, `rank_id`, `guildnick`, `lastlogout`, `blessings`, `online`) VALUES (NULL, " << db->escapeString(characterName) << ", " << g_config.getNumber(ConfigManager::WORLD_ID) << ", 1, " << accountId << ", " << level << ", " << vocationId << ", " << healthMax << ", " << healthMax << ", " << exp << ", 68, 76, 78, 39, " << lookType << ", 0, " << g_config.getNumber(ConfigManager::START_MAGICLEVEL) << ", " << manaMax << ", " << manaMax << ", 0, 100, " << g_config.getNumber(ConfigManager::SPAWNTOWN_ID) << ", " << g_config.getNumber(ConfigManager::SPAWNPOS_X) << ", " << g_config.getNumber(ConfigManager::SPAWNPOS_Y) << ", " << g_config.getNumber(ConfigManager::SPAWNPOS_Z) << ", 0, " << capMax << ", " << sex << ", 0, 0, 0, 0, 1, 0, '', 0, 0, 0)"; - return db->executeQuery(query.str()); + return db->query(query.str()); } -DeleteCharacter_t IOLoginData::deleteCharacter(uint32_t accountId, const std::string characterName) +DeleteCharacter_t IOLoginData::deleteCharacter(uint32_t accountId, const std::string& characterName) { if(g_game.getPlayerByName(characterName)) return DELETE_ONLINE; Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `id` FROM `players` WHERE `name` " << db->getStringComparison() << db->escapeString(characterName) << " AND `account_id` = " << accountId << " AND `deleted` = 0 LIMIT 1"; + query << "SELECT `id` FROM `players` WHERE `name` " << db->getStringComparer() << db->escapeString(characterName) << " AND `account_id` = " << accountId << " AND `deleted` = 0 LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -1505,25 +1684,33 @@ DeleteCharacter_t IOLoginData::deleteCharacter(uint32_t accountId, const std::st return DELETE_LEADER; query.str(""); - query << "UPDATE `players` SET `deleted` = 1 WHERE `id` = " << id << db->getUpdateLimiter(); - if(!db->executeQuery(query.str())) + query << "UPDATE `players` SET `deleted` = " << time(NULL) << " WHERE `id` = " << id << db->getUpdateLimiter(); + if(!db->query(query.str())) return DELETE_INTERNAL; query.str(""); query << "DELETE FROM `guild_invites` WHERE `player_id` = " << id; - db->executeQuery(query.str()); + db->query(query.str()); query.str(""); query << "DELETE FROM `player_viplist` WHERE `vip_id` = " << id; - db->executeQuery(query.str()); + db->query(query.str()); for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) { - VIPListSet::iterator it_ = it->second->VIPList.find(id); + VIPSet::iterator it_ = it->second->VIPList.find(id); if(it_ != it->second->VIPList.end()) it->second->VIPList.erase(it_); } + GuidCacheMap::iterator it = guidCacheMap.find(characterName); + if(it != guidCacheMap.end()) + guidCacheMap.erase(it); + + NameCacheMap::iterator it2 = nameCacheMap.find(id); + if(it2 != nameCacheMap.end()) + nameCacheMap.erase(it2); + return DELETE_SUCCESS; } @@ -1561,7 +1748,7 @@ uint32_t IOLoginData::getLastIPByName(const std::string& name) const { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `lastip` FROM `players` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; + query << "SELECT `lastip` FROM `players` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -1576,7 +1763,7 @@ uint32_t IOLoginData::getAccountIdByName(const std::string& name) const { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `account_id` FROM `players` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; + query << "SELECT `account_id` FROM `players` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -1591,9 +1778,13 @@ bool IOLoginData::getUnjustifiedDates(uint32_t guid, std::vector& dateLi { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `pd`.`date` FROM `player_killers` pk LEFT JOIN `killers` k ON `pk`.`kill_id` = `k`.`id`"; - query << "LEFT JOIN `player_deaths` pd ON `k`.`death_id` = `pd`.`id` WHERE `pk`.`player_id` = " << guid; - query << " AND `k`.`unjustified` = 1 AND `pd`.`date` >= " << (_time - (30 * 86400)); + + uint32_t maxLength = std::max(g_config.getNumber(ConfigManager::FRAG_THIRD_LIMIT), + std::max(g_config.getNumber(ConfigManager::FRAG_SECOND_LIMIT), + g_config.getNumber(ConfigManager::FRAG_LIMIT))); + query << "SELECT `pd`.`date` FROM `player_killers` pk LEFT JOIN `killers` k ON `pk`.`kill_id` = `k`.`id`" + << "LEFT JOIN `player_deaths` pd ON `k`.`death_id` = `pd`.`id` WHERE `pk`.`player_id` = " << guid + << " AND `k`.`unjustified` = 1 AND `pd`.`date` >= " << (_time - maxLength) << " AND `k`.`war` = 0"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -1610,7 +1801,7 @@ bool IOLoginData::getDefaultTownByName(const std::string& name, uint32_t& townId { Database* db = Database::getInstance(); DBQuery query; - query << "SELECT `town_id` FROM `players` WHERE `name` " << db->getStringComparison() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; + query << "SELECT `town_id` FROM `players` WHERE `name` " << db->getStringComparer() << db->escapeString(name) << " AND `deleted` = 0 LIMIT 1"; DBResult* result; if(!(result = db->storeQuery(query.str()))) @@ -1635,8 +1826,9 @@ bool IOLoginData::updatePremiumDays() if(!(result = db->storeQuery(query.str()))) return false; + Account account; do - removePremium(loadAccount(result->getDataInt("id"), true)); + removePremium((account = loadAccount(result->getDataInt("id"), true))); while(result->next()); result->free(); @@ -1668,7 +1860,7 @@ bool IOLoginData::updateOnlineStatus(uint32_t guid, bool login) } query << "UPDATE `players` SET `online` = " << value << " WHERE `id` = " << guid << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + return db->query(query.str()); } bool IOLoginData::resetGuildInformation(uint32_t guid) @@ -1676,5 +1868,12 @@ bool IOLoginData::resetGuildInformation(uint32_t guid) Database* db = Database::getInstance(); DBQuery query; query << "UPDATE `players` SET `rank_id` = 0, `guildnick` = '' WHERE `id` = " << guid << " AND `deleted` = 0" << db->getUpdateLimiter(); - return db->executeQuery(query.str()); + return db->query(query.str()); +} + +void IOLoginData::increaseBankBalance(uint32_t guid, uint64_t bankBalance) +{ + DBQuery query; + query << "UPDATE `players` SET `balance` = `balance` + " << bankBalance << " WHERE `id` = " << guid << ";"; + Database::getInstance()->query(query.str()); } diff --git a/iologindata.h b/iologindata.h index 784ef0d..e8842a6 100644 --- a/iologindata.h +++ b/iologindata.h @@ -48,6 +48,7 @@ class IOLoginData } Account loadAccount(uint32_t accountId, bool preLoad = false); + bool loadAccount(Account& account, const std::string& name); bool saveAccount(Account account); bool getAccountId(const std::string& name, uint32_t& number); @@ -61,20 +62,21 @@ class IOLoginData bool accountIdExists(uint32_t accountId); bool accountNameExists(const std::string& name); - bool getPassword(uint32_t accountId, std::string& password, std::string name = ""); + bool getPassword(uint32_t accountId, std::string& password, std::string& salt, std::string name = ""); bool setPassword(uint32_t accountId, std::string newPassword); bool validRecoveryKey(uint32_t accountId, std::string recoveryKey); bool setRecoveryKey(uint32_t accountId, std::string newRecoveryKey); uint64_t createAccount(std::string name, std::string password); - void removePremium(Account account); + void removePremium(Account& account); const Group* getPlayerGroupByAccount(uint32_t accountId); bool loadPlayer(Player* player, const std::string& name, bool preLoad = false); bool savePlayer(Player* player, bool preSave = true, bool shallow = false); - bool playerDeath(Player* player, const DeathList& dl); + bool playerStatement(Player* _player, uint16_t channelId, const std::string& text, uint32_t& statementId); + bool playerDeath(Player* _player, const DeathList& dl); bool playerMail(Creature* actor, std::string name, uint32_t townId, Item* item); bool hasFlag(const std::string& name, PlayerFlags value); @@ -92,7 +94,7 @@ class IOLoginData bool changeName(uint32_t guid, std::string newName, std::string oldName); bool createCharacter(uint32_t accountId, std::string characterName, int32_t vocationId, uint16_t sex); - DeleteCharacter_t deleteCharacter(uint32_t accountId, const std::string characterName); + DeleteCharacter_t deleteCharacter(uint32_t accountId, const std::string& characterName); uint32_t getLevel(uint32_t guid) const; uint32_t getLastIP(uint32_t guid) const; @@ -107,14 +109,14 @@ class IOLoginData bool updateOnlineStatus(uint32_t guid, bool login); bool resetGuildInformation(uint32_t guid); + void increaseBankBalance(uint32_t guid, uint64_t bankBalance); + protected: IOLoginData() {} + struct StringCompareCase { - bool operator()(const std::string& l, const std::string& r) const - { - return strcasecmp(l.c_str(), r.c_str()) < 0; - } + bool operator()(const std::string& l, const std::string& r) const {return asLowerCaseString(l).compare(asLowerCaseString(r)) < 0;} }; typedef std::map GuidCacheMap; @@ -128,6 +130,7 @@ class IOLoginData bool saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert); void loadItems(ItemMap& itemMap, DBResult* result); + void loadCharacters(Account& account); bool storeNameByGuid(uint32_t guid); }; #endif diff --git a/iomap.cpp b/iomap.cpp index 097517f..02f812d 100644 --- a/iomap.cpp +++ b/iomap.cpp @@ -15,9 +15,7 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" - #include "iomap.h" -#include "fileloader.h" #include "map.h" #include "town.h" @@ -29,7 +27,11 @@ #include "teleport.h" #include "beds.h" +#include "fileloader.h" +#include "configmanager.h" #include "game.h" + +extern ConfigManager g_config; extern Game g_game; typedef uint8_t attribute_t; @@ -106,7 +108,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) } OTBM_root_header* rootHeader; - if(!propStream.GET_STRUCT(rootHeader)) + if(!propStream.getStruct(rootHeader)) { setLastErrorString("Could not read header."); return false; @@ -122,7 +124,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) return false; } - if(headerVersion > 2) + if(headerVersion > 3) { setLastErrorString("Unknown OTBM version detected."); return false; @@ -151,7 +153,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) if(headerMinorItems > (uint32_t)Items::dwMinorVersion) setLastErrorString("This map needs an updated items.otb."); - std::cout << "> Map size: " << rootHeader->width << "x" << rootHeader->height << "." << std::endl; + std::clog << "> Map size: " << rootHeader->width << "x" << rootHeader->height << "." << std::endl; map->mapWidth = rootHeader->width; map->mapHeight = rootHeader->height; @@ -170,13 +172,13 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) std::string tmp; uint8_t attribute; - while(propStream.GET_UCHAR(attribute)) + while(propStream.getByte(attribute)) { switch(attribute) { case OTBM_ATTR_DESCRIPTION: { - if(!propStream.GET_STRING(tmp)) + if(!propStream.getString(tmp)) { setLastErrorString("Invalid description tag."); return false; @@ -187,7 +189,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) } case OTBM_ATTR_EXT_SPAWN_FILE: { - if(!propStream.GET_STRING(tmp)) + if(!propStream.getString(tmp)) { setLastErrorString("Invalid spawnfile tag."); return false; @@ -199,7 +201,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) } case OTBM_ATTR_EXT_HOUSE_FILE: { - if(!propStream.GET_STRING(tmp)) + if(!propStream.getString(tmp)) { setLastErrorString("Invalid housefile tag."); return false; @@ -217,10 +219,15 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) } } - std::cout << "> Map descriptions: " << std::endl; + std::clog << "> Map descriptions: " << std::endl; for(StringVec::iterator it = map->descriptions.begin(); it != map->descriptions.end(); ++it) - std::cout << "\"" << (*it) << "\"" << std::endl; + std::clog << "\"" << (*it) << "\"" << std::endl; + +#ifdef __GROUND_CACHE__ + typedef std::map > CacheMap; + CacheMap groundCache; +#endif NODE nodeMapData = f.getChildNode(nodeMap, type); while(nodeMapData != NO_NODE) { @@ -238,14 +245,14 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) return false; } - OTBM_Destination_coords* area_coord; - if(!propStream.GET_STRUCT(area_coord)) + OTBM_Destination_coords* areaCoord; + if(!propStream.getStruct(areaCoord)) { setLastErrorString("Invalid map node."); return false; } - int32_t base_x = area_coord->_x, base_y = area_coord->_y, base_z = area_coord->_z; + int32_t baseX = areaCoord->_x, baseY = areaCoord->_y, baseZ = areaCoord->_z; NODE nodeTile = f.getChildNode(nodeMapData, type); while(nodeTile != NO_NODE) { @@ -264,7 +271,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) } OTBM_Tile_coords* tileCoord; - if(!propStream.GET_STRUCT(tileCoord)) + if(!propStream.getStruct(tileCoord)) { setLastErrorString("Could not read tile position."); return false; @@ -272,14 +279,14 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) Tile* tile = NULL; Item* ground = NULL; - uint32_t tileflags = 0; + uint32_t flags = 0; - uint16_t px = base_x + tileCoord->_x, py = base_y + tileCoord->_y, pz = base_z; + uint16_t px = baseX + tileCoord->_x, py = baseY + tileCoord->_y, pz = baseZ; House* house = NULL; if(type == OTBM_HOUSETILE) { - uint32_t _houseid; - if(!propStream.GET_ULONG(_houseid)) + uint32_t houseId; + if(!propStream.getLong(houseId)) { std::stringstream ss; ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Could not read house id."; @@ -288,11 +295,11 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) return false; } - house = Houses::getInstance()->getHouse(_houseid, true); + house = Houses::getInstance()->getHouse(houseId, true); if(!house) { std::stringstream ss; - ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Could not create house id: " << _houseid; + ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Could not create house id: " << houseId; setLastErrorString(ss.str()); return false; @@ -303,15 +310,15 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) } //read tile attributes - uint8_t attribute; - while(propStream.GET_UCHAR(attribute)) + uint8_t attribute = 0; + while(propStream.getByte(attribute)) { switch(attribute) { case OTBM_ATTR_TILE_FLAGS: { - uint32_t flags; - if(!propStream.GET_ULONG(flags)) + uint32_t _flags; + if(!propStream.getLong(_flags)) { std::stringstream ss; ss << "[x:" << px << ", y:" << py << ", z:" << pz << "] Failed to read tile flags."; @@ -320,22 +327,22 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) return false; } - if((flags & TILESTATE_PROTECTIONZONE) == TILESTATE_PROTECTIONZONE) - tileflags |= TILESTATE_PROTECTIONZONE; - else if((flags & TILESTATE_NOPVPZONE) == TILESTATE_NOPVPZONE) - tileflags |= TILESTATE_NOPVPZONE; - else if((flags & TILESTATE_PVPZONE) == TILESTATE_PVPZONE) - tileflags |= TILESTATE_PVPZONE; + if((_flags & TILESTATE_PROTECTIONZONE) == TILESTATE_PROTECTIONZONE) + flags |= TILESTATE_PROTECTIONZONE; + else if((_flags & TILESTATE_OPTIONALZONE) == TILESTATE_OPTIONALZONE) + flags |= TILESTATE_OPTIONALZONE; + else if((_flags & TILESTATE_HARDCOREZONE) == TILESTATE_HARDCOREZONE) + flags |= TILESTATE_HARDCOREZONE; - if((flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT) - tileflags |= TILESTATE_NOLOGOUT; + if((_flags & TILESTATE_NOLOGOUT) == TILESTATE_NOLOGOUT) + flags |= TILESTATE_NOLOGOUT; - if((flags & TILESTATE_REFRESH) == TILESTATE_REFRESH) + if((_flags & TILESTATE_REFRESH) == TILESTATE_REFRESH) { if(house) - std::cout << "[x:" << px << ", y:" << py << ", z:" << pz << "] House tile flagged as refreshing!"; + std::clog << "[x:" << px << ", y:" << py << ", z:" << pz << "] House tile flagged as refreshing!"; - tileflags |= TILESTATE_REFRESH; + flags |= TILESTATE_REFRESH; } break; @@ -353,11 +360,14 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) return false; } - if(house && item->isMoveable()) + if(item->getItemCount() <= 0) + item->setItemCount(1); + + if(house && item->isMovable()) { - std::cout << "[Warning - IOMap::loadMap] Movable item in house: " << house->getId(); - std::cout << ", item type: " << item->getID() << ", at position " << px << "/" << py << "/"; - std::cout << pz << std::endl; + std::clog << "[Warning - IOMap::loadMap] Movable item in house: " << house->getId() + << ", item type: " << item->getID() << ", at position " << px << "/" << py << "/" + << pz << std::endl; delete item; item = NULL; @@ -365,23 +375,60 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) else if(tile) { tile->__internalAddThing(item); - item->__startDecaying(); - item->setLoadedFromMap(true); + if(item->getDecaying() != DECAYING_TRUE) + { + item->__startDecaying(); + item->setLoadedFromMap(true); + } } else if(item->isGroundTile()) { if(ground) - delete ground; - + { +#ifdef __GROUND_CACHE__ + CacheMap::iterator it = groundCache.find(ground->getID()); + bool erase = it == groundCache.end(); + if(!erase) + { + it->second.second--; + erase = it->second.second < 1; + if(erase) + groundCache.erase(it); + } + + if(erase) +#endif + delete ground; + } + +#ifdef __GROUND_CACHE__ + const ItemType& tit = Item::items[item->getID()]; + if(!(tit.magicEffect != MAGIC_EFFECT_NONE || !tit.walkStack || tit.transformUseTo != 0 || tit.cache || + item->floorChange() || item->canDecay() || item->getActionId() > 0 || item->getUniqueId() > 0)) + { + CacheMap::iterator it = groundCache.find(item->getID()); + if(it != groundCache.end()) + { + delete item; + item = it->second.first; + it->second.second++; + } + else + groundCache[item->getID()] = std::make_pair(item, 1); + } + +#endif ground = item; } else { tile = createTile(ground, item, px, py, pz); tile->__internalAddThing(item); - - item->__startDecaying(); - item->setLoadedFromMap(true); + if(item->getDecaying() != DECAYING_TRUE) + { + item->__startDecaying(); + item->setLoadedFromMap(true); + } } break; @@ -418,11 +465,14 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) if(item->unserializeItemNode(f, nodeItem, propStream)) { - if(house && item->isMoveable()) + if(item->getItemCount() <= 0) + item->setItemCount(1); + + if(house && item->isMovable()) { - std::cout << "[Warning - IOMap::loadMap] Movable item in house: "; - std::cout << house->getId() << ", item type: " << item->getID(); - std::cout << ", pos " << px << "/" << py << "/" << pz << std::endl; + std::clog << "[Warning - IOMap::loadMap] Movable item in house: " + << house->getId() << ", item type: " << item->getID() + << ", pos " << px << "/" << py << "/" << pz << std::endl; delete item; item = NULL; @@ -430,23 +480,60 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) else if(tile) { tile->__internalAddThing(item); - item->__startDecaying(); - item->setLoadedFromMap(true); + if(item->getDecaying() != DECAYING_TRUE) + { + item->__startDecaying(); + item->setLoadedFromMap(true); + } } else if(item->isGroundTile()) { if(ground) - delete ground; - + { +#ifdef __GROUND_CACHE__ + CacheMap::iterator it = groundCache.find(ground->getID()); + bool erase = it == groundCache.end(); + if(!erase) + { + it->second.second--; + erase = it->second.second < 1; + if(erase) + groundCache.erase(it); + } + + if(erase) +#endif + delete ground; + } + +#ifdef __GROUND_CACHE__ + const ItemType& tit = Item::items[item->getID()]; + if(!(tit.magicEffect != MAGIC_EFFECT_NONE || !tit.walkStack || tit.transformUseTo != 0 || tit.cache || + item->floorChange() || item->canDecay() || item->getActionId() > 0 || item->getUniqueId() > 0)) + { + CacheMap::iterator it = groundCache.find(item->getID()); + if(it != groundCache.end()) + { + delete item; + item = it->second.first; + it->second.second++; + } + else + groundCache[item->getID()] = std::make_pair(item, 1); + } + +#endif ground = item; } else { tile = createTile(ground, item, px, py, pz); tile->__internalAddThing(item); - - item->__startDecaying(); - item->setLoadedFromMap(true); + if(item->getDecaying() != DECAYING_TRUE) + { + item->__startDecaying(); + item->setLoadedFromMap(true); + } } } else @@ -473,7 +560,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) if(!tile) tile = createTile(ground, NULL, px, py, pz); - tile->setFlag((tileflags_t)tileflags); + tile->setFlag((tileflags_t)flags); map->setTile(px, py, pz, tile); } else @@ -499,7 +586,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) } uint32_t townId = 0; - if(!propStream.GET_ULONG(townId)) + if(!propStream.getLong(townId)) { setLastErrorString("Could not read town id."); return false; @@ -513,7 +600,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) } std::string townName; - if(!propStream.GET_STRING(townName)) + if(!propStream.getString(townName)) { setLastErrorString("Could not read town name."); return false; @@ -521,7 +608,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) town->setName(townName); OTBM_Destination_coords *townCoords; - if(!propStream.GET_STRUCT(townCoords)) + if(!propStream.getStruct(townCoords)) { setLastErrorString("Could not read town coordinates."); return false; @@ -552,14 +639,14 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) } std::string name; - if(!propStream.GET_STRING(name)) + if(!propStream.getString(name)) { setLastErrorString("Could not read waypoint name."); return false; } OTBM_Destination_coords* waypoint_coords; - if(!propStream.GET_STRUCT(waypoint_coords)) + if(!propStream.getStruct(waypoint_coords)) { setLastErrorString("Could not read waypoint coordinates."); return false; @@ -586,5 +673,30 @@ bool IOMap::loadMap(Map* map, const std::string& identifier) nodeMapData = f.getNextNode(nodeMapData, type); } +#ifdef __GROUND_CACHE__ + for(CacheMap::iterator it = groundCache.begin(); it != groundCache.end(); ++it) + { + //it->second.first->setParent(NULL); + g_game.grounds[it->second.first] = it->second.second; + } + + groundCache.clear(); +#endif return true; } + +bool IOMap::loadSpawns(Map* map) +{ + if(map->spawnfile.empty()) + map->spawnfile = g_config.getString(ConfigManager::MAP_NAME) + "-spawn.xml"; + + return Spawns::getInstance()->loadFromXml(map->spawnfile); +} + +bool IOMap::loadHouses(Map* map) +{ + if(map->housefile.empty()) + map->housefile = g_config.getString(ConfigManager::MAP_NAME) + "-house.xml"; + + return Houses::getInstance()->loadFromXml(map->housefile); +} diff --git a/iomap.h b/iomap.h index 9d25446..2906d39 100644 --- a/iomap.h +++ b/iomap.h @@ -101,46 +101,24 @@ struct OTBM_HouseTile_coords class IOMap { - static Tile* createTile(Item*& ground, Item* item, uint16_t px, uint16_t py, uint16_t pz); public: IOMap() {} virtual ~IOMap() {} + static Tile* createTile(Item*& ground, Item* item, uint16_t px, uint16_t py, uint16_t pz); bool loadMap(Map* map, const std::string& identifier); /* Load the spawns * \param map pointer to the Map class * \returns Returns true if the spawns were loaded successfully */ - bool loadSpawns(Map* map) - { - if(map->spawnfile.empty()) - { - //OTBM file doesn't tell us about the spawnfile, - //lets guess it is mapname-spawn.xml. - map->spawnfile = Status::getInstance()->getMapName(); - map->spawnfile += "-spawn.xml"; - } - - return Spawns::getInstance()->loadFromXml(map->spawnfile); - } + bool loadSpawns(Map* map); /* Load the houses (not house tile-data) * \param map pointer to the Map class * \returns Returns true if the houses were loaded successfully */ - bool loadHouses(Map* map) - { - if(map->housefile.empty()) - { - //OTBM file doesn't tell us about the housefile, - //lets guess it is mapname-house.xml. - map->housefile = Status::getInstance()->getMapName(); - map->housefile += "-house.xml"; - } - - return Houses::getInstance()->loadFromXml(map->housefile); - } + bool loadHouses(Map* map); const std::string& getLastErrorString() const {return errorString;} void setLastErrorString(const std::string& _errorString) {errorString = _errorString;} diff --git a/iomapserialize.cpp b/iomapserialize.cpp index bccf460..d8853b5 100644 --- a/iomapserialize.cpp +++ b/iomapserialize.cpp @@ -28,15 +28,36 @@ extern Game g_game; bool IOMapSerialize::loadMap(Map* map) { - if(g_config.getBool(ConfigManager::HOUSE_STORAGE)) - return loadMapBinary(map); + std::string config = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_STORAGE)); + bool result = false; + if(config == "binary-tilebased") + result = loadMapBinaryTileBased(map); + else if(config == "binary") + result = loadMapBinary(map); + else + result = loadMapRelational(map); + + if(!result) + return false; + + for(HouseMap::iterator it = Houses::getInstance()->getHouseBegin(); + it != Houses::getInstance()->getHouseEnd(); ++it) + { + if(!it->second->hasSyncFlag(House::HOUSE_SYNC_UPDATE)) + continue; - return loadMapRelational(map); + it->second->resetSyncFlag(House::HOUSE_SYNC_UPDATE); + it->second->updateDoorDescription(); + } + return true; } bool IOMapSerialize::saveMap(Map* map) { - if(g_config.getBool(ConfigManager::HOUSE_STORAGE)) + std::string config = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_STORAGE)); + if(config == "binary-tilebased") + return saveMapBinaryTileBased(map); + else if(config == "binary") return saveMapBinary(map); return saveMapRelational(map); @@ -61,7 +82,7 @@ bool IOMapSerialize::updateAuctions() query.str(""); query << "DELETE FROM `house_auctions` WHERE `house_id` = " << result->getDataInt("house_id"); if(!(house = Houses::getInstance()->getHouse(result->getDataInt( - "house_id"))) || !db->executeQuery(query.str())) + "house_id"))) || !db->query(query.str())) { success = false; continue; @@ -93,11 +114,14 @@ bool IOMapSerialize::loadHouses() house->setRentWarnings(result->getDataInt("warnings")); house->setLastWarning(result->getDataInt("lastwarning")); + house->setPaidUntil(result->getDataInt("paid")); + if(result->getDataInt("clear") == 1) + house->setPendingTransfer(true); house->setOwner(result->getDataInt("owner")); - if(result->getDataInt("clear")) - house->setPendingTransfer(true); + if(house->getOwner() && house->hasSyncFlag(House::HOUSE_SYNC_UPDATE)) + house->resetSyncFlag(House::HOUSE_SYNC_UPDATE); } while(result->next()); result->free(); @@ -133,10 +157,13 @@ bool IOMapSerialize::updateHouses() if(!(house = it->second)) continue; - query << "SELECT `id` FROM `houses` WHERE `id` = " << house->getId() << " AND `world_id` = " + query << "SELECT `price` FROM `houses` WHERE `id` = " << house->getId() << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " LIMIT 1"; if(DBResult* result = db->storeQuery(query.str())) { + if((uint32_t)result->getDataInt("price") != house->getPrice()) + house->setSyncFlag(House::HOUSE_SYNC_UPDATE); + result->free(); query.str(""); @@ -176,7 +203,7 @@ bool IOMapSerialize::updateHouses() << house->getTilesCount() << ", " << house->isGuild() << ")"; } - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; query.str(""); @@ -205,13 +232,13 @@ bool IOMapSerialize::saveHouse(Database* db, House* house) << house->getPaidUntil() << ", `warnings` = " << house->getRentWarnings() << ", `lastwarning` = " << house->getLastWarning() << ", `clear` = 0 WHERE `id` = " << house->getId() << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << db->getUpdateLimiter(); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; query.str(""); query << "DELETE FROM `house_lists` WHERE `house_id` = " << house->getId() << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; DBInsert queryInsert(db); @@ -236,25 +263,75 @@ bool IOMapSerialize::saveHouse(Database* db, House* house) return false; } + const Door* door; for(HouseDoorList::iterator it = house->getHouseDoorBegin(); it != house->getHouseDoorEnd(); ++it) { - const Door* door = (*it); - if(!door) + if(!(door = *it) || !door->getAccessList(listText) || listText.empty()) continue; - if(door->getAccessList(listText) && !listText.empty()) - { - query.str(""); - query << house->getId() << ", " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " - << door->getDoorId() << ", " << db->escapeString(listText); - if(!queryInsert.addRow(query.str())) - return false; - } + query.str(""); + query << house->getId() << ", " << g_config.getNumber(ConfigManager::WORLD_ID) + << ", " << (int32_t)door->getDoorId() << ", " << db->escapeString(listText); + if(!queryInsert.addRow(query.str())) + return false; } return query.str().empty() || queryInsert.execute(); } +bool IOMapSerialize::saveHouseItems(Database* db, House* house) +{ + std::string config = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_STORAGE)); + if(config == "binary-tilebased") + { + DBQuery query; + query << "DELETE FROM `tile_store` WHERE `house_id` = " << house->getId() + << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); + if(!db->query(query.str())) + return false; + + DBInsert stmt(db); + stmt.setQuery("INSERT INTO `tile_store` (`house_id`, `world_id`, `data`) VALUES "); + return saveHouseBinaryTileBased(db, stmt, house) && stmt.execute(); + } + else if(config == "binary") + { + DBQuery query; + query << "DELETE FROM `house_data` WHERE `house_id` = "<< house->getId() + << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); + if(!db->query(query.str())) + return false; + + DBInsert stmt(db); + stmt.setQuery("INSERT INTO `house_data` (`house_id`, `world_id`, `data`) VALUES "); + return saveHouseBinary(db, stmt, house) && stmt.execute(); + } + + DBQuery query; + query << "DELETE FROM `tile_items` WHERE `tile_id` IN (SELECT `id` FROM `tiles` WHERE `house_id` = " + << house->getId() << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) + << ") AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); + if(!db->query(query.str())) + return false; + + query.str(""); + query << "DELETE FROM `tiles` WHERE `house_id` = " << house->getId() + << " AND `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); + if(!db->query(query.str())) + return false; + + query.str(""); + query << "SELECT `id` FROM `tiles` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << " ORDER BY `id` DESC LIMIT 1;"; + + DBResult* result; + if(!(result = db->storeQuery(query.str()))) + return false; + + uint32_t tileId = result->getDataInt("id") + 1; + result->free(); + return saveHouseRelational(db, house, tileId); +} + bool IOMapSerialize::loadMapRelational(Map* map) { Database* db = Database::getInstance(); @@ -283,7 +360,7 @@ bool IOMapSerialize::loadMapRelational(Map* map) if(Player* player = g_game.getPlayerByGuidEx(house->getOwner())) { Depot* depot = player->getDepot(house->getTownId(), true); - loadItems(db, itemsResult, depot, true); + loadItems(itemsResult, depot, true); if(player->isVirtual()) { IOLoginData::getInstance()->savePlayer(player); @@ -295,9 +372,9 @@ bool IOMapSerialize::loadMapRelational(Map* map) { Position pos(result->getDataInt("x"), result->getDataInt("y"), result->getDataInt("z")); if(Tile* tile = map->getTile(pos)) - loadItems(db, itemsResult, tile, false); + loadItems(itemsResult, tile, false); else - std::cout << "[Error - IOMapSerialize::loadMapRelational] Unserialization" + std::clog << "[Error - IOMapSerialize::loadMapRelational] Unserialization" << " of invalid tile at position "<< pos << std::endl; } @@ -327,7 +404,7 @@ bool IOMapSerialize::loadMapRelational(Map* map) if(Player* player = g_game.getPlayerByGuidEx(house->getOwner())) { Depot* depot = player->getDepot(house->getTownId(), true); - loadItems(db, itemsResult, depot, true); + loadItems(itemsResult, depot, true); if(player->isVirtual()) { IOLoginData::getInstance()->savePlayer(player); @@ -336,7 +413,7 @@ bool IOMapSerialize::loadMapRelational(Map* map) } } else - loadItems(db, itemsResult, (*it), false); + loadItems(itemsResult, (*it), false); itemsResult->free(); } @@ -350,7 +427,7 @@ bool IOMapSerialize::loadMapRelational(Map* map) return true; } -bool IOMapSerialize::saveMapRelational(Map* map) +bool IOMapSerialize::saveMapRelational(Map*) { Database* db = Database::getInstance(); //Start the transaction @@ -361,21 +438,17 @@ bool IOMapSerialize::saveMapRelational(Map* map) //clear old tile data DBQuery query; query << "DELETE FROM `tile_items` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; query.str(""); query << "DELETE FROM `tiles` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; uint32_t tileId = 0; for(HouseMap::iterator it = Houses::getInstance()->getHouseBegin(); it != Houses::getInstance()->getHouseEnd(); ++it) - { - //save house items - for(HouseTileList::iterator tit = it->second->getHouseTileBegin(); tit != it->second->getHouseTileEnd(); ++tit) - saveItems(db, tileId, it->second->getId(), (*tit)); - } + saveHouseRelational(db, it->second, tileId); //End the transaction return trans.commit(); @@ -407,12 +480,12 @@ bool IOMapSerialize::loadMapBinary(Map* map) uint16_t x = 0, y = 0; uint8_t z = 0; - propStream.GET_USHORT(x); - propStream.GET_USHORT(y); - propStream.GET_UCHAR(z); + propStream.getShort(x); + propStream.getShort(y); + propStream.getByte(z); uint32_t itemCount = 0; - propStream.GET_ULONG(itemCount); + propStream.getLong(itemCount); Position pos(x, y, (int16_t)z); if(house && house->hasPendingTransfer()) @@ -437,7 +510,7 @@ bool IOMapSerialize::loadMapBinary(Map* map) } else { - std::cout << "[Error - IOMapSerialize::loadMapBinary] Unserialization of invalid tile" + std::clog << "[Error - IOMapSerialize::loadMapBinary] Unserialization of invalid tile" << " at position " << pos << std::endl; break; } @@ -448,7 +521,7 @@ bool IOMapSerialize::loadMapBinary(Map* map) return true; } -bool IOMapSerialize::saveMapBinary(Map* map) +bool IOMapSerialize::saveMapBinary(Map*) { Database* db = Database::getInstance(); //Start the transaction @@ -458,30 +531,106 @@ bool IOMapSerialize::saveMapBinary(Map* map) DBQuery query; query << "DELETE FROM `house_data` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; DBInsert stmt(db); stmt.setQuery("INSERT INTO `house_data` (`house_id`, `world_id`, `data`) VALUES "); for(HouseMap::iterator it = Houses::getInstance()->getHouseBegin(); it != Houses::getInstance()->getHouseEnd(); ++it) + saveHouseBinary(db, stmt, it->second); + + query.str(""); + if(!stmt.execute()) + return false; + + //End the transaction + return transaction.commit(); +} + +bool IOMapSerialize::loadMapBinaryTileBased(Map* map) +{ + Database* db = Database::getInstance(); + DBResult* result; + + DBQuery query; + query << "SELECT `house_id`, `data` FROM `tile_store` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); + if(!(result = db->storeQuery(query.str()))) + return false; + + House* house = NULL; + do { - //save house items - PropWriteStream stream; - for(HouseTileList::iterator tit = it->second->getHouseTileBegin(); tit != it->second->getHouseTileEnd(); ++tit) + int32_t houseId = result->getDataInt("house_id"); + house = Houses::getInstance()->getHouse(houseId); + + uint64_t attrSize = 0; + const char* attr = result->getDataStream("data", attrSize); + + PropStream propStream; + propStream.init(attr, attrSize); + while(propStream.size()) { - if(!saveTile(stream, *tit)) - return false; + uint16_t x = 0, y = 0; + uint8_t z = 0; + + propStream.getShort(x); + propStream.getShort(y); + propStream.getByte(z); + + uint32_t itemCount = 0; + propStream.getLong(itemCount); + + Position pos(x, y, (int16_t)z); + if(house && house->hasPendingTransfer()) + { + if(Player* player = g_game.getPlayerByGuidEx(house->getOwner())) + { + Depot* depot = player->getDepot(player->getTown(), true); + while(itemCount--) + loadItem(propStream, depot, true); + + if(player->isVirtual()) + { + IOLoginData::getInstance()->savePlayer(player); + delete player; + } + } + } + else if(Tile* tile = map->getTile(pos)) + { + while(itemCount--) + loadItem(propStream, tile, false); + } + else + { + std::clog << "[Error - IOMapSerialize::loadMapBinary] Unserialization of invalid tile" + << " at position " << pos << std::endl; + break; + } } + } + while(result->next()); + result->free(); + return true; +} - uint32_t attributesSize = 0; - const char* attributes = stream.getStream(attributesSize); - query.str(""); +bool IOMapSerialize::saveMapBinaryTileBased(Map*) +{ + Database* db = Database::getInstance(); + //Start the transaction + DBTransaction transaction(db); + if(!transaction.begin()) + return false; - query << it->second->getId() << ", " << g_config.getNumber(ConfigManager::WORLD_ID) - << ", " << db->escapeBlob(attributes, attributesSize); - if(!stmt.addRow(query)) - return false; - } + DBQuery query; + query << "DELETE FROM `tile_store` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID); + if(!db->query(query.str())) + return false; + + DBInsert stmt(db); + stmt.setQuery("INSERT INTO `tile_store` (`house_id`, `world_id`, `data`) VALUES "); + for(HouseMap::iterator it = Houses::getInstance()->getHouseBegin(); it != Houses::getInstance()->getHouseEnd(); ++it) + saveHouseBinaryTileBased(db, stmt, it->second); query.str(""); if(!stmt.execute()) @@ -491,14 +640,64 @@ bool IOMapSerialize::saveMapBinary(Map* map) return transaction.commit(); } -bool IOMapSerialize::loadItems(Database* db, DBResult* result, Cylinder* parent, bool depotTransfer/* = false*/) +bool IOMapSerialize::saveHouseRelational(Database* db, House* house, uint32_t& tileId) +{ + for(HouseTileList::iterator tit = house->getHouseTileBegin(); tit != house->getHouseTileEnd(); ++tit) + saveItems(db, tileId, house->getId(), (*tit)); + + return true; +} + +bool IOMapSerialize::saveHouseBinary(Database* db, DBInsert& stmt, House* house) +{ + PropWriteStream stream; + for(HouseTileList::iterator tit = house->getHouseTileBegin(); tit != house->getHouseTileEnd(); ++tit) + { + if(!saveTile(stream, *tit)) + continue; + } + + uint32_t attributesSize = 0; + const char* attributes = stream.getStream(attributesSize); + if(!attributesSize) + return true; + + DBQuery query; + query << house->getId() << ", " << g_config.getNumber(ConfigManager::WORLD_ID) + << ", " << db->escapeBlob(attributes, attributesSize); + return stmt.addRow(query); +} + +bool IOMapSerialize::saveHouseBinaryTileBased(Database* db, DBInsert& stmt, House* house) +{ + for(HouseTileList::iterator tit = house->getHouseTileBegin(); tit != house->getHouseTileEnd(); ++tit) + { + PropWriteStream stream; + if(!saveTile(stream, *tit)) + continue; + + uint32_t attributesSize = 0; + const char* attributes = stream.getStream(attributesSize); + if(!attributesSize) + continue; + + DBQuery query; + query << house->getId() << ", " << g_config.getNumber(ConfigManager::WORLD_ID) + << ", " << db->escapeBlob(attributes, attributesSize); + if(!stmt.addRow(query)) + return false; + } + + return true; +} + +bool IOMapSerialize::loadItems(DBResult* result, Cylinder* parent, bool depotTransfer/* = false*/) { ItemMap itemMap; Tile* tile = NULL; if(!parent->getItem()) tile = parent->getTile(); - Item* item = NULL; int32_t sid, pid, id, count; do @@ -516,7 +715,7 @@ bool IOMapSerialize::loadItems(Database* db, DBResult* result, Cylinder* parent, propStream.init(attr, attrSize); const ItemType& iType = Item::items[id]; - if(iType.moveable || iType.forceSerialize || pid) + if(iType.movable || iType.forceSerialize || pid) { if(!(item = Item::CreateItem(id, count))) continue; @@ -530,33 +729,32 @@ bool IOMapSerialize::loadItems(Database* db, DBResult* result, Cylinder* parent, } } else - std::cout << "[Warning - IOMapSerialize::loadItems] Unserialization error [0] for item type " << id << std::endl; + std::clog << "[Warning - IOMapSerialize::loadItems] Unserialization error [0] for item type " << id << std::endl; } else if(tile) { //find this type in the tile - Item* findItem = NULL; - for(uint32_t i = 0; i < tile->getThingCount(); ++i) + if(TileItemVector* items = tile->getItemList()) { - if(!(findItem = tile->__getThing(i)->getItem())) - continue; - - if(findItem->getID() == id) + for(ItemVector::iterator it = items->begin(); it != items->end(); ++it) { - item = findItem; - break; - } + if((*it)->getID() == id) + { + item = *it; + break; + } - if(iType.isDoor() && findItem->getDoor()) - { - item = findItem; - break; - } + if(iType.isBed() && (*it)->getBed()) + { + item = *it; + break; + } - if(iType.isBed() && findItem->getBed()) - { - item = findItem; - break; + if(iType.isDoor() && (*it)->getDoor()) + { + item = *it; + break; + } } } } @@ -565,17 +763,20 @@ bool IOMapSerialize::loadItems(Database* db, DBResult* result, Cylinder* parent, { if(item->unserializeAttr(propStream)) { - if((item = g_game.transformItem(item, id))) + if(!item->getDoor() || item->getID() == iType.transformUseTo) + item = g_game.transformItem(item, id); + + if(item) itemMap[sid] = std::make_pair(item, pid); } else - std::cout << "[Warning - IOMapSerialize::loadItems] Unserialization error [1] for item type " << id << std::endl; + std::clog << "[Warning - IOMapSerialize::loadItems] Unserialization error [1] for item type " << id << std::endl; } else if((item = Item::CreateItem(id))) { item->unserializeAttr(propStream); if(!depotTransfer) - std::cout << "[Warning - IOMapSerialize::loadItems] NULL item at " + std::clog << "[Warning - IOMapSerialize::loadItems] NULL item at " << tile->getPosition() << " (type = " << id << ", sid = " << sid << ", pid = " << pid << ")" << std::endl; else @@ -619,22 +820,22 @@ bool IOMapSerialize::saveItems(Database* db, uint32_t& tileId, uint32_t houseId, ContainerStackList containerStackList; bool stored = false; - DBInsert query_insert(db); - query_insert.setQuery("INSERT INTO `tile_items` (`tile_id`, `world_id`, `sid`, `pid`, `itemtype`, `count`, `attributes`) VALUES "); + DBInsert stmt(db); + stmt.setQuery("INSERT INTO `tile_items` (`tile_id`, `world_id`, `sid`, `pid`, `itemtype`, `count`, `attributes`) VALUES "); DBQuery query; for(int32_t i = 0; i < thingCount; ++i) { - if(!(item = tile->__getThing(i)->getItem()) || (item->isNotMoveable() && !item->forceSerialize())) + if(!(item = tile->__getThing(i)->getItem()) || (!item->isMovable() && !item->forceSerialize())) continue; if(!stored) { Position tilePosition = tile->getPosition(); query << "INSERT INTO `tiles` (`id`, `world_id`, `house_id`, `x`, `y`, `z`) VALUES (" - << tileId << ", " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " << houseId << ", " - << tilePosition.x << ", " << tilePosition.y << ", " << tilePosition.z << ")"; - if(!db->executeQuery(query.str())) + << tileId << ", " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " << houseId << ", " + << tilePosition.x << ", " << tilePosition.y << ", " << tilePosition.z << ")"; + if(!db->query(query.str())) return false; stored = true; @@ -649,7 +850,7 @@ bool IOMapSerialize::saveItems(Database* db, uint32_t& tileId, uint32_t houseId, query << tileId << ", " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " << ++runningId << ", " << parentId << ", " << item->getID() << ", " << (int32_t)item->getSubType() << ", " << db->escapeBlob(attributes, attributesSize); - if(!query_insert.addRow(query.str())) + if(!stmt.addRow(query.str())) return false; query.str(""); @@ -664,7 +865,7 @@ bool IOMapSerialize::saveItems(Database* db, uint32_t& tileId, uint32_t houseId, parentId = cit->second; for(ItemList::const_iterator it = container->getItems(); it != container->getEnd(); ++it) { - if(!(item = (*it))) + if(!(item = *it)) continue; PropWriteStream propWriteStream; @@ -675,7 +876,7 @@ bool IOMapSerialize::saveItems(Database* db, uint32_t& tileId, uint32_t houseId, query << tileId << ", " << g_config.getNumber(ConfigManager::WORLD_ID) << ", " << ++runningId << ", " << parentId << ", " << item->getID() << ", " << (int32_t)item->getSubType() << ", " << db->escapeBlob(attributes, attributesSize); - if(!query_insert.addRow(query.str())) + if(!stmt.addRow(query.str())) return false; query.str(""); @@ -687,7 +888,7 @@ bool IOMapSerialize::saveItems(Database* db, uint32_t& tileId, uint32_t houseId, if(stored) ++tileId; - return query_insert.execute(); + return stmt.execute(); } bool IOMapSerialize::loadContainer(PropStream& propStream, Container* container) @@ -696,7 +897,7 @@ bool IOMapSerialize::loadContainer(PropStream& propStream, Container* container) { if(!loadItem(propStream, container, false)) { - std::cout << "[Warning - IOMapSerialize::loadContainer] Unserialization error [0] for item in container " << container->getID() << std::endl; + std::clog << "[Warning - IOMapSerialize::loadContainer] Unserialization error [0] for item in container " << container->getID() << std::endl; return false; } @@ -704,11 +905,11 @@ bool IOMapSerialize::loadContainer(PropStream& propStream, Container* container) } uint8_t endAttr = ATTR_END; - propStream.GET_UCHAR(endAttr); + propStream.getByte(endAttr); if(endAttr == ATTR_END) return true; - std::cout << "[Warning - IOMapSerialize::loadContainer] Unserialization error [1] for item in container " << container->getID() << std::endl; + std::clog << "[Warning - IOMapSerialize::loadContainer] Unserialization error [1] for item in container " << container->getID() << std::endl; return false; } @@ -719,18 +920,18 @@ bool IOMapSerialize::loadItem(PropStream& propStream, Cylinder* parent, bool dep tile = parent->getTile(); uint16_t id = 0; - propStream.GET_USHORT(id); + propStream.getShort(id); Item* item = NULL; const ItemType& iType = Item::items[id]; - if(iType.moveable || iType.forceSerialize || (!depotTransfer && !tile)) + if(iType.movable || iType.forceSerialize || (!depotTransfer && !tile)) { if(!(item = Item::CreateItem(id))) return true; if(!item->unserializeAttr(propStream)) { - std::cout << "[Warning - IOMapSerialize::loadItem] Unserialization error [0] for item type " << id << std::endl; + std::clog << "[Warning - IOMapSerialize::loadItem] Unserialization error [0] for item type " << id << std::endl; delete item; return false; } @@ -758,28 +959,27 @@ bool IOMapSerialize::loadItem(PropStream& propStream, Cylinder* parent, bool dep if(tile) { //Stationary items - Item* findItem = NULL; - for(uint32_t i = 0; i < tile->getThingCount(); ++i) + if(TileItemVector* items = tile->getItemList()) { - if(!(findItem = tile->__getThing(i)->getItem())) - continue; - - if(findItem->getID() == id) + for(ItemVector::iterator it = items->begin(); it != items->end(); ++it) { - item = findItem; - break; - } + if((*it)->getID() == id) + { + item = *it; + break; + } - if(iType.isDoor() && findItem->getDoor()) - { - item = findItem; - break; - } + if(iType.isBed() && (*it)->getBed()) + { + item = *it; + break; + } - if(iType.isBed() && findItem->getBed()) - { - item = findItem; - break; + if(iType.isDoor() && (*it)->getDoor()) + { + item = *it; + break; + } } } } @@ -792,10 +992,11 @@ bool IOMapSerialize::loadItem(PropStream& propStream, Cylinder* parent, bool dep if(container && !loadContainer(propStream, container)) return false; - item = g_game.transformItem(item, id); + if(!item->getDoor() || item->getID() == iType.transformUseTo) + item = g_game.transformItem(item, id); } else - std::cout << "[Warning - IOMapSerialize::loadItem] Unserialization error [1] for item type " << id << std::endl; + std::clog << "[Warning - IOMapSerialize::loadItem] Unserialization error [1] for item type " << id << std::endl; return true; } @@ -836,19 +1037,19 @@ bool IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile) Item* item = NULL; for(; tileCount > 0; --tileCount) { - if((item = tile->__getThing(tileCount - 1)->getItem()) && - (item->isMoveable() || item->forceSerialize())) + if((item = tile->__getThing(tileCount - 1)->getItem()) && // CHECKME: wouldn't it be better to use TileItemVector in here? + (item->isMovable() || item->forceSerialize())) items.push_back(item); } tileCount = items.size(); //lame, but at least we don't need a new variable if(tileCount > 0) { - stream.ADD_USHORT(tile->getPosition().x); - stream.ADD_USHORT(tile->getPosition().y); - stream.ADD_UCHAR(tile->getPosition().z); + stream.addShort(tile->getPosition().x); + stream.addShort(tile->getPosition().y); + stream.addByte(tile->getPosition().z); - stream.ADD_ULONG(tileCount); + stream.addLong(tileCount); for(std::vector::iterator it = items.begin(); it != items.end(); ++it) saveItem(stream, (*it)); } @@ -858,16 +1059,16 @@ bool IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile) bool IOMapSerialize::saveItem(PropWriteStream& stream, const Item* item) { - stream.ADD_USHORT(item->getID()); + stream.addShort(item->getID()); item->serializeAttr(stream); if(const Container* container = item->getContainer()) { - stream.ADD_UCHAR(ATTR_CONTAINER_ITEMS); - stream.ADD_ULONG(container->size()); + stream.addByte(ATTR_CONTAINER_ITEMS); + stream.addLong(container->size()); for(ItemList::const_reverse_iterator rit = container->getReversedItems(); rit != container->getReversedEnd(); ++rit) saveItem(stream, (*rit)); } - stream.ADD_UCHAR(ATTR_END); + stream.addByte(ATTR_END); return true; } diff --git a/iomapserialize.h b/iomapserialize.h index 4ba8686..b4c15ca 100644 --- a/iomapserialize.h +++ b/iomapserialize.h @@ -46,6 +46,7 @@ class IOMapSerialize bool saveHouses(); bool saveHouse(Database* db, House* house); + bool saveHouseItems(Database* db, House* house); protected: IOMapSerialize() {} @@ -53,16 +54,23 @@ class IOMapSerialize // Relational storage uses a row for each item/tile bool loadMapRelational(Map* map); bool saveMapRelational(Map* map); - + bool saveHouseRelational(Database* db, House* house, uint32_t& tileId); + // Binary storage uses a giant BLOB field for storing everything bool loadMapBinary(Map* map); bool saveMapBinary(Map* map); + bool saveHouseBinary(Database* db, DBInsert& stmt, House* house); + + // Binary-tilebased storage uses a BLOB field for each tile in houses, so that corrupt blobs will only wipe tiles instead of entire houses + bool loadMapBinaryTileBased(Map* map); + bool saveMapBinaryTileBased(Map* map); + bool saveHouseBinaryTileBased(Database* db, DBInsert& stmt, House* house); - bool loadItems(Database* db, DBResult* result, Cylinder* parent, bool depotTransfer); + bool loadItems(DBResult* result, Cylinder* parent, bool depotTransfer); bool saveItems(Database* db, uint32_t& tileId, uint32_t houseId, const Tile* tile); - bool loadContainer(PropStream& propStream, Container* container); bool loadItem(PropStream& propStream, Cylinder* parent, bool depotTransfer); + bool loadContainer(PropStream& propStream, Container* container); bool saveTile(PropWriteStream& stream, const Tile* tile); bool saveItem(PropWriteStream& stream, const Item* item); diff --git a/iomarket.cpp b/iomarket.cpp new file mode 100644 index 0000000..bd2a2cd --- /dev/null +++ b/iomarket.cpp @@ -0,0 +1,323 @@ +////////////////////////////////////////////////////////////////////// +// OpenTibia - an opensource roleplaying game +////////////////////////////////////////////////////////////////////// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +////////////////////////////////////////////////////////////////////// + +#include "otpch.h" + +#include "iomarket.h" +#include "iologindata.h" +#include "configmanager.h" + +extern ConfigManager g_config; + +MarketOfferList IOMarket::getActiveOffers(MarketAction_t action, uint16_t itemId) +{ + MarketOfferList offerList; + + DBQuery query; + query << "SELECT `id`, `player_id`, `amount`, `price`, `created`, `anonymous` FROM `market_offers` WHERE `sale` = " << action << " AND `itemtype` = " << itemId << ";"; + + Database* db = Database::getInstance(); + DBResult* result; + if(!(result = db->storeQuery(query.str()))) + return offerList; + + const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); + do + { + MarketOffer offer; + offer.amount = result->getDataInt("amount"); + offer.price = result->getDataInt("price"); + offer.timestamp = result->getDataInt("created") + marketOfferDuration; + offer.counter = result->getDataInt("id") & 0xFFFF; + if(result->getDataInt("anonymous") == 0) + { + IOLoginData::getInstance()->getNameByGuid(result->getDataInt("player_id"), offer.playerName); + if(offer.playerName.empty()) + offer.playerName = "Anonymous"; + } + else + offer.playerName = "Anonymous"; + + offerList.push_back(offer); + } + while(result->next()); + result->free(); + return offerList; +} + +MarketOfferList IOMarket::getOwnOffers(MarketAction_t action, uint32_t playerId) +{ + MarketOfferList offerList; + + const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); + + DBQuery query; + query << "SELECT `id`, `amount`, `price`, `created`, `anonymous`, `itemtype` FROM `market_offers` WHERE `player_id` = " << playerId << " AND `sale` = " << action << ";"; + + Database* db = Database::getInstance(); + DBResult* result; + if(!(result = db->storeQuery(query.str()))) + return offerList; + + do + { + MarketOffer offer; + offer.amount = result->getDataInt("amount"); + offer.price = result->getDataInt("price"); + offer.timestamp = result->getDataInt("created") + marketOfferDuration; + offer.counter = result->getDataInt("id") & 0xFFFF; + offer.itemId = result->getDataInt("itemtype"); + + offerList.push_back(offer); + } + while(result->next()); + result->free(); + return offerList; +} + +HistoryMarketOfferList IOMarket::getOwnHistory(MarketAction_t action, uint32_t playerId) +{ + HistoryMarketOfferList offerList; + + DBQuery query; + query << "SELECT `id`, `itemtype`, `amount`, `price`, `expires_at`, `state` FROM `market_history` WHERE `player_id` = " << playerId << " AND `sale` = " << action << ";"; + + Database* db = Database::getInstance(); + DBResult* result; + if(!(result = db->storeQuery(query.str()))) + return offerList; + + do + { + HistoryMarketOffer offer; + offer.itemId = result->getDataInt("itemtype"); + offer.amount = result->getDataInt("amount"); + offer.price = result->getDataInt("price"); + offer.timestamp = result->getDataInt("expires_at"); + + MarketOfferState_t offerState = (MarketOfferState_t)result->getDataInt("state"); + if(offerState == OFFERSTATE_ACCEPTEDEX) + offerState = OFFERSTATE_ACCEPTED; + + offer.state = offerState; + + offerList.push_back(offer); + } + while(result->next()); + result->free(); + return offerList; +} + +ExpiredMarketOfferList IOMarket::getExpiredOffers(MarketAction_t action) +{ + ExpiredMarketOfferList offerList; + + const time_t lastExpireDate = time(NULL) - g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); + + DBQuery query; + query << "SELECT `id`, `amount`, `price`, `itemtype`, `player_id` FROM `market_offers` WHERE `sale` = " << action << " AND `created` <= " << lastExpireDate << ";"; + + Database* db = Database::getInstance(); + DBResult* result; + if(!(result = db->storeQuery(query.str()))) + return offerList; + + do + { + ExpiredMarketOffer offer; + offer.id = result->getDataInt("id"); + offer.amount = result->getDataInt("amount"); + offer.price = result->getDataInt("price"); + offer.itemId = result->getDataInt("itemtype"); + offer.playerId = result->getDataInt("player_id"); + + offerList.push_back(offer); + } + while(result->next()); + result->free(); + return offerList; +} + +int32_t IOMarket::getPlayerOfferCount(uint32_t playerId) +{ + int32_t count = -1; + Database* db = Database::getInstance(); + DBResult* result; + + DBQuery query; + query << "SELECT COUNT(*) AS `count` FROM `market_offers` WHERE `player_id` = " << playerId << ";"; + if(!(result = db->storeQuery(query.str()))) + return count; + + count = result->getDataInt("count"); + result->free(); + return count; +} + +MarketOfferEx IOMarket::getOfferById(uint32_t id) +{ + MarketOfferEx offer; + DBQuery query; + query << "SELECT `id`, `sale`, `itemtype`, `amount`, `created`, `price`, `player_id`, `anonymous` FROM `market_offers` WHERE `id` = " << id << ";"; + Database* db = Database::getInstance(); + DBResult* result; + if((result = db->storeQuery(query.str()))) + { + offer.type = (MarketAction_t)result->getDataInt("sale"); + offer.amount = result->getDataInt("amount"); + offer.counter = result->getDataInt("id") & 0xFFFF; + offer.timestamp = result->getDataInt("created"); + offer.price = result->getDataInt("price"); + offer.itemId = result->getDataInt("itemtype"); + + int32_t playerId = result->getDataInt("player_id"); + offer.playerId = playerId; + if(result->getDataInt("anonymous") == 0) + { + IOLoginData::getInstance()->getNameByGuid(playerId, offer.playerName); + if(offer.playerName.empty()) + offer.playerName = "Anonymous"; + } + else + offer.playerName = "Anonymous"; + + result->free(); + } + return offer; +} + +uint32_t IOMarket::getOfferIdByCounter(uint32_t timestamp, uint16_t counter) +{ + const int32_t created = timestamp - g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); + + DBQuery query; + query << "SELECT `id` FROM `market_offers` WHERE `created` = " << created << " AND (`id` & 65535) = " << counter << " LIMIT 1;"; + Database* db = Database::getInstance(); + DBResult* result; + if((result = db->storeQuery(query.str()))) + { + uint32_t offerId = result->getDataInt("id"); + result->free(); + return offerId; + } + return 0; +} + +void IOMarket::createOffer(uint32_t playerId, MarketAction_t action, uint32_t itemId, uint16_t amount, uint32_t price, bool anonymous) +{ + DBQuery query; + query << "INSERT INTO `market_offers` (`player_id`, `sale`, `itemtype`, `amount`, `price`, `created`, `anonymous`) VALUES (" << playerId << ", " << action << ", " << itemId << ", " << amount << ", " << price << ", " << time(NULL) << ", " << anonymous << ");"; + Database::getInstance()->query(query.str()); +} + +void IOMarket::acceptOffer(uint32_t offerId, uint16_t amount) +{ + Database* db = Database::getInstance(); + DBQuery query; + query << "UPDATE `market_offers` SET `amount` = `amount` - " << amount << " WHERE `id` = " << offerId << ";"; + db->query(query.str()); +} + +void IOMarket::appendHistory(uint32_t playerId, MarketAction_t type, uint16_t itemId, uint16_t amount, uint32_t price, time_t timestamp, MarketOfferState_t state) +{ + Database* db = Database::getInstance(); + DBQuery query; + query << "INSERT INTO `market_history` (`player_id`, `sale`, `itemtype`, `amount`, `price`, `expires_at`, `inserted`, `state`) VALUES " + << "(" << playerId << ", " << type << ", " << itemId << ", " << amount << ", " << price << ", " + << timestamp << ", " << time(NULL) << ", " << state << ");"; + db->query(query.str()); +} + +void IOMarket::moveOfferToHistory(uint32_t offerId, MarketOfferState_t state) +{ + const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); + + Database* db = Database::getInstance(); + + DBQuery query; + DBResult* result; + query << "SELECT `player_id`, `sale`, `itemtype`, `amount`, `price`, `created` FROM `market_offers` WHERE `id` = " << offerId << ";"; + if(!(result = db->storeQuery(query.str()))) + return; + + query.str(""); + query << "DELETE FROM `market_offers` WHERE `id` = " << offerId << ";"; + if(!db->query(query.str())) + { + result->free(); + return; + } + + appendHistory(result->getDataInt("player_id"), (MarketAction_t)result->getDataInt("sale"), result->getDataInt("itemtype"), result->getDataInt("amount"), result->getDataInt("price"), result->getDataInt("created") + marketOfferDuration, state); + result->free(); +} + +void IOMarket::clearOldHistory() +{ + const time_t lastExpireDate = time(NULL) - g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); + Database* db = Database::getInstance(); + DBQuery query; + query << "DELETE FROM `market_history` WHERE `inserted` <= " << lastExpireDate << ";"; + db->query(query.str()); +} + +void IOMarket::updateStatistics() +{ + Database* db = Database::getInstance(); + + DBQuery query; + query << "SELECT `sale`, `itemtype`, COUNT(`price`) AS `num`, MIN(`price`) AS `min`, MAX(`price`) AS `max`, SUM(`price`) AS `sum` FROM `market_history` WHERE `state` = " << OFFERSTATE_ACCEPTED << " GROUP BY `itemtype`, `sale`;"; + + DBResult* result; + if(!(result = db->storeQuery(query.str()))) + return; + + do + { + MarketStatistics* statistics; + if(result->getDataInt("sale") == MARKETACTION_BUY) + statistics = &purchaseStatistics[result->getDataInt("itemtype")]; + else + statistics = &saleStatistics[result->getDataInt("itemtype")]; + + statistics->numTransactions = result->getDataInt("num"); + statistics->lowestPrice = result->getDataInt("min"); + statistics->totalPrice = result->getDataLong("sum"); + statistics->highestPrice = result->getDataInt("max"); + } + while(result->next()); + result->free(); +} + +MarketStatistics* IOMarket::getPurchaseStatistics(uint16_t itemId) +{ + std::map::iterator it = purchaseStatistics.find(itemId); + if(it == purchaseStatistics.end()) + return NULL; + + return &it->second; +} + +MarketStatistics* IOMarket::getSaleStatistics(uint16_t itemId) +{ + std::map::iterator it = saleStatistics.find(itemId); + if(it == saleStatistics.end()) + return NULL; + + return &it->second; +} diff --git a/iomarket.h b/iomarket.h new file mode 100644 index 0000000..7c99c63 --- /dev/null +++ b/iomarket.h @@ -0,0 +1,66 @@ +////////////////////////////////////////////////////////////////////// +// OpenTibia - an opensource roleplaying game +////////////////////////////////////////////////////////////////////// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +////////////////////////////////////////////////////////////////////// + +#ifndef __IOMARKET__ +#define __IOMARKET__ + +#include +#include "account.h" +#include "player.h" +#include "database.h" + +class IOMarket +{ + public: + IOMarket() {} + ~IOMarket() {} + + static IOMarket* getInstance() + { + static IOMarket instance; + return &instance; + } + + MarketOfferList getActiveOffers(MarketAction_t action, uint16_t itemId); + MarketOfferList getOwnOffers(MarketAction_t action, uint32_t playerId); + HistoryMarketOfferList getOwnHistory(MarketAction_t action, uint32_t playerId); + + ExpiredMarketOfferList getExpiredOffers(MarketAction_t action); + + int32_t getPlayerOfferCount(uint32_t playerId); + uint32_t getOfferIdByCounter(uint32_t timestamp, uint16_t counter); + MarketOfferEx getOfferById(uint32_t id); + + void createOffer(uint32_t playerId, MarketAction_t action, uint32_t itemId, uint16_t amount, uint32_t price, bool anonymous); + void acceptOffer(uint32_t offerId, uint16_t amount); + + void appendHistory(uint32_t playerId, MarketAction_t type, uint16_t itemId, uint16_t amount, uint32_t price, time_t timestamp, MarketOfferState_t state); + void moveOfferToHistory(uint32_t offerId, MarketOfferState_t state); + void clearOldHistory(); + + void updateStatistics(); + + MarketStatistics* getPurchaseStatistics(uint16_t itemId); + MarketStatistics* getSaleStatistics(uint16_t itemId); + + private: + std::map purchaseStatistics; + std::map saleStatistics; +}; + +#endif diff --git a/item.cpp b/item.cpp index c6b87ab..6ccbd62 100644 --- a/item.cpp +++ b/item.cpp @@ -42,13 +42,13 @@ extern ConfigManager g_config; extern MoveEvents* g_moveEvents; Items Item::items; -Item* Item::CreateItem(const uint16_t type, uint16_t amount/* = 1*/) +Item* Item::CreateItem(const uint16_t type, uint16_t amount/* = 0*/) { const ItemType& it = Item::items[type]; if(it.group == ITEM_GROUP_DEPRECATED) { #ifdef __DEBUG__ - std::cout << "[Error - Item::CreateItem] Item " << it.id << " has been declared as deprecated" << std::endl; + std::clog << "[Error - Item::CreateItem] Item " << it.id << " has been declared as deprecated" << std::endl; #endif return NULL; } @@ -93,7 +93,7 @@ Item* Item::CreateItem(const uint16_t type, uint16_t amount/* = 1*/) Item* Item::CreateItem(PropStream& propStream) { uint16_t type; - if(!propStream.GET_USHORT(type)) + if(!propStream.getShort(type)) return NULL; return Item::CreateItem(items.getRandomizedItem(type), 0); @@ -116,10 +116,10 @@ bool Item::loadItem(xmlNodePtr node, Container* parent) if(readXMLString(node, "attributes", strValue)) { - StringVec v, attr = explodeString(";", strValue); + StringVec v, attr = explodeString(strValue, ";"); for(StringVec::iterator it = attr.begin(); it != attr.end(); ++it) { - v = explodeString(",", (*it)); + v = explodeString((*it), ","); if(v.size() < 2) continue; @@ -156,19 +156,13 @@ bool Item::loadItem(xmlNodePtr node, Container* parent) bool Item::loadContainer(xmlNodePtr parentNode, Container* parent) { - xmlNodePtr node = parentNode->children; - while(node) + for(xmlNodePtr node = parentNode->children; node; node = node->next) { if(node->type != XML_ELEMENT_NODE) - { - node = node->next; continue; - } if(!xmlStrcmp(node->name, (const xmlChar*)"item") && !loadItem(node, parent)) return false; - - node = node->next; } return true; @@ -177,21 +171,24 @@ bool Item::loadContainer(xmlNodePtr parentNode, Container* parent) Item::Item(const uint16_t type, uint16_t amount/* = 0*/): ItemAttributes(), id(type) { - count = 1; raid = NULL; loadedFromMap = false; - const ItemType& it = items[id]; - if(it.charges) - setCharges(it.charges); - + setItemCount(1); setDefaultDuration(); + + const ItemType& it = items[type]; if(it.isFluidContainer() || it.isSplash()) setFluidType(amount); - else if(it.stackable && amount) - count = amount; - else if(it.charges && amount) - setCharges(amount); + else if(it.stackable) + { + if(amount) + setItemCount(amount); + else if(it.charges) + setItemCount(it.charges); + } + else if(it.charges) + setCharges(amount ? amount : it.charges); } Item* Item::clone() const @@ -205,6 +202,7 @@ Item* Item::clone() const tmp->createAttributes(); *tmp->attributes = *attributes; + tmp->eraseAttribute("uid"); return tmp; } @@ -214,12 +212,23 @@ void Item::copyAttributes(Item* item) { createAttributes(); *attributes = *item->attributes; + eraseAttribute("uid"); } eraseAttribute("decaying"); eraseAttribute("duration"); } +void Item::makeUnique(Item* parent) +{ + if(!parent || !parent->getUniqueId()) + return; + + ScriptEnviroment::removeUniqueThing(parent); + setUniqueId(parent->getUniqueId()); + parent->eraseAttribute("uid"); +} + void Item::onRemoved() { if(raid) @@ -235,7 +244,7 @@ void Item::onRemoved() void Item::setDefaultSubtype() { - count = 1; + setItemCount(1); const ItemType& it = items[id]; if(it.charges) setCharges(it.charges); @@ -278,13 +287,10 @@ bool Item::floorChange(FloorChange_t change/* = CHANGE_NONE*/) const Player* Item::getHoldingPlayer() { - Cylinder* p = getParent(); - while(p) + for(Cylinder* p = getParent(); p; p = p->getParent()) { if(p->getCreature()) return p->getCreature()->getPlayer(); - - p = p->getParent(); } return NULL; @@ -325,7 +331,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_COUNT: { uint8_t _count; - if(!propStream.GET_UCHAR(_count)) + if(!propStream.getByte(_count)) return ATTR_READ_ERROR; setSubType((uint16_t)_count); @@ -335,7 +341,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_ACTION_ID: { uint16_t aid; - if(!propStream.GET_USHORT(aid)) + if(!propStream.getShort(aid)) return ATTR_READ_ERROR; setAttribute("aid", aid); @@ -345,7 +351,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_UNIQUE_ID: { uint16_t uid; - if(!propStream.GET_USHORT(uid)) + if(!propStream.getShort(uid)) return ATTR_READ_ERROR; setUniqueId(uid); @@ -355,7 +361,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_NAME: { std::string name; - if(!propStream.GET_STRING(name)) + if(!propStream.getString(name)) return ATTR_READ_ERROR; setAttribute("name", name); @@ -365,7 +371,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_PLURALNAME: { std::string name; - if(!propStream.GET_STRING(name)) + if(!propStream.getString(name)) return ATTR_READ_ERROR; setAttribute("pluralname", name); @@ -375,7 +381,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_ARTICLE: { std::string article; - if(!propStream.GET_STRING(article)) + if(!propStream.getString(article)) return ATTR_READ_ERROR; setAttribute("article", article); @@ -385,7 +391,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_ATTACK: { int32_t attack; - if(!propStream.GET_ULONG((uint32_t&)attack)) + if(!propStream.getLong((uint32_t&)attack)) return ATTR_READ_ERROR; setAttribute("attack", attack); @@ -395,7 +401,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_EXTRAATTACK: { int32_t attack; - if(!propStream.GET_ULONG((uint32_t&)attack)) + if(!propStream.getLong((uint32_t&)attack)) return ATTR_READ_ERROR; setAttribute("extraattack", attack); @@ -405,7 +411,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_DEFENSE: { int32_t defense; - if(!propStream.GET_ULONG((uint32_t&)defense)) + if(!propStream.getLong((uint32_t&)defense)) return ATTR_READ_ERROR; setAttribute("defense", defense); @@ -415,7 +421,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_EXTRADEFENSE: { int32_t defense; - if(!propStream.GET_ULONG((uint32_t&)defense)) + if(!propStream.getLong((uint32_t&)defense)) return ATTR_READ_ERROR; setAttribute("extradefense", defense); @@ -425,7 +431,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_ARMOR: { int32_t armor; - if(!propStream.GET_ULONG((uint32_t&)armor)) + if(!propStream.getLong((uint32_t&)armor)) return ATTR_READ_ERROR; setAttribute("armor", armor); @@ -435,7 +441,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_ATTACKSPEED: { int32_t attackSpeed; - if(!propStream.GET_ULONG((uint32_t&)attackSpeed)) + if(!propStream.getLong((uint32_t&)attackSpeed)) return ATTR_READ_ERROR; setAttribute("attackspeed", attackSpeed); @@ -445,7 +451,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_HITCHANCE: { int32_t hitChance; - if(!propStream.GET_ULONG((uint32_t&)hitChance)) + if(!propStream.getLong((uint32_t&)hitChance)) return ATTR_READ_ERROR; setAttribute("hitchance", hitChance); @@ -455,17 +461,27 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_SCRIPTPROTECTED: { uint8_t protection; - if(!propStream.GET_UCHAR(protection)) + if(!propStream.getByte(protection)) return ATTR_READ_ERROR; setAttribute("scriptprotected", protection != 0); break; } + case ATTR_DUALWIELD: + { + uint8_t wield; + if(!propStream.getByte(wield)) + return ATTR_READ_ERROR; + + setAttribute("dualwield", wield != 0); + break; + } + case ATTR_TEXT: { std::string text; - if(!propStream.GET_STRING(text)) + if(!propStream.getString(text)) return ATTR_READ_ERROR; setAttribute("text", text); @@ -475,7 +491,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_WRITTENDATE: { int32_t date; - if(!propStream.GET_ULONG((uint32_t&)date)) + if(!propStream.getLong((uint32_t&)date)) return ATTR_READ_ERROR; setAttribute("date", date); @@ -485,7 +501,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_WRITTENBY: { std::string writer; - if(!propStream.GET_STRING(writer)) + if(!propStream.getString(writer)) return ATTR_READ_ERROR; setAttribute("writer", writer); @@ -495,7 +511,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_DESC: { std::string text; - if(!propStream.GET_STRING(text)) + if(!propStream.getString(text)) return ATTR_READ_ERROR; setAttribute("description", text); @@ -505,7 +521,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_RUNE_CHARGES: { uint8_t charges; - if(!propStream.GET_UCHAR(charges)) + if(!propStream.getByte(charges)) return ATTR_READ_ERROR; setSubType((uint16_t)charges); @@ -515,7 +531,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_CHARGES: { uint16_t charges; - if(!propStream.GET_USHORT(charges)) + if(!propStream.getShort(charges)) return ATTR_READ_ERROR; setSubType(charges); @@ -525,7 +541,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_DURATION: { int32_t duration; - if(!propStream.GET_ULONG((uint32_t&)duration)) + if(!propStream.getLong((uint32_t&)duration)) return ATTR_READ_ERROR; setAttribute("duration", duration); @@ -535,7 +551,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_DECAYING_STATE: { uint8_t state; - if(!propStream.GET_UCHAR(state)) + if(!propStream.getByte(state)) return ATTR_READ_ERROR; if((ItemDecayState_t)state != DECAYING_FALSE) @@ -552,7 +568,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_DEPOT_ID: { uint16_t depot; - if(!propStream.GET_USHORT(depot)) + if(!propStream.getShort(depot)) return ATTR_READ_ERROR; break; @@ -562,7 +578,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_HOUSEDOORID: { uint8_t door; - if(!propStream.GET_UCHAR(door)) + if(!propStream.getByte(door)) return ATTR_READ_ERROR; break; @@ -572,7 +588,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_TELE_DEST: { TeleportDest* dest; - if(!propStream.GET_STRUCT(dest)) + if(!propStream.getStruct(dest)) return ATTR_READ_ERROR; break; @@ -582,7 +598,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_SLEEPERGUID: { uint32_t sleeper; - if(!propStream.GET_ULONG(sleeper)) + if(!propStream.getLong(sleeper)) return ATTR_READ_ERROR; break; @@ -591,7 +607,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_SLEEPSTART: { uint32_t sleepStart; - if(!propStream.GET_ULONG(sleepStart)) + if(!propStream.getLong(sleepStart)) return ATTR_READ_ERROR; break; @@ -601,7 +617,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) case ATTR_CONTAINER_ITEMS: { uint32_t _count; - propStream.GET_ULONG(_count); + propStream.getLong(_count); return ATTR_READ_ERROR; } @@ -630,7 +646,7 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) bool Item::unserializeAttr(PropStream& propStream) { uint8_t attrType = ATTR_END; - while(propStream.GET_UCHAR(attrType) && attrType != ATTR_END) + while(propStream.getByte(attrType) && attrType != ATTR_END) { switch(readAttr((AttrTypes_t)attrType, propStream)) { @@ -652,13 +668,13 @@ bool Item::serializeAttr(PropWriteStream& propWriteStream) const { if(isStackable() || isFluidContainer() || isSplash()) { - propWriteStream.ADD_UCHAR(ATTR_COUNT); - propWriteStream.ADD_UCHAR((uint8_t)getSubType()); + propWriteStream.addByte(ATTR_COUNT); + propWriteStream.addByte((uint8_t)getSubType()); } if(attributes && !attributes->empty()) { - propWriteStream.ADD_UCHAR(ATTR_ATTRIBUTE_MAP); + propWriteStream.addByte(ATTR_ATTRIBUTE_MAP); serializeMap(propWriteStream); } @@ -676,9 +692,9 @@ bool Item::hasProperty(enum ITEMPROPERTY prop) const break; - case MOVEABLE: - if(it.moveable && (!isLoadedFromMap() || (!getUniqueId() - && (!getActionId() || getContainer())))) + case MOVABLE: + if(it.movable && (!loadedFromMap || (!getUniqueId() + && (!getActionId() || !getContainer())))) return true; break; @@ -714,14 +730,14 @@ bool Item::hasProperty(enum ITEMPROPERTY prop) const break; case IMMOVABLEBLOCKSOLID: - if(it.blockSolid && (!it.moveable || (isLoadedFromMap() && + if(it.blockSolid && (!it.movable || (loadedFromMap && (getUniqueId() || (getActionId() && getContainer()))))) return true; break; case IMMOVABLEBLOCKPATH: - if(it.blockPathFind && (!it.moveable || (isLoadedFromMap() && + if(it.blockPathFind && (!it.movable || (loadedFromMap && (getUniqueId() || (getActionId() && getContainer()))))) return true; @@ -734,7 +750,7 @@ bool Item::hasProperty(enum ITEMPROPERTY prop) const break; case IMMOVABLENOFIELDBLOCKPATH: - if(!it.isMagicField() && it.blockPathFind && (!it.moveable || (isLoadedFromMap() && + if(!it.isMagicField() && it.blockPathFind && (!it.movable || (loadedFromMap && (getUniqueId() || (getActionId() && getContainer()))))) return true; @@ -746,6 +762,21 @@ bool Item::hasProperty(enum ITEMPROPERTY prop) const break; + case FLOORCHANGEDOWN: + if(it.floorChange[CHANGE_DOWN]) + return true; + + break; + + case FLOORCHANGEUP: + for(uint16_t i = CHANGE_FIRST; i <= CHANGE_PRE_LAST; ++i) + { + if(it.floorChange[i]) + return true; + } + + break; + default: break; } @@ -772,11 +803,9 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const bool dot = true; if(it.isRune()) { - s << "("; if(!it.runeSpellName.empty()) - s << "\"" << it.runeSpellName << "\", "; + s << "(\"" << it.runeSpellName << "\")"; - s << "Charges:" << subType << ")"; if(it.runeLevel > 0 || it.runeMagLevel > 0 || (it.vocationString != "" && it.wieldInfo == 0)) { s << "." << std::endl << "It can only be used"; @@ -816,6 +845,9 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const if(it.hitChance != -1 || (item && item->getHitChance() != -1)) s << ", Hit% " << std::showpos << (item ? item->getHitChance() : it.hitChance) << std::noshowpos; + + if(it.attackSpeed || (item && item->getAttackSpeed())) + s << ", AS: " << (item ? item->getAttackSpeed() : it.attackSpeed); } else if(it.weaponType != WEAPON_AMMO && it.weaponType != WEAPON_WAND) { @@ -823,13 +855,13 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const { begin = false; s << " (Atk:"; - if(it.abilities.elementType != COMBAT_NONE && it.decayTo < 1) + if(it.hasAbilities() && it.abilities->elementType != COMBAT_NONE) { - s << std::max((int32_t)0, int32_t((item ? item->getAttack() : it.attack) - it.abilities.elementDamage)); + s << std::max((int32_t)0, int32_t((item ? item->getAttack() : it.attack) - it.abilities->elementDamage)); if(it.extraAttack || (item && item->getExtraAttack())) s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos; - s << " physical + " << it.abilities.elementDamage << " " << getCombatName(it.abilities.elementType); + s << " physical + " << it.abilities->elementDamage << " " << getCombatName(it.abilities->elementType); } else { @@ -855,11 +887,8 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const } } - for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; i++) + if(it.attackSpeed || (item && item->getAttackSpeed())) { - if(!it.abilities.skills[i]) - continue; - if(begin) { begin = false; @@ -868,73 +897,263 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const else s << ", "; - s << getSkillName(i) << " " << std::showpos << (int32_t)it.abilities.skills[i] << std::noshowpos; + s << "AS: " << (item ? item->getAttackSpeed() : it.attackSpeed); } - if(it.abilities.stats[STAT_MAGICLEVEL]) + if(it.hasAbilities()) { - if(begin) + for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { - begin = false; - s << " ("; + if(!it.abilities->skills[i]) + continue; + + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << getSkillName(i) << " " << std::showpos << (int32_t)it.abilities->skills[i] << std::noshowpos; } - else - s << ", "; - s << "magic level " << std::showpos << (int32_t)it.abilities.stats[STAT_MAGICLEVEL] << std::noshowpos; - } + if(it.abilities->stats[STAT_MAGICLEVEL]) + { + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; - int32_t show = it.abilities.absorb[COMBAT_FIRST]; - for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i++) - { - if(it.abilities.absorb[i] == show) - continue; + s << "magic level " << std::showpos << (int32_t)it.abilities->stats[STAT_MAGICLEVEL] << std::noshowpos; + } - show = 0; - break; - } + int32_t show = it.abilities->absorb[COMBAT_ALL]; + if(!show) + { + bool tmp = true; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) + { + if(!it.abilities->absorb[i]) + continue; - if(!show) - { - bool tmp = true; - for(uint32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) + if(tmp) + { + tmp = false; + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "protection "; + } + else + s << ", "; + + s << getCombatName((CombatType_t)i) << " " << std::showpos << it.abilities->absorb[i] << std::noshowpos << "%"; + } + } + else { - if(!it.abilities.absorb[i]) - continue; + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; - if(tmp) + s << "protection all " << std::showpos << show << std::noshowpos << "%"; + } + + show = it.abilities->fieldAbsorb[COMBAT_ALL]; + if(!show) + { + bool tmp = true; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) { - if(begin) + if(!it.abilities->fieldAbsorb[i]) + continue; + + if(tmp) { - begin = false; - s << " ("; + tmp = false; + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "protection "; } else s << ", "; - tmp = false; - s << "protection "; + s << getCombatName((CombatType_t)i) << " field " << std::showpos << it.abilities->absorb[i] << std::noshowpos << "%"; + } + } + else + { + if(begin) + { + begin = false; + s << " ("; } else s << ", "; - s << getCombatName((CombatType_t)i) << " " << std::showpos << it.abilities.absorb[i] << std::noshowpos << "%"; + s << "protection all fields " << std::showpos << show << std::noshowpos << "%"; } - } - else - { - if(begin) + + show = it.abilities->reflect[REFLECT_CHANCE][COMBAT_ALL]; + if(!show) { - begin = false; - s << " ("; + bool tmp = true; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) + { + if(!it.abilities->reflect[REFLECT_CHANCE][i] || !it.abilities->reflect[REFLECT_PERCENT][i]) + continue; + + if(tmp) + { + tmp = false; + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "reflect: "; + } + else + s << ", "; + + s << it.abilities->reflect[REFLECT_CHANCE][i] << "% for "; + if(it.abilities->reflect[REFLECT_PERCENT][i] > 99) + s << "whole"; + else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 75) + s << "huge"; + else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 50) + s << "medium"; + else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 25) + s << "small"; + else + s << "tiny"; + + s << getCombatName((CombatType_t)i); + } + + if(!tmp) + s << " damage"; } else - s << ", "; + { + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + int32_t tmp = it.abilities->reflect[REFLECT_PERCENT][COMBAT_ALL]; + s << "reflect: " << show << "% for "; + if(tmp) + { + if(tmp > 99) + s << "whole"; + else if(tmp >= 75) + s << "huge"; + else if(tmp >= 50) + s << "medium"; + else if(tmp >= 25) + s << "small"; + else + s << "tiny"; + } + else + s << "mixed"; + + s << " damage"; + } + + if(it.abilities->speed) + { + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "speed " << std::showpos << (int32_t)(it.abilities->speed / 2) << std::noshowpos; + } - s << "protection all " << std::showpos << show << std::noshowpos << "%"; + if(it.abilities->invisible) + { + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "invisibility"; + } + + if(it.abilities->regeneration) + { + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "faster regeneration"; + } + + if(it.abilities->manaShield) + { + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "mana shield"; + } + + if(hasBitSet(CONDITION_DRUNK, it.abilities->conditionSuppressions)) + { + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "hard drinking"; + } } - if(it.abilities.speed) + if(it.dualWield || (item && item->isDualWield())) { if(begin) { @@ -944,7 +1163,7 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const else s << ", "; - s << "speed " << std::showpos << (int32_t)(it.abilities.speed / 2) << std::noshowpos; + s << "dual wielding"; } if(!begin) @@ -963,165 +1182,220 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const begin = false; } - for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; i++) + if(it.hasAbilities()) { - if(!it.abilities.skills[i]) - continue; - - if(begin) + for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { - begin = false; - s << " ("; - } - else - s << ", "; + if(!it.abilities->skills[i]) + continue; - s << getSkillName(i) << " " << std::showpos << (int32_t)it.abilities.skills[i] << std::noshowpos; - } + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; - if(it.abilities.stats[STAT_MAGICLEVEL]) - { - if(begin) - { - begin = false; - s << " ("; + s << getSkillName(i) << " " << std::showpos << (int32_t)it.abilities->skills[i] << std::noshowpos; } - else - s << ", "; - s << "magic level " << std::showpos << (int32_t)it.abilities.stats[STAT_MAGICLEVEL] << std::noshowpos; - } - - int32_t show = it.abilities.absorb[COMBAT_FIRST]; - for(int32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i++) - { - if(it.abilities.absorb[i] == show) - continue; + if(it.abilities->stats[STAT_MAGICLEVEL]) + { + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; - show = 0; - break; - } + s << "magic level " << std::showpos << (int32_t)it.abilities->stats[STAT_MAGICLEVEL] << std::noshowpos; + } - if(!show) - { - bool tmp = true; - for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) + int32_t show = it.abilities->absorb[COMBAT_ALL]; + if(!show) { - if(!it.abilities.absorb[i]) - continue; - - if(tmp) + bool tmp = true; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) { - tmp = false; - if(begin) + if(!it.abilities->absorb[i]) + continue; + + if(tmp) { - begin = false; - s << " ("; + tmp = false; + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "protection "; } else s << ", "; - s << "protection "; + s << getCombatName((CombatType_t)i) << " " << std::showpos << it.abilities->absorb[i] << std::noshowpos << "%"; + } + } + else + { + if(begin) + { + begin = false; + s << " ("; } else s << ", "; - s << getCombatName((CombatType_t)i) << " " << std::showpos << it.abilities.absorb[i] << std::noshowpos << "%"; + s << "protection all " << std::showpos << show << std::noshowpos << "%"; } - } - else - { - if(begin) + + show = it.abilities->reflect[REFLECT_CHANCE][COMBAT_ALL]; + if(!show) { - begin = false; - s << " ("; - } - else - s << ", "; + bool tmp = true; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) + { + if(!it.abilities->reflect[REFLECT_CHANCE][i] || !it.abilities->reflect[REFLECT_PERCENT][i]) + continue; - s << "protection all " << std::showpos << show << std::noshowpos << "%"; - } + if(tmp) + { + tmp = false; + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "reflect: "; + } + else + s << ", "; - show = it.abilities.reflect[REFLECT_CHANCE][COMBAT_FIRST]; - for(int32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i++) - { - if(it.abilities.reflect[REFLECT_CHANCE][i] == show) - continue; + s << it.abilities->reflect[REFLECT_CHANCE][i] << "% for "; + if(it.abilities->reflect[REFLECT_PERCENT][i] > 99) + s << "whole"; + else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 75) + s << "huge"; + else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 50) + s << "medium"; + else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 25) + s << "small"; + else + s << "tiny"; - show = 0; - break; - } + s << getCombatName((CombatType_t)i); + } - if(!show) - { - bool tmp = true; - for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) + if(!tmp) + s << " damage"; + } + else { - if(!it.abilities.reflect[REFLECT_CHANCE][i]) - continue; + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + int32_t tmp = it.abilities->reflect[REFLECT_PERCENT][COMBAT_ALL]; + s << "reflect: " << show << "% for "; if(tmp) { - tmp = false; - if(begin) - { - begin = false; - s << " ("; - } + if(tmp > 99) + s << "whole"; + else if(tmp >= 75) + s << "huge"; + else if(tmp >= 50) + s << "medium"; + else if(tmp >= 25) + s << "small"; else - s << ", "; + s << "tiny"; + } + else + s << "mixed"; + + s << " damage"; + } - s << "reflect "; + if(it.abilities->speed) + { + if(begin) + { + begin = false; + s << " ("; } else s << ", "; - std::string ss = "no"; - if(it.abilities.reflect[REFLECT_PERCENT][i] > 99) - ss = "whole"; - else if(it.abilities.reflect[REFLECT_PERCENT][i] >= 75) - ss = "huge"; - else if(it.abilities.reflect[REFLECT_PERCENT][i] >= 50) - ss = "medium"; - else if(it.abilities.reflect[REFLECT_PERCENT][i] >= 25) - ss = "small"; - else if(it.abilities.reflect[REFLECT_PERCENT][i] > 0) - ss = "tiny"; - - s << getCombatName((CombatType_t)i) << " " << std::showpos << it.abilities.reflect[REFLECT_PERCENT][i] << std::noshowpos << "% for " << ss; + s << "speed " << std::showpos << (int32_t)(it.abilities->speed / 2) << std::noshowpos; } - if(!tmp) - s << " damage"; - } - else - { - if(begin) + if(it.abilities->invisible) { - begin = false; - s << " ("; + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "invisibility"; } - else - s << ", "; - s << "reflect all " << std::showpos << show << std::noshowpos << "% for mixed damage"; - } + if(it.abilities->regeneration) + { + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; - if(it.abilities.speed) - { - if(begin) + s << "faster regeneration"; + } + + if(it.abilities->manaShield) { - begin = false; - s << " ("; + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; + + s << "mana shield"; } - else - s << ", "; - s << "speed " << std::showpos << (int32_t)(it.abilities.speed / 2) << std::noshowpos; - } + if(hasBitSet(CONDITION_DRUNK, it.abilities->conditionSuppressions)) + { + if(begin) + { + begin = false; + s << " ("; + } + else + s << ", "; - if(!begin) - s << ")"; + s << "hard drinking"; + } + + if(!begin) + s << ")"; + } } else if(it.isContainer()) s << " (Vol:" << (int32_t)it.maxItems << ")"; @@ -1144,7 +1418,7 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const } else if(it.allowDistRead) { - s << std::endl; + s << "." << std::endl; if(item && !item->getText().empty()) { if(lookDistance <= 4) @@ -1163,12 +1437,10 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const std::string text = item->getText(); s << text; - if(!text.empty()) - { - char end = *text.rbegin(); - if(end == '?' || end == '!' || end == '.') - dot = false; - } + + char end = *text.rbegin(); + if(end == '?' || end == '!' || end == '.') + dot = false; } else s << "You are too far away to read it"; @@ -1188,14 +1460,33 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const if(item && item->hasIntegerAttribute("duration")) { int32_t duration = item->getDuration() / 1000; - s << " that has energy for "; - - if(duration >= 120) - s << duration / 60 << " minutes left"; - else if(duration > 60) - s << "1 minute left"; + s << " that will expire in "; + if(duration >= 86400) + { + uint16_t days = duration / 86400; + uint16_t hours = (duration % 86400) / 3600; + s << days << " day" << (days > 1 ? "s" : ""); + if(hours > 0) + s << " and " << hours << " hour" << (hours > 1 ? "s" : ""); + } + else if(duration >= 3600) + { + uint16_t hours = duration / 3600; + uint16_t minutes = (duration % 3600) / 60; + s << hours << " hour" << (hours > 1 ? "s" : ""); + if(hours > 0) + s << " and " << minutes << " minute" << (minutes > 1 ? "s" : ""); + } + else if(duration >= 60) + { + uint16_t minutes = duration / 60; + uint16_t seconds = duration % 60; + s << minutes << " minute" << (minutes > 1 ? "s" : ""); + if(seconds > 0) + s << " and " << seconds << " second" << (seconds > 1 ? "s" : ""); + } else - s << " less than a minute left"; + s << duration << " second" << (duration > 1 ? "s" : ""); } else s << " that is brand-new"; @@ -1235,7 +1526,7 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const { std::string tmp; if(!item) - tmp = getWeightDescription(it.weight, it.stackable, subType); + tmp = getWeightDescription(it.weight, it.stackable && it.showCount, subType); else tmp = item->getWeightDescription(); @@ -1243,25 +1534,12 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const s << std::endl << tmp; } - if(it.abilities.elementType != COMBAT_NONE && it.decayTo > 0) - { - s << std::endl << "It is temporarily enchanted with " << getCombatName(it.abilities.elementType) << " ("; - s << std::max((int32_t)0, int32_t((item ? item->getAttack() : it.attack) - it.abilities.elementDamage)); - if(it.extraAttack || (item && item->getExtraAttack())) - s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos; - - s << " physical + " << it.abilities.elementDamage << " " << getCombatName(it.abilities.elementType) << " damage)."; - } - - std::string str; if(item && !item->getSpecialDescription().empty()) - str = item->getSpecialDescription(); + s << std::endl << item->getSpecialDescription(); else if(!it.description.empty() && lookDistance <= 1) - str = it.description; - - if(str.empty()) - return s.str(); + s << std::endl << it.description; + std::string str = s.str(); if(str.find("|PLAYERNAME|") != std::string::npos) { std::string tmp = "You"; @@ -1320,11 +1598,10 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const replaceString(str, "|TIME|", ss.str()); ss.str(""); - replaceString(str, "|DATE|", formatDateShort(now, true)); + replaceString(str, "|DATE|", formatDateEx(now)); } - s << std::endl << str; - return s.str(); + return str; } std::string Item::getNameDescription(const ItemType& it, const Item* item/* = NULL*/, int32_t subType/* = -1*/, bool addArticle/* = true*/) @@ -1333,7 +1610,7 @@ std::string Item::getNameDescription(const ItemType& it, const Item* item/* = NU subType = item->getSubType(); std::stringstream s; - if(it.name.length() || (item && item->getName().length())) + if(it.loaded || (item && !item->getName().empty())) { if(subType > 1 && it.stackable && it.showCount) s << subType << " " << (item ? item->getPluralName() : it.pluralName); @@ -1350,8 +1627,10 @@ std::string Item::getNameDescription(const ItemType& it, const Item* item/* = NU s << (item ? item->getName() : it.name); } } - else + else if(it.name.empty()) s << "an item of type " << it.id << ", please report it to gamemaster"; + else + s << "an item '" << it.name << "', please report it to gamemaster"; return s.str(); } @@ -1370,22 +1649,32 @@ std::string Item::getWeightDescription(double weight, bool stackable, uint32_t c return s.str(); } -void Item::setActionId(int32_t aid) +void Item::setActionId(int32_t aid, bool callEvent/* = true*/) { - if(getActionId()) - g_moveEvents->onRemoveTileItem(getTile(), this); + Tile* tile = NULL; + if(callEvent) + tile = getTile(); + + if(tile && getActionId()) + g_moveEvents->onRemoveTileItem(tile, this); setAttribute("aid", aid); - g_moveEvents->onAddTileItem(getTile(), this); + if(tile) + g_moveEvents->onAddTileItem(tile, this); } -void Item::resetActionId() +void Item::resetActionId(bool callEvent/* = true*/) { if(!getActionId()) return; + Tile* tile = NULL; + if(callEvent) + tile = getTile(); + eraseAttribute("aid"); - g_moveEvents->onAddTileItem(getTile(), this); + if(tile) + g_moveEvents->onAddTileItem(tile, this); } void Item::setUniqueId(int32_t uid) @@ -1402,7 +1691,7 @@ bool Item::canDecay() if(isRemoved()) return false; - if(isLoadedFromMap() && (getUniqueId() || (getActionId() && getContainer()))) + if(loadedFromMap && (getUniqueId() || (getActionId() && getContainer()))) return false; const ItemType& it = Item::items[id]; diff --git a/item.h b/item.h index d09f63f..a9d5920 100644 --- a/item.h +++ b/item.h @@ -51,12 +51,14 @@ enum ITEMPROPERTY BLOCKPATH, ISVERTICAL, ISHORIZONTAL, - MOVEABLE, + MOVABLE, IMMOVABLEBLOCKSOLID, IMMOVABLEBLOCKPATH, IMMOVABLENOFIELDBLOCKPATH, NOFIELDBLOCKPATH, - SUPPORTHANGABLE + SUPPORTHANGABLE, + FLOORCHANGEDOWN, + FLOORCHANGEUP }; enum TradeEvents_t @@ -110,6 +112,7 @@ enum AttrTypes_t ATTR_SHOOTRANGE = 40, ATTR_ARTICLE = 41, ATTR_SCRIPTPROTECTED = 42, + ATTR_DUALWIELD = 43, ATTR_ATTRIBUTE_MAP = 128 }; @@ -130,6 +133,7 @@ struct TeleportDest #pragma pack() typedef std::list ItemList; +typedef std::vector ItemVector; class Item : virtual public Thing, public ItemAttributes { @@ -137,7 +141,7 @@ class Item : virtual public Thing, public ItemAttributes static Items items; //Factory member to create item of right type based on type - static Item* CreateItem(const uint16_t type, uint16_t amount = 1); + static Item* CreateItem(const uint16_t type, uint16_t amount = 0); static Item* CreateItem(PropStream& propStream); static bool loadItem(xmlNodePtr node, Container* parent); @@ -150,6 +154,7 @@ class Item : virtual public Thing, public ItemAttributes virtual Item* clone() const; virtual void copyAttributes(Item* item); + void makeUnique(Item* parent); virtual Item* getItem() {return this;} virtual const Item* getItem() const {return this;} @@ -185,7 +190,7 @@ class Item : virtual public Thing, public ItemAttributes virtual std::string getDescription(int32_t lookDistance) const {return getDescription(items[id], lookDistance, this);} std::string getNameDescription() const {return getNameDescription(items[id], this);} - std::string getWeightDescription() const {return getWeightDescription(getWeight(), items[id].stackable, count);} + std::string getWeightDescription() const {return getWeightDescription(getWeight(), items[id].stackable && items[id].showCount, count);} Player* getHoldingPlayer(); const Player* getHoldingPlayer() const; @@ -194,7 +199,7 @@ class Item : virtual public Thing, public ItemAttributes virtual Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream); virtual bool unserializeAttr(PropStream& propStream); virtual bool serializeAttr(PropWriteStream& propWriteStream) const; - virtual bool unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream) {return unserializeAttr(propStream);} + virtual bool unserializeItemNode(FileLoader&, NODE, PropStream& propStream) {return unserializeAttr(propStream);} // Item attributes void setDuration(int32_t time) {setAttribute("duration", time);} @@ -217,17 +222,19 @@ class Item : virtual public Thing, public ItemAttributes void resetWriter() {eraseAttribute("writer");} std::string getWriter() const; - void setActionId(int32_t aid); - void resetActionId(); + void setActionId(int32_t aid, bool callEvent = true); + void resetActionId(bool callEvent = true); int32_t getActionId() const; void setUniqueId(int32_t uid); int32_t getUniqueId() const; void setCharges(uint16_t charges) {setAttribute("charges", charges);} + void resetCharges() {eraseAttribute("charges");} uint16_t getCharges() const; void setFluidType(uint16_t fluidType) {setAttribute("fluidtype", fluidType);} + void resetFluidType() {eraseAttribute("fluidtype");} uint16_t getFluidType() const; void setOwner(uint32_t owner) {setAttribute("owner", (int32_t)owner);} @@ -242,7 +249,9 @@ class Item : virtual public Thing, public ItemAttributes std::string getName() const; std::string getPluralName() const; std::string getArticle() const; + bool isScriptProtected() const; + bool isDualWield() const; int32_t getAttack() const; int32_t getExtraAttack() const; @@ -262,8 +271,8 @@ class Item : virtual public Thing, public ItemAttributes virtual double getWeight() const; void getLight(LightInfo& lightInfo); - int32_t getMaxWriteLength() const {return items[id].maxTextLen;} - int32_t getWorth() const {return getItemCount() * items[id].worth;} + int32_t getMaxWriteLength() const {return items[id].maxTextLength;} + int32_t getWorth() const {return count * items[id].worth;} virtual int32_t getThrowRange() const {return (isPickupable() ? 15 : 2);} bool floorChange(FloorChange_t change = CHANGE_NONE) const; @@ -271,14 +280,15 @@ class Item : virtual public Thing, public ItemAttributes bool hasProperty(enum ITEMPROPERTY prop) const; bool hasSubType() const {return items[id].hasSubType();} - bool hasCharges() const {return items[id].charges;} + bool hasCharges() const {return hasIntegerAttribute("charges");} bool canDecay(); virtual bool canRemove() const {return true;} virtual bool canTransform() const {return true;} bool canWriteText() const {return items[id].canWriteText;} - virtual bool isPushable() const {return !isNotMoveable();} + virtual bool isPushable() const {return isMovable();} + virtual bool isBlocking(const Creature*) const {return items[id].blockSolid;} bool isGroundTile() const {return items[id].isGroundTile();} bool isContainer() const {return items[id].isContainer();} bool isSplash() const {return items[id].isSplash();} @@ -292,23 +302,25 @@ class Item : virtual public Thing, public ItemAttributes bool isTrashHolder() const {return items[id].isTrashHolder();} bool isBed() const {return items[id].isBed();} bool isRune() const {return items[id].isRune();} - bool isBlocking(const Creature* creature) const {return items[id].blockSolid;} bool isStackable() const {return items[id].stackable;} bool isAlwaysOnTop() const {return items[id].alwaysOnTop;} - bool isNotMoveable() const {return !items[id].moveable;} - bool isMoveable() const {return items[id].moveable;} + bool isMovable() const {return items[id].movable;} bool isPickupable() const {return items[id].pickupable;} - bool isUseable() const {return items[id].useable;} + bool isUsable() const {return items[id].usable;} bool isHangable() const {return items[id].isHangable;} - bool isRoteable() const {const ItemType& it = items[id]; return it.rotable && it.rotateTo;} + bool isRoteable() const {const ItemType& it = items[id]; return it.rotable && it.rotateTo != 0;} bool isWeapon() const {return (items[id].weaponType != WEAPON_NONE);} bool isReadable() const {return items[id].canReadText;} + bool isWare() const {return items[id].wareId != 0;} bool isLoadedFromMap() const {return loadedFromMap;} void setLoadedFromMap(bool value) {loadedFromMap = value;} + CombatType_t getElementType() const {return items[id].hasAbilities() ? items[id].abilities->elementType : COMBAT_NONE;} + int32_t getElementDamage() const {return items[id].hasAbilities() ? items[id].abilities->elementDamage : 0;} + uint16_t getItemCount() const {return count;} - void setItemCount(uint16_t n) {count = n;} + void setItemCount(uint16_t n) {count = std::max((uint16_t)1, n);} uint16_t getSubType() const; void setSubType(uint16_t n); @@ -321,15 +333,16 @@ class Item : virtual public Thing, public ItemAttributes setDuration(duration); } + void setDefaultSubtype(); + Raid* getRaid() {return raid;} void setRaid(Raid* _raid) {raid = _raid;} + virtual void __startDecaying(); virtual void onRemoved(); - virtual bool onTradeEvent(TradeEvents_t event, Player* owner, Player* seller) {return true;} + virtual bool onTradeEvent(TradeEvents_t, Player*, Player*) {return true;} - void setDefaultSubtype(); - virtual void __startDecaying(); - static uint32_t countByType(const Item* item, int32_t checkType, bool multiCount); + static uint32_t countByType(const Item* item, int32_t checkType); protected: uint16_t id; @@ -447,6 +460,15 @@ inline int32_t Item::getShootRange() const return items[id].shootRange; } +inline bool Item::isDualWield() const +{ + const bool* v = getBooleanAttribute("dualwield"); + if(v) + return *v; + + return items[id].dualWield; +} + inline void Item::decreaseDuration(int32_t time) { const int32_t* v = getIntegerAttribute("duration"); @@ -478,7 +500,7 @@ inline std::string Item::getText() const if(v) return *v; - return ""; + return items[id].text; } inline time_t Item::getDate() const @@ -487,7 +509,7 @@ inline time_t Item::getDate() const if(v) return (time_t)*v; - return 0; + return items[id].date; } inline std::string Item::getWriter() const @@ -496,7 +518,7 @@ inline std::string Item::getWriter() const if(v) return *v; - return ""; + return items[id].writer; } inline int32_t Item::getActionId() const @@ -562,17 +584,11 @@ inline ItemDecayState_t Item::getDecaying() const return DECAYING_FALSE; } -inline uint32_t Item::countByType(const Item* item, int32_t checkType, bool multiCount) +inline uint32_t Item::countByType(const Item* item, int32_t checkType) { if(checkType != -1 && checkType != (int32_t)item->getSubType()) return 0; - if(multiCount) - return item->getItemCount(); - - if(item->isRune()) - return item->getCharges(); - return item->getItemCount(); } #endif diff --git a/itemattributes.cpp b/itemattributes.cpp index fe9f7ca..bf4d50c 100644 --- a/itemattributes.cpp +++ b/itemattributes.cpp @@ -35,7 +35,7 @@ void ItemAttributes::eraseAttribute(const std::string& key) { if(!attributes) return; - + AttributeMap::iterator it = attributes->find(key); if(it != attributes->end()) attributes->erase(it); @@ -47,18 +47,6 @@ void ItemAttributes::setAttribute(const std::string& key, boost::any value) (*attributes)[key].set(value); } -boost::any ItemAttributes::getAttribute(const std::string& key) const -{ - if(!attributes) - return boost::any(); - - AttributeMap::iterator it = attributes->find(key); - if(it != attributes->end()) - return it->second.get(); - - return boost::any(); -} - void ItemAttributes::setAttribute(const std::string& key, const std::string& value) { createAttributes(); @@ -83,6 +71,18 @@ void ItemAttributes::setAttribute(const std::string& key, bool value) (*attributes)[key].set(value); } +boost::any ItemAttributes::getAttribute(const std::string& key) const +{ + if(!attributes) + return boost::any(); + + AttributeMap::iterator it = attributes->find(key); + if(it != attributes->end()) + return it->second.get(); + + return boost::any(); +} + const std::string* ItemAttributes::getStringAttribute(const std::string& key) const { if(!attributes) @@ -156,16 +156,17 @@ ItemAttribute& ItemAttribute::operator=(const ItemAttribute& o) type = NONE; break; } - + return *this; } void ItemAttribute::clear() { - type = NONE; if(type == STRING) (reinterpret_cast(&data))->~basic_string(); + + type = NONE; } void ItemAttribute::set(const std::string& s) @@ -263,7 +264,7 @@ boost::any ItemAttribute::get() const case STRING: return *reinterpret_cast(&data); case INTEGER: - return *reinterpret_cast(&data); + return *reinterpret_cast(&data); case FLOAT: return *reinterpret_cast(&data); case BOOLEAN: @@ -278,14 +279,14 @@ boost::any ItemAttribute::get() const bool ItemAttributes::unserializeMap(PropStream& stream) { uint16_t n; - if(!stream.GET_USHORT(n)) + if(!stream.getShort(n)) return true; createAttributes(); while(n--) { std::string key; - if(!stream.GET_STRING(key)) + if(!stream.getString(key)) return false; ItemAttribute attr; @@ -300,15 +301,15 @@ bool ItemAttributes::unserializeMap(PropStream& stream) void ItemAttributes::serializeMap(PropWriteStream& stream) const { - stream.ADD_USHORT((uint16_t)std::min((size_t)0xFFFF, attributes->size())); + stream.addShort((uint16_t)std::min((size_t)0xFFFF, attributes->size())); AttributeMap::const_iterator it = attributes->begin(); for(int32_t i = 0; it != attributes->end() && i <= 0xFFFF; ++it, ++i) { std::string key = it->first; if(key.size() > 0xFFFF) - stream.ADD_STRING(key.substr(0, 0xFFFF)); + stream.addString(key.substr(0, 0xFFFF)); else - stream.ADD_STRING(key); + stream.addString(key); it->second.serialize(stream); } @@ -317,13 +318,13 @@ void ItemAttributes::serializeMap(PropWriteStream& stream) const bool ItemAttribute::unserialize(PropStream& stream) { uint8_t type = 0; - stream.GET_UCHAR(type); + stream.getByte(type); switch(type) { case STRING: { std::string v; - if(!stream.GET_LSTRING(v)) + if(!stream.getLongString(v)) return false; set(v); @@ -332,7 +333,7 @@ bool ItemAttribute::unserialize(PropStream& stream) case INTEGER: { uint32_t v; - if(!stream.GET_ULONG(v)) + if(!stream.getLong(v)) return false; set(*reinterpret_cast(&v)); @@ -340,8 +341,8 @@ bool ItemAttribute::unserialize(PropStream& stream) } case FLOAT: { - uint32_t v; - if(!stream.GET_ULONG(v)) + float v; + if(!stream.getFloat(v)) return false; set(*reinterpret_cast(&v)); @@ -350,7 +351,7 @@ bool ItemAttribute::unserialize(PropStream& stream) case BOOLEAN: { uint8_t v; - if(!stream.GET_UCHAR(v)) + if(!stream.getByte(v)) return false; set(v != 0); @@ -364,20 +365,20 @@ bool ItemAttribute::unserialize(PropStream& stream) void ItemAttribute::serialize(PropWriteStream& stream) const { - stream.ADD_UCHAR((uint8_t)type); + stream.addByte((uint8_t)type); switch(type) { case STRING: - stream.ADD_LSTRING(*getString()); + stream.addLongString(*getString()); break; case INTEGER: - stream.ADD_ULONG(*(uint32_t*)getInteger()); + stream.addLong(*(uint32_t*)getInteger()); break; case FLOAT: - stream.ADD_ULONG(*(uint32_t*)getFloat()); + stream.addLong(*(uint32_t*)getFloat()); break; case BOOLEAN: - stream.ADD_UCHAR(*(uint8_t*)getBoolean()); + stream.addByte(*(uint8_t*)getBoolean()); default: break; } diff --git a/itemattributes.h b/itemattributes.h index 98404fb..75267d5 100644 --- a/itemattributes.h +++ b/itemattributes.h @@ -90,10 +90,10 @@ class ItemAttributes const float* getFloatAttribute(const std::string& key) const; const bool* getBooleanAttribute(const std::string& key) const; - bool hasStringAttribute(const std::string& key) const {return getStringAttribute(key);} - bool hasIntegerAttribute(const std::string& key) const {return getIntegerAttribute(key);} - bool hasFloatAttribute(const std::string& key) const {return getFloatAttribute(key);} - bool hasBooleanAttribute(const std::string& key) const {return getBooleanAttribute(key);} + bool hasStringAttribute(const std::string& key) const {return getStringAttribute(key) != NULL;} + bool hasIntegerAttribute(const std::string& key) const {return getIntegerAttribute(key) != NULL;} + bool hasFloatAttribute(const std::string& key) const {return getFloatAttribute(key) != NULL;} + bool hasBooleanAttribute(const std::string& key) const {return getBooleanAttribute(key) != NULL;} protected: void createAttributes(); diff --git a/itemloader.h b/itemloader.h index 4d84b21..2ee978d 100644 --- a/itemloader.h +++ b/itemloader.h @@ -61,7 +61,26 @@ enum clientVersion_t CLIENT_VERSION_841 = 13, CLIENT_VERSION_842 = 14, CLIENT_VERSION_850 = 15, - CLIENT_VERSION_854 = 16 + CLIENT_VERSION_854_OLD = 16, + CLIENT_VERSION_854 = 17, + CLIENT_VERSION_855 = 18, + CLIENT_VERSION_860_OLD = 19, + CLIENT_VERSION_860 = 20, + CLIENT_VERSION_861 = 21, + CLIENT_VERSION_862 = 22, + CLIENT_VERSION_870 = 23, + CLIENT_VERSION_871 = 24, + CLIENT_VERSION_872 = 25, + CLIENT_VERSION_873 = 26, + CLIENT_VERSION_900 = 27, + CLIENT_VERSION_910 = 28, + CLIENT_VERSION_920 = 29, + CLIENT_VERSION_940 = 30, + CLIENT_VERSION_944_V1 = 31, + CLIENT_VERSION_944_V2 = 32, + CLIENT_VERSION_944_V3 = 33, + CLIENT_VERSION_944_V4 = 34, + CLIENT_VERSION_946 = 35 }; enum rootattrib_ @@ -93,17 +112,15 @@ enum itemattrib_t ITEM_ATTR_07, ITEM_ATTR_08, ITEM_ATTR_LIGHT, - - //1-byte aligned ITEM_ATTR_DECAY2, ITEM_ATTR_WEAPON2, ITEM_ATTR_AMU2, ITEM_ATTR_ARMOR2, ITEM_ATTR_WRITEABLE2, ITEM_ATTR_LIGHT2, - ITEM_ATTR_TOPORDER, ITEM_ATTR_WRITEABLE3, + ITEM_ATTR_WAREID, ITEM_ATTR_LAST }; @@ -113,9 +130,9 @@ enum itemflags_t FLAG_BLOCK_PROJECTILE = 1 << 1, FLAG_BLOCK_PATHFIND = 1 << 2, FLAG_HAS_HEIGHT = 1 << 3, - FLAG_USEABLE = 1 << 4, + FLAG_USABLE = 1 << 4, FLAG_PICKUPABLE = 1 << 5, - FLAG_MOVEABLE = 1 << 6, + FLAG_MOVABLE = 1 << 6, FLAG_STACKABLE = 1 << 7, FLAG_FLOORCHANGEDOWN = 1 << 8, FLAG_FLOORCHANGENORTH = 1 << 9, @@ -131,8 +148,10 @@ enum itemflags_t FLAG_CANNOTDECAY = 1 << 19, FLAG_ALLOWDISTREAD = 1 << 20, FLAG_UNUSED = 1 << 21, - FLAG_CLIENTCHARGES = 1 << 22, - FLAG_LOOKTHROUGH = 1 << 23 + FLAG_CLIENTCHARGES = 1 << 22, //deprecated + FLAG_LOOKTHROUGH = 1 << 23, + FLAG_ANIMATION = 1 << 24, + FLAG_WALKSTACK = 1 << 25 }; #pragma pack(1) diff --git a/items.cpp b/items.cpp index 15df6aa..91ebb2d 100644 --- a/items.cpp +++ b/items.cpp @@ -20,13 +20,16 @@ #include "items.h" #include "condition.h" -#include "weapons.h" - #include "configmanager.h" + +#include "movement.h" +#include "weapons.h" #include "spells.h" extern Spells* g_spells; extern ConfigManager g_config; +extern MoveEvents* g_moveEvents; +extern Weapons* g_weapons; uint32_t Items::dwMajorVersion = 0; uint32_t Items::dwMinorVersion = 0; @@ -34,11 +37,12 @@ uint32_t Items::dwBuildNumber = 0; ItemType::ItemType() { + abilities = NULL; group = ITEM_GROUP_NONE; type = ITEM_TYPE_NONE; - stackable = useable = alwaysOnTop = lookThrough = pickupable = rotable = hasHeight = forceSerialize = false; - blockSolid = blockProjectile = blockPathFind = allowPickupable = false; - moveable = true; + stackable = usable = alwaysOnTop = lookThrough = pickupable = rotable = hasHeight = forceSerialize = false; + loaded = blockSolid = blockProjectile = blockPathFind = allowPickupable = isAnimation = cache = false; + movable = walkStack = true; alwaysOnTopOrder = 0; rotateTo = 0; @@ -54,7 +58,7 @@ ItemType::ItemType() weight = 0; //weight of the item, e.g. throwing distance depends on it showCount = true; weaponType = WEAPON_NONE; - slotPosition = SLOTP_HAND | SLOTP_AMMO; + slotPosition = SLOTP_HAND; wieldPosition = SLOT_HAND; ammoType = AMMO_NONE; ammoAction = AMMOACTION_NONE; @@ -69,18 +73,18 @@ ItemType::ItemType() stopTime = false; corpseType = RACE_NONE; fluidSource = FLUID_NONE; - clientCharges = false; allowDistRead = false; isVertical = isHorizontal = isHangable = false; lightLevel = lightColor = 0; - maxTextLen = 0; + maxTextLength = 0; canReadText = canWriteText = false; - writeOnceItemId = 0; + date = 0; + writeOnceItemId = wareId = 0; - transformEquipTo = transformDeEquipTo = 0; - showDuration = showCharges = showAttributes = false; + transformEquipTo = transformDeEquipTo = transformUseTo = 0; + showDuration = showCharges = showAttributes = dualWield = false; charges = 0; hitChance = maxHitChance = breakChance = -1; shootRange = 1; @@ -88,14 +92,14 @@ ItemType::ItemType() condition = NULL; combatType = COMBAT_NONE; - replaceable = true; + replacable = true; worth = 0; bedPartnerDir = NORTH; - transformUseTo[PLAYERSEX_FEMALE] = 0; - transformUseTo[PLAYERSEX_MALE] = 0; - transformToFree = 0; + transformBed[PLAYERSEX_FEMALE] = transformBed[PLAYERSEX_MALE] = 0; + levelDoor = 0; + specialDoor = closingDoor = false; memset(floorChange, 0, sizeof(floorChange)); } @@ -107,21 +111,28 @@ ItemType::~ItemType() void Items::clear() { - //TODO: clear items? moneyMap.clear(); randomizationMap.clear(); + reverseItemMap.clear(); + if(items.size()) + items.clear(); } bool Items::reload() { - //TODO: reload items? - /*for(ItemMap::iterator it = items.begin(); it != items.end(); ++it) - delete it->second->condition; - clear(); - return loadFromXml();*/ - return false; + if(!items.size()) + return false; + + items.reload(); //? + loadFromOtb(getFilePath(FILE_TYPE_OTHER, "items/items.otb")); + if(!loadFromXml()) + return false; + + g_moveEvents->reload(); + g_weapons->reload(); + return true; } int32_t Items::loadFromOtb(std::string file) @@ -140,24 +151,24 @@ int32_t Items::loadFromOtb(std::string file) //attributes //0x01 = version data uint32_t flags; - if(!props.GET_ULONG(flags)) + if(!props.getLong(flags)) return ERROR_INVALID_FORMAT; attribute_t attr; - if(!props.GET_VALUE(attr)) + if(!props.getType(attr)) return ERROR_INVALID_FORMAT; if(attr == ROOT_ATTR_VERSION) { datasize_t length = 0; - if(!props.GET_VALUE(length)) + if(!props.getType(length)) return ERROR_INVALID_FORMAT; if(length != sizeof(VERSIONINFO)) return ERROR_INVALID_FORMAT; VERSIONINFO *vi; - if(!props.GET_STRUCT(vi)) + if(!props.getStruct(vi)) return ERROR_INVALID_FORMAT; Items::dwMajorVersion = vi->dwMajorVersion; //items otb format file version @@ -167,20 +178,15 @@ int32_t Items::loadFromOtb(std::string file) } if(Items::dwMajorVersion == 0xFFFFFFFF) - std::cout << "[Warning - Items::loadFromOtb] items.otb using generic client version." << std::endl; - else if(Items::dwMajorVersion < 3) + std::clog << "[Warning - Items::loadFromOtb] items.otb using generic client version." << std::endl; + else if(Items::dwMajorVersion != 3) { - std::cout << "[Error - Items::loadFromOtb] Old version detected, a newer version of items.otb is required." << std::endl; + std::clog << "[Error - Items::loadFromOtb] Incorrect version detected, please use official items.otb." << std::endl; return ERROR_INVALID_FORMAT; } - else if(Items::dwMajorVersion > 3) + else if(!g_config.getBool(ConfigManager::SKIP_ITEMS_VERSION) && Items::dwMinorVersion != CLIENT_VERSION_946) { - std::cout << "[Error - Items::loadFromOtb] New version detected, an older version of items.otb is required." << std::endl; - return ERROR_INVALID_FORMAT; - } - else if(Items::dwMinorVersion != CLIENT_VERSION_854) - { - std::cout << "[Error - Items::loadFromOtb] Another (client) version of items.otb is required." << std::endl; + std::clog << "[Error - Items::loadFromOtb] Another client version of items.otb is required." << std::endl; return ERROR_INVALID_FORMAT; } @@ -224,16 +230,16 @@ int32_t Items::loadFromOtb(std::string file) } //read 4 byte flags - if(!props.GET_VALUE(flags)) + if(!props.getType(flags)) return ERROR_INVALID_FORMAT; iType->blockSolid = hasBitSet(FLAG_BLOCK_SOLID, flags); iType->blockProjectile = hasBitSet(FLAG_BLOCK_PROJECTILE, flags); iType->blockPathFind = hasBitSet(FLAG_BLOCK_PATHFIND, flags); iType->hasHeight = hasBitSet(FLAG_HAS_HEIGHT, flags); - iType->useable = hasBitSet(FLAG_USEABLE, flags); + iType->usable = hasBitSet(FLAG_USABLE, flags); iType->pickupable = hasBitSet(FLAG_PICKUPABLE, flags); - iType->moveable = hasBitSet(FLAG_MOVEABLE, flags); + iType->movable = hasBitSet(FLAG_MOVABLE, flags); iType->stackable = hasBitSet(FLAG_STACKABLE, flags); iType->alwaysOnTop = hasBitSet(FLAG_ALWAYSONTOP, flags); @@ -243,15 +249,16 @@ int32_t Items::loadFromOtb(std::string file) iType->allowDistRead = hasBitSet(FLAG_ALLOWDISTREAD, flags); iType->rotable = hasBitSet(FLAG_ROTABLE, flags); iType->canReadText = hasBitSet(FLAG_READABLE, flags); - iType->clientCharges = hasBitSet(FLAG_CLIENTCHARGES, flags); iType->lookThrough = hasBitSet(FLAG_LOOKTHROUGH, flags); + iType->isAnimation = hasBitSet(FLAG_ANIMATION, flags); + iType->walkStack = !hasBitSet(FLAG_WALKSTACK, flags); attribute_t attr; - while(props.GET_VALUE(attr)) + while(props.getType(attr)) { //size of data datasize_t length = 0; - if(!props.GET_VALUE(length)) + if(!props.getType(length)) { delete iType; return ERROR_INVALID_FORMAT; @@ -265,7 +272,7 @@ int32_t Items::loadFromOtb(std::string file) return ERROR_INVALID_FORMAT; uint16_t serverId; - if(!props.GET_USHORT(serverId)) + if(!props.getShort(serverId)) return ERROR_INVALID_FORMAT; if(serverId > 20000 && serverId < 20100) @@ -290,7 +297,7 @@ int32_t Items::loadFromOtb(std::string file) return ERROR_INVALID_FORMAT; uint16_t clientId; - if(!props.GET_USHORT(clientId)) + if(!props.getShort(clientId)) return ERROR_INVALID_FORMAT; iType->clientId = clientId; @@ -302,7 +309,7 @@ int32_t Items::loadFromOtb(std::string file) return ERROR_INVALID_FORMAT; uint16_t speed; - if(!props.GET_USHORT(speed)) + if(!props.getShort(speed)) return ERROR_INVALID_FORMAT; iType->speed = speed; @@ -314,7 +321,7 @@ int32_t Items::loadFromOtb(std::string file) return ERROR_INVALID_FORMAT; lightBlock2* block; - if(!props.GET_STRUCT(block)) + if(!props.getStruct(block)) return ERROR_INVALID_FORMAT; iType->lightLevel = block->lightLevel; @@ -327,16 +334,37 @@ int32_t Items::loadFromOtb(std::string file) return ERROR_INVALID_FORMAT; uint8_t topOrder; - if(!props.GET_UCHAR(topOrder)) + if(!props.getByte(topOrder)) return ERROR_INVALID_FORMAT; iType->alwaysOnTopOrder = topOrder; break; } + case ITEM_ATTR_WAREID: + { + if(length != sizeof(uint16_t)) + return ERROR_INVALID_FORMAT; + + uint16_t wareId; + if(!props.getShort(wareId)) + return ERROR_INVALID_FORMAT; + + iType->wareId = wareId; + break; + } + case ITEM_ATTR_NAME: + { + std::string name; + if(!props.getString(name, length)) + return ERROR_INVALID_FORMAT; + + iType->name = name; + break; + } default: { //skip unknown attributes - if(!props.SKIP_N(length)) + if(!props.skip(length)) return ERROR_INVALID_FORMAT; break; @@ -355,38 +383,19 @@ int32_t Items::loadFromOtb(std::string file) bool Items::loadFromXml() { - xmlDocPtr itemDoc = xmlParseFile(getFilePath(FILE_TYPE_OTHER, "items/items.xml").c_str()), - paletteDoc = xmlParseFile(getFilePath(FILE_TYPE_OTHER, "items/randomization.xml").c_str()); - if(!itemDoc) - { - std::cout << "[Warning - Items::loadFromXml] Cannot load items file." << std::endl; - std::cout << getLastXMLError() << std::endl; - return false; - } - - if(!paletteDoc) + xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_OTHER, "items/items.xml").c_str()); + if(!doc) { - std::cout << "[Warning - Items::loadFromXml] Cannot load randomization file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Items::loadFromXml] Cannot load items file." + << std::endl << getLastXMLError() << std::endl; return false; } - xmlNodePtr itemRoot = xmlDocGetRootElement(itemDoc), paletteRoot = xmlDocGetRootElement(paletteDoc); - if(xmlStrcmp(itemRoot->name,(const xmlChar*)"items")) + xmlNodePtr root = xmlDocGetRootElement(doc); + if(xmlStrcmp(root->name,(const xmlChar*)"items")) { - xmlFreeDoc(itemDoc); - xmlFreeDoc(paletteDoc); - - std::cout << "[Warning - Items::loadFromXml] Malformed items file." << std::endl; - return false; - } - - if(xmlStrcmp(paletteRoot->name,(const xmlChar*)"randomization")) - { - xmlFreeDoc(itemDoc); - xmlFreeDoc(paletteDoc); - - std::cout << "[Warning - Items::loadFromXml] Malformed randomization file." << std::endl; + xmlFreeDoc(doc); + std::clog << "[Warning - Items::loadFromXml] Malformed items file." << std::endl; return false; } @@ -395,102 +404,132 @@ bool Items::loadFromXml() StringVec strVector; int32_t intValue, id = 0, endId = 0, fromId = 0, toId = 0; - for(xmlNodePtr itemNode = itemRoot->children; itemNode; itemNode = itemNode->next) + for(xmlNodePtr node = root->children; node; node = node->next) { - if(xmlStrcmp(itemNode->name,(const xmlChar*)"item")) + if(xmlStrcmp(node->name,(const xmlChar*)"item")) continue; - if(readXMLInteger(itemNode, "id", intValue)) - parseItemNode(itemNode, intValue); - else if(readXMLString(itemNode, "fromid", strValue) && readXMLString(itemNode, "toid", endValue)) + if(readXMLString(node, "id", strValue)) + { + strVector = explodeString(strValue, ";"); + for(StringVec::iterator it = strVector.begin(); it != strVector.end(); ++it) + { + intVector = vectorAtoi(explodeString(*it, "-")); + if(intVector.size() > 1) + { + int32_t i = intVector[0]; + while(i <= intVector[1]) + parseItemNode(node, i++); + } + else + parseItemNode(node, atoi((*it).c_str())); + } + } + else if(readXMLString(node, "fromid", strValue) && readXMLString(node, "toid", endValue)) { intVector = vectorAtoi(explodeString(strValue, ";")); endVector = vectorAtoi(explodeString(endValue, ";")); - if(intVector[0] && endVector[0] && intVector.size() == endVector.size()) + if(intVector[0] && intVector.size() == endVector.size()) { size_t size = intVector.size(); for(size_t i = 0; i < size; ++i) { - parseItemNode(itemNode, intVector[i]); - while(intVector[i] < endVector[i]) - parseItemNode(itemNode, ++intVector[i]); + while(intVector[i] <= endVector[i]) + parseItemNode(node, intVector[i]++); } } else - std::cout << "[Warning - Items::loadFromXml] Malformed entry (from: \"" << strValue << "\", to: \"" << endValue << "\")" << std::endl; + std::clog << "[Warning - Items::loadFromXml] Malformed entry (from: \"" << strValue << "\", to: \"" << endValue << "\")" << std::endl; } else - std::cout << "[Warning - Items::loadFromXml] No itemid found" << std::endl; + std::clog << "[Warning - Items::loadFromXml] No itemid found" << std::endl; } - xmlFreeDoc(itemDoc); + const ItemType* it; for(uint32_t i = 0; i < Item::items.size(); ++i) //lets do some checks... { - const ItemType* it = Item::items.getElement(i); - if(!it) + if(!(it = Item::items.getElement(i))) continue; //check bed items - if((it->transformToFree || it->transformUseTo[PLAYERSEX_FEMALE] || it->transformUseTo[PLAYERSEX_MALE]) && it->type != ITEM_TYPE_BED) - std::cout << "[Warning - Items::loadFromXml] Item " << it->id << " is not set as a bed-type." << std::endl; + if((it->transformBed[PLAYERSEX_FEMALE] || it->transformBed[PLAYERSEX_MALE]) && it->type != ITEM_TYPE_BED) + std::clog << "[Warning - Items::loadFromXml] Item " << it->id << " is not set as a bed-type." << std::endl; + } + + xmlFreeDoc(doc); + if(!(doc = xmlParseFile(getFilePath(FILE_TYPE_OTHER, "items/randomization.xml").c_str()))) + { + std::clog << "[Warning - Items::loadFromXml] Cannot load randomization file." + << std::endl << getLastXMLError() << std::endl; + return false; } - for(xmlNodePtr paletteNode = paletteRoot->children; paletteNode; paletteNode = paletteNode->next) + root = xmlDocGetRootElement(doc); + if(xmlStrcmp(root->name,(const xmlChar*)"randomization")) { - if(!xmlStrcmp(paletteNode->name, (const xmlChar*)"config")) + xmlFreeDoc(doc); + std::clog << "[Warning - Items::loadFromXml] Malformed randomization file." << std::endl; + return false; + } + + for(xmlNodePtr node = root->children; node; node = node->next) + { + if(!xmlStrcmp(node->name, (const xmlChar*)"config")) { - if(readXMLInteger(paletteNode, "chance", intValue) || readXMLInteger(paletteNode, "defaultChance", intValue)) + if(readXMLInteger(node, "chance", intValue) || readXMLInteger(node, "defaultChance", intValue)) { if(intValue > 100) { intValue = 100; - std::cout << "[Warning - Items::loadFromXml] Randomize chance cannot be higher than 100." << std::endl; + std::clog << "[Warning - Items::loadFromXml] Randomize chance cannot be higher than 100." << std::endl; } m_randomizationChance = intValue; } } - else if(!xmlStrcmp(paletteNode->name, (const xmlChar*)"palette")) + else if(!xmlStrcmp(node->name, (const xmlChar*)"palette")) { - if(readXMLString(paletteNode, "randomize", strValue)) + if(!readXMLString(node, "randomize", strValue)) + continue; + + IntegerVec itemList = vectorAtoi(explodeString(strValue, ";")); + if(itemList.size() < 2) + itemList = vectorAtoi(explodeString(strValue, "-")); + + if(itemList.size() > 1) { - std::vector itemList = vectorAtoi(explodeString(strValue, ";")); - if(itemList.size() >= 2) + if(itemList[0] < itemList[1]) { - if(itemList[0] < itemList[1]) - { - fromId = itemList[0]; - toId = itemList[1]; - } - else - std::cout << "[Warning - Items::loadFromXml] Randomize min cannot be higher than max." << std::endl; + fromId = itemList[0]; + toId = itemList[1]; } + else + std::clog << "[Warning - Items::loadFromXml] Randomize min cannot be higher than max." << std::endl; + } - int32_t chance = getRandomizationChance(); - if(readXMLInteger(paletteNode, "chance", intValue)) + int32_t chance = getRandomizationChance(); + if(readXMLInteger(node, "chance", intValue)) + { + if(intValue > 100) { - if(intValue > 100) - { - intValue = 100; - std::cout << "[Warning: Items::loadRandomization] Randomize chance cannot be higher than 100." << std::endl; - } - - chance = intValue; + intValue = 100; + std::clog << "[Warning: Items::loadRandomization] Randomize chance cannot be higher than 100." << std::endl; } - if(readXMLInteger(paletteNode, "itemid", id)) - parseRandomizationBlock(id, fromId, toId, chance); - else if(readXMLInteger(paletteNode, "fromid", id) && readXMLInteger(paletteNode, "toid", endId)) - { - parseRandomizationBlock(id, fromId, toId, chance); - while(id < endId) - parseRandomizationBlock(++id, fromId, toId, chance); - } + chance = intValue; + } + + if(readXMLInteger(node, "itemid", id)) + parseRandomizationBlock(id, fromId, toId, chance); + else if(readXMLInteger(node, "fromid", id) && readXMLInteger(node, "toid", endId)) + { + while(id <= endId) + parseRandomizationBlock(id++, fromId, toId, chance); } } } - xmlFreeDoc(paletteDoc); + xmlFreeDoc(doc); return true; } @@ -498,19 +537,35 @@ void Items::parseItemNode(xmlNodePtr itemNode, uint32_t id) { int32_t intValue; std::string strValue; - if(id > 20000 && id < 20100) { - id = id - 20000; - + id -= 20000; ItemType* iType = new ItemType(); + iType->id = id; items.addElement(iType, iType->id); } + bool override = readXMLString(itemNode, "override", strValue) && booleanString(strValue); ItemType& it = Item::items.getItemType(id); - if(!it.name.empty() && (!readXMLString(itemNode, "override", strValue) || !booleanString(strValue))) - std::cout << "[Warning - Items::loadFromXml] Duplicate registered item with id " << id << std::endl; + if(it.loaded) + { + if(override) + { + // setup some defaults + if(it.hasAbilities()) + { + memset(it.getAbilities()->fieldAbsorb, 0, sizeof(it.getAbilities()->fieldAbsorb)); + memset(it.getAbilities()->absorb, 0, sizeof(it.getAbilities()->absorb)); + for(uint32_t j = REFLECT_FIRST; j <= REFLECT_LAST; ++j) + memset(it.getAbilities()->reflect[j], 0, sizeof(it.getAbilities()->reflect[j])); + } + } + else + std::clog << "[Warning - Items::loadFromXml] Duplicate registered item with id " << id << std::endl; + } + else + it.loaded = true; if(readXMLString(itemNode, "name", strValue)) it.name = strValue; @@ -521,1199 +576,1337 @@ void Items::parseItemNode(xmlNodePtr itemNode, uint32_t id) if(readXMLString(itemNode, "plural", strValue)) it.pluralName = strValue; - xmlNodePtr itemAttributesNode = itemNode->children; - while(itemAttributesNode) + for(xmlNodePtr itemAttributesNode = itemNode->children; itemAttributesNode; itemAttributesNode = itemAttributesNode->next) { - if(readXMLString(itemAttributesNode, "key", strValue)) + if(!readXMLString(itemAttributesNode, "key", strValue)) + continue; + +#ifdef _MSC_VER + bool notLoaded = false; +#endif + std::string tmpStrValue = asLowerCaseString(strValue); + if(tmpStrValue == "type") { - std::string tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "type") + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLString(itemAttributesNode, "value", strValue)) + tmpStrValue = asLowerCaseString(strValue); + if(tmpStrValue == "container") { - tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "container") - { - it.type = ITEM_TYPE_CONTAINER; - it.group = ITEM_GROUP_CONTAINER; - } - else if(tmpStrValue == "key") - it.type = ITEM_TYPE_KEY; - else if(tmpStrValue == "magicfield") - it.type = ITEM_TYPE_MAGICFIELD; - else if(tmpStrValue == "depot") - it.type = ITEM_TYPE_DEPOT; - else if(tmpStrValue == "mailbox") - it.type = ITEM_TYPE_MAILBOX; - else if(tmpStrValue == "trashholder") - it.type = ITEM_TYPE_TRASHHOLDER; - else if(tmpStrValue == "teleport") - it.type = ITEM_TYPE_TELEPORT; - else if(tmpStrValue == "door") - it.type = ITEM_TYPE_DOOR; - else if(tmpStrValue == "bed") - it.type = ITEM_TYPE_BED; - else - std::cout << "[Warning - Items::loadFromXml] Unknown type " << strValue << std::endl; + it.type = ITEM_TYPE_CONTAINER; + it.group = ITEM_GROUP_CONTAINER; } + else if(tmpStrValue == "key") + it.type = ITEM_TYPE_KEY; + else if(tmpStrValue == "magicfield") + it.type = ITEM_TYPE_MAGICFIELD; + else if(tmpStrValue == "depot") + it.type = ITEM_TYPE_DEPOT; + else if(tmpStrValue == "mailbox") + it.type = ITEM_TYPE_MAILBOX; + else if(tmpStrValue == "trashholder") + it.type = ITEM_TYPE_TRASHHOLDER; + else if(tmpStrValue == "teleport") + it.type = ITEM_TYPE_TELEPORT; + else if(tmpStrValue == "door") + it.type = ITEM_TYPE_DOOR; + else if(tmpStrValue == "bed") + it.type = ITEM_TYPE_BED; + else if(tmpStrValue == "rune") + it.type = ITEM_TYPE_RUNE; + else + std::clog << "[Warning - Items::loadFromXml] Unknown type " << strValue << std::endl; } - else if(tmpStrValue == "name") - { - if(readXMLString(itemAttributesNode, "value", strValue)) - it.name = strValue; - } - else if(tmpStrValue == "article") - { - if(readXMLString(itemAttributesNode, "value", strValue)) - it.article = strValue; - } - else if(tmpStrValue == "plural") - { - if(readXMLString(itemAttributesNode, "value", strValue)) - it.pluralName = strValue; - } - else if(tmpStrValue == "description") - { - if(readXMLString(itemAttributesNode, "value", strValue)) - it.description = strValue; - } - else if(tmpStrValue == "runespellname") - { - if(readXMLString(itemAttributesNode, "value", strValue)) - it.runeSpellName = strValue; - } - else if(tmpStrValue == "weight") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.weight = intValue / 100.f; - } - else if(tmpStrValue == "showcount") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.showCount = (intValue != 0); - } - else if(tmpStrValue == "armor") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.armor = intValue; - } - else if(tmpStrValue == "defense") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.defense = intValue; - } - else if(tmpStrValue == "extradefense" || tmpStrValue == "extradef") + } + else if(tmpStrValue == "name") + { + if(readXMLString(itemAttributesNode, "value", strValue)) + it.name = strValue; + } + else if(tmpStrValue == "article") + { + if(readXMLString(itemAttributesNode, "value", strValue)) + it.article = strValue; + } + else if(tmpStrValue == "plural") + { + if(readXMLString(itemAttributesNode, "value", strValue)) + it.pluralName = strValue; + } + else if(tmpStrValue == "clientid") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.extraDefense = intValue; + it.clientId = intValue; + if(it.group == ITEM_GROUP_DEPRECATED) + it.group = ITEM_GROUP_NONE; } - else if(tmpStrValue == "attack") + } + else if(tmpStrValue == "cache") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.cache = (intValue != 0); + } + else if(tmpStrValue == "wareid") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.wareId = intValue; + } + else if(tmpStrValue == "blocksolid" || tmpStrValue == "blocking") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.blockSolid = (intValue != 0); + } + else if(tmpStrValue == "blockprojectile") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.blockProjectile = (intValue != 0); + } + else if(tmpStrValue == "blockpathfind" || tmpStrValue == "blockpathing" || tmpStrValue == "blockpath") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.blockPathFind = (intValue != 0); + } + else if(tmpStrValue == "lightlevel") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.lightLevel = intValue; + } + else if(tmpStrValue == "lightcolor") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.lightColor = intValue; + } + else if(tmpStrValue == "description") + { + if(readXMLString(itemAttributesNode, "value", strValue)) + it.description = strValue; + } + else if(tmpStrValue == "runespellname") + { + if(readXMLString(itemAttributesNode, "value", strValue)) + it.runeSpellName = strValue; + } + else if(tmpStrValue == "weight") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.weight = intValue / 100.f; + } + else if(tmpStrValue == "showcount") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.showCount = (intValue != 0); + } + else if(tmpStrValue == "armor") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.armor = intValue; + } + else if(tmpStrValue == "defense") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.defense = intValue; + } + else if(tmpStrValue == "extradefense" || tmpStrValue == "extradef") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.extraDefense = intValue; + } + else if(tmpStrValue == "attack") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.attack = intValue; + } + else if(tmpStrValue == "extraattack" || tmpStrValue == "extraatk") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.extraAttack = intValue; + } + else if(tmpStrValue == "attackspeed") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.attackSpeed = intValue; + } + else if(tmpStrValue == "rotateto") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.rotateTo = intValue; + } + else if(tmpStrValue == "movable" || tmpStrValue == "moveable") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.movable = (intValue != 0); + } + else if(tmpStrValue == "vertical" || tmpStrValue == "isvertical") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.isVertical = (intValue != 0); + } + else if(tmpStrValue == "horizontal" || tmpStrValue == "ishorizontal") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.isHorizontal = (intValue != 0); + } + else if(tmpStrValue == "pickupable") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.pickupable = (intValue != 0); + } + else if(tmpStrValue == "allowpickupable") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.allowPickupable = (intValue != 0); + } + else if(tmpStrValue == "floorchange") + { + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.attack = intValue; + tmpStrValue = asLowerCaseString(strValue); + if(tmpStrValue == "down") + it.floorChange[CHANGE_DOWN] = true; + else if(tmpStrValue == "north") + it.floorChange[CHANGE_NORTH] = true; + else if(tmpStrValue == "south") + it.floorChange[CHANGE_SOUTH] = true; + else if(tmpStrValue == "west") + it.floorChange[CHANGE_WEST] = true; + else if(tmpStrValue == "east") + it.floorChange[CHANGE_EAST] = true; + else if(tmpStrValue == "northex") + it.floorChange[CHANGE_NORTH_EX] = true; + else if(tmpStrValue == "southex") + it.floorChange[CHANGE_SOUTH_EX] = true; + else if(tmpStrValue == "westex") + it.floorChange[CHANGE_WEST_EX] = true; + else if(tmpStrValue == "eastex") + it.floorChange[CHANGE_EAST_EX] = true; } - else if(tmpStrValue == "extraattack" || tmpStrValue == "extraatk") + } + else if(tmpStrValue == "corpsetype") + { + tmpStrValue = asLowerCaseString(strValue); + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.extraAttack = intValue; + tmpStrValue = asLowerCaseString(strValue); + if(tmpStrValue == "venom") + it.corpseType = RACE_VENOM; + else if(tmpStrValue == "blood") + it.corpseType = RACE_BLOOD; + else if(tmpStrValue == "undead") + it.corpseType = RACE_UNDEAD; + else if(tmpStrValue == "fire") + it.corpseType = RACE_FIRE; + else if(tmpStrValue == "energy") + it.corpseType = RACE_ENERGY; + else + std::clog << "[Warning - Items::loadFromXml] Unknown corpseType " << strValue << std::endl; } - else if(tmpStrValue == "attackspeed") + } + else if(tmpStrValue == "containersize") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.attackSpeed = intValue; + it.maxItems = intValue; + if(it.group == ITEM_GROUP_NONE) + { + it.group = ITEM_GROUP_CONTAINER; + it.type = ITEM_TYPE_CONTAINER; + } } - else if(tmpStrValue == "rotateto") + } + else if(tmpStrValue == "fluidsource") + { + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.rotateTo = intValue; + tmpStrValue = asLowerCaseString(strValue); + FluidTypes_t fluid = getFluidType(tmpStrValue); + if(fluid != FLUID_NONE) + it.fluidSource = fluid; + else + std::clog << "[Warning - Items::loadFromXml] Unknown fluidSource " << strValue << std::endl; } - else if(tmpStrValue == "moveable" || tmpStrValue == "movable") + } + else if(tmpStrValue == "writeable" || tmpStrValue == "writable") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.moveable = (intValue != 0); + it.canWriteText = (intValue != 0); + it.canReadText = (intValue != 0); } - else if(tmpStrValue == "blockprojectile") + } + else if(tmpStrValue == "readable") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.canReadText = (intValue != 0); + } + else if(tmpStrValue == "maxtextlen" || tmpStrValue == "maxtextlength") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.maxTextLength = intValue; + } + else if(tmpStrValue == "text") + { + if(readXMLString(itemAttributesNode, "value", strValue)) + it.text = strValue; + } + else if(tmpStrValue == "author" || tmpStrValue == "writer") + { + if(readXMLString(itemAttributesNode, "value", strValue)) + it.writer = strValue; + } + else if(tmpStrValue == "date") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.date = intValue; + } + else if(tmpStrValue == "writeonceitemid") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.writeOnceItemId = intValue; + } + else if(tmpStrValue == "wareid") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.wareId = intValue; + } + else if(tmpStrValue == "worth") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.blockProjectile = (intValue != 0); + if(moneyMap.find(intValue) != moneyMap.end() && !override) + std::clog << "[Warning - Items::loadFromXml] Duplicated money item " << id << " with worth " << intValue << "!" << std::endl; + else + { + moneyMap[intValue] = id; + it.worth = intValue; + } } - else if(tmpStrValue == "allowpickupable") + } + else if(tmpStrValue == "forceserialize" || tmpStrValue == "forceserialization" || tmpStrValue == "forcesave") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.forceSerialize = (intValue != 0); + } + else if(tmpStrValue == "leveldoor") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.levelDoor = intValue; + } + else if(tmpStrValue == "specialdoor") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.specialDoor = (intValue != 0); + } + else if(tmpStrValue == "closingdoor") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.closingDoor = (intValue != 0); + } + else if(tmpStrValue == "weapontype") + { + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.allowPickupable = (intValue != 0); + tmpStrValue = asLowerCaseString(strValue); + if(tmpStrValue == "sword") + it.weaponType = WEAPON_SWORD; + else if(tmpStrValue == "club") + it.weaponType = WEAPON_CLUB; + else if(tmpStrValue == "axe") + it.weaponType = WEAPON_AXE; + else if(tmpStrValue == "shield") + it.weaponType = WEAPON_SHIELD; + else if(tmpStrValue == "distance" || tmpStrValue == "dist") + it.weaponType = WEAPON_DIST; + else if(tmpStrValue == "wand" || tmpStrValue == "rod") + it.weaponType = WEAPON_WAND; + else if(tmpStrValue == "ammunition" || tmpStrValue == "ammo") + it.weaponType = WEAPON_AMMO; + else if(tmpStrValue == "fist") + it.weaponType = WEAPON_FIST; + else + std::clog << "[Warning - Items::loadFromXml] Unknown weaponType " << strValue << std::endl; } - else if(tmpStrValue == "floorchange") + } + else if(tmpStrValue == "slottype") + { + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLString(itemAttributesNode, "value", strValue)) + tmpStrValue = asLowerCaseString(strValue); + if(tmpStrValue == "head") { - tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "down") - it.floorChange[CHANGE_DOWN] = true; - else if(tmpStrValue == "north") - it.floorChange[CHANGE_NORTH] = true; - else if(tmpStrValue == "south") - it.floorChange[CHANGE_SOUTH] = true; - else if(tmpStrValue == "west") - it.floorChange[CHANGE_WEST] = true; - else if(tmpStrValue == "east") - it.floorChange[CHANGE_EAST] = true; - else if(tmpStrValue == "northex") - it.floorChange[CHANGE_NORTH_EX] = true; - else if(tmpStrValue == "southex") - it.floorChange[CHANGE_SOUTH_EX] = true; - else if(tmpStrValue == "westex") - it.floorChange[CHANGE_WEST_EX] = true; - else if(tmpStrValue == "eastex") - it.floorChange[CHANGE_EAST_EX] = true; + it.slotPosition |= SLOTP_HEAD; + it.wieldPosition = SLOT_HEAD; } - } - else if(tmpStrValue == "corpsetype") - { - tmpStrValue = asLowerCaseString(strValue); - if(readXMLString(itemAttributesNode, "value", strValue)) + else if(tmpStrValue == "body") { - tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "venom") - it.corpseType = RACE_VENOM; - else if(tmpStrValue == "blood") - it.corpseType = RACE_BLOOD; - else if(tmpStrValue == "undead") - it.corpseType = RACE_UNDEAD; - else if(tmpStrValue == "fire") - it.corpseType = RACE_FIRE; - else if(tmpStrValue == "energy") - it.corpseType = RACE_ENERGY; - else - std::cout << "[Warning - Items::loadFromXml] Unknown corpseType " << strValue << std::endl; + it.slotPosition |= SLOTP_ARMOR; + it.wieldPosition = SLOT_ARMOR; } - } - else if(tmpStrValue == "containersize") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.maxItems = intValue; - } - else if(tmpStrValue == "fluidsource") - { - if(readXMLString(itemAttributesNode, "value", strValue)) + else if(tmpStrValue == "legs") { - tmpStrValue = asLowerCaseString(strValue); - FluidTypes_t fluid = getFluidType(tmpStrValue); - if(fluid != FLUID_NONE) - it.fluidSource = fluid; - else - std::cout << "[Warning - Items::loadFromXml] Unknown fluidSource " << strValue << std::endl; + it.slotPosition |= SLOTP_LEGS; + it.wieldPosition = SLOT_LEGS; } - } - else if(tmpStrValue == "writeable" || tmpStrValue == "writable") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) + else if(tmpStrValue == "feet") { - it.canWriteText = (intValue != 0); - it.canReadText = (intValue != 0); + it.slotPosition |= SLOTP_FEET; + it.wieldPosition = SLOT_FEET; } - } - else if(tmpStrValue == "readable") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.canReadText = (intValue != 0); - } - else if(tmpStrValue == "maxtextlen" || tmpStrValue == "maxtextlength") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.maxTextLen = intValue; - } - else if(tmpStrValue == "writeonceitemid") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.writeOnceItemId = intValue; - } - else if(tmpStrValue == "worth") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) + else if(tmpStrValue == "backpack") { - if(moneyMap.find(intValue) != moneyMap.end() && (!readXMLString(itemNode, "override", strValue) || !booleanString(strValue))) - std::cout << "[Warning - Items::loadFromXml] Duplicated money item " << id << " with worth " << intValue << "!" << std::endl; - else - { - moneyMap[intValue] = id; - it.worth = intValue; - } + it.slotPosition |= SLOTP_BACKPACK; + it.wieldPosition = SLOT_BACKPACK; } - } - else if(tmpStrValue == "forceserialize" || tmpStrValue == "forceserialization" || tmpStrValue == "forcesave") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.forceSerialize = (intValue != 0); - } - else if(tmpStrValue == "leveldoor") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.levelDoor = intValue; - } - else if(tmpStrValue == "weapontype") - { - if(readXMLString(itemAttributesNode, "value", strValue)) + else if(tmpStrValue == "two-handed") { - tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "sword") - it.weaponType = WEAPON_SWORD; - else if(tmpStrValue == "club") - it.weaponType = WEAPON_CLUB; - else if(tmpStrValue == "axe") - it.weaponType = WEAPON_AXE; - else if(tmpStrValue == "shield") - it.weaponType = WEAPON_SHIELD; - else if(tmpStrValue == "distance") - it.weaponType = WEAPON_DIST; - else if(tmpStrValue == "wand" || tmpStrValue == "rod") - it.weaponType = WEAPON_WAND; - else if(tmpStrValue == "ammunition") - it.weaponType = WEAPON_AMMO; - else if(tmpStrValue == "fist") - it.weaponType = WEAPON_FIST; - else - std::cout << "[Warning - Items::loadFromXml] Unknown weaponType " << strValue << std::endl; + it.slotPosition |= SLOTP_TWO_HAND; + it.wieldPosition = SLOT_TWO_HAND; } - } - else if(tmpStrValue == "slottype") - { - if(readXMLString(itemAttributesNode, "value", strValue)) + else if(tmpStrValue == "right-hand") { - tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "head") - { - it.slotPosition |= SLOTP_HEAD; - it.wieldPosition = SLOT_HEAD; - } - else if(tmpStrValue == "body") - { - it.slotPosition |= SLOTP_ARMOR; - it.wieldPosition = SLOT_ARMOR; - } - else if(tmpStrValue == "legs") - { - it.slotPosition |= SLOTP_LEGS; - it.wieldPosition = SLOT_LEGS; - } - else if(tmpStrValue == "feet") - { - it.slotPosition |= SLOTP_FEET; - it.wieldPosition = SLOT_FEET; - } - else if(tmpStrValue == "backpack") - { - it.slotPosition |= SLOTP_BACKPACK; - it.wieldPosition = SLOT_BACKPACK; - } - else if(tmpStrValue == "two-handed") - { - it.slotPosition |= SLOTP_TWO_HAND; - it.wieldPosition = SLOT_TWO_HAND; - } - else if(tmpStrValue == "necklace") - { - it.slotPosition |= SLOTP_NECKLACE; - it.wieldPosition = SLOT_NECKLACE; - } - else if(tmpStrValue == "ring") - { - it.slotPosition |= SLOTP_RING; - it.wieldPosition = SLOT_RING; - } - else if(tmpStrValue == "ammo") - it.wieldPosition = SLOT_AMMO; - else if(tmpStrValue == "hand") - it.wieldPosition = SLOT_HAND; - else - std::cout << "[Warning - Items::loadFromXml] Unknown slotType " << strValue << std::endl; + it.slotPosition &= ~SLOTP_LEFT; + it.wieldPosition = SLOT_RIGHT; } - } - else if(tmpStrValue == "ammotype") - { - if(readXMLString(itemAttributesNode, "value", strValue)) + else if(tmpStrValue == "left-hand") { - it.ammoType = getAmmoType(strValue); - if(it.ammoType == AMMO_NONE) - std::cout << "[Warning - Items::loadFromXml] Unknown ammoType " << strValue << std::endl; + it.slotPosition &= ~SLOTP_RIGHT; + it.wieldPosition = SLOT_LEFT; } - } - else if(tmpStrValue == "shoottype") - { - if(readXMLString(itemAttributesNode, "value", strValue)) + else if(tmpStrValue == "necklace") { - ShootEffect_t shoot = getShootType(strValue); - if(shoot != SHOOT_EFFECT_UNKNOWN) - it.shootType = shoot; - else - std::cout << "[Warning - Items::loadFromXml] Unknown shootType " << strValue << std::endl; + it.slotPosition |= SLOTP_NECKLACE; + it.wieldPosition = SLOT_NECKLACE; } - } - else if(tmpStrValue == "effect") - { - if(readXMLString(itemAttributesNode, "value", strValue)) + else if(tmpStrValue == "ring") + { + it.slotPosition |= SLOTP_RING; + it.wieldPosition = SLOT_RING; + } + else if(tmpStrValue == "ammo") { - MagicEffect_t effect = getMagicEffect(strValue); - if(effect != MAGIC_EFFECT_UNKNOWN) - it.magicEffect = effect; - else - std::cout << "[Warning - Items::loadFromXml] Unknown effect " << strValue << std::endl; + it.slotPosition |= SLOTP_AMMO; + it.wieldPosition = SLOT_AMMO; } + else if(tmpStrValue == "hand") + it.wieldPosition = SLOT_HAND; + else + std::clog << "[Warning - Items::loadFromXml] Unknown slotType " << strValue << std::endl; } - else if(tmpStrValue == "range") + } + else if(tmpStrValue == "ammotype") + { + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.shootRange = intValue; + it.ammoType = getAmmoType(strValue); + if(it.ammoType == AMMO_NONE) + std::clog << "[Warning - Items::loadFromXml] Unknown ammoType " << strValue << std::endl; } - else if(tmpStrValue == "stopduration") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.stopTime = (intValue != 0); - } - else if(tmpStrValue == "decayto") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.decayTo = intValue; - } - else if(tmpStrValue == "transformequipto") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.transformEquipTo = intValue; - } - else if(tmpStrValue == "transformdeequipto") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.transformDeEquipTo = intValue; - } - else if(tmpStrValue == "duration") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.decayTime = std::max((int32_t)0, intValue); - } - else if(tmpStrValue == "showduration") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.showDuration = (intValue != 0); - } - else if(tmpStrValue == "charges") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.charges = intValue; - } - else if(tmpStrValue == "showcharges") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.showCharges = (intValue != 0); - } - else if(tmpStrValue == "showattributes") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.showAttributes = (intValue != 0); - } - else if(tmpStrValue == "breakchance") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.breakChance = std::max(0, std::min(100, intValue)); - } - else if(tmpStrValue == "ammoaction") - { - if(readXMLString(itemAttributesNode, "value", strValue)) - { - AmmoAction_t ammo = getAmmoAction(strValue); - if(ammo != AMMOACTION_NONE) - it.ammoAction = ammo; - else - std::cout << "[Warning - Items::loadFromXml] Unknown ammoAction " << strValue << std::endl; - } - } - else if(tmpStrValue == "hitchance") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.hitChance = std::max(0, std::min(100, intValue)); - } - else if(tmpStrValue == "maxhitchance") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.maxHitChance = std::max(0, std::min(100, intValue)); - } - else if(tmpStrValue == "preventloss") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.preventLoss = (intValue != 0); - } - else if(tmpStrValue == "preventdrop") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.preventDrop = (intValue != 0); - } - else if(tmpStrValue == "invisible") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.invisible = (intValue != 0); - } - else if(tmpStrValue == "speed") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.speed = intValue; - } - else if(tmpStrValue == "healthgain") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.regeneration = true; - it.abilities.healthGain = intValue; - } - } - else if(tmpStrValue == "healthticks") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.regeneration = true; - it.abilities.healthTicks = intValue; - } - } - else if(tmpStrValue == "managain") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.regeneration = true; - it.abilities.manaGain = intValue; - } - } - else if(tmpStrValue == "manaticks") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.regeneration = true; - it.abilities.manaTicks = intValue; - } - } - else if(tmpStrValue == "manashield") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.manaShield = (intValue != 0); - } - else if(tmpStrValue == "skillsword") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.skills[SKILL_SWORD] = intValue; - } - else if(tmpStrValue == "skillaxe") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.skills[SKILL_AXE] = intValue; - } - else if(tmpStrValue == "skillclub") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.skills[SKILL_CLUB] = intValue; - } - else if(tmpStrValue == "skilldist") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.skills[SKILL_DIST] = intValue; - } - else if(tmpStrValue == "skillfish") + } + else if(tmpStrValue == "shoottype") + { + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.skills[SKILL_FISH] = intValue; + ShootEffect_t shoot = getShootType(strValue); + if(shoot != SHOOT_EFFECT_UNKNOWN) + it.shootType = shoot; + else + std::clog << "[Warning - Items::loadFromXml] Unknown shootType " << strValue << std::endl; } - else if(tmpStrValue == "skillshield") + } + else if(tmpStrValue == "effect") + { + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.skills[SKILL_SHIELD] = intValue; + MagicEffect_t effect = getMagicEffect(strValue); + if(effect != MAGIC_EFFECT_UNKNOWN) + it.magicEffect = effect; + else + std::clog << "[Warning - Items::loadFromXml] Unknown effect " << strValue << std::endl; } - else if(tmpStrValue == "skillfist") + } + else if(tmpStrValue == "range") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.shootRange = intValue; + } + else if(tmpStrValue == "stopduration") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.stopTime = (intValue != 0); + } + else if(tmpStrValue == "decayto") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.decayTo = intValue; + } + else if(tmpStrValue == "transformequipto" || tmpStrValue == "onequipto") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.transformEquipTo = intValue; + } + else if(tmpStrValue == "transformdeequipto" || tmpStrValue == "ondeequipto") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.transformDeEquipTo = intValue; + } + else if(tmpStrValue == "duration") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.decayTime = std::max((int32_t)0, intValue); + } + else if(tmpStrValue == "showduration") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.showDuration = (intValue != 0); + } + else if(tmpStrValue == "charges") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.charges = intValue; + } + else if(tmpStrValue == "showcharges") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.showCharges = (intValue != 0); + } + else if(tmpStrValue == "showattributes") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.showAttributes = (intValue != 0); + } + else if(tmpStrValue == "breakchance") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.breakChance = std::max(0, std::min(100, intValue)); + } + else if(tmpStrValue == "ammoaction") + { + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.skills[SKILL_FIST] = intValue; + AmmoAction_t ammo = getAmmoAction(strValue); + if(ammo != AMMOACTION_NONE) + it.ammoAction = ammo; + else + std::clog << "[Warning - Items::loadFromXml] Unknown ammoAction " << strValue << std::endl; } - else if(tmpStrValue == "maxhealthpoints" || tmpStrValue == "maxhitpoints") + } + else if(tmpStrValue == "hitchance") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.hitChance = std::max(-100, std::min(100, intValue)); + } + else if(tmpStrValue == "maxhitchance") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.maxHitChance = std::max(0, std::min(100, intValue)); + } + else if(tmpStrValue == "dualwield") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.dualWield = (intValue != 0); + } + else if(tmpStrValue == "preventloss") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->preventLoss = (intValue != 0); + } + else if(tmpStrValue == "preventdrop") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->preventDrop = (intValue != 0); + } + else if(tmpStrValue == "invisible") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->invisible = (intValue != 0); + } + else if(tmpStrValue == "speed") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->speed = intValue; + } + else if(tmpStrValue == "healthgain") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.stats[STAT_MAXHEALTH] = intValue; + it.getAbilities()->regeneration = true; + it.getAbilities()->healthGain = intValue; } - else if(tmpStrValue == "maxhealthpercent" || tmpStrValue == "maxhitpointspercent") + } + else if(tmpStrValue == "healthticks") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.statsPercent[STAT_MAXHEALTH] = intValue; + it.getAbilities()->regeneration = true; + it.getAbilities()->healthTicks = intValue; } - else if(tmpStrValue == "maxmanapoints") + } + else if(tmpStrValue == "managain") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.stats[STAT_MAXMANA] = intValue; + it.getAbilities()->regeneration = true; + it.getAbilities()->manaGain = intValue; } - else if(tmpStrValue == "maxmanapercent" || tmpStrValue == "maxmanapointspercent") + } + else if(tmpStrValue == "manaticks") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.statsPercent[STAT_MAXMANA] = intValue; + it.getAbilities()->regeneration = true; + it.getAbilities()->manaTicks = intValue; } - else if(tmpStrValue == "soulpoints") + } + else if(tmpStrValue == "manashield") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->manaShield = (intValue != 0); + } + else if(tmpStrValue == "skillsword") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->skills[SKILL_SWORD] = intValue; + } + else if(tmpStrValue == "skillaxe") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->skills[SKILL_AXE] = intValue; + } + else if(tmpStrValue == "skillclub") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->skills[SKILL_CLUB] = intValue; + } + else if(tmpStrValue == "skilldist") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->skills[SKILL_DIST] = intValue; + } + else if(tmpStrValue == "skillfish") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->skills[SKILL_FISH] = intValue; + } + else if(tmpStrValue == "skillshield") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->skills[SKILL_SHIELD] = intValue; + } + else if(tmpStrValue == "skillfist") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->skills[SKILL_FIST] = intValue; + } + else if(tmpStrValue == "maxhealthpoints" || tmpStrValue == "maxhitpoints") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->stats[STAT_MAXHEALTH] = intValue; + } + else if(tmpStrValue == "maxhealthpercent" || tmpStrValue == "maxhitpointspercent") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->statsPercent[STAT_MAXHEALTH] = intValue; + } + else if(tmpStrValue == "maxmanapoints") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->stats[STAT_MAXMANA] = intValue; + } + else if(tmpStrValue == "maxmanapercent" || tmpStrValue == "maxmanapointspercent") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->statsPercent[STAT_MAXMANA] = intValue; + } + else if(tmpStrValue == "soulpoints") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->stats[STAT_SOUL] = intValue; + } + else if(tmpStrValue == "soulpercent" || tmpStrValue == "soulpointspercent") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->statsPercent[STAT_SOUL] = intValue; + } + else if(tmpStrValue == "magiclevelpoints" || tmpStrValue == "magicpoints") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->stats[STAT_MAGICLEVEL] = intValue; + } + else if(tmpStrValue == "magiclevelpercent" || tmpStrValue == "magicpointspercent") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->statsPercent[STAT_MAGICLEVEL] = intValue; + } + else if(tmpStrValue == "increasemagicvalue") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->increment[MAGIC_VALUE] = intValue; + } + else if(tmpStrValue == "increasemagicpercent") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->increment[MAGIC_PERCENT] = intValue; + } + else if(tmpStrValue == "increasehealingvalue") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->increment[HEALING_VALUE] = intValue; + } + else if(tmpStrValue == "increasehealingpercent") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->increment[HEALING_PERCENT] = intValue; + } + else if(tmpStrValue == "fieldabsorbpercentenergy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->fieldAbsorb[COMBAT_ENERGYDAMAGE] += intValue; + } + else if(tmpStrValue == "fieldabsorbpercentfire") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->fieldAbsorb[COMBAT_FIREDAMAGE] += intValue; + } + else if(tmpStrValue == "fieldabsorbpercentpoison" || tmpStrValue == "fieldabsorbpercentearth") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->fieldAbsorb[COMBAT_EARTHDAMAGE] += intValue; + } + else if(tmpStrValue == "absorbpercentall") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.stats[STAT_SOUL] = intValue; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) + it.getAbilities()->absorb[i] += intValue; } - else if(tmpStrValue == "soulpercent" || tmpStrValue == "soulpointspercent") + } + else if(tmpStrValue == "absorbpercentelements") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.statsPercent[STAT_SOUL] = intValue; + it.getAbilities()->absorb[COMBAT_ENERGYDAMAGE] += intValue; + it.getAbilities()->absorb[COMBAT_FIREDAMAGE] += intValue; + it.getAbilities()->absorb[COMBAT_EARTHDAMAGE] += intValue; + it.getAbilities()->absorb[COMBAT_ICEDAMAGE] += intValue; } - else if(tmpStrValue == "magiclevelpoints" || tmpStrValue == "magicpoints") + } + else if(tmpStrValue == "absorbpercentmagic") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.stats[STAT_MAGICLEVEL] = intValue; + it.getAbilities()->absorb[COMBAT_ENERGYDAMAGE] += intValue; + it.getAbilities()->absorb[COMBAT_FIREDAMAGE] += intValue; + it.getAbilities()->absorb[COMBAT_EARTHDAMAGE] += intValue; + it.getAbilities()->absorb[COMBAT_ICEDAMAGE] += intValue; + it.getAbilities()->absorb[COMBAT_HOLYDAMAGE] += intValue; + it.getAbilities()->absorb[COMBAT_DEATHDAMAGE] += intValue; } - else if(tmpStrValue == "magiclevelpercent" || tmpStrValue == "magicpointspercent") + } + else if(tmpStrValue == "absorbpercentenergy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_ENERGYDAMAGE] += intValue; + } + else if(tmpStrValue == "absorbpercentfire") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_FIREDAMAGE] += intValue; + } + else if(tmpStrValue == "absorbpercentpoison" || tmpStrValue == "absorbpercentearth") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_EARTHDAMAGE] += intValue; + } + else if(tmpStrValue == "absorbpercentice") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_ICEDAMAGE] += intValue; + } + else if(tmpStrValue == "absorbpercentholy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_HOLYDAMAGE] += intValue; + } + else if(tmpStrValue == "absorbpercentdeath") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_DEATHDAMAGE] += intValue; + } + else if(tmpStrValue == "absorbpercentlifedrain") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_LIFEDRAIN] += intValue; + } + else if(tmpStrValue == "absorbpercentmanadrain") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_MANADRAIN] += intValue; + } + else if(tmpStrValue == "absorbpercentdrown") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_DROWNDAMAGE] += intValue; + } + else if(tmpStrValue == "absorbpercentphysical") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_PHYSICALDAMAGE] += intValue; + } + else if(tmpStrValue == "absorbpercenthealing") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_HEALING] += intValue; + } + else if(tmpStrValue == "absorbpercentundefined") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->absorb[COMBAT_UNDEFINEDDAMAGE] += intValue; + } +#ifndef _MSC_VER + else if(tmpStrValue == "reflectpercentall") +#else + else + notLoaded = true; + + if(!notLoaded) + continue; + + if(tmpStrValue == "reflectpercentall") +#endif + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.statsPercent[STAT_MAGICLEVEL] = intValue; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) + it.getAbilities()->reflect[REFLECT_PERCENT][i] += intValue; } - else if(tmpStrValue == "increasemagicvalue") + } + else if(tmpStrValue == "reflectpercentelements") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.increment[MAGIC_VALUE] = intValue; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_ENERGYDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_FIREDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_EARTHDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_ICEDAMAGE] += intValue; } - else if(tmpStrValue == "increasemagicpercent") + } + else if(tmpStrValue == "reflectpercentmagic") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.increment[MAGIC_PERCENT] = intValue; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_ENERGYDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_FIREDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_EARTHDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_ICEDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_HOLYDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_DEATHDAMAGE] += intValue; } - else if(tmpStrValue == "increasehealingvalue") + } + else if(tmpStrValue == "reflectpercentenergy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_ENERGYDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectpercentfire") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_FIREDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectpercentpoison" || tmpStrValue == "reflectpercentearth") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_EARTHDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectpercentice") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_ICEDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectpercentholy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_HOLYDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectpercentdeath") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_DEATHDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectpercentlifedrain") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_LIFEDRAIN] += intValue; + } + else if(tmpStrValue == "reflectpercentmanadrain") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_MANADRAIN] += intValue; + } + else if(tmpStrValue == "reflectpercentdrown") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_DROWNDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectpercentphysical") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_PHYSICALDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectpercenthealing") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_HEALING] += intValue; + } + else if(tmpStrValue == "reflectpercentundefined") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_UNDEFINEDDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectchanceall") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.increment[HEALING_VALUE] = intValue; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) + it.getAbilities()->reflect[REFLECT_CHANCE][i] += intValue; } - else if(tmpStrValue == "increasehealingpercent") + } + else if(tmpStrValue == "reflectchanceelements") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.increment[HEALING_PERCENT] = intValue; + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_ENERGYDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_FIREDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_EARTHDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_ICEDAMAGE] += intValue; } - else if(tmpStrValue == "absorbpercentall") + } + else if(tmpStrValue == "reflectchancemagic") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) - it.abilities.absorb[i] += intValue; - } + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_ENERGYDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_FIREDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_EARTHDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_ICEDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_HOLYDAMAGE] += intValue; + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_DEATHDAMAGE] += intValue; } - else if(tmpStrValue == "absorbpercentelements") + } + else if(tmpStrValue == "reflectchanceenergy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_ENERGYDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectchancefire") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_FIREDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectchancepoison" || tmpStrValue == "reflectchanceearth") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_EARTHDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectchanceice") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_ICEDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectchanceholy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_HOLYDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectchancedeath") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_DEATHDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectchancelifedrain") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_LIFEDRAIN] += intValue; + } + else if(tmpStrValue == "reflectchancemanadrain") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_MANADRAIN] += intValue; + } + else if(tmpStrValue == "reflectchancedrown") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_DROWNDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectchancephysical") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_PHYSICALDAMAGE] += intValue; + } + else if(tmpStrValue == "reflectchancehealing") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_HEALING] += intValue; + } + else if(tmpStrValue == "reflectchanceundefined") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_UNDEFINEDDAMAGE] += intValue; + } + else if(tmpStrValue == "suppressshock" || tmpStrValue == "suppressenergy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_ENERGY; + } + else if(tmpStrValue == "suppressburn" || tmpStrValue == "suppressfire") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_FIRE; + } + else if(tmpStrValue == "suppresspoison" || tmpStrValue == "suppressearth") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_POISON; + } + else if(tmpStrValue == "suppressfreeze" || tmpStrValue == "suppressice") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_FREEZING; + } + else if(tmpStrValue == "suppressdazzle" || tmpStrValue == "suppressholy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_DAZZLED; + } + else if(tmpStrValue == "suppresscurse" || tmpStrValue == "suppressdeath") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_CURSED; + } + else if(tmpStrValue == "suppressdrown") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_DROWN; + } + else if(tmpStrValue == "suppressphysical") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_BLEEDING; + } + else if(tmpStrValue == "suppresshaste") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_HASTE; + } + else if(tmpStrValue == "suppressparalyze") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_PARALYZE; + } + else if(tmpStrValue == "suppressdrunk") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_DRUNK; + } + else if(tmpStrValue == "suppressregeneration") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_REGENERATION; + } + else if(tmpStrValue == "suppresssoul") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_SOUL; + } + else if(tmpStrValue == "suppressoutfit") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_OUTFIT; + } + else if(tmpStrValue == "suppressinvisible") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_INVISIBLE; + } + else if(tmpStrValue == "suppressinfight") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_INFIGHT; + } + else if(tmpStrValue == "suppressexhaust") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_EXHAUST; + } + else if(tmpStrValue == "suppressmuted") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_MUTED; + } + else if(tmpStrValue == "suppresspacified") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_PACIFIED; + } + else if(tmpStrValue == "suppresslight") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_LIGHT; + } + else if(tmpStrValue == "suppressattributes") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_ATTRIBUTES; + } + else if(tmpStrValue == "suppressmanashield") + { + if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) + it.getAbilities()->conditionSuppressions |= CONDITION_MANASHIELD; + } + else if(tmpStrValue == "field") + { + it.group = ITEM_GROUP_MAGICFIELD; + it.type = ITEM_TYPE_MAGICFIELD; + + CombatType_t combatType = COMBAT_NONE; + ConditionDamage* conditionDamage = NULL; + if(readXMLString(itemAttributesNode, "value", strValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) + tmpStrValue = asLowerCaseString(strValue); + if(tmpStrValue == "fire") { - it.abilities.absorb[COMBAT_ENERGYDAMAGE] += intValue; - it.abilities.absorb[COMBAT_FIREDAMAGE] += intValue; - it.abilities.absorb[COMBAT_EARTHDAMAGE] += intValue; - it.abilities.absorb[COMBAT_ICEDAMAGE] += intValue; + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_FIRE, false, 0); + combatType = COMBAT_FIREDAMAGE; } - } - else if(tmpStrValue == "absorbpercentmagic") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) + else if(tmpStrValue == "energy") { - it.abilities.absorb[COMBAT_ENERGYDAMAGE] += intValue; - it.abilities.absorb[COMBAT_FIREDAMAGE] += intValue; - it.abilities.absorb[COMBAT_EARTHDAMAGE] += intValue; - it.abilities.absorb[COMBAT_ICEDAMAGE] += intValue; - it.abilities.absorb[COMBAT_HOLYDAMAGE] += intValue; - it.abilities.absorb[COMBAT_DEATHDAMAGE] += intValue; + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_ENERGY, false, 0); + combatType = COMBAT_ENERGYDAMAGE; } - } - else if(tmpStrValue == "absorbpercentenergy") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_ENERGYDAMAGE] += intValue; - } - else if(tmpStrValue == "absorbpercentfire") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_FIREDAMAGE] += intValue; - } - else if(tmpStrValue == "absorbpercentpoison" || tmpStrValue == "absorbpercentearth") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_EARTHDAMAGE] += intValue; - } - else if(tmpStrValue == "absorbpercentice") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_ICEDAMAGE] += intValue; - } - else if(tmpStrValue == "absorbpercentholy") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_HOLYDAMAGE] += intValue; - } - else if(tmpStrValue == "absorbpercentdeath") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_DEATHDAMAGE] += intValue; - } - else if(tmpStrValue == "absorbpercentlifedrain") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_LIFEDRAIN] += intValue; - } - else if(tmpStrValue == "absorbpercentmanadrain") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_MANADRAIN] += intValue; - } - else if(tmpStrValue == "absorbpercentdrown") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_DROWNDAMAGE] += intValue; - } - else if(tmpStrValue == "absorbpercentphysical") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_PHYSICALDAMAGE] += intValue; - } - else if(tmpStrValue == "absorbpercenthealing") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_HEALING] += intValue; - } - else if(tmpStrValue == "absorbpercentundefined") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.absorb[COMBAT_UNDEFINEDDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectpercentall") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) + else if(tmpStrValue == "earth" || tmpStrValue == "poison") { - for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) - it.abilities.reflect[REFLECT_PERCENT][i] += intValue; + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_POISON, false, 0); + combatType = COMBAT_EARTHDAMAGE; } - } - else if(tmpStrValue == "reflectpercentelements") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) + else if(tmpStrValue == "ice" || tmpStrValue == "freezing") { - it.abilities.reflect[REFLECT_PERCENT][COMBAT_ENERGYDAMAGE] += intValue; - it.abilities.reflect[REFLECT_PERCENT][COMBAT_FIREDAMAGE] += intValue; - it.abilities.reflect[REFLECT_PERCENT][COMBAT_EARTHDAMAGE] += intValue; - it.abilities.reflect[REFLECT_PERCENT][COMBAT_ICEDAMAGE] += intValue; + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_FREEZING, false, 0); + combatType = COMBAT_ICEDAMAGE; } - } - else if(tmpStrValue == "reflectpercentmagic") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) + else if(tmpStrValue == "holy" || tmpStrValue == "dazzled") { - it.abilities.reflect[REFLECT_PERCENT][COMBAT_ENERGYDAMAGE] += intValue; - it.abilities.reflect[REFLECT_PERCENT][COMBAT_FIREDAMAGE] += intValue; - it.abilities.reflect[REFLECT_PERCENT][COMBAT_EARTHDAMAGE] += intValue; - it.abilities.reflect[REFLECT_PERCENT][COMBAT_ICEDAMAGE] += intValue; - it.abilities.reflect[REFLECT_PERCENT][COMBAT_HOLYDAMAGE] += intValue; - it.abilities.reflect[REFLECT_PERCENT][COMBAT_DEATHDAMAGE] += intValue; + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_DAZZLED, false, 0); + combatType = COMBAT_HOLYDAMAGE; } - } - else if(tmpStrValue == "reflectpercentenergy") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_ENERGYDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectpercentfire") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_FIREDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectpercentpoison" || tmpStrValue == "reflectpercentearth") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_EARTHDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectpercentice") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_ICEDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectpercentholy") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_HOLYDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectpercentdeath") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_DEATHDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectpercentlifedrain") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_LIFEDRAIN] += intValue; - } - else if(tmpStrValue == "reflectpercentmanadrain") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_MANADRAIN] += intValue; - } - else if(tmpStrValue == "reflectpercentdrown") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_DROWNDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectpercentphysical") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_PHYSICALDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectpercenthealing") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_HEALING] += intValue; - } - else if(tmpStrValue == "reflectpercentundefined") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_PERCENT][COMBAT_UNDEFINEDDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectchanceall") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) + else if(tmpStrValue == "death" || tmpStrValue == "cursed") { - for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) - it.abilities.reflect[REFLECT_CHANCE][i] += intValue; + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_CURSED, false, 0); + combatType = COMBAT_DEATHDAMAGE; } - } - else if(tmpStrValue == "reflectchanceelements") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) + else if(tmpStrValue == "drown") { - it.abilities.reflect[REFLECT_CHANCE][COMBAT_ENERGYDAMAGE] += intValue; - it.abilities.reflect[REFLECT_CHANCE][COMBAT_FIREDAMAGE] += intValue; - it.abilities.reflect[REFLECT_CHANCE][COMBAT_EARTHDAMAGE] += intValue; - it.abilities.reflect[REFLECT_CHANCE][COMBAT_ICEDAMAGE] += intValue; + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_DROWN, false, 0); + combatType = COMBAT_DROWNDAMAGE; } - } - else if(tmpStrValue == "reflectchancemagic") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) + else if(tmpStrValue == "physical" || tmpStrValue == "bleed") { - it.abilities.reflect[REFLECT_CHANCE][COMBAT_ENERGYDAMAGE] += intValue; - it.abilities.reflect[REFLECT_CHANCE][COMBAT_FIREDAMAGE] += intValue; - it.abilities.reflect[REFLECT_CHANCE][COMBAT_EARTHDAMAGE] += intValue; - it.abilities.reflect[REFLECT_CHANCE][COMBAT_ICEDAMAGE] += intValue; - it.abilities.reflect[REFLECT_CHANCE][COMBAT_HOLYDAMAGE] += intValue; - it.abilities.reflect[REFLECT_CHANCE][COMBAT_DEATHDAMAGE] += intValue; + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_BLEEDING, false, 0); + combatType = COMBAT_PHYSICALDAMAGE; } - } - else if(tmpStrValue == "reflectchanceenergy") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_ENERGYDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectchancefire") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_FIREDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectchancepoison" || tmpStrValue == "reflectchanceearth") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_EARTHDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectchanceice") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_ICEDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectchanceholy") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_HOLYDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectchancedeath") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_DEATHDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectchancelifedrain") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_LIFEDRAIN] += intValue; - } - else if(tmpStrValue == "reflectchancemanadrain") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_MANADRAIN] += intValue; - } - else if(tmpStrValue == "reflectchancedrown") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_DROWNDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectchancephysical") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_PHYSICALDAMAGE] += intValue; - } - else if(tmpStrValue == "reflectchancehealing") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_HEALING] += intValue; - } - else if(tmpStrValue == "reflectchanceundefined") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.abilities.reflect[REFLECT_CHANCE][COMBAT_UNDEFINEDDAMAGE] += intValue; - } - else if(tmpStrValue == "suppressshock" || tmpStrValue == "suppressenergy") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_ENERGY; - } - else if(tmpStrValue == "suppressburn" || tmpStrValue == "suppressfire") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_FIRE; - } - else if(tmpStrValue == "suppresspoison" || tmpStrValue == "suppressearth") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_POISON; - } - else if(tmpStrValue == "suppressfreeze" || tmpStrValue == "suppressice") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_FREEZING; - } - else if(tmpStrValue == "suppressdazzle" || tmpStrValue == "suppressholy") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_DAZZLED; - } - else if(tmpStrValue == "suppresscurse" || tmpStrValue == "suppressdeath") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_CURSED; - } - else if(tmpStrValue == "suppressdrown") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_DROWN; - } - else if(tmpStrValue == "suppressphysical") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_PHYSICAL; - } - else if(tmpStrValue == "suppresshaste") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_HASTE; - } - else if(tmpStrValue == "suppressparalyze") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_PARALYZE; - } - else if(tmpStrValue == "suppressdrunk") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_DRUNK; - } - else if(tmpStrValue == "suppressregeneration") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_REGENERATION; - } - else if(tmpStrValue == "suppresssoul") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_SOUL; - } - else if(tmpStrValue == "suppressoutfit") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_OUTFIT; - } - else if(tmpStrValue == "suppressinvisible") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_INVISIBLE; - } - else if(tmpStrValue == "suppressinfight") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_INFIGHT; - } - else if(tmpStrValue == "suppressexhaust") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_EXHAUST; - } - else if(tmpStrValue == "suppressmuted") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_MUTED; - } - else if(tmpStrValue == "suppresspacified") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_PACIFIED; - } - else if(tmpStrValue == "suppresslight") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_LIGHT; - } - else if(tmpStrValue == "suppressattributes") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_ATTRIBUTES; - } - else if(tmpStrValue == "suppressmanashield") - { - if(readXMLInteger(itemAttributesNode, "value", intValue) && intValue != 0) - it.abilities.conditionSuppressions |= CONDITION_MANASHIELD; - } - else if(tmpStrValue == "field") - { - it.group = ITEM_GROUP_MAGICFIELD; - it.type = ITEM_TYPE_MAGICFIELD; - CombatType_t combatType = COMBAT_NONE; - ConditionDamage* conditionDamage = NULL; + else + std::clog << "[Warning - Items::loadFromXml] Unknown field value " << strValue << std::endl; - if(readXMLString(itemAttributesNode, "value", strValue)) + if(combatType != COMBAT_NONE) { - tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "fire") - { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_FIRE, false, 0); - combatType = COMBAT_FIREDAMAGE; - } - else if(tmpStrValue == "energy") - { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_ENERGY, false, 0); - combatType = COMBAT_ENERGYDAMAGE; - } - else if(tmpStrValue == "earth" || tmpStrValue == "poison") - { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_POISON, false, 0); - combatType = COMBAT_EARTHDAMAGE; - } - else if(tmpStrValue == "ice" || tmpStrValue == "freezing") - { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_FREEZING, false, 0); - combatType = COMBAT_ICEDAMAGE; - } - else if(tmpStrValue == "holy" || tmpStrValue == "dazzled") - { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_DAZZLED, false, 0); - combatType = COMBAT_HOLYDAMAGE; - } - else if(tmpStrValue == "death" || tmpStrValue == "cursed") - { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_CURSED, false, 0); - combatType = COMBAT_DEATHDAMAGE; - } - else if(tmpStrValue == "drown") - { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_DROWN, false, 0); - combatType = COMBAT_DROWNDAMAGE; - } - else if(tmpStrValue == "physical") - { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_PHYSICAL, false, 0); - combatType = COMBAT_PHYSICALDAMAGE; - } - else - std::cout << "[Warning - Items::loadFromXml] Unknown field value " << strValue << std::endl; + it.combatType = combatType; + it.condition = conditionDamage; - if(combatType != COMBAT_NONE) + uint32_t ticks = 0; + int32_t damage = 0, start = 0, count = 1; + for(xmlNodePtr fieldAttributesNode = itemAttributesNode->children; fieldAttributesNode; fieldAttributesNode = fieldAttributesNode->next) { - it.combatType = combatType; - it.condition = conditionDamage; - uint32_t ticks = 0; - int32_t damage = 0, start = 0, count = 1; + if(!readXMLString(fieldAttributesNode, "key", strValue)) + continue; - xmlNodePtr fieldAttributesNode = itemAttributesNode->children; - while(fieldAttributesNode) + tmpStrValue = asLowerCaseString(strValue); + if(tmpStrValue == "ticks") { - if(readXMLString(fieldAttributesNode, "key", strValue)) - { - tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "ticks") - { - if(readXMLInteger(fieldAttributesNode, "value", intValue)) - ticks = std::max(0, intValue); - } + if(readXMLInteger(fieldAttributesNode, "value", intValue)) + ticks = std::max(0, intValue); + } - if(tmpStrValue == "count") - { - if(readXMLInteger(fieldAttributesNode, "value", intValue)) - count = std::max(1, intValue); - } + if(tmpStrValue == "count") + { + if(readXMLInteger(fieldAttributesNode, "value", intValue)) + count = std::max(1, intValue); + } - if(tmpStrValue == "start") - { - if(readXMLInteger(fieldAttributesNode, "value", intValue)) - start = std::max(0, intValue); - } + if(tmpStrValue == "start") + { + if(readXMLInteger(fieldAttributesNode, "value", intValue)) + start = std::max(0, intValue); + } - if(tmpStrValue == "damage") + if(tmpStrValue == "damage") + { + if(readXMLInteger(fieldAttributesNode, "value", intValue)) + { + damage = -intValue; + if(start > 0) { - if(readXMLInteger(fieldAttributesNode, "value", intValue)) - { - damage = -intValue; - if(start > 0) - { - std::list damageList; - ConditionDamage::generateDamageList(damage, start, damageList); - - for(std::list::iterator it = damageList.begin(); it != damageList.end(); ++it) - conditionDamage->addDamage(1, ticks, -*it); - - start = 0; - } - else - conditionDamage->addDamage(count, ticks, damage); - } + std::list damageList; + ConditionDamage::generateDamageList(damage, start, damageList); + for(std::list::iterator it = damageList.begin(); it != damageList.end(); ++it) + conditionDamage->addDamage(1, ticks, -*it); + + start = 0; } + else + conditionDamage->addDamage(count, ticks, damage); } - - fieldAttributesNode = fieldAttributesNode->next; } - - if(conditionDamage->getTotalDamage() > 0) - it.condition->setParam(CONDITIONPARAM_FORCEUPDATE, true); } + + conditionDamage->setParam(CONDITIONPARAM_FIELD, true); + if(conditionDamage->getTotalDamage() > 0) + conditionDamage->setParam(CONDITIONPARAM_FORCEUPDATE, true); } } - else if(tmpStrValue == "elementphysical") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_PHYSICALDAMAGE; - } - } - else if(tmpStrValue == "elementfire") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_FIREDAMAGE; - } - } - else if(tmpStrValue == "elementenergy") + } + else if(tmpStrValue == "elementphysical") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_ENERGYDAMAGE; - } + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_PHYSICALDAMAGE; } - else if(tmpStrValue == "elementearth") + } + else if(tmpStrValue == "elementfire") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_EARTHDAMAGE; - } + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_FIREDAMAGE; } - else if(tmpStrValue == "elementice") + } + else if(tmpStrValue == "elementenergy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_ICEDAMAGE; - } + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_ENERGYDAMAGE; } - else if(tmpStrValue == "elementholy") + } + else if(tmpStrValue == "elementearth") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_HOLYDAMAGE; - } + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_EARTHDAMAGE; } - else if(tmpStrValue == "elementdeath") + } + else if(tmpStrValue == "elementice") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_DEATHDAMAGE; - } + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_ICEDAMAGE; } - else if(tmpStrValue == "elementlifedrain") + } + else if(tmpStrValue == "elementholy") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_LIFEDRAIN; - } + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_HOLYDAMAGE; } - else if(tmpStrValue == "elementmanadrain") + } + else if(tmpStrValue == "elementdeath") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_MANADRAIN; - } + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_DEATHDAMAGE; } - else if(tmpStrValue == "elementhealing") + } + else if(tmpStrValue == "elementlifedrain") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_HEALING; - } + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_LIFEDRAIN; } - else if(tmpStrValue == "elementundefined") + } + else if(tmpStrValue == "elementmanadrain") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.abilities.elementDamage = intValue; - it.abilities.elementType = COMBAT_UNDEFINEDDAMAGE; - } + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_MANADRAIN; } - else if(tmpStrValue == "replaceable" || tmpStrValue == "replacable") + } + else if(tmpStrValue == "elementhealing") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.replaceable = (intValue != 0); + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_HEALING; } - else if(tmpStrValue == "partnerdirection") + } + else if(tmpStrValue == "elementundefined") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLString(itemAttributesNode, "value", strValue)) - it.bedPartnerDir = getDirection(strValue); + it.getAbilities()->elementDamage = intValue; + it.getAbilities()->elementType = COMBAT_UNDEFINEDDAMAGE; } - else if(tmpStrValue == "maletransformto") + } + else if(tmpStrValue == "replacable" || tmpStrValue == "replaceable") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.replacable = (intValue != 0); + } + else if(tmpStrValue == "partnerdirection") + { + if(readXMLString(itemAttributesNode, "value", strValue)) + it.bedPartnerDir = getDirection(strValue); + } + else if(tmpStrValue == "maletransformto") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.transformUseTo[PLAYERSEX_MALE] = intValue; - ItemType& ot = getItemType(intValue); - if(!ot.transformToFree) - ot.transformToFree = it.id; + it.transformBed[PLAYERSEX_MALE] = intValue; + ItemType& ot = getItemType(intValue); + if(!ot.transformUseTo) + ot.transformUseTo = it.id; - if(!it.transformUseTo[PLAYERSEX_FEMALE]) - it.transformUseTo[PLAYERSEX_FEMALE] = intValue; - } + if(!it.transformBed[PLAYERSEX_FEMALE]) + it.transformBed[PLAYERSEX_FEMALE] = intValue; } - else if(tmpStrValue == "femaletransformto") + } + else if(tmpStrValue == "femaletransformto") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - { - it.transformUseTo[PLAYERSEX_FEMALE] = intValue; - ItemType& ot = getItemType(intValue); - if(!ot.transformToFree) - ot.transformToFree = it.id; + it.transformBed[PLAYERSEX_FEMALE] = intValue; + ItemType& ot = getItemType(intValue); + if(!ot.transformUseTo) + ot.transformUseTo = it.id; - if(!it.transformUseTo[PLAYERSEX_MALE]) - it.transformUseTo[PLAYERSEX_MALE] = intValue; - } - } - else if(tmpStrValue == "transformto") - { - if(readXMLInteger(itemAttributesNode, "value", intValue)) - it.transformToFree = intValue; + if(!it.transformBed[PLAYERSEX_MALE]) + it.transformBed[PLAYERSEX_MALE] = intValue; } - else - std::cout << "[Warning - Items::loadFromXml] Unknown key value " << strValue << std::endl; } - - itemAttributesNode = itemAttributesNode->next; + else if(tmpStrValue == "transformto" || tmpStrValue == "transformuseto" || tmpStrValue == "onuseto") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.transformUseTo = intValue; + } + else if(tmpStrValue == "walkstack") + { + if(readXMLInteger(itemAttributesNode, "value", intValue)) + it.walkStack = (intValue != 0); + } + else + std::clog << "[Warning - Items::loadFromXml] Unknown key value " << strValue << std::endl; } if(it.pluralName.empty() && !it.name.empty()) @@ -1722,6 +1915,21 @@ void Items::parseItemNode(xmlNodePtr itemNode, uint32_t id) if(it.showCount) it.pluralName += "s"; } + + it.getAbilities()->absorb[COMBAT_ALL] = it.getAbilities()->absorb[COMBAT_FIRST + 1]; + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_ALL] = it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_FIRST + 1]; + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_ALL] = it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_FIRST + 1]; + for(uint32_t i = (COMBAT_FIRST + 1) << 1; i <= COMBAT_LAST; i <<= 1) + { + if(it.getAbilities()->absorb[COMBAT_ALL] != it.getAbilities()->absorb[i]) + it.getAbilities()->absorb[COMBAT_ALL] = 0; + + if(it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_ALL] != it.getAbilities()->reflect[REFLECT_PERCENT][i]) + it.getAbilities()->reflect[REFLECT_PERCENT][COMBAT_ALL] = 0; + + if(it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_ALL] != it.getAbilities()->reflect[REFLECT_CHANCE][i]) + it.getAbilities()->reflect[REFLECT_CHANCE][COMBAT_ALL] = 0; + } } void Items::parseRandomizationBlock(int32_t id, int32_t fromId, int32_t toId, int32_t chance) @@ -1729,15 +1937,15 @@ void Items::parseRandomizationBlock(int32_t id, int32_t fromId, int32_t toId, in RandomizationMap::iterator it = randomizationMap.find(id); if(it != randomizationMap.end()) { - std::cout << "[Warning - Items::parseRandomizationBlock] Duplicated item with id: " << id << std::endl; + std::clog << "[Warning - Items::parseRandomizationBlock] Duplicated item with id: " << id << std::endl; return; } RandomizationBlock rand; - rand.chance = chance; rand.fromRange = fromId; rand.toRange = toId; + rand.chance = chance; randomizationMap[id] = rand; } @@ -1747,7 +1955,7 @@ uint16_t Items::getRandomizedItem(uint16_t id) return id; RandomizationBlock randomize = getRandomization(id); - if(randomize.chance > 0 && random_range(0, 100) <= randomize.chance) + if(randomize.chance >= random_range(1, 100)) id = random_range(randomize.fromRange, randomize.toRange); return id; @@ -1760,7 +1968,7 @@ ItemType& Items::getItemType(int32_t id) return *iType; #ifdef __DEBUG__ - std::cout << "[Warning - Items::getItemType] Unknown itemtype with id " << id << ", using defaults." << std::endl; + std::clog << "[Warning - Items::getItemType] Unknown itemtype with id " << id << ", using defaults." << std::endl; #endif static ItemType dummyItemType; // use this for invalid ids return dummyItemType; @@ -1781,10 +1989,8 @@ const ItemType& Items::getItemIdByClientId(int32_t spriteId) const ItemType* iType; do { - if((iType = items.getElement(i)) && iType->clientId == spriteId) + if((iType = items.getElement(i++)) && iType->clientId == spriteId) return *iType; - - i++; } while(iType); static ItemType dummyItemType; // use this for invalid ids @@ -1799,10 +2005,10 @@ int32_t Items::getItemIdByName(const std::string& name) ItemType* iType = NULL; do { - if((iType = items.getElement(i)) && !strcasecmp(name.c_str(), iType->name.c_str())) + if((iType = items.getElement(i)) && boost::algorithm::iequals(name, iType->name)) return i; - i++; + ++i; } while(iType); } diff --git a/items.h b/items.h index 446ca5a..54ccbd1 100644 --- a/items.h +++ b/items.h @@ -26,21 +26,24 @@ #include "position.h" #include -#define ITEMS 11500 +#define ITEMS_SIZE 15000 +#define ITEMS_INCREMENT 500 +#define ITEMS_RANDOMIZATION 50 + #define SLOTP_WHEREEVER 0xFFFFFFFF -#define SLOTP_HEAD 1 << 0 -#define SLOTP_NECKLACE 1 << 1 -#define SLOTP_BACKPACK 1 << 2 -#define SLOTP_ARMOR 1 << 3 -#define SLOTP_RIGHT 1 << 4 -#define SLOTP_LEFT 1 << 5 -#define SLOTP_LEGS 1 << 6 -#define SLOTP_FEET 1 << 7 -#define SLOTP_RING 1 << 8 -#define SLOTP_AMMO 1 << 9 -#define SLOTP_DEPOT 1 << 10 -#define SLOTP_TWO_HAND 1 << 11 -#define SLOTP_HAND SLOTP_LEFT | SLOTP_RIGHT +#define SLOTP_HEAD (1 << 0) +#define SLOTP_NECKLACE (1 << 1) +#define SLOTP_BACKPACK (1 << 2) +#define SLOTP_ARMOR (1 << 3) +#define SLOTP_RIGHT (1 << 4) +#define SLOTP_LEFT (1 << 5) +#define SLOTP_LEGS (1 << 6) +#define SLOTP_FEET (1 << 7) +#define SLOTP_RING (1 << 8) +#define SLOTP_AMMO (1 << 9) +#define SLOTP_DEPOT (1 << 10) +#define SLOTP_TWO_HAND (1 << 11) +#define SLOTP_HAND (SLOTP_LEFT | SLOTP_RIGHT) enum ItemTypes_t { @@ -54,6 +57,7 @@ enum ItemTypes_t ITEM_TYPE_TELEPORT, ITEM_TYPE_BED, ITEM_TYPE_KEY, + ITEM_TYPE_RUNE, ITEM_TYPE_LAST }; @@ -72,6 +76,7 @@ enum FloorChange_t CHANGE_SOUTH_EX = 7, CHANGE_WEST_EX = 8, CHANGE_NONE = 9, + CHANGE_PRE_LAST = 8, CHANGE_LAST = CHANGE_NONE }; @@ -85,6 +90,7 @@ struct Abilities memset(statsPercent, 0, sizeof(statsPercent)); memset(absorb, 0, sizeof(absorb)); + memset(fieldAbsorb, 0, sizeof(fieldAbsorb)); memset(increment, 0, sizeof(increment)); memset(reflect[REFLECT_PERCENT], 0, sizeof(reflect[REFLECT_PERCENT])); memset(reflect[REFLECT_CHANCE], 0, sizeof(reflect[REFLECT_CHANCE])); @@ -97,7 +103,8 @@ struct Abilities bool manaShield, invisible, regeneration, preventLoss, preventDrop; CombatType_t elementType; - int16_t elementDamage, absorb[COMBAT_LAST + 1], increment[INCREMENT_LAST + 1], reflect[REFLECT_LAST + 1][COMBAT_LAST + 1]; + int16_t elementDamage, absorb[COMBAT_LAST + 1], increment[INCREMENT_LAST + 1], + reflect[REFLECT_LAST + 1][COMBAT_LAST + 1], fieldAbsorb[COMBAT_LAST + 1]; int32_t skills[SKILL_LAST + 1], skillsPercent[SKILL_LAST + 1], stats[STAT_LAST + 1], statsPercent[STAT_LAST + 1], speed, healthGain, healthTicks, manaGain, manaTicks, conditionSuppressions; }; @@ -106,11 +113,12 @@ class Condition; class ItemType { private: - ItemType(const ItemType& it) {} + ItemType(const ItemType&) {} //TODO! public: ItemType(); virtual ~ItemType(); + Abilities* getAbilities() {if(!abilities) abilities = new Abilities; return abilities;} bool isGroundTile() const {return (group == ITEM_GROUP_GROUND);} bool isContainer() const {return (group == ITEM_GROUP_CONTAINER);} @@ -124,16 +132,17 @@ class ItemType bool isDepot() const {return (type == ITEM_TYPE_DEPOT);} bool isMailbox() const {return (type == ITEM_TYPE_MAILBOX);} bool isTrashHolder() const {return (type == ITEM_TYPE_TRASHHOLDER);} + bool isRune() const {return (type == ITEM_TYPE_RUNE);} bool isBed() const {return (type == ITEM_TYPE_BED);} - bool isRune() const {return clientCharges;} bool hasSubType() const {return (isFluidContainer() || isSplash() || stackable || charges);} + bool hasAbilities() const {return abilities != NULL;} - bool stopTime, showCount, clientCharges, stackable, showDuration, showCharges, showAttributes, + bool loaded, stopTime, showCount, stackable, showDuration, showCharges, showAttributes, dualWield, allowDistRead, canReadText, canWriteText, forceSerialize, isVertical, isHorizontal, isHangable, - useable, moveable, pickupable, rotable, replaceable, lookThrough, - hasHeight, blockSolid, blockPickupable, blockProjectile, blockPathFind, allowPickupable, alwaysOnTop, - floorChange[CHANGE_LAST]; + usable, movable, pickupable, rotable, replacable, lookThrough, walkStack, hasHeight, blockSolid, + blockPickupable, blockProjectile, blockPathFind, allowPickupable, alwaysOnTop, floorChange[CHANGE_LAST], + isAnimation, specialDoor, closingDoor, cache; MagicEffect_t magicEffect; FluidTypes_t fluidSource; @@ -145,18 +154,18 @@ class ItemType ShootEffect_t shootType; Ammo_t ammoType; - uint16_t transformUseTo[2], transformToFree, transformEquipTo, transformDeEquipTo, - id, clientId, maxItems, slotPosition, wieldPosition, speed, maxTextLen, writeOnceItemId; + uint16_t transformBed[PLAYERSEX_MALE + 1], transformUseTo, transformEquipTo, transformDeEquipTo, + id, clientId, maxItems, slotPosition, wieldPosition, speed, maxTextLength, writeOnceItemId, wareId; int32_t attack, extraAttack, defense, extraDefense, armor, breakChance, hitChance, maxHitChance, runeLevel, runeMagLevel, lightLevel, lightColor, decayTo, rotateTo, alwaysOnTopOrder; uint32_t shootRange, charges, decayTime, attackSpeed, wieldInfo, minReqLevel, minReqMagicLevel, - worth, levelDoor; + worth, levelDoor, date; - std::string name, pluralName, article, description, runeSpellName, vocationString; + std::string name, pluralName, article, description, text, writer, runeSpellName, vocationString; Condition* condition; - Abilities abilities; + Abilities* abilities; itemgroup_t group; ItemTypes_t type; float weight; @@ -167,12 +176,22 @@ class Array { public: Array(uint32_t n); - virtual ~Array(); + virtual ~Array() {clear();} + + void clear() + { + if(m_data && m_size) + { + free(m_data); + m_size = 0; + } + } + void reload(); A getElement(uint32_t id); const A getElement(uint32_t id) const; - void addElement(A a, uint32_t pos); + void addElement(A a, uint32_t pos); uint32_t size() {return m_size;} private: @@ -189,9 +208,10 @@ Array::Array(uint32_t n) } template -Array::~Array() +void Array::reload() { - free(m_data); + m_data = (A*)malloc(sizeof(A) * m_size); + memset(m_data, 0, sizeof(A) * m_size); } template @@ -215,12 +235,11 @@ const A Array::getElement(uint32_t id) const template void Array::addElement(A a, uint32_t pos) { - #define INCREMENT 5000 if(pos >= m_size) { - m_data = (A*)realloc(m_data, sizeof(A) * (pos + INCREMENT)); - memset(m_data + m_size, 0, sizeof(A) * (pos + INCREMENT - m_size)); - m_size = pos + INCREMENT; + m_data = (A*)realloc(m_data, sizeof(A) * (pos + ITEMS_INCREMENT)); + memset(m_data + m_size, 0, sizeof(A) * (pos + ITEMS_INCREMENT - m_size)); + m_size = pos + ITEMS_INCREMENT; } m_data[pos] = a; @@ -237,7 +256,7 @@ typedef std::map IntegerMap; class Items { public: - Items(): m_randomizationChance(50), items(ITEMS) {} + Items(): m_randomizationChance(ITEMS_RANDOMIZATION), items(ITEMS_SIZE) {} virtual ~Items() {clear();} bool reload(); @@ -255,10 +274,10 @@ class Items uint16_t getRandomizedItem(uint16_t id); uint8_t getRandomizationChance() const {return m_randomizationChance;} - RandomizationBlock getRandomization(int16_t id) {return randomizationMap[id];} + const RandomizationBlock getRandomization(int16_t id) {return randomizationMap[id];} uint32_t size() {return items.size();} - IntegerMap getMoneyMap() {return moneyMap;} + const IntegerMap getMoneyMap() const {return moneyMap;} const ItemType* getElement(uint32_t id) const {return items.getElement(id);} static uint32_t dwMajorVersion; diff --git a/luascript.cpp b/luascript.cpp index 5448478..df0280b 100644 --- a/luascript.cpp +++ b/luascript.cpp @@ -26,37 +26,47 @@ #include "player.h" #include "item.h" #include "teleport.h" +#include "beds.h" #include "town.h" #include "house.h" #include "housetile.h" #include "database.h" +#include "databasemanager.h" #include "iologindata.h" #include "ioban.h" +#include "iomap.h" #include "iomapserialize.h" -#include "talkaction.h" +#include "monsters.h" +#include "movement.h" #include "spells.h" +#include "talkaction.h" +#include "creatureevent.h" + #include "combat.h" #include "condition.h" #include "baseevents.h" -#include "monsters.h" #include "raids.h" +#include "mounts.h" #include "configmanager.h" #include "vocation.h" #include "status.h" #include "game.h" #include "chat.h" +#include "tools.h" +extern ConfigManager g_config; extern Game g_game; -extern Monsters g_monsters; extern Chat g_chat; -extern ConfigManager g_config; +extern Monsters g_monsters; +extern MoveEvents* g_moveEvents; extern Spells* g_spells; extern TalkActions* g_talkActions; +extern CreatureEvents* g_creatureEvents; enum { @@ -70,10 +80,12 @@ ScriptEnviroment::CombatMap ScriptEnviroment::m_combatMap; uint32_t ScriptEnviroment::m_lastCombatId = 0; ScriptEnviroment::ConditionMap ScriptEnviroment::m_conditionMap; uint32_t ScriptEnviroment::m_lastConditionId = 0; +ScriptEnviroment::ConditionMap ScriptEnviroment::m_tempConditionMap; +uint32_t ScriptEnviroment::m_lastTempConditionId = 0; ScriptEnviroment::ThingMap ScriptEnviroment::m_globalMap; -ScriptEnviroment::StorageMap ScriptEnviroment::m_storageMap; ScriptEnviroment::TempItemListMap ScriptEnviroment::m_tempItems; +StorageMap ScriptEnviroment::m_storageMap; ScriptEnviroment::ScriptEnviroment() { @@ -92,20 +104,23 @@ ScriptEnviroment::~ScriptEnviroment() delete it->second; m_areaMap.clear(); + for(ConditionMap::iterator it = m_conditionMap.begin(); it != m_conditionMap.end(); ++it) + delete it->second; + + m_conditionMap.clear(); reset(); } void ScriptEnviroment::reset() { m_scriptId = m_callbackId = 0; - m_timerEvent = false; m_realPos = Position(); + m_timerEvent = false; m_interface = NULL; for(TempItemListMap::iterator mit = m_tempItems.begin(); mit != m_tempItems.end(); ++mit) { - ItemList itemList = mit->second; - for(ItemList::iterator it = itemList.begin(); it != itemList.end(); ++it) + for(ItemList::iterator it = mit->second.begin(); it != mit->second.end(); ++it) { if((*it)->getParent() == VirtualCylinder::virtualCylinder) g_game.freeThing(*it); @@ -120,6 +135,10 @@ void ScriptEnviroment::reset() } m_tempResults.clear(); + for(ConditionMap::iterator it = m_tempConditionMap.begin(); it != m_tempConditionMap.end(); ++it) + delete it->second; + + m_tempConditionMap.clear(); m_localMap.clear(); } @@ -132,20 +151,20 @@ bool ScriptEnviroment::saveGameState() DBQuery query; query << "DELETE FROM `global_storage` WHERE `world_id` = " << g_config.getNumber(ConfigManager::WORLD_ID) << ";"; - if(!db->executeQuery(query.str())) + if(!db->query(query.str())) return false; - DBInsert query_insert(db); - query_insert.setQuery("INSERT INTO `global_storage` (`key`, `world_id`, `value`) VALUES "); + DBInsert stmt(db); + stmt.setQuery("INSERT INTO `global_storage` (`key`, `world_id`, `value`) VALUES "); for(StorageMap::const_iterator it = m_storageMap.begin(); it != m_storageMap.end(); ++it) { - char buffer[25 + it->second.length()]; - sprintf(buffer, "%u, %u, %s", it->first, g_config.getNumber(ConfigManager::WORLD_ID), db->escapeString(it->second).c_str()); - if(!query_insert.addRow(buffer)) + query.str(""); + query << db->escapeString(it->first) << "," << g_config.getNumber(ConfigManager::WORLD_ID) << "," << db->escapeString(it->second); + if(!stmt.addRow(query)) return false; } - return query_insert.execute(); + return stmt.execute(); } bool ScriptEnviroment::loadGameState() @@ -158,7 +177,7 @@ bool ScriptEnviroment::loadGameState() if((result = db->storeQuery(query.str()))) { do - m_storageMap[result->getDataInt("key")] = result->getDataString("value"); + m_storageMap[result->getDataString("key")] = result->getDataString("value"); while(result->next()); result->free(); } @@ -167,7 +186,7 @@ bool ScriptEnviroment::loadGameState() return true; } -bool ScriptEnviroment::setCallbackId(int32_t callbackId, LuaScriptInterface* interface) +bool ScriptEnviroment::setCallbackId(int32_t callbackId, LuaInterface* interface) { if(!m_callbackId) { @@ -183,10 +202,10 @@ bool ScriptEnviroment::setCallbackId(int32_t callbackId, LuaScriptInterface* int return false; } -void ScriptEnviroment::getInfo(int32_t& scriptId, std::string& desc, LuaScriptInterface*& interface, int32_t& callbackId, bool& timerEvent) +void ScriptEnviroment::getInfo(int32_t& scriptId, std::string& desc, LuaInterface*& interface, int32_t& callbackId, bool& timerEvent) { scriptId = m_scriptId; - desc = m_eventdesc; + desc = m_event; interface = m_interface; callbackId = m_callbackId; timerEvent = m_timerEvent; @@ -201,7 +220,7 @@ void ScriptEnviroment::addUniqueThing(Thing* thing) if(m_globalMap[item->getUniqueId()]) { if(item->getActionId() != 2000) //scripted quest system - std::cout << "Duplicate uniqueId " << item->getUniqueId() << std::endl; + std::clog << "Duplicate uniqueId " << item->getUniqueId() << std::endl; } else m_globalMap[item->getUniqueId()] = thing; @@ -254,10 +273,10 @@ uint32_t ScriptEnviroment::addThing(Thing* thing) void ScriptEnviroment::insertThing(uint32_t uid, Thing* thing) { - if(!m_localMap[uid]) - m_localMap[uid] = thing; + if(m_localMap[uid]) + std::clog << "[Error - ScriptEnviroment::insertThing] Thing uid already taken" << std::endl; else - std::cout << "[Error - ScriptEnviroment::insertThing] Thing uid already taken" << std::endl; + m_localMap[uid] = thing; } Thing* ScriptEnviroment::getThingByUID(uint32_t uid) @@ -270,17 +289,14 @@ Thing* ScriptEnviroment::getThingByUID(uint32_t uid) if(tmp && !tmp->isRemoved()) return tmp; - if(uid >= 0x10000000) - { - tmp = g_game.getCreatureByID(uid); - if(tmp && !tmp->isRemoved()) - { - m_localMap[uid] = tmp; - return tmp; - } - } + if(uid < PLAYER_ID_RANGE) + return NULL; - return NULL; + if(!(tmp = g_game.getCreatureByID(uid)) || tmp->isRemoved()) + return NULL; + + m_localMap[uid] = tmp; + return tmp; } Item* ScriptEnviroment::getItemByUID(uint32_t uid) @@ -380,18 +396,31 @@ Combat* ScriptEnviroment::getCombatObject(uint32_t combatId) uint32_t ScriptEnviroment::addConditionObject(Condition* condition) { - uint32_t newConditionId = m_lastConditionId + 1; - m_conditionMap[newConditionId] = condition; - - m_lastConditionId++; + m_conditionMap[++m_lastConditionId] = condition; return m_lastConditionId; } -Condition* ScriptEnviroment::getConditionObject(uint32_t conditionId) +uint32_t ScriptEnviroment::addTempConditionObject(Condition* condition) { - ConditionMap::iterator it = m_conditionMap.find(conditionId); - if(it != m_conditionMap.end()) - return it->second; + m_tempConditionMap[++m_lastTempConditionId] = condition; + return m_lastTempConditionId; +} + +Condition* ScriptEnviroment::getConditionObject(uint32_t conditionId, bool loaded) +{ + ConditionMap::iterator it; + if(loaded) + { + it = m_conditionMap.find(conditionId); + if(it != m_conditionMap.end()) + return it->second; + } + else + { + it = m_tempConditionMap.find(conditionId); + if(it != m_tempConditionMap.end()) + return it->second; + } return NULL; } @@ -403,20 +432,19 @@ void ScriptEnviroment::addTempItem(ScriptEnviroment* env, Item* item) void ScriptEnviroment::removeTempItem(ScriptEnviroment* env, Item* item) { - ItemList itemList = m_tempItems[env]; - ItemList::iterator it = std::find(itemList.begin(), itemList.end(), item); - if(it != itemList.end()) - itemList.erase(it); + ItemList::iterator it = std::find(m_tempItems[env].begin(), m_tempItems[env].end(), item); + if(it != m_tempItems[env].end()) + m_tempItems[env].erase(it); } void ScriptEnviroment::removeTempItem(Item* item) { + ItemList::iterator it; for(TempItemListMap::iterator mit = m_tempItems.begin(); mit != m_tempItems.end(); ++mit) { - ItemList itemList = mit->second; - ItemList::iterator it = std::find(itemList.begin(), itemList.end(), item); - if(it != itemList.end()) - itemList.erase(it); + it = std::find(mit->second.begin(), mit->second.end(), item); + if(it != mit->second.end()) + mit->second.erase(it); } } @@ -452,7 +480,7 @@ DBResult* ScriptEnviroment::getResultByID(uint32_t id) return NULL; } -bool ScriptEnviroment::getStorage(const uint32_t key, std::string& value) const +bool ScriptEnviroment::getStorage(const std::string& key, std::string& value) const { StorageMap::const_iterator it = m_storageMap.find(key); if(it != m_storageMap.end()) @@ -506,14 +534,17 @@ void ScriptEnviroment::streamThing(std::stringstream& stream, const std::string& if(!id) id = addThing(thing); + stream << "uniqueid = " << id << "," << std::endl; stream << "uid = " << id << "," << std::endl; stream << "itemid = " << item->getID() << "," << std::endl; + stream << "id = " << item->getID() << "," << std::endl; if(item->hasSubType()) stream << "type = " << item->getSubType() << "," << std::endl; else stream << "type = 0," << std::endl; - stream << "actionid = " << item->getActionId() << std::endl; + stream << "actionid = " << item->getActionId() << "," << std::endl; + stream << "aid = " << item->getActionId() << std::endl; } else if(thing && thing->getCreature()) { @@ -521,8 +552,10 @@ void ScriptEnviroment::streamThing(std::stringstream& stream, const std::string& if(!id) id = creature->getID(); + stream << "uniqueid = " << id << "," << std::endl; stream << "uid = " << id << "," << std::endl; stream << "itemid = 1," << std::endl; + stream << "id = 1," << std::endl; if(creature->getPlayer()) stream << "type = 1," << std::endl; else if(creature->getMonster()) @@ -531,15 +564,24 @@ void ScriptEnviroment::streamThing(std::stringstream& stream, const std::string& stream << "type = 3," << std::endl; if(const Player* player = creature->getPlayer()) + { stream << "actionid = " << player->getGUID() << "," << std::endl; + stream << "aid = " << player->getGUID() << std::endl; + } else - stream << "actionid = 0" << std::endl; + { + stream << "actionid = 0," << std::endl; + stream << "aid = 0" << std::endl; + } } else { + stream << "uniqueid = 0," << std::endl; stream << "uid = 0," << std::endl; stream << "itemid = 0," << std::endl; + stream << "id = 0," << std::endl; stream << "type = 0," << std::endl; + stream << "aid = 0," << std::endl; stream << "actionid = 0" << std::endl; } @@ -574,12 +616,13 @@ void ScriptEnviroment::streamOutfit(std::stringstream& stream, const std::string stream << "lookLegs = " << outfit.lookLegs << "," << std::endl; stream << "lookFeet = " << outfit.lookFeet << "," << std::endl; - stream << "lookAddons = " << outfit.lookAddons << std::endl; + stream << "lookAddons = " << outfit.lookAddons << "," << std::endl; + stream << "lookMount = " << outfit.lookMount << std::endl; if(!local.empty()) stream << "}" << std::endl; } -std::string LuaScriptInterface::getError(ErrorCode_t code) +std::string LuaInterface::getError(ErrorCode_t code) { switch(code) { @@ -620,35 +663,39 @@ std::string LuaScriptInterface::getError(ErrorCode_t code) return "Invalid error code!"; } -ScriptEnviroment LuaScriptInterface::m_scriptEnv[21]; -int32_t LuaScriptInterface::m_scriptEnvIndex = -1; +ScriptEnviroment LuaInterface::m_scriptEnv[21]; +int32_t LuaInterface::m_scriptEnvIndex = -1; -LuaScriptInterface::LuaScriptInterface(std::string interfaceName) +LuaInterface::LuaInterface(std::string interfaceName) { m_luaState = NULL; m_interfaceName = interfaceName; - m_lastEventTimerId = 1000; + m_lastTimer = 1000; + m_errors = true; } -LuaScriptInterface::~LuaScriptInterface() +LuaInterface::~LuaInterface() { + for(LuaTimerEvents::iterator it = m_timerEvents.begin(); it != m_timerEvents.end(); ++it) + Scheduler::getInstance().stopEvent(it->second.eventId); + closeState(); } -bool LuaScriptInterface::reInitState() +bool LuaInterface::reInitState() { closeState(); return initState(); } -bool LuaScriptInterface::loadBuffer(const std::string& text, Npc* npc/* = NULL*/) +bool LuaInterface::loadBuffer(const std::string& text, Npc* npc/* = NULL*/) { //loads buffer as a chunk at stack top - int32_t ret = luaL_loadbuffer(m_luaState, text.c_str(), text.length(), "loadBuffer"); + int32_t ret = luaL_loadbuffer(m_luaState, text.c_str(), text.length(), "LuaInterface::loadBuffer"); if(ret) { m_lastError = popString(m_luaState); - error(NULL, m_lastError); + std::clog << "[Error - LuaInterface::loadBuffer] " << m_lastError << std::endl; return false; } @@ -656,7 +703,7 @@ bool LuaScriptInterface::loadBuffer(const std::string& text, Npc* npc/* = NULL*/ if(!lua_isfunction(m_luaState, -1)) return false; - m_loadingFile = "buffer"; + m_loadingFile = text; reserveEnv(); ScriptEnviroment* env = getEnv(); @@ -676,14 +723,14 @@ bool LuaScriptInterface::loadBuffer(const std::string& text, Npc* npc/* = NULL*/ return true; } -bool LuaScriptInterface::loadFile(const std::string& file, Npc* npc/* = NULL*/) +bool LuaInterface::loadFile(const std::string& file, Npc* npc/* = NULL*/) { //loads file as a chunk at stack top int32_t ret = luaL_loadfile(m_luaState, file.c_str()); if(ret) { m_lastError = popString(m_luaState); - std::cout << "[Error - LuaScriptInterface::loadFile] " << m_lastError << std::endl; + std::clog << "[Error - LuaInterface::loadFile] " << m_lastError << std::endl; return false; } @@ -711,13 +758,24 @@ bool LuaScriptInterface::loadFile(const std::string& file, Npc* npc/* = NULL*/) return true; } -bool LuaScriptInterface::loadDirectory(const std::string& dir, Npc* npc/* = NULL*/) +bool LuaInterface::loadDirectory(std::string dir, bool recursively, bool loadSystems, Npc* npc/* = NULL*/) { + if(dir[dir.size() - 1] != '/') + dir += '/'; + StringVec files; for(boost::filesystem::directory_iterator it(dir), end; it != end; ++it) { std::string s = it->leaf(); - if(!boost::filesystem::is_directory(it->status()) && (s.size() > 4 ? s.substr(s.size() - 4) : "") == ".lua") + if(!loadSystems && s[0] == '_') + continue; + + if(boost::filesystem::is_directory(it->status())) + { + if(recursively && !loadDirectory(dir + s, recursively, loadSystems, npc)) + return false; + } + else if((s.size() > 4 ? s.substr(s.size() - 4) : "") == ".lua") files.push_back(s); } @@ -731,7 +789,7 @@ bool LuaScriptInterface::loadDirectory(const std::string& dir, Npc* npc/* = NULL return true; } -int32_t LuaScriptInterface::getEvent(const std::string& eventName) +int32_t LuaInterface::getEvent(const std::string& eventName) { //get our events table lua_getfield(m_luaState, LUA_REGISTRYINDEX, "EVENTS"); @@ -750,7 +808,7 @@ int32_t LuaScriptInterface::getEvent(const std::string& eventName) } //save in our events table - lua_pushnumber(m_luaState, m_runningEventId); + lua_pushnumber(m_luaState, m_runningEvent); lua_pushvalue(m_luaState, -2); lua_rawset(m_luaState, -4); @@ -760,90 +818,93 @@ int32_t LuaScriptInterface::getEvent(const std::string& eventName) lua_pushnil(m_luaState); lua_setglobal(m_luaState, eventName.c_str()); - m_cacheFiles[m_runningEventId] = m_loadingFile + ":" + eventName; - ++m_runningEventId; - return m_runningEventId - 1; + m_cacheFiles[m_runningEvent] = m_loadingFile + ":" + eventName; + ++m_runningEvent; + return m_runningEvent - 1; } -std::string LuaScriptInterface::getScript(int32_t scriptId) +std::string LuaInterface::getScript(int32_t scriptId) { - const static std::string tmp = "(Unknown script file)"; - if(scriptId != EVENT_ID_LOADING) - { - ScriptsCache::iterator it = m_cacheFiles.find(scriptId); - if(it != m_cacheFiles.end()) - return it->second; + if(scriptId == EVENT_ID_LOADING) + return m_loadingFile; - return tmp; - } + ScriptsCache::iterator it = m_cacheFiles.find(scriptId); + if(it != m_cacheFiles.end()) + return it->second; - return m_loadingFile; + return "(Unknown script file)"; } -void LuaScriptInterface::error(const char* function, const std::string& desc) +void LuaInterface::error(const char* function, const std::string& desc) { + if(g_config.getBool(ConfigManager::SILENT_LUA)) + return; + int32_t script, callback; bool timer; std::string event; - LuaScriptInterface* interface; + LuaInterface* interface; getEnv()->getInfo(script, event, interface, callback, timer); if(interface) { - std::cout << std::endl << "[Error - " << interface->getName() << "] " << std::endl; + if(!interface->m_errors) + return; + + std::clog << std::endl << "[Error - " << interface->getName() << "] " << std::endl; if(callback) - std::cout << "In a callback: " << interface->getScript(callback) << std::endl; + std::clog << "In a callback: " << interface->getScript(callback) << std::endl; if(timer) - std::cout << (callback ? "from" : "In") << " a timer event called from: " << std::endl; + std::clog << (callback ? "from" : "In") << " a timer event called from: " << std::endl; - std::cout << interface->getScript(script) << std::endl << "Description: "; + std::clog << interface->getScript(script) << std::endl << "Description: "; } else - std::cout << std::endl << "[Lua Error] "; + std::clog << std::endl << "[Lua Error] "; - std::cout << event << std::endl; + std::clog << event << std::endl; if(function) - std::cout << "(" << function << ") "; + std::clog << "(" << function << ") "; - std::cout << desc << std::endl; + std::clog << desc << std::endl; } -bool LuaScriptInterface::pushFunction(int32_t function) +bool LuaInterface::pushFunction(int32_t function) { lua_getfield(m_luaState, LUA_REGISTRYINDEX, "EVENTS"); - if(lua_istable(m_luaState, -1)) - { - lua_pushnumber(m_luaState, function); - lua_rawget(m_luaState, -2); + if(!lua_istable(m_luaState, -1)) + return false; - lua_remove(m_luaState, -2); - if(lua_isfunction(m_luaState, -1)) - return true; - } + lua_pushnumber(m_luaState, function); + lua_rawget(m_luaState, -2); - return false; + lua_remove(m_luaState, -2); + return lua_isfunction(m_luaState, -1); } -bool LuaScriptInterface::initState() +bool LuaInterface::initState() { m_luaState = luaL_newstate(); if(!m_luaState) return false; luaL_openlibs(m_luaState); +#ifdef __LUAJIT__ + luaJIT_setmode(m_luaState, 0, LUAJIT_MODE_ENGINE | LUAJIT_MODE_ON); +#endif + registerFunctions(); - if(!loadDirectory(getFilePath(FILE_TYPE_OTHER, "lib/"), NULL)) - std::cout << "[Warning - LuaScriptInterface::initState] Cannot load " << getFilePath(FILE_TYPE_OTHER, "lib/") << std::endl; + if(!loadDirectory(getFilePath(FILE_TYPE_OTHER, "lib/"), false, true)) + std::clog << "[Warning - LuaInterface::initState] Cannot load " << getFilePath(FILE_TYPE_OTHER, "lib/") << std::endl; lua_newtable(m_luaState); lua_setfield(m_luaState, LUA_REGISTRYINDEX, "EVENTS"); - - m_runningEventId = EVENT_ID_USER; + m_runningEvent = EVENT_ID_USER; return true; } -bool LuaScriptInterface::closeState() +bool LuaInterface::closeState() { if(!m_luaState) return false; @@ -863,42 +924,43 @@ bool LuaScriptInterface::closeState() return true; } -void LuaScriptInterface::executeTimer(uint32_t eventIndex) +void LuaInterface::executeTimer(uint32_t eventIndex) { LuaTimerEvents::iterator it = m_timerEvents.find(eventIndex); - if(it != m_timerEvents.end()) - { - //push function - lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, it->second.function); - - //push parameters - for(std::list::reverse_iterator rt = it->second.parameters.rbegin(); rt != it->second.parameters.rend(); ++rt) - lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, *rt); + if(it == m_timerEvents.end()) + return; - //call the function - if(reserveEnv()) - { - ScriptEnviroment* env = getEnv(); - env->setTimerEvent(); - env->setScriptId(it->second.scriptId, this); + //push function + lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, it->second.function); + //push parameters + for(std::list::reverse_iterator rt = it->second.parameters.rbegin(); rt != it->second.parameters.rend(); ++rt) + lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, *rt); - callFunction(it->second.parameters.size()); - releaseEnv(); - } - else - std::cout << "[Error] Call stack overflow. LuaScriptInterface::executeTimer" << std::endl; + //call the function + if(reserveEnv()) + { + ScriptEnviroment* env = getEnv(); + env->setTimerEvent(); - //free resources - for(std::list::iterator lt = it->second.parameters.begin(); lt != it->second.parameters.end(); ++lt) - luaL_unref(m_luaState, LUA_REGISTRYINDEX, *lt); + env->setScriptId(it->second.scriptId, this); + env->setNpc(it->second.npc); - it->second.parameters.clear(); - luaL_unref(m_luaState, LUA_REGISTRYINDEX, it->second.function); - m_timerEvents.erase(it); + callFunction(it->second.parameters.size()); + releaseEnv(); } + else + std::clog << "[Error - LuaInterface::executeTimer] Call stack overflow." << std::endl; + + //free resources + for(std::list::iterator lt = it->second.parameters.begin(); lt != it->second.parameters.end(); ++lt) + luaL_unref(m_luaState, LUA_REGISTRYINDEX, *lt); + + it->second.parameters.clear(); + luaL_unref(m_luaState, LUA_REGISTRYINDEX, it->second.function); + m_timerEvents.erase(it); } -int32_t LuaScriptInterface::handleFunction(lua_State* L) +int32_t LuaInterface::handleFunction(lua_State* L) { lua_getfield(L, LUA_GLOBALSINDEX, "debug"); if(!lua_istable(L, -1)) @@ -921,7 +983,7 @@ int32_t LuaScriptInterface::handleFunction(lua_State* L) return 1; } -bool LuaScriptInterface::callFunction(uint32_t params) +bool LuaInterface::callFunction(uint32_t params) { int32_t size = lua_gettop(m_luaState), handler = lua_gettop(m_luaState) - params; lua_pushcfunction(m_luaState, handleFunction); @@ -929,18 +991,18 @@ bool LuaScriptInterface::callFunction(uint32_t params) bool result = false; lua_insert(m_luaState, handler); if(lua_pcall(m_luaState, params, 1, handler)) - LuaScriptInterface::error(NULL, LuaScriptInterface::popString(m_luaState)); + LuaInterface::error(NULL, LuaInterface::popString(m_luaState)); else - result = (int32_t)LuaScriptInterface::popBoolean(m_luaState); + result = (int32_t)LuaInterface::popBoolean(m_luaState); lua_remove(m_luaState, handler); if((lua_gettop(m_luaState) + (int32_t)params + 1) != size) - LuaScriptInterface::error(NULL, "Stack size changed!"); + LuaInterface::error(NULL, "Stack size changed!"); return result; } -void LuaScriptInterface::dumpStack(lua_State* L/* = NULL*/) +void LuaInterface::dumpStack(lua_State* L/* = NULL*/) { if(!L) L = m_luaState; @@ -949,12 +1011,12 @@ void LuaScriptInterface::dumpStack(lua_State* L/* = NULL*/) if(!stack) return; - std::cout << "Stack size: " << stack << std::endl; + std::clog << "Stack size: " << stack << std::endl; for(int32_t i = 1; i <= stack ; ++i) - std::cout << lua_typename(m_luaState, lua_type(m_luaState, -i)) << " " << lua_topointer(m_luaState, -i) << std::endl; + std::clog << lua_typename(m_luaState, lua_type(m_luaState, -i)) << " " << lua_topointer(m_luaState, -i) << std::endl; } -void LuaScriptInterface::pushVariant(lua_State* L, const LuaVariant& var) +void LuaInterface::pushVariant(lua_State* L, const LuaVariant& var) { lua_newtable(L); setField(L, "type", var.type); @@ -979,7 +1041,7 @@ void LuaScriptInterface::pushVariant(lua_State* L, const LuaVariant& var) } } -void LuaScriptInterface::pushThing(lua_State* L, Thing* thing, uint32_t id/* = 0*/) +void LuaInterface::pushThing(lua_State* L, Thing* thing, uint32_t id/* = 0*/, Recursive_t recursive/* = RECURSE_FIRST*/) { lua_newtable(L); if(thing && thing->getItem()) @@ -988,14 +1050,36 @@ void LuaScriptInterface::pushThing(lua_State* L, Thing* thing, uint32_t id/* = 0 if(!id) id = getEnv()->addThing(thing); + setField(L, "uniqueid", id); setField(L, "uid", id); setField(L, "itemid", item->getID()); + setField(L, "id", item->getID()); if(item->hasSubType()) setField(L, "type", item->getSubType()); else setField(L, "type", 0); setField(L, "actionid", item->getActionId()); + setField(L, "aid", item->getActionId()); + if(recursive != RECURSE_NONE) + { + if(const Container* container = item->getContainer()) + { + if(recursive == RECURSE_FIRST) + recursive = RECURSE_NONE; + + ItemList::const_iterator it = container->getItems(); + createTable(L, "items"); + for(int32_t i = 1; it != container->getEnd(); ++it, ++i) + { + lua_pushnumber(L, i); + pushThing(L, *it, getEnv()->addThing(*it), recursive); + pushTable(L); + } + + pushTable(L); + } + } } else if(thing && thing->getCreature()) { @@ -1003,8 +1087,10 @@ void LuaScriptInterface::pushThing(lua_State* L, Thing* thing, uint32_t id/* = 0 if(!id) id = creature->getID(); + setField(L, "uniqueid", id); setField(L, "uid", id); setField(L, "itemid", 1); + setField(L, "id", 1); if(creature->getPlayer()) setField(L, "type", 1); else if(creature->getMonster()) @@ -1013,20 +1099,29 @@ void LuaScriptInterface::pushThing(lua_State* L, Thing* thing, uint32_t id/* = 0 setField(L, "type", 3); if(const Player* player = creature->getPlayer()) + { setField(L, "actionid", player->getGUID()); + setField(L, "aid", player->getGUID()); + } else + { setField(L, "actionid", 0); + setField(L, "aid", 0); + } } else { setField(L, "uid", 0); + setField(L, "uniqueid", 0); setField(L, "itemid", 0); + setField(L, "id", 0); setField(L, "type", 0); setField(L, "actionid", 0); + setField(L, "aid", 0); } } -void LuaScriptInterface::pushPosition(lua_State* L, const Position& position, uint32_t stackpos) +void LuaInterface::pushPosition(lua_State* L, const Position& position, uint32_t stackpos) { lua_newtable(L); setField(L, "x", position.x); @@ -1035,7 +1130,7 @@ void LuaScriptInterface::pushPosition(lua_State* L, const Position& position, ui setField(L, "stackpos", stackpos); } -void LuaScriptInterface::pushOutfit(lua_State* L, const Outfit_t& outfit) +void LuaInterface::pushOutfit(lua_State* L, const Outfit_t& outfit) { lua_newtable(L); setField(L, "lookType", outfit.lookType); @@ -1045,14 +1140,15 @@ void LuaScriptInterface::pushOutfit(lua_State* L, const Outfit_t& outfit) setField(L, "lookLegs", outfit.lookLegs); setField(L, "lookFeet", outfit.lookFeet); setField(L, "lookAddons", outfit.lookAddons); + setField(L, "lookMount", outfit.lookMount); } -void LuaScriptInterface::pushCallback(lua_State* L, int32_t callback) +void LuaInterface::pushCallback(lua_State* L, int32_t callback) { lua_rawgeti(L, LUA_REGISTRYINDEX, callback); } -LuaVariant LuaScriptInterface::popVariant(lua_State* L) +LuaVariant LuaInterface::popVariant(lua_State* L) { LuaVariant var; var.type = (LuaVariantType_t)getField(L, "type"); @@ -1081,7 +1177,7 @@ LuaVariant LuaScriptInterface::popVariant(lua_State* L) return var; } -void LuaScriptInterface::popPosition(lua_State* L, PositionEx& position) +void LuaInterface::popPosition(lua_State* L, PositionEx& position) { if(!lua_isboolean(L, -1)) { @@ -1096,7 +1192,7 @@ void LuaScriptInterface::popPosition(lua_State* L, PositionEx& position) lua_pop(L, 1); //table } -void LuaScriptInterface::popPosition(lua_State* L, Position& position, uint32_t& stackpos) +void LuaInterface::popPosition(lua_State* L, Position& position, uint32_t& stackpos) { stackpos = 0; if(!lua_isboolean(L, -1)) @@ -1112,13 +1208,13 @@ void LuaScriptInterface::popPosition(lua_State* L, Position& position, uint32_t& lua_pop(L, 1); //table } -bool LuaScriptInterface::popBoolean(lua_State* L) +bool LuaInterface::popBoolean(lua_State* L) { lua_pop(L, 1); - return lua_toboolean(L, 0); + return (lua_toboolean(L, 0) != 0); } -int64_t LuaScriptInterface::popNumber(lua_State* L) +int64_t LuaInterface::popNumber(lua_State* L) { lua_pop(L, 1); if(lua_isboolean(L, 0)) @@ -1127,30 +1223,34 @@ int64_t LuaScriptInterface::popNumber(lua_State* L) return (int64_t)lua_tonumber(L, 0); } -double LuaScriptInterface::popFloatNumber(lua_State* L) +double LuaInterface::popFloatNumber(lua_State* L) { lua_pop(L, 1); return lua_tonumber(L, 0); } -std::string LuaScriptInterface::popString(lua_State* L) +std::string LuaInterface::popString(lua_State* L) { lua_pop(L, 1); + if(!lua_isstring(L, 0) && !lua_isnumber(L, 0)) + return std::string(); + const char* str = lua_tostring(L, 0); - if(!str || !strlen(str)) + if(*str == '\0') return std::string(); return str; } -int32_t LuaScriptInterface::popCallback(lua_State* L) +int32_t LuaInterface::popCallback(lua_State* L) { return luaL_ref(L, LUA_REGISTRYINDEX); } -Outfit_t LuaScriptInterface::popOutfit(lua_State* L) +Outfit_t LuaInterface::popOutfit(lua_State* L) { Outfit_t outfit; + outfit.lookMount = getField(L, "lookMount"); outfit.lookAddons = getField(L, "lookAddons"); outfit.lookFeet = getField(L, "lookFeet"); @@ -1165,64 +1265,64 @@ Outfit_t LuaScriptInterface::popOutfit(lua_State* L) return outfit; } -void LuaScriptInterface::setField(lua_State* L, const char* index, int32_t val) +void LuaInterface::setField(lua_State* L, const char* index, int32_t val) { lua_pushstring(L, index); lua_pushnumber(L, val); pushTable(L); } -void LuaScriptInterface::setField(lua_State* L, const char* index, const std::string& val) +void LuaInterface::setField(lua_State* L, const char* index, const std::string& val) { lua_pushstring(L, index); lua_pushstring(L, val.c_str()); pushTable(L); } -void LuaScriptInterface::setFieldBool(lua_State* L, const char* index, bool val) +void LuaInterface::setFieldBool(lua_State* L, const char* index, bool val) { lua_pushstring(L, index); lua_pushboolean(L, val); pushTable(L); } -void LuaScriptInterface::setFieldFloat(lua_State* L, const char* index, double val) +void LuaInterface::setFieldFloat(lua_State* L, const char* index, double val) { lua_pushstring(L, index); lua_pushnumber(L, val); pushTable(L); } -void LuaScriptInterface::createTable(lua_State* L, const char* index) +void LuaInterface::createTable(lua_State* L, const char* index) { lua_pushstring(L, index); lua_newtable(L); } -void LuaScriptInterface::createTable(lua_State* L, const char* index, int32_t narr, int32_t nrec) +void LuaInterface::createTable(lua_State* L, const char* index, int32_t narr, int32_t nrec) { lua_pushstring(L, index); lua_createtable(L, narr, nrec); } -void LuaScriptInterface::createTable(lua_State* L, int32_t index) +void LuaInterface::createTable(lua_State* L, int32_t index) { lua_pushnumber(L, index); lua_newtable(L); } -void LuaScriptInterface::createTable(lua_State* L, int32_t index, int32_t narr, int32_t nrec) +void LuaInterface::createTable(lua_State* L, int32_t index, int32_t narr, int32_t nrec) { lua_pushnumber(L, index); lua_createtable(L, narr, nrec); } -void LuaScriptInterface::pushTable(lua_State* L) +void LuaInterface::pushTable(lua_State* L) { lua_settable(L, -3); } -int64_t LuaScriptInterface::getField(lua_State* L, const char* key) +int64_t LuaInterface::getField(lua_State* L, const char* key) { lua_pushstring(L, key); lua_gettable(L, -2); // get table[key] @@ -1232,7 +1332,7 @@ int64_t LuaScriptInterface::getField(lua_State* L, const char* key) return result; } -uint64_t LuaScriptInterface::getFieldUnsigned(lua_State* L, const char* key) +uint64_t LuaInterface::getFieldUnsigned(lua_State* L, const char* key) { lua_pushstring(L, key); lua_gettable(L, -2); // get table[key] @@ -1242,17 +1342,17 @@ uint64_t LuaScriptInterface::getFieldUnsigned(lua_State* L, const char* key) return result; } -bool LuaScriptInterface::getFieldBool(lua_State* L, const char* key) +bool LuaInterface::getFieldBool(lua_State* L, const char* key) { lua_pushstring(L, key); lua_gettable(L, -2); // get table[key] - bool result = lua_toboolean(L, -1); + bool result = (lua_toboolean(L, -1) != 0); lua_pop(L, 1); // remove number and key return result; } -std::string LuaScriptInterface::getFieldString(lua_State* L, const char* key) +std::string LuaInterface::getFieldString(lua_State* L, const char* key) { lua_pushstring(L, key); lua_gettable(L, -2); // get table[key] @@ -1262,7 +1362,7 @@ std::string LuaScriptInterface::getFieldString(lua_State* L, const char* key) return result; } -std::string LuaScriptInterface::getGlobalString(lua_State* L, const std::string& _identifier, const std::string& _default/* = ""*/) +std::string LuaInterface::getGlobalString(lua_State* L, const std::string& _identifier, const std::string& _default/* = ""*/) { lua_getglobal(L, _identifier.c_str()); if(!lua_isstring(L, -1)) @@ -1278,26 +1378,26 @@ std::string LuaScriptInterface::getGlobalString(lua_State* L, const std::string& return ret; } -bool LuaScriptInterface::getGlobalBool(lua_State* L, const std::string& _identifier, bool _default/* = false*/) +bool LuaInterface::getGlobalBool(lua_State* L, const std::string& _identifier, bool _default/* = false*/) { lua_getglobal(L, _identifier.c_str()); if(!lua_isboolean(L, -1)) { lua_pop(L, 1); - return booleanString(LuaScriptInterface::getGlobalString(L, _identifier, _default ? "yes" : "no")); + return booleanString(LuaInterface::getGlobalString(L, _identifier, _default ? "yes" : "no")); } - bool val = lua_toboolean(L, -1); + bool val = (lua_toboolean(L, -1) != 0); lua_pop(L, 1); return val; } -int32_t LuaScriptInterface::getGlobalNumber(lua_State* L, const std::string& _identifier, const int32_t _default/* = 0*/) +int64_t LuaInterface::getGlobalNumber(lua_State* L, const std::string& _identifier, const int64_t _default/* = 0*/) { - return (int32_t)LuaScriptInterface::getGlobalDouble(L, _identifier, _default); + return (int64_t)LuaInterface::getGlobalDouble(L, _identifier, _default); } -double LuaScriptInterface::getGlobalDouble(lua_State* L, const std::string& _identifier, const double _default/* = 0*/) +double LuaInterface::getGlobalDouble(lua_State* L, const std::string& _identifier, const double _default/* = 0*/) { lua_getglobal(L, _identifier.c_str()); if(!lua_isnumber(L, -1)) @@ -1311,13 +1411,13 @@ double LuaScriptInterface::getGlobalDouble(lua_State* L, const std::string& _ide return val; } -void LuaScriptInterface::getValue(const std::string& key, lua_State* L, lua_State* _L) +void LuaInterface::getValue(const std::string& key, lua_State* L, lua_State* _L) { lua_getglobal(L, key.c_str()); moveValue(L, _L); } -void LuaScriptInterface::moveValue(lua_State* from, lua_State* to) +void LuaInterface::moveValue(lua_State* from, lua_State* to) { switch(lua_type(from, -1)) { @@ -1364,1083 +1464,1214 @@ void LuaScriptInterface::moveValue(lua_State* from, lua_State* to) lua_pop(from, 1); // Pop the value we just read } -void LuaScriptInterface::registerFunctions() +void LuaInterface::registerFunctions() { //example(...) //lua_register(L, "name", C_function); //getCreatureHealth(cid) - lua_register(m_luaState, "getCreatureHealth", LuaScriptInterface::luaGetCreatureHealth); + lua_register(m_luaState, "getCreatureHealth", LuaInterface::luaGetCreatureHealth); - //getCreatureMaxHealth(cid) - lua_register(m_luaState, "getCreatureMaxHealth", LuaScriptInterface::luaGetCreatureMaxHealth); + //getCreatureMaxHealth(cid[, ignoreModifiers = false]) + lua_register(m_luaState, "getCreatureMaxHealth", LuaInterface::luaGetCreatureMaxHealth); //getCreatureMana(cid) - lua_register(m_luaState, "getCreatureMana", LuaScriptInterface::luaGetCreatureMana); + lua_register(m_luaState, "getCreatureMana", LuaInterface::luaGetCreatureMana); - //getCreatureMaxMana(cid) - lua_register(m_luaState, "getCreatureMaxMana", LuaScriptInterface::luaGetCreatureMaxMana); + //getCreatureMaxMana(cid[, ignoreModifiers = false]) + lua_register(m_luaState, "getCreatureMaxMana", LuaInterface::luaGetCreatureMaxMana); //getCreatureHideHealth(cid) - lua_register(m_luaState, "getCreatureHideHealth", LuaScriptInterface::luaGetCreatureHideHealth); + lua_register(m_luaState, "getCreatureHideHealth", LuaInterface::luaGetCreatureHideHealth); //doCreatureSetHideHealth(cid, hide) - lua_register(m_luaState, "doCreatureSetHideHealth", LuaScriptInterface::luaDoCreatureSetHideHealth); + lua_register(m_luaState, "doCreatureSetHideHealth", LuaInterface::luaDoCreatureSetHideHealth); //getCreatureSpeakType(cid) - lua_register(m_luaState, "getCreatureSpeakType", LuaScriptInterface::luaGetCreatureSpeakType); + lua_register(m_luaState, "getCreatureSpeakType", LuaInterface::luaGetCreatureSpeakType); //doCreatureSetSpeakType(cid, type) - lua_register(m_luaState, "doCreatureSetSpeakType", LuaScriptInterface::luaDoCreatureSetSpeakType); + lua_register(m_luaState, "doCreatureSetSpeakType", LuaInterface::luaDoCreatureSetSpeakType); //getCreatureLookDirection(cid) - lua_register(m_luaState, "getCreatureLookDirection", LuaScriptInterface::luaGetCreatureLookDirection); + lua_register(m_luaState, "getCreatureLookDirection", LuaInterface::luaGetCreatureLookDirection); //getPlayerLevel(cid) - lua_register(m_luaState, "getPlayerLevel", LuaScriptInterface::luaGetPlayerLevel); + lua_register(m_luaState, "getPlayerLevel", LuaInterface::luaGetPlayerLevel); //getPlayerExperience(cid) - lua_register(m_luaState, "getPlayerExperience", LuaScriptInterface::luaGetPlayerExperience); + lua_register(m_luaState, "getPlayerExperience", LuaInterface::luaGetPlayerExperience); - //getPlayerMagLevel(cid[, ignoreBuffs = false]) - lua_register(m_luaState, "getPlayerMagLevel", LuaScriptInterface::luaGetPlayerMagLevel); + //getPlayerMagLevel(cid[, ignoreModifiers = false]) + lua_register(m_luaState, "getPlayerMagLevel", LuaInterface::luaGetPlayerMagLevel); //getPlayerSpentMana(cid) - lua_register(m_luaState, "getPlayerSpentMana", LuaScriptInterface::luaGetPlayerSpentMana); + lua_register(m_luaState, "getPlayerSpentMana", LuaInterface::luaGetPlayerSpentMana); //getPlayerFood(cid) - lua_register(m_luaState, "getPlayerFood", LuaScriptInterface::luaGetPlayerFood); + lua_register(m_luaState, "getPlayerFood", LuaInterface::luaGetPlayerFood); //getPlayerAccess(cid) - lua_register(m_luaState, "getPlayerAccess", LuaScriptInterface::luaGetPlayerAccess); + lua_register(m_luaState, "getPlayerAccess", LuaInterface::luaGetPlayerAccess); //getPlayerGhostAccess(cid) - lua_register(m_luaState, "getPlayerGhostAccess", LuaScriptInterface::luaGetPlayerGhostAccess); + lua_register(m_luaState, "getPlayerGhostAccess", LuaInterface::luaGetPlayerGhostAccess); - //getPlayerSkillLevel(cid, skillid) - lua_register(m_luaState, "getPlayerSkillLevel", LuaScriptInterface::luaGetPlayerSkillLevel); + //getPlayerSkillLevel(cid, skill[, ignoreModifiers = false]) + lua_register(m_luaState, "getPlayerSkillLevel", LuaInterface::luaGetPlayerSkillLevel); - //getPlayerSkillTries(cid, skillid) - lua_register(m_luaState, "getPlayerSkillTries", LuaScriptInterface::luaGetPlayerSkillTries); + //getPlayerSkillTries(cid, skill) + lua_register(m_luaState, "getPlayerSkillTries", LuaInterface::luaGetPlayerSkillTries); //getPlayerTown(cid) - lua_register(m_luaState, "getPlayerTown", LuaScriptInterface::luaGetPlayerTown); + lua_register(m_luaState, "getPlayerTown", LuaInterface::luaGetPlayerTown); //getPlayerVocation(cid) - lua_register(m_luaState, "getPlayerVocation", LuaScriptInterface::luaGetPlayerVocation); + lua_register(m_luaState, "getPlayerVocation", LuaInterface::luaGetPlayerVocation); //getPlayerIp(cid) - lua_register(m_luaState, "getPlayerIp", LuaScriptInterface::luaGetPlayerIp); + lua_register(m_luaState, "getPlayerIp", LuaInterface::luaGetPlayerIp); //getPlayerRequiredMana(cid, magicLevel) - lua_register(m_luaState, "getPlayerRequiredMana", LuaScriptInterface::luaGetPlayerRequiredMana); + lua_register(m_luaState, "getPlayerRequiredMana", LuaInterface::luaGetPlayerRequiredMana); //getPlayerRequiredSkillTries(cid, skillId, skillLevel) - lua_register(m_luaState, "getPlayerRequiredSkillTries", LuaScriptInterface::luaGetPlayerRequiredSkillTries); + lua_register(m_luaState, "getPlayerRequiredSkillTries", LuaInterface::luaGetPlayerRequiredSkillTries); //getPlayerItemCount(cid, itemid[, subType = -1]) - lua_register(m_luaState, "getPlayerItemCount", LuaScriptInterface::luaGetPlayerItemCount); + lua_register(m_luaState, "getPlayerItemCount", LuaInterface::luaGetPlayerItemCount); //getPlayerMoney(cid) - lua_register(m_luaState, "getPlayerMoney", LuaScriptInterface::luaGetPlayerMoney); + lua_register(m_luaState, "getPlayerMoney", LuaInterface::luaGetPlayerMoney); - //getPlayerSoul(cid) - lua_register(m_luaState, "getPlayerSoul", LuaScriptInterface::luaGetPlayerSoul); + //getPlayerSoul(cid[, ignoreModifiers = false]) + lua_register(m_luaState, "getPlayerSoul", LuaInterface::luaGetPlayerSoul); //getPlayerFreeCap(cid) - lua_register(m_luaState, "getPlayerFreeCap", LuaScriptInterface::luaGetPlayerFreeCap); + lua_register(m_luaState, "getPlayerFreeCap", LuaInterface::luaGetPlayerFreeCap); //getPlayerLight(cid) - lua_register(m_luaState, "getPlayerLight", LuaScriptInterface::luaGetPlayerLight); + lua_register(m_luaState, "getPlayerLight", LuaInterface::luaGetPlayerLight); //getPlayerSlotItem(cid, slot) - lua_register(m_luaState, "getPlayerSlotItem", LuaScriptInterface::luaGetPlayerSlotItem); + lua_register(m_luaState, "getPlayerSlotItem", LuaInterface::luaGetPlayerSlotItem); //getPlayerWeapon(cid[, ignoreAmmo = false]) - lua_register(m_luaState, "getPlayerWeapon", LuaScriptInterface::luaGetPlayerWeapon); + lua_register(m_luaState, "getPlayerWeapon", LuaInterface::luaGetPlayerWeapon); //getPlayerItemById(cid, deepSearch, itemId[, subType = -1]) - lua_register(m_luaState, "getPlayerItemById", LuaScriptInterface::luaGetPlayerItemById); + lua_register(m_luaState, "getPlayerItemById", LuaInterface::luaGetPlayerItemById); //getPlayerDepotItems(cid, depotid) - lua_register(m_luaState, "getPlayerDepotItems", LuaScriptInterface::luaGetPlayerDepotItems); + lua_register(m_luaState, "getPlayerDepotItems", LuaInterface::luaGetPlayerDepotItems); //getPlayerGuildId(cid) - lua_register(m_luaState, "getPlayerGuildId", LuaScriptInterface::luaGetPlayerGuildId); + lua_register(m_luaState, "getPlayerGuildId", LuaInterface::luaGetPlayerGuildId); //getPlayerGuildName(cid) - lua_register(m_luaState, "getPlayerGuildName", LuaScriptInterface::luaGetPlayerGuildName); + lua_register(m_luaState, "getPlayerGuildName", LuaInterface::luaGetPlayerGuildName); //getPlayerGuildRankId(cid) - lua_register(m_luaState, "getPlayerGuildRankId", LuaScriptInterface::luaGetPlayerGuildRankId); + lua_register(m_luaState, "getPlayerGuildRankId", LuaInterface::luaGetPlayerGuildRankId); //getPlayerGuildRank(cid) - lua_register(m_luaState, "getPlayerGuildRank", LuaScriptInterface::luaGetPlayerGuildRank); + lua_register(m_luaState, "getPlayerGuildRank", LuaInterface::luaGetPlayerGuildRank); //getPlayerGuildNick(cid) - lua_register(m_luaState, "getPlayerGuildNick", LuaScriptInterface::luaGetPlayerGuildNick); + lua_register(m_luaState, "getPlayerGuildNick", LuaInterface::luaGetPlayerGuildNick); //getPlayerGuildLevel(cid) - lua_register(m_luaState, "getPlayerGuildLevel", LuaScriptInterface::luaGetPlayerGuildLevel); + lua_register(m_luaState, "getPlayerGuildLevel", LuaInterface::luaGetPlayerGuildLevel); //getPlayerGUID(cid) - lua_register(m_luaState, "getPlayerGUID", LuaScriptInterface::luaGetPlayerGUID); + lua_register(m_luaState, "getPlayerGUID", LuaInterface::luaGetPlayerGUID); //getPlayerNameDescription(cid) - lua_register(m_luaState, "getPlayerNameDescription", LuaScriptInterface::luaGetPlayerNameDescription); + lua_register(m_luaState, "getPlayerNameDescription", LuaInterface::luaGetPlayerNameDescription); //doPlayerSetNameDescription(cid, desc) - lua_register(m_luaState, "doPlayerSetNameDescription", LuaScriptInterface::luaDoPlayerSetNameDescription); + lua_register(m_luaState, "doPlayerSetNameDescription", LuaInterface::luaDoPlayerSetNameDescription); //getPlayerSpecialDescription(cid) - lua_register(m_luaState, "getPlayerSpecialDescription", LuaScriptInterface::luaGetPlayerSpecialDescription); + lua_register(m_luaState, "getPlayerSpecialDescription", LuaInterface::luaGetPlayerSpecialDescription); //doPlayerSetSpecialDescription(cid, desc) - lua_register(m_luaState, "doPlayerSetSpecialDescription", LuaScriptInterface::luaDoPlayerSetSpecialDescription); + lua_register(m_luaState, "doPlayerSetSpecialDescription", LuaInterface::luaDoPlayerSetSpecialDescription); //getPlayerAccountId(cid) - lua_register(m_luaState, "getPlayerAccountId", LuaScriptInterface::luaGetPlayerAccountId); + lua_register(m_luaState, "getPlayerAccountId", LuaInterface::luaGetPlayerAccountId); //getPlayerAccount(cid) - lua_register(m_luaState, "getPlayerAccount", LuaScriptInterface::luaGetPlayerAccount); + lua_register(m_luaState, "getPlayerAccount", LuaInterface::luaGetPlayerAccount); //getPlayerFlagValue(cid, flag) - lua_register(m_luaState, "getPlayerFlagValue", LuaScriptInterface::luaGetPlayerFlagValue); + lua_register(m_luaState, "getPlayerFlagValue", LuaInterface::luaGetPlayerFlagValue); //getPlayerCustomFlagValue(cid, flag) - lua_register(m_luaState, "getPlayerCustomFlagValue", LuaScriptInterface::luaGetPlayerCustomFlagValue); + lua_register(m_luaState, "getPlayerCustomFlagValue", LuaInterface::luaGetPlayerCustomFlagValue); //getPlayerPromotionLevel(cid) - lua_register(m_luaState, "getPlayerPromotionLevel", LuaScriptInterface::luaGetPlayerPromotionLevel); + lua_register(m_luaState, "getPlayerPromotionLevel", LuaInterface::luaGetPlayerPromotionLevel); //doPlayerSetPromotionLevel(cid, level) - lua_register(m_luaState, "doPlayerSetPromotionLevel", LuaScriptInterface::luaDoPlayerSetPromotionLevel); + lua_register(m_luaState, "doPlayerSetPromotionLevel", LuaInterface::luaDoPlayerSetPromotionLevel); //getPlayerGroupId(cid) - lua_register(m_luaState, "getPlayerGroupId", LuaScriptInterface::luaGetPlayerGroupId); + lua_register(m_luaState, "getPlayerGroupId", LuaInterface::luaGetPlayerGroupId); //doPlayerSetGroupId(cid, newGroupId) - lua_register(m_luaState, "doPlayerSetGroupId", LuaScriptInterface::luaDoPlayerSetGroupId); + lua_register(m_luaState, "doPlayerSetGroupId", LuaInterface::luaDoPlayerSetGroupId); //doPlayerSendOutfitWindow(cid) - lua_register(m_luaState, "doPlayerSendOutfitWindow", LuaScriptInterface::luaDoPlayerSendOutfitWindow); + lua_register(m_luaState, "doPlayerSendOutfitWindow", LuaInterface::luaDoPlayerSendOutfitWindow); //doPlayerLearnInstantSpell(cid, name) - lua_register(m_luaState, "doPlayerLearnInstantSpell", LuaScriptInterface::luaDoPlayerLearnInstantSpell); + lua_register(m_luaState, "doPlayerLearnInstantSpell", LuaInterface::luaDoPlayerLearnInstantSpell); //doPlayerUnlearnInstantSpell(cid, name) - lua_register(m_luaState, "doPlayerUnlearnInstantSpell", LuaScriptInterface::luaDoPlayerUnlearnInstantSpell); + lua_register(m_luaState, "doPlayerUnlearnInstantSpell", LuaInterface::luaDoPlayerUnlearnInstantSpell); //getPlayerLearnedInstantSpell(cid, name) - lua_register(m_luaState, "getPlayerLearnedInstantSpell", LuaScriptInterface::luaGetPlayerLearnedInstantSpell); + lua_register(m_luaState, "getPlayerLearnedInstantSpell", LuaInterface::luaGetPlayerLearnedInstantSpell); //getPlayerInstantSpellCount(cid) - lua_register(m_luaState, "getPlayerInstantSpellCount", LuaScriptInterface::luaGetPlayerInstantSpellCount); + lua_register(m_luaState, "getPlayerInstantSpellCount", LuaInterface::luaGetPlayerInstantSpellCount); //getPlayerInstantSpellInfo(cid, index) - lua_register(m_luaState, "getPlayerInstantSpellInfo", LuaScriptInterface::luaGetPlayerInstantSpellInfo); + lua_register(m_luaState, "getPlayerInstantSpellInfo", LuaInterface::luaGetPlayerInstantSpellInfo); //getInstantSpellInfo(cid, name) - lua_register(m_luaState, "getInstantSpellInfo", LuaScriptInterface::luaGetInstantSpellInfo); + lua_register(m_luaState, "getInstantSpellInfo", LuaInterface::luaGetInstantSpellInfo); + + //getCreatureStorageList(cid) + lua_register(m_luaState, "getCreatureStorageList", LuaInterface::luaGetCreatureStorageList); //getCreatureStorage(uid, key) - lua_register(m_luaState, "getCreatureStorage", LuaScriptInterface::luaGetCreatureStorage); + lua_register(m_luaState, "getCreatureStorage", LuaInterface::luaGetCreatureStorage); //doCreatureSetStorage(uid, key, value) - lua_register(m_luaState, "doCreatureSetStorage", LuaScriptInterface::luaDoCreatureSetStorage); + lua_register(m_luaState, "doCreatureSetStorage", LuaInterface::luaDoCreatureSetStorage); + + //getStorageList() + lua_register(m_luaState, "getStorageList", LuaInterface::luaGetStorageList); //getStorage(key) - lua_register(m_luaState, "getStorage", LuaScriptInterface::luaGetStorage); + lua_register(m_luaState, "getStorage", LuaInterface::luaGetStorage); //doSetStorage(key, value) - lua_register(m_luaState, "doSetStorage", LuaScriptInterface::luaDoSetStorage); + lua_register(m_luaState, "doSetStorage", LuaInterface::luaDoSetStorage); //getChannelUsers(channelId) - lua_register(m_luaState, "getChannelUsers", LuaScriptInterface::luaGetChannelUsers); + lua_register(m_luaState, "getChannelUsers", LuaInterface::luaGetChannelUsers); //getPlayersOnline() - lua_register(m_luaState, "getPlayersOnline", LuaScriptInterface::luaGetPlayersOnline); + lua_register(m_luaState, "getPlayersOnline", LuaInterface::luaGetPlayersOnline); //getTileInfo(pos) - lua_register(m_luaState, "getTileInfo", LuaScriptInterface::luaGetTileInfo); + lua_register(m_luaState, "getTileInfo", LuaInterface::luaGetTileInfo); - //getThingFromPos(pos[, displayError = true]) - lua_register(m_luaState, "getThingFromPos", LuaScriptInterface::luaGetThingFromPos); + //getThingFromPosition(pos) + lua_register(m_luaState, "getThingFromPosition", LuaInterface::luaGetThingFromPosition); - //getThing(uid) - lua_register(m_luaState, "getThing", LuaScriptInterface::luaGetThing); + //getThing(uid[, recursive = RECURSE_FIRST]) + lua_register(m_luaState, "getThing", LuaInterface::luaGetThing); - //doTileQueryAdd(uid, pos[, flags[, displayError = true]]) - lua_register(m_luaState, "doTileQueryAdd", LuaScriptInterface::luaDoTileQueryAdd); + //doTileQueryAdd(uid, pos[, flags]) + lua_register(m_luaState, "doTileQueryAdd", LuaInterface::luaDoTileQueryAdd); //doItemRaidUnref(uid) - lua_register(m_luaState, "doItemRaidUnref", LuaScriptInterface::luaDoItemRaidUnref); + lua_register(m_luaState, "doItemRaidUnref", LuaInterface::luaDoItemRaidUnref); //getThingPosition(uid) - lua_register(m_luaState, "getThingPosition", LuaScriptInterface::luaGetThingPosition); + lua_register(m_luaState, "getThingPosition", LuaInterface::luaGetThingPosition); //getTileItemById(pos, itemId[, subType = -1]) - lua_register(m_luaState, "getTileItemById", LuaScriptInterface::luaGetTileItemById); + lua_register(m_luaState, "getTileItemById", LuaInterface::luaGetTileItemById); //getTileItemByType(pos, type) - lua_register(m_luaState, "getTileItemByType", LuaScriptInterface::luaGetTileItemByType); + lua_register(m_luaState, "getTileItemByType", LuaInterface::luaGetTileItemByType); //getTileThingByPos(pos) - lua_register(m_luaState, "getTileThingByPos", LuaScriptInterface::luaGetTileThingByPos); + lua_register(m_luaState, "getTileThingByPos", LuaInterface::luaGetTileThingByPos); //getTopCreature(pos) - lua_register(m_luaState, "getTopCreature", LuaScriptInterface::luaGetTopCreature); + lua_register(m_luaState, "getTopCreature", LuaInterface::luaGetTopCreature); - //doRemoveItem(uid[, count]) - lua_register(m_luaState, "doRemoveItem", LuaScriptInterface::luaDoRemoveItem); + //doRemoveItem(uid[, count = -1]) + lua_register(m_luaState, "doRemoveItem", LuaInterface::luaDoRemoveItem); //doPlayerFeed(cid, food) - lua_register(m_luaState, "doPlayerFeed", LuaScriptInterface::luaDoFeedPlayer); + lua_register(m_luaState, "doPlayerFeed", LuaInterface::luaDoPlayerFeed); //doPlayerSendCancel(cid, text) - lua_register(m_luaState, "doPlayerSendCancel", LuaScriptInterface::luaDoPlayerSendCancel); + lua_register(m_luaState, "doPlayerSendCancel", LuaInterface::luaDoPlayerSendCancel); //doPlayerSendDefaultCancel(cid, ReturnValue) - lua_register(m_luaState, "doPlayerSendDefaultCancel", LuaScriptInterface::luaDoSendDefaultCancel); + lua_register(m_luaState, "doPlayerSendDefaultCancel", LuaInterface::luaDoSendDefaultCancel); //getSearchString(fromPosition, toPosition[, fromIsCreature = false[, toIsCreature = false]]) - lua_register(m_luaState, "getSearchString", LuaScriptInterface::luaGetSearchString); + lua_register(m_luaState, "getSearchString", LuaInterface::luaGetSearchString); //getClosestFreeTile(cid, targetpos[, extended = false[, ignoreHouse = true]]) - lua_register(m_luaState, "getClosestFreeTile", LuaScriptInterface::luaGetClosestFreeTile); + lua_register(m_luaState, "getClosestFreeTile", LuaInterface::luaGetClosestFreeTile); - //doTeleportThing(cid, newpos[, pushmove]) - lua_register(m_luaState, "doTeleportThing", LuaScriptInterface::luaDoTeleportThing); + //doTeleportThing(cid, destination[, pushmove = true[, fullTeleport = true]]) + lua_register(m_luaState, "doTeleportThing", LuaInterface::luaDoTeleportThing); + + //doItemSetDestination(uid, destination) + lua_register(m_luaState, "doItemSetDestination", LuaInterface::luaDoItemSetDestination); //doTransformItem(uid, newId[, count/subType]) - lua_register(m_luaState, "doTransformItem", LuaScriptInterface::luaDoTransformItem); + lua_register(m_luaState, "doTransformItem", LuaInterface::luaDoTransformItem); //doCreatureSay(uid, text[, type = SPEAK_SAY[, ghost = false[, cid = 0[, pos]]]]) - lua_register(m_luaState, "doCreatureSay", LuaScriptInterface::luaDoCreatureSay); + lua_register(m_luaState, "doCreatureSay", LuaInterface::luaDoCreatureSay); + + //doSendCreatureSquare(cid, color[, player]) + lua_register(m_luaState, "doSendCreatureSquare", LuaInterface::luaDoSendCreatureSquare); //doSendMagicEffect(pos, type[, player]) - lua_register(m_luaState, "doSendMagicEffect", LuaScriptInterface::luaDoSendMagicEffect); + lua_register(m_luaState, "doSendMagicEffect", LuaInterface::luaDoSendMagicEffect); //doSendDistanceShoot(fromPos, toPos, type[, player]) - lua_register(m_luaState, "doSendDistanceShoot", LuaScriptInterface::luaDoSendDistanceShoot); + lua_register(m_luaState, "doSendDistanceShoot", LuaInterface::luaDoSendDistanceShoot); - //doSendAnimatedText(pos, text, color[, player]) - lua_register(m_luaState, "doSendAnimatedText", LuaScriptInterface::luaDoSendAnimatedText); - - //doPlayerAddSkillTry(cid, skillid, n[, useMultiplier]) - lua_register(m_luaState, "doPlayerAddSkillTry", LuaScriptInterface::luaDoPlayerAddSkillTry); + //doPlayerAddSkillTry(cid, skillid, n[, useMultiplier = true]) + lua_register(m_luaState, "doPlayerAddSkillTry", LuaInterface::luaDoPlayerAddSkillTry); //doCreatureAddHealth(cid, health[, hitEffect[, hitColor[, force]]]) - lua_register(m_luaState, "doCreatureAddHealth", LuaScriptInterface::luaDoCreatureAddHealth); + lua_register(m_luaState, "doCreatureAddHealth", LuaInterface::luaDoCreatureAddHealth); //doCreatureAddMana(cid, mana) - lua_register(m_luaState, "doCreatureAddMana", LuaScriptInterface::luaDoCreatureAddMana); + lua_register(m_luaState, "doCreatureAddMana", LuaInterface::luaDoCreatureAddMana); //setCreatureMaxHealth(cid, health) - lua_register(m_luaState, "setCreatureMaxHealth", LuaScriptInterface::luaSetCreatureMaxHealth); + lua_register(m_luaState, "setCreatureMaxHealth", LuaInterface::luaSetCreatureMaxHealth); //setCreatureMaxMana(cid, mana) - lua_register(m_luaState, "setCreatureMaxMana", LuaScriptInterface::luaSetCreatureMaxMana); + lua_register(m_luaState, "setCreatureMaxMana", LuaInterface::luaSetCreatureMaxMana); //doPlayerSetMaxCapacity(cid, cap) - lua_register(m_luaState, "doPlayerSetMaxCapacity", LuaScriptInterface::luaDoPlayerSetMaxCapacity); + lua_register(m_luaState, "doPlayerSetMaxCapacity", LuaInterface::luaDoPlayerSetMaxCapacity); - //doPlayerAddSpentMana(cid, amount[, useMultiplier]) - lua_register(m_luaState, "doPlayerAddSpentMana", LuaScriptInterface::luaDoPlayerAddSpentMana); + //doPlayerAddSpentMana(cid, amount[, useMultiplier = true]) + lua_register(m_luaState, "doPlayerAddSpentMana", LuaInterface::luaDoPlayerAddSpentMana); - //doPlayerAddSoul(cid, soul) - lua_register(m_luaState, "doPlayerAddSoul", LuaScriptInterface::luaDoPlayerAddSoul); + //doPlayerAddSoul(cid, amount) + lua_register(m_luaState, "doPlayerAddSoul", LuaInterface::luaDoPlayerAddSoul); - //doPlayerAddItem(cid, itemid[, count/subtype[, canDropOnMap]]) - //doPlayerAddItem(cid, itemid[, count[, canDropOnMap[, subtype]]]) + //doPlayerAddItem(cid, itemid[, count/subtype = 1[, canDropOnMap = true[, slot = 0]]]) + //doPlayerAddItem(cid, itemid[, count = 1[, canDropOnMap = true[, subtype = 1[, slot = 0]]]]) //Returns uid of the created item - lua_register(m_luaState, "doPlayerAddItem", LuaScriptInterface::luaDoPlayerAddItem); + lua_register(m_luaState, "doPlayerAddItem", LuaInterface::luaDoPlayerAddItem); + + //doPlayerAddItemEx(cid, uid[, canDropOnMap = false[, slot = 0]]) + lua_register(m_luaState, "doPlayerAddItemEx", LuaInterface::luaDoPlayerAddItemEx); - //doPlayerAddItemEx(cid, uid[, canDropOnMap = FALSE]) - lua_register(m_luaState, "doPlayerAddItemEx", LuaScriptInterface::luaDoPlayerAddItemEx); + //doPlayerSendTextMessage(cid, MessageClasses, message[, value[, color[, position]]]) + lua_register(m_luaState, "doPlayerSendTextMessage", LuaInterface::luaDoPlayerSendTextMessage); - //doPlayerSendTextMessage(cid, MessageClasses, message) - lua_register(m_luaState, "doPlayerSendTextMessage", LuaScriptInterface::luaDoPlayerSendTextMessage); + //doPlayerSendChannelMessage(cid, author, message, MessageClasses, channel) + lua_register(m_luaState, "doPlayerSendChannelMessage", LuaInterface::luaDoPlayerSendChannelMessage); - //doPlayerSendChannelMessage(cid, author, message, SpeakClasses, channel) - lua_register(m_luaState, "doPlayerSendChannelMessage", LuaScriptInterface::luaDoPlayerSendChannelMessage); + //doCreatureChannelSay(cid, targetId, MessageClasses, message, channel[, time]) + lua_register(m_luaState, "doCreatureChannelSay", LuaInterface::luaDoCreatureChannelSay); - //doPlayerSendToChannel(cid, targetId, SpeakClasses, message, channel[, time]) - lua_register(m_luaState, "doPlayerSendToChannel", LuaScriptInterface::luaDoPlayerSendToChannel); + //doPlayerOpenChannel(cid, channelId) + lua_register(m_luaState, "doPlayerOpenChannel", LuaInterface::luaDoPlayerOpenChannel); + + //doPlayerSendChannels(cid[, list]) + lua_register(m_luaState, "doPlayerSendChannels", LuaInterface::luaDoPlayerSendChannels); //doPlayerAddMoney(cid, money) - lua_register(m_luaState, "doPlayerAddMoney", LuaScriptInterface::luaDoPlayerAddMoney); + lua_register(m_luaState, "doPlayerAddMoney", LuaInterface::luaDoPlayerAddMoney); //doPlayerRemoveMoney(cid, money) - lua_register(m_luaState, "doPlayerRemoveMoney", LuaScriptInterface::luaDoPlayerRemoveMoney); + lua_register(m_luaState, "doPlayerRemoveMoney", LuaInterface::luaDoPlayerRemoveMoney); //doPlayerTransferMoneyTo(cid, target, money) - lua_register(m_luaState, "doPlayerTransferMoneyTo", LuaScriptInterface::luaDoPlayerTransferMoneyTo); + lua_register(m_luaState, "doPlayerTransferMoneyTo", LuaInterface::luaDoPlayerTransferMoneyTo); - //doShowTextDialog(cid, itemid, text) - lua_register(m_luaState, "doShowTextDialog", LuaScriptInterface::luaDoShowTextDialog); + //doShowTextDialog(cid, itemid[, (text/canWrite)[, (canWrite/length)[, length]]]) + lua_register(m_luaState, "doShowTextDialog", LuaInterface::luaDoShowTextDialog); //doDecayItem(uid) - lua_register(m_luaState, "doDecayItem", LuaScriptInterface::luaDoDecayItem); + lua_register(m_luaState, "doDecayItem", LuaInterface::luaDoDecayItem); //doCreateItem(itemid[, type/count], pos) //Returns uid of the created item, only works on tiles. - lua_register(m_luaState, "doCreateItem", LuaScriptInterface::luaDoCreateItem); + lua_register(m_luaState, "doCreateItem", LuaInterface::luaDoCreateItem); //doCreateItemEx(itemid[, count/subType = -1]) - lua_register(m_luaState, "doCreateItemEx", LuaScriptInterface::luaDoCreateItemEx); + lua_register(m_luaState, "doCreateItemEx", LuaInterface::luaDoCreateItemEx); //doTileAddItemEx(pos, uid) - lua_register(m_luaState, "doTileAddItemEx", LuaScriptInterface::luaDoTileAddItemEx); + lua_register(m_luaState, "doTileAddItemEx", LuaInterface::luaDoTileAddItemEx); //doAddContainerItemEx(uid, virtuid) - lua_register(m_luaState, "doAddContainerItemEx", LuaScriptInterface::luaDoAddContainerItemEx); + lua_register(m_luaState, "doAddContainerItemEx", LuaInterface::luaDoAddContainerItemEx); - //doRelocate(pos, posTo[, creatures = true]) - //Moves all moveable objects from pos to posTo - lua_register(m_luaState, "doRelocate", LuaScriptInterface::luaDoRelocate); + //doRelocate(pos, posTo[, creatures = true[, unmovable = true]]) + //Moves all movable objects from pos to posTo + lua_register(m_luaState, "doRelocate", LuaInterface::luaDoRelocate); //doCleanTile(pos[, forceMapLoaded = false]) - lua_register(m_luaState, "doCleanTile", LuaScriptInterface::luaDoCleanTile); + lua_register(m_luaState, "doCleanTile", LuaInterface::luaDoCleanTile); - //doCreateTeleport(itemid, topos, createpos) - lua_register(m_luaState, "doCreateTeleport", LuaScriptInterface::luaDoCreateTeleport); + //doCreateTeleport(itemId, destination, position) + lua_register(m_luaState, "doCreateTeleport", LuaInterface::luaDoCreateTeleport); - //doCreateMonster(name, pos[, displayError = true]) - lua_register(m_luaState, "doCreateMonster", LuaScriptInterface::luaDoCreateMonster); + //doCreateMonster(name, pos[, extend = false[, force = false]]) + lua_register(m_luaState, "doCreateMonster", LuaInterface::luaDoCreateMonster); - //doCreateNpc(name, pos[, displayError = true]) - lua_register(m_luaState, "doCreateNpc", LuaScriptInterface::luaDoCreateNpc); + //doCreateNpc(name, pos) + lua_register(m_luaState, "doCreateNpc", LuaInterface::luaDoCreateNpc); //doSummonMonster(cid, name) - lua_register(m_luaState, "doSummonMonster", LuaScriptInterface::luaDoSummonMonster); + lua_register(m_luaState, "doSummonMonster", LuaInterface::luaDoSummonMonster); //doConvinceCreature(cid, target) - lua_register(m_luaState, "doConvinceCreature", LuaScriptInterface::luaDoConvinceCreature); + lua_register(m_luaState, "doConvinceCreature", LuaInterface::luaDoConvinceCreature); //getMonsterTargetList(cid) - lua_register(m_luaState, "getMonsterTargetList", LuaScriptInterface::luaGetMonsterTargetList); + lua_register(m_luaState, "getMonsterTargetList", LuaInterface::luaGetMonsterTargetList); //getMonsterFriendList(cid) - lua_register(m_luaState, "getMonsterFriendList", LuaScriptInterface::luaGetMonsterFriendList); + lua_register(m_luaState, "getMonsterFriendList", LuaInterface::luaGetMonsterFriendList); //doMonsterSetTarget(cid, target) - lua_register(m_luaState, "doMonsterSetTarget", LuaScriptInterface::luaDoMonsterSetTarget); + lua_register(m_luaState, "doMonsterSetTarget", LuaInterface::luaDoMonsterSetTarget); //doMonsterChangeTarget(cid) - lua_register(m_luaState, "doMonsterChangeTarget", LuaScriptInterface::luaDoMonsterChangeTarget); + lua_register(m_luaState, "doMonsterChangeTarget", LuaInterface::luaDoMonsterChangeTarget); //getMonsterInfo(name) - lua_register(m_luaState, "getMonsterInfo", LuaScriptInterface::luaGetMonsterInfo); + lua_register(m_luaState, "getMonsterInfo", LuaInterface::luaGetMonsterInfo); //doAddCondition(cid, condition) - lua_register(m_luaState, "doAddCondition", LuaScriptInterface::luaDoAddCondition); + lua_register(m_luaState, "doAddCondition", LuaInterface::luaDoAddCondition); - //doRemoveCondition(cid, type[, subId]) - lua_register(m_luaState, "doRemoveCondition", LuaScriptInterface::luaDoRemoveCondition); + //doRemoveCondition(cid, type[, subId = 0]) + lua_register(m_luaState, "doRemoveCondition", LuaInterface::luaDoRemoveCondition); //doRemoveConditions(cid[, onlyPersistent]) - lua_register(m_luaState, "doRemoveConditions", LuaScriptInterface::luaDoRemoveConditions); + lua_register(m_luaState, "doRemoveConditions", LuaInterface::luaDoRemoveConditions); //doRemoveCreature(cid[, forceLogout = true]) - lua_register(m_luaState, "doRemoveCreature", LuaScriptInterface::luaDoRemoveCreature); + lua_register(m_luaState, "doRemoveCreature", LuaInterface::luaDoRemoveCreature); + + //doMoveCreature(cid, direction[, flag = FLAG_NOLIMIT]) + lua_register(m_luaState, "doMoveCreature", LuaInterface::luaDoMoveCreature); - //doMoveCreature(cid, direction) - lua_register(m_luaState, "doMoveCreature", LuaScriptInterface::luaDoMoveCreature); + //doSteerCreature(cid, position[, maxNodes]) + lua_register(m_luaState, "doSteerCreature", LuaInterface::luaDoSteerCreature); //doPlayerSetPzLocked(cid, locked) - lua_register(m_luaState, "doPlayerSetPzLocked", LuaScriptInterface::luaDoPlayerSetPzLocked); + lua_register(m_luaState, "doPlayerSetPzLocked", LuaInterface::luaDoPlayerSetPzLocked); //doPlayerSetTown(cid, townid) - lua_register(m_luaState, "doPlayerSetTown", LuaScriptInterface::luaDoPlayerSetTown); + lua_register(m_luaState, "doPlayerSetTown", LuaInterface::luaDoPlayerSetTown); //doPlayerSetVocation(cid,voc) - lua_register(m_luaState, "doPlayerSetVocation", LuaScriptInterface::luaDoPlayerSetVocation); + lua_register(m_luaState, "doPlayerSetVocation", LuaInterface::luaDoPlayerSetVocation); - //doPlayerRemoveItem(cid, itemid[, count[, subType]]) - lua_register(m_luaState, "doPlayerRemoveItem", LuaScriptInterface::luaDoPlayerRemoveItem); + //doPlayerRemoveItem(cid, itemid[, count[, subType = -1[, ignoreEquipped = false]]]) + lua_register(m_luaState, "doPlayerRemoveItem", LuaInterface::luaDoPlayerRemoveItem); //doPlayerAddExperience(cid, amount) - lua_register(m_luaState, "doPlayerAddExperience", LuaScriptInterface::luaDoPlayerAddExperience); + lua_register(m_luaState, "doPlayerAddExperience", LuaInterface::luaDoPlayerAddExperience); //doPlayerSetGuildId(cid, id) - lua_register(m_luaState, "doPlayerSetGuildId", LuaScriptInterface::luaDoPlayerSetGuildId); + lua_register(m_luaState, "doPlayerSetGuildId", LuaInterface::luaDoPlayerSetGuildId); //doPlayerSetGuildLevel(cid, level[, rank]) - lua_register(m_luaState, "doPlayerSetGuildLevel", LuaScriptInterface::luaDoPlayerSetGuildLevel); + lua_register(m_luaState, "doPlayerSetGuildLevel", LuaInterface::luaDoPlayerSetGuildLevel); //doPlayerSetGuildNick(cid, nick) - lua_register(m_luaState, "doPlayerSetGuildNick", LuaScriptInterface::luaDoPlayerSetGuildNick); + lua_register(m_luaState, "doPlayerSetGuildNick", LuaInterface::luaDoPlayerSetGuildNick); //doPlayerAddOutfit(cid, looktype, addon) - lua_register(m_luaState, "doPlayerAddOutfit", LuaScriptInterface::luaDoPlayerAddOutfit); + lua_register(m_luaState, "doPlayerAddOutfit", LuaInterface::luaDoPlayerAddOutfit); //doPlayerRemoveOutfit(cid, looktype[, addon = 0]) - lua_register(m_luaState, "doPlayerRemoveOutfit", LuaScriptInterface::luaDoPlayerRemoveOutfit); + lua_register(m_luaState, "doPlayerRemoveOutfit", LuaInterface::luaDoPlayerRemoveOutfit); //doPlayerAddOutfitId(cid, outfitId, addon) - lua_register(m_luaState, "doPlayerAddOutfitId", LuaScriptInterface::luaDoPlayerAddOutfitId); + lua_register(m_luaState, "doPlayerAddOutfitId", LuaInterface::luaDoPlayerAddOutfitId); //doPlayerRemoveOutfitId(cid, outfitId[, addon = 0]) - lua_register(m_luaState, "doPlayerRemoveOutfitId", LuaScriptInterface::luaDoPlayerRemoveOutfitId); + lua_register(m_luaState, "doPlayerRemoveOutfitId", LuaInterface::luaDoPlayerRemoveOutfitId); //canPlayerWearOutfit(cid, looktype[, addon = 0]) - lua_register(m_luaState, "canPlayerWearOutfit", LuaScriptInterface::luaCanPlayerWearOutfit); + lua_register(m_luaState, "canPlayerWearOutfit", LuaInterface::luaCanPlayerWearOutfit); //canPlayerWearOutfitId(cid, outfitId[, addon = 0]) - lua_register(m_luaState, "canPlayerWearOutfitId", LuaScriptInterface::luaCanPlayerWearOutfitId); + lua_register(m_luaState, "canPlayerWearOutfitId", LuaInterface::luaCanPlayerWearOutfitId); - //doSetCreatureLight(cid, lightLevel, lightColor, time) - lua_register(m_luaState, "doSetCreatureLight", LuaScriptInterface::luaDoSetCreatureLight); + //hasCreatureCondition(cid, condition[, subId = 0[, conditionId = (both)]]) + lua_register(m_luaState, "hasCreatureCondition", LuaInterface::luaHasCreatureCondition); - //getCreatureCondition(cid, condition[, subId]) - lua_register(m_luaState, "getCreatureCondition", LuaScriptInterface::luaGetCreatureCondition); + //getCreatureConditionInfo(cid, condition[, subId = 0[, conditionId = CONDITIONID_COMBAT]]) + lua_register(m_luaState, "getCreatureConditionInfo", LuaInterface::luaGetCreatureConditionInfo); //doCreatureSetDropLoot(cid, doDrop) - lua_register(m_luaState, "doCreatureSetDropLoot", LuaScriptInterface::luaDoCreatureSetDropLoot); + lua_register(m_luaState, "doCreatureSetDropLoot", LuaInterface::luaDoCreatureSetDropLoot); //getPlayerLossPercent(cid, lossType) - lua_register(m_luaState, "getPlayerLossPercent", LuaScriptInterface::luaGetPlayerLossPercent); + lua_register(m_luaState, "getPlayerLossPercent", LuaInterface::luaGetPlayerLossPercent); //doPlayerSetLossPercent(cid, lossType, newPercent) - lua_register(m_luaState, "doPlayerSetLossPercent", LuaScriptInterface::luaDoPlayerSetLossPercent); + lua_register(m_luaState, "doPlayerSetLossPercent", LuaInterface::luaDoPlayerSetLossPercent); //doPlayerSetLossSkill(cid, doLose) - lua_register(m_luaState, "doPlayerSetLossSkill", LuaScriptInterface::luaDoPlayerSetLossSkill); + lua_register(m_luaState, "doPlayerSetLossSkill", LuaInterface::luaDoPlayerSetLossSkill); //getPlayerLossSkill(cid) - lua_register(m_luaState, "getPlayerLossSkill", LuaScriptInterface::luaGetPlayerLossSkill); + lua_register(m_luaState, "getPlayerLossSkill", LuaInterface::luaGetPlayerLossSkill); //doPlayerSwitchSaving(cid) - lua_register(m_luaState, "doPlayerSwitchSaving", LuaScriptInterface::luaDoPlayerSwitchSaving); + lua_register(m_luaState, "doPlayerSwitchSaving", LuaInterface::luaDoPlayerSwitchSaving); //doPlayerSave(cid[, shallow = false]) - lua_register(m_luaState, "doPlayerSave", LuaScriptInterface::luaDoPlayerSave); + lua_register(m_luaState, "doPlayerSave", LuaInterface::luaDoPlayerSave); //isPlayerPzLocked(cid) - lua_register(m_luaState, "isPlayerPzLocked", LuaScriptInterface::luaIsPlayerPzLocked); + lua_register(m_luaState, "isPlayerPzLocked", LuaInterface::luaIsPlayerPzLocked); //isPlayerSaving(cid) - lua_register(m_luaState, "isPlayerSaving", LuaScriptInterface::luaIsPlayerSaving); + lua_register(m_luaState, "isPlayerSaving", LuaInterface::luaIsPlayerSaving); + + //isPlayerProtected(cid) + lua_register(m_luaState, "isPlayerProtected", LuaInterface::luaIsPlayerProtected); //isCreature(cid) - lua_register(m_luaState, "isCreature", LuaScriptInterface::luaIsCreature); - - //isContainer(uid) - lua_register(m_luaState, "isContainer", LuaScriptInterface::luaIsContainer); + lua_register(m_luaState, "isCreature", LuaInterface::luaIsCreature); //isMovable(uid) - lua_register(m_luaState, "isMovable", LuaScriptInterface::luaIsMovable); + lua_register(m_luaState, "isMovable", LuaInterface::luaIsMovable); //getCreatureByName(name) - lua_register(m_luaState, "getCreatureByName", LuaScriptInterface::luaGetCreatureByName); + lua_register(m_luaState, "getCreatureByName", LuaInterface::luaGetCreatureByName); //getPlayerByGUID(guid) - lua_register(m_luaState, "getPlayerByGUID", LuaScriptInterface::luaGetPlayerByGUID); + lua_register(m_luaState, "getPlayerByGUID", LuaInterface::luaGetPlayerByGUID); //getPlayerByNameWildcard(name~[, ret = false]) - lua_register(m_luaState, "getPlayerByNameWildcard", LuaScriptInterface::luaGetPlayerByNameWildcard); + lua_register(m_luaState, "getPlayerByNameWildcard", LuaInterface::luaGetPlayerByNameWildcard); //getPlayerGUIDByName(name[, multiworld = false]) - lua_register(m_luaState, "getPlayerGUIDByName", LuaScriptInterface::luaGetPlayerGUIDByName); + lua_register(m_luaState, "getPlayerGUIDByName", LuaInterface::luaGetPlayerGUIDByName); - //getPlayerNameByGUID(guid[, multiworld = false[, displayError = true]]) - lua_register(m_luaState, "getPlayerNameByGUID", LuaScriptInterface::luaGetPlayerNameByGUID); + //getPlayerNameByGUID(guid[, multiworld = false]) + lua_register(m_luaState, "getPlayerNameByGUID", LuaInterface::luaGetPlayerNameByGUID); - //registerCreatureEvent(uid, eventName) - lua_register(m_luaState, "registerCreatureEvent", LuaScriptInterface::luaRegisterCreatureEvent); + //doPlayerChangeName(guid, oldName, newName) + lua_register(m_luaState, "doPlayerChangeName", LuaInterface::luaDoPlayerChangeName); + + //registerCreatureEvent(uid, name) + lua_register(m_luaState, "registerCreatureEvent", LuaInterface::luaRegisterCreatureEvent); + + //unregisterCreatureEvent(uid, name) + lua_register(m_luaState, "unregisterCreatureEvent", LuaInterface::luaUnregisterCreatureEvent); + + //unregisterCreatureEventType(uid, type) + lua_register(m_luaState, "unregisterCreatureEventType", LuaInterface::luaUnregisterCreatureEventType); //getContainerSize(uid) - lua_register(m_luaState, "getContainerSize", LuaScriptInterface::luaGetContainerSize); + lua_register(m_luaState, "getContainerSize", LuaInterface::luaGetContainerSize); //getContainerCap(uid) - lua_register(m_luaState, "getContainerCap", LuaScriptInterface::luaGetContainerCap); + lua_register(m_luaState, "getContainerCap", LuaInterface::luaGetContainerCap); //getContainerItem(uid, slot) - lua_register(m_luaState, "getContainerItem", LuaScriptInterface::luaGetContainerItem); + lua_register(m_luaState, "getContainerItem", LuaInterface::luaGetContainerItem); - //doAddContainerItem(uid, itemid[, count/subType]) - lua_register(m_luaState, "doAddContainerItem", LuaScriptInterface::luaDoAddContainerItem); + //doAddContainerItem(uid, itemid[, count/subType = 1]) + lua_register(m_luaState, "doAddContainerItem", LuaInterface::luaDoAddContainerItem); - //getHouseInfo(houseId) - lua_register(m_luaState, "getHouseInfo", LuaScriptInterface::luaGetHouseInfo); + //getHouseInfo(houseId[, full = true]) + lua_register(m_luaState, "getHouseInfo", LuaInterface::luaGetHouseInfo); //getHouseAccessList(houseid, listId) - lua_register(m_luaState, "getHouseAccessList", LuaScriptInterface::luaGetHouseAccessList); + lua_register(m_luaState, "getHouseAccessList", LuaInterface::luaGetHouseAccessList); //getHouseByPlayerGUID(playerGUID) - lua_register(m_luaState, "getHouseByPlayerGUID", LuaScriptInterface::luaGetHouseByPlayerGUID); + lua_register(m_luaState, "getHouseByPlayerGUID", LuaInterface::luaGetHouseByPlayerGUID); - //getHouseFromPos(pos) - lua_register(m_luaState, "getHouseFromPos", LuaScriptInterface::luaGetHouseFromPos); + //getHouseFromPosition(pos) + lua_register(m_luaState, "getHouseFromPosition", LuaInterface::luaGetHouseFromPosition); //setHouseAccessList(houseid, listid, listtext) - lua_register(m_luaState, "setHouseAccessList", LuaScriptInterface::luaSetHouseAccessList); + lua_register(m_luaState, "setHouseAccessList", LuaInterface::luaSetHouseAccessList); - //setHouseOwner(houseId, owner[, clean]) - lua_register(m_luaState, "setHouseOwner", LuaScriptInterface::luaSetHouseOwner); + //setHouseOwner(houseId, owner[, clean = true]) + lua_register(m_luaState, "setHouseOwner", LuaInterface::luaSetHouseOwner); //getWorldType() - lua_register(m_luaState, "getWorldType", LuaScriptInterface::luaGetWorldType); + lua_register(m_luaState, "getWorldType", LuaInterface::luaGetWorldType); //setWorldType(type) - lua_register(m_luaState, "setWorldType", LuaScriptInterface::luaSetWorldType); + lua_register(m_luaState, "setWorldType", LuaInterface::luaSetWorldType); //getWorldTime() - lua_register(m_luaState, "getWorldTime", LuaScriptInterface::luaGetWorldTime); + lua_register(m_luaState, "getWorldTime", LuaInterface::luaGetWorldTime); //getWorldLight() - lua_register(m_luaState, "getWorldLight", LuaScriptInterface::luaGetWorldLight); + lua_register(m_luaState, "getWorldLight", LuaInterface::luaGetWorldLight); //getWorldCreatures(type) //0 players, 1 monsters, 2 npcs, 3 all - lua_register(m_luaState, "getWorldCreatures", LuaScriptInterface::luaGetWorldCreatures); + lua_register(m_luaState, "getWorldCreatures", LuaInterface::luaGetWorldCreatures); //getWorldUpTime() - lua_register(m_luaState, "getWorldUpTime", LuaScriptInterface::luaGetWorldUpTime); + lua_register(m_luaState, "getWorldUpTime", LuaInterface::luaGetWorldUpTime); //getGuildId(guildName) - lua_register(m_luaState, "getGuildId", LuaScriptInterface::luaGetGuildId); + lua_register(m_luaState, "getGuildId", LuaInterface::luaGetGuildId); //getGuildMotd(guildId) - lua_register(m_luaState, "getGuildMotd", LuaScriptInterface::luaGetGuildMotd); + lua_register(m_luaState, "getGuildMotd", LuaInterface::luaGetGuildMotd); //getPlayerSex(cid[, full = false]) - lua_register(m_luaState, "getPlayerSex", LuaScriptInterface::luaGetPlayerSex); + lua_register(m_luaState, "getPlayerSex", LuaInterface::luaGetPlayerSex); //doPlayerSetSex(cid, newSex) - lua_register(m_luaState, "doPlayerSetSex", LuaScriptInterface::luaDoPlayerSetSex); + lua_register(m_luaState, "doPlayerSetSex", LuaInterface::luaDoPlayerSetSex); //createCombatArea({area}[, {extArea}]) - lua_register(m_luaState, "createCombatArea", LuaScriptInterface::luaCreateCombatArea); + lua_register(m_luaState, "createCombatArea", LuaInterface::luaCreateCombatArea); - //createConditionObject(type[, ticks[, buff[, subId]]]) - lua_register(m_luaState, "createConditionObject", LuaScriptInterface::luaCreateConditionObject); + //createConditionObject(type[, ticks = 0[, buff = false[, subId = 0[, conditionId = CONDITIONID_COMBAT]]]]) + lua_register(m_luaState, "createConditionObject", LuaInterface::luaCreateConditionObject); //setCombatArea(combat, area) - lua_register(m_luaState, "setCombatArea", LuaScriptInterface::luaSetCombatArea); + lua_register(m_luaState, "setCombatArea", LuaInterface::luaSetCombatArea); //setCombatCondition(combat, condition) - lua_register(m_luaState, "setCombatCondition", LuaScriptInterface::luaSetCombatCondition); + lua_register(m_luaState, "setCombatCondition", LuaInterface::luaSetCombatCondition); //setCombatParam(combat, key, value) - lua_register(m_luaState, "setCombatParam", LuaScriptInterface::luaSetCombatParam); + lua_register(m_luaState, "setCombatParam", LuaInterface::luaSetCombatParam); //setConditionParam(condition, key, value) - lua_register(m_luaState, "setConditionParam", LuaScriptInterface::luaSetConditionParam); + lua_register(m_luaState, "setConditionParam", LuaInterface::luaSetConditionParam); //addDamageCondition(condition, rounds, time, value) - lua_register(m_luaState, "addDamageCondition", LuaScriptInterface::luaAddDamageCondition); + lua_register(m_luaState, "addDamageCondition", LuaInterface::luaAddDamageCondition); //addOutfitCondition(condition, outfit) - lua_register(m_luaState, "addOutfitCondition", LuaScriptInterface::luaAddOutfitCondition); + lua_register(m_luaState, "addOutfitCondition", LuaInterface::luaAddOutfitCondition); //setCombatCallBack(combat, key, function_name) - lua_register(m_luaState, "setCombatCallback", LuaScriptInterface::luaSetCombatCallBack); + lua_register(m_luaState, "setCombatCallback", LuaInterface::luaSetCombatCallBack); //setCombatFormula(combat, type, mina, minb, maxa, maxb[, minl, maxl[, minm, maxm[, minc[, maxc]]]]) - lua_register(m_luaState, "setCombatFormula", LuaScriptInterface::luaSetCombatFormula); + lua_register(m_luaState, "setCombatFormula", LuaInterface::luaSetCombatFormula); //setConditionFormula(combat, mina, minb, maxa, maxb) - lua_register(m_luaState, "setConditionFormula", LuaScriptInterface::luaSetConditionFormula); + lua_register(m_luaState, "setConditionFormula", LuaInterface::luaSetConditionFormula); //doCombat(cid, combat, param) - lua_register(m_luaState, "doCombat", LuaScriptInterface::luaDoCombat); + lua_register(m_luaState, "doCombat", LuaInterface::luaDoCombat); //createCombatObject() - lua_register(m_luaState, "createCombatObject", LuaScriptInterface::luaCreateCombatObject); + lua_register(m_luaState, "createCombatObject", LuaInterface::luaCreateCombatObject); //doCombatAreaHealth(cid, type, pos, area, min, max, effect) - lua_register(m_luaState, "doCombatAreaHealth", LuaScriptInterface::luaDoCombatAreaHealth); + lua_register(m_luaState, "doCombatAreaHealth", LuaInterface::luaDoCombatAreaHealth); //doTargetCombatHealth(cid, target, type, min, max, effect) - lua_register(m_luaState, "doTargetCombatHealth", LuaScriptInterface::luaDoTargetCombatHealth); + lua_register(m_luaState, "doTargetCombatHealth", LuaInterface::luaDoTargetCombatHealth); //doCombatAreaMana(cid, pos, area, min, max, effect) - lua_register(m_luaState, "doCombatAreaMana", LuaScriptInterface::luaDoCombatAreaMana); + lua_register(m_luaState, "doCombatAreaMana", LuaInterface::luaDoCombatAreaMana); //doTargetCombatMana(cid, target, min, max, effect) - lua_register(m_luaState, "doTargetCombatMana", LuaScriptInterface::luaDoTargetCombatMana); + lua_register(m_luaState, "doTargetCombatMana", LuaInterface::luaDoTargetCombatMana); //doCombatAreaCondition(cid, pos, area, condition, effect) - lua_register(m_luaState, "doCombatAreaCondition", LuaScriptInterface::luaDoCombatAreaCondition); + lua_register(m_luaState, "doCombatAreaCondition", LuaInterface::luaDoCombatAreaCondition); //doTargetCombatCondition(cid, target, condition, effect) - lua_register(m_luaState, "doTargetCombatCondition", LuaScriptInterface::luaDoTargetCombatCondition); + lua_register(m_luaState, "doTargetCombatCondition", LuaInterface::luaDoTargetCombatCondition); //doCombatAreaDispel(cid, pos, area, type, effect) - lua_register(m_luaState, "doCombatAreaDispel", LuaScriptInterface::luaDoCombatAreaDispel); + lua_register(m_luaState, "doCombatAreaDispel", LuaInterface::luaDoCombatAreaDispel); //doTargetCombatDispel(cid, target, type, effect) - lua_register(m_luaState, "doTargetCombatDispel", LuaScriptInterface::luaDoTargetCombatDispel); + lua_register(m_luaState, "doTargetCombatDispel", LuaInterface::luaDoTargetCombatDispel); //doChallengeCreature(cid, target) - lua_register(m_luaState, "doChallengeCreature", LuaScriptInterface::luaDoChallengeCreature); + lua_register(m_luaState, "doChallengeCreature", LuaInterface::luaDoChallengeCreature); //numberToVariant(number) - lua_register(m_luaState, "numberToVariant", LuaScriptInterface::luaNumberToVariant); + lua_register(m_luaState, "numberToVariant", LuaInterface::luaNumberToVariant); //stringToVariant(string) - lua_register(m_luaState, "stringToVariant", LuaScriptInterface::luaStringToVariant); + lua_register(m_luaState, "stringToVariant", LuaInterface::luaStringToVariant); //positionToVariant(pos) - lua_register(m_luaState, "positionToVariant", LuaScriptInterface::luaPositionToVariant); + lua_register(m_luaState, "positionToVariant", LuaInterface::luaPositionToVariant); //targetPositionToVariant(pos) - lua_register(m_luaState, "targetPositionToVariant", LuaScriptInterface::luaTargetPositionToVariant); + lua_register(m_luaState, "targetPositionToVariant", LuaInterface::luaTargetPositionToVariant); //variantToNumber(var) - lua_register(m_luaState, "variantToNumber", LuaScriptInterface::luaVariantToNumber); + lua_register(m_luaState, "variantToNumber", LuaInterface::luaVariantToNumber); //variantToString(var) - lua_register(m_luaState, "variantToString", LuaScriptInterface::luaVariantToString); + lua_register(m_luaState, "variantToString", LuaInterface::luaVariantToString); //variantToPosition(var) - lua_register(m_luaState, "variantToPosition", LuaScriptInterface::luaVariantToPosition); + lua_register(m_luaState, "variantToPosition", LuaInterface::luaVariantToPosition); //doChangeSpeed(cid, delta) - lua_register(m_luaState, "doChangeSpeed", LuaScriptInterface::luaDoChangeSpeed); + lua_register(m_luaState, "doChangeSpeed", LuaInterface::luaDoChangeSpeed); //doCreatureChangeOutfit(cid, outfit) - lua_register(m_luaState, "doCreatureChangeOutfit", LuaScriptInterface::luaDoCreatureChangeOutfit); + lua_register(m_luaState, "doCreatureChangeOutfit", LuaInterface::luaDoCreatureChangeOutfit); - //doSetMonsterOutfit(cid, name, time) - lua_register(m_luaState, "doSetMonsterOutfit", LuaScriptInterface::luaSetMonsterOutfit); + //doSetMonsterOutfit(cid, name[, time = -1]) + lua_register(m_luaState, "doSetMonsterOutfit", LuaInterface::luaSetMonsterOutfit); - //doSetItemOutfit(cid, item, time) - lua_register(m_luaState, "doSetItemOutfit", LuaScriptInterface::luaSetItemOutfit); + //doSetItemOutfit(cid, item[, time = -1]) + lua_register(m_luaState, "doSetItemOutfit", LuaInterface::luaSetItemOutfit); - //doSetCreatureOutfit(cid, outfit, time) - lua_register(m_luaState, "doSetCreatureOutfit", LuaScriptInterface::luaSetCreatureOutfit); + //doSetCreatureOutfit(cid, outfit[, time = -1]) + lua_register(m_luaState, "doSetCreatureOutfit", LuaInterface::luaSetCreatureOutfit); //getCreatureOutfit(cid) - lua_register(m_luaState, "getCreatureOutfit", LuaScriptInterface::luaGetCreatureOutfit); + lua_register(m_luaState, "getCreatureOutfit", LuaInterface::luaGetCreatureOutfit); //getCreatureLastPosition(cid) - lua_register(m_luaState, "getCreatureLastPosition", LuaScriptInterface::luaGetCreatureLastPosition); + lua_register(m_luaState, "getCreatureLastPosition", LuaInterface::luaGetCreatureLastPosition); //getCreatureName(cid) - lua_register(m_luaState, "getCreatureName", LuaScriptInterface::luaGetCreatureName); + lua_register(m_luaState, "getCreatureName", LuaInterface::luaGetCreatureName); //getCreatureSpeed(cid) - lua_register(m_luaState, "getCreatureSpeed", LuaScriptInterface::luaGetCreatureSpeed); + lua_register(m_luaState, "getCreatureSpeed", LuaInterface::luaGetCreatureSpeed); //getCreatureBaseSpeed(cid) - lua_register(m_luaState, "getCreatureBaseSpeed", LuaScriptInterface::luaGetCreatureBaseSpeed); + lua_register(m_luaState, "getCreatureBaseSpeed", LuaInterface::luaGetCreatureBaseSpeed); //getCreatureTarget(cid) - lua_register(m_luaState, "getCreatureTarget", LuaScriptInterface::luaGetCreatureTarget); + lua_register(m_luaState, "getCreatureTarget", LuaInterface::luaGetCreatureTarget); //isSightClear(fromPos, toPos, floorCheck) - lua_register(m_luaState, "isSightClear", LuaScriptInterface::luaIsSightClear); + lua_register(m_luaState, "isSightClear", LuaInterface::luaIsSightClear); //isInArray(array, value[, caseSensitive = false]) - lua_register(m_luaState, "isInArray", LuaScriptInterface::luaIsInArray); + lua_register(m_luaState, "isInArray", LuaInterface::luaIsInArray); //addEvent(callback, delay, ...) - lua_register(m_luaState, "addEvent", LuaScriptInterface::luaAddEvent); + lua_register(m_luaState, "addEvent", LuaInterface::luaAddEvent); //stopEvent(eventid) - lua_register(m_luaState, "stopEvent", LuaScriptInterface::luaStopEvent); + lua_register(m_luaState, "stopEvent", LuaInterface::luaStopEvent); //getPlayersByAccountId(accId) - lua_register(m_luaState, "getPlayersByAccountId", LuaScriptInterface::luaGetPlayersByAccountId); + lua_register(m_luaState, "getPlayersByAccountId", LuaInterface::luaGetPlayersByAccountId); //getAccountIdByName(name) - lua_register(m_luaState, "getAccountIdByName", LuaScriptInterface::luaGetAccountIdByName); + lua_register(m_luaState, "getAccountIdByName", LuaInterface::luaGetAccountIdByName); //getAccountByName(name) - lua_register(m_luaState, "getAccountByName", LuaScriptInterface::luaGetAccountByName); + lua_register(m_luaState, "getAccountByName", LuaInterface::luaGetAccountByName); //getAccountIdByAccount(accName) - lua_register(m_luaState, "getAccountIdByAccount", LuaScriptInterface::luaGetAccountIdByAccount); + lua_register(m_luaState, "getAccountIdByAccount", LuaInterface::luaGetAccountIdByAccount); //getAccountByAccountId(accId) - lua_register(m_luaState, "getAccountByAccountId", LuaScriptInterface::luaGetAccountByAccountId); + lua_register(m_luaState, "getAccountByAccountId", LuaInterface::luaGetAccountByAccountId); + + //getAccountFlagValue(name/id) + lua_register(m_luaState, "getAccountFlagValue", LuaInterface::luaGetAccountFlagValue); + + //getAccountCustomFlagValue(name/id) + lua_register(m_luaState, "getAccountCustomFlagValue", LuaInterface::luaGetAccountCustomFlagValue); //getIpByName(name) - lua_register(m_luaState, "getIpByName", LuaScriptInterface::luaGetIpByName); + lua_register(m_luaState, "getIpByName", LuaInterface::luaGetIpByName); //getPlayersByIp(ip[, mask = 0xFFFFFFFF]) - lua_register(m_luaState, "getPlayersByIp", LuaScriptInterface::luaGetPlayersByIp); + lua_register(m_luaState, "getPlayersByIp", LuaInterface::luaGetPlayersByIp); //doPlayerPopupFYI(cid, message) - lua_register(m_luaState, "doPlayerPopupFYI", LuaScriptInterface::luaDoPlayerPopupFYI); + lua_register(m_luaState, "doPlayerPopupFYI", LuaInterface::luaDoPlayerPopupFYI); //doPlayerSendTutorial(cid, id) - lua_register(m_luaState, "doPlayerSendTutorial", LuaScriptInterface::luaDoPlayerSendTutorial); + lua_register(m_luaState, "doPlayerSendTutorial", LuaInterface::luaDoPlayerSendTutorial); //doPlayerSendMailByName(name, item[, town[, actor]]) - lua_register(m_luaState, "doPlayerSendMailByName", LuaScriptInterface::luaDoPlayerSendMailByName); + lua_register(m_luaState, "doPlayerSendMailByName", LuaInterface::luaDoPlayerSendMailByName); //doPlayerAddMapMark(cid, pos, type[, description]) - lua_register(m_luaState, "doPlayerAddMapMark", LuaScriptInterface::luaDoPlayerAddMapMark); + lua_register(m_luaState, "doPlayerAddMapMark", LuaInterface::luaDoPlayerAddMapMark); //doPlayerAddPremiumDays(cid, days) - lua_register(m_luaState, "doPlayerAddPremiumDays", LuaScriptInterface::luaDoPlayerAddPremiumDays); + lua_register(m_luaState, "doPlayerAddPremiumDays", LuaInterface::luaDoPlayerAddPremiumDays); //getPlayerPremiumDays(cid) - lua_register(m_luaState, "getPlayerPremiumDays", LuaScriptInterface::luaGetPlayerPremiumDays); + lua_register(m_luaState, "getPlayerPremiumDays", LuaInterface::luaGetPlayerPremiumDays); //doCreatureSetLookDirection(cid, dir) - lua_register(m_luaState, "doCreatureSetLookDirection", LuaScriptInterface::luaDoCreatureSetLookDir); + lua_register(m_luaState, "doCreatureSetLookDirection", LuaInterface::luaDoCreatureSetLookDir); + + //getCreatureGuildEmblem(cid[, target]) + lua_register(m_luaState, "getCreatureGuildEmblem", LuaInterface::luaGetCreatureGuildEmblem); + + //doCreatureSetGuildEmblem(cid, emblem) + lua_register(m_luaState, "doCreatureSetGuildEmblem", LuaInterface::luaDoCreatureSetGuildEmblem); + + //getCreaturePartyShield(cid[, target]) + lua_register(m_luaState, "getCreaturePartyShield", LuaInterface::luaGetCreaturePartyShield); + + //doCreatureSetPartyShield(cid, shield) + lua_register(m_luaState, "doCreatureSetPartyShield", LuaInterface::luaDoCreatureSetPartyShield); //getCreatureSkullType(cid[, target]) - lua_register(m_luaState, "getCreatureSkullType", LuaScriptInterface::luaGetCreatureSkullType); + lua_register(m_luaState, "getCreatureSkullType", LuaInterface::luaGetCreatureSkullType); //doCreatureSetSkullType(cid, skull) - lua_register(m_luaState, "doCreatureSetSkullType", LuaScriptInterface::luaDoCreatureSetSkullType); + lua_register(m_luaState, "doCreatureSetSkullType", LuaInterface::luaDoCreatureSetSkullType); //getPlayerSkullEnd(cid) - lua_register(m_luaState, "getPlayerSkullEnd", LuaScriptInterface::luaGetPlayerSkullEnd); + lua_register(m_luaState, "getPlayerSkullEnd", LuaInterface::luaGetPlayerSkullEnd); //doPlayerSetSkullEnd(cid, time, type) - lua_register(m_luaState, "doPlayerSetSkullEnd", LuaScriptInterface::luaDoPlayerSetSkullEnd); - - //getPlayerBalance(cid) - lua_register(m_luaState, "getPlayerBalance", LuaScriptInterface::luaGetPlayerBalance); + lua_register(m_luaState, "doPlayerSetSkullEnd", LuaInterface::luaDoPlayerSetSkullEnd); //getPlayerBlessing(cid, blessing) - lua_register(m_luaState, "getPlayerBlessing", LuaScriptInterface::luaGetPlayerBlessing); + lua_register(m_luaState, "getPlayerBlessing", LuaInterface::luaGetPlayerBlessing); //doPlayerAddBlessing(cid, blessing) - lua_register(m_luaState, "doPlayerAddBlessing", LuaScriptInterface::luaDoPlayerAddBlessing); + lua_register(m_luaState, "doPlayerAddBlessing", LuaInterface::luaDoPlayerAddBlessing); + + //getPlayerPVPBlessing(cid) + lua_register(m_luaState, "getPlayerPVPBlessing", LuaInterface::luaGetPlayerPVPBlessing); + + //doPlayerSetPVPBlessing(cid[, value]) + lua_register(m_luaState, "doPlayerSetPVPBlessing", LuaInterface::luaDoPlayerSetPVPBlessing); //getPlayerStamina(cid) - lua_register(m_luaState, "getPlayerStamina", LuaScriptInterface::luaGetPlayerStamina); + lua_register(m_luaState, "getPlayerStamina", LuaInterface::luaGetPlayerStamina); //doPlayerSetStamina(cid, minutes) - lua_register(m_luaState, "doPlayerSetStamina", LuaScriptInterface::luaDoPlayerSetStamina); + lua_register(m_luaState, "doPlayerSetStamina", LuaInterface::luaDoPlayerSetStamina); - //doPlayerAddStamina(cid, minutes) - lua_register(m_luaState, "doPlayerAddStamina", LuaScriptInterface::luaDoPlayerAddStamina); + //getPlayerBalance(cid) + lua_register(m_luaState, "getPlayerBalance", LuaInterface::luaGetPlayerBalance); //doPlayerSetBalance(cid, balance) - lua_register(m_luaState, "doPlayerSetBalance", LuaScriptInterface::luaDoPlayerSetBalance); + lua_register(m_luaState, "doPlayerSetBalance", LuaInterface::luaDoPlayerSetBalance); //getCreatureNoMove(cid) - lua_register(m_luaState, "getCreatureNoMove", LuaScriptInterface::luaGetCreatureNoMove); + lua_register(m_luaState, "getCreatureNoMove", LuaInterface::luaGetCreatureNoMove); //doCreatureSetNoMove(cid, block) - lua_register(m_luaState, "doCreatureSetNoMove", LuaScriptInterface::luaDoCreatureSetNoMove); + lua_register(m_luaState, "doCreatureSetNoMove", LuaInterface::luaDoCreatureSetNoMove); //getPlayerIdleTime(cid) - lua_register(m_luaState, "getPlayerIdleTime", LuaScriptInterface::luaGetPlayerIdleTime); + lua_register(m_luaState, "getPlayerIdleTime", LuaInterface::luaGetPlayerIdleTime); //doPlayerSetIdleTime(cid, amount) - lua_register(m_luaState, "doPlayerSetIdleTime", LuaScriptInterface::luaDoPlayerSetIdleTime); + lua_register(m_luaState, "doPlayerSetIdleTime", LuaInterface::luaDoPlayerSetIdleTime); //getPlayerLastLoad(cid) - lua_register(m_luaState, "getPlayerLastLoad", LuaScriptInterface::luaGetPlayerLastLoad); + lua_register(m_luaState, "getPlayerLastLoad", LuaInterface::luaGetPlayerLastLoad); //getPlayerLastLogin(cid) - lua_register(m_luaState, "getPlayerLastLogin", LuaScriptInterface::luaGetPlayerLastLogin); + lua_register(m_luaState, "getPlayerLastLogin", LuaInterface::luaGetPlayerLastLogin); //getPlayerAccountManager(cid) - lua_register(m_luaState, "getPlayerAccountManager", LuaScriptInterface::luaGetPlayerAccountManager); + lua_register(m_luaState, "getPlayerAccountManager", LuaInterface::luaGetPlayerAccountManager); + + //getPlayerTradeState(cid) + lua_register(m_luaState, "getPlayerTradeState", LuaInterface::luaGetPlayerTradeState); + + //getPlayerModes(cid) + lua_register(m_luaState, "getPlayerModes", LuaInterface::luaGetPlayerModes); //getPlayerRates(cid) - lua_register(m_luaState, "getPlayerRates", LuaScriptInterface::luaGetPlayerRates); + lua_register(m_luaState, "getPlayerRates", LuaInterface::luaGetPlayerRates); //doPlayerSetRate(cid, type, value) - lua_register(m_luaState, "doPlayerSetRate", LuaScriptInterface::luaDoPlayerSetRate); + lua_register(m_luaState, "doPlayerSetRate", LuaInterface::luaDoPlayerSetRate); //getPlayerPartner(cid) - lua_register(m_luaState, "getPlayerPartner", LuaScriptInterface::luaGetPlayerPartner); + lua_register(m_luaState, "getPlayerPartner", LuaInterface::luaGetPlayerPartner); //doPlayerSetPartner(cid, guid) - lua_register(m_luaState, "doPlayerSetPartner", LuaScriptInterface::luaDoPlayerSetPartner); + lua_register(m_luaState, "doPlayerSetPartner", LuaInterface::luaDoPlayerSetPartner); + + //doPlayerFollowCreature(cid, target) + lua_register(m_luaState, "doPlayerFollowCreature", LuaInterface::luaDoPlayerFollowCreature); //getPlayerParty(cid) - lua_register(m_luaState, "getPlayerParty", LuaScriptInterface::luaGetPlayerParty); + lua_register(m_luaState, "getPlayerParty", LuaInterface::luaGetPlayerParty); //doPlayerJoinParty(cid, lid) - lua_register(m_luaState, "doPlayerJoinParty", LuaScriptInterface::luaDoPlayerJoinParty); + lua_register(m_luaState, "doPlayerJoinParty", LuaInterface::luaDoPlayerJoinParty); + + //doPlayerLeaveParty(cid[, forced = false]) + lua_register(m_luaState, "doPlayerLeaveParty", LuaInterface::luaDoPlayerLeaveParty); + + //doPlayerAddMount(cid, mountId) + lua_register(m_luaState, "doPlayerAddMount", LuaInterface::luaDoPlayerAddMount); + + //doPlayerRemoveMount(cid, mountId) + lua_register(m_luaState, "doPlayerRemoveMount", LuaInterface::luaDoPlayerRemoveMount); + + //canPlayerRideMount(cid, mountId) + lua_register(m_luaState, "canPlayerRideMount", LuaInterface::luaCanPlayerRideMount); + + //doPlayerSetMounted(cid, mounting[, force]) + lua_register(m_luaState, "doPlayerSetMounted", LuaInterface::luaDoPlayerSetMounted); + + //getMountInfo([mountId]) + lua_register(m_luaState, "getMountInfo", LuaInterface::luaGetMountInfo); //getPartyMembers(lid) - lua_register(m_luaState, "getPartyMembers", LuaScriptInterface::luaGetPartyMembers); + lua_register(m_luaState, "getPartyMembers", LuaInterface::luaGetPartyMembers); //getCreatureMaster(cid) - lua_register(m_luaState, "getCreatureMaster", LuaScriptInterface::luaGetCreatureMaster); + lua_register(m_luaState, "getCreatureMaster", LuaInterface::luaGetCreatureMaster); //getCreatureSummons(cid) - lua_register(m_luaState, "getCreatureSummons", LuaScriptInterface::luaGetCreatureSummons); + lua_register(m_luaState, "getCreatureSummons", LuaInterface::luaGetCreatureSummons); //getTownId(townName) - lua_register(m_luaState, "getTownId", LuaScriptInterface::luaGetTownId); + lua_register(m_luaState, "getTownId", LuaInterface::luaGetTownId); //getTownName(townId) - lua_register(m_luaState, "getTownName", LuaScriptInterface::luaGetTownName); + lua_register(m_luaState, "getTownName", LuaInterface::luaGetTownName); - //getTownTemplePosition(townId[, displayError]) - lua_register(m_luaState, "getTownTemplePosition", LuaScriptInterface::luaGetTownTemplePosition); + //getTownTemplePosition(townId) + lua_register(m_luaState, "getTownTemplePosition", LuaInterface::luaGetTownTemplePosition); //getTownHouses(townId) - lua_register(m_luaState, "getTownHouses", LuaScriptInterface::luaGetTownHouses); + lua_register(m_luaState, "getTownHouses", LuaInterface::luaGetTownHouses); //getSpectators(centerPos, rangex, rangey[, multifloor = false]) - lua_register(m_luaState, "getSpectators", LuaScriptInterface::luaGetSpectators); + lua_register(m_luaState, "getSpectators", LuaInterface::luaGetSpectators); //getVocationInfo(id) - lua_register(m_luaState, "getVocationInfo", LuaScriptInterface::luaGetVocationInfo); + lua_register(m_luaState, "getVocationInfo", LuaInterface::luaGetVocationInfo); - //getGroupInfo(id) - lua_register(m_luaState, "getGroupInfo", LuaScriptInterface::luaGetGroupInfo); + //getGroupInfo(id[, premium = false]) + lua_register(m_luaState, "getGroupInfo", LuaInterface::luaGetGroupInfo); + + //getVocationList() + lua_register(m_luaState, "getVocationList", LuaInterface::luaGetVocationList); + + //getGroupList() + lua_register(m_luaState, "getGroupList", LuaInterface::luaGetGroupList); + + //getChannelList() + lua_register(m_luaState, "getChannelList", LuaInterface::luaGetChannelList); + + //getTownList() + lua_register(m_luaState, "getTownList", LuaInterface::luaGetTownList); //getWaypointList() - lua_register(m_luaState, "getWaypointList", LuaScriptInterface::luaGetWaypointList); + lua_register(m_luaState, "getWaypointList", LuaInterface::luaGetWaypointList); //getTalkActionList() - lua_register(m_luaState, "getTalkActionList", LuaScriptInterface::luaGetTalkActionList); + lua_register(m_luaState, "getTalkActionList", LuaInterface::luaGetTalkActionList); //getExperienceStageList() - lua_register(m_luaState, "getExperienceStageList", LuaScriptInterface::luaGetExperienceStageList); + lua_register(m_luaState, "getExperienceStageList", LuaInterface::luaGetExperienceStageList); - //getItemIdByName(name[, displayError = true]) - lua_register(m_luaState, "getItemIdByName", LuaScriptInterface::luaGetItemIdByName); + //getItemIdByName(name) + lua_register(m_luaState, "getItemIdByName", LuaInterface::luaGetItemIdByName); //getItemInfo(itemid) - lua_register(m_luaState, "getItemInfo", LuaScriptInterface::luaGetItemInfo); + lua_register(m_luaState, "getItemInfo", LuaInterface::luaGetItemInfo); //getItemAttribute(uid, key) - lua_register(m_luaState, "getItemAttribute", LuaScriptInterface::luaGetItemAttribute); + lua_register(m_luaState, "getItemAttribute", LuaInterface::luaGetItemAttribute); //doItemSetAttribute(uid, key, value) - lua_register(m_luaState, "doItemSetAttribute", LuaScriptInterface::luaDoItemSetAttribute); + lua_register(m_luaState, "doItemSetAttribute", LuaInterface::luaDoItemSetAttribute); //doItemEraseAttribute(uid, key) - lua_register(m_luaState, "doItemEraseAttribute", LuaScriptInterface::luaDoItemEraseAttribute); + lua_register(m_luaState, "doItemEraseAttribute", LuaInterface::luaDoItemEraseAttribute); //getItemWeight(uid[, precise = true]) - lua_register(m_luaState, "getItemWeight", LuaScriptInterface::luaGetItemWeight); + lua_register(m_luaState, "getItemWeight", LuaInterface::luaGetItemWeight); + + //getItemParent(uid) + lua_register(m_luaState, "getItemParent", LuaInterface::luaGetItemParent); + + //hasItemProperty(uid, prop) + lua_register(m_luaState, "hasItemProperty", LuaInterface::luaHasItemProperty); - //hasItemProperty(uid) - lua_register(m_luaState, "hasItemProperty", LuaScriptInterface::luaHasItemProperty); - //hasPlayerClient(cid) - lua_register(m_luaState, "hasPlayerClient", LuaScriptInterface::luaHasPlayerClient); + lua_register(m_luaState, "hasPlayerClient", LuaInterface::luaHasPlayerClient); + + //hasMonsterRaid(cid) + lua_register(m_luaState, "hasMonsterRaid", LuaInterface::luaHasMonsterRaid); //isIpBanished(ip[, mask]) - lua_register(m_luaState, "isIpBanished", LuaScriptInterface::luaIsIpBanished); + lua_register(m_luaState, "isIpBanished", LuaInterface::luaIsIpBanished); //isPlayerBanished(name/guid, type) - lua_register(m_luaState, "isPlayerBanished", LuaScriptInterface::luaIsPlayerBanished); + lua_register(m_luaState, "isPlayerBanished", LuaInterface::luaIsPlayerBanished); //isAccountBanished(accountId[, playerId]) - lua_register(m_luaState, "isAccountBanished", LuaScriptInterface::luaIsAccountBanished); + lua_register(m_luaState, "isAccountBanished", LuaInterface::luaIsAccountBanished); //doAddIpBanishment(...) - lua_register(m_luaState, "doAddIpBanishment", LuaScriptInterface::luaDoAddIpBanishment); + lua_register(m_luaState, "doAddIpBanishment", LuaInterface::luaDoAddIpBanishment); //doAddPlayerBanishment(...) - lua_register(m_luaState, "doAddPlayerBanishment", LuaScriptInterface::luaDoAddPlayerBanishment); + lua_register(m_luaState, "doAddPlayerBanishment", LuaInterface::luaDoAddPlayerBanishment); //doAddAccountBanishment(...) - lua_register(m_luaState, "doAddAccountBanishment", LuaScriptInterface::luaDoAddAccountBanishment); + lua_register(m_luaState, "doAddAccountBanishment", LuaInterface::luaDoAddAccountBanishment); - //doAddNotation(...) - lua_register(m_luaState, "doAddNotation", LuaScriptInterface::luaDoAddNotation); + //doAddAccountWarnings(...) + lua_register(m_luaState, "doAddAccountWarnings", LuaInterface::luaDoAddAccountWarnings); - //doAddStatement(...) - lua_register(m_luaState, "doAddStatement", LuaScriptInterface::luaDoAddStatement); + //doAddNotation(...) + lua_register(m_luaState, "doAddNotation", LuaInterface::luaDoAddNotation); //doRemoveIpBanishment(ip[, mask]) - lua_register(m_luaState, "doRemoveIpBanishment", LuaScriptInterface::luaDoRemoveIpBanishment); + lua_register(m_luaState, "doRemoveIpBanishment", LuaInterface::luaDoRemoveIpBanishment); //doRemovePlayerBanishment(name/guid, type) - lua_register(m_luaState, "doRemovePlayerBanishment", LuaScriptInterface::luaDoRemovePlayerBanishment); + lua_register(m_luaState, "doRemovePlayerBanishment", LuaInterface::luaDoRemovePlayerBanishment); //doRemoveAccountBanishment(accountId[, playerId]) - lua_register(m_luaState, "doRemoveAccountBanishment", LuaScriptInterface::luaDoRemoveAccountBanishment); + lua_register(m_luaState, "doRemoveAccountBanishment", LuaInterface::luaDoRemoveAccountBanishment); + + //doRemoveAccountWarnings(accountId[, warnings]) + lua_register(m_luaState, "doRemoveAccountWarnings", LuaInterface::luaDoRemoveAccountWarnings); //doRemoveNotations(accountId[, playerId]) - lua_register(m_luaState, "doRemoveNotations", LuaScriptInterface::luaDoRemoveNotations); + lua_register(m_luaState, "doRemoveNotations", LuaInterface::luaDoRemoveNotations); - //doRemoveStatements(name/guid[, channelId]) - lua_register(m_luaState, "doRemoveStatements", LuaScriptInterface::luaDoRemoveStatements); + //getAccountWarnings(accountId) + lua_register(m_luaState, "getAccountWarnings", LuaInterface::luaGetAccountWarnings); //getNotationsCount(accountId[, playerId]) - lua_register(m_luaState, "getNotationsCount", LuaScriptInterface::luaGetNotationsCount); - - //getStatementsCount(name/guid[, channelId]) - lua_register(m_luaState, "getStatementsCount", LuaScriptInterface::luaGetStatementsCount); + lua_register(m_luaState, "getNotationsCount", LuaInterface::luaGetNotationsCount); //getBanData(value[, type[, param]]) - lua_register(m_luaState, "getBanData", LuaScriptInterface::luaGetBanData); - - //getBanReason(id) - lua_register(m_luaState, "getBanReason", LuaScriptInterface::luaGetBanReason); - - //getBanAction(id) - lua_register(m_luaState, "getBanAction", LuaScriptInterface::luaGetBanAction); + lua_register(m_luaState, "getBanData", LuaInterface::luaGetBanData); //getBanList(type[, value[, param]]) - lua_register(m_luaState, "getBanList", LuaScriptInterface::luaGetBanList); + lua_register(m_luaState, "getBanList", LuaInterface::luaGetBanList); //getExperienceStage(level) - lua_register(m_luaState, "getExperienceStage", LuaScriptInterface::luaGetExperienceStage); + lua_register(m_luaState, "getExperienceStage", LuaInterface::luaGetExperienceStage); //getDataDir() - lua_register(m_luaState, "getDataDir", LuaScriptInterface::luaGetDataDir); + lua_register(m_luaState, "getDataDir", LuaInterface::luaGetDataDir); //getLogsDir() - lua_register(m_luaState, "getLogsDir", LuaScriptInterface::luaGetLogsDir); + lua_register(m_luaState, "getLogsDir", LuaInterface::luaGetLogsDir); //getConfigFile() - lua_register(m_luaState, "getConfigFile", LuaScriptInterface::luaGetConfigFile); + lua_register(m_luaState, "getConfigFile", LuaInterface::luaGetConfigFile); //getConfigValue(key) - lua_register(m_luaState, "getConfigValue", LuaScriptInterface::luaGetConfigValue); + lua_register(m_luaState, "getConfigValue", LuaInterface::luaGetConfigValue); //getModList() - lua_register(m_luaState, "getModList", LuaScriptInterface::luaGetModList); + lua_register(m_luaState, "getModList", LuaInterface::luaGetModList); //getHighscoreString(skillId) - lua_register(m_luaState, "getHighscoreString", LuaScriptInterface::luaGetHighscoreString); + lua_register(m_luaState, "getHighscoreString", LuaInterface::luaGetHighscoreString); //getWaypointPosition(name) - lua_register(m_luaState, "getWaypointPosition", LuaScriptInterface::luaGetWaypointPosition); + lua_register(m_luaState, "getWaypointPosition", LuaInterface::luaGetWaypointPosition); //doWaypointAddTemporial(name, pos) - lua_register(m_luaState, "doWaypointAddTemporial", LuaScriptInterface::luaDoWaypointAddTemporial); + lua_register(m_luaState, "doWaypointAddTemporial", LuaInterface::luaDoWaypointAddTemporial); //getGameState() - lua_register(m_luaState, "getGameState", LuaScriptInterface::luaGetGameState); + lua_register(m_luaState, "getGameState", LuaInterface::luaGetGameState); //doSetGameState(id) - lua_register(m_luaState, "doSetGameState", LuaScriptInterface::luaDoSetGameState); + lua_register(m_luaState, "doSetGameState", LuaInterface::luaDoSetGameState); //doExecuteRaid(name) - lua_register(m_luaState, "doExecuteRaid", LuaScriptInterface::luaDoExecuteRaid); + lua_register(m_luaState, "doExecuteRaid", LuaInterface::luaDoExecuteRaid); - //doCreatureExecuteTalkAction(cid, text[, ignoreAccess[, channelId]]) - lua_register(m_luaState, "doCreatureExecuteTalkAction", LuaScriptInterface::luaDoCreatureExecuteTalkAction); + //doCreatureExecuteTalkAction(cid, text[, ignoreAccess = false[, channelId = CHANNEL_DEFAULT]]) + lua_register(m_luaState, "doCreatureExecuteTalkAction", LuaInterface::luaDoCreatureExecuteTalkAction); //doReloadInfo(id[, cid]) - lua_register(m_luaState, "doReloadInfo", LuaScriptInterface::luaDoReloadInfo); + lua_register(m_luaState, "doReloadInfo", LuaInterface::luaDoReloadInfo); + + //doSaveServer([flags = 13]) + lua_register(m_luaState, "doSaveServer", LuaInterface::luaDoSaveServer); - //doSaveServer() - lua_register(m_luaState, "doSaveServer", LuaScriptInterface::luaDoSaveServer); + //doSaveHouse({list}) + lua_register(m_luaState, "doSaveHouse", LuaInterface::luaDoSaveHouse); //doCleanHouse(houseId) - lua_register(m_luaState, "doCleanHouse", LuaScriptInterface::luaDoCleanHouse); + lua_register(m_luaState, "doCleanHouse", LuaInterface::luaDoCleanHouse); //doCleanMap() - lua_register(m_luaState, "doCleanMap", LuaScriptInterface::luaDoCleanMap); + lua_register(m_luaState, "doCleanMap", LuaInterface::luaDoCleanMap); //doRefreshMap() - lua_register(m_luaState, "doRefreshMap", LuaScriptInterface::luaDoRefreshMap); + lua_register(m_luaState, "doRefreshMap", LuaInterface::luaDoRefreshMap); + + //doPlayerSetWalkthrough(cid, uid, walkthrough) + lua_register(m_luaState, "doPlayerSetWalkthrough", LuaInterface::luaDoPlayerSetWalkthrough); + + //doGuildAddEnemy(guild, enemy, war, type) + lua_register(m_luaState, "doGuildAddEnemy", LuaInterface::luaDoGuildAddEnemy); + + //doGuildRemoveEnemy(guild, enemy) + lua_register(m_luaState, "doGuildRemoveEnemy", LuaInterface::luaDoGuildRemoveEnemy); //doUpdateHouseAuctions() - lua_register(m_luaState, "doUpdateHouseAuctions", LuaScriptInterface::luaDoUpdateHouseAuctions); + lua_register(m_luaState, "doUpdateHouseAuctions", LuaInterface::luaDoUpdateHouseAuctions); //loadmodlib(lib) - lua_register(m_luaState, "loadmodlib", LuaScriptInterface::luaL_loadmodlib); + lua_register(m_luaState, "loadmodlib", LuaInterface::luaL_loadmodlib); //domodlib(lib) - lua_register(m_luaState, "domodlib", LuaScriptInterface::luaL_domodlib); + lua_register(m_luaState, "domodlib", LuaInterface::luaL_domodlib); + + //dodirectory(dir[, recursively = false]) + lua_register(m_luaState, "dodirectory", LuaInterface::luaL_dodirectory); - //dodirectory(dir) - lua_register(m_luaState, "dodirectory", LuaScriptInterface::luaL_dodirectory); + //errors(var) + lua_register(m_luaState, "errors", LuaInterface::luaL_errors); + + //os table + luaL_register(m_luaState, "os", LuaInterface::luaSystemTable); //db table - luaL_register(m_luaState, "db", LuaScriptInterface::luaDatabaseTable); + luaL_register(m_luaState, "db", LuaInterface::luaDatabaseTable); //result table - luaL_register(m_luaState, "result", LuaScriptInterface::luaResultTable); + luaL_register(m_luaState, "result", LuaInterface::luaResultTable); //bit table - luaL_register(m_luaState, "bit", LuaScriptInterface::luaBitTable); + luaL_register(m_luaState, "bit", LuaInterface::luaBitTable); //std table - luaL_register(m_luaState, "std", LuaScriptInterface::luaStdTable); + luaL_register(m_luaState, "std", LuaInterface::luaStdTable); } -const luaL_Reg LuaScriptInterface::luaDatabaseTable[] = +const luaL_Reg LuaInterface::luaSystemTable[] = +{ + //os.mtime() + {"mtime", LuaInterface::luaSystemTime}, + + {NULL, NULL} +}; + +const luaL_Reg LuaInterface::luaDatabaseTable[] = { - //db.executeQuery(query) - {"executeQuery", LuaScriptInterface::luaDatabaseExecute}, + //db.query(query) + {"query", LuaInterface::luaDatabaseExecute}, //db.storeQuery(query) - {"storeQuery", LuaScriptInterface::luaDatabaseStoreQuery}, + {"storeQuery", LuaInterface::luaDatabaseStoreQuery}, //db.escapeString(str) - {"escapeString", LuaScriptInterface::luaDatabaseEscapeString}, + {"escapeString", LuaInterface::luaDatabaseEscapeString}, //db.escapeBlob(s, length) - {"escapeBlob", LuaScriptInterface::luaDatabaseEscapeBlob}, + {"escapeBlob", LuaInterface::luaDatabaseEscapeBlob}, //db.lastInsertId() - {"lastInsertId", LuaScriptInterface::luaDatabaseLastInsertId}, + {"lastInsertId", LuaInterface::luaDatabaseLastInsertId}, - //db.stringComparison() - {"stringComparison", LuaScriptInterface::luaDatabaseStringComparison}, + //db.stringComparer() + {"stringComparer", LuaInterface::luaDatabaseStringComparer}, //db.updateLimiter() - {"updateLimiter", LuaScriptInterface::luaDatabaseUpdateLimiter}, + {"updateLimiter", LuaInterface::luaDatabaseUpdateLimiter}, + + //db.connected() + {"connected", LuaInterface::luaDatabaseConnected}, + + //db.tableExists(name) + {"tableExists", LuaInterface::luaDatabaseTableExists}, - {NULL,NULL} + //db.transBegin() + {"transBegin", LuaInterface::luaDatabaseTransBegin}, + + //db.transRollback() + {"transRollback", LuaInterface::luaDatabaseTransRollback}, + + //db.transCommit() + {"transCommit", LuaInterface::luaDatabaseTransCommit}, + + {NULL, NULL} }; -const luaL_Reg LuaScriptInterface::luaResultTable[] = +const luaL_Reg LuaInterface::luaResultTable[] = { //result.getDataInt(resId, s) - {"getDataInt", LuaScriptInterface::luaResultGetDataInt}, + {"getDataInt", LuaInterface::luaResultGetDataInt}, //result.getDataLong(resId, s) - {"getDataLong", LuaScriptInterface::luaResultGetDataLong}, + {"getDataLong", LuaInterface::luaResultGetDataLong}, //result.getDataString(resId, s) - {"getDataString", LuaScriptInterface::luaResultGetDataString}, + {"getDataString", LuaInterface::luaResultGetDataString}, //result.getDataStream(resId, s, length) - {"getDataStream", LuaScriptInterface::luaResultGetDataStream}, + {"getDataStream", LuaInterface::luaResultGetDataStream}, //result.next(resId) - {"next", LuaScriptInterface::luaResultNext}, + {"next", LuaInterface::luaResultNext}, //result.free(resId) - {"free", LuaScriptInterface::luaResultFree}, + {"free", LuaInterface::luaResultFree}, - {NULL,NULL} + {NULL, NULL} }; -const luaL_Reg LuaScriptInterface::luaBitTable[] = -{ - //{"cast", LuaScriptInterface::luaBitCast}, - {"bnot", LuaScriptInterface::luaBitNot}, - {"band", LuaScriptInterface::luaBitAnd}, - {"bor", LuaScriptInterface::luaBitOr}, - {"bxor", LuaScriptInterface::luaBitXor}, - {"lshift", LuaScriptInterface::luaBitLeftShift}, - {"rshift", LuaScriptInterface::luaBitRightShift}, - //{"arshift", LuaScriptInterface::luaBitArithmeticalRightShift}, - - //{"ucast", LuaScriptInterface::luaBitUCast}, - {"ubnot", LuaScriptInterface::luaBitUNot}, - {"uband", LuaScriptInterface::luaBitUAnd}, - {"ubor", LuaScriptInterface::luaBitUOr}, - {"ubxor", LuaScriptInterface::luaBitUXor}, - {"ulshift", LuaScriptInterface::luaBitULeftShift}, - {"urshift", LuaScriptInterface::luaBitURightShift}, - //{"uarshift", LuaScriptInterface::luaBitUArithmeticalRightShift}, - - {NULL,NULL} +const luaL_Reg LuaInterface::luaBitTable[] = +{ + //{"cast", LuaInterface::luaBitCast}, + {"bnot", LuaInterface::luaBitNot}, + {"band", LuaInterface::luaBitAnd}, + {"bor", LuaInterface::luaBitOr}, + {"bxor", LuaInterface::luaBitXor}, + {"lshift", LuaInterface::luaBitLeftShift}, + {"rshift", LuaInterface::luaBitRightShift}, + //{"arshift", LuaInterface::luaBitArithmeticalRightShift}, + + //{"ucast", LuaInterface::luaBitUCast}, + {"ubnot", LuaInterface::luaBitUNot}, + {"uband", LuaInterface::luaBitUAnd}, + {"ubor", LuaInterface::luaBitUOr}, + {"ubxor", LuaInterface::luaBitUXor}, + {"ulshift", LuaInterface::luaBitULeftShift}, + {"urshift", LuaInterface::luaBitURightShift}, + //{"uarshift", LuaInterface::luaBitUArithmeticalRightShift}, + + {NULL, NULL} }; -const luaL_Reg LuaScriptInterface::luaStdTable[] = +const luaL_Reg LuaInterface::luaStdTable[] = { - {"cout", LuaScriptInterface::luaStdCout}, - {"cerr", LuaScriptInterface::luaStdCerr}, - {"clog", LuaScriptInterface::luaStdClog}, + {"cout", LuaInterface::luaStdCout}, + {"clog", LuaInterface::luaStdClog}, + {"cerr", LuaInterface::luaStdCerr}, - {"md5", LuaScriptInterface::luaStdMD5}, - {"sha1", LuaScriptInterface::luaStdSHA1}, + {"md5", LuaInterface::luaStdMD5}, + {"sha1", LuaInterface::luaStdSHA1}, + {"sha256", LuaInterface::luaStdSHA256}, + {"sha512", LuaInterface::luaStdSHA512}, + {"checkName", LuaInterface::luaStdCheckName}, {NULL, NULL} }; -int32_t LuaScriptInterface::internalGetPlayerInfo(lua_State* L, PlayerInfo_t info) +int32_t LuaInterface::internalGetPlayerInfo(lua_State* L, PlayerInfo_t info) { ScriptEnviroment* env = getEnv(); const Player* player = env->getPlayerByUID(popNumber(L)); @@ -2507,8 +2738,8 @@ int32_t LuaScriptInterface::internalGetPlayerInfo(lua_State* L, PlayerInfo_t inf case PlayerInfoVocation: value = player->getVocationId(); break; - case PlayerInfoSoul: - value = player->getSoul(); + case PlayerInfoMoney: + value = g_game.getMoney(player); break; case PlayerInfoFreeCap: value = (int64_t)player->getFreeCapacity(); @@ -2535,8 +2766,12 @@ int32_t LuaScriptInterface::internalGetPlayerInfo(lua_State* L, PlayerInfo_t inf value = player->getGroupId(); break; case PlayerInfoBalance: - value = (g_config.getBool(ConfigManager::BANK_SYSTEM) ? player->balance : 0); - break; + if(g_config.getBool(ConfigManager::BANK_SYSTEM)) + lua_pushnumber(L, player->balance); + else + lua_pushnumber(L, 0); + + return 1; case PlayerInfoStamina: value = player->getStaminaMinutes(); break; @@ -2552,6 +2787,9 @@ int32_t LuaScriptInterface::internalGetPlayerInfo(lua_State* L, PlayerInfo_t inf case PlayerInfoSaving: lua_pushboolean(L, player->isSaving()); return 1; + case PlayerInfoProtected: + lua_pushboolean(L, player->isProtected()); + return 1; case PlayerInfoIp: value = player->getIP(); break; @@ -2577,6 +2815,9 @@ int32_t LuaScriptInterface::internalGetPlayerInfo(lua_State* L, PlayerInfo_t inf case PlayerInfoAccountManager: value = player->accountManager; break; + case PlayerInfoTradeState: + value = player->tradeState; + break; default: errorEx("Unknown player info #" + info); value = 0; @@ -2588,203 +2829,213 @@ int32_t LuaScriptInterface::internalGetPlayerInfo(lua_State* L, PlayerInfo_t inf } //getPlayer[Info](uid) -int32_t LuaScriptInterface::luaGetPlayerNameDescription(lua_State* L) +int32_t LuaInterface::luaGetPlayerNameDescription(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoNameDescription); } -int32_t LuaScriptInterface::luaGetPlayerSpecialDescription(lua_State* L) +int32_t LuaInterface::luaGetPlayerSpecialDescription(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoSpecialDescription); } -int32_t LuaScriptInterface::luaGetPlayerFood(lua_State* L) +int32_t LuaInterface::luaGetPlayerFood(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoFood); } -int32_t LuaScriptInterface::luaGetPlayerAccess(lua_State* L) +int32_t LuaInterface::luaGetPlayerAccess(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoAccess); } -int32_t LuaScriptInterface::luaGetPlayerGhostAccess(lua_State* L) +int32_t LuaInterface::luaGetPlayerGhostAccess(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoGhostAccess); } -int32_t LuaScriptInterface::luaGetPlayerLevel(lua_State* L) +int32_t LuaInterface::luaGetPlayerLevel(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoLevel); } -int32_t LuaScriptInterface::luaGetPlayerExperience(lua_State* L) +int32_t LuaInterface::luaGetPlayerExperience(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoExperience); } -int32_t LuaScriptInterface::luaGetPlayerSpentMana(lua_State* L) +int32_t LuaInterface::luaGetPlayerSpentMana(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoManaSpent); } -int32_t LuaScriptInterface::luaGetPlayerVocation(lua_State* L) +int32_t LuaInterface::luaGetPlayerVocation(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoVocation); } -int32_t LuaScriptInterface::luaGetPlayerSoul(lua_State* L) +int32_t LuaInterface::luaGetPlayerMoney(lua_State* L) { - return internalGetPlayerInfo(L, PlayerInfoSoul); + return internalGetPlayerInfo(L, PlayerInfoMoney); } -int32_t LuaScriptInterface::luaGetPlayerFreeCap(lua_State* L) +int32_t LuaInterface::luaGetPlayerFreeCap(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoFreeCap); } -int32_t LuaScriptInterface::luaGetPlayerGuildId(lua_State* L) +int32_t LuaInterface::luaGetPlayerGuildId(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoGuildId); } -int32_t LuaScriptInterface::luaGetPlayerGuildName(lua_State* L) +int32_t LuaInterface::luaGetPlayerGuildName(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoGuildName); } -int32_t LuaScriptInterface::luaGetPlayerGuildRankId(lua_State* L) +int32_t LuaInterface::luaGetPlayerGuildRankId(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoGuildRankId); } -int32_t LuaScriptInterface::luaGetPlayerGuildRank(lua_State* L) +int32_t LuaInterface::luaGetPlayerGuildRank(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoGuildRank); } -int32_t LuaScriptInterface::luaGetPlayerGuildLevel(lua_State* L) +int32_t LuaInterface::luaGetPlayerGuildLevel(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoGuildLevel); } -int32_t LuaScriptInterface::luaGetPlayerGuildNick(lua_State* L) +int32_t LuaInterface::luaGetPlayerGuildNick(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoGuildNick); } -int32_t LuaScriptInterface::luaGetPlayerTown(lua_State* L) +int32_t LuaInterface::luaGetPlayerTown(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoTown); } -int32_t LuaScriptInterface::luaGetPlayerPromotionLevel(lua_State* L) +int32_t LuaInterface::luaGetPlayerPromotionLevel(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoPromotionLevel); } -int32_t LuaScriptInterface::luaGetPlayerGroupId(lua_State* L) +int32_t LuaInterface::luaGetPlayerGroupId(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoGroupId); } -int32_t LuaScriptInterface::luaGetPlayerGUID(lua_State* L) +int32_t LuaInterface::luaGetPlayerGUID(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoGUID); } -int32_t LuaScriptInterface::luaGetPlayerAccountId(lua_State* L) +int32_t LuaInterface::luaGetPlayerAccountId(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoAccountId); } -int32_t LuaScriptInterface::luaGetPlayerAccount(lua_State* L) +int32_t LuaInterface::luaGetPlayerAccount(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoAccount); } -int32_t LuaScriptInterface::luaGetPlayerPremiumDays(lua_State* L) +int32_t LuaInterface::luaGetPlayerPremiumDays(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoPremiumDays); } -int32_t LuaScriptInterface::luaGetPlayerBalance(lua_State* L) +int32_t LuaInterface::luaGetPlayerBalance(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoBalance); } -int32_t LuaScriptInterface::luaGetPlayerStamina(lua_State* L) +int32_t LuaInterface::luaGetPlayerStamina(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoStamina); } -int32_t LuaScriptInterface::luaGetPlayerLossSkill(lua_State* L) +int32_t LuaInterface::luaGetPlayerLossSkill(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoLossSkill); } -int32_t LuaScriptInterface::luaGetPlayerPartner(lua_State* L) +int32_t LuaInterface::luaGetPlayerPartner(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoMarriage); } -int32_t LuaScriptInterface::luaIsPlayerPzLocked(lua_State* L) +int32_t LuaInterface::luaIsPlayerPzLocked(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoPzLock); } -int32_t LuaScriptInterface::luaIsPlayerSaving(lua_State* L) +int32_t LuaInterface::luaIsPlayerSaving(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoSaving); } -int32_t LuaScriptInterface::luaGetPlayerIp(lua_State* L) +int32_t LuaInterface::luaIsPlayerProtected(lua_State* L) +{ + return internalGetPlayerInfo(L, PlayerInfoProtected); +} + +int32_t LuaInterface::luaGetPlayerIp(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoIp); } -int32_t LuaScriptInterface::luaGetPlayerSkullEnd(lua_State* L) +int32_t LuaInterface::luaGetPlayerSkullEnd(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoSkullEnd); } -int32_t LuaScriptInterface::luaDoPlayerSendOutfitWindow(lua_State* L) +int32_t LuaInterface::luaDoPlayerSendOutfitWindow(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoOutfitWindow); } -int32_t LuaScriptInterface::luaGetPlayerIdleTime(lua_State* L) +int32_t LuaInterface::luaGetPlayerIdleTime(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoIdleTime); } -int32_t LuaScriptInterface::luaHasPlayerClient(lua_State* L) +int32_t LuaInterface::luaHasPlayerClient(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoClient); } -int32_t LuaScriptInterface::luaGetPlayerLastLoad(lua_State* L) +int32_t LuaInterface::luaGetPlayerLastLoad(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoLastLoad); } -int32_t LuaScriptInterface::luaGetPlayerLastLogin(lua_State* L) +int32_t LuaInterface::luaGetPlayerLastLogin(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoLastLogin); } -int32_t LuaScriptInterface::luaGetPlayerAccountManager(lua_State* L) +int32_t LuaInterface::luaGetPlayerAccountManager(lua_State* L) { return internalGetPlayerInfo(L, PlayerInfoAccountManager); } + +int32_t LuaInterface::luaGetPlayerTradeState(lua_State* L) +{ + return internalGetPlayerInfo(L, PlayerInfoTradeState); +} // -int32_t LuaScriptInterface::luaGetPlayerSex(lua_State* L) +int32_t LuaInterface::luaGetPlayerSex(lua_State* L) { //getPlayerSex(cid[, full = false]) bool full = false; if(lua_gettop(L) > 1) - full = popNumber(L); + full = popBoolean(L); ScriptEnviroment* env = getEnv(); Player* player = env->getPlayerByUID((uint32_t)popNumber(L)); @@ -2799,7 +3050,7 @@ int32_t LuaScriptInterface::luaGetPlayerSex(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetNameDescription(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetNameDescription(lua_State* L) { //doPlayerSetNameDescription(cid, description) std::string description = popString(L); @@ -2815,10 +3066,11 @@ int32_t LuaScriptInterface::luaDoPlayerSetNameDescription(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetSpecialDescription(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetSpecialDescription(lua_State* L) { //doPlayerSetSpecialDescription(cid, description) std::string description = popString(L); @@ -2834,19 +3086,20 @@ int32_t LuaScriptInterface::luaDoPlayerSetSpecialDescription(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetPlayerMagLevel(lua_State* L) +int32_t LuaInterface::luaGetPlayerMagLevel(lua_State* L) { - //getPlayerMagLevel(cid[, ignoreBuffs = false]) - bool ignoreBuffs = false; + //getPlayerMagLevel(cid[, ignoreModifiers = false]) + bool ignoreModifiers = false; if(lua_gettop(L) > 1) - ignoreBuffs = popNumber(L); + ignoreModifiers = popBoolean(L); ScriptEnviroment* env = getEnv(); if(const Player* player = env->getPlayerByUID(popNumber(L))) - lua_pushnumber(L, (ignoreBuffs ? player->magLevel : player->getMagicLevel())); + lua_pushnumber(L, ignoreModifiers ? player->magLevel : player->getMagicLevel()); else { errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); @@ -2856,7 +3109,7 @@ int32_t LuaScriptInterface::luaGetPlayerMagLevel(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerRequiredMana(lua_State* L) +int32_t LuaInterface::luaGetPlayerRequiredMana(lua_State* L) { //getPlayerRequiredMana(cid, magicLevel) uint32_t magLevel = popNumber(L); @@ -2873,14 +3126,14 @@ int32_t LuaScriptInterface::luaGetPlayerRequiredMana(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerRequiredSkillTries(lua_State* L) +int32_t LuaInterface::luaGetPlayerRequiredSkillTries(lua_State* L) { - //getPlayerRequiredSkillTries(cid, skillId, skillLevel) - int32_t sLevel = popNumber(L), sId = popNumber(L); + //getPlayerRequiredSkillTries(cid, skill, level) + int32_t level = popNumber(L), skill = popNumber(L); ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) - lua_pushnumber(L, player->vocation->getReqSkillTries(sId, sLevel)); + lua_pushnumber(L, player->vocation->getReqSkillTries(skill, level)); else { errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); @@ -2890,7 +3143,7 @@ int32_t LuaScriptInterface::luaGetPlayerRequiredSkillTries(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerFlagValue(lua_State* L) +int32_t LuaInterface::luaGetPlayerFlagValue(lua_State* L) { //getPlayerFlagValue(cid, flag) uint32_t index = popNumber(L); @@ -2902,9 +3155,7 @@ int32_t LuaScriptInterface::luaGetPlayerFlagValue(lua_State* L) lua_pushboolean(L, player->hasFlag((PlayerFlags)index)); else { - std::stringstream ss; - ss << index; - errorEx("No valid flag index - " + ss.str()); + errorEx("No valid flag index - " + asString(index)); lua_pushboolean(L, false); } } @@ -2917,7 +3168,7 @@ int32_t LuaScriptInterface::luaGetPlayerFlagValue(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerCustomFlagValue(lua_State* L) +int32_t LuaInterface::luaGetPlayerCustomFlagValue(lua_State* L) { //getPlayerCustomFlagValue(cid, flag) uint32_t index = popNumber(L); @@ -2929,9 +3180,7 @@ int32_t LuaScriptInterface::luaGetPlayerCustomFlagValue(lua_State* L) lua_pushboolean(L, player->hasCustomFlag((PlayerCustomFlags)index)); else { - std::stringstream ss; - ss << index; - errorEx("No valid flag index - " + ss.str()); + errorEx("No valid flag index - " + asString(index)); lua_pushboolean(L, false); } } @@ -2944,7 +3193,7 @@ int32_t LuaScriptInterface::luaGetPlayerCustomFlagValue(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerLearnInstantSpell(lua_State* L) +int32_t LuaInterface::luaDoPlayerLearnInstantSpell(lua_State* L) { //doPlayerLearnInstantSpell(cid, name) std::string spellName = popString(L); @@ -2970,7 +3219,7 @@ int32_t LuaScriptInterface::luaDoPlayerLearnInstantSpell(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerUnlearnInstantSpell(lua_State* L) +int32_t LuaInterface::luaDoPlayerUnlearnInstantSpell(lua_State* L) { //doPlayerUnlearnInstantSpell(cid, name) std::string spellName = popString(L); @@ -2996,7 +3245,7 @@ int32_t LuaScriptInterface::luaDoPlayerUnlearnInstantSpell(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerLearnedInstantSpell(lua_State* L) +int32_t LuaInterface::luaGetPlayerLearnedInstantSpell(lua_State* L) { //getPlayerLearnedInstantSpell(cid, name) std::string spellName = popString(L); @@ -3021,7 +3270,7 @@ int32_t LuaScriptInterface::luaGetPlayerLearnedInstantSpell(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerInstantSpellCount(lua_State* L) +int32_t LuaInterface::luaGetPlayerInstantSpellCount(lua_State* L) { //getPlayerInstantSpellCount(cid) ScriptEnviroment* env = getEnv(); @@ -3032,10 +3281,11 @@ int32_t LuaScriptInterface::luaGetPlayerInstantSpellCount(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetPlayerInstantSpellInfo(lua_State* L) +int32_t LuaInterface::luaGetPlayerInstantSpellInfo(lua_State* L) { //getPlayerInstantSpellInfo(cid, index) uint32_t index = popNumber(L); @@ -3067,7 +3317,7 @@ int32_t LuaScriptInterface::luaGetPlayerInstantSpellInfo(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetInstantSpellInfo(lua_State* L) +int32_t LuaInterface::luaGetInstantSpellInfo(lua_State* L) { //getInstantSpellInfo(name) InstantSpell* spell = g_spells->getInstantSpellByName(popString(L)); @@ -3088,9 +3338,9 @@ int32_t LuaScriptInterface::luaGetInstantSpellInfo(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoRemoveItem(lua_State* L) +int32_t LuaInterface::luaDoRemoveItem(lua_State* L) { - //doRemoveItem(uid[, count]) + //doRemoveItem(uid[, count = -1]) int32_t count = -1; if(lua_gettop(L) > 1) count = popNumber(L); @@ -3114,11 +3364,15 @@ int32_t LuaScriptInterface::luaDoRemoveItem(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerRemoveItem(lua_State* L) +int32_t LuaInterface::luaDoPlayerRemoveItem(lua_State* L) { - //doPlayerRemoveItem(cid, itemid, count[, subType]) - int32_t subType = -1; - if(lua_gettop(L) > 3) + //doPlayerRemoveItem(cid, itemid, count[, subType = -1[, ignoreEquipped = false]]) + int32_t params = lua_gettop(L), subType = -1; + bool ignoreEquipped = false; + if(params > 4) + ignoreEquipped = popBoolean(L); + + if(params > 3) subType = popNumber(L); uint32_t count = popNumber(L); @@ -3126,18 +3380,19 @@ int32_t LuaScriptInterface::luaDoPlayerRemoveItem(lua_State* L) ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) - lua_pushboolean(L, g_game.removeItemOfType(player, itemId, count, subType)); + lua_pushboolean(L, g_game.removeItemOfType(player, itemId, count, subType, ignoreEquipped)); else { errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoFeedPlayer(lua_State* L) +int32_t LuaInterface::luaDoPlayerFeed(lua_State* L) { - //doFeedPlayer(cid, food) + //doPlayerFeed(cid, food) int32_t food = (int32_t)popNumber(L); ScriptEnviroment* env = getEnv(); @@ -3151,10 +3406,11 @@ int32_t LuaScriptInterface::luaDoFeedPlayer(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSendCancel(lua_State* L) +int32_t LuaInterface::luaDoPlayerSendCancel(lua_State* L) { //doPlayerSendCancel(cid, text) std::string text = popString(L); @@ -3169,10 +3425,11 @@ int32_t LuaScriptInterface::luaDoPlayerSendCancel(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoSendDefaultCancel(lua_State* L) +int32_t LuaInterface::luaDoSendDefaultCancel(lua_State* L) { //doPlayerSendDefaultCancel(cid, ReturnValue) ReturnValue ret = (ReturnValue)popNumber(L); @@ -3191,7 +3448,7 @@ int32_t LuaScriptInterface::luaDoSendDefaultCancel(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetSearchString(lua_State* L) +int32_t LuaInterface::luaGetSearchString(lua_State* L) { //getSearchString(fromPosition, toPosition[, fromIsCreature = false[, toIsCreature = false]]) PositionEx toPos, fromPos; @@ -3199,16 +3456,16 @@ int32_t LuaScriptInterface::luaGetSearchString(lua_State* L) int32_t params = lua_gettop(L); if(params > 3) - toIsCreature = popNumber(L); + toIsCreature = popBoolean(L); if(params > 2) - fromIsCreature = popNumber(L); + fromIsCreature = popBoolean(L); popPosition(L, toPos); popPosition(L, fromPos); if(!toPos.x || !toPos.y || !fromPos.x || !fromPos.y) { - errorEx("wrong position(s) specified."); + errorEx("Wrong position(s) specified"); lua_pushboolean(L, false); } else @@ -3217,16 +3474,16 @@ int32_t LuaScriptInterface::luaGetSearchString(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetClosestFreeTile(lua_State* L) +int32_t LuaInterface::luaGetClosestFreeTile(lua_State* L) { //getClosestFreeTile(cid, targetPos[, extended = false[, ignoreHouse = true]]) uint32_t params = lua_gettop(L); bool ignoreHouse = true, extended = false; if(params > 3) - ignoreHouse = popNumber(L); + ignoreHouse = popBoolean(L); if(params > 2) - extended = popNumber(L); + extended = popBoolean(L); PositionEx pos; popPosition(L, pos); @@ -3245,39 +3502,71 @@ int32_t LuaScriptInterface::luaGetClosestFreeTile(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoTeleportThing(lua_State* L) +int32_t LuaInterface::luaDoTeleportThing(lua_State* L) { - //doTeleportThing(cid, newpos[, pushmove = TRUE]) - bool pushMove = true; - if(lua_gettop(L) > 2) - pushMove = popNumber(L); + //doTeleportThing(cid, destination[, pushMove = true[, fullTeleport = true]]) + bool fullTeleport = true, pushMove = true; + int32_t params = lua_gettop(L); + if(params > 3) + fullTeleport = popBoolean(L); + + if(params > 2) + pushMove = popBoolean(L); PositionEx pos; popPosition(L, pos); ScriptEnviroment* env = getEnv(); if(Thing* tmp = env->getThingByUID(popNumber(L))) - lua_pushboolean(L, g_game.internalTeleport(tmp, pos, pushMove) == RET_NOERROR); + lua_pushboolean(L, g_game.internalTeleport(tmp, pos, !pushMove, FLAG_NOLIMIT, fullTeleport) == RET_NOERROR); else { errorEx(getError(LUA_ERROR_THING_NOT_FOUND)); lua_pushboolean(L, false); } + + return 1; +} + +int32_t LuaInterface::luaDoItemSetDestination(lua_State* L) +{ + //doItemSetDestination(uid, destination) + PositionEx destination; + popPosition(L, destination); + + ScriptEnviroment* env = getEnv(); + Item* item = env->getItemByUID(popNumber(L)); + if(!item) + { + errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + if(Teleport* teleport = item->getTeleport()) + { + teleport->setDestination(destination); + lua_pushboolean(L, true); + return 1; + } + + errorEx("Target item is not a teleport."); + lua_pushboolean(L, false); return 1; } -int32_t LuaScriptInterface::luaDoTransformItem(lua_State* L) +int32_t LuaInterface::luaDoTransformItem(lua_State* L) { //doTransformItem(uid, newId[, count/subType]) int32_t count = -1; if(lua_gettop(L) > 2) count = popNumber(L); - uint16_t newId = popNumber(L); - uint32_t uid = popNumber(L); + uint32_t newId = popNumber(L), uid = popNumber(L); ScriptEnviroment* env = getEnv(); Item* item = env->getItemByUID(uid); @@ -3293,17 +3582,17 @@ int32_t LuaScriptInterface::luaDoTransformItem(lua_State* L) count = 100; Item* newItem = g_game.transformItem(item, newId, count); - if(item->isRemoved()) - env->removeThing(uid); - if(newItem && newItem != item) + { + env->removeThing(uid); env->insertThing(uid, newItem); + } lua_pushboolean(L, true); return 1; } -int32_t LuaScriptInterface::luaDoCreatureSay(lua_State* L) +int32_t LuaInterface::luaDoCreatureSay(lua_State* L) { //doCreatureSay(uid, text[, type = SPEAK_SAY[, ghost = false[, cid = 0[, pos]]]]) uint32_t params = lua_gettop(L), cid = 0, uid = 0; @@ -3316,18 +3605,18 @@ int32_t LuaScriptInterface::luaDoCreatureSay(lua_State* L) bool ghost = false; if(params > 3) - ghost = popNumber(L); + ghost = popBoolean(L); - SpeakClasses type = SPEAK_SAY; + MessageClasses type = MSG_SPEAK_SAY; if(params > 2) - type = (SpeakClasses)popNumber(L); + type = (MessageClasses)popNumber(L); std::string text = popString(L); uid = popNumber(L); if(params > 5 && (!pos.x || !pos.y)) { - errorEx("Invalid position specified."); + errorEx("Invalid position specified"); lua_pushboolean(L, false); return 1; } @@ -3363,7 +3652,36 @@ int32_t LuaScriptInterface::luaDoCreatureSay(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoSendMagicEffect(lua_State* L) +int32_t LuaInterface::luaDoCreatureChannelSay(lua_State* L) +{ + //doCreatureChannelSay(target, uid, message, type, channel) + ScriptEnviroment* env = getEnv(); + uint16_t channelId = popNumber(L); + std::string text = popString(L); + uint32_t speakClass = popNumber(L), targetId = popNumber(L); + + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + Creature* creature = env->getCreatureByUID(targetId); + if(!creature) + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + player->sendCreatureChannelSay(creature, (MessageClasses)speakClass, text, channelId); + lua_pushboolean(L, true); + return 1; +} + +int32_t LuaInterface::luaDoSendMagicEffect(lua_State* L) { //doSendMagicEffect(pos, type[, player]) ScriptEnviroment* env = getEnv(); @@ -3390,7 +3708,7 @@ int32_t LuaScriptInterface::luaDoSendMagicEffect(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoSendDistanceShoot(lua_State* L) +int32_t LuaInterface::luaDoSendDistanceShoot(lua_State* L) { //doSendDistanceShoot(fromPos, toPos, type[, player]) ScriptEnviroment* env = getEnv(); @@ -3421,14 +3739,16 @@ int32_t LuaScriptInterface::luaDoSendDistanceShoot(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddSkillTry(lua_State* L) +int32_t LuaInterface::luaDoPlayerAddSkillTry(lua_State* L) { - //doPlayerAddSkillTry(uid, skillid, n[, useMultiplier]) + //doPlayerAddSkillTry(uid, skillid, n[, useMultiplier = true]) bool multiplier = true; if(lua_gettop(L) > 3) - multiplier = popNumber(L); + multiplier = popBoolean(L); + + uint64_t n = popNumber(L); + uint16_t skillid = popNumber(L); - uint32_t n = popNumber(L), skillid = popNumber(L); ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) { @@ -3440,34 +3760,37 @@ int32_t LuaScriptInterface::luaDoPlayerAddSkillTry(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureSpeakType(lua_State* L) +int32_t LuaInterface::luaGetCreatureSpeakType(lua_State* L) { //getCreatureSpeakType(uid) ScriptEnviroment* env = getEnv(); if(const Creature* creature = env->getCreatureByUID(popNumber(L))) - lua_pushnumber(L, (SpeakClasses)creature->getSpeakType()); + lua_pushnumber(L, (MessageClasses)creature->getSpeakType()); else { errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoCreatureSetSpeakType(lua_State* L) +int32_t LuaInterface::luaDoCreatureSetSpeakType(lua_State* L) { //doCreatureSetSpeakType(uid, type) - SpeakClasses type = (SpeakClasses)popNumber(L); + MessageClasses type = (MessageClasses)popNumber(L); ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) { - if(type < SPEAK_CLASS_FIRST || type > SPEAK_CLASS_LAST) + if(!((type >= MSG_SPEAK_FIRST && type <= MSG_SPEAK_LAST) || + (type >= MSG_SPEAK_MONSTER_FIRST && type <= MSG_SPEAK_MONSTER_LAST))) { - errorEx("Invalid speak type!"); + errorEx("Invalid speak type"); lua_pushboolean(L, false); return 1; } @@ -3480,10 +3803,11 @@ int32_t LuaScriptInterface::luaDoCreatureSetSpeakType(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureHideHealth(lua_State* L) +int32_t LuaInterface::luaGetCreatureHideHealth(lua_State* L) { //getCreatureHideHealth(cid) ScriptEnviroment* env = getEnv(); @@ -3495,13 +3819,14 @@ int32_t LuaScriptInterface::luaGetCreatureHideHealth(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoCreatureSetHideHealth(lua_State* L) +int32_t LuaInterface::luaDoCreatureSetHideHealth(lua_State* L) { //doCreatureSetHideHealth(cid, hide) - bool hide = popNumber(L); + bool hide = popBoolean(L); ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) @@ -3515,20 +3840,21 @@ int32_t LuaScriptInterface::luaDoCreatureSetHideHealth(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoCreatureAddHealth(lua_State* L) +int32_t LuaInterface::luaDoCreatureAddHealth(lua_State* L) { //doCreatureAddHealth(uid, health[, hitEffect[, hitColor[, force]]]) int32_t params = lua_gettop(L); bool force = false; if(params > 4) - force = popNumber(L); + force = popBoolean(L); - TextColor_t hitColor = TEXTCOLOR_UNKNOWN; + Color_t hitColor = COLOR_UNKNOWN; if(params > 3) - hitColor = (TextColor_t)popNumber(L); + hitColor = (Color_t)popNumber(L); MagicEffect_t hitEffect = MAGIC_EFFECT_UNKNOWN; if(params > 2) @@ -3553,12 +3879,12 @@ int32_t LuaScriptInterface::luaDoCreatureAddHealth(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCreatureAddMana(lua_State* L) +int32_t LuaInterface::luaDoCreatureAddMana(lua_State* L) { //doCreatureAddMana(uid, mana[, aggressive]) bool aggressive = true; if(lua_gettop(L) > 2) - aggressive = popNumber(L); + aggressive = popBoolean(L); int32_t manaChange = popNumber(L); ScriptEnviroment* env = getEnv(); @@ -3576,15 +3902,16 @@ int32_t LuaScriptInterface::luaDoCreatureAddMana(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddSpentMana(lua_State* L) +int32_t LuaInterface::luaDoPlayerAddSpentMana(lua_State* L) { - //doPlayerAddSpentMana(cid, amount[, useMultiplier]) + //doPlayerAddSpentMana(cid, amount[, useMultiplier = true]) bool multiplier = true; if(lua_gettop(L) > 2) - multiplier = popNumber(L); + multiplier = popBoolean(L); uint32_t amount = popNumber(L); ScriptEnviroment* env = getEnv(); @@ -3598,28 +3925,43 @@ int32_t LuaScriptInterface::luaDoPlayerAddSpentMana(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddItem(lua_State* L) +int32_t LuaInterface::luaDoPlayerAddItem(lua_State* L) { - //doPlayerAddItem(cid, itemid[, count/subtype[, canDropOnMap]]) - //doPlayerAddItem(cid, itemid[, count[, canDropOnMap[, subtype]]]) - int32_t params = lua_gettop(L), subType = 1; - if(params > 4) - subType = popNumber(L); + //doPlayerAddItem(cid, itemid[, count/subtype = 1[, canDropOnMap = true[, slot = 0]]]) + //doPlayerAddItem(cid, itemid[, count = 1[, canDropOnMap = true[, subtype = 1[, slot = 0]]]]) + int32_t params = lua_gettop(L), subType = 1, slot = SLOT_WHEREEVER; + if(params > 5) + slot = popNumber(L); + + if(params > 4) + { + if(params > 5) + subType = popNumber(L); + else + slot = popNumber(L); + } bool canDropOnMap = true; if(params > 3) - canDropOnMap = popNumber(L); + canDropOnMap = popBoolean(L); uint32_t count = 1; if(params > 2) count = popNumber(L); uint32_t itemId = popNumber(L); - ScriptEnviroment* env = getEnv(); + if(slot > SLOT_AMMO) + { + errorEx("Invalid slot"); + lua_pushboolean(L, false); + return 1; + } + ScriptEnviroment* env = getEnv(); Player* player = env->getPlayerByUID((uint32_t)popNumber(L)); if(!player) { @@ -3640,54 +3982,67 @@ int32_t LuaScriptInterface::luaDoPlayerAddItem(lua_State* L) subType = count; } + uint32_t ret = 0; + Item* newItem = NULL; while(itemCount > 0) { int32_t stackCount = std::min(100, subType); - Item* newItem = Item::CreateItem(itemId, stackCount); - if(!newItem) + if(!(newItem = Item::CreateItem(itemId, stackCount))) { errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND)); lua_pushboolean(L, false); - return 1; + return ++ret; } if(it.stackable) subType -= stackCount; - ReturnValue ret = g_game.internalPlayerAddItem(NULL, player, newItem, canDropOnMap); - if(ret != RET_NOERROR) + Item* stackItem = NULL; + if(g_game.internalPlayerAddItem(NULL, player, newItem, canDropOnMap, (slots_t)slot, &stackItem) != RET_NOERROR) { delete newItem; lua_pushboolean(L, false); - return 1; + return ++ret; } - --itemCount; - if(itemCount) - continue; - + ++ret; if(newItem->getParent()) lua_pushnumber(L, env->addThing(newItem)); - else //stackable item stacked with existing object, newItem will be released + else if(stackItem) + lua_pushnumber(L, env->addThing(stackItem)); + else lua_pushnil(L); - return 1; + --itemCount; } + if(ret) + return ret; + lua_pushnil(L); return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddItemEx(lua_State* L) +int32_t LuaInterface::luaDoPlayerAddItemEx(lua_State* L) { - //doPlayerAddItemEx(cid, uid[, canDropOnMap = false]) + //doPlayerAddItemEx(cid, uid[, canDropOnMap = false[, slot = 0]]) + int32_t params = lua_gettop(L), slot = SLOT_WHEREEVER; + if(params > 3) + slot = popNumber(L); + bool canDropOnMap = false; - if(lua_gettop(L) > 2) - canDropOnMap = popNumber(L); + if(params > 2) + canDropOnMap = popBoolean(L); uint32_t uid = (uint32_t)popNumber(L); - ScriptEnviroment* env = getEnv(); + if(slot > SLOT_AMMO) + { + errorEx("Invalid slot"); + lua_pushboolean(L, false); + return 1; + } + ScriptEnviroment* env = getEnv(); Player* player = env->getPlayerByUID(popNumber(L)); if(!player) { @@ -3705,14 +4060,21 @@ int32_t LuaScriptInterface::luaDoPlayerAddItemEx(lua_State* L) } if(item->getParent() == VirtualCylinder::virtualCylinder) - lua_pushnumber(L, g_game.internalPlayerAddItem(NULL, player, item, canDropOnMap)); + { + env->removeTempItem(env, item); + ReturnValue ret = g_game.internalPlayerAddItem(NULL, player, item, canDropOnMap, (slots_t)slot); + if(ret != RET_NOERROR) + env->addTempItem(env, item); + + lua_pushnumber(L, ret); + } else lua_pushboolean(L, false); return 1; } -int32_t LuaScriptInterface::luaDoTileAddItemEx(lua_State* L) +int32_t LuaInterface::luaDoTileAddItemEx(lua_State* L) { //doTileAddItemEx(pos, uid) uint32_t uid = (uint32_t)popNumber(L); @@ -3720,38 +4082,65 @@ int32_t LuaScriptInterface::luaDoTileAddItemEx(lua_State* L) popPosition(L, pos); ScriptEnviroment* env = getEnv(); - Tile* tile = g_game.getTile(pos.x, pos.y, pos.z); - if(!tile) + Item* item = env->getItemByUID(uid); + if(!item) { - errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); + errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND)); lua_pushboolean(L, false); return 1; } - Item* item = env->getItemByUID(uid); - if(!item) + Tile* tile = g_game.getTile(pos); + if(!tile) { - errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND)); - lua_pushboolean(L, false); + if(item->isGroundTile()) + { + if(item->getParent() == VirtualCylinder::virtualCylinder) + { + tile = IOMap::createTile(item, NULL, pos.x, pos.y, pos.z); + g_game.setTile(tile); + + env->removeTempItem(env, item); + lua_pushnumber(L, RET_NOERROR); + } + else + lua_pushboolean(L, false); + } + else + { + errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); + lua_pushboolean(L, false); + } + return 1; } if(item->getParent() == VirtualCylinder::virtualCylinder) - lua_pushnumber(L, g_game.internalAddItem(NULL, tile, item)); + { + ReturnValue ret = g_game.internalAddItem(NULL, tile, item); + if(ret == RET_NOERROR) + env->removeTempItem(env, item); + + lua_pushnumber(L, ret); + } else lua_pushboolean(L, false); return 1; } -int32_t LuaScriptInterface::luaDoRelocate(lua_State* L) +int32_t LuaInterface::luaDoRelocate(lua_State* L) { - //doRelocate(pos, posTo[, creatures = true]) - //Moves all moveable objects from pos to posTo + //doRelocate(pos, posTo[, creatures = true[, unmovable = true]]) + //Moves all[ movable] objects from pos to posTo + //Uses protected methods for optimal speed + bool unmovable = true, creatures = true; + int32_t params = lua_gettop(L); + if(params > 3) + unmovable = popBoolean(L); - bool creatures = true; - if(lua_gettop(L) > 2) - creatures = popNumber(L); + if(params > 2) + creatures = popBoolean(L); PositionEx toPos; popPosition(L, toPos); @@ -3759,7 +4148,7 @@ int32_t LuaScriptInterface::luaDoRelocate(lua_State* L) PositionEx fromPos; popPosition(L, fromPos); - Tile* fromTile = g_game.getTile(fromPos.x, fromPos.y, fromPos.z); + Tile* fromTile = g_game.getTile(fromPos); if(!fromTile) { errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); @@ -3767,7 +4156,7 @@ int32_t LuaScriptInterface::luaDoRelocate(lua_State* L) return 1; } - Tile* toTile = g_game.getTile(toPos.x, toPos.y, toPos.z); + Tile* toTile = g_game.getTile(toPos); if(!toTile) { errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); @@ -3775,26 +4164,73 @@ int32_t LuaScriptInterface::luaDoRelocate(lua_State* L) return 1; } - if(fromTile != toTile) + if(fromTile == toTile) + { + lua_pushboolean(L, false); + return 1; + } + + TileItemVector *toItems = toTile->getItemList(), + *fromItems = fromTile->getItemList(); + if(fromItems && toItems) { - for(int32_t i = fromTile->getThingCount() - 1; i >= 0; --i) + int32_t itemLimit = g_config.getNumber(toTile->hasFlag(TILESTATE_PROTECTIONZONE) + ? ConfigManager::PROTECTION_TILE_LIMIT : ConfigManager::TILE_LIMIT), count = 0; + for(ItemVector::iterator it = fromItems->getBeginDownItem(); it != fromItems->getEndDownItem(); ) { - Thing* thing = fromTile->__getThing(i); - if(thing) + if(itemLimit && (int32_t)toItems->size() > itemLimit) + break; + + const ItemType& iType = Item::items[(*it)->getID()]; + if(!iType.isGroundTile() && !iType.alwaysOnTop && !iType.isMagicField() && (unmovable || iType.movable)) { - if(Item* item = thing->getItem()) + if(Item* item = (*it)) { - const ItemType& it = Item::items[item->getID()]; - if(!it.isGroundTile() && !it.alwaysOnTop && !it.isMagicField()) - g_game.internalTeleport(item, toPos, false, FLAG_IGNORENOTMOVEABLE); - } - else if(creatures) - { - Creature* creature = thing->getCreature(); - if(creature) - g_game.internalTeleport(creature, toPos, true); + it = fromItems->erase(it); + fromItems->removeDownItem(); + fromTile->updateTileFlags(item, true); + + g_moveEvents->onItemMove(NULL, item, fromTile, false); + g_moveEvents->onRemoveTileItem(fromTile, item); + + item->setParent(toTile); + ++count; + + toItems->insert(toItems->getBeginDownItem(), item); + toItems->addDownItem(); + toTile->updateTileFlags(item, false); + + g_moveEvents->onAddTileItem(toTile, item); + g_moveEvents->onItemMove(NULL, item, toTile, true); } + else + ++it; } + else + ++it; + } + + fromTile->updateThingCount(-count); + toTile->updateThingCount(count); + + fromTile->onUpdateTile(); + toTile->onUpdateTile(); + if(g_config.getBool(ConfigManager::STORE_TRASH) + && fromTile->hasFlag(TILESTATE_TRASHED)) + { + g_game.addTrash(toPos); + toTile->setFlag(TILESTATE_TRASHED); + } + } + + if(creatures) + { + CreatureVector* creatureVector = fromTile->getCreatures(); + Creature* creature = NULL; + while(creatureVector && !creatureVector->empty()) + { + if((creature = (*creatureVector->begin()))) + g_game.internalMoveCreature(NULL, creature, fromTile, toTile, FLAG_NOLIMIT); } } @@ -3802,13 +4238,13 @@ int32_t LuaScriptInterface::luaDoRelocate(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCleanTile(lua_State* L) +int32_t LuaInterface::luaDoCleanTile(lua_State* L) { //doCleanTile(pos, forceMapLoaded = false) //Remove all items from tile, ignore creatures bool forceMapLoaded = false; if(lua_gettop(L) > 1) - forceMapLoaded = popNumber(L); + forceMapLoaded = popBoolean(L); PositionEx pos; popPosition(L, pos); @@ -3821,25 +4257,36 @@ int32_t LuaScriptInterface::luaDoCleanTile(lua_State* L) return 1; } + Thing* thing = NULL; + Item* item = NULL; + for(int32_t i = tile->getThingCount() - 1; i >= 1; --i) //ignore ground { - if(Thing* thing = tile->__getThing(i)) - { - if(Item* item = thing->getItem()) - { - if(!item->isLoadedFromMap() || forceMapLoaded) - g_game.internalRemoveItem(NULL, item); - } - } + if(!(thing = tile->__getThing(i)) || !(item = thing->getItem())) + continue; + + if(!item->isLoadedFromMap() || forceMapLoaded) + g_game.internalRemoveItem(NULL, item); } lua_pushboolean(L, true); return 1; } -int32_t LuaScriptInterface::luaDoPlayerSendTextMessage(lua_State* L) +int32_t LuaInterface::luaDoPlayerSendTextMessage(lua_State* L) { - //doPlayerSendTextMessage(cid, MessageClasses, message) + //doPlayerSendTextMessage(cid, MessageClasses, message[, value[, color[, position]]]) + int32_t args = lua_gettop(L), value = 0, color = COLOR_WHITE; + PositionEx position; + if(args > 5) + popPosition(L, position); + + if(args > 4) + color = popNumber(L); + + if(args > 3) + value = popNumber(L); + std::string text = popString(L); uint32_t messageClass = popNumber(L); @@ -3852,14 +4299,25 @@ int32_t LuaScriptInterface::luaDoPlayerSendTextMessage(lua_State* L) return 1; } - player->sendTextMessage((MessageClasses)messageClass, text); + if(args > 3) + { + if(!position.x || !position.y) + position = player->getPosition(); + + MessageDetails* details = new MessageDetails(value, (Color_t)color); + player->sendStatsMessage((MessageClasses)messageClass, text, position, details); + delete details; + } + else + player->sendTextMessage((MessageClasses)messageClass, text); + lua_pushboolean(L, true); return 1; } -int32_t LuaScriptInterface::luaDoPlayerSendChannelMessage(lua_State* L) +int32_t LuaInterface::luaDoPlayerSendChannelMessage(lua_State* L) { - //doPlayerSendChannelMessage(cid, author, message, SpeakClasses, channel) + //doPlayerSendChannelMessage(cid, author, message, MessageClasses, channel) uint16_t channelId = popNumber(L); uint32_t speakClass = popNumber(L); std::string text = popString(L), name = popString(L); @@ -3873,82 +4331,115 @@ int32_t LuaScriptInterface::luaDoPlayerSendChannelMessage(lua_State* L) return 1; } - player->sendChannelMessage(name, text, (SpeakClasses)speakClass, channelId); + player->sendChannelMessage(name, text, (MessageClasses)speakClass, channelId); lua_pushboolean(L, true); return 1; } -int32_t LuaScriptInterface::luaDoPlayerSendToChannel(lua_State* L) +int32_t LuaInterface::luaDoPlayerOpenChannel(lua_State* L) { - //doPlayerSendToChannel(cid, targetId, SpeakClasses, message, channel[, time]) - ScriptEnviroment* env = getEnv(); - uint32_t time = 0; - if(lua_gettop(L) > 5) - time = popNumber(L); - + //doPlayerOpenChannel(cid, channelId) uint16_t channelId = popNumber(L); - std::string text = popString(L); - uint32_t speakClass = popNumber(L), targetId = popNumber(L); + uint32_t cid = popNumber(L); - Player* player = env->getPlayerByUID(popNumber(L)); - if(!player) + ScriptEnviroment* env = getEnv(); + if(env->getPlayerByUID(cid)) { - errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); - lua_pushboolean(L, false); + lua_pushboolean(L, g_game.playerOpenChannel(cid, channelId)); return 1; } - Creature* creature = env->getCreatureByUID(targetId); - if(!creature) + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; +} + +int32_t LuaInterface::luaDoPlayerSendChannels(lua_State* L) +{ + //doPlayerSendChannels(cid[, list]) + ChannelsList channels; + uint32_t params = lua_gettop(L); + if(params > 1) { - errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); - lua_pushboolean(L, false); + if(!lua_istable(L, -1)) + { + errorEx("Channel list is not a table"); + lua_pushboolean(L, false); + return 1; + } + + lua_pushnil(L); + while(lua_next(L, -2)) + { + channels.push_back(std::make_pair((uint16_t)lua_tonumber(L, -2), lua_tostring(L, -1))); + lua_pop(L, 1); + } + + lua_pop(L, 1); + } + + ScriptEnviroment* env = getEnv(); + if(Player* player = env->getPlayerByUID(popNumber(L))) + { + if(params < 2) + channels = g_chat.getChannelList(player); + + player->sendChannelsDialog(channels); + player->setSentChat(params < 2); + lua_pushboolean(L, true); return 1; } - player->sendToChannel(creature, (SpeakClasses)speakClass, text, channelId, time); - lua_pushboolean(L, true); + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); return 1; } -int32_t LuaScriptInterface::luaDoSendAnimatedText(lua_State* L) +int32_t LuaInterface::luaDoSendCreatureSquare(lua_State* L) { - //doSendAnimatedText(pos, text, color[, player]) + //doSendCreatureSquare(cid, color[, player]) ScriptEnviroment* env = getEnv(); SpectatorVec list; - if(lua_gettop(L) > 3) + if(lua_gettop(L) > 2) { if(Creature* creature = env->getCreatureByUID(popNumber(L))) list.push_back(creature); } - uint32_t color = popNumber(L); - std::string text = popString(L); - - PositionEx pos; - popPosition(L, pos); - if(pos.x == 0xFFFF) - pos = env->getRealPos(); + uint8_t color = popNumber(L); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) + { + if(!list.empty()) + g_game.addCreatureSquare(list, creature, color); + else + g_game.addCreatureSquare(creature, color); - if(!list.empty()) - g_game.addAnimatedText(list, pos, color, text); + lua_pushboolean(L, true); + } else - g_game.addAnimatedText(pos, color, text); + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } - lua_pushboolean(L, true); return 1; } -int32_t LuaScriptInterface::luaGetPlayerSkillLevel(lua_State* L) +int32_t LuaInterface::luaGetPlayerSkillLevel(lua_State* L) { - //getPlayerSkillLevel(cid, skillid) - uint32_t skillId = popNumber(L); + //getPlayerSkillLevel(cid, skill[, ignoreModifiers = false]) + bool ignoreModifiers = false; + if(lua_gettop(L) > 2) + ignoreModifiers = popBoolean(L); + + uint32_t skill = popNumber(L); ScriptEnviroment* env = getEnv(); if(const Player* player = env->getPlayerByUID(popNumber(L))) { - if(skillId <= SKILL_LAST) - lua_pushnumber(L, player->skills[skillId][SKILL_LEVEL]); + if(skill <= SKILL_LAST) + lua_pushnumber(L, ignoreModifiers ? player->skills[skill][SKILL_LEVEL] : + player->skills[skill][SKILL_LEVEL] + player->getVarSkill((skills_t)skill)); else lua_pushboolean(L, false); } @@ -3957,19 +4448,20 @@ int32_t LuaScriptInterface::luaGetPlayerSkillLevel(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetPlayerSkillTries(lua_State* L) +int32_t LuaInterface::luaGetPlayerSkillTries(lua_State* L) { - //getPlayerSkillTries(cid, skillid) - uint32_t skillid = popNumber(L); + //getPlayerSkillTries(cid, skill) + uint32_t skill = popNumber(L); ScriptEnviroment* env = getEnv(); if(const Player* player = env->getPlayerByUID(popNumber(L))) { - if(skillid <= SKILL_LAST) - lua_pushnumber(L, player->skills[skillid][SKILL_TRIES]); + if(skill <= SKILL_LAST) + lua_pushnumber(L, player->skills[skill][SKILL_TRIES]); else lua_pushboolean(L, false); } @@ -3978,13 +4470,14 @@ int32_t LuaScriptInterface::luaGetPlayerSkillTries(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoCreatureSetDropLoot(lua_State* L) +int32_t LuaInterface::luaDoCreatureSetDropLoot(lua_State* L) { //doCreatureSetDropLoot(cid, doDrop) - bool doDrop = popNumber(L); + bool doDrop = popBoolean(L); ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) @@ -4001,7 +4494,7 @@ int32_t LuaScriptInterface::luaDoCreatureSetDropLoot(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerLossPercent(lua_State* L) +int32_t LuaInterface::luaGetPlayerLossPercent(lua_State* L) { //getPlayerLossPercent(cid, lossType) uint8_t lossType = (uint8_t)popNumber(L); @@ -4022,10 +4515,11 @@ int32_t LuaScriptInterface::luaGetPlayerLossPercent(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetLossPercent(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetLossPercent(lua_State* L) { //doPlayerSetLossPercent(cid, lossType, newPercent) uint32_t newPercent = popNumber(L); @@ -4047,13 +4541,14 @@ int32_t LuaScriptInterface::luaDoPlayerSetLossPercent(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetLossSkill(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetLossSkill(lua_State* L) { //doPlayerSetLossSkill(cid, doLose) - bool doLose = popNumber(L); + bool doLose = popBoolean(L); ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) @@ -4066,20 +4561,54 @@ int32_t LuaScriptInterface::luaDoPlayerSetLossSkill(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoShowTextDialog(lua_State* L) +int32_t LuaInterface::luaDoShowTextDialog(lua_State* L) { - //doShowTextDialog(cid, itemid, text) - std::string text = popString(L); - uint32_t itemId = popNumber(L); + //doShowTextDialog(cid, itemid[, (text/canWrite)[, (canWrite/length)[, length]]]) + int32_t length = -1, params = lua_gettop(L); + if(params > 4) + length = std::abs(popNumber(L)); + + bool canWrite = false; + if(params > 3) + { + if(lua_isboolean(L, -1)) + canWrite = popBoolean(L); + else + length = popNumber(L); + } + + std::string text; + if(params > 2) + { + if(lua_isboolean(L, -1)) + canWrite = popBoolean(L); + else + text = popString(L); + } + uint32_t itemId = popNumber(L); ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) { - player->setWriteItem(NULL, 0); - player->sendTextWindow(itemId, text); + Item* item = Item::CreateItem(itemId); + if(length < 0) + length = item->getMaxWriteLength(); + + player->transferContainer.__addThing(NULL, item); + if(text.size()) + { + item->setText(text); + length = std::max((int32_t)text.size(), length); + } + + player->setWriteItem(item, length); + player->transferContainer.setParent(player); + + player->sendTextWindow(item, length, canWrite); lua_pushboolean(L, true); } else @@ -4087,10 +4616,11 @@ int32_t LuaScriptInterface::luaDoShowTextDialog(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoDecayItem(lua_State* L) +int32_t LuaInterface::luaDoDecayItem(lua_State* L) { //doDecayItem(uid) //Note: to stop decay set decayTo = 0 in items.xml @@ -4105,21 +4635,17 @@ int32_t LuaScriptInterface::luaDoDecayItem(lua_State* L) errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetThingFromPos(lua_State* L) +int32_t LuaInterface::luaGetThingFromPosition(lua_State* L) { - //getThingFromPos(pos[, displayError = true]) - //Note: + //getThingFromPosition(pos) + // Note: // stackpos = 255- top thing (movable item or creature) // stackpos = 254- magic field // stackpos = 253- top creature - - bool displayError = true; - if(lua_gettop(L) > 1) - displayError = popNumber(L); - PositionEx pos; popPosition(L, pos); @@ -4132,7 +4658,7 @@ int32_t LuaScriptInterface::luaGetThingFromPos(lua_State* L) if(!(thing = tile->getTopCreature())) { Item* item = tile->getTopDownItem(); - if(item && item->isMoveable()) + if(item && item->isMovable()) thing = item; } } @@ -4151,14 +4677,12 @@ int32_t LuaScriptInterface::luaGetThingFromPos(lua_State* L) return 1; } - if(displayError) - errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); - + errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); pushThing(L, NULL, 0); return 1; } -int32_t LuaScriptInterface::luaGetTileItemById(lua_State* L) +int32_t LuaInterface::luaGetTileItemById(lua_State* L) { //getTileItemById(pos, itemId[, subType = -1]) ScriptEnviroment* env = getEnv(); @@ -4189,7 +4713,7 @@ int32_t LuaScriptInterface::luaGetTileItemById(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetTileItemByType(lua_State* L) +int32_t LuaInterface::luaGetTileItemByType(lua_State* L) { //getTileItemByType(pos, type) uint32_t rType = (uint32_t)popNumber(L); @@ -4248,6 +4772,13 @@ int32_t LuaScriptInterface::luaGetTileItemByType(lua_State* L) break; } + case ITEM_TYPE_DEPOT: + { + if(!tile->hasFlag(TILESTATE_DEPOT)) + found = false; + + break; + } default: break; } @@ -4259,29 +4790,27 @@ int32_t LuaScriptInterface::luaGetTileItemByType(lua_State* L) } ScriptEnviroment* env = getEnv(); - Item* item = NULL; - for(uint32_t i = 0; i < tile->getThingCount(); ++i) + if(TileItemVector* items = tile->getItemList()) { - if(!(item = tile->__getThing(i)->getItem())) - continue; - - if(Item::items[item->getID()].type != (ItemTypes_t)rType) - continue; + for(ItemVector::iterator it = items->begin(); it != items->end(); ++it) + { + if(Item::items[(*it)->getID()].type != (ItemTypes_t)rType) + continue; - pushThing(L, item, env->addThing(item)); - return 1; + pushThing(L, *it, env->addThing(*it)); + return 1; + } } pushThing(L, NULL, 0); return 1; } -int32_t LuaScriptInterface::luaGetTileThingByPos(lua_State* L) +int32_t LuaInterface::luaGetTileThingByPos(lua_State* L) { //getTileThingByPos(pos) PositionEx pos; popPosition(L, pos); - ScriptEnviroment* env = getEnv(); Tile* tile = g_game.getTile(pos.x, pos.y, pos.z); @@ -4316,7 +4845,7 @@ int32_t LuaScriptInterface::luaGetTileThingByPos(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetTopCreature(lua_State* L) +int32_t LuaInterface::luaGetTopCreature(lua_State* L) { //getTopCreature(pos) PositionEx pos; @@ -4341,9 +4870,9 @@ int32_t LuaScriptInterface::luaGetTopCreature(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCreateItem(lua_State* L) +int32_t LuaInterface::luaDoCreateItem(lua_State* L) { - //doCreateItem(itemid[, type/count], pos) + //doCreateItem(itemid[, type/count = 1], pos) //Returns uid of the created item, only works on tiles. PositionEx pos; popPosition(L, pos); @@ -4352,68 +4881,83 @@ int32_t LuaScriptInterface::luaDoCreateItem(lua_State* L) if(lua_gettop(L) > 1) count = popNumber(L); - uint32_t itemId = popNumber(L); + const ItemType& it = Item::items[popNumber(L)]; ScriptEnviroment* env = getEnv(); Tile* tile = g_game.getTile(pos); if(!tile) { - errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); - lua_pushboolean(L, false); + if(it.group == ITEM_GROUP_GROUND) + { + Item* item = Item::CreateItem(it.id); + tile = IOMap::createTile(item, NULL, pos.x, pos.y, pos.z); + + g_game.setTile(tile); + lua_pushnumber(L, env->addThing(item)); + } + else + { + errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); + lua_pushboolean(L, false); + } + return 1; } - const ItemType& it = Item::items[itemId]; int32_t itemCount = 1, subType = 1; if(it.hasSubType()) { if(it.stackable) - itemCount = (int32_t)std::ceil((float)count / 100); + itemCount = (int32_t)std::ceil(count / 100.); subType = count; } else - itemCount = std::max((uint32_t)1, count); + itemCount = std::max(1U, count); + uint32_t ret = 0; + Item* newItem = NULL; while(itemCount > 0) { int32_t stackCount = std::min(100, subType); - Item* newItem = Item::CreateItem(itemId, stackCount); - if(!newItem) + if(!(newItem = Item::CreateItem(it.id, stackCount))) { errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND)); lua_pushboolean(L, false); - return 1; + return ++ret; } if(it.stackable) subType -= stackCount; - ReturnValue ret = g_game.internalAddItem(NULL, tile, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT); - if(ret != RET_NOERROR) + uint32_t dummy = 0; + Item* stackItem = NULL; + if(g_game.internalAddItem(NULL, tile, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT, false, dummy, &stackItem) != RET_NOERROR) { delete newItem; lua_pushboolean(L, false); - return 1; + return ++ret; } - --itemCount; - if(itemCount) - continue; - + ++ret; if(newItem->getParent()) lua_pushnumber(L, env->addThing(newItem)); - else //stackable item stacked with existing object, newItem will be released + else if(stackItem) + lua_pushnumber(L, env->addThing(stackItem)); + else lua_pushnil(L); - return 1; + --itemCount; } + if(ret) + return ret; + lua_pushnil(L); return 1; } -int32_t LuaScriptInterface::luaDoCreateItemEx(lua_State* L) +int32_t LuaInterface::luaDoCreateItemEx(lua_State* L) { //doCreateItemEx(itemid[, count/subType]) uint32_t count = 0; @@ -4440,18 +4984,18 @@ int32_t LuaScriptInterface::luaDoCreateItemEx(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCreateTeleport(lua_State* L) +int32_t LuaInterface::luaDoCreateTeleport(lua_State* L) { - //doCreateTeleport(itemid, toPosition, fromPosition) - PositionEx createPos; - popPosition(L, createPos); - PositionEx toPos; - popPosition(L, toPos); + //doCreateTeleport(itemid, destination, position) + PositionEx position; + popPosition(L, position); + PositionEx destination; + popPosition(L, destination); uint32_t itemId = (uint32_t)popNumber(L); ScriptEnviroment* env = getEnv(); - Tile* tile = g_game.getMap()->getTile(createPos); + Tile* tile = g_game.getMap()->getTile(position); if(!tile) { errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); @@ -4464,44 +5008,76 @@ int32_t LuaScriptInterface::luaDoCreateTeleport(lua_State* L) if(!newTeleport) { delete newItem; + errorEx("Item " + asString(itemId) + " is not a teleport."); lua_pushboolean(L, false); return 1; } - newTeleport->setDestination(toPos); - if(g_game.internalAddItem(NULL, tile, newTeleport, INDEX_WHEREEVER, FLAG_NOLIMIT) != RET_NOERROR) + uint32_t dummy = 0; + Item* stackItem = NULL; + if(g_game.internalAddItem(NULL, tile, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT, false, dummy, &stackItem) != RET_NOERROR) { delete newItem; lua_pushboolean(L, false); return 1; } + newTeleport->setDestination(destination); if(newItem->getParent()) lua_pushnumber(L, env->addThing(newItem)); - else //stackable item stacked with existing object, newItem will be released + else if(stackItem) + lua_pushnumber(L, env->addThing(stackItem)); + else lua_pushnil(L); return 1; } -int32_t LuaScriptInterface::luaGetCreatureStorage(lua_State* L) +int32_t LuaInterface::luaGetCreatureStorageList(lua_State* L) +{ + //getCreatureStorageList(cid) + ScriptEnviroment* env = getEnv(); + + if(Creature* creature = env->getCreatureByUID(popNumber(L))) + { + StorageMap::const_iterator it = creature->getStorageBegin(); + lua_newtable(L); + for(uint32_t i = 1; it != creature->getStorageEnd(); ++i, ++it) + { + lua_pushnumber(L, i); + lua_pushstring(L, it->first.c_str()); + pushTable(L); + } + } + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + + return 1; +} + +int32_t LuaInterface::luaGetCreatureStorage(lua_State* L) { //getCreatureStorage(cid, key) - uint32_t key = popNumber(L); + std::string key = popString(L); ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) { std::string strValue; - if(creature->getStorage(key, strValue)) + if(!creature->getStorage(key, strValue)) { - int32_t intValue = atoi(strValue.c_str()); - if(intValue || strValue == "0") - lua_pushnumber(L, intValue); - else - lua_pushstring(L, strValue.c_str()); + lua_pushnumber(L, -1); + lua_pushnil(L); + return 2; } + + int32_t intValue = atoi(strValue.c_str()); + if(intValue || strValue == "0") + lua_pushnumber(L, intValue); else - lua_pushnumber(L, -1); + lua_pushstring(L, strValue.c_str()); } else { @@ -4512,32 +5088,32 @@ int32_t LuaScriptInterface::luaGetCreatureStorage(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCreatureSetStorage(lua_State* L) +int32_t LuaInterface::luaDoCreatureSetStorage(lua_State* L) { //doCreatureSetStorage(cid, key[, value]) std::string value; - bool nil = true; + bool tmp = true; if(lua_gettop(L) > 2) { if(!lua_isnil(L, -1)) { value = popString(L); - nil = false; + tmp = false; } else lua_pop(L, 1); } - uint32_t key = popNumber(L); + std::string key = popString(L); ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) { - if(!nil) - nil = creature->setStorage(key, value); + if(!tmp) + tmp = creature->setStorage(key, value); else creature->eraseStorage(key); - lua_pushboolean(L, nil); + lua_pushboolean(L, tmp); } else { @@ -4548,7 +5124,7 @@ int32_t LuaScriptInterface::luaDoCreatureSetStorage(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetTileInfo(lua_State* L) +int32_t LuaInterface::luaGetTileInfo(lua_State* L) { //getTileInfo(pos) PositionEx pos; @@ -4559,20 +5135,35 @@ int32_t LuaScriptInterface::luaGetTileInfo(lua_State* L) pushThing(L, tile->ground, env->addThing(tile->ground)); setFieldBool(L, "protection", tile->hasFlag(TILESTATE_PROTECTIONZONE)); - setFieldBool(L, "nopvp", tile->hasFlag(TILESTATE_NOPVPZONE)); - setFieldBool(L, "nologout", tile->hasFlag(TILESTATE_NOLOGOUT)); - setFieldBool(L, "pvp", tile->hasFlag(TILESTATE_PVPZONE)); + setFieldBool(L, "optional", tile->hasFlag(TILESTATE_OPTIONALZONE)); + setFieldBool(L, "hardcore", tile->hasFlag(TILESTATE_HARDCOREZONE)); + setFieldBool(L, "noLogout", tile->hasFlag(TILESTATE_NOLOGOUT)); setFieldBool(L, "refresh", tile->hasFlag(TILESTATE_REFRESH)); setFieldBool(L, "trashed", tile->hasFlag(TILESTATE_TRASHED)); - setFieldBool(L, "house", tile->hasFlag(TILESTATE_HOUSE)); - setFieldBool(L, "bed", tile->hasFlag(TILESTATE_BED)); + setFieldBool(L, "magicField", tile->hasFlag(TILESTATE_MAGICFIELD)); + setFieldBool(L, "trashHolder", tile->hasFlag(TILESTATE_TRASHHOLDER)); + setFieldBool(L, "mailbox", tile->hasFlag(TILESTATE_MAILBOX)); setFieldBool(L, "depot", tile->hasFlag(TILESTATE_DEPOT)); + setFieldBool(L, "bed", tile->hasFlag(TILESTATE_BED)); + + createTable(L, "floorChange"); + for(int32_t i = CHANGE_FIRST; i <= CHANGE_LAST; ++i) + { + lua_pushnumber(L, i); + lua_pushboolean(L, tile->floorChange((FloorChange_t)i)); + pushTable(L); + } + + pushTable(L); + setFieldBool(L, "teleport", tile->hasFlag(TILESTATE_TELEPORT)); setField(L, "things", tile->getThingCount()); setField(L, "creatures", tile->getCreatureCount()); setField(L, "items", tile->getItemCount()); setField(L, "topItems", tile->getTopItemCount()); setField(L, "downItems", tile->getDownItemCount()); + if(House* house = tile->getHouse()) + setField(L, "house", house->getId()); } else { @@ -4583,9 +5174,9 @@ int32_t LuaScriptInterface::luaGetTileInfo(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetHouseFromPos(lua_State* L) +int32_t LuaInterface::luaGetHouseFromPosition(lua_State* L) { - //getHouseFromPos(pos) + //getHouseFromPosition(pos) PositionEx pos; popPosition(L, pos); @@ -4615,12 +5206,16 @@ int32_t LuaScriptInterface::luaGetHouseFromPos(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCreateMonster(lua_State* L) +int32_t LuaInterface::luaDoCreateMonster(lua_State* L) { - //doCreateMonster(name, pos[, displayError = true]) - bool displayError = true; - if(lua_gettop(L) > 2) - displayError = popNumber(L); + //doCreateMonster(name, pos[, extend = false[, force = false]]) + bool force = false, extend = false; + int32_t params = lua_gettop(L); + if(params > 3) + force = popBoolean(L); + + if(params > 2) + extend = popBoolean(L); PositionEx pos; popPosition(L, pos); @@ -4629,20 +5224,17 @@ int32_t LuaScriptInterface::luaDoCreateMonster(lua_State* L) Monster* monster = Monster::createMonster(name.c_str()); if(!monster) { - if(displayError) - errorEx("Monster with name '" + name + "' not found"); - + errorEx("Monster with name '" + name + "' not found"); lua_pushboolean(L, false); return 1; } - if(!g_game.placeCreature(monster, pos)) + if(!g_game.placeCreature(monster, pos, extend, force)) { delete monster; - if(displayError) - errorEx("Cannot create monster: " + name); + errorEx("Cannot create monster: " + name); - lua_pushboolean(L, true); + lua_pushboolean(L, false); return 1; } @@ -4651,23 +5243,17 @@ int32_t LuaScriptInterface::luaDoCreateMonster(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCreateNpc(lua_State* L) +int32_t LuaInterface::luaDoCreateNpc(lua_State* L) { - //doCreateNpc(name, pos[, displayError = true]) - bool displayError = true; - if(lua_gettop(L) > 2) - displayError = popNumber(L); - + //doCreateNpc(name, pos) PositionEx pos; popPosition(L, pos); - std::string name = popString(L); + Npc* npc = Npc::createNpc(name.c_str()); if(!npc) { - if(displayError) - errorEx("Npc with name '" + name + "' not found"); - + errorEx("Npc with name '" + name + "' not found"); lua_pushboolean(L, false); return 1; } @@ -4675,8 +5261,7 @@ int32_t LuaScriptInterface::luaDoCreateNpc(lua_State* L) if(!g_game.placeCreature(npc, pos)) { delete npc; - if(displayError) - errorEx("Cannot create npc: " + name); + errorEx("Cannot create npc: " + name); lua_pushboolean(L, true); //for scripting compatibility return 1; @@ -4687,18 +5272,18 @@ int32_t LuaScriptInterface::luaDoCreateNpc(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoRemoveCreature(lua_State* L) +int32_t LuaInterface::luaDoRemoveCreature(lua_State* L) { //doRemoveCreature(cid[, forceLogout = true]) bool forceLogout = true; if(lua_gettop(L) > 1) - forceLogout = popNumber(L); + forceLogout = popBoolean(L); ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) { if(Player* player = creature->getPlayer()) - player->kickPlayer(true, forceLogout); //Players will get kicked without restrictions + player->kick(true, forceLogout); //Players will get kicked without restrictions else g_game.removeCreature(creature); //Monsters/NPCs will get removed @@ -4709,10 +5294,11 @@ int32_t LuaScriptInterface::luaDoRemoveCreature(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddMoney(lua_State* L) +int32_t LuaInterface::luaDoPlayerAddMoney(lua_State* L) { //doPlayerAddMoney(cid, money) uint64_t money = popNumber(L); @@ -4728,10 +5314,11 @@ int32_t LuaScriptInterface::luaDoPlayerAddMoney(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerRemoveMoney(lua_State* L) +int32_t LuaInterface::luaDoPlayerRemoveMoney(lua_State* L) { //doPlayerRemoveMoney(cid,money) uint64_t money = popNumber(L); @@ -4744,10 +5331,11 @@ int32_t LuaScriptInterface::luaDoPlayerRemoveMoney(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerTransferMoneyTo(lua_State* L) +int32_t LuaInterface::luaDoPlayerTransferMoneyTo(lua_State* L) { //doPlayerTransferMoneyTo(cid, target, money) uint64_t money = popNumber(L); @@ -4761,13 +5349,14 @@ int32_t LuaScriptInterface::luaDoPlayerTransferMoneyTo(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetPzLocked(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetPzLocked(lua_State* L) { //doPlayerSetPzLocked(cid, locked) - bool locked = popNumber(L); + bool locked = popBoolean(L); ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) @@ -4785,10 +5374,11 @@ int32_t LuaScriptInterface::luaDoPlayerSetPzLocked(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetTown(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetTown(lua_State* L) { //doPlayerSetTown(cid, townid) uint32_t townid = (uint32_t)popNumber(L); @@ -4810,10 +5400,11 @@ int32_t LuaScriptInterface::luaDoPlayerSetTown(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetVocation(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetVocation(lua_State* L) { //doPlayerSetVocation(cid, voc) uint32_t voc = popNumber(L); @@ -4829,10 +5420,11 @@ int32_t LuaScriptInterface::luaDoPlayerSetVocation(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetSex(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetSex(lua_State* L) { //doPlayerSetSex(cid, sex) uint32_t newSex = popNumber(L); @@ -4848,18 +5440,19 @@ int32_t LuaScriptInterface::luaDoPlayerSetSex(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddSoul(lua_State* L) +int32_t LuaInterface::luaDoPlayerAddSoul(lua_State* L) { - //doPlayerAddSoul(cid, soul) - int32_t soul = popNumber(L); + //doPlayerAddSoul(cid, amount) + int32_t amount = popNumber(L); ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) { - player->changeSoul(soul); + player->changeSoul(amount); lua_pushboolean(L, true); } else @@ -4867,10 +5460,11 @@ int32_t LuaScriptInterface::luaDoPlayerAddSoul(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetPlayerItemCount(lua_State* L) +int32_t LuaInterface::luaGetPlayerItemCount(lua_State* L) { //getPlayerItemCount(cid, itemid[, subType = -1]) int32_t subType = -1; @@ -4886,36 +5480,21 @@ int32_t LuaScriptInterface::luaGetPlayerItemCount(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } - return 1; -} -int32_t LuaScriptInterface::luaGetPlayerMoney(lua_State* L) -{ - //getPlayerMoney(cid) - ScriptEnviroment* env = getEnv(); - if(Player* player = env->getPlayerByUID(popNumber(L))) - lua_pushnumber(L, g_game.getMoney(player)); - else - { - errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); - lua_pushboolean(L, false); - } return 1; } -int32_t LuaScriptInterface::luaGetHouseInfo(lua_State* L) +int32_t LuaInterface::luaGetHouseInfo(lua_State* L) { - //getHouseInfo(houseId) - bool displayError = true; + //getHouseInfo(houseId[, full = true]) + bool full = true; if(lua_gettop(L) > 1) - displayError = popNumber(L); + full = popBoolean(L); House* house = Houses::getInstance()->getHouse(popNumber(L)); if(!house) { - if(displayError) - errorEx(getError(LUA_ERROR_HOUSE_NOT_FOUND)); - + errorEx(getError(LUA_ERROR_HOUSE_NOT_FOUND)); lua_pushboolean(L, false); return 1; } @@ -4938,14 +5517,48 @@ int32_t LuaScriptInterface::luaGetHouseInfo(lua_State* L) setFieldBool(L, "guildHall", house->isGuild()); setField(L, "size", house->getSize()); - setField(L, "doors", house->getDoorsCount()); - setField(L, "beds", house->getBedsCount()); - setField(L, "tiles", house->getTilesCount()); + + if(full) + { + createTable(L, "doors"); + + HouseDoorList::iterator dit = house->getHouseDoorBegin(); + for(uint32_t i = 1; dit != house->getHouseDoorEnd(); ++dit, ++i) + { + lua_pushnumber(L, i); + pushPosition(L, (*dit)->getPosition(), 0); + pushTable(L); + } + + pushTable(L); + createTable(L, "beds"); + + HouseBedList::iterator bit = house->getHouseBedsBegin(); + for(uint32_t i = 1; bit != house->getHouseBedsEnd(); ++bit, ++i) + { + lua_pushnumber(L, i); + pushPosition(L, (*bit)->getPosition(), 0); + pushTable(L); + } + + pushTable(L); + createTable(L, "tiles"); + + HouseTileList::iterator tit = house->getHouseTileBegin(); + for(uint32_t i = 1; tit != house->getHouseTileEnd(); ++tit, ++i) + { + lua_pushnumber(L, i); + pushPosition(L, (*tit)->getPosition(), 0); + pushTable(L); + } + + pushTable(L); + } return 1; } -int32_t LuaScriptInterface::luaGetHouseAccessList(lua_State* L) +int32_t LuaInterface::luaGetHouseAccessList(lua_State* L) { //getHouseAccessList(houseid, listid) uint32_t listid = popNumber(L); @@ -4962,10 +5575,11 @@ int32_t LuaScriptInterface::luaGetHouseAccessList(lua_State* L) errorEx(getError(LUA_ERROR_HOUSE_NOT_FOUND)); lua_pushnil(L); } + return 1; } -int32_t LuaScriptInterface::luaGetHouseByPlayerGUID(lua_State* L) +int32_t LuaInterface::luaGetHouseByPlayerGUID(lua_State* L) { //getHouseByPlayerGUID(guid) if(House* house = Houses::getInstance()->getHouseByPlayerId(popNumber(L))) @@ -4975,7 +5589,7 @@ int32_t LuaScriptInterface::luaGetHouseByPlayerGUID(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaSetHouseAccessList(lua_State* L) +int32_t LuaInterface::luaSetHouseAccessList(lua_State* L) { //setHouseAccessList(houseid, listid, listtext) std::string list = popString(L); @@ -4991,15 +5605,16 @@ int32_t LuaScriptInterface::luaSetHouseAccessList(lua_State* L) errorEx(getError(LUA_ERROR_HOUSE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaSetHouseOwner(lua_State* L) +int32_t LuaInterface::luaSetHouseOwner(lua_State* L) { - //setHouseOwner(houseId, owner[, clean]) + //setHouseOwner(houseId, owner[, clean = true]) bool clean = true; if(lua_gettop(L) > 2) - clean = popNumber(L); + clean = popBoolean(L); uint32_t owner = popNumber(L); if(House* house = Houses::getInstance()->getHouse(popNumber(L))) @@ -5009,21 +5624,21 @@ int32_t LuaScriptInterface::luaSetHouseOwner(lua_State* L) errorEx(getError(LUA_ERROR_HOUSE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetWorldType(lua_State* L) +int32_t LuaInterface::luaGetWorldType(lua_State* L) { lua_pushnumber(L, (uint32_t)g_game.getWorldType()); return 1; } -int32_t LuaScriptInterface::luaSetWorldType(lua_State* L) +int32_t LuaInterface::luaSetWorldType(lua_State* L) { //setWorldType(type) WorldType_t type = (WorldType_t)popNumber(L); - - if(type >= WORLD_TYPE_FIRST && type <= WORLD_TYPE_LAST) + if(type >= WORLDTYPE_FIRST && type <= WORLDTYPE_LAST) { g_game.setWorldType(type); lua_pushboolean(L, true); @@ -5034,24 +5649,25 @@ int32_t LuaScriptInterface::luaSetWorldType(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetWorldTime(lua_State* L) +int32_t LuaInterface::luaGetWorldTime(lua_State* L) { //getWorldTime() lua_pushnumber(L, g_game.getLightHour()); return 1; } -int32_t LuaScriptInterface::luaGetWorldLight(lua_State* L) +int32_t LuaInterface::luaGetWorldLight(lua_State* L) { //getWorldLight() LightInfo lightInfo; g_game.getWorldLightInfo(lightInfo); + lua_pushnumber(L, lightInfo.level); lua_pushnumber(L, lightInfo.color); - return 1; + return 2; } -int32_t LuaScriptInterface::luaGetWorldCreatures(lua_State* L) +int32_t LuaInterface::luaGetWorldCreatures(lua_State* L) { //getWorldCreatures(type) //0 players, 1 monsters, 2 npcs, 3 all @@ -5079,7 +5695,7 @@ int32_t LuaScriptInterface::luaGetWorldCreatures(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetWorldUpTime(lua_State* L) +int32_t LuaInterface::luaGetWorldUpTime(lua_State* L) { //getWorldUpTime() uint32_t uptime = 0; @@ -5090,7 +5706,7 @@ int32_t LuaScriptInterface::luaGetWorldUpTime(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerLight(lua_State* L) +int32_t LuaInterface::luaGetPlayerLight(lua_State* L) { //getPlayerLight(cid) ScriptEnviroment* env = getEnv(); @@ -5098,18 +5714,39 @@ int32_t LuaScriptInterface::luaGetPlayerLight(lua_State* L) { LightInfo lightInfo; player->getCreatureLight(lightInfo); + lua_pushnumber(L, lightInfo.level); lua_pushnumber(L, lightInfo.color); + return 2; } else + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } +} + +int32_t LuaInterface::luaGetPlayerSoul(lua_State* L) +{ + //getPlayerSoul(cid[, ignoreModifiers = false]) + bool ignoreModifiers = false; + if(lua_gettop(L) > 1) + ignoreModifiers = popBoolean(L); + + ScriptEnviroment* env = getEnv(); + if(const Player* player = env->getPlayerByUID(popNumber(L))) + lua_pushnumber(L, ignoreModifiers ? player->soul : player->getSoul()); + else { errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddExperience(lua_State* L) +int32_t LuaInterface::luaDoPlayerAddExperience(lua_State* L) { //doPlayerAddExperience(cid, amount) int64_t amount = popNumber(L); @@ -5134,10 +5771,11 @@ int32_t LuaScriptInterface::luaDoPlayerAddExperience(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetPlayerSlotItem(lua_State* L) +int32_t LuaInterface::luaGetPlayerSlotItem(lua_State* L) { //getPlayerSlotItem(cid, slot) uint32_t slot = popNumber(L); @@ -5155,15 +5793,16 @@ int32_t LuaScriptInterface::luaGetPlayerSlotItem(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); pushThing(L, NULL, 0); } + return 1; } -int32_t LuaScriptInterface::luaGetPlayerWeapon(lua_State* L) +int32_t LuaInterface::luaGetPlayerWeapon(lua_State* L) { //getPlayerWeapon(cid[, ignoreAmmo = false]) bool ignoreAmmo = false; if(lua_gettop(L) > 1) - ignoreAmmo = popNumber(L); + ignoreAmmo = popBoolean(L); ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) @@ -5178,10 +5817,11 @@ int32_t LuaScriptInterface::luaGetPlayerWeapon(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushnil(L); } + return 1; } -int32_t LuaScriptInterface::luaGetPlayerItemById(lua_State* L) +int32_t LuaInterface::luaGetPlayerItemById(lua_State* L) { //getPlayerItemById(cid, deepSearch, itemId[, subType = -1]) ScriptEnviroment* env = getEnv(); @@ -5191,7 +5831,7 @@ int32_t LuaScriptInterface::luaGetPlayerItemById(lua_State* L) subType = (int32_t)popNumber(L); int32_t itemId = (int32_t)popNumber(L); - bool deepSearch = popNumber(L); + bool deepSearch = popBoolean(L); Player* player = env->getPlayerByUID(popNumber(L)); if(!player) @@ -5212,30 +5852,30 @@ int32_t LuaScriptInterface::luaGetPlayerItemById(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetThing(lua_State* L) +int32_t LuaInterface::luaGetThing(lua_State* L) { - //getThing(uid) - uint32_t uid = popNumber(L); + //getThing(uid[, recursive = RECURSE_FIRST]) + Recursive_t recursive = RECURSE_FIRST; + if(lua_gettop(L) > 1) + recursive = (Recursive_t)popNumber(L); + uint32_t uid = popNumber(L); ScriptEnviroment* env = getEnv(); if(Thing* thing = env->getThingByUID(uid)) - pushThing(L, thing, uid); + pushThing(L, thing, uid, recursive); else { errorEx(getError(LUA_ERROR_THING_NOT_FOUND)); pushThing(L, NULL, 0); } + return 1; } -int32_t LuaScriptInterface::luaDoTileQueryAdd(lua_State* L) +int32_t LuaInterface::luaDoTileQueryAdd(lua_State* L) { - //doTileQueryAdd(uid, pos[, flags[, displayError = true]]) + //doTileQueryAdd(uid, pos[, flags]) uint32_t flags = 0, params = lua_gettop(L); - bool displayError = true; - if(params > 3) - displayError = popNumber(L); - if(params > 2) flags = popNumber(L); @@ -5247,9 +5887,7 @@ int32_t LuaScriptInterface::luaDoTileQueryAdd(lua_State* L) Tile* tile = g_game.getTile(pos); if(!tile) { - if(displayError) - errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); - + errorEx(getError(LUA_ERROR_TILE_NOT_FOUND)); lua_pushnumber(L, (uint32_t)RET_NOTPOSSIBLE); return 1; } @@ -5257,9 +5895,7 @@ int32_t LuaScriptInterface::luaDoTileQueryAdd(lua_State* L) Thing* thing = env->getThingByUID(uid); if(!thing) { - if(displayError) - errorEx(getError(LUA_ERROR_THING_NOT_FOUND)); - + errorEx(getError(LUA_ERROR_THING_NOT_FOUND)); lua_pushnumber(L, (uint32_t)RET_NOTPOSSIBLE); return 1; } @@ -5268,7 +5904,7 @@ int32_t LuaScriptInterface::luaDoTileQueryAdd(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoItemRaidUnref(lua_State* L) +int32_t LuaInterface::luaDoItemRaidUnref(lua_State* L) { //doItemRaidUnref(uid) ScriptEnviroment* env = getEnv(); @@ -5288,10 +5924,11 @@ int32_t LuaScriptInterface::luaDoItemRaidUnref(lua_State* L) errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetThingPosition(lua_State* L) +int32_t LuaInterface::luaGetThingPosition(lua_State* L) { //getThingPosition(uid) ScriptEnviroment* env = getEnv(); @@ -5313,13 +5950,13 @@ int32_t LuaScriptInterface::luaGetThingPosition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaCreateCombatObject(lua_State* L) +int32_t LuaInterface::luaCreateCombatObject(lua_State* L) { //createCombatObject() ScriptEnviroment* env = getEnv(); if(env->getScriptId() != EVENT_ID_LOADING) { - errorEx("This function can only be used while loading the script."); + errorEx("This function can only be used while loading the script"); lua_pushboolean(L, false); return 1; } @@ -5336,10 +5973,14 @@ int32_t LuaScriptInterface::luaCreateCombatObject(lua_State* L) return 1; } -bool LuaScriptInterface::getArea(lua_State* L, std::list& list, uint32_t& rows) +bool LuaInterface::getArea(lua_State* L, std::list& list, uint32_t& rows) { rows = 0; - uint32_t i = 0; + if(!lua_istable(L, -1)) + { + errorEx("Object on the stack is not a table"); + return false; + } lua_pushnil(L); while(lua_next(L, -2)) @@ -5348,83 +5989,77 @@ bool LuaScriptInterface::getArea(lua_State* L, std::list& list, uint32 while(lua_next(L, -2)) { list.push_back((uint32_t)lua_tonumber(L, -1)); - lua_pop(L, 1); //removes value, keeps key for next iteration - ++i; + lua_pop(L, 1); } - lua_pop(L, 1); //removes value, keeps key for next iteration + lua_pop(L, 1); ++rows; - i = 0; } lua_pop(L, 1); - return rows; + return (rows != 0); } -int32_t LuaScriptInterface::luaCreateCombatArea(lua_State* L) +int32_t LuaInterface::luaCreateCombatArea(lua_State* L) { - //createCombatArea( {area}[, {extArea}]) + //createCombatArea({area}[, {extArea}]) ScriptEnviroment* env = getEnv(); if(env->getScriptId() != EVENT_ID_LOADING) { - errorEx("This function can only be used while loading the script."); + errorEx("This function can only be used while loading the script"); lua_pushboolean(L, false); return 1; } CombatArea* area = new CombatArea; - if(lua_gettop(L) > 1) + if(lua_gettop(L) > 1) //has extra parameter with diagonal area information { - //has extra parameter with diagonal area information - uint32_t rowsExtArea; + uint32_t rowsExtArea = 0; std::list listExtArea; - - getArea(L, listExtArea, rowsExtArea); - /*setup all possible rotations*/ - area->setupExtArea(listExtArea, rowsExtArea); - } - - if(lua_isnoneornil(L, -1)) //prevent crash - { - lua_pop(L, 2); - lua_pushboolean(L, false); - return 1; + if(getArea(L, listExtArea, rowsExtArea)) + area->setupExtArea(listExtArea, rowsExtArea); } uint32_t rowsArea = 0; std::list listArea; - getArea(L, listArea, rowsArea); + if(getArea(L, listArea, rowsArea)) + { + area->setupArea(listArea, rowsArea); + lua_pushnumber(L, env->addCombatArea(area)); + } + else + lua_pushboolean(L, false); - area->setupArea(listArea, rowsArea); - lua_pushnumber(L, env->addCombatArea(area)); return 1; } -int32_t LuaScriptInterface::luaCreateConditionObject(lua_State* L) +int32_t LuaInterface::luaCreateConditionObject(lua_State* L) { - //createConditionObject(type[, ticks[, buff[, subId]]]) + //createConditionObject(type[, ticks = 0[, buff = false[, subId = 0[, conditionId = CONDITIONID_COMBAT]]]]) + int32_t conditionId = CONDITIONID_COMBAT; uint32_t params = lua_gettop(L), subId = 0; + if(params > 4) + conditionId = popNumber(L); + if(params > 3) subId = popNumber(L); bool buff = false; if(params > 2) - buff = popNumber(L); + buff = popBoolean(L); int32_t ticks = 0; if(params > 1) ticks = popNumber(L); ScriptEnviroment* env = getEnv(); - if(env->getScriptId() != EVENT_ID_LOADING) + if(Condition* condition = Condition::createCondition((ConditionId_t)conditionId, (ConditionType_t)popNumber(L), ticks, 0, buff, subId)) { - errorEx("This function can only be used while loading the script."); - lua_pushboolean(L, false); - return 1; + if(env->getScriptId() != EVENT_ID_LOADING) + lua_pushnumber(L, env->addTempConditionObject(condition)); + else + lua_pushnumber(L, env->addConditionObject(condition)); } - - if(Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, (ConditionType_t)popNumber(L), ticks, 0, buff, subId)) - lua_pushnumber(L, env->addConditionObject(condition)); else { errorEx(getError(LUA_ERROR_CONDITION_NOT_FOUND)); @@ -5434,14 +6069,14 @@ int32_t LuaScriptInterface::luaCreateConditionObject(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaSetCombatArea(lua_State* L) +int32_t LuaInterface::luaSetCombatArea(lua_State* L) { //setCombatArea(combat, area) uint32_t areaId = popNumber(L); ScriptEnviroment* env = getEnv(); if(env->getScriptId() != EVENT_ID_LOADING) { - errorEx("This function can only be used while loading the script."); + errorEx("This function can only be used while loading the script"); lua_pushboolean(L, false); return 1; } @@ -5467,17 +6102,15 @@ int32_t LuaScriptInterface::luaSetCombatArea(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaSetCombatCondition(lua_State* L) +int32_t LuaInterface::luaSetCombatCondition(lua_State* L) { - //setCombatCondition(combat, condition) + //setCombatCondition(combat, condition[, loaded]) + bool loaded = true; + if(lua_gettop(L) > 2) + loaded = popBoolean(L); + uint32_t conditionId = popNumber(L); ScriptEnviroment* env = getEnv(); - if(env->getScriptId() != EVENT_ID_LOADING) - { - errorEx("This function can only be used while loading the script."); - lua_pushboolean(L, false); - return 1; - } Combat* combat = env->getCombatObject(popNumber(L)); if(!combat) @@ -5487,7 +6120,7 @@ int32_t LuaScriptInterface::luaSetCombatCondition(lua_State* L) return 1; } - const Condition* condition = env->getConditionObject(conditionId); + const Condition* condition = env->getConditionObject(conditionId, loaded); if(!condition) { errorEx(getError(LUA_ERROR_CONDITION_NOT_FOUND)); @@ -5500,7 +6133,7 @@ int32_t LuaScriptInterface::luaSetCombatCondition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaSetCombatParam(lua_State* L) +int32_t LuaInterface::luaSetCombatParam(lua_State* L) { //setCombatParam(combat, key, value) uint32_t value = popNumber(L); @@ -5509,7 +6142,7 @@ int32_t LuaScriptInterface::luaSetCombatParam(lua_State* L) ScriptEnviroment* env = getEnv(); if(env->getScriptId() != EVENT_ID_LOADING) { - errorEx("This function can only be used while loading the script."); + errorEx("This function can only be used while loading the script"); lua_pushboolean(L, false); return 1; } @@ -5525,24 +6158,22 @@ int32_t LuaScriptInterface::luaSetCombatParam(lua_State* L) combat->setParam(key, value); lua_pushboolean(L, true); } + return 1; } -int32_t LuaScriptInterface::luaSetConditionParam(lua_State* L) +int32_t LuaInterface::luaSetConditionParam(lua_State* L) { - //setConditionParam(condition, key, value) - int32_t value = (int32_t)popNumber(L); - ConditionParam_t key = (ConditionParam_t)popNumber(L); + //setConditionParam(condition, key, value[, loaded]) + bool loaded = true; + if(lua_gettop(L) > 3) + loaded = popBoolean(L); + int32_t value = popNumber(L); ScriptEnviroment* env = getEnv(); - if(env->getScriptId() != EVENT_ID_LOADING) - { - errorEx("This function can only be used while loading the script."); - lua_pushboolean(L, false); - return 1; - } - if(Condition* condition = env->getConditionObject(popNumber(L))) + ConditionParam_t key = (ConditionParam_t)popNumber(L); + if(Condition* condition = env->getConditionObject(popNumber(L), loaded)) { condition->setParam(key, value); lua_pushboolean(L, true); @@ -5556,19 +6187,16 @@ int32_t LuaScriptInterface::luaSetConditionParam(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaAddDamageCondition(lua_State* L) +int32_t LuaInterface::luaAddDamageCondition(lua_State* L) { - //addDamageCondition(condition, rounds, time, value) + //addDamageCondition(condition, rounds, time, value[, loaded]) + bool loaded = true; + if(lua_gettop(L) > 4) + loaded = popBoolean(L); + int32_t value = popNumber(L), time = popNumber(L), rounds = popNumber(L); ScriptEnviroment* env = getEnv(); - if(env->getScriptId() != EVENT_ID_LOADING) - { - errorEx("This function can only be used while loading the script."); - lua_pushboolean(L, false); - return 1; - } - - if(ConditionDamage* condition = dynamic_cast(env->getConditionObject(popNumber(L)))) + if(ConditionDamage* condition = dynamic_cast(env->getConditionObject(popNumber(L), loaded))) { condition->addDamage(rounds, time, value); lua_pushboolean(L, true); @@ -5582,19 +6210,16 @@ int32_t LuaScriptInterface::luaAddDamageCondition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaAddOutfitCondition(lua_State* L) +int32_t LuaInterface::luaAddOutfitCondition(lua_State* L) { - //addOutfitCondition(condition, outfit) + //addOutfitCondition(condition, outfit[, loaded]) + bool loaded = true; + if(lua_gettop(L) > 2) + loaded = popBoolean(L); + Outfit_t outfit = popOutfit(L); ScriptEnviroment* env = getEnv(); - if(env->getScriptId() != EVENT_ID_LOADING) - { - errorEx("This function can only be used while loading the script."); - lua_pushboolean(L, false); - return 1; - } - - if(ConditionOutfit* condition = dynamic_cast(env->getConditionObject(popNumber(L)))) + if(ConditionOutfit* condition = dynamic_cast(env->getConditionObject(popNumber(L), loaded))) { condition->addOutfit(outfit); lua_pushboolean(L, true); @@ -5608,7 +6233,7 @@ int32_t LuaScriptInterface::luaAddOutfitCondition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaSetCombatCallBack(lua_State* L) +int32_t LuaInterface::luaSetCombatCallBack(lua_State* L) { //setCombatCallBack(combat, key, functionName) std::string function = popString(L); @@ -5617,7 +6242,7 @@ int32_t LuaScriptInterface::luaSetCombatCallBack(lua_State* L) ScriptEnviroment* env = getEnv(); if(env->getScriptId() != EVENT_ID_LOADING) { - errorEx("This function can only be used while loading the script."); + errorEx("This function can only be used while loading the script"); lua_pushboolean(L, false); return 1; } @@ -5630,16 +6255,13 @@ int32_t LuaScriptInterface::luaSetCombatCallBack(lua_State* L) return 1; } - LuaScriptInterface* interface = env->getInterface(); + LuaInterface* interface = env->getInterface(); combat->setCallback(key); CallBack* callback = combat->getCallback(key); if(!callback) { - std::stringstream ss; - ss << key; - - errorEx(ss.str() + " is not a valid callback key."); + errorEx(asString(key) + " is not a valid callback key"); lua_pushboolean(L, false); return 1; } @@ -5655,13 +6277,13 @@ int32_t LuaScriptInterface::luaSetCombatCallBack(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaSetCombatFormula(lua_State* L) +int32_t LuaInterface::luaSetCombatFormula(lua_State* L) { //setCombatFormula(combat, type, mina, minb, maxa, maxb[, minl, maxl[, minm, maxm[, minc[, maxc]]]]) ScriptEnviroment* env = getEnv(); if(env->getScriptId() != EVENT_ID_LOADING) { - errorEx("This function can only be used while loading the script."); + errorEx("This function can only be used while loading the script"); lua_pushboolean(L, false); return 1; } @@ -5704,21 +6326,17 @@ int32_t LuaScriptInterface::luaSetCombatFormula(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaSetConditionFormula(lua_State* L) +int32_t LuaInterface::luaSetConditionFormula(lua_State* L) { - //setConditionFormula(condition, mina, minb, maxa, maxb) - ScriptEnviroment* env = getEnv(); - if(env->getScriptId() != EVENT_ID_LOADING) - { - errorEx("This function can only be used while loading the script."); - lua_pushboolean(L, false); - return 1; - } + //setConditionFormula(condition, mina, minb, maxa, maxb[, loaded]) + bool loaded = true; + if(lua_gettop(L) > 5) + loaded = popBoolean(L); double maxb = popFloatNumber(L), maxa = popFloatNumber(L), minb = popFloatNumber(L), mina = popFloatNumber(L); - - if(ConditionSpeed* condition = dynamic_cast(env->getConditionObject(popNumber(L)))) + ScriptEnviroment* env = getEnv(); + if(ConditionSpeed* condition = dynamic_cast(env->getConditionObject(popNumber(L), loaded))) { condition->setFormulaVars(mina, minb, maxa, maxb); lua_pushboolean(L, true); @@ -5732,7 +6350,7 @@ int32_t LuaScriptInterface::luaSetConditionFormula(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCombat(lua_State* L) +int32_t LuaInterface::luaDoCombat(lua_State* L) { //doCombat(cid, combat, param) ScriptEnviroment* env = getEnv(); @@ -5822,7 +6440,7 @@ int32_t LuaScriptInterface::luaDoCombat(lua_State* L) { errorEx(getError(LUA_ERROR_VARIANT_UNKNOWN)); lua_pushboolean(L, false); - return 1; + break; } } @@ -5830,9 +6448,13 @@ int32_t LuaScriptInterface::luaDoCombat(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCombatAreaHealth(lua_State* L) +int32_t LuaInterface::luaDoCombatAreaHealth(lua_State* L) { - //doCombatAreaHealth(cid, type, pos, area, min, max, effect) + //doCombatAreaHealth(cid, type, pos, area, min, max, effect[, aggressive]) + bool aggressive = true; + if(lua_gettop(L) > 7) // shouldn't it be enough if we check only is conditionType == CONDITION_HEALING? + aggressive = popBoolean(L); + MagicEffect_t effect = (MagicEffect_t)popNumber(L); int32_t maxChange = (int32_t)popNumber(L), minChange = (int32_t)popNumber(L); uint32_t areaId = popNumber(L); @@ -5861,6 +6483,7 @@ int32_t LuaScriptInterface::luaDoCombatAreaHealth(lua_State* L) CombatParams params; params.combatType = combatType; params.effects.impact = effect; + params.isAggressive = aggressive; Combat::doCombatHealth(creature, pos, area, minChange, maxChange, params); lua_pushboolean(L, true); @@ -5874,9 +6497,13 @@ int32_t LuaScriptInterface::luaDoCombatAreaHealth(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L) +int32_t LuaInterface::luaDoTargetCombatHealth(lua_State* L) { - //doTargetCombatHealth(cid, target, type, min, max, effect) + //doTargetCombatHealth(cid, target, type, min, max, effect[, aggressive]) + bool aggressive = true; + if(lua_gettop(L) > 6) // shouldn't it be enough if we check only is conditionType == CONDITION_HEALING? + aggressive = popBoolean(L); + MagicEffect_t effect = (MagicEffect_t)popNumber(L); int32_t maxChange = (int32_t)popNumber(L), minChange = (int32_t)popNumber(L); @@ -5901,6 +6528,7 @@ int32_t LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L) CombatParams params; params.combatType = combatType; params.effects.impact = effect; + params.isAggressive = aggressive; Combat::doCombatHealth(creature, target, minChange, maxChange, params); lua_pushboolean(L, true); @@ -5914,9 +6542,13 @@ int32_t LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCombatAreaMana(lua_State* L) +int32_t LuaInterface::luaDoCombatAreaMana(lua_State* L) { - //doCombatAreaMana(cid, pos, area, min, max, effect) + //doCombatAreaMana(cid, pos, area, min, max, effect[, aggressive]) + bool aggressive = true; + if(lua_gettop(L) > 6) + aggressive = popBoolean(L); + MagicEffect_t effect = (MagicEffect_t)popNumber(L); int32_t maxChange = (int32_t)popNumber(L), minChange = (int32_t)popNumber(L); uint32_t areaId = popNumber(L); @@ -5942,6 +6574,7 @@ int32_t LuaScriptInterface::luaDoCombatAreaMana(lua_State* L) { CombatParams params; params.effects.impact = effect; + params.isAggressive = aggressive; Combat::doCombatMana(creature, pos, area, minChange, maxChange, params); lua_pushboolean(L, true); @@ -5955,9 +6588,13 @@ int32_t LuaScriptInterface::luaDoCombatAreaMana(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoTargetCombatMana(lua_State* L) +int32_t LuaInterface::luaDoTargetCombatMana(lua_State* L) { - //doTargetCombatMana(cid, target, min, max, effect) + //doTargetCombatMana(cid, target, min, max, effect[, aggressive]) + bool aggressive = true; + if(lua_gettop(L) > 5) + aggressive = popBoolean(L); + MagicEffect_t effect = (MagicEffect_t)popNumber(L); int32_t maxChange = (int32_t)popNumber(L), minChange = (int32_t)popNumber(L); uint32_t targetCid = popNumber(L), cid = popNumber(L); @@ -5978,6 +6615,7 @@ int32_t LuaScriptInterface::luaDoTargetCombatMana(lua_State* L) { CombatParams params; params.effects.impact = effect; + params.isAggressive = aggressive; Combat::doCombatMana(creature, target, minChange, maxChange, params); lua_pushboolean(L, true); @@ -5991,9 +6629,13 @@ int32_t LuaScriptInterface::luaDoTargetCombatMana(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCombatAreaCondition(lua_State* L) +int32_t LuaInterface::luaDoCombatAreaCondition(lua_State* L) { - //doCombatAreaCondition(cid, pos, area, condition, effect) + //doCombatAreaCondition(cid, pos, area, condition, effect[, loaded]) + bool loaded = true; + if(lua_gettop(L) > 5) + loaded = popBoolean(L); + MagicEffect_t effect = (MagicEffect_t)popNumber(L); uint32_t conditionId = popNumber(L), areaId = popNumber(L); @@ -6013,7 +6655,7 @@ int32_t LuaScriptInterface::luaDoCombatAreaCondition(lua_State* L) } } - if(const Condition* condition = env->getConditionObject(conditionId)) + if(const Condition* condition = env->getConditionObject(conditionId, loaded)) { const CombatArea* area = env->getCombatArea(areaId); if(area || !areaId) @@ -6021,8 +6663,8 @@ int32_t LuaScriptInterface::luaDoCombatAreaCondition(lua_State* L) CombatParams params; params.effects.impact = effect; params.conditionList.push_back(condition); - Combat::doCombatCondition(creature, pos, area, params); + Combat::doCombatCondition(creature, pos, area, params); lua_pushboolean(L, true); } else @@ -6040,9 +6682,13 @@ int32_t LuaScriptInterface::luaDoCombatAreaCondition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoTargetCombatCondition(lua_State* L) +int32_t LuaInterface::luaDoTargetCombatCondition(lua_State* L) { - //doTargetCombatCondition(cid, target, condition, effect) + //doTargetCombatCondition(cid, target, condition, effect[, loaded]) + bool loaded = true; + if(lua_gettop(L) > 4) + loaded = popBoolean(L); + MagicEffect_t effect = (MagicEffect_t)popNumber(L); uint32_t conditionId = popNumber(L), targetCid = popNumber(L), cid = popNumber(L); @@ -6060,7 +6706,7 @@ int32_t LuaScriptInterface::luaDoTargetCombatCondition(lua_State* L) if(Creature* target = env->getCreatureByUID(targetCid)) { - if(const Condition* condition = env->getConditionObject(conditionId)) + if(const Condition* condition = env->getConditionObject(conditionId, loaded)) { CombatParams params; params.effects.impact = effect; @@ -6084,7 +6730,7 @@ int32_t LuaScriptInterface::luaDoTargetCombatCondition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCombatAreaDispel(lua_State* L) +int32_t LuaInterface::luaDoCombatAreaDispel(lua_State* L) { //doCombatAreaDispel(cid, pos, area, type, effect) MagicEffect_t effect = (MagicEffect_t)popNumber(L); @@ -6126,7 +6772,7 @@ int32_t LuaScriptInterface::luaDoCombatAreaDispel(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoTargetCombatDispel(lua_State* L) +int32_t LuaInterface::luaDoTargetCombatDispel(lua_State* L) { //doTargetCombatDispel(cid, target, type, effect) MagicEffect_t effect = (MagicEffect_t)popNumber(L); @@ -6163,7 +6809,7 @@ int32_t LuaScriptInterface::luaDoTargetCombatDispel(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoChallengeCreature(lua_State* L) +int32_t LuaInterface::luaDoChallengeCreature(lua_State* L) { //doChallengeCreature(cid, target) ScriptEnviroment* env = getEnv(); @@ -6190,7 +6836,7 @@ int32_t LuaScriptInterface::luaDoChallengeCreature(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoSummonMonster(lua_State* L) +int32_t LuaInterface::luaDoSummonMonster(lua_State* L) { //doSummonMonster(cid, name) std::string name = popString(L); @@ -6208,7 +6854,7 @@ int32_t LuaScriptInterface::luaDoSummonMonster(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoConvinceCreature(lua_State* L) +int32_t LuaInterface::luaDoConvinceCreature(lua_State* L) { //doConvinceCreature(cid, target) uint32_t cid = popNumber(L); @@ -6235,7 +6881,7 @@ int32_t LuaScriptInterface::luaDoConvinceCreature(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetMonsterTargetList(lua_State* L) +int32_t LuaInterface::luaGetMonsterTargetList(lua_State* L) { //getMonsterTargetList(cid) ScriptEnviroment* env = getEnv(); @@ -6272,7 +6918,7 @@ int32_t LuaScriptInterface::luaGetMonsterTargetList(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetMonsterFriendList(lua_State* L) +int32_t LuaInterface::luaGetMonsterFriendList(lua_State* L) { //getMonsterFriendList(cid) ScriptEnviroment* env = getEnv(); @@ -6311,7 +6957,7 @@ int32_t LuaScriptInterface::luaGetMonsterFriendList(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoMonsterSetTarget(lua_State* L) +int32_t LuaInterface::luaDoMonsterSetTarget(lua_State* L) { //doMonsterSetTarget(cid, target) uint32_t targetId = popNumber(L); @@ -6349,7 +6995,7 @@ int32_t LuaScriptInterface::luaDoMonsterSetTarget(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoMonsterChangeTarget(lua_State* L) +int32_t LuaInterface::luaDoMonsterChangeTarget(lua_State* L) { //doMonsterChangeTarget(cid) ScriptEnviroment* env = getEnv(); @@ -6376,7 +7022,7 @@ int32_t LuaScriptInterface::luaDoMonsterChangeTarget(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetMonsterInfo(lua_State* L) +int32_t LuaInterface::luaGetMonsterInfo(lua_State* L) { //getMonsterInfo(name) const MonsterType* mType = g_monsters.getMonsterType(popString(L)); @@ -6390,6 +7036,7 @@ int32_t LuaScriptInterface::luaGetMonsterInfo(lua_State* L) lua_newtable(L); setField(L, "name", mType->name.c_str()); setField(L, "description", mType->nameDescription.c_str()); + setField(L, "file", mType->file.c_str()); setField(L, "experience", mType->experience); setField(L, "health", mType->health); setField(L, "healthMax", mType->healthMax); @@ -6398,14 +7045,21 @@ int32_t LuaScriptInterface::luaGetMonsterInfo(lua_State* L) setField(L, "armor", mType->armor); setField(L, "baseSpeed", mType->baseSpeed); setField(L, "lookCorpse", mType->lookCorpse); + setField(L, "corpseUnique", mType->corpseUnique); + setField(L, "corpseAction", mType->corpseAction); setField(L, "race", mType->race); setField(L, "skull", mType->skull); setField(L, "partyShield", mType->partyShield); + setField(L, "guildEmblem", mType->guildEmblem); setFieldBool(L, "summonable", mType->isSummonable); setFieldBool(L, "illusionable", mType->isIllusionable); setFieldBool(L, "convinceable", mType->isConvinceable); setFieldBool(L, "attackable", mType->isAttackable); setFieldBool(L, "hostile", mType->isHostile); + + lua_pushstring(L, "outfit"); // name the table created by pushOutfit + pushOutfit(L, mType->outfit); + pushTable(L); createTable(L, "defenses"); SpellList::const_iterator it = mType->spellDefenseList.begin(); @@ -6527,20 +7181,34 @@ int32_t LuaScriptInterface::luaGetMonsterInfo(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetTalkActionList(lua_State* L) +int32_t LuaInterface::luaGetTalkActionList(lua_State* L) { //getTalkactionList() - TalkActionsMap::const_iterator it = g_talkActions->getFirstTalk(); lua_newtable(L); + + TalkActionsMap::const_iterator it = g_talkActions->getFirstTalk(); for(uint32_t i = 1; it != g_talkActions->getLastTalk(); ++it, ++i) { createTable(L, i); setField(L, "words", it->first); setField(L, "access", it->second->getAccess()); + createTable(L, "groups"); + IntegerVec::const_iterator git = it->second->getGroupsBegin(); + for(uint32_t j = 1; git != it->second->getGroupsEnd(); ++git, ++j) + { + lua_pushnumber(L, j); + lua_pushnumber(L, *git); + pushTable(L); + } + + pushTable(L); setFieldBool(L, "log", it->second->isLogged()); + setFieldBool(L, "logged", it->second->isLogged()); setFieldBool(L, "hide", it->second->isHidden()); + setFieldBool(L, "hidden", it->second->isHidden()); + setField(L, "functionName", it->second->getFunctionName()); setField(L, "channel", it->second->getChannel()); pushTable(L); } @@ -6548,7 +7216,7 @@ int32_t LuaScriptInterface::luaGetTalkActionList(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetExperienceStageList(lua_State* L) +int32_t LuaInterface::luaGetExperienceStageList(lua_State* L) { //getExperienceStageList() if(!g_config.getBool(ConfigManager::EXPERIENCE_STAGES)) @@ -6570,12 +7238,16 @@ int32_t LuaScriptInterface::luaGetExperienceStageList(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoAddCondition(lua_State* L) +int32_t LuaInterface::luaDoAddCondition(lua_State* L) { - //doAddCondition(cid, condition) - uint32_t conditionId = popNumber(L); + //doAddCondition(cid, condition[, loaded]) + bool loaded = true; + if(lua_gettop(L) > 2) + loaded = popBoolean(L); + uint32_t conditionId = popNumber(L); ScriptEnviroment* env = getEnv(); + Creature* creature = env->getCreatureByUID(popNumber(L)); if(!creature) { @@ -6584,7 +7256,7 @@ int32_t LuaScriptInterface::luaDoAddCondition(lua_State* L) return 1; } - Condition* condition = env->getConditionObject(conditionId); + Condition* condition = env->getConditionObject(conditionId, loaded); if(!condition) { errorEx(getError(LUA_ERROR_CONDITION_NOT_FOUND)); @@ -6597,16 +7269,20 @@ int32_t LuaScriptInterface::luaDoAddCondition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoRemoveCondition(lua_State* L) +int32_t LuaInterface::luaDoRemoveCondition(lua_State* L) { - //doRemoveCondition(cid, type[, subId]) - uint32_t subId = 0; - if(lua_gettop(L) > 2) + //doRemoveCondition(cid, type[, subId = 0[, conditionId = CONDITIONID_COMBAT]]) + int32_t conditionId = CONDITIONID_COMBAT; + uint32_t params = lua_gettop(L), subId = 0; + if(params > 3) + conditionId = popNumber(L); + + if(params > 2) subId = popNumber(L); ConditionType_t conditionType = (ConditionType_t)popNumber(L); - ScriptEnviroment* env = getEnv(); + Creature* creature = env->getCreatureByUID(popNumber(L)); if(!creature) { @@ -6615,23 +7291,20 @@ int32_t LuaScriptInterface::luaDoRemoveCondition(lua_State* L) return 1; } - Condition* condition = creature->getCondition(conditionType, CONDITIONID_COMBAT, subId); - if(!condition) - condition = creature->getCondition(conditionType, CONDITIONID_DEFAULT, subId); - - if(condition) + Condition* condition = NULL; + while((condition = creature->getCondition(conditionType, (ConditionId_t)conditionId, subId))) creature->removeCondition(condition); lua_pushboolean(L, true); return 1; } -int32_t LuaScriptInterface::luaDoRemoveConditions(lua_State* L) +int32_t LuaInterface::luaDoRemoveConditions(lua_State* L) { //doRemoveConditions(cid[, onlyPersistent]) bool onlyPersistent = true; if(lua_gettop(L) > 1) - onlyPersistent = popNumber(L); + onlyPersistent = popBoolean(L); ScriptEnviroment* env = getEnv(); Creature* creature = env->getCreatureByUID(popNumber(L)); @@ -6647,51 +7320,51 @@ int32_t LuaScriptInterface::luaDoRemoveConditions(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaNumberToVariant(lua_State* L) +int32_t LuaInterface::luaNumberToVariant(lua_State* L) { //numberToVariant(number) LuaVariant var; var.type = VARIANT_NUMBER; var.number = popNumber(L); - LuaScriptInterface::pushVariant(L, var); + LuaInterface::pushVariant(L, var); return 1; } -int32_t LuaScriptInterface::luaStringToVariant(lua_State* L) +int32_t LuaInterface::luaStringToVariant(lua_State* L) { //stringToVariant(string) LuaVariant var; var.type = VARIANT_STRING; var.text = popString(L); - LuaScriptInterface::pushVariant(L, var); + LuaInterface::pushVariant(L, var); return 1; } -int32_t LuaScriptInterface::luaPositionToVariant(lua_State* L) +int32_t LuaInterface::luaPositionToVariant(lua_State* L) { //positionToVariant(pos) LuaVariant var; var.type = VARIANT_POSITION; popPosition(L, var.pos); - LuaScriptInterface::pushVariant(L, var); + LuaInterface::pushVariant(L, var); return 1; } -int32_t LuaScriptInterface::luaTargetPositionToVariant(lua_State* L) +int32_t LuaInterface::luaTargetPositionToVariant(lua_State* L) { //targetPositionToVariant(pos) LuaVariant var; var.type = VARIANT_TARGETPOSITION; popPosition(L, var.pos); - LuaScriptInterface::pushVariant(L, var); + LuaInterface::pushVariant(L, var); return 1; } -int32_t LuaScriptInterface::luaVariantToNumber(lua_State* L) +int32_t LuaInterface::luaVariantToNumber(lua_State* L) { //variantToNumber(var) LuaVariant var = popVariant(L); @@ -6704,7 +7377,7 @@ int32_t LuaScriptInterface::luaVariantToNumber(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaVariantToString(lua_State* L) +int32_t LuaInterface::luaVariantToString(lua_State* L) { //variantToString(var) LuaVariant var = popVariant(L); @@ -6717,7 +7390,7 @@ int32_t LuaScriptInterface::luaVariantToString(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaVariantToPosition(lua_State* L) +int32_t LuaInterface::luaVariantToPosition(lua_State* L) { //luaVariantToPosition(var) LuaVariant var = popVariant(L); @@ -6730,7 +7403,7 @@ int32_t LuaScriptInterface::luaVariantToPosition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoChangeSpeed(lua_State* L) +int32_t LuaInterface::luaDoChangeSpeed(lua_State* L) { //doChangeSpeed(cid, delta) int32_t delta = (int32_t)popNumber(L); @@ -6746,16 +7419,20 @@ int32_t LuaScriptInterface::luaDoChangeSpeed(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaSetCreatureOutfit(lua_State* L) +int32_t LuaInterface::luaSetCreatureOutfit(lua_State* L) { - //doSetCreatureOutfit(cid, outfit, time) - int32_t time = (int32_t)popNumber(L); - Outfit_t outfit = popOutfit(L); + //doSetCreatureOutfit(cid, outfit[, time = -1]) + int32_t time = -1; + if(lua_gettop(L) > 2) + time = (int32_t)popNumber(L); + Outfit_t outfit = popOutfit(L); ScriptEnviroment* env = getEnv(); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) lua_pushboolean(L, Spell::CreateIllusion(creature, outfit, time) == RET_NOERROR); else @@ -6763,10 +7440,11 @@ int32_t LuaScriptInterface::luaSetCreatureOutfit(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureOutfit(lua_State* L) +int32_t LuaInterface::luaGetCreatureOutfit(lua_State* L) { //getCreatureOutfit(cid) ScriptEnviroment* env = getEnv(); @@ -6777,16 +7455,20 @@ int32_t LuaScriptInterface::luaGetCreatureOutfit(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaSetMonsterOutfit(lua_State* L) +int32_t LuaInterface::luaSetMonsterOutfit(lua_State* L) { - //doSetMonsterOutfit(cid, name, time) - int32_t time = (int32_t)popNumber(L); - std::string name = popString(L); + //doSetMonsterOutfit(cid, name[, time = -1]) + int32_t time = -1; + if(lua_gettop(L) > 2) + time = (int32_t)popNumber(L); + std::string name = popString(L); ScriptEnviroment* env = getEnv(); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) lua_pushboolean(L, Spell::CreateIllusion(creature, name, time) == RET_NOERROR); else @@ -6794,16 +7476,20 @@ int32_t LuaScriptInterface::luaSetMonsterOutfit(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaSetItemOutfit(lua_State* L) +int32_t LuaInterface::luaSetItemOutfit(lua_State* L) { - //doSetItemOutfit(cid, item, time) - int32_t time = (int32_t)popNumber(L); - uint32_t item = (uint32_t)popNumber(L); + //doSetItemOutfit(cid, item[, time = -1]) + int32_t time = -1; + if(lua_gettop(L) > 2) + time = (int32_t)popNumber(L); + uint32_t item = (uint32_t)popNumber(L); ScriptEnviroment* env = getEnv(); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) lua_pushboolean(L, Spell::CreateIllusion(creature, item, time) == RET_NOERROR); else @@ -6811,52 +7497,72 @@ int32_t LuaScriptInterface::luaSetItemOutfit(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + + return 1; +} + +int32_t LuaInterface::luaGetStorageList(lua_State* L) +{ + //getStorageList() + ScriptEnviroment* env = getEnv(); + + StorageMap::const_iterator it = env->getStorageBegin(); + lua_newtable(L); + for(uint32_t i = 1; it != env->getStorageEnd(); ++i, ++it) + { + lua_pushnumber(L, i); + lua_pushstring(L, it->first.c_str()); + pushTable(L); + } + return 1; } -int32_t LuaScriptInterface::luaGetStorage(lua_State* L) +int32_t LuaInterface::luaGetStorage(lua_State* L) { //getStorage(key) ScriptEnviroment* env = getEnv(); std::string strValue; - if(env->getStorage(popNumber(L), strValue)) + if(!env->getStorage(popString(L), strValue)) { - int32_t intValue = atoi(strValue.c_str()); - if(intValue || strValue == "0") - lua_pushnumber(L, intValue); - else - lua_pushstring(L, strValue.c_str()); + lua_pushnumber(L, -1); + lua_pushnil(L); + return 2; } + + int32_t intValue = atoi(strValue.c_str()); + if(intValue || strValue == "0") + lua_pushnumber(L, intValue); else - lua_pushnumber(L, -1); + lua_pushstring(L, strValue.c_str()); return 1; } -int32_t LuaScriptInterface::luaDoSetStorage(lua_State* L) +int32_t LuaInterface::luaDoSetStorage(lua_State* L) { - //doSetStorage(value, key) + //doSetStorage(key, value) std::string value; - bool nil = false; + bool tmp = false; if(lua_isnil(L, -1)) { - nil = true; + tmp = true; lua_pop(L, 1); } else value = popString(L); ScriptEnviroment* env = getEnv(); - if(!nil) - env->setStorage(popNumber(L), value); + if(!tmp) + env->setStorage(popString(L), value); else - env->eraseStorage(popNumber(L)); + env->eraseStorage(popString(L)); lua_pushboolean(L, true); return 1; } -int32_t LuaScriptInterface::luaGetPlayerDepotItems(lua_State* L) +int32_t LuaInterface::luaGetPlayerDepotItems(lua_State* L) { //getPlayerDepotItems(cid, depotid) uint32_t depotid = popNumber(L); @@ -6874,10 +7580,11 @@ int32_t LuaScriptInterface::luaGetPlayerDepotItems(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetGuildId(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetGuildId(lua_State* L) { //doPlayerSetGuildId(cid, id) uint32_t id = popNumber(L); @@ -6885,9 +7592,10 @@ int32_t LuaScriptInterface::luaDoPlayerSetGuildId(lua_State* L) ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) { - if(player->guildId) + if(player->getGuildId()) { player->leaveGuild(); + if(!id) lua_pushboolean(L, true); else if(IOGuild::getInstance()->guildExists(id)) @@ -6905,10 +7613,11 @@ int32_t LuaScriptInterface::luaDoPlayerSetGuildId(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetGuildLevel(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetGuildLevel(lua_State* L) { //doPlayerSetGuildLevel(cid, level[, rank]) uint32_t rank = 0; @@ -6928,7 +7637,7 @@ int32_t LuaScriptInterface::luaDoPlayerSetGuildLevel(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetGuildNick(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetGuildNick(lua_State* L) { //doPlayerSetGuildNick(cid, nick) std::string nick = popString(L); @@ -6948,7 +7657,7 @@ int32_t LuaScriptInterface::luaDoPlayerSetGuildNick(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetGuildId(lua_State* L) +int32_t LuaInterface::luaGetGuildId(lua_State* L) { //getGuildId(guildName) uint32_t guildId; @@ -6960,7 +7669,7 @@ int32_t LuaScriptInterface::luaGetGuildId(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetGuildMotd(lua_State* L) +int32_t LuaInterface::luaGetGuildMotd(lua_State* L) { //getGuildMotd(guildId) uint32_t guildId = popNumber(L); @@ -6972,10 +7681,14 @@ int32_t LuaScriptInterface::luaGetGuildMotd(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoMoveCreature(lua_State* L) +int32_t LuaInterface::luaDoMoveCreature(lua_State* L) { - //doMoveCreature(cid, direction) - uint32_t direction = popNumber(L); + //doMoveCreature(cid, direction[, flag = FLAG_NOLIMIT]) + uint32_t flags = FLAG_NOLIMIT; + if(lua_gettop(L) > 2) + flags = popNumber(L); + + int32_t direction = popNumber(L); if(direction < NORTH || direction > NORTHEAST) { lua_pushboolean(L, false); @@ -6984,7 +7697,7 @@ int32_t LuaScriptInterface::luaDoMoveCreature(lua_State* L) ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) - lua_pushnumber(L, g_game.internalMoveCreature(creature, (Direction)direction, FLAG_NOLIMIT)); + lua_pushnumber(L, g_game.internalMoveCreature(creature, (Direction)direction, flags)); else { errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); @@ -6994,23 +7707,37 @@ int32_t LuaScriptInterface::luaDoMoveCreature(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaIsCreature(lua_State* L) +int32_t LuaInterface::luaDoSteerCreature(lua_State* L) { - //isCreature(cid) + //doSteerCreature(cid, position[, maxNodes]) + uint16_t maxNodes = 100; + if(lua_gettop(L) > 2) + maxNodes = popNumber(L); + + PositionEx pos; + popPosition(L, pos); + ScriptEnviroment* env = getEnv(); - lua_pushboolean(L, env->getCreatureByUID(popNumber(L)) ? true : false); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) + lua_pushboolean(L, g_game.steerCreature(creature, pos, maxNodes)); + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + return 1; } -int32_t LuaScriptInterface::luaIsContainer(lua_State* L) +int32_t LuaInterface::luaIsCreature(lua_State* L) { - //isContainer(uid) + //isCreature(cid) ScriptEnviroment* env = getEnv(); - lua_pushboolean(L, env->getContainerByUID(popNumber(L)) ? true : false); + lua_pushboolean(L, env->getCreatureByUID(popNumber(L)) ? true : false); return 1; } -int32_t LuaScriptInterface::luaIsMovable(lua_State* L) +int32_t LuaInterface::luaIsMovable(lua_State* L) { //isMovable(uid) ScriptEnviroment* env = getEnv(); @@ -7023,7 +7750,7 @@ int32_t LuaScriptInterface::luaIsMovable(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetCreatureByName(lua_State* L) +int32_t LuaInterface::luaGetCreatureByName(lua_State* L) { //getCreatureByName(name) ScriptEnviroment* env = getEnv(); @@ -7035,7 +7762,7 @@ int32_t LuaScriptInterface::luaGetCreatureByName(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerByGUID(lua_State* L) +int32_t LuaInterface::luaGetPlayerByGUID(lua_State* L) { //getPlayerByGUID(guid) ScriptEnviroment* env = getEnv(); @@ -7047,19 +7774,19 @@ int32_t LuaScriptInterface::luaGetPlayerByGUID(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerByNameWildcard(lua_State* L) +int32_t LuaInterface::luaGetPlayerByNameWildcard(lua_State* L) { //getPlayerByNameWildcard(name~[, ret = false]) Player* player = NULL; - bool pushRet = false; + bool pushValue = false; if(lua_gettop(L) > 1) - pushRet = popNumber(L); + pushValue = popBoolean(L); ScriptEnviroment* env = getEnv(); ReturnValue ret = g_game.getPlayerByNameWildcard(popString(L), player); if(ret == RET_NOERROR) lua_pushnumber(L, env->addThing(player)); - else if(pushRet) + else if(pushValue) lua_pushnumber(L, ret); else lua_pushnil(L); @@ -7067,12 +7794,12 @@ int32_t LuaScriptInterface::luaGetPlayerByNameWildcard(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerGUIDByName(lua_State* L) +int32_t LuaInterface::luaGetPlayerGUIDByName(lua_State* L) { //getPlayerGUIDByName(name[, multiworld = false]) bool multiworld = false; if(lua_gettop(L) > 1) - multiworld = popNumber(L); + multiworld = popBoolean(L); std::string name = popString(L); uint32_t guid; @@ -7086,34 +7813,45 @@ int32_t LuaScriptInterface::luaGetPlayerGUIDByName(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerNameByGUID(lua_State* L) +int32_t LuaInterface::luaGetPlayerNameByGUID(lua_State* L) { - //getPlayerNameByGUID(guid[, multiworld = false[, displayError = true]]) - int32_t parameters = lua_gettop(L); - bool multiworld = false, displayError = true; - - if(parameters > 2) - displayError = popNumber(L); - - if(parameters > 1) - multiworld = popNumber(L); + //getPlayerNameByGUID(guid[, multiworld = false]) + bool multiworld = false; + if(lua_gettop(L) > 1) + multiworld = popBoolean(L); uint32_t guid = popNumber(L); std::string name; if(!IOLoginData::getInstance()->getNameByGuid(guid, name, multiworld)) { - if(displayError) - errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushnil(L); + return 1; + } + + lua_pushstring(L, name.c_str()); + return 1; +} + +int32_t LuaInterface::luaDoPlayerChangeName(lua_State* L) +{ + //doPlayerChangeName(guid, oldName, newName) + std::string newName = popString(L), oldName = popString(L); + uint32_t guid = popNumber(L); + if(IOLoginData::getInstance()->changeName(guid, newName, oldName)) + { + if(House* house = Houses::getInstance()->getHouseByPlayerId(guid)) + house->updateDoorDescription(newName); - lua_pushnil(L); - return 1; + lua_pushboolean(L, true); } + else + lua_pushboolean(L, false); - lua_pushstring(L, name.c_str()); return 1; } -int32_t LuaScriptInterface::luaGetPlayersByAccountId(lua_State* L) +int32_t LuaInterface::luaGetPlayersByAccountId(lua_State* L) { //getPlayersByAccountId(accId) PlayerVector players = g_game.getPlayersByAccount(popNumber(L)); @@ -7132,20 +7870,21 @@ int32_t LuaScriptInterface::luaGetPlayersByAccountId(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetIpByName(lua_State* L) +int32_t LuaInterface::luaGetIpByName(lua_State* L) { //getIpByName(name) std::string name = popString(L); - if(Player* player = g_game.getPlayerByName(name)) lua_pushnumber(L, player->getIP()); - else + else if(IOLoginData::getInstance()->playerExists(name)) lua_pushnumber(L, IOLoginData::getInstance()->getLastIPByName(name)); + else + lua_pushnil(L); return 1; } -int32_t LuaScriptInterface::luaGetPlayersByIp(lua_State* L) +int32_t LuaInterface::luaGetPlayersByIp(lua_State* L) { //getPlayersByIp(ip[, mask]) uint32_t mask = 0xFFFFFFFF; @@ -7168,7 +7907,7 @@ int32_t LuaScriptInterface::luaGetPlayersByIp(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetAccountIdByName(lua_State* L) +int32_t LuaInterface::luaGetAccountIdByName(lua_State* L) { //getAccountIdByName(name) std::string name = popString(L); @@ -7181,7 +7920,7 @@ int32_t LuaScriptInterface::luaGetAccountIdByName(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetAccountByName(lua_State* L) +int32_t LuaInterface::luaGetAccountByName(lua_State* L) { //getAccountByName(name) std::string name = popString(L); @@ -7198,7 +7937,7 @@ int32_t LuaScriptInterface::luaGetAccountByName(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetAccountIdByAccount(lua_State* L) +int32_t LuaInterface::luaGetAccountIdByAccount(lua_State* L) { //getAccountIdByAccount(accName) uint32_t value = 0; @@ -7207,7 +7946,7 @@ int32_t LuaScriptInterface::luaGetAccountIdByAccount(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetAccountByAccountId(lua_State* L) +int32_t LuaInterface::luaGetAccountByAccountId(lua_State* L) { //getAccountByAccountId(accId) std::string value = 0; @@ -7216,7 +7955,31 @@ int32_t LuaScriptInterface::luaGetAccountByAccountId(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaRegisterCreatureEvent(lua_State* L) +int32_t LuaInterface::luaGetAccountFlagValue(lua_State* L) +{ + //getAccountFlagValue(name/id) + PlayerFlags flag = (PlayerFlags)popNumber(L); + if(lua_isnumber(L, -1)) + lua_pushboolean(L, IOLoginData::getInstance()->hasFlag((uint32_t)popNumber(L), flag)); + else + lua_pushboolean(L, IOLoginData::getInstance()->hasFlag(flag, popString(L))); + + return 1; +} + +int32_t LuaInterface::luaGetAccountCustomFlagValue(lua_State* L) +{ + //getAccountCustomFlagValue(name/id) + PlayerCustomFlags flag = (PlayerCustomFlags)popNumber(L); + if(lua_isnumber(L, -1)) + lua_pushboolean(L, IOLoginData::getInstance()->hasCustomFlag((uint32_t)popNumber(L), flag)); + else + lua_pushboolean(L, IOLoginData::getInstance()->hasCustomFlag(flag, popString(L))); + + return 1; +} + +int32_t LuaInterface::luaRegisterCreatureEvent(lua_State* L) { //registerCreatureEvent(cid, name) std::string name = popString(L); @@ -7233,7 +7996,53 @@ int32_t LuaScriptInterface::luaRegisterCreatureEvent(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetContainerSize(lua_State* L) +int32_t LuaInterface::luaUnregisterCreatureEvent(lua_State* L) +{ + //unregisterCreatureEvent(cid, name) + std::string name = popString(L); + + ScriptEnviroment* env = getEnv(); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) + lua_pushboolean(L, creature->unregisterCreatureEvent(name)); + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + + return 1; +} + +int32_t LuaInterface::luaUnregisterCreatureEventType(lua_State* L) +{ + //unregisterCreatureEventType(cid, type) + std::string type = popString(L); + + ScriptEnviroment* env = getEnv(); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) + { + CreatureEventType_t _type = g_creatureEvents->getType(type); + if(_type != CREATURE_EVENT_NONE) + { + creature->unregisterCreatureEvent(_type); + lua_pushboolean(L, true); + } + else + { + errorEx("Invalid event type"); + lua_pushboolean(L, false); + } + } + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + + return 1; +} + +int32_t LuaInterface::luaGetContainerSize(lua_State* L) { //getContainerSize(uid) ScriptEnviroment* env = getEnv(); @@ -7244,10 +8053,11 @@ int32_t LuaScriptInterface::luaGetContainerSize(lua_State* L) errorEx(getError(LUA_ERROR_CONTAINER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetContainerCap(lua_State* L) +int32_t LuaInterface::luaGetContainerCap(lua_State* L) { //getContainerCap(uid) ScriptEnviroment* env = getEnv(); @@ -7258,14 +8068,14 @@ int32_t LuaScriptInterface::luaGetContainerCap(lua_State* L) errorEx(getError(LUA_ERROR_CONTAINER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetContainerItem(lua_State* L) +int32_t LuaInterface::luaGetContainerItem(lua_State* L) { //getContainerItem(uid, slot) uint32_t slot = popNumber(L); - ScriptEnviroment* env = getEnv(); if(Container* container = env->getContainerByUID(popNumber(L))) { @@ -7279,11 +8089,11 @@ int32_t LuaScriptInterface::luaGetContainerItem(lua_State* L) errorEx(getError(LUA_ERROR_CONTAINER_NOT_FOUND)); pushThing(L, NULL, 0); } - return 1; + return 1; } -int32_t LuaScriptInterface::luaDoAddContainerItemEx(lua_State* L) +int32_t LuaInterface::luaDoAddContainerItemEx(lua_State* L) { //doAddContainerItemEx(uid, virtuid) uint32_t virtuid = popNumber(L); @@ -7306,7 +8116,7 @@ int32_t LuaScriptInterface::luaDoAddContainerItemEx(lua_State* L) ReturnValue ret = g_game.internalAddItem(NULL, container, item); if(ret == RET_NOERROR) - env->removeTempItem(item); + env->removeTempItem(env, item); lua_pushnumber(L, ret); return 1; @@ -7319,9 +8129,9 @@ int32_t LuaScriptInterface::luaDoAddContainerItemEx(lua_State* L) } } -int32_t LuaScriptInterface::luaDoAddContainerItem(lua_State* L) +int32_t LuaInterface::luaDoAddContainerItem(lua_State* L) { - //doAddContainerItem(uid, itemid[, count/subType]) + //doAddContainerItem(uid, itemid[, count/subType = 1]) uint32_t count = 1; if(lua_gettop(L) > 2) count = popNumber(L); @@ -7349,11 +8159,12 @@ int32_t LuaScriptInterface::luaDoAddContainerItem(lua_State* L) else itemCount = std::max((uint32_t)1, count); + uint32_t ret = 0; + Item* newItem = NULL; while(itemCount > 0) { int32_t stackCount = std::min(100, subType); - Item* newItem = Item::CreateItem(itemId, stackCount); - if(!newItem) + if(!(newItem = Item::CreateItem(itemId, stackCount))) { errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND)); lua_pushboolean(L, false); @@ -7363,31 +8174,34 @@ int32_t LuaScriptInterface::luaDoAddContainerItem(lua_State* L) if(it.stackable) subType -= stackCount; - ReturnValue ret = g_game.internalAddItem(NULL, container, newItem); - if(ret != RET_NOERROR) + uint32_t dummy = 0; + Item* stackItem = NULL; + if(g_game.internalAddItem(NULL, container, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT, false, dummy, &stackItem) != RET_NOERROR) { delete newItem; lua_pushboolean(L, false); - return 1; + return ++ret; } - --itemCount; - if(itemCount) - continue; - + ++ret; if(newItem->getParent()) lua_pushnumber(L, env->addThing(newItem)); + else if(stackItem) + lua_pushnumber(L, env->addThing(stackItem)); else //stackable item stacked with existing object, newItem will be released lua_pushnil(L); - return 1; + --itemCount; } + if(ret) + return ret; + lua_pushnil(L); return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddOutfit(lua_State *L) +int32_t LuaInterface::luaDoPlayerAddOutfit(lua_State *L) { //Consider using doPlayerAddOutfitId instead //doPlayerAddOutfit(cid, looktype, addon) @@ -7413,7 +8227,7 @@ int32_t LuaScriptInterface::luaDoPlayerAddOutfit(lua_State *L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerRemoveOutfit(lua_State *L) +int32_t LuaInterface::luaDoPlayerRemoveOutfit(lua_State *L) { //Consider using doPlayerRemoveOutfitId instead //doPlayerRemoveOutfit(cid, looktype[, addon = 0]) @@ -7443,7 +8257,7 @@ int32_t LuaScriptInterface::luaDoPlayerRemoveOutfit(lua_State *L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddOutfitId(lua_State *L) +int32_t LuaInterface::luaDoPlayerAddOutfitId(lua_State *L) { //doPlayerAddOutfitId(cid, outfitId, addon) uint32_t addon = popNumber(L), outfitId = popNumber(L); @@ -7461,7 +8275,7 @@ int32_t LuaScriptInterface::luaDoPlayerAddOutfitId(lua_State *L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerRemoveOutfitId(lua_State *L) +int32_t LuaInterface::luaDoPlayerRemoveOutfitId(lua_State *L) { //doPlayerRemoveOutfitId(cid, outfitId[, addon = 0]) uint32_t addon = 0xFF; @@ -7483,7 +8297,7 @@ int32_t LuaScriptInterface::luaDoPlayerRemoveOutfitId(lua_State *L) return 1; } -int32_t LuaScriptInterface::luaCanPlayerWearOutfit(lua_State* L) +int32_t LuaInterface::luaCanPlayerWearOutfit(lua_State* L) { //canPlayerWearOutfit(cid, looktype[, addon = 0]) uint32_t addon = 0; @@ -7512,7 +8326,7 @@ int32_t LuaScriptInterface::luaCanPlayerWearOutfit(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaCanPlayerWearOutfitId(lua_State* L) +int32_t LuaInterface::luaCanPlayerWearOutfitId(lua_State* L) { //canPlayerWearOutfitId(cid, outfitId[, addon = 0]) uint32_t addon = 0; @@ -7534,7 +8348,7 @@ int32_t LuaScriptInterface::luaCanPlayerWearOutfitId(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCreatureChangeOutfit(lua_State* L) +int32_t LuaInterface::luaDoCreatureChangeOutfit(lua_State* L) { //doCreatureChangeOutfit(cid, outfit) Outfit_t outfit = popOutfit(L); @@ -7556,31 +8370,11 @@ int32_t LuaScriptInterface::luaDoCreatureChangeOutfit(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } - return 1; -} -int32_t LuaScriptInterface::luaDoSetCreatureLight(lua_State* L) -{ - //doSetCreatureLight(cid, lightLevel, lightColor, time) - uint32_t time = popNumber(L); - uint8_t color = (uint8_t)popNumber(L), level = (uint8_t)popNumber(L); - - ScriptEnviroment* env = getEnv(); - if(Creature* creature = env->getCreatureByUID(popNumber(L))) - { - Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_LIGHT, time, level | (color << 8)); - creature->addCondition(condition); - lua_pushboolean(L, true); - } - else - { - errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); - lua_pushboolean(L, false); - } return 1; } -int32_t LuaScriptInterface::luaDoPlayerPopupFYI(lua_State* L) +int32_t LuaInterface::luaDoPlayerPopupFYI(lua_State* L) { //doPlayerPopupFYI(cid, message) std::string message = popString(L); @@ -7596,10 +8390,11 @@ int32_t LuaScriptInterface::luaDoPlayerPopupFYI(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSendTutorial(lua_State* L) +int32_t LuaInterface::luaDoPlayerSendTutorial(lua_State* L) { //doPlayerSendTutorial(cid, id) uint8_t id = (uint8_t)popNumber(L); @@ -7619,7 +8414,7 @@ int32_t LuaScriptInterface::luaDoPlayerSendTutorial(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerSendMailByName(lua_State* L) +int32_t LuaInterface::luaDoPlayerSendMailByName(lua_State* L) { //doPlayerSendMailByName(name, item[, town[, actor]]) ScriptEnviroment* env = getEnv(); @@ -7647,11 +8442,15 @@ int32_t LuaScriptInterface::luaDoPlayerSendMailByName(lua_State* L) return 1; } - lua_pushboolean(L, IOLoginData::getInstance()->playerMail(actor, popString(L), town, item)); + bool result = IOLoginData::getInstance()->playerMail(actor, popString(L), town, item); + if(result) + env->removeTempItem(env, item); + + lua_pushboolean(L, result); return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddMapMark(lua_State* L) +int32_t LuaInterface::luaDoPlayerAddMapMark(lua_State* L) { //doPlayerAddMapMark(cid, pos, type[, description]) std::string description; @@ -7676,11 +8475,10 @@ int32_t LuaScriptInterface::luaDoPlayerAddMapMark(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddPremiumDays(lua_State* L) +int32_t LuaInterface::luaDoPlayerAddPremiumDays(lua_State* L) { //doPlayerAddPremiumDays(cid, days) int32_t days = popNumber(L); - ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) { @@ -7706,14 +8504,14 @@ int32_t LuaScriptInterface::luaDoPlayerAddPremiumDays(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureLastPosition(lua_State* L) +int32_t LuaInterface::luaGetCreatureLastPosition(lua_State* L) { //getCreatureLastPosition(cid) ScriptEnviroment* env = getEnv(); - if(Creature* creature = env->getCreatureByUID(popNumber(L))) pushPosition(L, creature->getLastPosition(), 0); else @@ -7721,10 +8519,11 @@ int32_t LuaScriptInterface::luaGetCreatureLastPosition(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureName(lua_State* L) +int32_t LuaInterface::luaGetCreatureName(lua_State* L) { //getCreatureName(cid) ScriptEnviroment* env = getEnv(); @@ -7739,7 +8538,7 @@ int32_t LuaScriptInterface::luaGetCreatureName(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetCreatureNoMove(lua_State* L) +int32_t LuaInterface::luaGetCreatureNoMove(lua_State* L) { //getCreatureNoMove(cid) ScriptEnviroment* env = getEnv(); @@ -7754,7 +8553,105 @@ int32_t LuaScriptInterface::luaGetCreatureNoMove(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetCreatureSkullType(lua_State* L) +int32_t LuaInterface::luaGetCreatureGuildEmblem(lua_State* L) +{ + //getCreatureGuildEmblem(cid[, target]) + uint32_t tid = 0; + if(lua_gettop(L) > 1) + tid = popNumber(L); + + ScriptEnviroment* env = getEnv(); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) + { + if(!tid) + lua_pushnumber(L, creature->getEmblem()); + else if(Creature* target = env->getCreatureByUID(tid)) + lua_pushnumber(L, creature->getGuildEmblem(target)); + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + } + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + + return 1; +} + +int32_t LuaInterface::luaDoCreatureSetGuildEmblem(lua_State* L) +{ + //doCreatureSetGuildEmblem(cid, emblem) + GuildEmblems_t emblem = (GuildEmblems_t)popNumber(L); + ScriptEnviroment* env = getEnv(); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) + { + creature->setEmblem(emblem); + g_game.updateCreatureEmblem(creature); + lua_pushboolean(L, true); + } + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + + return 1; +} + +int32_t LuaInterface::luaGetCreaturePartyShield(lua_State* L) +{ + //getCreaturePartyShield(cid[, target]) + uint32_t tid = 0; + if(lua_gettop(L) > 1) + tid = popNumber(L); + + ScriptEnviroment* env = getEnv(); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) + { + if(!tid) + lua_pushnumber(L, creature->getShield()); + else if(Creature* target = env->getCreatureByUID(tid)) + lua_pushnumber(L, creature->getPartyShield(target)); + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + } + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + + return 1; +} + +int32_t LuaInterface::luaDoCreatureSetPartyShield(lua_State* L) +{ + //doCreatureSetPartyShield(cid, shield) + PartyShields_t shield = (PartyShields_t)popNumber(L); + ScriptEnviroment* env = getEnv(); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) + { + creature->setShield(shield); + g_game.updateCreatureShield(creature); + lua_pushboolean(L, true); + } + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + + return 1; +} + +int32_t LuaInterface::luaGetCreatureSkullType(lua_State* L) { //getCreatureSkullType(cid[, target]) uint32_t tid = 0; @@ -7767,7 +8664,7 @@ int32_t LuaScriptInterface::luaGetCreatureSkullType(lua_State* L) if(!tid) lua_pushnumber(L, creature->getSkull()); else if(Creature* target = env->getCreatureByUID(tid)) - lua_pushnumber(L, creature->getSkullClient(target)); + lua_pushnumber(L, creature->getSkullType(target)); else { errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); @@ -7783,7 +8680,7 @@ int32_t LuaScriptInterface::luaGetCreatureSkullType(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCreatureSetLookDir(lua_State* L) +int32_t LuaInterface::luaDoCreatureSetLookDir(lua_State* L) { //doCreatureSetLookDirection(cid, dir) Direction dir = (Direction)popNumber(L); @@ -7804,10 +8701,11 @@ int32_t LuaScriptInterface::luaDoCreatureSetLookDir(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoCreatureSetSkullType(lua_State* L) +int32_t LuaInterface::luaDoCreatureSetSkullType(lua_State* L) { //doCreatureSetSkullType(cid, skull) Skulls_t skull = (Skulls_t)popNumber(L); @@ -7823,10 +8721,11 @@ int32_t LuaScriptInterface::luaDoCreatureSetSkullType(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetSkullEnd(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetSkullEnd(lua_State* L) { //doPlayerSetSkullEnd(cid, time, type) Skulls_t _skull = (Skulls_t)popNumber(L); @@ -7843,10 +8742,11 @@ int32_t LuaScriptInterface::luaDoPlayerSetSkullEnd(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureSpeed(lua_State* L) +int32_t LuaInterface::luaGetCreatureSpeed(lua_State* L) { //getCreatureSpeed(cid) ScriptEnviroment* env = getEnv(); @@ -7857,10 +8757,11 @@ int32_t LuaScriptInterface::luaGetCreatureSpeed(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureBaseSpeed(lua_State* L) +int32_t LuaInterface::luaGetCreatureBaseSpeed(lua_State* L) { //getCreatureBaseSpeed(cid) ScriptEnviroment* env = getEnv(); @@ -7871,10 +8772,11 @@ int32_t LuaScriptInterface::luaGetCreatureBaseSpeed(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureTarget(lua_State* L) +int32_t LuaInterface::luaGetCreatureTarget(lua_State* L) { //getCreatureTarget(cid) ScriptEnviroment* env = getEnv(); @@ -7888,14 +8790,15 @@ int32_t LuaScriptInterface::luaGetCreatureTarget(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaIsSightClear(lua_State* L) +int32_t LuaInterface::luaIsSightClear(lua_State* L) { //isSightClear(fromPos, toPos, floorCheck) PositionEx fromPos, toPos; - bool floorCheck = popNumber(L); + bool floorCheck = popBoolean(L); popPosition(L, toPos); popPosition(L, fromPos); @@ -7904,12 +8807,12 @@ int32_t LuaScriptInterface::luaIsSightClear(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaIsInArray(lua_State* L) +int32_t LuaInterface::luaIsInArray(lua_State* L) { //isInArray(array, value[, caseSensitive = false]) bool caseSensitive = false; if(lua_gettop(L) > 2) - caseSensitive = popNumber(L); + caseSensitive = popBoolean(L); boost::any value; if(lua_isnumber(L, -1)) @@ -8014,11 +8917,11 @@ int32_t LuaScriptInterface::luaIsInArray(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaAddEvent(lua_State* L) +int32_t LuaInterface::luaAddEvent(lua_State* L) { //addEvent(callback, delay, ...) ScriptEnviroment* env = getEnv(); - LuaScriptInterface* interface = env->getInterface(); + LuaInterface* interface = env->getInterface(); if(!interface) { errorEx("No valid script interface!"); @@ -8029,7 +8932,7 @@ int32_t LuaScriptInterface::luaAddEvent(lua_State* L) int32_t parameters = lua_gettop(L); if(!lua_isfunction(L, -parameters)) //-parameters means the first parameter from left to right { - errorEx("Callback parameter should be a function."); + errorEx("Callback parameter should be a function"); lua_pushboolean(L, false); return 1; } @@ -8038,28 +8941,27 @@ int32_t LuaScriptInterface::luaAddEvent(lua_State* L) for(int32_t i = 0; i < parameters - 2; ++i) //-2 because addEvent needs at least two parameters params.push_back(luaL_ref(L, LUA_REGISTRYINDEX)); - uint32_t delay = std::max((int64_t)SCHEDULER_MINTICKS, popNumber(L)); - LuaTimerEvent eventDesc; + LuaTimerEvent event; + event.eventId = Scheduler::getInstance().addEvent(createSchedulerTask(std::max((int64_t)SCHEDULER_MINTICKS, popNumber(L)), + boost::bind(&LuaInterface::executeTimer, interface, ++interface->m_lastTimer))); - eventDesc.parameters = params; - eventDesc.function = luaL_ref(L, LUA_REGISTRYINDEX); - eventDesc.scriptId = env->getScriptId(); + event.parameters = params; + event.function = luaL_ref(L, LUA_REGISTRYINDEX); + event.scriptId = env->getScriptId(); + event.npc = env->getNpc(); - interface->m_timerEvents[++interface->m_lastEventTimerId] = eventDesc; - Scheduler::getInstance().addEvent(createSchedulerTask(delay, boost::bind( - &LuaScriptInterface::executeTimer, interface, interface->m_lastEventTimerId))); - - lua_pushnumber(L, interface->m_lastEventTimerId); + interface->m_timerEvents[interface->m_lastTimer] = event; + lua_pushnumber(L, interface->m_lastTimer); return 1; } -int32_t LuaScriptInterface::luaStopEvent(lua_State* L) +int32_t LuaInterface::luaStopEvent(lua_State* L) { //stopEvent(eventid) uint32_t eventId = popNumber(L); ScriptEnviroment* env = getEnv(); - LuaScriptInterface* interface = env->getInterface(); + LuaInterface* interface = env->getInterface(); if(!interface) { errorEx("No valid script interface!"); @@ -8070,11 +8972,13 @@ int32_t LuaScriptInterface::luaStopEvent(lua_State* L) LuaTimerEvents::iterator it = interface->m_timerEvents.find(eventId); if(it != interface->m_timerEvents.end()) { + Scheduler::getInstance().stopEvent(it->second.eventId); for(std::list::iterator lt = it->second.parameters.begin(); lt != it->second.parameters.end(); ++lt) luaL_unref(interface->m_luaState, LUA_REGISTRYINDEX, *lt); - it->second.parameters.clear(); + it->second.parameters.clear(); luaL_unref(interface->m_luaState, LUA_REGISTRYINDEX, it->second.function); + interface->m_timerEvents.erase(it); lua_pushboolean(L, true); } @@ -8084,17 +8988,72 @@ int32_t LuaScriptInterface::luaStopEvent(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetCreatureCondition(lua_State* L) +int32_t LuaInterface::luaHasCreatureCondition(lua_State* L) { - //getCreatureCondition(cid, condition[, subId]) - uint32_t subId = 0, condition = 0; - if(lua_gettop(L) > 2) + //hasCreatureCondition(cid, conditionType[, subId = 0[, conditionId = (both)]]) + int32_t conditionId = CONDITIONID_COMBAT; + uint32_t params = lua_gettop(L), subId = 0; + + bool both = true; + if(params > 3) + { + conditionId = popNumber(L); + both = false; + } + + if(params > 2) subId = popNumber(L); - condition = popNumber(L); + ConditionType_t conditionType = (ConditionType_t)popNumber(L); + ScriptEnviroment* env = getEnv(); + if(Creature* creature = env->getCreatureByUID(popNumber(L))) + { + if(!both) + lua_pushboolean(L, creature->getCondition(conditionType, (ConditionId_t)conditionId, subId) != NULL); + else if(creature->getCondition(conditionType, CONDITIONID_DEFAULT, subId) != NULL) + { + lua_pushboolean(L, true); + return 1; + } + else + lua_pushboolean(L, creature->getCondition(conditionType, CONDITIONID_COMBAT, subId) != NULL); + } + else + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + } + + return 1; +} + +int32_t LuaInterface::luaGetCreatureConditionInfo(lua_State* L) +{ + //getCreatureConditionInfo(cid, conditionType[, subId = 0[, conditionId = CONDITIONID_COMBAT]]) + int32_t conditionId = CONDITIONID_COMBAT; + uint32_t params = lua_gettop(L), subId = 0; + if(params > 3) + conditionId = popNumber(L); + + if(params > 2) + subId = popNumber(L); + + ConditionType_t conditionType = (ConditionType_t)popNumber(L); ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) - lua_pushboolean(L, creature->hasCondition((ConditionType_t)condition, subId)); + { + if(Condition* condition = creature->getCondition(conditionType, (ConditionId_t)conditionId, subId)) + { + lua_newtable(L); + setField(L, "icons", condition->getIcons()); + setField(L, "endTime", condition->getEndTime()); + setField(L, "ticks", condition->getTicks()); + setFieldBool(L, "persistent", condition->isPersistent()); + setField(L, "subId", condition->getSubId()); + } + else + lua_pushboolean(L, false); + } else { errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); @@ -8104,9 +9063,9 @@ int32_t LuaScriptInterface::luaGetCreatureCondition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerBlessing(lua_State* L) +int32_t LuaInterface::luaGetPlayerBlessing(lua_State* L) { - //getPlayerBlessings(cid, blessing) + //getPlayerBlessing(cid, blessing) int16_t blessing = popNumber(L) - 1; ScriptEnviroment* env = getEnv(); @@ -8117,10 +9076,11 @@ int32_t LuaScriptInterface::luaGetPlayerBlessing(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddBlessing(lua_State* L) +int32_t LuaInterface::luaDoPlayerAddBlessing(lua_State* L) { //doPlayerAddBlessing(cid, blessing) int16_t blessing = popNumber(L) - 1; @@ -8140,10 +9100,48 @@ int32_t LuaScriptInterface::luaDoPlayerAddBlessing(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + + return 1; +} + +int32_t LuaInterface::luaGetPlayerPVPBlessing(lua_State* L) +{ + //getPlayerPVPBlessing(cid) + ScriptEnviroment* env = getEnv(); + if(Player* player = env->getPlayerByUID(popNumber(L))) + lua_pushboolean(L, player->hasPVPBlessing()); + else + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + } + + return 1; +} + +int32_t LuaInterface::luaDoPlayerSetPVPBlessing(lua_State* L) +{ + //doPlayerSetPVPBlessing(cid[, value]) + bool value = true; + if(lua_gettop(L) > 1) + value = popBoolean(L); + + ScriptEnviroment* env = getEnv(); + if(Player* player = env->getPlayerByUID(popNumber(L))) + { + player->setPVPBlessing(value); + lua_pushboolean(L, true); + } + else + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetPromotionLevel(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetPromotionLevel(lua_State* L) { //doPlayerSetPromotionLevel(cid, level) uint32_t level = popNumber(L); @@ -8158,10 +9156,11 @@ int32_t LuaScriptInterface::luaDoPlayerSetPromotionLevel(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetGroupId(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetGroupId(lua_State* L) { //doPlayerSetGroupId(cid, groupId) uint32_t groupId = popNumber(L); @@ -8181,10 +9180,11 @@ int32_t LuaScriptInterface::luaDoPlayerSetGroupId(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureMana(lua_State* L) +int32_t LuaInterface::luaGetCreatureMana(lua_State* L) { //getCreatureMana(cid) ScriptEnviroment* env = getEnv(); @@ -8195,24 +9195,30 @@ int32_t LuaScriptInterface::luaGetCreatureMana(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureMaxMana(lua_State* L) +int32_t LuaInterface::luaGetCreatureMaxMana(lua_State* L) { - //getCreatureMaxMana(cid) + //getCreatureMaxMana(cid[, ignoreModifiers = false]) + bool ignoreModifiers = false; + if(lua_gettop(L) > 1) + ignoreModifiers = popBoolean(L); + ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) - lua_pushnumber(L, creature->getMaxMana()); + lua_pushnumber(L, creature->getPlayer() && ignoreModifiers ? creature->manaMax : creature->getMaxMana()); else { errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureHealth(lua_State* L) +int32_t LuaInterface::luaGetCreatureHealth(lua_State* L) { //getCreatureHealth(cid) ScriptEnviroment* env = getEnv(); @@ -8223,10 +9229,11 @@ int32_t LuaScriptInterface::luaGetCreatureHealth(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureLookDirection(lua_State* L) +int32_t LuaInterface::luaGetCreatureLookDirection(lua_State* L) { //getCreatureLookDirection(cid) ScriptEnviroment* env = getEnv(); @@ -8238,24 +9245,30 @@ int32_t LuaScriptInterface::luaGetCreatureLookDirection(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureMaxHealth(lua_State* L) +int32_t LuaInterface::luaGetCreatureMaxHealth(lua_State* L) { - //getCreatureMaxHealth(cid) + //getCreatureMaxHealth(cid[, ignoreModifiers = false]) + bool ignoreModifiers = false; + if(lua_gettop(L) > 1) + ignoreModifiers = popBoolean(L); + ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) - lua_pushnumber(L, creature->getMaxHealth()); + lua_pushnumber(L, creature->getPlayer() && ignoreModifiers ? creature->healthMax : creature->getMaxHealth()); else { errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetStamina(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetStamina(lua_State* L) { //doPlayerSetStamina(cid, minutes) uint32_t minutes = popNumber(L); @@ -8264,6 +9277,7 @@ int32_t LuaScriptInterface::luaDoPlayerSetStamina(lua_State* L) if(Player* player = env->getPlayerByUID(popNumber(L))) { player->setStaminaMinutes(minutes); + player->sendStats(); lua_pushboolean(L, true); } else @@ -8271,18 +9285,19 @@ int32_t LuaScriptInterface::luaDoPlayerSetStamina(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerAddStamina(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetBalance(lua_State* L) { - //doPlayerAddStamina(cid, minutes) - int32_t minutes = popNumber(L); + //doPlayerSetBalance(cid, balance) + uint64_t balance = popNumber(L); ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) { - player->setStaminaMinutes(player->getStaminaMinutes() + minutes); + player->balance = balance; lua_pushboolean(L, true); } else @@ -8290,18 +9305,19 @@ int32_t LuaScriptInterface::luaDoPlayerAddStamina(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetBalance(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetPartner(lua_State* L) { - //doPlayerSetBalance(cid, balance) - uint32_t balance = popNumber(L); + //doPlayerSetPartner(cid, guid) + uint32_t guid = popNumber(L); ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) { - player->balance = balance; + player->marriage = guid; lua_pushboolean(L, true); } else @@ -8309,30 +9325,36 @@ int32_t LuaScriptInterface::luaDoPlayerSetBalance(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetPartner(lua_State* L) +int32_t LuaInterface::luaDoPlayerFollowCreature(lua_State* L) { - //doPlayerSetPartner(cid, guid) - uint32_t guid = popNumber(L); - + //doPlayerFollowCreature(cid, target) ScriptEnviroment* env = getEnv(); - if(Player* player = env->getPlayerByUID(popNumber(L))) + + Creature* creature = env->getCreatureByUID(popNumber(L)); + if(!creature) { - player->marriage = guid; - lua_pushboolean(L, true); + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; } - else + + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) { errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); + return 1; } + lua_pushboolean(L, g_game.playerFollowCreature(player->getID(), creature->getID())); return 1; } -int32_t LuaScriptInterface::luaGetPlayerParty(lua_State* L) +int32_t LuaInterface::luaGetPlayerParty(lua_State* L) { //getPlayerParty(cid) uint32_t cid = popNumber(L); @@ -8354,7 +9376,7 @@ int32_t LuaScriptInterface::luaGetPlayerParty(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerJoinParty(lua_State* L) +int32_t LuaInterface::luaDoPlayerJoinParty(lua_State* L) { //doPlayerJoinParty(cid, lid) ScriptEnviroment* env = getEnv(); @@ -8378,7 +9400,153 @@ int32_t LuaScriptInterface::luaDoPlayerJoinParty(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPartyMembers(lua_State* L) +int32_t LuaInterface::luaDoPlayerLeaveParty(lua_State* L) +{ + //doPlayerLeaveParty(cid[, forced = false]) + bool forced = false; + if(lua_gettop(L) > 1) + forced = popBoolean(L); + + ScriptEnviroment* env = getEnv(); + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + } + + g_game.playerLeaveParty(player->getID(), forced); + lua_pushboolean(L, true); + return 1; +} + +int32_t LuaInterface::luaDoPlayerAddMount(lua_State* L) +{ + //doPlayerAddMount(cid, mountId) + uint8_t mountId = (uint8_t)popNumber(L); + ScriptEnviroment* env = getEnv(); + + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + } + else + lua_pushboolean(L, player->tameMount(mountId)); + + return 1; +} + +int32_t LuaInterface::luaDoPlayerRemoveMount(lua_State* L) +{ + //doPlayerRemoveMount(cid, mountId) + uint8_t mountId = (uint8_t)popNumber(L); + ScriptEnviroment* env = getEnv(); + + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + } + else + lua_pushboolean(L, player->untameMount(mountId)); + + return 1; +} + +int32_t LuaInterface::luaCanPlayerRideMount(lua_State* L) +{ + //canPlayerRideMount(cid, mountId) + uint8_t mountId = popNumber(L); + ScriptEnviroment* env = getEnv(); + + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + } + else if(Mount* mount = Mounts::getInstance()->getMountById(mountId)) + lua_pushboolean(L, mount->isTamed(player)); + else + lua_pushboolean(L, false); + + return 1; +} + +int32_t LuaInterface::luaDoPlayerSetMounted(lua_State* L) +{ + //doPlayerSetMounted(cid, mounting[, force]) + bool force = true; + if(lua_gettop(L) > 2) + force = popBoolean(L); + + bool mounting = popBoolean(L); + ScriptEnviroment* env = getEnv(); + + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + } + else + { + Mount* mount = Mounts::getInstance()->getMountByCid(player->getDefaultOutfit().lookMount); + if(mount && (force || mount->isTamed(player))) + { + player->setMounted(mounting); + lua_pushboolean(L, true); + } + else + lua_pushboolean(L, false); + } + + return 1; +} + +int32_t LuaInterface::luaGetMountInfo(lua_State* L) +{ + //getMountInfo([mountId]) + uint16_t mountId = 0; + if(lua_gettop(L) > 0) + mountId = popNumber(L); + + if(mountId) + { + Mount* mount = Mounts::getInstance()->getMountById(mountId); + if(!mount && !(mount = Mounts::getInstance()->getMountByCid(mountId))) + { + lua_pushboolean(L, false); + return 1; + } + + lua_newtable(L); + setField(L, "name", mount->getName().c_str()); + setField(L, "clientId", mount->getClientId()); + setField(L, "speed", mount->getSpeed()); + setField(L, "attackSpeed", mount->getAttackSpeed()); + return 1; + } + + lua_newtable(L); + MountList::const_iterator it = Mounts::getInstance()->getFirstMount(); + for(uint32_t i = 1; it != Mounts::getInstance()->getLastMount(); ++it, ++i) + { + createTable(L, i); + setField(L, "id", (*it)->getId()); + setField(L, "name", (*it)->getName().c_str()); + setField(L, "speed", (*it)->getSpeed()); + setField(L, "clientId", (*it)->getClientId()); + pushTable(L); + } + + return 1; +} + +int32_t LuaInterface::luaGetPartyMembers(lua_State* L) { //getPartyMembers(cid) ScriptEnviroment* env = getEnv(); @@ -8406,7 +9574,7 @@ int32_t LuaScriptInterface::luaGetPartyMembers(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetVocationInfo(lua_State* L) +int32_t LuaInterface::luaGetVocationInfo(lua_State* L) { //getVocationInfo(id) uint32_t id = popNumber(L); @@ -8441,12 +9609,12 @@ int32_t LuaScriptInterface::luaGetVocationInfo(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetGroupInfo(lua_State* L) +int32_t LuaInterface::luaGetGroupInfo(lua_State* L) { - //getGroupInfo(id[, premium]) + //getGroupInfo(id[, premium = false]) bool premium = false; if(lua_gettop(L) > 1) - premium = popNumber(L); + premium = popBoolean(L); Group* group = Groups::getInstance()->getGroup(popNumber(L)); if(!group) @@ -8460,9 +9628,6 @@ int32_t LuaScriptInterface::luaGetGroupInfo(lua_State* L) setField(L, "name", group->getName().c_str()); setField(L, "access", group->getAccess()); setField(L, "ghostAccess", group->getGhostAccess()); - setField(L, "violationReasons", group->getViolationReasons()); - setField(L, "statementViolationFlags", group->getStatementViolationFlags()); - setField(L, "nameViolationFlags", group->getNameViolationFlags()); setField(L, "flags", group->getFlags()); setField(L, "customFlags", group->getCustomFlags()); setField(L, "depotLimit", group->getDepotLimit(premium)); @@ -8471,7 +9636,7 @@ int32_t LuaScriptInterface::luaGetGroupInfo(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetChannelUsers(lua_State* L) +int32_t LuaInterface::luaGetChannelUsers(lua_State* L) { //getChannelUsers(channelId) ScriptEnviroment* env = getEnv(); @@ -8496,7 +9661,7 @@ int32_t LuaScriptInterface::luaGetChannelUsers(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayersOnline(lua_State* L) +int32_t LuaInterface::luaGetPlayersOnline(lua_State* L) { //getPlayersOnline() ScriptEnviroment* env = getEnv(); @@ -8509,10 +9674,11 @@ int32_t LuaScriptInterface::luaGetPlayersOnline(lua_State* L) lua_pushnumber(L, env->addThing(it->second)); pushTable(L); } + return 1; } -int32_t LuaScriptInterface::luaSetCreatureMaxHealth(lua_State* L) +int32_t LuaInterface::luaSetCreatureMaxHealth(lua_State* L) { //setCreatureMaxHealth(uid, health) uint32_t maxHealth = (uint32_t)popNumber(L); @@ -8528,10 +9694,11 @@ int32_t LuaScriptInterface::luaSetCreatureMaxHealth(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaSetCreatureMaxMana(lua_State* L) +int32_t LuaInterface::luaSetCreatureMaxMana(lua_State* L) { //setCreatureMaxMana(uid, mana) uint32_t maxMana = (uint32_t)popNumber(L); @@ -8547,10 +9714,11 @@ int32_t LuaScriptInterface::luaSetCreatureMaxMana(lua_State* L) errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetMaxCapacity(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetMaxCapacity(lua_State* L) { //doPlayerSetMaxCapacity(uid, cap) double cap = popFloatNumber(L); @@ -8566,14 +9734,14 @@ int32_t LuaScriptInterface::luaDoPlayerSetMaxCapacity(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetCreatureMaster(lua_State* L) +int32_t LuaInterface::luaGetCreatureMaster(lua_State* L) { //getCreatureMaster(cid) uint32_t cid = popNumber(L); - ScriptEnviroment* env = getEnv(); Creature* creature = env->getCreatureByUID(cid); @@ -8584,12 +9752,15 @@ int32_t LuaScriptInterface::luaGetCreatureMaster(lua_State* L) return 1; } - Creature* master = creature->getMaster(); - lua_pushnumber(L, master ? env->addThing(master) : cid); + if(Creature* master = creature->getMaster()) + lua_pushnumber(L, env->addThing(master)); + else + lua_pushnil(L); + return 1; } -int32_t LuaScriptInterface::luaGetCreatureSummons(lua_State* L) +int32_t LuaInterface::luaGetCreatureSummons(lua_State* L) { //getCreatureSummons(cid) ScriptEnviroment* env = getEnv(); @@ -8616,7 +9787,7 @@ int32_t LuaScriptInterface::luaGetCreatureSummons(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetIdleTime(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetIdleTime(lua_State* L) { //doPlayerSetIdleTime(cid, amount) int64_t amount = popNumber(L); @@ -8631,18 +9802,20 @@ int32_t LuaScriptInterface::luaDoPlayerSetIdleTime(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoCreatureSetNoMove(lua_State* L) +int32_t LuaInterface::luaDoCreatureSetNoMove(lua_State* L) { //doCreatureSetNoMove(cid, block) - bool block = popNumber(L); + bool block = popBoolean(L); ScriptEnviroment* env = getEnv(); if(Creature* creature = env->getCreatureByUID(popNumber(L))) { creature->setNoMove(block); + creature->onWalkAborted(); lua_pushboolean(L, true); } else @@ -8654,7 +9827,27 @@ int32_t LuaScriptInterface::luaDoCreatureSetNoMove(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetPlayerRates(lua_State* L) +int32_t LuaInterface::luaGetPlayerModes(lua_State* L) +{ + //getPlayerModes(cid) + ScriptEnviroment* env = getEnv(); + + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + lua_newtable(L); + setField(L, "chase", player->getChaseMode()); + setField(L, "fight", player->getFightMode()); + setField(L, "secure", player->getSecureMode()); + return 1; +} + +int32_t LuaInterface::luaGetPlayerRates(lua_State* L) { //getPlayerRates(cid) ScriptEnviroment* env = getEnv(); @@ -8678,7 +9871,7 @@ int32_t LuaScriptInterface::luaGetPlayerRates(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerSetRate(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetRate(lua_State* L) { //doPlayerSetRate(cid, type, value) float value = popFloatNumber(L); @@ -8704,7 +9897,7 @@ int32_t LuaScriptInterface::luaDoPlayerSetRate(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoPlayerSwitchSaving(lua_State* L) +int32_t LuaInterface::luaDoPlayerSwitchSaving(lua_State* L) { //doPlayerSwitchSaving(cid) ScriptEnviroment* env = getEnv(); @@ -8718,28 +9911,33 @@ int32_t LuaScriptInterface::luaDoPlayerSwitchSaving(lua_State* L) errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaDoPlayerSave(lua_State* L) +int32_t LuaInterface::luaDoPlayerSave(lua_State* L) { //doPlayerSave(cid[, shallow = false]) bool shallow = false; if(lua_gettop(L) > 1) - shallow = popNumber(L); + shallow = popBoolean(L); ScriptEnviroment* env = getEnv(); if(Player* player = env->getPlayerByUID(popNumber(L))) + { + player->loginPosition = player->getPosition(); lua_pushboolean(L, IOLoginData::getInstance()->savePlayer(player, false, shallow)); + } else { errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); } + return 1; } -int32_t LuaScriptInterface::luaGetTownId(lua_State* L) +int32_t LuaInterface::luaGetTownId(lua_State* L) { //getTownId(townName) std::string townName = popString(L); @@ -8751,7 +9949,7 @@ int32_t LuaScriptInterface::luaGetTownId(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetTownName(lua_State* L) +int32_t LuaInterface::luaGetTownName(lua_State* L) { //getTownName(townId) uint32_t townId = popNumber(L); @@ -8763,13 +9961,9 @@ int32_t LuaScriptInterface::luaGetTownName(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetTownTemplePosition(lua_State* L) +int32_t LuaInterface::luaGetTownTemplePosition(lua_State* L) { //getTownTemplePosition(townId) - bool displayError = true; - if(lua_gettop(L) >= 2) - displayError = popNumber(L); - uint32_t townId = popNumber(L); if(Town* town = Towns::getInstance()->getTown(townId)) pushPosition(L, town->getPosition(), 255); @@ -8779,9 +9973,9 @@ int32_t LuaScriptInterface::luaGetTownTemplePosition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetTownHouses(lua_State* L) +int32_t LuaInterface::luaGetTownHouses(lua_State* L) { - //getTownHouses(townId) + //getTownHouses([townId]) uint32_t townId = 0; if(lua_gettop(L) > 0) townId = popNumber(L); @@ -8790,7 +9984,7 @@ int32_t LuaScriptInterface::luaGetTownHouses(lua_State* L) lua_newtable(L); for(uint32_t i = 1; it != Houses::getInstance()->getHouseEnd(); ++i, ++it) { - if(townId != 0 && it->second->getTownId() != townId) + if(townId && it->second->getTownId() != townId) continue; lua_pushnumber(L, i); @@ -8801,12 +9995,12 @@ int32_t LuaScriptInterface::luaGetTownHouses(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetSpectators(lua_State* L) +int32_t LuaInterface::luaGetSpectators(lua_State* L) { //getSpectators(centerPos, rangex, rangey[, multifloor = false]) bool multifloor = false; if(lua_gettop(L) > 3) - multifloor = popNumber(L); + multifloor = popBoolean(L); uint32_t rangey = popNumber(L), rangex = popNumber(L); PositionEx centerPos; @@ -8834,7 +10028,7 @@ int32_t LuaScriptInterface::luaGetSpectators(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetHighscoreString(lua_State* L) +int32_t LuaInterface::luaGetHighscoreString(lua_State* L) { //getHighscoreString(skillId) uint16_t skillId = popNumber(L); @@ -8846,7 +10040,77 @@ int32_t LuaScriptInterface::luaGetHighscoreString(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetWaypointList(lua_State* L) +int32_t LuaInterface::luaGetVocationList(lua_State* L) +{ + //getVocationList() + VocationsMap::iterator it = Vocations::getInstance()->getFirstVocation(); + lua_newtable(L); + for(uint32_t i = 1; it != Vocations::getInstance()->getLastVocation(); ++i, ++it) + { + createTable(L, i); + setField(L, "id", it->first); + setField(L, "name", it->second->getName()); + pushTable(L); + } + + return 1; +} + +int32_t LuaInterface::luaGetGroupList(lua_State* L) +{ + //getGroupList() + GroupsMap::iterator it = Groups::getInstance()->getFirstGroup(); + lua_newtable(L); + for(uint32_t i = 1; it != Groups::getInstance()->getLastGroup(); ++i, ++it) + { + createTable(L, i); + setField(L, "id", it->first); + setField(L, "name", it->second->getName()); + pushTable(L); + } + + return 1; +} + +int32_t LuaInterface::luaGetChannelList(lua_State* L) +{ + //getChannelList() + lua_newtable(L); + ChannelList list = g_chat.getPublicChannels(); + + ChannelList::const_iterator it = list.begin(); + for(uint32_t i = 1; it != list.end(); ++it, ++i) + { + createTable(L, i); + setField(L, "id", (*it)->getId()); + setField(L, "name", (*it)->getName()); + + setField(L, "flags", (*it)->getFlags()); + setField(L, "level", (*it)->getLevel()); + setField(L, "access", (*it)->getAccess()); + pushTable(L); + } + + return 1; +} + +int32_t LuaInterface::luaGetTownList(lua_State* L) +{ + //getTownList() + TownMap::const_iterator it = Towns::getInstance()->getFirstTown(); + lua_newtable(L); + for(uint32_t i = 1; it != Towns::getInstance()->getLastTown(); ++it, ++i) + { + createTable(L, i); + setField(L, "id", it->first); + setField(L, "name", it->second->getName()); + pushTable(L); + } + + return 1; +} + +int32_t LuaInterface::luaGetWaypointList(lua_State* L) { //getWaypointList() WaypointMap waypointsMap = g_game.getMap()->waypoints.getWaypointsMap(); @@ -8864,7 +10128,7 @@ int32_t LuaScriptInterface::luaGetWaypointList(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetWaypointPosition(lua_State* L) +int32_t LuaInterface::luaGetWaypointPosition(lua_State* L) { //getWaypointPosition(name) if(WaypointPtr waypoint = g_game.getMap()->waypoints.getWaypointByName(popString(L))) @@ -8875,7 +10139,7 @@ int32_t LuaScriptInterface::luaGetWaypointPosition(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoWaypointAddTemporial(lua_State* L) +int32_t LuaInterface::luaDoWaypointAddTemporial(lua_State* L) { //doWaypointAddTemporial(name, pos) PositionEx pos; @@ -8886,20 +10150,21 @@ int32_t LuaScriptInterface::luaDoWaypointAddTemporial(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetGameState(lua_State* L) +int32_t LuaInterface::luaGetGameState(lua_State* L) { //getGameState() lua_pushnumber(L, g_game.getGameState()); return 1; } -int32_t LuaScriptInterface::luaDoSetGameState(lua_State* L) +int32_t LuaInterface::luaDoSetGameState(lua_State* L) { //doSetGameState(id) uint32_t id = popNumber(L); - if(id >= GAME_STATE_FIRST && id <= GAME_STATE_LAST) + if(id >= GAMESTATE_FIRST && id <= GAMESTATE_LAST) { - Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, &g_game, (GameState_t)id))); + Dispatcher::getInstance().addTask(createTask( + boost::bind(&Game::setGameState, &g_game, (GameState_t)id))); lua_pushboolean(L, true); } else @@ -8908,16 +10173,16 @@ int32_t LuaScriptInterface::luaDoSetGameState(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCreatureExecuteTalkAction(lua_State* L) +int32_t LuaInterface::luaDoCreatureExecuteTalkAction(lua_State* L) { - //doCreatureExecuteTalkAction(cid, text[, ignoreAccess[, channelId]]) + //doCreatureExecuteTalkAction(cid, text[, ignoreAccess = false[, channelId = CHANNEL_DEFAULT]]) uint32_t params = lua_gettop(L), channelId = CHANNEL_DEFAULT; if(params > 3) channelId = popNumber(L); bool ignoreAccess = false; if(params > 2) - ignoreAccess = popNumber(L); + ignoreAccess = popBoolean(L); std::string text = popString(L); ScriptEnviroment* env = getEnv(); @@ -8932,7 +10197,7 @@ int32_t LuaScriptInterface::luaDoCreatureExecuteTalkAction(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoExecuteRaid(lua_State* L) +int32_t LuaInterface::luaDoExecuteRaid(lua_State* L) { //doExecuteRaid(name) std::string raidName = popString(L); @@ -8945,7 +10210,7 @@ int32_t LuaScriptInterface::luaDoExecuteRaid(lua_State* L) Raid* raid = Raids::getInstance()->getRaidByName(raidName); if(!raid || !raid->isLoaded()) { - errorEx("Raid with name " + raidName + " does not exists."); + errorEx("Raid with name " + raidName + " does not exists"); lua_pushboolean(L, false); return 1; } @@ -8954,7 +10219,7 @@ int32_t LuaScriptInterface::luaDoExecuteRaid(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoReloadInfo(lua_State* L) +int32_t LuaInterface::luaDoReloadInfo(lua_State* L) { //doReloadInfo(id[, cid]) uint32_t cid = 0; @@ -8964,8 +10229,10 @@ int32_t LuaScriptInterface::luaDoReloadInfo(lua_State* L) uint32_t id = popNumber(L); if(id >= RELOAD_FIRST && id <= RELOAD_LAST) { + // we're passing it to scheduler since talkactions reload will + // re-init our lua state and crash due to unfinished call Scheduler::getInstance().addEvent(createSchedulerTask(SCHEDULER_MINTICKS, - boost::bind(&Game::reloadInfo, &g_game, (ReloadInfo_t)id, cid))); + boost::bind(&Game::reloadInfo, &g_game, (ReloadInfo_t)id, cid, false))); lua_pushboolean(L, true); } else @@ -8974,18 +10241,80 @@ int32_t LuaScriptInterface::luaDoReloadInfo(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoSaveServer(lua_State* L) +int32_t LuaInterface::luaDoSaveServer(lua_State* L) { - //doSaveServer([shallow]) - bool shallow = false; + //doSaveServer([flags = 13]) + uint8_t flags = 13; if(lua_gettop(L) > 0) - shallow = popNumber(L); + flags = popNumber(L); + + Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::saveGameState, &g_game, flags))); + lua_pushnil(L); + return 1; +} + +int32_t LuaInterface::luaDoSaveHouse(lua_State* L) +{ + //doSaveHouse({list}) + IntegerVec list; + if(lua_istable(L, -1)) + { + lua_pushnil(L); + while(lua_next(L, -2)) + list.push_back(popNumber(L)); + + lua_pop(L, 2); + } + else + list.push_back(popNumber(L)); + + House* house; + std::vector houses; + for(IntegerVec::const_iterator it = list.begin(); it != list.end(); ++it) + { + if(!(house = Houses::getInstance()->getHouse(*it))) + { + std::stringstream s; + s << "House not found, ID: " << (*it); + errorEx(s.str()); + + lua_pushboolean(L, false); + return 1; + } + + houses.push_back(house); + } + + Database* db = Database::getInstance(); + DBTransaction trans(db); + if(!trans.begin()) + { + lua_pushboolean(L, false); + return 1; + } + + for(std::vector::iterator it = houses.begin(); it != houses.end(); ++it) + { + if(!IOMapSerialize::getInstance()->saveHouse(db, *it)) + { + std::stringstream s; + s << "Unable to save house information, ID: " << (*it)->getId(); + errorEx(s.str()); + } + + if(!IOMapSerialize::getInstance()->saveHouseItems(db, *it)) + { + std::stringstream s; + s << "Unable to save house items, ID: " << (*it)->getId(); + errorEx(s.str()); + } + } - Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::saveGameState, &g_game, shallow))); + lua_pushboolean(L, trans.commit()); return 1; } -int32_t LuaScriptInterface::luaDoCleanHouse(lua_State* L) +int32_t LuaInterface::luaDoCleanHouse(lua_State* L) { //doCleanHouse(houseId) uint32_t houseId = popNumber(L); @@ -9000,42 +10329,37 @@ int32_t LuaScriptInterface::luaDoCleanHouse(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoCleanMap(lua_State* L) +int32_t LuaInterface::luaDoCleanMap(lua_State* L) { //doCleanMap() uint32_t count = 0; - g_game.cleanMap(count); + g_game.cleanMapEx(count); lua_pushnumber(L, count); return 1; } -int32_t LuaScriptInterface::luaDoRefreshMap(lua_State* L) +int32_t LuaInterface::luaDoRefreshMap(lua_State* L) { //doRefreshMap() g_game.proceduralRefresh(); + lua_pushnil(L); return 1; } -int32_t LuaScriptInterface::luaDoUpdateHouseAuctions(lua_State* L) +int32_t LuaInterface::luaDoUpdateHouseAuctions(lua_State* L) { //doUpdateHouseAuctions() - lua_pushboolean(L, IOMapSerialize::getInstance()->updateAuctions()); + lua_pushboolean(L, g_game.getMap()->updateAuctions()); return 1; } -int32_t LuaScriptInterface::luaGetItemIdByName(lua_State* L) +int32_t LuaInterface::luaGetItemIdByName(lua_State* L) { - //getItemIdByName(name[, displayError = true]) - bool displayError = true; - if(lua_gettop(L) >= 2) - displayError = popNumber(L); - + //getItemIdByName(name) int32_t itemId = Item::items.getItemIdByName(popString(L)); if(itemId == -1) { - if(displayError) - errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND)); - + errorEx(getError(LUA_ERROR_ITEM_NOT_FOUND)); lua_pushboolean(L, false); } else @@ -9044,7 +10368,7 @@ int32_t LuaScriptInterface::luaGetItemIdByName(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetItemInfo(lua_State* L) +int32_t LuaInterface::luaGetItemInfo(lua_State* L) { //getItemInfo(itemid) const ItemType* item; @@ -9057,11 +10381,10 @@ int32_t LuaScriptInterface::luaGetItemInfo(lua_State* L) lua_newtable(L); setFieldBool(L, "stopTime", item->stopTime); setFieldBool(L, "showCount", item->showCount); - setFieldBool(L, "clientCharges", item->clientCharges); setFieldBool(L, "stackable", item->stackable); setFieldBool(L, "showDuration", item->showDuration); setFieldBool(L, "showCharges", item->showCharges); - setFieldBool(L, "showAttributes", item->showCharges); + setFieldBool(L, "showAttributes", item->showAttributes); setFieldBool(L, "distRead", item->allowDistRead); setFieldBool(L, "readable", item->canReadText); setFieldBool(L, "writable", item->canWriteText); @@ -9069,11 +10392,11 @@ int32_t LuaScriptInterface::luaGetItemInfo(lua_State* L) setFieldBool(L, "vertical", item->isVertical); setFieldBool(L, "horizontal", item->isHorizontal); setFieldBool(L, "hangable", item->isHangable); - setFieldBool(L, "usable", item->useable); - setFieldBool(L, "movable", item->moveable); + setFieldBool(L, "usable", item->usable); + setFieldBool(L, "movable", item->movable); setFieldBool(L, "pickupable", item->pickupable); setFieldBool(L, "rotable", item->rotable); - setFieldBool(L, "replacable", item->replaceable); + setFieldBool(L, "replacable", item->replacable); setFieldBool(L, "hasHeight", item->hasHeight); setFieldBool(L, "blockSolid", item->blockSolid); setFieldBool(L, "blockPickupable", item->blockPickupable); @@ -9101,12 +10424,12 @@ int32_t LuaScriptInterface::luaGetItemInfo(lua_State* L) setField(L, "shootType", (int32_t)item->shootType); setField(L, "ammoType", (int32_t)item->ammoType); - createTable(L, "transformUseTo"); - setField(L, "female", item->transformUseTo[PLAYERSEX_FEMALE]); - setField(L, "male", item->transformUseTo[PLAYERSEX_MALE]); + createTable(L, "transformBed"); + setField(L, "female", item->transformBed[PLAYERSEX_FEMALE]); + setField(L, "male", item->transformBed[PLAYERSEX_MALE]); pushTable(L); - setField(L, "transformToFree", item->transformToFree); + setField(L, "transformUseTo", item->transformUseTo); setField(L, "transformEquipTo", item->transformEquipTo); setField(L, "transformDeEquipTo", item->transformDeEquipTo); setField(L, "clientId", item->clientId); @@ -9114,8 +10437,11 @@ int32_t LuaScriptInterface::luaGetItemInfo(lua_State* L) setField(L, "slotPosition", item->slotPosition); setField(L, "wieldPosition", item->wieldPosition); setField(L, "speed", item->speed); - setField(L, "maxTextLength", item->maxTextLen); + setField(L, "maxTextLength", item->maxTextLength); setField(L, "writeOnceItemId", item->writeOnceItemId); + setField(L, "date", item->date); + setField(L, "writer", item->writer); + setField(L, "text", item->text); setField(L, "attack", item->attack); setField(L, "extraAttack", item->extraAttack); setField(L, "defense", item->defense); @@ -9140,6 +10466,8 @@ int32_t LuaScriptInterface::luaGetItemInfo(lua_State* L) setField(L, "minRequiredMagicLevel", item->minReqMagicLevel); setField(L, "worth", item->worth); setField(L, "levelDoor", item->levelDoor); + setFieldBool(L, "specialDoor", item->specialDoor); + setFieldBool(L, "closingDoor", item->closingDoor); setField(L, "name", item->name.c_str()); setField(L, "plural", item->pluralName.c_str()); setField(L, "article", item->article.c_str()); @@ -9148,19 +10476,19 @@ int32_t LuaScriptInterface::luaGetItemInfo(lua_State* L) setField(L, "vocationString", item->vocationString.c_str()); createTable(L, "abilities"); - setFieldBool(L, "manaShield", item->abilities.manaShield); - setFieldBool(L, "invisible", item->abilities.invisible); - setFieldBool(L, "regeneration", item->abilities.regeneration); - setFieldBool(L, "preventLoss", item->abilities.preventLoss); - setFieldBool(L, "preventDrop", item->abilities.preventDrop); - setField(L, "elementType", (int32_t)item->abilities.elementType); - setField(L, "elementDamage", item->abilities.elementDamage); - setField(L, "speed", item->abilities.speed); - setField(L, "healthGain", item->abilities.healthGain); - setField(L, "healthTicks", item->abilities.healthTicks); - setField(L, "manaGain", item->abilities.manaGain); - setField(L, "manaTicks", item->abilities.manaTicks); - setField(L, "conditionSuppressions", item->abilities.conditionSuppressions); + setFieldBool(L, "manaShield", item->hasAbilities() ? item->abilities->manaShield : false); + setFieldBool(L, "invisible", item->hasAbilities() ? item->abilities->invisible : false); + setFieldBool(L, "regeneration", item->hasAbilities() ? item->abilities->regeneration : false); + setFieldBool(L, "preventLoss", item->hasAbilities() ? item->abilities->preventLoss : false); + setFieldBool(L, "preventDrop", item->hasAbilities() ? item->abilities->preventDrop : false); + setField(L, "elementType", (int32_t)item->hasAbilities() ? item->abilities->elementType : 0); + setField(L, "elementDamage", item->hasAbilities() ? item->abilities->elementDamage : 0); + setField(L, "speed", item->hasAbilities() ? item->abilities->speed : 0); + setField(L, "healthGain", item->hasAbilities() ? item->abilities->healthGain : 0); + setField(L, "healthTicks", item->hasAbilities() ? item->abilities->healthTicks : 0); + setField(L, "manaGain", item->hasAbilities() ? item->abilities->manaGain : 0); + setField(L, "manaTicks", item->hasAbilities() ? item->abilities->manaTicks : 0); + setField(L, "conditionSuppressions", item->hasAbilities() ? item->abilities->conditionSuppressions : 0); //TODO: absorb, increment, reflect, skills, skillsPercent, stats, statsPercent @@ -9171,7 +10499,7 @@ int32_t LuaScriptInterface::luaGetItemInfo(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetItemAttribute(lua_State* L) +int32_t LuaInterface::luaGetItemAttribute(lua_State* L) { //getItemAttribute(uid, key) std::string key = popString(L); @@ -9202,7 +10530,7 @@ int32_t LuaScriptInterface::luaGetItemAttribute(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoItemSetAttribute(lua_State* L) +int32_t LuaInterface::luaDoItemSetAttribute(lua_State* L) { //doItemSetAttribute(uid, key, value) boost::any value; @@ -9251,7 +10579,7 @@ int32_t LuaScriptInterface::luaDoItemSetAttribute(lua_State* L) } item->setUniqueId(tmp); - } + } else if(key == "aid") item->setActionId(boost::any_cast(value)); else @@ -9264,7 +10592,7 @@ int32_t LuaScriptInterface::luaDoItemSetAttribute(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoItemEraseAttribute(lua_State* L) +int32_t LuaInterface::luaDoItemEraseAttribute(lua_State* L) { //doItemEraseAttribute(uid, key) std::string key = popString(L); @@ -9281,7 +10609,7 @@ int32_t LuaScriptInterface::luaDoItemEraseAttribute(lua_State* L) bool ret = true; if(key == "uid") { - errorEx("Attempt to erase protected key \"uid\"."); + errorEx("Attempt to erase protected key \"uid\""); ret = false; } else if(key != "aid") @@ -9293,12 +10621,12 @@ int32_t LuaScriptInterface::luaDoItemEraseAttribute(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetItemWeight(lua_State* L) +int32_t LuaInterface::luaGetItemWeight(lua_State* L) { //getItemWeight(itemid[, precise = true]) bool precise = true; if(lua_gettop(L) > 2) - precise = popNumber(L); + precise = popBoolean(L); ScriptEnviroment* env = getEnv(); Item* item = env->getItemByUID(popNumber(L)); @@ -9321,7 +10649,43 @@ int32_t LuaScriptInterface::luaGetItemWeight(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaHasItemProperty(lua_State* L) +int32_t LuaInterface::luaGetItemParent(lua_State* L) +{ + //getItemParent(uid) + ScriptEnviroment* env = getEnv(); + + Thing* thing = env->getThingByUID(popNumber(L)); + if(!thing) + { + errorEx(getError(LUA_ERROR_THING_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + if(!thing->getParent()) + { + pushThing(L, NULL, 0); + return 1; + } + + if(Tile* tile = thing->getParent()->getTile()) + { + if(tile->ground) + pushThing(L, tile->ground, env->addThing(tile->ground)); + else + pushThing(L, NULL, 0); + } + if(Item* container = thing->getParent()->getItem()) + pushThing(L, container, env->addThing(container)); + else if(Creature* creature = thing->getParent()->getCreature()) + pushThing(L, creature, env->addThing(creature)); + else + pushThing(L, NULL, 0); + + return 1; +} + +int32_t LuaInterface::luaHasItemProperty(lua_State* L) { //hasItemProperty(uid, prop) uint32_t prop = popNumber(L); @@ -9344,7 +10708,32 @@ int32_t LuaScriptInterface::luaHasItemProperty(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaIsIpBanished(lua_State* L) +int32_t LuaInterface::luaHasMonsterRaid(lua_State* L) +{ + //hasMonsterRaid(cid) + ScriptEnviroment* env = getEnv(); + + Creature* creature = env->getCreatureByUID(popNumber(L)); + if(!creature) + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + Monster* monster = creature->getMonster(); + if(!monster) + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + lua_pushboolean(L, monster->hasRaid()); + return 1; +} + +int32_t LuaInterface::luaIsIpBanished(lua_State* L) { //isIpBanished(ip[, mask]) uint32_t mask = 0xFFFFFFFF; @@ -9355,7 +10744,7 @@ int32_t LuaScriptInterface::luaIsIpBanished(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaIsPlayerBanished(lua_State* L) +int32_t LuaInterface::luaIsPlayerBanished(lua_State* L) { //isPlayerBanished(name/guid, type) PlayerBan_t type = (PlayerBan_t)popNumber(L); @@ -9367,7 +10756,7 @@ int32_t LuaScriptInterface::luaIsPlayerBanished(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaIsAccountBanished(lua_State* L) +int32_t LuaInterface::luaIsAccountBanished(lua_State* L) { //isAccountBanished(accountId[, playerId]) uint32_t playerId = 0; @@ -9378,24 +10767,18 @@ int32_t LuaScriptInterface::luaIsAccountBanished(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoAddIpBanishment(lua_State* L) +int32_t LuaInterface::luaDoAddIpBanishment(lua_State* L) { - //doAddIpBanishment(ip[, mask[, length[, reason[, comment[, admin[, statement]]]]]]) - uint32_t admin = 0, reason = 21, mask = 0xFFFFFFFF, params = lua_gettop(L); - int64_t length = time(NULL) + g_config.getNumber(ConfigManager::IPBANISHMENT_LENGTH); - std::string statement, comment; - - if(params > 6) - statement = popString(L); - - if(params > 5) - admin = popNumber(L); + //doAddIpBanishment(ip[, mask[, length[, comment[, admin]]]]]) + uint32_t admin = 0, mask = 0xFFFFFFFF, params = lua_gettop(L); + int64_t length = time(NULL) + g_config.getNumber(ConfigManager::IPBAN_LENGTH); + std::string comment; if(params > 4) - comment = popString(L); + admin = popNumber(L); if(params > 3) - reason = popNumber(L); + comment = popString(L); if(params > 2) length = popNumber(L); @@ -9403,34 +10786,23 @@ int32_t LuaScriptInterface::luaDoAddIpBanishment(lua_State* L) if(params > 1) mask = popNumber(L); - lua_pushboolean(L, IOBan::getInstance()->addIpBanishment((uint32_t)popNumber(L), - length, reason, comment, admin, mask, statement)); + lua_pushboolean(L, IOBan::getInstance()->addIpBanishment((uint32_t)popNumber(L), length, comment, admin, mask)); return 1; } -int32_t LuaScriptInterface::luaDoAddPlayerBanishment(lua_State* L) +int32_t LuaInterface::luaDoAddPlayerBanishment(lua_State* L) { - //doAddPlayerBanishment(name/guid[, type[, length[, reason[, action[, comment[, admin[, statement]]]]]]]) - uint32_t admin = 0, reason = 21, params = lua_gettop(L); + //doAddPlayerBanishment(name/guid[, type[, length[, comment[, admin]]]]]]) + uint32_t admin = 0, params = lua_gettop(L); int64_t length = -1; - std::string statement, comment; + std::string comment; - ViolationAction_t action = ACTION_NAMELOCK; PlayerBan_t type = PLAYERBAN_LOCK; - if(params > 7) - statement = popString(L); - - if(params > 6) - admin = popNumber(L); - - if(params > 5) - comment = popString(L); - if(params > 4) - action = (ViolationAction_t)popNumber(L); + admin = popNumber(L); if(params > 3) - reason = popNumber(L); + comment = popString(L); if(params > 2) length = popNumber(L); @@ -9439,38 +10811,25 @@ int32_t LuaScriptInterface::luaDoAddPlayerBanishment(lua_State* L) type = (PlayerBan_t)popNumber(L); if(lua_isnumber(L, -1)) - lua_pushboolean(L, IOBan::getInstance()->addPlayerBanishment((uint32_t)popNumber(L), - length, reason, action, comment, admin, type, statement)); + lua_pushboolean(L, IOBan::getInstance()->addPlayerBanishment((uint32_t)popNumber(L), length, comment, admin, type)); else - lua_pushboolean(L, IOBan::getInstance()->addPlayerBanishment(popString(L), - length, reason, action, comment, admin, type, statement)); + lua_pushboolean(L, IOBan::getInstance()->addPlayerBanishment(popString(L), length, comment, admin, type)); return 1; } -int32_t LuaScriptInterface::luaDoAddAccountBanishment(lua_State* L) +int32_t LuaInterface::luaDoAddAccountBanishment(lua_State* L) { - //doAddAccountBanishment(accountId[, playerId[, length[, reason[, action[, comment[, admin[, statement]]]]]]]) - uint32_t admin = 0, reason = 21, playerId = 0, params = lua_gettop(L); + //doAddAccountBanishment(accountId[, playerId[, length[, comment[, admin]]]]]]) + uint32_t admin = 0, playerId = 0, params = lua_gettop(L); int64_t length = time(NULL) + g_config.getNumber(ConfigManager::BAN_LENGTH); - std::string statement, comment; - - ViolationAction_t action = ACTION_BANISHMENT; - if(params > 7) - statement = popString(L); - - if(params > 6) - admin = popNumber(L); - - if(params > 5) - comment = popString(L); + std::string comment; if(params > 4) - action = (ViolationAction_t)popNumber(L); + admin = popNumber(L); if(params > 3) - - reason = popNumber(L); + comment = popString(L); if(params > 2) length = popNumber(L); @@ -9478,70 +10837,46 @@ int32_t LuaScriptInterface::luaDoAddAccountBanishment(lua_State* L) if(params > 1) playerId = popNumber(L); - lua_pushboolean(L, IOBan::getInstance()->addAccountBanishment((uint32_t)popNumber(L), - length, reason, action, comment, admin, playerId, statement)); + lua_pushboolean(L, IOBan::getInstance()->addAccountBanishment((uint32_t)popNumber(L), length, comment, admin, playerId)); return 1; } -int32_t LuaScriptInterface::luaDoAddNotation(lua_State* L) +int32_t LuaInterface::luaDoAddAccountWarnings(lua_State* L) { - //doAddNotation(accountId[, playerId[, reason[, comment[, admin[, statement]]]]]]) - uint32_t admin = 0, reason = 21, playerId = 0, params = lua_gettop(L); - std::string statement, comment; - - if(params > 5) - statement = popString(L); - - if(params > 4) - admin = popNumber(L); - - if(params > 3) - comment = popString(L); - - if(params > 2) - reason = popNumber(L); - + //doAddAccountWarnings(accountId[, warnings]) + uint32_t warnings = 1; + int32_t params = lua_gettop(L); if(params > 1) - playerId = popNumber(L); + warnings = popNumber(L); + + Account account = IOLoginData::getInstance()->loadAccount(popNumber(L), true); + account.warnings += warnings; - lua_pushboolean(L, IOBan::getInstance()->addNotation((uint32_t)popNumber(L), - reason, comment, admin, playerId, statement)); + IOLoginData::getInstance()->saveAccount(account); + lua_pushboolean(L, true); return 1; } -int32_t LuaScriptInterface::luaDoAddStatement(lua_State* L) +int32_t LuaInterface::luaDoAddNotation(lua_State* L) { - //doAddStatement(name/guid[, channelId[, reason[, comment[, admin[, statement]]]]]]) - uint32_t admin = 0, reason = 21, params = lua_gettop(L); - int16_t channelId = -1; - std::string statement, comment; - - if(params > 5) - statement = popString(L); - - if(params > 4) - admin = popNumber(L); + //doAddNotation(accountId[, playerId[, comment[, admin]]]]]) + uint32_t admin = 0, playerId = 0, params = lua_gettop(L); + std::string comment; if(params > 3) - comment = popString(L); + admin = popNumber(L); if(params > 2) - reason = popNumber(L); + comment = popString(L); if(params > 1) - channelId = popNumber(L); - - if(lua_isnumber(L, -1)) - lua_pushboolean(L, IOBan::getInstance()->addStatement((uint32_t)popNumber(L), - reason, comment, admin, channelId, statement)); - else - lua_pushboolean(L, IOBan::getInstance()->addStatement(popString(L), - reason, comment, admin, channelId, statement)); + playerId = popNumber(L); + lua_pushboolean(L, IOBan::getInstance()->addNotation((uint32_t)popNumber(L), comment, admin, playerId)); return 1; } -int32_t LuaScriptInterface::luaDoRemoveIpBanishment(lua_State* L) +int32_t LuaInterface::luaDoRemoveIpBanishment(lua_State* L) { //doRemoveIpBanishment(ip[, mask]) uint32_t mask = 0xFFFFFFFF; @@ -9553,7 +10888,7 @@ int32_t LuaScriptInterface::luaDoRemoveIpBanishment(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoRemovePlayerBanishment(lua_State* L) +int32_t LuaInterface::luaDoRemovePlayerBanishment(lua_State* L) { //doRemovePlayerBanishment(name/guid, type) PlayerBan_t type = (PlayerBan_t)popNumber(L); @@ -9565,7 +10900,7 @@ int32_t LuaScriptInterface::luaDoRemovePlayerBanishment(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoRemoveAccountBanishment(lua_State* L) +int32_t LuaInterface::luaDoRemoveAccountBanishment(lua_State* L) { //doRemoveAccountBanishment(accountId[, playerId]) uint32_t playerId = 0; @@ -9576,7 +10911,23 @@ int32_t LuaScriptInterface::luaDoRemoveAccountBanishment(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoRemoveNotations(lua_State* L) +int32_t LuaInterface::luaDoRemoveAccountWarnings(lua_State* L) +{ + //doRemoveAccountWarnings(accountId[, warnings]) + uint32_t warnings = 1; + int32_t params = lua_gettop(L); + if(params > 1) + warnings = popNumber(L); + + Account account = IOLoginData::getInstance()->loadAccount(popNumber(L), true); + account.warnings -= warnings; + + IOLoginData::getInstance()->saveAccount(account); + lua_pushboolean(L, true); + return 1; +} + +int32_t LuaInterface::luaDoRemoveNotations(lua_State* L) { //doRemoveNotations(accountId[, playerId]) uint32_t playerId = 0; @@ -9587,22 +10938,15 @@ int32_t LuaScriptInterface::luaDoRemoveNotations(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDoRemoveStatements(lua_State* L) +int32_t LuaInterface::luaGetAccountWarnings(lua_State* L) { - //doRemoveStatements(name/guid[, channelId]) - int16_t channelId = -1; - if(lua_gettop(L) > 1) - channelId = popNumber(L); - - if(lua_isnumber(L, -1)) - lua_pushboolean(L, IOBan::getInstance()->removeStatements((uint32_t)popNumber(L), channelId)); - else - lua_pushboolean(L, IOBan::getInstance()->removeStatements(popString(L), channelId)); - + //getAccountWarnings(accountId) + Account account = IOLoginData::getInstance()->loadAccount(popNumber(L)); + lua_pushnumber(L, account.warnings); return 1; } -int32_t LuaScriptInterface::luaGetNotationsCount(lua_State* L) +int32_t LuaInterface::luaGetNotationsCount(lua_State* L) { //getNotationsCount(accountId[, playerId]) uint32_t playerId = 0; @@ -9613,22 +10957,7 @@ int32_t LuaScriptInterface::luaGetNotationsCount(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetStatementsCount(lua_State* L) -{ - //getStatementsCount(name/guid[, channelId]) - int16_t channelId = -1; - if(lua_gettop(L) > 1) - channelId = popNumber(L); - - if(lua_isnumber(L, -1)) - lua_pushnumber(L, IOBan::getInstance()->getStatementsCount((uint32_t)popNumber(L), channelId)); - else - lua_pushnumber(L, IOBan::getInstance()->getStatementsCount(popString(L), channelId)); - - return 1; -} - -int32_t LuaScriptInterface::luaGetBanData(lua_State* L) +int32_t LuaInterface::luaGetBanData(lua_State* L) { //getBanData(value[, type[, param]]) Ban tmp; @@ -9654,32 +10983,11 @@ int32_t LuaScriptInterface::luaGetBanData(lua_State* L) setField(L, "added", tmp.added); setField(L, "expires", tmp.expires); setField(L, "adminId", tmp.adminId); - setField(L, "reason", tmp.reason); - setField(L, "action", tmp.action); setField(L, "comment", tmp.comment); - setField(L, "statement", tmp.statement); - return 1; -} - -int32_t LuaScriptInterface::luaGetBanReason(lua_State* L) -{ - //getBanReason(id) - lua_pushstring(L, getReason((ViolationAction_t)popNumber(L)).c_str()); - return 1; -} - -int32_t LuaScriptInterface::luaGetBanAction(lua_State* L) -{ - //getBanAction(id[, ipBanishment]) - bool ipBanishment = false; - if(lua_gettop(L) > 1) - ipBanishment = popNumber(L); - - lua_pushstring(L, getAction((ViolationAction_t)popNumber(L), ipBanishment).c_str()); return 1; } -int32_t LuaScriptInterface::luaGetBanList(lua_State* L) +int32_t LuaInterface::luaGetBanList(lua_State* L) { //getBanList(type[, value[, param]]) int32_t param = 0, params = lua_gettop(L); @@ -9704,17 +11012,14 @@ int32_t LuaScriptInterface::luaGetBanList(lua_State* L) setField(L, "added", it->added); setField(L, "expires", it->expires); setField(L, "adminId", it->adminId); - setField(L, "reason", it->reason); - setField(L, "action", it->action); setField(L, "comment", it->comment); - setField(L, "statement", it->statement); pushTable(L); } return 1; } -int32_t LuaScriptInterface::luaGetExperienceStage(lua_State* L) +int32_t LuaInterface::luaGetExperienceStage(lua_State* L) { //getExperienceStage(level[, divider]) double divider = 1.0f; @@ -9725,40 +11030,114 @@ int32_t LuaScriptInterface::luaGetExperienceStage(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaGetDataDir(lua_State* L) +int32_t LuaInterface::luaGetDataDir(lua_State* L) { //getDataDir() lua_pushstring(L, getFilePath(FILE_TYPE_OTHER, "").c_str()); return 1; } -int32_t LuaScriptInterface::luaGetLogsDir(lua_State* L) +int32_t LuaInterface::luaGetLogsDir(lua_State* L) { //getLogsDir() lua_pushstring(L, getFilePath(FILE_TYPE_LOG, "").c_str()); return 1; } -int32_t LuaScriptInterface::luaGetConfigFile(lua_State* L) +int32_t LuaInterface::luaGetConfigFile(lua_State* L) { //getConfigFile() lua_pushstring(L, g_config.getString(ConfigManager::CONFIG_FILE).c_str()); return 1; } -int32_t LuaScriptInterface::luaGetConfigValue(lua_State* L) +int32_t LuaInterface::luaDoPlayerSetWalkthrough(lua_State* L) +{ + //doPlayerSetWalkthrough(cid, uid, walkthrough) + bool walkthrough = popBoolean(L); + uint32_t uid = popNumber(L); + + ScriptEnviroment* env = getEnv(); + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) + { + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + Creature* creature = env->getCreatureByUID(uid); + if(!creature) + { + errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + if(player != creature) + { + player->setWalkthrough(creature, walkthrough); + lua_pushboolean(L, true); + } + else + lua_pushboolean(L, false); + + return 1; +} + +int32_t LuaInterface::luaDoGuildAddEnemy(lua_State* L) +{ + //doGuildAddEnemy(guild, enemy, war, type) + War_t war; + war.type = (WarType_t)popNumber(L); + war.war = popNumber(L); + + uint32_t enemy = popNumber(L), guild = popNumber(L), count = 0; + for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) + { + if(it->second->isRemoved() || it->second->getGuildId() != guild) + continue; + + ++count; + it->second->addEnemy(enemy, war); + g_game.updateCreatureEmblem(it->second); + } + + lua_pushnumber(L, count); + return 1; +} + +int32_t LuaInterface::luaDoGuildRemoveEnemy(lua_State* L) +{ + //doGuildRemoveEnemy(guild, enemy) + uint32_t enemy = popNumber(L), guild = popNumber(L), count = 0; + for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) + { + if(it->second->isRemoved() || it->second->getGuildId() != guild) + continue; + + ++count; + it->second->removeEnemy(enemy); + g_game.updateCreatureEmblem(it->second); + } + + lua_pushnumber(L, count); + return 1; +} + +int32_t LuaInterface::luaGetConfigValue(lua_State* L) { //getConfigValue(key) g_config.getValue(popString(L), L); return 1; } -int32_t LuaScriptInterface::luaGetModList(lua_State* L) +int32_t LuaInterface::luaGetModList(lua_State* L) { //getModList() - ModMap::iterator it = ScriptingManager::getInstance()->getFirstMod(); + ModMap::iterator it = ScriptManager::getInstance()->getFirstMod(); lua_newtable(L); - for(uint32_t i = 1; it != ScriptingManager::getInstance()->getLastMod(); ++it, ++i) + for(uint32_t i = 1; it != ScriptManager::getInstance()->getLastMod(); ++it, ++i) { createTable(L, i); setField(L, "name", it->first); @@ -9776,12 +11155,12 @@ int32_t LuaScriptInterface::luaGetModList(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaL_loadmodlib(lua_State* L) +int32_t LuaInterface::luaL_loadmodlib(lua_State* L) { //loadmodlib(lib) std::string name = asLowerCaseString(popString(L)); - for(LibMap::iterator it = ScriptingManager::getInstance()->getFirstLib(); - it != ScriptingManager::getInstance()->getLastLib(); ++it) + for(LibMap::iterator it = ScriptManager::getInstance()->getFirstLib(); + it != ScriptManager::getInstance()->getLastLib(); ++it) { if(asLowerCaseString(it->first) != name) continue; @@ -9794,12 +11173,12 @@ int32_t LuaScriptInterface::luaL_loadmodlib(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaL_domodlib(lua_State* L) +int32_t LuaInterface::luaL_domodlib(lua_State* L) { //domodlib(lib) std::string name = asLowerCaseString(popString(L)); - for(LibMap::iterator it = ScriptingManager::getInstance()->getFirstLib(); - it != ScriptingManager::getInstance()->getLastLib(); ++it) + for(LibMap::iterator it = ScriptManager::getInstance()->getFirstLib(); + it != ScriptManager::getInstance()->getLastLib(); ++it) { if(asLowerCaseString(it->first) != name) continue; @@ -9815,12 +11194,21 @@ int32_t LuaScriptInterface::luaL_domodlib(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaL_dodirectory(lua_State* L) +int32_t LuaInterface::luaL_dodirectory(lua_State* L) { + //dodirectory(dir[, recursively = false[, loadSystems = true]]) + bool recursively = false, loadSystems = true; + int32_t params = lua_gettop(L); + if(params > 2) + loadSystems = popBoolean(L); + + if(params > 1) + recursively = popBoolean(L); + std::string dir = popString(L); - if(!getEnv()->getInterface()->loadDirectory(dir, NULL)) + if(!getEnv()->getInterface()->loadDirectory(dir, recursively, loadSystems, NULL)) { - errorEx("Failed to load directory " + dir + "."); + errorEx("Failed to load directory " + dir); lua_pushboolean(L, false); } else @@ -9829,12 +11217,33 @@ int32_t LuaScriptInterface::luaL_dodirectory(lua_State* L) return 1; } +int32_t LuaInterface::luaL_errors(lua_State* L) +{ + //errors(var) + bool status = getEnv()->getInterface()->m_errors; + getEnv()->getInterface()->m_errors = popBoolean(L); + lua_pushboolean(L, status); + return 1; +} + #define EXPOSE_LOG(Name, Stream)\ - int32_t LuaScriptInterface::luaStd##Name(lua_State* L)\ + int32_t LuaInterface::luaStd##Name(lua_State* L)\ {\ StringVec data;\ for(int32_t i = 0, params = lua_gettop(L); i < params; ++i)\ - data.push_back(popString(L));\ + {\ + if(lua_isnil(L, -1))\ + {\ + data.push_back("nil");\ + lua_pop(L, 1);\ + }\ + else if(lua_isboolean(L, -1))\ + data.push_back(popBoolean(L) ? "true" : "false");\ + else if(lua_istable(L, -1)) {/* "table: address" */}\ + else if(lua_isfunction(L, -1)) {/* "function: address" */}\ + else\ + data.push_back(popString(L));\ + }\ \ for(StringVec::reverse_iterator it = data.rbegin(); it != data.rend(); ++it)\ Stream << (*it) << std::endl;\ @@ -9844,48 +11253,91 @@ int32_t LuaScriptInterface::luaL_dodirectory(lua_State* L) } EXPOSE_LOG(Cout, std::cout) -EXPOSE_LOG(Cerr, std::cerr) EXPOSE_LOG(Clog, std::clog) +EXPOSE_LOG(Cerr, std::cerr) #undef EXPOSE_LOG -int32_t LuaScriptInterface::luaStdMD5(lua_State* L) +int32_t LuaInterface::luaStdMD5(lua_State* L) { - //std.md5(string[, upperCase]) + //std.md5(string[, upperCase = false]) bool upperCase = false; if(lua_gettop(L) > 1) - upperCase = popNumber(L); + upperCase = popBoolean(L); lua_pushstring(L, transformToMD5(popString(L), upperCase).c_str()); return 1; } -int32_t LuaScriptInterface::luaStdSHA1(lua_State* L) +int32_t LuaInterface::luaStdSHA1(lua_State* L) { - //std.sha1(string[, upperCase]) + //std.sha1(string[, upperCase = false]) bool upperCase = false; if(lua_gettop(L) > 1) - upperCase = popNumber(L); + upperCase = popBoolean(L); lua_pushstring(L, transformToSHA1(popString(L), upperCase).c_str()); return 1; } -int32_t LuaScriptInterface::luaDatabaseExecute(lua_State* L) +int32_t LuaInterface::luaStdSHA256(lua_State* L) +{ + //std.sha256(string[, upperCase = false]) + bool upperCase = false; + if(lua_gettop(L) > 1) + upperCase = popBoolean(L); + + lua_pushstring(L, transformToSHA256(popString(L), upperCase).c_str()); + return 1; +} + +int32_t LuaInterface::luaStdSHA512(lua_State* L) +{ + //std.sha512(string[, upperCase = false]) + bool upperCase = false; + if(lua_gettop(L) > 1) + upperCase = popBoolean(L); + + lua_pushstring(L, transformToSHA512(popString(L), upperCase).c_str()); + return 1; +} + +int32_t LuaInterface::luaStdCheckName(lua_State* L) +{ + //std.checkName(string[, forceUppercaseOnFirstLetter = true]) + bool forceUppercaseOnFirstLetter = true; + if(lua_gettop(L) > 1) + forceUppercaseOnFirstLetter = popBoolean(L); + + lua_pushboolean(L, isValidName(popString(L), forceUppercaseOnFirstLetter)); + return 1; +} + +int32_t LuaInterface::luaSystemTime(lua_State* L) { - //db.executeQuery(query) + //os.mtime() + lua_pushnumber(L, OTSYS_TIME()); + return 1; +} + +int32_t LuaInterface::luaDatabaseExecute(lua_State* L) +{ + //db.query(query) DBQuery query; //lock mutex - lua_pushboolean(L, Database::getInstance()->executeQuery(popString(L))); + query << popString(L); + + lua_pushboolean(L, Database::getInstance()->query(query.str())); return 1; } -int32_t LuaScriptInterface::luaDatabaseStoreQuery(lua_State* L) +int32_t LuaInterface::luaDatabaseStoreQuery(lua_State* L) { //db.storeQuery(query) ScriptEnviroment* env = getEnv(); - DBQuery query; //lock mutex - if(DBResult* res = Database::getInstance()->storeQuery(popString(L))) + + query << popString(L); + if(DBResult* res = Database::getInstance()->storeQuery(query.str())) lua_pushnumber(L, env->addResult(res)); else lua_pushboolean(L, false); @@ -9893,46 +11345,77 @@ int32_t LuaScriptInterface::luaDatabaseStoreQuery(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaDatabaseEscapeString(lua_State* L) +int32_t LuaInterface::luaDatabaseEscapeString(lua_State* L) { //db.escapeString(str) - DBQuery query; //lock mutex lua_pushstring(L, Database::getInstance()->escapeString(popString(L)).c_str()); return 1; } -int32_t LuaScriptInterface::luaDatabaseEscapeBlob(lua_State* L) +int32_t LuaInterface::luaDatabaseEscapeBlob(lua_State* L) { //db.escapeBlob(s, length) uint32_t length = popNumber(L); - DBQuery query; //lock mutex - lua_pushstring(L, Database::getInstance()->escapeBlob(popString(L).c_str(), length).c_str()); return 1; } -int32_t LuaScriptInterface::luaDatabaseLastInsertId(lua_State* L) +int32_t LuaInterface::luaDatabaseLastInsertId(lua_State* L) { //db.lastInsertId() - DBQuery query; //lock mutex lua_pushnumber(L, Database::getInstance()->getLastInsertId()); return 1; } -int32_t LuaScriptInterface::luaDatabaseStringComparison(lua_State* L) +int32_t LuaInterface::luaDatabaseStringComparer(lua_State* L) { - //db.stringComparison() - lua_pushstring(L, Database::getInstance()->getStringComparison().c_str()); + //db.stringComparer() + lua_pushstring(L, Database::getInstance()->getStringComparer().c_str()); return 1; } -int32_t LuaScriptInterface::luaDatabaseUpdateLimiter(lua_State* L) +int32_t LuaInterface::luaDatabaseUpdateLimiter(lua_State* L) { //db.updateLimiter() lua_pushstring(L, Database::getInstance()->getUpdateLimiter().c_str()); return 1; } +int32_t LuaInterface::luaDatabaseConnected(lua_State* L) +{ + //db.connected() + lua_pushboolean(L, Database::getInstance()->isConnected()); + return 1; +} + +int32_t LuaInterface::luaDatabaseTableExists(lua_State* L) +{ + //db.tableExists(table) + lua_pushboolean(L, DatabaseManager::getInstance()->tableExists(popString(L))); + return 1; +} + +int32_t LuaInterface::luaDatabaseTransBegin(lua_State* L) +{ + //db.transBegin() + lua_pushboolean(L, Database::getInstance()->beginTransaction()); + return 1; +} + +int32_t LuaInterface::luaDatabaseTransRollback(lua_State* L) +{ + //db.transRollback() + lua_pushboolean(L, Database::getInstance()->rollback()); + return 1; +} + +int32_t LuaInterface::luaDatabaseTransCommit(lua_State* L) +{ + //db.transCommit() + lua_pushboolean(L, Database::getInstance()->commit()); + return 1; +} + #define CHECK_RESULT()\ if(!res)\ {\ @@ -9940,7 +11423,7 @@ int32_t LuaScriptInterface::luaDatabaseUpdateLimiter(lua_State* L) return 1;\ } -int32_t LuaScriptInterface::luaResultGetDataInt(lua_State* L) +int32_t LuaInterface::luaResultGetDataInt(lua_State* L) { //result.getDataInt(res, s) const std::string& s = popString(L); @@ -9953,7 +11436,7 @@ int32_t LuaScriptInterface::luaResultGetDataInt(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaResultGetDataLong(lua_State* L) +int32_t LuaInterface::luaResultGetDataLong(lua_State* L) { //result.getDataLong(res, s) const std::string& s = popString(L); @@ -9966,7 +11449,7 @@ int32_t LuaScriptInterface::luaResultGetDataLong(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaResultGetDataString(lua_State* L) +int32_t LuaInterface::luaResultGetDataString(lua_State* L) { //result.getDataString(res, s) const std::string& s = popString(L); @@ -9979,7 +11462,7 @@ int32_t LuaScriptInterface::luaResultGetDataString(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaResultGetDataStream(lua_State* L) +int32_t LuaInterface::luaResultGetDataStream(lua_State* L) { //result.getDataStream(res, s) const std::string s = popString(L); @@ -9992,10 +11475,10 @@ int32_t LuaScriptInterface::luaResultGetDataStream(lua_State* L) lua_pushstring(L, res->getDataStream(s, length)); lua_pushnumber(L, length); - return 1; + return 2; } -int32_t LuaScriptInterface::luaResultNext(lua_State* L) +int32_t LuaInterface::luaResultNext(lua_State* L) { //result.next(res) ScriptEnviroment* env = getEnv(); @@ -10007,7 +11490,7 @@ int32_t LuaScriptInterface::luaResultNext(lua_State* L) return 1; } -int32_t LuaScriptInterface::luaResultFree(lua_State* L) +int32_t LuaInterface::luaResultFree(lua_State* L) { //result.free(res) uint32_t rid = popNumber(L); @@ -10022,14 +11505,14 @@ int32_t LuaScriptInterface::luaResultFree(lua_State* L) #undef CHECK_RESULT -int32_t LuaScriptInterface::luaBitNot(lua_State* L) +int32_t LuaInterface::luaBitNot(lua_State* L) { int32_t number = (int32_t)popNumber(L); lua_pushnumber(L, ~number); return 1; } -int32_t LuaScriptInterface::luaBitUNot(lua_State* L) +int32_t LuaInterface::luaBitUNot(lua_State* L) { uint32_t number = (uint32_t)popNumber(L); lua_pushnumber(L, ~number); @@ -10037,7 +11520,7 @@ int32_t LuaScriptInterface::luaBitUNot(lua_State* L) } #define MULTI_OPERATOR(type, name, op)\ - int32_t LuaScriptInterface::luaBit##name(lua_State* L)\ + int32_t LuaInterface::luaBit##name(lua_State* L)\ {\ int32_t params = lua_gettop(L);\ type value = (type)popNumber(L);\ @@ -10058,7 +11541,7 @@ MULTI_OPERATOR(uint32_t, UXor, ^=) #undef MULTI_OPERATOR #define SHIFT_OPERATOR(type, name, op)\ - int32_t LuaScriptInterface::luaBit##name(lua_State* L)\ + int32_t LuaInterface::luaBit##name(lua_State* L)\ {\ type v2 = (type)popNumber(L), v1 = (type)popNumber(L);\ lua_pushnumber(L, (v1 op v2));\ diff --git a/luascript.h b/luascript.h index c64e9be..ebe79a0 100644 --- a/luascript.h +++ b/luascript.h @@ -19,12 +19,25 @@ #define __LUASCRIPT__ #include "otsystem.h" +#if defined(__ALT_LUA_PATH__) extern "C" { - #include "lua.h" - #include "lualib.h" - #include "lauxlib.h" + #include + #include + #include } +#else +extern "C" +{ + #include + #include + #include + + #ifdef __LUAJIT__ + #include + #endif +} +#endif #include "database.h" #include "position.h" @@ -54,9 +67,11 @@ struct LuaVariant uint32_t number; }; +typedef std::map StorageMap; + class Game; class Thing; -class LuaScriptInterface; +class LuaInterface; class Creature; class Player; @@ -79,21 +94,24 @@ class ScriptEnviroment static bool saveGameState(); static bool loadGameState(); - bool getStorage(const uint32_t key, std::string& value) const; - void setStorage(const uint32_t key, const std::string& value) {m_storageMap[key] = value;} - void eraseStorage(const uint32_t key) {m_storageMap.erase(key);} + bool getStorage(const std::string& key, std::string& value) const; + void setStorage(const std::string& key, const std::string& value) {m_storageMap[key] = value;} + void eraseStorage(const std::string& key) {m_storageMap.erase(key);} + + inline StorageMap::const_iterator getStorageBegin() const {return m_storageMap.begin();} + inline StorageMap::const_iterator getStorageEnd() const {return m_storageMap.end();} - int32_t getScriptId() {return m_scriptId;} - void setScriptId(int32_t scriptId, LuaScriptInterface* interface) + int32_t getScriptId() const {return m_scriptId;}; + void setScriptId(int32_t scriptId, LuaInterface* interface) {m_scriptId = scriptId; m_interface = interface;} - int32_t getCallbackId() {return m_callbackId;} - bool setCallbackId(int32_t callbackId, LuaScriptInterface* interface); + int32_t getCallbackId() const {return m_callbackId;}; + bool setCallbackId(int32_t callbackId, LuaInterface* interface); - std::string getEventDesc() {return m_eventdesc;} - void setEventDesc(const std::string& desc) {m_eventdesc = desc;} + std::string getEvent() const {return m_event;}; + void setEvent(const std::string& desc) {m_event = desc;} - Position getRealPos() {return m_realPos;} + Position getRealPos() const {return m_realPos;}; void setRealPos(const Position& realPos) {m_realPos = realPos;} Npc* getNpc() const {return m_curNpc;} @@ -106,7 +124,8 @@ class ScriptEnviroment static Combat* getCombatObject(uint32_t combatId); static uint32_t addConditionObject(Condition* condition); - static Condition* getConditionObject(uint32_t conditionId); + static uint32_t addTempConditionObject(Condition* condition); + static Condition* getConditionObject(uint32_t conditionId, bool loaded); Thing* getThingByUID(uint32_t uid); Item* getItemByUID(uint32_t uid); @@ -136,8 +155,8 @@ class ScriptEnviroment void setTimerEvent() {m_timerEvent = true;} void resetTimerEvent() {m_timerEvent = false;} - LuaScriptInterface* getInterface() {return m_interface;} - void getInfo(int32_t& scriptId, std::string& desc, LuaScriptInterface*& interface, int32_t& callbackId, bool& timerEvent); + LuaInterface* getInterface() {return m_interface;} + void getInfo(int32_t& scriptId, std::string& desc, LuaInterface*& interface, int32_t& callbackId, bool& timerEvent); void reset(); void resetCallback() {m_callbackId = 0;} @@ -151,17 +170,17 @@ class ScriptEnviroment private: typedef std::map ThingMap; typedef std::vector VariantVector; - typedef std::map StorageMap; + typedef std::list ItemList; + typedef std::map TempItemListMap; + typedef std::map AreaMap; typedef std::map CombatMap; typedef std::map ConditionMap; - typedef std::list ItemList; - typedef std::map TempItemListMap; typedef std::map DBResultMap; - LuaScriptInterface* m_interface; + LuaInterface* m_interface; int32_t m_scriptId, m_callbackId; - std::string m_eventdesc; + std::string m_event; bool m_timerEvent; ThingMap m_localMap; @@ -177,8 +196,9 @@ class ScriptEnviroment static uint32_t m_lastCombatId; static CombatMap m_combatMap; - static uint32_t m_lastConditionId; + static uint32_t m_lastConditionId, m_lastTempConditionId; static ConditionMap m_conditionMap; + static ConditionMap m_tempConditionMap; int32_t m_lastUID; bool m_loaded; @@ -205,21 +225,26 @@ enum ErrorCode_t LUA_ERROR_SPELL_NOT_FOUND }; -#define errorEx(a) error(__FUNCTION__, a) +enum Recursive_t +{ + RECURSE_FIRST = -1, + RECURSE_NONE = 0, + RECURSE_ALL = 1 +}; -class LuaScriptInterface +#define errorEx(a) error(__FUNCTION__, a) +class LuaInterface { public: - LuaScriptInterface(std::string interfaceName); - virtual ~LuaScriptInterface(); + LuaInterface(std::string interfaceName); + virtual ~LuaInterface(); virtual bool initState(); bool reInitState(); static bool reserveEnv() { - ++m_scriptEnvIndex; - if(m_scriptEnvIndex > 20) + if(++m_scriptEnvIndex > 20) { --m_scriptEnvIndex; return false; @@ -238,9 +263,9 @@ class LuaScriptInterface bool loadBuffer(const std::string& text, Npc* npc = NULL); bool loadFile(const std::string& file, Npc* npc = NULL); - bool loadDirectory(const std::string& dir, Npc* npc = NULL); + bool loadDirectory(std::string dir, bool recursively, bool loadSystems, Npc* npc = NULL); - std::string getName() {return m_interfaceName;} + std::string getName() const {return m_interfaceName;}; std::string getScript(int32_t scriptId); std::string getLastError() const {return m_lastError;} @@ -259,7 +284,7 @@ class LuaScriptInterface void dumpStack(lua_State* L = NULL); //push/pop common structures - static void pushThing(lua_State* L, Thing* thing, uint32_t id = 0); + static void pushThing(lua_State* L, Thing* thing, uint32_t id = 0, Recursive_t recursive = RECURSE_FIRST); static void pushVariant(lua_State* L, const LuaVariant& var); static void pushPosition(lua_State* L, const PositionEx& position) {pushPosition(L, position, position.stackpos);} static void pushPosition(lua_State* L, const Position& position, uint32_t stackpos); @@ -294,7 +319,7 @@ class LuaScriptInterface static std::string getGlobalString(lua_State* L, const std::string& _identifier, const std::string& _default = ""); static bool getGlobalBool(lua_State* L, const std::string& _identifier, bool _default = false); - static int32_t getGlobalNumber(lua_State* L, const std::string& _identifier, const int32_t _default = 0); + static int64_t getGlobalNumber(lua_State* L, const std::string& _identifier, const int64_t _default = 0); static double getGlobalDouble(lua_State* L, const std::string& _identifier, const double _default = 0); static void getValue(const std::string& key, lua_State* L, lua_State* _L); @@ -312,15 +337,16 @@ class LuaScriptInterface //lua functions static int32_t luaDoRemoveItem(lua_State* L); - static int32_t luaDoFeedPlayer(lua_State* L); + static int32_t luaDoPlayerFeed(lua_State* L); static int32_t luaDoPlayerSendCancel(lua_State* L); static int32_t luaDoSendDefaultCancel(lua_State* L); static int32_t luaGetSearchString(lua_State* L); static int32_t luaGetClosestFreeTile(lua_State* L); static int32_t luaDoTeleportThing(lua_State* L); + static int32_t luaDoItemSetDestination(lua_State* L); static int32_t luaDoTransformItem(lua_State* L); + static int32_t luaDoSendCreatureSquare(lua_State* L); static int32_t luaDoSendMagicEffect(lua_State* L); - static int32_t luaDoSendAnimatedText(lua_State* L); static int32_t luaDoSendDistanceShoot(lua_State* L); static int32_t luaDoShowTextWindow(lua_State* L); static int32_t luaDoShowTextDialog(lua_State* L); @@ -341,6 +367,7 @@ class LuaScriptInterface static int32_t luaDoRemoveConditions(lua_State* L); static int32_t luaDoRemoveCreature(lua_State* L); static int32_t luaDoMoveCreature(lua_State* L); + static int32_t luaDoSteerCreature(lua_State* L); static int32_t luaDoCreatureSay(lua_State* L); static int32_t luaDoPlayerAddSkillTry(lua_State* L); static int32_t luaDoCreatureAddHealth(lua_State* L); @@ -357,7 +384,9 @@ class LuaScriptInterface static int32_t luaDoCleanTile(lua_State* L); static int32_t luaDoPlayerSendTextMessage(lua_State* L); static int32_t luaDoPlayerSendChannelMessage(lua_State* L); - static int32_t luaDoPlayerSendToChannel(lua_State* L); + static int32_t luaDoCreatureChannelSay(lua_State* L); + static int32_t luaDoPlayerOpenChannel(lua_State* L); + static int32_t luaDoPlayerSendChannels(lua_State* L); static int32_t luaDoPlayerAddMoney(lua_State* L); static int32_t luaDoPlayerRemoveMoney(lua_State* L); static int32_t luaDoPlayerTransferMoneyTo(lua_State* L); @@ -366,7 +395,6 @@ class LuaScriptInterface static int32_t luaDoPlayerSetVocation(lua_State* L); static int32_t luaDoPlayerRemoveItem(lua_State* L); static int32_t luaDoPlayerAddSoul(lua_State* L); - static int32_t luaDoPlayerAddStamina(lua_State* L); static int32_t luaDoPlayerSetStamina(lua_State* L); static int32_t luaDoPlayerAddExperience(lua_State* L); static int32_t luaDoPlayerSetGuildId(lua_State* L); @@ -375,12 +403,15 @@ class LuaScriptInterface static int32_t luaDoPlayerSetSex(lua_State* L); static int32_t luaDoPlayerSetIdleTime(lua_State* L); static int32_t luaGetPlayerIdleTime(lua_State* L); - static int32_t luaDoSetCreatureLight(lua_State* L); static int32_t luaDoCreatureSetLookDir(lua_State* L); static int32_t luaGetCreatureHideHealth(lua_State* L); static int32_t luaDoCreatureSetHideHealth(lua_State* L); static int32_t luaGetCreatureSpeakType(lua_State* L); static int32_t luaDoCreatureSetSpeakType(lua_State* L); + static int32_t luaGetCreatureGuildEmblem(lua_State* L); + static int32_t luaDoCreatureSetGuildEmblem(lua_State* L); + static int32_t luaGetCreaturePartyShield(lua_State* L); + static int32_t luaDoCreatureSetPartyShield(lua_State* L); static int32_t luaGetCreatureSkullType(lua_State* L); static int32_t luaDoCreatureSetSkullType(lua_State* L); static int32_t luaGetPlayerSkullEnd(lua_State* L); @@ -394,11 +425,14 @@ class LuaScriptInterface static int32_t luaGetPlayerByNameWildcard(lua_State* L); static int32_t luaGetPlayerGUIDByName(lua_State* L); static int32_t luaGetPlayerNameByGUID(lua_State* L); + static int32_t luaDoPlayerChangeName(lua_State* L); static int32_t luaGetPlayersByAccountId(lua_State* L); static int32_t luaGetAccountIdByName(lua_State* L); static int32_t luaGetAccountByName(lua_State* L); static int32_t luaGetAccountIdByAccount(lua_State* L); static int32_t luaGetAccountByAccountId(lua_State* L); + static int32_t luaGetAccountFlagValue(lua_State* L); + static int32_t luaGetAccountCustomFlagValue(lua_State* L); static int32_t luaGetIpByName(lua_State* L); static int32_t luaGetPlayersByIp(lua_State* L); static int32_t luaIsIpBanished(lua_State* L); @@ -407,19 +441,23 @@ class LuaScriptInterface static int32_t luaDoAddIpBanishment(lua_State* L); static int32_t luaDoAddPlayerBanishment(lua_State* L); static int32_t luaDoAddAccountBanishment(lua_State* L); + static int32_t luaDoAddAccountWarnings(lua_State* L); static int32_t luaDoAddNotation(lua_State* L); static int32_t luaDoAddStatement(lua_State* L); static int32_t luaDoRemoveIpBanishment(lua_State* L); static int32_t luaDoRemovePlayerBanishment(lua_State* L); static int32_t luaDoRemoveAccountBanishment(lua_State* L); + static int32_t luaDoRemoveAccountWarnings(lua_State* L); static int32_t luaDoRemoveNotations(lua_State* L); static int32_t luaDoRemoveStatements(lua_State* L); + static int32_t luaGetAccountWarnings(lua_State* L); static int32_t luaGetNotationsCount(lua_State* L); static int32_t luaGetStatementsCount(lua_State* L); static int32_t luaGetBanData(lua_State* L); static int32_t luaGetBanReason(lua_State* L); static int32_t luaGetBanAction(lua_State* L); static int32_t luaGetBanList(lua_State* L); + static int32_t luaGetPlayerModes(lua_State* L); static int32_t luaGetPlayerRates(lua_State* L); static int32_t luaDoPlayerSetRate(lua_State* L); static int32_t luaDoCreatureSetDropLoot(lua_State* L); @@ -431,7 +469,8 @@ class LuaScriptInterface static int32_t luaGetThingPosition(lua_State* L); static int32_t luaDoItemRaidUnref(lua_State* L); static int32_t luaHasItemProperty(lua_State* L); - static int32_t luaGetThingFromPos(lua_State* L); + static int32_t luaHasMonsterRaid(lua_State* L); + static int32_t luaGetThingFromPosition(lua_State* L); static int32_t luaGetTileItemById(lua_State* L); static int32_t luaGetTileItemByType(lua_State* L); static int32_t luaGetTileThingByPos(lua_State* L); @@ -441,7 +480,7 @@ class LuaScriptInterface static int32_t luaGetHouseInfo(lua_State* L); static int32_t luaGetHouseAccessList(lua_State* L); static int32_t luaGetHouseByPlayerGUID(lua_State* L); - static int32_t luaGetHouseFromPos(lua_State* L); + static int32_t luaGetHouseFromPosition(lua_State* L); static int32_t luaSetHouseOwner(lua_State* L); static int32_t luaSetHouseAccessList(lua_State* L); static int32_t luaDoPlayerSetNameDescription(lua_State* L); @@ -481,6 +520,7 @@ class LuaScriptInterface static int32_t luaGetPlayerIp(lua_State* L); static int32_t luaGetPlayerLastLoad(lua_State* L); static int32_t luaGetPlayerLastLogin(lua_State* L); + static int32_t luaGetPlayerTradeState(lua_State* L); static int32_t luaGetPlayerAccountManager(lua_State* L); static int32_t luaGetPlayerAccountId(lua_State* L); static int32_t luaGetPlayerAccount(lua_State* L); @@ -495,7 +535,8 @@ class LuaScriptInterface static int32_t luaGetPlayerGUID(lua_State* L); static int32_t luaGetPlayerFlagValue(lua_State* L); static int32_t luaGetPlayerCustomFlagValue(lua_State* L); - static int32_t luaGetCreatureCondition(lua_State* L); + static int32_t luaHasCreatureCondition(lua_State* L); + static int32_t luaGetCreatureConditionInfo(lua_State* L); static int32_t luaHasPlayerClient(lua_State* L); static int32_t luaGetDepotId(lua_State* L); static int32_t luaGetVocationInfo(lua_State* L); @@ -513,13 +554,21 @@ class LuaScriptInterface static int32_t luaGetInstantSpellInfo(lua_State* L); static int32_t luaGetPlayerPartner(lua_State* L); static int32_t luaDoPlayerSetPartner(lua_State* L); + static int32_t luaDoPlayerFollowCreature(lua_State* L); static int32_t luaGetPlayerParty(lua_State* L); static int32_t luaDoPlayerJoinParty(lua_State* L); + static int32_t luaDoPlayerLeaveParty(lua_State* L); static int32_t luaGetPartyMembers(lua_State* L); + static int32_t luaGetCreatureStorageList(lua_State* L); static int32_t luaGetCreatureStorage(lua_State* L); static int32_t luaDoCreatureSetStorage(lua_State* L); static int32_t luaDoPlayerAddBlessing(lua_State* L); static int32_t luaGetPlayerBlessing(lua_State* L); + static int32_t luaDoPlayerSetPVPBlessing(lua_State* L); + static int32_t luaGetPlayerPVPBlessing(lua_State* L); + static int32_t luaDoGuildAddEnemy(lua_State* L); + static int32_t luaDoGuildRemoveEnemy(lua_State* L); + static int32_t luaGetStorageList(lua_State* L); static int32_t luaGetStorage(lua_State* L); static int32_t luaDoSetStorage(lua_State* L); static int32_t luaDoPlayerAddOutfit(lua_State* L); @@ -538,8 +587,8 @@ class LuaScriptInterface static int32_t luaGetGuildMotd(lua_State* L); static int32_t luaIsPlayerPzLocked(lua_State* L); static int32_t luaIsPlayerSaving(lua_State* L); + static int32_t luaIsPlayerProtected(lua_State* L); static int32_t luaIsCreature(lua_State* L); - static int32_t luaIsContainer(lua_State* L); static int32_t luaIsMovable(lua_State* L); static int32_t luaGetContainerSize(lua_State* L); static int32_t luaGetContainerCap(lua_State* L); @@ -591,6 +640,8 @@ class LuaScriptInterface static int32_t luaAddEvent(lua_State* L); static int32_t luaStopEvent(lua_State* L); static int32_t luaRegisterCreatureEvent(lua_State* L); + static int32_t luaUnregisterCreatureEvent(lua_State* L); + static int32_t luaUnregisterCreatureEventType(lua_State* L); static int32_t luaGetPlayerBalance(lua_State* L); static int32_t luaDoPlayerSetBalance(lua_State* L); static int32_t luaDoPlayerPopupFYI(lua_State* L); @@ -613,6 +664,7 @@ class LuaScriptInterface static int32_t luaDoExecuteRaid(lua_State* L); static int32_t luaDoReloadInfo(lua_State* L); static int32_t luaDoSaveServer(lua_State* L); + static int32_t luaDoSaveHouse(lua_State* L); static int32_t luaDoCleanHouse(lua_State* L); static int32_t luaDoCleanMap(lua_State* L); static int32_t luaDoRefreshMap(lua_State* L); @@ -620,11 +672,16 @@ class LuaScriptInterface static int32_t luaGetItemIdByName(lua_State* L); static int32_t luaGetItemInfo(lua_State* L); static int32_t luaGetItemWeight(lua_State* L); + static int32_t luaGetItemParent(lua_State* L); static int32_t luaGetItemAttribute(lua_State* L); static int32_t luaDoItemSetAttribute(lua_State* L); static int32_t luaDoItemEraseAttribute(lua_State* L); + static int32_t luaGetVocationList(lua_State* L); + static int32_t luaGetGroupList(lua_State* L); + static int32_t luaGetChannelList(lua_State* L); static int32_t luaGetTalkActionList(lua_State* L); static int32_t luaGetExperienceStageList(lua_State* L); + static int32_t luaGetTownList(lua_State* L); static int32_t luaGetWaypointList(lua_State* L); static int32_t luaGetWaypointPosition(lua_State* L); static int32_t luaDoWaypointAddTemporial(lua_State* L); @@ -633,19 +690,34 @@ class LuaScriptInterface static int32_t luaGetConfigFile(lua_State* L); static int32_t luaGetConfigValue(lua_State* L); static int32_t luaGetModList(lua_State* L); - + static int32_t luaDoPlayerSetWalkthrough(lua_State* L); + static int32_t luaDoPlayerAddMount(lua_State* L); + static int32_t luaDoPlayerRemoveMount(lua_State* L); + static int32_t luaCanPlayerRideMount(lua_State* L); + static int32_t luaDoPlayerSetMounted(lua_State* L); + static int32_t luaGetMountInfo(lua_State* L); + + static int32_t luaL_errors(lua_State* L); static int32_t luaL_loadmodlib(lua_State* L); static int32_t luaL_domodlib(lua_State* L); static int32_t luaL_dodirectory(lua_State* L); - static const luaL_Reg luaDatabaseTable[8]; + static const luaL_Reg luaSystemTable[2]; + static int32_t luaSystemTime(lua_State* L); + + static const luaL_Reg luaDatabaseTable[13]; static int32_t luaDatabaseExecute(lua_State* L); static int32_t luaDatabaseStoreQuery(lua_State* L); static int32_t luaDatabaseEscapeString(lua_State* L); static int32_t luaDatabaseEscapeBlob(lua_State* L); static int32_t luaDatabaseLastInsertId(lua_State* L); - static int32_t luaDatabaseStringComparison(lua_State* L); + static int32_t luaDatabaseStringComparer(lua_State* L); static int32_t luaDatabaseUpdateLimiter(lua_State* L); + static int32_t luaDatabaseConnected(lua_State* L); + static int32_t luaDatabaseTableExists(lua_State* L); + static int32_t luaDatabaseTransBegin(lua_State* L); + static int32_t luaDatabaseTransRollback(lua_State* L); + static int32_t luaDatabaseTransCommit(lua_State* L); static const luaL_Reg luaResultTable[7]; static int32_t luaResultGetDataInt(lua_State* L); @@ -669,14 +741,18 @@ class LuaScriptInterface static int32_t luaBitULeftShift(lua_State* L); static int32_t luaBitURightShift(lua_State* L); - static const luaL_Reg luaStdTable[6]; + static const luaL_Reg luaStdTable[9]; static int32_t luaStdCout(lua_State* L); - static int32_t luaStdCerr(lua_State* L); static int32_t luaStdClog(lua_State* L); + static int32_t luaStdCerr(lua_State* L); static int32_t luaStdMD5(lua_State* L); static int32_t luaStdSHA1(lua_State* L); + static int32_t luaStdSHA256(lua_State* L); + static int32_t luaStdSHA512(lua_State* L); + static int32_t luaStdCheckName(lua_State* L); lua_State* m_luaState; + bool m_errors; std::string m_lastError; private: @@ -693,7 +769,7 @@ class LuaScriptInterface PlayerInfoVocation, PlayerInfoTown, PlayerInfoPromotionLevel, - PlayerInfoSoul, + PlayerInfoMoney, PlayerInfoFreeCap, PlayerInfoGuildId, PlayerInfoGuildName, @@ -712,6 +788,7 @@ class LuaScriptInterface PlayerInfoMarriage, PlayerInfoPzLock, PlayerInfoSaving, + PlayerInfoProtected, PlayerInfoIp, PlayerInfoSkullEnd, PlayerInfoOutfitWindow, @@ -721,12 +798,13 @@ class LuaScriptInterface PlayerInfoClient, PlayerInfoLastLoad, PlayerInfoLastLogin, - PlayerInfoAccountManager + PlayerInfoAccountManager, + PlayerInfoTradeState }; static int32_t internalGetPlayerInfo(lua_State* L, PlayerInfo_t info); - int32_t m_runningEventId; - uint32_t m_lastEventTimerId; + int32_t m_runningEvent; + uint32_t m_lastTimer; std::string m_loadingFile, m_interfaceName; static ScriptEnviroment m_scriptEnv[21]; @@ -736,14 +814,16 @@ class LuaScriptInterface struct LuaTimerEvent { int32_t scriptId, function; + uint32_t eventId; + Npc* npc; std::list parameters; }; - typedef std::map LuaTimerEvents; + typedef std::map LuaTimerEvents; LuaTimerEvents m_timerEvents; //script file cache - typedef std::map ScriptsCache; + typedef std::map ScriptsCache; ScriptsCache m_cacheFiles; }; #endif diff --git a/mailbox.cpp b/mailbox.cpp index b468356..8bb205a 100644 --- a/mailbox.cpp +++ b/mailbox.cpp @@ -19,8 +19,6 @@ #include "player.h" #include "iologindata.h" - -#include "depot.h" #include "town.h" #include "configmanager.h" @@ -29,32 +27,61 @@ extern ConfigManager g_config; extern Game g_game; -ReturnValue Mailbox::__queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const +ReturnValue Mailbox::canSend(const Item* item, Creature* actor) const { - if(const Item* item = thing->getItem()) + if(item->getID() != ITEM_PARCEL && item->getID() != ITEM_LETTER) + return RET_NOTPOSSIBLE; + + if(actor) { - if(canSend(item)) - return RET_NOERROR; + if(Player* player = actor->getPlayer()) + { + if(player->hasCondition(CONDITION_MUTED, 2)) + return RET_YOUAREEXHAUSTED; + + if(player->getMailAttempts() >= g_config.getNumber(ConfigManager::MAIL_ATTEMPTS)) + { + if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, + CONDITION_MUTED, g_config.getNumber(ConfigManager::MAIL_BLOCK), 0, false, 2)) + { + player->addCondition(condition); + player->setLastMail(1); // auto erase + } + + return RET_YOUAREEXHAUSTED; + } + + player->setLastMail(OTSYS_TIME()); + player->addMailAttempt(); + } } + return RET_NOERROR; +} + +ReturnValue Mailbox::__queryAdd(int32_t, const Thing* thing, uint32_t, + uint32_t, Creature* actor/* = NULL*/) const +{ + if(const Item* item = thing->getItem()) + return canSend(item, actor); + return RET_NOTPOSSIBLE; } -ReturnValue Mailbox::__queryMaxCount(int32_t index, const Thing* thing, uint32_t count, uint32_t& maxQueryCount, - uint32_t flags) const +ReturnValue Mailbox::__queryMaxCount(int32_t, const Thing*, uint32_t count, uint32_t& maxQueryCount, + uint32_t) const { maxQueryCount = std::max((uint32_t)1, count); return RET_NOERROR; } -void Mailbox::__addThing(Creature* actor, int32_t index, Thing* thing) +void Mailbox::__addThing(Creature* actor, int32_t, Thing* thing) { Item* item = thing->getItem(); if(!item) return; - if(canSend(item)) + if(canSend(item, actor) == RET_NOERROR) sendItem(actor, item); } @@ -70,18 +97,17 @@ bool Mailbox::sendItem(Creature* actor, Item* item) bool Mailbox::getDepotId(const std::string& townString, uint32_t& depotId) { + Town* town = Towns::getInstance()->getTown(townString); if(!town) return false; - IntegerVec disabledTowns = vectorAtoi(explodeString(g_config.getString(ConfigManager::MAILBOX_DISABLED_TOWNS), ",")); - if(disabledTowns[0] != -1) - { - for(IntegerVec::iterator it = disabledTowns.begin(); it != disabledTowns.end(); ++it) - { - if(town->getID() == uint32_t(*it)) - return false; - } + std::string disabledTowns = g_config.getString(ConfigManager::MAILBOX_DISABLED_TOWNS); + if(disabledTowns.size()) + { + IntegerVec tmpVec = vectorAtoi(explodeString(disabledTowns, ",")); + if(tmpVec[0] != 0 && std::find(tmpVec.begin(), tmpVec.end(), town->getID()) != tmpVec.end()) + return false; } depotId = town->getID(); @@ -107,13 +133,13 @@ bool Mailbox::getRecipient(Item* item, std::string& name, uint32_t& depotId) } } } - else if(item->getID() != ITEM_LETTER) /**The item is somehow not a parcel or letter**/ + else if(item->getID() != ITEM_LETTER) // The item is somehow not a parcel or letter { - std::cout << "[Error - Mailbox::getReciver] Trying to get receiver from unkown item with id: " << item->getID() << "!" << std::endl; + std::clog << "[Error - Mailbox::getReciver] Trying to get receiver from unkown item with id: " << item->getID() << "!" << std::endl; return false; } - if(!item || item->getText().empty()) /**No label/letter found or its empty.**/ + if(!item || item->getText().empty()) // No label or letter found or its empty return false; std::istringstream iss(item->getText(), std::istringstream::in); diff --git a/mailbox.h b/mailbox.h index 31b7b46..801e2b3 100644 --- a/mailbox.h +++ b/mailbox.h @@ -44,31 +44,35 @@ class Mailbox : public Item, public Cylinder virtual const Creature* getCreature() const {return NULL;} virtual ReturnValue __queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const; + uint32_t flags, Creature* actor = NULL) const; virtual ReturnValue __queryMaxCount(int32_t index, const Thing* thing, uint32_t count, uint32_t& maxQueryCount, uint32_t flags) const; - virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, uint32_t flags) const {return RET_NOTPOSSIBLE;} - virtual Cylinder* __queryDestination(int32_t& index, const Thing* thing, Item** destItem, - uint32_t& flags) {return this;} + virtual ReturnValue __queryRemove(const Thing*, uint32_t, uint32_t, Creature*) const {return RET_NOTPOSSIBLE;} + virtual Cylinder* __queryDestination(int32_t&, const Thing*, Item**, + uint32_t&) {return this;} virtual void __addThing(Creature* actor, Thing* thing) {__addThing(actor, 0, thing);} virtual void __addThing(Creature* actor, int32_t index, Thing* thing); - virtual void __updateThing(Thing* thing, uint16_t itemId, uint32_t count) {} - virtual void __replaceThing(uint32_t index, Thing* thing) {} + virtual void __updateThing(Thing*, uint16_t, uint32_t) {} + virtual void __replaceThing(uint32_t, Thing*) {} - virtual void __removeThing(Thing* thing, uint32_t count) {} + virtual void __removeThing(Thing*, uint32_t) {} virtual void postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link = LINK_OWNER) - {if(getParent()) getParent()->postAddNotification(actor, thing, - oldParent, index, LINK_PARENT);} + int32_t index, CylinderLink_t = LINK_OWNER) + { + if(getParent()) + getParent()->postAddNotification(actor, thing, oldParent, index, LINK_PARENT); + } virtual void postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link = LINK_OWNER) - {if(getParent()) getParent()->postRemoveNotification(actor, thing, - newParent, index, isCompleteRemoval, LINK_PARENT);} + int32_t index, bool isCompleteRemoval, CylinderLink_t = LINK_OWNER) + { + if(getParent()) + getParent()->postRemoveNotification(actor, thing, newParent, index, isCompleteRemoval, LINK_PARENT); + } - bool canSend(const Item* item) const {return (item->getID() == ITEM_PARCEL || item->getID() == ITEM_LETTER);} + ReturnValue canSend(const Item* item, Creature* actor) const; bool sendItem(Creature* actor, Item* item); bool getDepotId(const std::string& townString, uint32_t& depotId); diff --git a/manager.cpp b/manager.cpp new file mode 100644 index 0000000..a4be086 --- /dev/null +++ b/manager.cpp @@ -0,0 +1,636 @@ +//////////////////////////////////////////////////////////////////////// +// OpenTibia - an opensource roleplaying game +//////////////////////////////////////////////////////////////////////// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +//////////////////////////////////////////////////////////////////////// +#include "otpch.h" +#include + +#include "manager.h" +#include "player.h" +#include "tools.h" + +#include "configmanager.h" +#include "game.h" +#include "chat.h" + +#include "connection.h" +#include "outputmessage.h" +#include "networkmessage.h" + +extern ConfigManager g_config; +extern Game g_game; +extern Chat g_chat; + +#ifdef __ENABLE_SERVER_DIAGNOSTIC__ +uint32_t ProtocolManager::protocolManagerCount = 0; +#endif + +void ProtocolManager::onRecvFirstMessage(NetworkMessage&) +{ + m_state = NO_CONNECTED; + if(g_config.getString(ConfigManager::MANAGER_PASSWORD).empty()) + { + addLogLine(LOGTYPE_WARNING, "Connection attempt on disabled protocol"); + getConnection()->close(); + return; + } + + if(!Manager::getInstance()->allow(getIP())) + { + addLogLine(LOGTYPE_WARNING, "IP not allowed"); + getConnection()->close(); + return; + } + + if(!Manager::getInstance()->addConnection(this)) + { + addLogLine(LOGTYPE_WARNING, "Cannot add more connections"); + getConnection()->close(); + return; + } + + if(NetworkMessage_ptr msg = getOutputBuffer()) + { + TRACK_MESSAGE(msg); + msg->put(MP_MSG_HELLO); + + msg->put(1); //version + msg->putString("TFMANAGER"); + } + + m_lastCommand = time(NULL); + m_state = NO_LOGGED_IN; +} + +void ProtocolManager::parsePacket(NetworkMessage& msg) +{ + if(g_game.getGameState() == GAMESTATE_SHUTDOWN) + { + getConnection()->close(); + return; + } + + uint8_t recvbyte = msg.get(); + OutputMessage_ptr output = getOutputBuffer(); + if(!output) + return; + + TRACK_MESSAGE(output); + switch(m_state) + { + case NO_LOGGED_IN: + { + if((time(NULL) - m_startTime) > 30000) + { + //login timeout + getConnection()->close(); + addLogLine(LOGTYPE_WARNING, "Login timeout"); + return; + } + + if(m_loginTries > 3) + { + output->put(MP_MSG_ERROR); + output->putString("Too many login attempts"); + + getConnection()->close(); + addLogLine(LOGTYPE_WARNING, "Too many login attempts"); + return; + } + + if(recvbyte != MP_MSG_LOGIN) + { + output->put(MP_MSG_ERROR); + output->putString("You are not logged in"); + + getConnection()->close(); + addLogLine(LOGTYPE_WARNING, "Wrong command while not logged in"); + return; + } + break; + } + + case LOGGED_IN: + break; + + default: + { + getConnection()->close(); + addLogLine(LOGTYPE_ERROR, "No valid connection state"); + return; + } + } + + m_lastCommand = time(NULL); + switch(recvbyte) + { + case MP_MSG_LOGIN: + { + if(m_state == NO_LOGGED_IN) + { + std::string pass = msg.getString(), word = g_config.getString(ConfigManager::MANAGER_PASSWORD); + _encrypt(word, false); + if(pass == word) + { + if(!Manager::getInstance()->loginConnection(this)) + { + output->put(MP_MSG_FAILURE); + output->putString("Unknown connection"); + + getConnection()->close(); + addLogLine(LOGTYPE_ERROR, "Login failed due to unknown connection"); + return; + } + else + { + output->put(MP_MSG_USERS); + addLogLine(LOGTYPE_EVENT, "Logged in, sending users"); + + std::map users; + for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) + { + if(!it->second->isRemoved()) + users[it->first] = it->second->getName(); + } + + output->put(users.size()); + for(std::map::iterator it = users.begin(); it != users.end(); ++it) + { + output->put(it->first); + output->putString(it->second); + } + + OutputMessagePool::getInstance()->send(output); + m_state = LOGGED_IN; + } + } + else + { + output->put(MP_MSG_ERROR); + output->putString("Wrong password"); + + m_loginTries++; + addLogLine(LOGTYPE_EVENT, "Login failed due to wrong password (" + pass + ")"); + } + } + + break; + } + + case MP_MSG_LOGOUT: + { + output->put(MP_MSG_BYE); + output->putString("Bye, bye!"); + + getConnection()->close(); + addLogLine(LOGTYPE_EVENT, "Logout"); + return; + } + + case MP_MSG_KEEP_ALIVE: + break; + + case MP_MSG_PING: + Dispatcher::getInstance().addTask(createTask(boost::bind(&ProtocolManager::pong, this))); + break; + + case MP_MSG_LUA: + Dispatcher::getInstance().addTask(createTask(boost::bind(&ProtocolManager::execute, this, msg.getString()))); + break; + + case MP_MSG_USER_INFO: + Dispatcher::getInstance().addTask(createTask(boost::bind(&ProtocolManager::user, this, msg.get()))); + break; + + case MP_MSG_CHAT_REQUEST: + Dispatcher::getInstance().addTask(createTask(boost::bind(&ProtocolManager::channels, this))); + break; + + case MP_MSG_CHAT_OPEN: + Dispatcher::getInstance().addTask(createTask(boost::bind(&ProtocolManager::channel, this, msg.get(), true))); + break; + + case MP_MSG_CHAT_CLOSE: + Dispatcher::getInstance().addTask(createTask(boost::bind(&ProtocolManager::channel, this, msg.get(), false))); + break; + + case MP_MSG_CHAT_TALK: + Dispatcher::getInstance().addTask(createTask(boost::bind(&ProtocolManager::chat, this, msg.getString(), msg.get(), (MessageClasses)msg.get(), msg.getString()))); + break; + + default: + break; + } +} + +void ProtocolManager::releaseProtocol() +{ + addLogLine(LOGTYPE_EVENT, "Closing protocol"); + Manager::getInstance()->removeConnection(this); + Protocol::releaseProtocol(); +} + +#ifdef __DEBUG_NET_DETAIL__ +void ProtocolManager::deleteProtocolTask() +{ + std::clog << "Deleting ProtocolManager" << std::endl; + Protocol::deleteProtocolTask(); +} + +#endif +void ProtocolManager::pong() +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(msg) + msg->put(MP_MSG_PONG); +} + +void ProtocolManager::execute(std::string lua) +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + switch(Manager::getInstance()->execute(lua)) + { + case LUA_TRUE: + { + msg->put(MP_MSG_SUCCESS); + addLogLine(LOGTYPE_EVENT, "Executed Lua script"); + break; + } + case LUA_RESERVE: + { + msg->put(MP_MSG_FAILURE); + msg->putString("Unable to reserve enviroment for Lua script"); + addLogLine(LOGTYPE_ERROR, "Unable to reserve enviroment for Lua script"); + break; + } + default: + { + msg->put(MP_MSG_FAILURE); + msg->putString("An error occured while executing Lua script, please check Server Log"); + addLogLine(LOGTYPE_ERROR, "An error occured while executing Lua script"); + break; + } + } +} + +void ProtocolManager::user(uint32_t playerId) +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + if(Player* player = g_game.getPlayerByID(playerId)) + { + msg->put(MP_MSG_USER_DATA); + msg->put(playerId); + + msg->put(player->getGroupId()); + msg->put(player->getVocationId()); + + msg->put(player->getLevel()); + msg->put(player->getMagicLevel()); + // TODO: continue... + } + else + { + msg->put(MP_MSG_ERROR); + msg->putString("Player not found"); + } +} + +void ProtocolManager::channels() +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + msg->put(MP_MSG_CHAT_LIST); + ChannelList list = g_chat.getPublicChannels(); + + msg->put(list.size()); + for(ChannelList::const_iterator it = list.begin(); it != list.end(); ++it) + { + msg->put((*it)->getId()); + msg->putString((*it)->getName()); + + msg->put((*it)->getFlags()); + msg->put((*it)->getUsers().size()); + } +} + +void ProtocolManager::chat(std::string name, uint16_t channelId, MessageClasses type, std::string message) +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + ChatChannel* channel = NULL; + if((channel = g_chat.getChannelById(channelId)) && g_chat.isPublicChannel(channelId)) + { + if(!channel->talk(name, type, message)) + { + msg->put(MP_MSG_FAILURE); + msg->putString("Could not talk to channel"); + } + else + msg->put(MP_MSG_SUCCESS); + } + else + { + msg->put(MP_MSG_ERROR); + msg->putString("Invalid channel"); + } +} + +void ProtocolManager::channel(uint16_t channelId, bool opening) +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + if(opening) + { + ChatChannel* channel = NULL; + if((channel = g_chat.getChannelById(channelId)) && g_chat.isPublicChannel(channelId)) + { + m_channels |= (1 << (uint32_t)channelId); + msg->put(MP_MSG_CHAT_USERS); + msg->put(channelId); + + UsersMap users = channel->getUsers(); + msg->put(users.size()); + for(UsersMap::const_iterator it = users.begin(); it != users.end(); ++it) + msg->put(it->first); + } + else + { + msg->put(MP_MSG_ERROR); + msg->putString("Invalid channel"); + } + } + else if(g_chat.getChannelById(channelId) && g_chat.isPublicChannel(channelId)) + { + m_channels &= ~(1 << (uint32_t)channelId); + msg->put(MP_MSG_SUCCESS); + } + else + { + msg->put(MP_MSG_ERROR); + msg->putString("Invalid channel"); + } +} + +void ProtocolManager::output(const std::string& message) +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(MP_MSG_OUTPUT); + msg->putString(message); +} + +void ProtocolManager::addUser(Player* player) +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(MP_MSG_USER_ADD); + + msg->put(player->getID()); + msg->putString(player->getName()); +} + +void ProtocolManager::removeUser(uint32_t playerId) +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(MP_MSG_USER_REMOVE); + msg->put(playerId); +} + +void ProtocolManager::talk(uint32_t playerId, uint16_t channelId, MessageClasses type, const std::string& message) +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + if(!(m_channels & (1 << (uint32_t)channelId))) + return; + + TRACK_MESSAGE(msg); + msg->put(MP_MSG_CHAT_MESSAGE); + msg->put(playerId); + + msg->put(channelId); + msg->put(type); + msg->putString(message); +} + +void ProtocolManager::addUser(uint32_t playerId, uint16_t channelId) +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(MP_MSG_CHAT_USER_ADD); + + msg->put(playerId); + msg->put(channelId); +} + +void ProtocolManager::removeUser(uint32_t playerId, uint16_t channelId) +{ + if(m_state != LOGGED_IN) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(MP_MSG_CHAT_USER_REMOVE); + + msg->put(playerId); + msg->put(channelId); +} + +bool Manager::addConnection(ProtocolManager* client) +{ + if(g_config.getNumber(ConfigManager::MANAGER_CONNECTIONS_LIMIT) > 0 && m_clients.size() + >= (size_t)g_config.getNumber(ConfigManager::MANAGER_CONNECTIONS_LIMIT)) + return false; + + m_clients[client] = false; + return true; +} + +bool Manager::loginConnection(ProtocolManager* client) +{ + ClientMap::iterator it = m_clients.find(client); + if(it == m_clients.end()) + return false; + + it->second = true; + return true; +} + +void Manager::removeConnection(ProtocolManager* client) +{ + ClientMap::iterator it = m_clients.find(client); + if(it != m_clients.end()) + m_clients.erase(it); +} + +bool Manager::allow(uint32_t ip) const +{ + if(!g_config.getBool(ConfigManager::MANAGER_LOCALHOST_ONLY)) + return !ConnectionManager::getInstance()->isDisabled(ip, 0xFE); + + if(ip == 0x0100007F) //127.0.0.1 + return true; + + if(g_config.getBool(ConfigManager::MANAGER_LOGS)) + LOG_MESSAGE(LOGTYPE_EVENT, "forbidden connection try", "MANAGER " + convertIPAddress(ip)); + + return false; +} + +LuaReturn_t Manager::execute(const std::string& script) +{ + if(!m_interface) + { + // create upon first execution to save some memory and + // avoid any startup crashes related to creation on load + m_interface = new LuaInterface("Manager Interface"); + m_interface->initState(); + } + + if(!m_interface->reserveEnv()) + return LUA_RESERVE; + + LuaReturn_t tmp = (LuaReturn_t)m_interface->loadBuffer(script); + m_interface->releaseEnv(); + return tmp; +} + +void Manager::output(const std::string& message) +{ + for(ClientMap::const_iterator it = m_clients.begin(); it != m_clients.end(); ++it) + { + if(it->second) + it->first->output(message); + } +} + +void Manager::addUser(Player* player) +{ + for(ClientMap::const_iterator it = m_clients.begin(); it != m_clients.end(); ++it) + { + if(it->second) + it->first->addUser(player); + } +} + +void Manager::removeUser(uint32_t playerId) +{ + for(ClientMap::const_iterator it = m_clients.begin(); it != m_clients.end(); ++it) + { + if(it->second) + it->first->removeUser(playerId); + } +} + +void Manager::talk(uint32_t playerId, uint16_t channelId, MessageClasses type, const std::string& message) +{ + for(ClientMap::const_iterator it = m_clients.begin(); it != m_clients.end(); ++it) + { + if(it->second && it->first->checkChannel(channelId)) + it->first->talk(playerId, channelId, type, message); + } +} + +void Manager::addUser(uint32_t playerId, uint16_t channelId) +{ + for(ClientMap::const_iterator it = m_clients.begin(); it != m_clients.end(); ++it) + { + if(it->second && it->first->checkChannel(channelId)) + it->first->addUser(playerId, channelId); + } +} + +void Manager::removeUser(uint32_t playerId, uint16_t channelId) +{ + for(ClientMap::const_iterator it = m_clients.begin(); it != m_clients.end(); ++it) + { + if(it->second && it->first->checkChannel(channelId)) + it->first->removeUser(playerId, channelId); + } +} + +void ProtocolManager::addLogLine(LogType_t type, std::string message) +{ + if(!g_config.getBool(ConfigManager::MANAGER_LOGS)) + return; + + std::string tmp = "MANAGER"; + if(getIP()) + tmp += " " + convertIPAddress(getIP()); + + LOG_MESSAGE(type, message, tmp) +} diff --git a/manager.h b/manager.h new file mode 100644 index 0000000..f0081d7 --- /dev/null +++ b/manager.h @@ -0,0 +1,170 @@ +//////////////////////////////////////////////////////////////////////// +// OpenTibia - an opensource roleplaying game +//////////////////////////////////////////////////////////////////////// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +//////////////////////////////////////////////////////////////////////// +#ifndef __MANAGER__ +#define __MANAGER__ +#include "otsystem.h" +#include "const.h" + +#include "textlogger.h" +#include "protocol.h" +#include "luascript.h" + +enum +{ + MP_MSG_LOGIN = 1, + MP_MSG_LOGOUT = 2, + MP_MSG_KEEP_ALIVE = 3, + MP_MSG_PING = 4, + MP_MSG_LUA = 5, + MP_MSG_USER_INFO = 6, + MP_MSG_CHAT_REQUEST = 7, + MP_MSG_CHAT_OPEN = 8, + MP_MSG_CHAT_CLOSE = 9, + MP_MSG_CHAT_TALK = 10, + + MP_MSG_ERROR = 1, + MP_MSG_SUCCESS = 2, + MP_MSG_FAILURE = 3, + MP_MSG_HELLO = 4, + MP_MSG_BYE = 5, + MP_MSG_PONG = 6, + MP_MSG_OUTPUT = 7, + MP_MSG_USERS = 8, + MP_MSG_USER_ADD = 9, + MP_MSG_USER_REMOVE = 10, + MP_MSG_USER_DATA = 11, + MP_MSG_CHAT_LIST = 12, + MP_MSG_CHAT_USERS = 13, + MP_MSG_CHAT_USER_ADD = 14, + MP_MSG_CHAT_USER_REMOVE = 15, + MP_MSG_CHAT_MESSAGE = 16 +}; + +enum LuaReturn_t +{ + LUA_RESERVE = -1, + LUA_FALSE = 0, + LUA_TRUE = 1 +}; + +class ProtocolManager; // TODO +class NetworkMessage; +class Player; + +class Manager +{ + public: + virtual ~Manager() {m_clients.clear();} + static Manager* getInstance() + { + static Manager instance; + return &instance; + } + + bool addConnection(ProtocolManager* client); + bool loginConnection(ProtocolManager* client); + void removeConnection(ProtocolManager* client); + + bool allow(uint32_t ip) const; + LuaReturn_t execute(const std::string& script); + + void output(const std::string& message); + void addUser(Player* player); + void removeUser(uint32_t playerId); + + void talk(uint32_t playerId, uint16_t channelId, MessageClasses type, const std::string& message); + void addUser(uint32_t playerId, uint16_t channelId); + void removeUser(uint32_t playerId, uint16_t channelId); + + protected: + Manager() {} + LuaInterface* m_interface; + + typedef std::map ClientMap; + ClientMap m_clients; +}; + + +class ProtocolManager : public Protocol +{ + public: +#ifdef __ENABLE_SERVER_DIAGNOSTIC__ + static uint32_t protocolManagerCount; +#endif + virtual void onRecvFirstMessage(NetworkMessage& msg); + + ProtocolManager(Connection_ptr connection): Protocol(connection) + { + m_state = NO_CONNECTED; + m_loginTries = m_lastCommand = m_channels = 0; + m_startTime = time(NULL); +#ifdef __ENABLE_SERVER_DIAGNOSTIC__ + protocolManagerCount++; +#endif + } + virtual ~ProtocolManager() + { +#ifdef __ENABLE_SERVER_DIAGNOSTIC__ + protocolManagerCount--; +#endif + } + + enum {protocolId = 0xFD}; + enum {isSingleSocket = false}; + enum {hasChecksum = false}; + static const char* protocolName() {return "manager protocol";} + + bool checkChannel(uint16_t channelId) const {return ((m_channels & (uint32_t)channelId) == (uint32_t)channelId);} + + void output(const std::string& message); + void addUser(Player* player); + void removeUser(uint32_t playerId); + + void talk(uint32_t playerId, uint16_t channelId, MessageClasses type, const std::string& message); + void addUser(uint32_t playerId, uint16_t channelId); + void removeUser(uint32_t playerId, uint16_t channelId); + + protected: + enum ProtocolState_t + { + NO_CONNECTED, + NO_LOGGED_IN, + LOGGED_IN, + }; + + virtual void parsePacket(NetworkMessage& msg); + virtual void releaseProtocol(); +#ifdef __DEBUG_NET_DETAIL__ + virtual void deleteProtocolTask(); +#endif + + void pong(); + void execute(std::string lua); + void user(uint32_t playerId); + + void channels(); + void chat(std::string name, uint16_t channelId, MessageClasses type, std::string message); + void channel(uint16_t channelId, bool opening); + + private: + void addLogLine(LogType_t type, std::string message); + + int32_t m_loginTries; + ProtocolState_t m_state; + uint32_t m_lastCommand, m_startTime, m_channels; +}; +#endif diff --git a/map.cpp b/map.cpp index 5481fb4..d0f76c7 100644 --- a/map.cpp +++ b/map.cpp @@ -47,31 +47,31 @@ bool Map::loadMap(const std::string& identifier) IOMap* loader = new IOMap(); if(!loader->loadMap(this, identifier)) { - std::cout << "> FATAL: OTBM Loader - " << loader->getLastErrorString() << std::endl; + std::clog << "> FATAL: OTBM Loader - " << loader->getLastErrorString() << std::endl; return false; } - std::cout << "> Map loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; + std::clog << "> Loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; start = OTSYS_TIME(); if(!loader->loadSpawns(this)) - std::cout << "> WARNING: Could not load spawn data." << std::endl; + std::clog << "> WARNING: Could not load spawn data." << std::endl; if(!loader->loadHouses(this)) - std::cout << "> WARNING: Could not load house data." << std::endl; + std::clog << "> WARNING: Could not load house data." << std::endl; delete loader; - std::cout << "> Data parsing time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; + std::clog << "> Parsing time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; start = OTSYS_TIME(); IOMapSerialize::getInstance()->updateHouses(); IOMapSerialize::getInstance()->updateAuctions(); - std::cout << "> Houses synchronization time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; + std::clog << "> Synchronization time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; start = OTSYS_TIME(); IOMapSerialize::getInstance()->loadHouses(); IOMapSerialize::getInstance()->loadMap(this); - std::cout << "> Content unserialization time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; + std::clog << "> Unserialization time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; return true; } @@ -104,6 +104,11 @@ bool Map::saveMap() return saved; } +bool Map::updateAuctions() +{ + return IOMapSerialize::getInstance()->updateAuctions(); +} + Tile* Map::getTile(int32_t x, int32_t y, int32_t z) { if(x < 0 || x > 0xFFFF || y < 0 || y > 0xFFFF || z < 0 || z >= MAP_MAX_LAYERS) @@ -124,7 +129,7 @@ void Map::setTile(uint16_t x, uint16_t y, uint16_t z, Tile* newTile) { if(z >= MAP_MAX_LAYERS) { - std::cout << "[Error - Map::setTile]: Attempt to set tile on invalid Z coordinate - " << z << "!" << std::endl; + std::clog << "[Error - Map::setTile]: Attempt to set tile on invalid Z coordinate - " << z << "!" << std::endl; return; } @@ -161,7 +166,7 @@ void Map::setTile(uint16_t x, uint16_t y, uint16_t z, Tile* newTile) newTile->qt_node = leaf; } else - std::cout << "[Error - Map::setTile] Tile already exists - pos " << offsetX << "/" << offsetY << "/" << z << std::endl; + std::clog << "[Error - Map::setTile] Tile already exists - pos " << offsetX << "/" << offsetY << "/" << z << std::endl; if(newTile->hasFlag(TILESTATE_REFRESH)) { @@ -181,7 +186,7 @@ bool Map::placeCreature(const Position& centerPos, Creature* creature, bool exte { bool foundTile = false, placeInPz = false; Tile* tile = getTile(centerPos); - if(tile) + if(tile && !extendedPos) { placeInPz = tile->hasFlag(TILESTATE_PROTECTIONZONE); uint32_t flags = FLAG_IGNOREBLOCKITEM; @@ -303,21 +308,19 @@ void Map::getSpectatorsInternal(SpectatorVec& list, const Position& centerPos, b { do { - Creature* creature = (*it); - const Position& pos = creature->getPosition(); - - int32_t offsetZ = centerPos.z - pos.z; + const Position& pos = (*it)->getPosition(); if(pos.z < minRangeZ || pos.z > maxRangeZ) continue; + int32_t offsetZ = centerPos.z - pos.z; if(pos.y < (centerPos.y + minRangeY + offsetZ) || pos.y > (centerPos.y + maxRangeY + offsetZ)) continue; if(pos.x < (centerPos.x + minRangeX + offsetZ) || pos.x > (centerPos.x + maxRangeX + offsetZ)) continue; - if(!checkForDuplicate || std::find(list.begin(), list.end(), creature) == list.end()) - list.push_back(creature); + if(!checkForDuplicate || std::find(list.begin(), list.end(), *it) == list.end()) + list.push_back(*it); } while(++it != nodeList.end()); } @@ -454,7 +457,7 @@ bool Map::canThrowObjectTo(const Position& fromPos, const Position& toPos, bool //underground 8->15 //ground level and above 7->0 if((fromPos.z >= 8 && toPos.z < 8) || (toPos.z >= 8 && - fromPos.z < 8) || fromPos.z - fromPos.z > 2) + fromPos.z < 8) || fromPos.z - toPos.z > 2) return false; int32_t deltax = std::abs(fromPos.x - toPos.x), deltay = std::abs( @@ -470,7 +473,7 @@ bool Map::canThrowObjectTo(const Position& fromPos, const Position& toPos, bool bool Map::checkSightLine(const Position& fromPos, const Position& toPos) const { - Position start = fromPos; + /*Position start = fromPos; Position end = toPos; int32_t x, y, z, dx = std::abs(start.x - end.x), dy = std::abs(start.y - end.y), @@ -571,6 +574,43 @@ bool Map::checkSightLine(const Position& fromPos, const Position& toPos) const } } + return true;*/ + if(Position::areInRange<0,0,15>(fromPos, toPos)) + return true; + + Position start(fromPos.z > toPos.z ? toPos : fromPos); + Position destination(fromPos.z > toPos.z ? fromPos : toPos); + + const int8_t mx = start.x < destination.x ? 1 : start.x == destination.x ? 0 : -1; + const int8_t my = start.y < destination.y ? 1 : start.y == destination.y ? 0 : -1; + + int32_t A = destination.y - start.y, B = start.x - destination.x, + C = -(A * destination.x + B * destination.y); + while(!Position::areInRange<0,0,15>(start, destination)) + { + int32_t moveH = std::abs(A * (start.x + mx) + B * (start.y) + C), + moveV = std::abs(A * (start.x) + B * (start.y + my) + C), + moveX = std::abs(A * (start.x + mx) + B * (start.y + my) + C); + if(start.y != destination.y && (start.x == destination.x || moveH > moveV || moveH > moveX)) + start.y += my; + + if(start.x != destination.x && (start.y == destination.y || moveV > moveH || moveV > moveX)) + start.x += mx; + + const Tile* tile = const_cast(this)->getTile(start); + if(tile && tile->hasProperty(BLOCKPROJECTILE)) + return false; + } + + while(start.z != destination.z) // now we need to perform a jump between floors to see if everything is clear (literally) + { + const Tile* tile = const_cast(this)->getTile(start); + if(tile && tile->getThingCount() > 0) + return false; + + start.z++; + } + return true; } @@ -783,7 +823,7 @@ bool Map::getPathMatching(const Creature* creature, std::list& dirLis AStarNode* n = NULL; const Tile* tile = NULL; - while(fpp.maxSearchDist != -1 || nodes.countClosedNodes() < 100) + while(fpp.maxSearchDist != -1 || nodes.countClosedNodes() < fpp.maxClosedNodes) { if(!(n = nodes.getBestNode())) { @@ -925,7 +965,7 @@ AStarNode* AStarNodes::getBestNode() uint32_t bestNode = 0; bool found = false; - for(uint32_t i = 0; i < curNode; i++) + for(uint32_t i = 0; i < curNode; ++i) { if(nodes[i].f < bestNodeF && openNodes[i] == 1) { @@ -951,7 +991,7 @@ void AStarNodes::closeNode(AStarNode* node) } assert(pos >= MAX_NODES); - std::cout << "AStarNodes. trying to close node out of range" << std::endl; + std::clog << "AStarNodes. trying to close node out of range" << std::endl; return; } @@ -965,14 +1005,14 @@ void AStarNodes::openNode(AStarNode* node) } assert(pos >= MAX_NODES); - std::cout << "AStarNodes. trying to open node out of range" << std::endl; + std::clog << "AStarNodes. trying to open node out of range" << std::endl; return; } uint32_t AStarNodes::countClosedNodes() { uint32_t counter = 0; - for(uint32_t i = 0; i < curNode; i++) + for(uint32_t i = 0; i < curNode; ++i) { if(!openNodes[i]) counter++; @@ -984,7 +1024,7 @@ uint32_t AStarNodes::countClosedNodes() uint32_t AStarNodes::countOpenNodes() { uint32_t counter = 0; - for(uint32_t i = 0; i < curNode; i++) + for(uint32_t i = 0; i < curNode; ++i) { if(openNodes[i] == 1) counter++; @@ -995,7 +1035,7 @@ uint32_t AStarNodes::countOpenNodes() bool AStarNodes::isInList(uint16_t x, uint16_t y) { - for(uint32_t i = 0; i < curNode; i++) + for(uint32_t i = 0; i < curNode; ++i) { if(nodes[i].x == x && nodes[i].y == y) return true; @@ -1006,7 +1046,7 @@ bool AStarNodes::isInList(uint16_t x, uint16_t y) AStarNode* AStarNodes::getNodeInList(uint16_t x, uint16_t y) { - for(uint32_t i = 0; i < curNode; i++) + for(uint32_t i = 0; i < curNode; ++i) { if(nodes[i].x == x && nodes[i].y == y) return &nodes[i]; @@ -1015,8 +1055,8 @@ AStarNode* AStarNodes::getNodeInList(uint16_t x, uint16_t y) return NULL; } -int32_t AStarNodes::getMapWalkCost(const Creature* creature, AStarNode* node, - const Tile* neighbourTile, const Position& neighbourPos) +int32_t AStarNodes::getMapWalkCost(const Creature*, AStarNode* node, + const Tile*, const Position& neighbourPos) { if(std::abs(node->x - neighbourPos.x) == std::abs(node->y - neighbourPos.y)) //diagonal movement extra cost return MAP_DIAGONALWALKCOST; @@ -1078,7 +1118,7 @@ QTreeLeafNode* QTreeNode::getLeaf(uint16_t x, uint16_t y) uint32_t index = ((x & 0x8000) >> 15) | ((y & 0x8000) >> 14); if(m_child[index]) - return m_child[index]->getLeaf(x * 2, y * 2); + return m_child[index]->getLeaf(x << 1, y << 1); return NULL; } @@ -1097,8 +1137,8 @@ QTreeLeafNode* QTreeNode::getLeafStatic(QTreeNode* root, uint16_t x, uint16_t y) return NULL; currentNode = currentNode->m_child[index]; - currentX = currentX * 2; - currentY = currentY * 2; + currentX <<= 1; + currentY <<= 1; } return NULL; @@ -1120,7 +1160,7 @@ QTreeLeafNode* QTreeNode::createLeaf(uint16_t x, uint16_t y, uint16_t level) } } - return m_child[index]->createLeaf(x * 2, y * 2, level - 1); + return m_child[index]->createLeaf(x << 1, y << 1, level - 1); } return static_cast(this); diff --git a/map.h b/map.h index ed10260..51e0e2b 100644 --- a/map.h +++ b/map.h @@ -175,6 +175,11 @@ class Map */ bool saveMap(); + /** + * \returns true if updating house auctions completed + */ + bool updateAuctions(); + /** * Get a single tile. * \returns A pointer to that tile. diff --git a/md5.cpp b/md5.cpp deleted file mode 100644 index 5579bf7..0000000 --- a/md5.cpp +++ /dev/null @@ -1,255 +0,0 @@ -// Free for all implementation of the MD5 hash algorithm - -/* - ********************************************************************** - ** MD5.cpp ** - ** ** - ** - Style modified by Tony Ray, January 2001 ** - ** Added support for randomizing initialization constants ** - ** - Style modified by Dominik Reichl, April 2003 ** - ** Optimized code ** - ** ** - ********************************************************************** - */ - -/* - ********************************************************************** - ** MD5.c ** - ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** - ** Created: 2/17/90 RLR ** - ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version ** - ********************************************************************** - */ - -/* - ********************************************************************** - ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** - ** ** - ** License to copy and use this software is granted provided that ** - ** it is identified as the "RSA Data Security, Inc. MD5 Message ** - ** Digest Algorithm" in all material mentioning or referencing this ** - ** software or this function. ** - ** ** - ** License is also granted to make and use derivative works ** - ** provided that such works are identified as "derived from the RSA ** - ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** - ** material mentioning or referencing the derived work. ** - ** ** - ** RSA Data Security, Inc. makes no representations concerning ** - ** either the merchantability of this software or the suitability ** - ** of this software for any particular purpose. It is provided "as ** - ** is" without express or implied warranty of any kind. ** - ** ** - ** These notices must be retained in any copies of any part of this ** - ** documentation and/or software. ** - ********************************************************************** - */ -#include "otpch.h" -#include "md5.h" - -/* Padding */ -static unsigned char MD5_PADDING[64] = -{ - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -/* MD5_F, MD5_G and MD5_H are basic MD5 functions: selection, majority, parity */ -#define MD5_F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define MD5_G(x, y, z) (((x) & (z)) | ((y) & (~z))) -#define MD5_H(x, y, z) ((x) ^ (y) ^ (z)) -#define MD5_I(x, y, z) ((y) ^ ((x) | (~z))) - -/* ROTATE_LEFT rotates x left n bits */ -#ifndef ROTATE_LEFT -#define ROTATE_LEFT(x, n) ((((x) << (n)) & 0xffffffffU) | ((x) >> (32-(n)))) -#endif - -/* MD5_FF, MD5_GG, MD5_HH, and MD5_II transformations for rounds 1, 2, 3, and 4 */ -/* Rotation is separate from addition to prevent recomputation */ -#define MD5_FF(a, b, c, d, x, s, ac) {(a) += MD5_F ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } -#define MD5_GG(a, b, c, d, x, s, ac) {(a) += MD5_G ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } -#define MD5_HH(a, b, c, d, x, s, ac) {(a) += MD5_H ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } -#define MD5_II(a, b, c, d, x, s, ac) {(a) += MD5_I ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } - -/* Constants for transformation */ -#define MD5_S11 7 /* Round 1 */ -#define MD5_S12 12 -#define MD5_S13 17 -#define MD5_S14 22 -#define MD5_S21 5 /* Round 2 */ -#define MD5_S22 9 -#define MD5_S23 14 -#define MD5_S24 20 -#define MD5_S31 4 /* Round 3 */ -#define MD5_S32 11 -#define MD5_S33 16 -#define MD5_S34 23 -#define MD5_S41 6 /* Round 4 */ -#define MD5_S42 10 -#define MD5_S43 15 -#define MD5_S44 21 - -/* Basic MD5 step. MD5_Transform buf based on in */ -void MD5_Transform (UINT4 *buf, UINT4 *in) -{ - UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; - - /* Round 1 */ - MD5_FF ( a, b, c, d, in[ 0], MD5_S11, (UINT4) 3614090360u); /* 1 */ - MD5_FF ( d, a, b, c, in[ 1], MD5_S12, (UINT4) 3905402710u); /* 2 */ - MD5_FF ( c, d, a, b, in[ 2], MD5_S13, (UINT4) 606105819u); /* 3 */ - MD5_FF ( b, c, d, a, in[ 3], MD5_S14, (UINT4) 3250441966u); /* 4 */ - MD5_FF ( a, b, c, d, in[ 4], MD5_S11, (UINT4) 4118548399u); /* 5 */ - MD5_FF ( d, a, b, c, in[ 5], MD5_S12, (UINT4) 1200080426u); /* 6 */ - MD5_FF ( c, d, a, b, in[ 6], MD5_S13, (UINT4) 2821735955u); /* 7 */ - MD5_FF ( b, c, d, a, in[ 7], MD5_S14, (UINT4) 4249261313u); /* 8 */ - MD5_FF ( a, b, c, d, in[ 8], MD5_S11, (UINT4) 1770035416u); /* 9 */ - MD5_FF ( d, a, b, c, in[ 9], MD5_S12, (UINT4) 2336552879u); /* 10 */ - MD5_FF ( c, d, a, b, in[10], MD5_S13, (UINT4) 4294925233u); /* 11 */ - MD5_FF ( b, c, d, a, in[11], MD5_S14, (UINT4) 2304563134u); /* 12 */ - MD5_FF ( a, b, c, d, in[12], MD5_S11, (UINT4) 1804603682u); /* 13 */ - MD5_FF ( d, a, b, c, in[13], MD5_S12, (UINT4) 4254626195u); /* 14 */ - MD5_FF ( c, d, a, b, in[14], MD5_S13, (UINT4) 2792965006u); /* 15 */ - MD5_FF ( b, c, d, a, in[15], MD5_S14, (UINT4) 1236535329u); /* 16 */ - - /* Round 2 */ - MD5_GG ( a, b, c, d, in[ 1], MD5_S21, (UINT4) 4129170786u); /* 17 */ - MD5_GG ( d, a, b, c, in[ 6], MD5_S22, (UINT4) 3225465664u); /* 18 */ - MD5_GG ( c, d, a, b, in[11], MD5_S23, (UINT4) 643717713u); /* 19 */ - MD5_GG ( b, c, d, a, in[ 0], MD5_S24, (UINT4) 3921069994u); /* 20 */ - MD5_GG ( a, b, c, d, in[ 5], MD5_S21, (UINT4) 3593408605u); /* 21 */ - MD5_GG ( d, a, b, c, in[10], MD5_S22, (UINT4) 38016083u); /* 22 */ - MD5_GG ( c, d, a, b, in[15], MD5_S23, (UINT4) 3634488961u); /* 23 */ - MD5_GG ( b, c, d, a, in[ 4], MD5_S24, (UINT4) 3889429448u); /* 24 */ - MD5_GG ( a, b, c, d, in[ 9], MD5_S21, (UINT4) 568446438u); /* 25 */ - MD5_GG ( d, a, b, c, in[14], MD5_S22, (UINT4) 3275163606u); /* 26 */ - MD5_GG ( c, d, a, b, in[ 3], MD5_S23, (UINT4) 4107603335u); /* 27 */ - MD5_GG ( b, c, d, a, in[ 8], MD5_S24, (UINT4) 1163531501u); /* 28 */ - MD5_GG ( a, b, c, d, in[13], MD5_S21, (UINT4) 2850285829u); /* 29 */ - MD5_GG ( d, a, b, c, in[ 2], MD5_S22, (UINT4) 4243563512u); /* 30 */ - MD5_GG ( c, d, a, b, in[ 7], MD5_S23, (UINT4) 1735328473u); /* 31 */ - MD5_GG ( b, c, d, a, in[12], MD5_S24, (UINT4) 2368359562u); /* 32 */ - - /* Round 3 */ - MD5_HH ( a, b, c, d, in[ 5], MD5_S31, (UINT4) 4294588738u); /* 33 */ - MD5_HH ( d, a, b, c, in[ 8], MD5_S32, (UINT4) 2272392833u); /* 34 */ - MD5_HH ( c, d, a, b, in[11], MD5_S33, (UINT4) 1839030562u); /* 35 */ - MD5_HH ( b, c, d, a, in[14], MD5_S34, (UINT4) 4259657740u); /* 36 */ - MD5_HH ( a, b, c, d, in[ 1], MD5_S31, (UINT4) 2763975236u); /* 37 */ - MD5_HH ( d, a, b, c, in[ 4], MD5_S32, (UINT4) 1272893353u); /* 38 */ - MD5_HH ( c, d, a, b, in[ 7], MD5_S33, (UINT4) 4139469664u); /* 39 */ - MD5_HH ( b, c, d, a, in[10], MD5_S34, (UINT4) 3200236656u); /* 40 */ - MD5_HH ( a, b, c, d, in[13], MD5_S31, (UINT4) 681279174u); /* 41 */ - MD5_HH ( d, a, b, c, in[ 0], MD5_S32, (UINT4) 3936430074u); /* 42 */ - MD5_HH ( c, d, a, b, in[ 3], MD5_S33, (UINT4) 3572445317u); /* 43 */ - MD5_HH ( b, c, d, a, in[ 6], MD5_S34, (UINT4) 76029189u); /* 44 */ - MD5_HH ( a, b, c, d, in[ 9], MD5_S31, (UINT4) 3654602809u); /* 45 */ - MD5_HH ( d, a, b, c, in[12], MD5_S32, (UINT4) 3873151461u); /* 46 */ - MD5_HH ( c, d, a, b, in[15], MD5_S33, (UINT4) 530742520u); /* 47 */ - MD5_HH ( b, c, d, a, in[ 2], MD5_S34, (UINT4) 3299628645u); /* 48 */ - - /* Round 4 */ - MD5_II ( a, b, c, d, in[ 0], MD5_S41, (UINT4) 4096336452u); /* 49 */ - MD5_II ( d, a, b, c, in[ 7], MD5_S42, (UINT4) 1126891415u); /* 50 */ - MD5_II ( c, d, a, b, in[14], MD5_S43, (UINT4) 2878612391u); /* 51 */ - MD5_II ( b, c, d, a, in[ 5], MD5_S44, (UINT4) 4237533241u); /* 52 */ - MD5_II ( a, b, c, d, in[12], MD5_S41, (UINT4) 1700485571u); /* 53 */ - MD5_II ( d, a, b, c, in[ 3], MD5_S42, (UINT4) 2399980690u); /* 54 */ - MD5_II ( c, d, a, b, in[10], MD5_S43, (UINT4) 4293915773u); /* 55 */ - MD5_II ( b, c, d, a, in[ 1], MD5_S44, (UINT4) 2240044497u); /* 56 */ - MD5_II ( a, b, c, d, in[ 8], MD5_S41, (UINT4) 1873313359u); /* 57 */ - MD5_II ( d, a, b, c, in[15], MD5_S42, (UINT4) 4264355552u); /* 58 */ - MD5_II ( c, d, a, b, in[ 6], MD5_S43, (UINT4) 2734768916u); /* 59 */ - MD5_II ( b, c, d, a, in[13], MD5_S44, (UINT4) 1309151649u); /* 60 */ - MD5_II ( a, b, c, d, in[ 4], MD5_S41, (UINT4) 4149444226u); /* 61 */ - MD5_II ( d, a, b, c, in[11], MD5_S42, (UINT4) 3174756917u); /* 62 */ - MD5_II ( c, d, a, b, in[ 2], MD5_S43, (UINT4) 718787259u); /* 63 */ - MD5_II ( b, c, d, a, in[ 9], MD5_S44, (UINT4) 3951481745u); /* 64 */ - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -// Set pseudoRandomNumber to zero for RFC MD5 implementation -void MD5Init (MD5_CTX *mdContext, unsigned long pseudoRandomNumber) -{ - mdContext->i[0] = mdContext->i[1] = (UINT4)0; - - /* Load magic initialization constants */ - mdContext->buf[0] = (UINT4)0x67452301 + (pseudoRandomNumber * 11); - mdContext->buf[1] = (UINT4)0xefcdab89 + (pseudoRandomNumber * 71); - mdContext->buf[2] = (UINT4)0x98badcfe + (pseudoRandomNumber * 37); - mdContext->buf[3] = (UINT4)0x10325476 + (pseudoRandomNumber * 97); -} - -void MD5Update (MD5_CTX *mdContext, const unsigned char *inBuf, unsigned int inLen) -{ - UINT4 in[16]; - int mdi = 0; - unsigned int i = 0, ii = 0; - - /* Compute number of bytes mod 64 */ - mdi = (int)((mdContext->i[0] >> 3) & 0x3F); - - /* Update number of bits */ - if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) - mdContext->i[1]++; - mdContext->i[0] += ((UINT4)inLen << 3); - mdContext->i[1] += ((UINT4)inLen >> 29); - - while (inLen--) - { - /* Add new character to buffer, increment mdi */ - mdContext->in[mdi++] = *inBuf++; - - /* Transform if necessary */ - if (mdi == 0x40) - { - for (i = 0, ii = 0; i < 16; i++, ii += 4) - in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | (((UINT4)mdContext->in[ii+2]) << 16) | (((UINT4)mdContext->in[ii+1]) << 8) | ((UINT4)mdContext->in[ii]); - MD5_Transform (mdContext->buf, in); - mdi = 0; - } - } -} - -void MD5Final (MD5_CTX *mdContext) -{ - UINT4 in[16]; - int mdi = 0; - unsigned int i = 0, ii = 0, padLen = 0; - - /* Save number of bits */ - in[14] = mdContext->i[0]; - in[15] = mdContext->i[1]; - - /* Compute number of bytes mod 64 */ - mdi = (int)((mdContext->i[0] >> 3) & 0x3F); - - /* Pad out to 56 mod 64 */ - padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); - MD5Update (mdContext, MD5_PADDING, padLen); - - /* Append length in bits and transform */ - for(i = 0, ii = 0; i < 14; i++, ii += 4) - in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | (((UINT4)mdContext->in[ii+2]) << 16) | (((UINT4)mdContext->in[ii+1]) << 8) | ((UINT4)mdContext->in[ii]); - MD5_Transform (mdContext->buf, in); - - /* Store buffer in digest */ - for(i = 0, ii = 0; i < 4; i++, ii += 4) - { - mdContext->digest[ii] = (unsigned char)( mdContext->buf[i] & 0xFF); - mdContext->digest[ii+1] = (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); - mdContext->digest[ii+2] = (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); - mdContext->digest[ii+3] = (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); - } -} diff --git a/md5.h b/md5.h deleted file mode 100644 index 7ce691e..0000000 --- a/md5.h +++ /dev/null @@ -1,91 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// - -/* - ********************************************************************** - ** MD5.h ** - ** ** - ** - Style modified by Tony Ray, January 2001 ** - ** Added support for randomizing initialization constants ** - ** - Style modified by Dominik Reichl, September 2002 ** - ** Optimized code ** - ** ** - ********************************************************************** - */ - -/* - ********************************************************************** - ** MD5.h -- Header file for implementation of MD5 ** - ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** - ** Created: 2/17/90 RLR ** - ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** - ** Revised (for MD5): RLR 4/27/91 ** - ** -- G modified to have y&~z instead of y&z ** - ** -- FF, GG, HH modified to add in last register done ** - ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** - ** -- distinct additive constant for each step ** - ** -- round 4 added, working mod 7 ** - ********************************************************************** - */ - -/* - ********************************************************************** - ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** - ** ** - ** License to copy and use this software is granted provided that ** - ** it is identified as the "RSA Data Security, Inc. MD5 Message ** - ** Digest Algorithm" in all material mentioning or referencing this ** - ** software or this function. ** - ** ** - ** License is also granted to make and use derivative works ** - ** provided that such works are identified as "derived from the RSA ** - ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** - ** material mentioning or referencing the derived work. ** - ** ** - ** RSA Data Security, Inc. makes no representations concerning ** - ** either the merchantability of this software or the suitability ** - ** of this software for any particular purpose. It is provided "as ** - ** is" without express or implied warranty of any kind. ** - ** ** - ** These notices must be retained in any copies of any part of this ** - ** documentation and/or software. ** - ********************************************************************** - */ - -#ifndef __MD5__ -#define __MD5__ -#include "definitions.h" - -/* Typedef a 32 bit type */ -#ifndef UINT4 -typedef uint32_t UINT4; -#endif - -/* Data structure for MD5 (Message Digest) computation */ -typedef struct -{ - UINT4 i[2]; /* Number of _bits_ handled mod 2^64 */ - UINT4 buf[4]; /* Scratch buffer */ - unsigned char in[64]; /* Input buffer */ - unsigned char digest[16]; /* Actual digest after MD5Final call */ -} MD5_CTX; -void MD5_Transform (UINT4 *buf, UINT4 *in); - -void MD5Init(MD5_CTX *mdContext, unsigned long pseudoRandomNumber = 0); -void MD5Update(MD5_CTX *mdContext, const unsigned char *inBuf, unsigned int inLen); -void MD5Final(MD5_CTX *mdContext); -#endif diff --git a/mods/change-gold.xml b/mods/change-gold.xml new file mode 100644 index 0000000..059bd60 --- /dev/null +++ b/mods/change-gold.xml @@ -0,0 +1,39 @@ + + + + diff --git a/mods/changender_command.xml b/mods/changender_command.xml index 1448f26..4044760 100644 --- a/mods/changender_command.xml +++ b/mods/changender_command.xml @@ -12,19 +12,21 @@ ]]> = 2) then + if(getPlayerSex(cid) > 1) then doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "You cannot change your gender.") return end - if(getPlayerPremiumDays(cid) < config.costPremiumDays) then - doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Sorry, not enough premium time - changing gender costs " .. config.costPremiumDays .. " premium days.") - doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF) - return - end + if(not getConfigValue('freePremium')) then + if(getPlayerPremiumDays(cid) < config.costPremiumDays) then + doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Sorry, not enough premium time - changing gender costs " .. config.costPremiumDays .. " premium days.") + doSendMagicEffect(getCreaturePosition(cid), CONST_ME_POFF) + return + end - if(getPlayerPremiumDays(cid) < 65535) then - doPlayerAddPremiumDays(cid, -config.costPremiumDays) + if(getPlayerPremiumDays(cid) < 65535) then + doPlayerAddPremiumDays(cid, -config.costPremiumDays) + end end doPlayerSetSex(cid, getPlayerSex(cid) == PLAYERSEX_FEMALE and PLAYERSEX_MALE or PLAYERSEX_FEMALE) diff --git a/mods/firstitems.xml b/mods/firstitems.xml index fc480d6..7dd0260 100644 --- a/mods/firstitems.xml +++ b/mods/firstitems.xml @@ -6,27 +6,23 @@ items = {2050, 2382} } ]]> - 0) then + return + end - function onLogin(cid) - if(getPlayerStorageValue(cid, config.storage) > 0) then - return true - end - - for _, id in ipairs(config.items) do - doPlayerAddItem(cid, id, 1) - end - - if(getPlayerSex(cid) == PLAYERSEX_FEMALE) then - doPlayerAddItem(cid, 2651, 1) - else - doPlayerAddItem(cid, 2650, 1) - end + for _, id in ipairs(config.items) do + doPlayerAddItem(cid, id, 1) + end - doAddContainerItem(doPlayerAddItem(cid, 1987, 1), 2674, 1) - setPlayerStorageValue(cid, config.storage, 1) - return true + if(getPlayerSex(cid) == PLAYERSEX_FEMALE) then + doPlayerAddItem(cid, 2651, 1) + else + doPlayerAddItem(cid, 2650, 1) end + + doAddContainerItem(doPlayerAddItem(cid, 1987, 1), 2674, 1) + setPlayerStorageValue(cid, config.storage, 1) ]]> diff --git a/monster.cpp b/monster.cpp index 7372850..37743c1 100644 --- a/monster.cpp +++ b/monster.cpp @@ -25,10 +25,12 @@ #include "configmanager.h" #include "game.h" +#include "creatureevent.h" extern Game g_game; extern ConfigManager g_config; extern Monsters g_monsters; +extern CreatureEvents* g_creatureEvents; AutoListMonster::autoList; #ifdef __ENABLE_SERVER_DIAGNOSTIC__ @@ -52,10 +54,15 @@ Monster* Monster::createMonster(const std::string& name) Monster::Monster(MonsterType* _mType): Creature() { +#ifdef __ENABLE_SERVER_DIAGNOSTIC__ + monsterCount++; +#endif + mType = _mType; + isIdle = true; isMasterInRange = false; teleportToMaster = false; - mType = _mType; + spawn = NULL; raid = NULL; defaultOutfit = mType->outfit; @@ -63,19 +70,24 @@ Monster::Monster(MonsterType* _mType): double multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_HEALTH); health = (int32_t)(mType->health * multiplier); - healthMax = (int32_t)(mType->healthMax * multiplier); + healthMin = mType->healthMin, healthMax = mType->healthMax; + if(healthMin > 0) + healthMax = random_range(healthMin, healthMax); + healthMax = (int32_t)(healthMax * multiplier); baseSpeed = mType->baseSpeed; internalLight.level = mType->lightLevel; internalLight.color = mType->lightColor; setSkull(mType->skull); setShield(mType->partyShield); + setEmblem(mType->guildEmblem); hideName = mType->hideName, hideHealth = mType->hideHealth; minCombatValue = 0; maxCombatValue = 0; + lastDamage = 0; targetTicks = 0; targetChangeTicks = 0; targetChangeCooldown = 0; @@ -88,12 +100,8 @@ Monster::Monster(MonsterType* _mType): for(StringVec::iterator it = mType->scriptList.begin(); it != mType->scriptList.end(); ++it) { if(!registerCreatureEvent(*it)) - std::cout << "[Warning - Monster::Monster] Unknown event name - " << *it << std::endl; + std::clog << "[Warning - Monster::Monster] Unknown event name - " << *it << std::endl; } - -#ifdef __ENABLE_SERVER_DIAGNOSTIC__ - monsterCount++; -#endif } Monster::~Monster() @@ -111,27 +119,29 @@ Monster::~Monster() } } -void Monster::onAttackedCreature(Creature* target) +void Monster::onTarget(Creature* target) { - Creature::onAttackedCreature(target); + Creature::onTarget(target); if(isSummon()) - getMaster()->onSummonAttackedCreature(this, target); + master->onSummonTarget(this, target); } -void Monster::onAttackedCreatureDisappear(bool isLogout) +void Monster::onTargetDisappear(bool) { #ifdef __DEBUG__ - std::cout << "Attacked creature disappeared." << std::endl; + std::clog << "Attacked creature disappeared." << std::endl; #endif attackTicks = 0; extraMeleeAttack = true; + if(g_config.getBool(ConfigManager::MONSTER_SPAWN_WALKBACK)) + g_game.steerCreature(this, masterPosition, 5000); } -void Monster::onAttackedCreatureDrain(Creature* target, int32_t points) +void Monster::onTargetDrain(Creature* target, int32_t points) { - Creature::onAttackedCreatureDrain(target, points); + Creature::onTargetDrain(target, points); if(isSummon()) - getMaster()->onSummonAttackedCreatureDrain(this, target, points); + master->onSummonTargetDrain(this, target, points); } void Monster::onCreatureAppear(const Creature* creature) @@ -139,9 +149,14 @@ void Monster::onCreatureAppear(const Creature* creature) Creature::onCreatureAppear(creature); if(creature == this) { + CreatureEventList spawnEvents = getCreatureEvents(CREATURE_EVENT_SPAWN_SINGLE); + for(CreatureEventList::iterator it = spawnEvents.begin(); it != spawnEvents.end(); ++it) + (*it)->executeSpawn(this); + + g_creatureEvents->monsterSpawn(this); //We just spawned lets look around to see who is there. if(isSummon()) - isMasterInRange = canSee(getMaster()->getPosition()); + isMasterInRange = canSee(master->getPosition()); updateTargetList(); updateIdleStatus(); @@ -171,7 +186,7 @@ void Monster::onCreatureMove(const Creature* creature, const Tile* newTile, cons if(creature == this) { if(isSummon()) - isMasterInRange = canSee(getMaster()->getPosition()); + isMasterInRange = canSee(master->getPosition()); updateTargetList(); updateIdleStatus(); @@ -184,7 +199,7 @@ void Monster::onCreatureMove(const Creature* creature, const Tile* newTile, cons else if(!canSeeNewPos && canSeeOldPos) onCreatureLeave(const_cast(creature)); - if(isSummon() && getMaster() == creature && canSeeNewPos) //Turn the summon on again + if(isSummon() && master == creature && canSeeNewPos) //Turn the summon on again isMasterInRange = true; updateIdleStatus(); @@ -272,7 +287,7 @@ void Monster::onCreatureFound(Creature* creature, bool pushFront /*= false*/) void Monster::onCreatureEnter(Creature* creature) { - if(getMaster() == creature) //Turn the summon on again + if(master == creature) //Turn the summon on again { isMasterInRange = true; updateIdleStatus(); @@ -283,31 +298,30 @@ void Monster::onCreatureEnter(Creature* creature) bool Monster::isFriend(const Creature* creature) { - if(!isSummon() || !getMaster()->getPlayer()) + if(!isSummon() || !master->getPlayer()) return creature->getMonster() && !creature->isSummon(); const Player* tmpPlayer = NULL; if(creature->getPlayer()) tmpPlayer = creature->getPlayer(); - else if(creature->getMaster() && creature->getMaster()->getPlayer()) - tmpPlayer = creature->getMaster()->getPlayer(); + else if(creature->getPlayerMaster()) + tmpPlayer = creature->getPlayerMaster(); - const Player* masterPlayer = getMaster()->getPlayer(); - return tmpPlayer && (tmpPlayer == getMaster() || masterPlayer->isPartner(tmpPlayer)); + const Player* masterPlayer = master->getPlayer(); + return tmpPlayer && (tmpPlayer == masterPlayer || masterPlayer->isPartner(tmpPlayer) || masterPlayer->isAlly(tmpPlayer)); } bool Monster::isOpponent(const Creature* creature) { - return (isSummon() && getMaster()->getPlayer() && creature != getMaster()) || ((creature->getPlayer() - && !creature->getPlayer()->hasFlag(PlayerFlag_IgnoredByMonsters)) || - (creature->getMaster() && creature->getMaster()->getPlayer())); + return (isSummon() && master->getPlayer() && creature != master) || ((creature->getPlayer() + && !creature->getPlayer()->hasFlag(PlayerFlag_IgnoredByMonsters)) || creature->getPlayerMaster()); } bool Monster::doTeleportToMaster() { const Position& tmp = getPosition(); if(g_game.internalTeleport(this, g_game.getClosestFreeTile(this, - getMaster()->getPosition(), true), false) != RET_NOERROR) + master->getPosition(), true), true) != RET_NOERROR) return false; g_game.addMagicEffect(tmp, MAGIC_EFFECT_POFF); @@ -318,11 +332,11 @@ bool Monster::doTeleportToMaster() void Monster::onCreatureLeave(Creature* creature) { #ifdef __DEBUG__ - std::cout << "onCreatureLeave - " << creature->getName() << std::endl; + std::clog << "onCreatureLeave - " << creature->getName() << std::endl; #endif - if(isSummon() && getMaster() == creature) + if(isSummon() && master == creature) { - if(!g_config.getBool(ConfigManager::TELEPORT_SUMMONS) && (!getMaster()->getPlayer() + if(!g_config.getBool(ConfigManager::TELEPORT_SUMMONS) && (!master->getPlayer() || !g_config.getBool(ConfigManager::TELEPORT_PLAYER_SUMMONS))) { //Turn the monster off until its master comes back @@ -344,7 +358,7 @@ void Monster::onCreatureLeave(Creature* creature) } #ifdef __DEBUG__ else - std::cout << "Monster: " << creature->getName() << " not found in the friendList." << std::endl; + std::clog << "Monster: " << creature->getName() << " not found in the friendList." << std::endl; #endif } @@ -361,7 +375,7 @@ void Monster::onCreatureLeave(Creature* creature) } #ifdef __DEBUG__ else - std::cout << "Player: " << creature->getName() << " not found in the targetList." << std::endl; + std::clog << "Player: " << creature->getName() << " not found in the targetList." << std::endl; #endif } } @@ -369,7 +383,7 @@ void Monster::onCreatureLeave(Creature* creature) bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAULT*/) { #ifdef __DEBUG__ - std::cout << "Searching target... " << std::endl; + std::clog << "Searching target... " << std::endl; #endif std::list resultList; @@ -399,7 +413,7 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL } if(target && selectTarget(target)) - return target; + return true; break; } @@ -411,7 +425,7 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL std::advance(it, random_range(0, resultList.size() - 1)); #ifdef __DEBUG__ - std::cout << "Selecting target " << (*it)->getName() << std::endl; + std::clog << "Selecting target " << (*it)->getName() << std::endl; #endif return selectTarget(*it); } @@ -431,7 +445,7 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL continue; #ifdef __DEBUG__ - std::cout << "Selecting target " << (*it)->getName() << std::endl; + /*std::clog << "Selecting target " << (*it)->getName() << std::endl;*/ // Caused a strange crash, will look at it later #endif return true; } @@ -460,7 +474,7 @@ void Monster::onFollowCreatureComplete(const Creature* creature) } BlockType_t Monster::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, - bool checkDefense/* = false*/, bool checkArmor/* = false*/) + bool checkDefense/* = false*/, bool checkArmor/* = false*/, bool/* reflect = true*/, bool/* field = false*/, bool/* element = false*/) { BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor); if(!damage) @@ -492,17 +506,14 @@ bool Monster::isTarget(Creature* creature) bool Monster::selectTarget(Creature* creature) { #ifdef __DEBUG__ - std::cout << "Selecting target... " << std::endl; + std::clog << "Selecting target... " << std::endl; #endif - if(!isTarget(creature)) - return false; - - CreatureList::iterator it = std::find(targetList.begin(), targetList.end(), creature); - if(it == targetList.end()) + if(!isTarget(creature) || std::find(targetList.begin(), + targetList.end(), creature) == targetList.end()) { //Target not found in our target list. #ifdef __DEBUG__ - std::cout << "Target not found in targetList." << std::endl; + std::clog << "Target not found in targetList." << std::endl; #endif return false; } @@ -538,7 +549,7 @@ void Monster::updateIdleStatus() { if(isSummon()) { - if((!isMasterInRange && !teleportToMaster) || (getMaster()->getMonster() && getMaster()->getMonster()->getIdleStatus())) + if((!isMasterInRange && !teleportToMaster) || (master->getMonster() && master->getMonster()->getIdleStatus())) idle = true; } else if(targetList.empty()) @@ -552,9 +563,7 @@ void Monster::onAddCondition(ConditionType_t type, bool hadCondition) { Creature::onAddCondition(type, hadCondition); //the walkCache need to be updated if the monster becomes "resistent" to the damage, see Tile::__queryAdd() - if(type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON) - updateMapCache(); - + updateMapCache(); updateIdleStatus(); } @@ -562,9 +571,7 @@ void Monster::onEndCondition(ConditionType_t type) { Creature::onEndCondition(type); //the walkCache need to be updated if the monster loose the "resistent" to the damage, see Tile::__queryAdd() - if(type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON) - updateMapCache(); - + updateMapCache(); updateIdleStatus(); } @@ -590,10 +597,10 @@ void Monster::onThink(uint32_t interval) { if(!attackedCreature) { - if(getMaster() && getMaster()->getAttackedCreature()) //This happens if the monster is summoned during combat - selectTarget(getMaster()->getAttackedCreature()); - else if(getMaster() != followCreature) //Our master has not ordered us to attack anything, lets follow him around instead. - setFollowCreature(getMaster()); + if(master && master->getAttackedCreature()) //This happens if the monster is summoned during combat + selectTarget(master->getAttackedCreature()); + else if(master != followCreature) //Our master has not ordered us to attack anything, lets follow him around instead. + setFollowCreature(master); } else if(attackedCreature == this) setFollowCreature(NULL); @@ -610,7 +617,12 @@ void Monster::onThink(uint32_t interval) onThinkTarget(interval); onThinkYell(interval); - onThinkDefense(interval); +} + +void Monster::onAttacking(uint32_t interval) +{ + Creature::onAttacking(interval); + doHealing(interval); } void Monster::doAttacking(uint32_t interval) @@ -618,14 +630,17 @@ void Monster::doAttacking(uint32_t interval) if(!attackedCreature || (isSummon() && attackedCreature == this)) return; - bool updateLook = true, outOfRange = true; - resetTicks = interval; + bool updateLook = true; + resetTicks = (interval != 0); attackTicks += interval; const Position& myPos = getPosition(); - const Position& targetPos = attackedCreature->getPosition(); for(SpellList::iterator it = mType->spellAttackList.begin(); it != mType->spellAttackList.end(); ++it) { + if(!attackedCreature || attackedCreature->isRemoved()) + break; + + const Position& targetPos = attackedCreature->getPosition(); if(it->isMelee && isFleeing()) continue; @@ -653,16 +668,15 @@ void Monster::doAttacking(uint32_t interval) if(it->isMelee) extraMeleeAttack = false; #ifdef __DEBUG__ + static uint64_t prevTicks = OTSYS_TIME(); - std::cout << "doAttacking ticks: " << OTSYS_TIME() - prevTicks << std::endl; + std::clog << "doAttacking ticks: " << OTSYS_TIME() - prevTicks << std::endl; prevTicks = OTSYS_TIME(); #endif } } - if(inRange) - outOfRange = false; - else if(it->isMelee) //melee swing out of reach + if(!inRange && it->isMelee) //melee swing out of reach extraMeleeAttack = true; } @@ -747,7 +761,7 @@ void Monster::onThinkTarget(uint32_t interval) searchTarget(TARGETSEARCH_NEAREST); } -void Monster::onThinkDefense(uint32_t interval) +void Monster::doHealing(uint32_t interval) { resetTicks = true; defenseTicks += interval; @@ -795,9 +809,8 @@ void Monster::onThinkDefense(uint32_t interval) uint32_t typeCount = 0; for(CreatureList::iterator cit = summons.begin(); cit != summons.end(); ++cit) { - if(!(*cit)->isRemoved() && (*cit)->getMonster() && - (*cit)->getMonster()->getName() == it->name) - typeCount++; + if(!(*cit)->isRemoved() && (*cit)->getName() == it->name) + ++typeCount; } if(typeCount >= it->amount) @@ -809,7 +822,10 @@ void Monster::onThinkDefense(uint32_t interval) { addSummon(summon); if(g_game.placeCreature(summon, getPosition())) + { g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_WRAPS_BLUE); + g_game.addMagicEffect(summon->getPosition(), MAGIC_EFFECT_TELEPORT); + } else removeSummon(summon); } @@ -837,15 +853,16 @@ void Monster::onThinkYell(uint32_t interval) const voiceBlock_t& vb = mType->voiceVector[random_range(0, mType->voiceVector.size() - 1)]; if(vb.yellText) - g_game.internalCreatureSay(this, SPEAK_MONSTER_YELL, vb.text, false); + g_game.internalCreatureSay(this, MSG_SPEAK_MONSTER_YELL, vb.text, false); else - g_game.internalCreatureSay(this, SPEAK_MONSTER_SAY, vb.text, false); + g_game.internalCreatureSay(this, MSG_SPEAK_MONSTER_SAY, vb.text, false); } bool Monster::pushItem(Item* item, int32_t radius) { const Position& centerPos = item->getPosition(); PairVector pairVector; + pairVector.push_back(PositionPair(-1, -1)); pairVector.push_back(PositionPair(-1, 0)); pairVector.push_back(PositionPair(-1, 1)); @@ -886,12 +903,12 @@ void Monster::pushItems(Tile* tile) //We cannot use iterators here since we can push the item to another tile //which will invalidate the iterator. //start from the end to minimize the amount of traffic - int32_t moveCount = 0, removeCount = 0, downItemsSize = tile->getDownItemCount(); + int32_t moveCount = 0, removeCount = 0, downItemsSize = items->getDownItemCount(); Item* item = NULL; for(int32_t i = downItemsSize - 1; i >= 0; --i) { assert(i >= 0 && i < downItemsSize); - if((item = items->at(i)) && item->hasProperty(MOVEABLE) && + if((item = items->at(i)) && item->hasProperty(MOVABLE) && (item->hasProperty(BLOCKPATH) || item->hasProperty(BLOCKSOLID))) { if(moveCount < 20 && pushItem(item, 1)) @@ -933,22 +950,20 @@ void Monster::pushCreatures(Tile* tile) if(!creatures) return; - bool effect = false; Monster* monster = NULL; - for(uint32_t i = 0; i < creatures->size();) - { - if((monster = creatures->at(i)->getMonster()) && monster->isPushable()) - { - if(pushCreature(monster)) - continue; + Creature* _master = NULL; - monster->setDropLoot(LOOT_DROP_NONE); - monster->changeHealth(-monster->getHealth()); - if(!effect) - effect = true; - } + bool effect = false; + for(uint32_t i = 0; i < creatures->size(); ++i) + { + if(!(monster = creatures->at(i)->getMonster()) || (monster->isPushable() && pushCreature(monster)) + || (!monster->isEliminable() && (!(_master = monster->getMaster()) || _master != this))) + continue; - ++i; + monster->setDropLoot(LOOT_DROP_NONE); + monster->changeHealth(-monster->getHealth()); + if(!effect) + effect = true; } if(effect) @@ -1000,7 +1015,7 @@ bool Monster::getNextStep(Direction& dir, uint32_t& flags) } #ifdef __DEBUG__ else - std::cout << "[Warning - Monster::getNextStep] no tile found." << std::endl; + std::clog << "[Warning - Monster::getNextStep] no tile found." << std::endl; #endif } @@ -1107,7 +1122,7 @@ bool Monster::isInSpawnRange(const Position& toPos) bool Monster::canWalkTo(Position pos, Direction dir) { - if(getNoMove()) + if(cannotMove) return false; switch(dir) @@ -1132,7 +1147,12 @@ bool Monster::canWalkTo(Position pos, Direction dir) return false; Tile* tile = g_game.getTile(pos); - if(!tile || getTile()->isSwimmingPool(false) != tile->isSwimmingPool(false)) // prevent monsters entering/exiting to swimming pool + if(!tile || g_game.isSwimmingPool(NULL, getTile(), false) != g_game.isSwimmingPool(NULL, tile, false)) // prevent monsters entering/exiting to swimming pool + return false; + + // If we don't follow, or attack, and we can't handle the damage, then we can't move on this field + MagicField* field = NULL; + if(!followCreature && !attackedCreature && (field = tile->getFieldItem()) && !isImmune(field->getCombatType())) return false; return !tile->getTopVisibleCreature(this) && tile->__queryAdd( @@ -1166,29 +1186,42 @@ Item* Monster::createCorpse(DeathList deathList) if(!corpse) return NULL; + if(master) + { + corpse->setAttribute("summon", true); + return corpse; + } + if(mType->corpseUnique) corpse->setUniqueId(mType->corpseUnique); if(mType->corpseAction) - corpse->setActionId(mType->corpseAction); + corpse->setActionId(mType->corpseAction, false); - DeathEntry ownerEntry = deathList[0]; - if(ownerEntry.isNameKill()) + if(deathList[0].isNameKill()) return corpse; - Creature* owner = ownerEntry.getKillerCreature(); - if(!owner) + Creature* _owner = deathList[0].getKillerCreature(); + if(deathList.size() > 1 && deathList[1].getDamage() > deathList[0].getDamage()) + _owner = deathList[1].getKillerCreature(); + + if(!_owner) return corpse; - uint32_t ownerId = 0; - if(owner->getPlayer()) - ownerId = owner->getID(); - else if(owner->getMaster() && owner->getPlayerMaster()) - ownerId = owner->getMaster()->getID(); + Player* owner = NULL; + if(_owner->getPlayer()) + owner = _owner->getPlayer(); + else if(_owner->isPlayerSummon()) + owner = _owner->getPlayerMaster(); - if(ownerId) - corpse->setCorpseOwner(ownerId); + if(!owner) + return corpse; + + uint64_t stamina = g_config.getNumber(ConfigManager::STAMINA_DESTROY_LOOT); + if(stamina && owner->getStamina() <= (stamina * STAMINA_MULTIPLIER)) + lootDrop = LOOT_DROP_NONE; + corpse->setCorpseOwner(owner->getGUID()); return corpse; } @@ -1293,6 +1326,54 @@ void Monster::dropLoot(Container* corpse) mType->dropLoot(corpse); } +bool Monster::isAttackable() const +{ + std::string value; + if(!getStorage("attackable", value)) + return mType->isAttackable; + + return booleanString(value); +} + +bool Monster::isHostile() const +{ + std::string value; + if(!getStorage("hostile", value)) + return mType->isHostile; + + return booleanString(value); +} + +bool Monster::isPushable() const +{ + if(baseSpeed < 1) + return false; + + std::string value; + if(!getStorage("pushable", value)) + return mType->pushable; + + return booleanString(value); +} + +bool Monster::isWalkable() const +{ + std::string value; + if(!getStorage("walkable", value)) + return mType->isWalkable; + + return booleanString(value); +} + +bool Monster::isFleeing() const +{ + std::string value; + if(!getStorage("fleeing", value)) + return getHealth() <= mType->runAwayHealth; + + return booleanString(value); +} + bool Monster::isImmune(CombatType_t type) const { ElementMap::const_iterator it = mType->elementMap.find(type); @@ -1302,7 +1383,7 @@ bool Monster::isImmune(CombatType_t type) const return it->second >= 100; } -void Monster::setNormalCreatureLight() +void Monster::resetLight() { internalLight.level = mType->lightLevel; internalLight.color = mType->lightColor; @@ -1319,6 +1400,12 @@ void Monster::changeHealth(int32_t healthChange) { //In case a player with ignore flag set attacks the monster setIdle(false); + if(!hasRecentBattle()) + { + lastDamage = OTSYS_TIME(); + updateMapCache(); + } + Creature::changeHealth(healthChange); } @@ -1338,16 +1425,12 @@ bool Monster::convinceCreature(Creature* creature) if(player && !player->hasFlag(PlayerFlag_CanConvinceAll) && !mType->isConvinceable) return false; - Creature* oldMaster = NULL; - if(isSummon()) - oldMaster = getMaster(); - - if(oldMaster) + if(master) { - if(oldMaster->getPlayer() || oldMaster == creature) + if(master == creature) return false; - oldMaster->removeSummon(this); + master->removeSummon(this); } setFollowCreature(NULL); @@ -1397,9 +1480,10 @@ void Monster::getPathSearchParams(const Creature* creature, FindPathParams& fpp) Creature::getPathSearchParams(creature, fpp); fpp.minTargetDist = 1; fpp.maxTargetDist = mType->targetDistance; + if(isSummon()) { - if(getMaster() == creature) + if(master == creature) { fpp.maxTargetDist = 2; fpp.fullPathSearch = true; diff --git a/monster.h b/monster.h index a25f177..f37b025 100644 --- a/monster.h +++ b/monster.h @@ -18,6 +18,7 @@ #ifndef __MONSTER__ #define __MONSTER__ +#include "otsystem.h" #include "monsters.h" #include "raids.h" #include "tile.h" @@ -51,8 +52,9 @@ class Monster : public Creature virtual Monster* getMonster() {return this;} virtual const Monster* getMonster() const {return this;} + virtual CreatureType_t getType() const {return CREATURETYPE_MONSTER;} - virtual uint32_t rangeId() {return 0x40000000;} + virtual uint32_t rangeId() {return MONSTER_ID_RANGE;} static AutoList autoList; void addList() {autoList[id] = this;} @@ -60,29 +62,31 @@ class Monster : public Creature virtual const std::string& getName() const {return mType->name;} virtual const std::string& getNameDescription() const {return mType->nameDescription;} - virtual std::string getDescription(int32_t lookDistance) const {return mType->nameDescription + ".";} + virtual std::string getDescription(int32_t) const {return mType->nameDescription + ".";} virtual RaceType_t getRace() const {return mType->race;} virtual int32_t getArmor() const {return mType->armor;} virtual int32_t getDefense() const {return mType->defense;} virtual MonsterType* getMonsterType() const {return mType;} - virtual bool isPushable() const {return mType->pushable && (baseSpeed > 0);} - virtual bool isAttackable() const {return mType->isAttackable;} + virtual bool isPushable() const; + virtual bool isAttackable() const; virtual bool isImmune(CombatType_t type) const; + bool isEliminable() const {return mType->eliminable;} bool canPushItems() const {return mType->canPushItems;} bool canPushCreatures() const {return mType->canPushCreatures;} - bool isHostile() const {return mType->isHostile;} - virtual bool isWalkable() const {return mType->isWalkable;} + bool isHostile() const; + virtual bool isWalkable() const; virtual bool canSeeInvisibility() const {return Creature::isImmune(CONDITION_INVISIBLE);} uint32_t getManaCost() const {return mType->manaCost;} + bool hasRecentBattle() const {return lastDamage && (uint64_t)OTSYS_TIME() < (lastDamage + 30000);} void setSpawn(Spawn* _spawn) {spawn = _spawn;} void setRaid(Raid* _raid) {raid = _raid;} - virtual void onAttackedCreature(Creature* target); - virtual void onAttackedCreatureDisappear(bool isLogout); - virtual void onAttackedCreatureDrain(Creature* target, int32_t points); + virtual void onTarget(Creature* target); + virtual void onTargetDisappear(bool isLogout); + virtual void onTargetDrain(Creature* target, int32_t points); virtual void onCreatureAppear(const Creature* creature); virtual void onCreatureDisappear(const Creature* creature, bool isLogout); @@ -99,9 +103,10 @@ class Monster : public Creature virtual bool challengeCreature(Creature* creature); virtual bool convinceCreature(Creature* creature); - virtual void setNormalCreatureLight(); + virtual void resetLight(); virtual bool getCombatValues(int32_t& min, int32_t& max); + virtual void onAttacking(uint32_t interval); virtual void doAttacking(uint32_t interval); virtual bool hasExtraSwing() {return extraMeleeAttack;} @@ -112,10 +117,12 @@ class Monster : public Creature const CreatureList& getFriendList() {return friendList;} bool isTarget(Creature* creature); - bool isFleeing() const {return getHealth() <= mType->runAwayHealth;} + bool getIdleStatus() const {return isIdle;} + bool isFleeing() const; + bool hasRaid() const {return raid != NULL;} virtual BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, - bool checkDefense = false, bool checkArmor = false); + bool checkDefense = false, bool checkArmor = false, bool reflect = true, bool field = false, bool element = false); private: CreatureList targetList; @@ -123,6 +130,7 @@ class Monster : public Creature MonsterType* mType; + int32_t healthMin; int32_t minCombatValue; int32_t maxCombatValue; uint32_t attackTicks; @@ -131,6 +139,7 @@ class Monster : public Creature uint32_t defenseTicks; uint32_t yellTicks; int32_t targetChangeCooldown; + uint64_t lastDamage; bool resetTicks; bool isIdle; bool extraMeleeAttack; @@ -159,7 +168,6 @@ class Monster : public Creature void setIdle(bool _idle); void updateIdleStatus(); - bool getIdleStatus() const {return isIdle;} virtual void onAddCondition(ConditionType_t type, bool hadCondition); virtual void onEndCondition(ConditionType_t type); @@ -181,7 +189,7 @@ class Monster : public Creature void onThinkTarget(uint32_t interval); void onThinkYell(uint32_t interval); - void onThinkDefense(uint32_t interval); + void doHealing(uint32_t interval); bool isFriend(const Creature* creature); bool isOpponent(const Creature* creature); diff --git a/monsters.cpp b/monsters.cpp index 9f6e94c..4ab4712 100644 --- a/monsters.cpp +++ b/monsters.cpp @@ -39,11 +39,11 @@ extern ConfigManager g_config; void MonsterType::reset() { - canPushItems = canPushCreatures = isSummonable = isIllusionable = isConvinceable = isLureable = isWalkable = hideName = hideHealth = false; + canPushItems = canPushCreatures = isSummonable = isIllusionable = isConvinceable = isLureable = isWalkable = hideName = hideHealth = eliminable = false; pushable = isAttackable = isHostile = true; - outfit.lookHead = outfit.lookBody = outfit.lookLegs = outfit.lookFeet = outfit.lookType = outfit.lookTypeEx = outfit.lookAddons = 0; - runAwayHealth = manaCost = lightLevel = lightColor = yellSpeedTicks = yellChance = changeTargetSpeed = changeTargetChance = 0; + outfit.lookHead = outfit.lookBody = outfit.lookLegs = outfit.lookFeet = outfit.lookType = outfit.lookTypeEx = outfit.lookAddons = outfit.lookMount = 0; + runAwayHealth = healthMin = manaCost = lightLevel = lightColor = yellSpeedTicks = yellChance = changeTargetSpeed = changeTargetChance = 0; experience = defense = armor = lookCorpse = corpseUnique = corpseAction = conditionImmunities = damageImmunities = 0; maxSummons = -1; @@ -55,6 +55,7 @@ void MonsterType::reset() race = RACE_BLOOD; skull = SKULL_NONE; partyShield = SHIELD_NONE; + guildEmblem = EMBLEM_NONE; lootMessage = LOOTMSG_IGNORE; for(SpellList::iterator it = spellAttackList.begin(); it != spellAttackList.end(); ++it) @@ -85,6 +86,76 @@ void MonsterType::reset() elementMap.clear(); } +ItemList MonsterType::createLoot(const LootBlock& lootBlock) +{ + uint16_t item = lootBlock.ids[0], random = Monsters::getLootRandom(), count = 0; + if(lootBlock.ids.size() > 1) + item = lootBlock.ids[random_range((size_t)0, lootBlock.ids.size() - 1)]; + + ItemList items; + if(random < lootBlock.chance) + count = random % lootBlock.count + 1; + + Item* tmpItem = NULL; + while(count > 0) + { + uint16_t n = 0; + if(Item::items[item].stackable) + n = std::min(count, (uint16_t)100); + + if(!(tmpItem = Item::CreateItem(item, n))) + break; + + count -= (!n ? 1 : n); + if(lootBlock.subType != -1) + tmpItem->setSubType(lootBlock.subType); + + if(lootBlock.actionId != -1) + tmpItem->setActionId(lootBlock.actionId, false); + + if(lootBlock.uniqueId != -1) + tmpItem->setUniqueId(lootBlock.uniqueId); + + if(!lootBlock.text.empty()) + tmpItem->setText(lootBlock.text); + + items.push_back(tmpItem); + } + + return items; +} + +bool MonsterType::createChildLoot(Container* parent, const LootBlock& lootBlock) +{ + LootItems::const_iterator it = lootBlock.childLoot.begin(); + if(it == lootBlock.childLoot.end()) + return true; + + ItemList items; + for(; it != lootBlock.childLoot.end() && !parent->full(); ++it) + { + items = createLoot(*it); + if(items.empty()) + continue; + + for(ItemList::iterator iit = items.begin(); iit != items.end(); ++iit) + { + Item* tmpItem = *iit; + if(Container* container = tmpItem->getContainer()) + { + if(createChildLoot(container, *it)) + parent->__internalAddThing(tmpItem); + else + delete container; + } + else + parent->__internalAddThing(tmpItem); + } + } + + return !parent->empty(); +} + uint16_t Monsters::getLootRandom() { return (uint16_t)std::ceil((double)random_range(0, MAX_LOOTCHANCE) / g_config.getDouble(ConfigManager::RATE_LOOT)); @@ -92,14 +163,19 @@ uint16_t Monsters::getLootRandom() void MonsterType::dropLoot(Container* corpse) { - Item* tmpItem = NULL; + ItemList items; for(LootItems::const_iterator it = lootItems.begin(); it != lootItems.end() && !corpse->full(); ++it) { - if((tmpItem = createLoot(*it))) + items = createLoot(*it); + if(items.empty()) + continue; + + for(ItemList::iterator iit = items.begin(); iit != items.end(); ++iit) { + Item* tmpItem = *iit; if(Container* container = tmpItem->getContainer()) { - if(createChildLoot(container, (*it))) + if(createChildLoot(container, *it)) corpse->__internalAddThing(tmpItem); else delete container; @@ -114,7 +190,7 @@ void MonsterType::dropLoot(Container* corpse) if(!ownerId) return; - Player* owner = g_game.getPlayerByID(ownerId); + Player* owner = g_game.getPlayerByGuid(ownerId); if(!owner) return; @@ -133,108 +209,42 @@ void MonsterType::dropLoot(Container* corpse) owner->sendTextMessage((MessageClasses)g_config.getNumber(ConfigManager::LOOT_MESSAGE_TYPE), ss.str()); } -Item* MonsterType::createLoot(const LootBlock& lootBlock) -{ - uint16_t item = lootBlock.ids[0], random = Monsters::getLootRandom(); - if(lootBlock.ids.size() > 1) - item = lootBlock.ids[random_range((size_t)0, lootBlock.ids.size() - 1)]; - - Item* tmpItem = NULL; - if(Item::items[item].stackable) - { - if(random < lootBlock.chance) - tmpItem = Item::CreateItem(item, (random % lootBlock.count + 1)); - } - else if(random < lootBlock.chance) - tmpItem = Item::CreateItem(item, 0); - - if(!tmpItem) - return NULL; - - if(lootBlock.subType != -1) - tmpItem->setSubType(lootBlock.subType); - - if(lootBlock.actionId != -1) - tmpItem->setActionId(lootBlock.actionId); - - if(lootBlock.uniqueId != -1) - tmpItem->setUniqueId(lootBlock.uniqueId); - - if(!lootBlock.text.empty()) - tmpItem->setText(lootBlock.text); - - return tmpItem; -} - -bool MonsterType::createChildLoot(Container* parent, const LootBlock& lootBlock) -{ - LootItems::const_iterator it = lootBlock.childLoot.begin(); - if(it == lootBlock.childLoot.end()) - return true; - - Item* tmpItem = NULL; - for(; it != lootBlock.childLoot.end() && !parent->full(); ++it) - { - if((tmpItem = createLoot(*it))) - { - if(Container* container = tmpItem->getContainer()) - { - if(createChildLoot(container, (*it))) - parent->__internalAddThing(container); - else - delete container; - } - else - parent->__internalAddThing(tmpItem); - } - } - - return !parent->empty(); -} - bool Monsters::loadFromXml(bool reloading /*= false*/) { loaded = false; xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_OTHER, "monster/monsters.xml").c_str()); if(!doc) { - std::cout << "[Warning - Monsters::loadFromXml] Cannot load monsters file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Monsters::loadFromXml] Cannot load monsters file." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr p, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"monsters")) { - std::cout << "[Error - Monsters::loadFromXml] Malformed monsters file." << std::endl; + std::clog << "[Error - Monsters::loadFromXml] Malformed monsters file." << std::endl; xmlFreeDoc(doc); return false; } - p = root->children; - while(p) + for(xmlNodePtr p = root->children; p; p = p->next) { if(p->type != XML_ELEMENT_NODE) - { - p = p->next; continue; - } if(xmlStrcmp(p->name, (const xmlChar*)"monster")) { - std::cout << "[Warning - Monsters::loadFromXml] Unknown node name (" << p->name << ")." << std::endl; - p = p->next; + std::clog << "[Warning - Monsters::loadFromXml] Unknown node name (" << p->name << ")." << std::endl; continue; } std::string file, name; - if(readXMLString(p, "file", file) && readXMLString(p, "name", name)) - { - file = getFilePath(FILE_TYPE_OTHER, "monster/" + file); - loadMonster(file, name, reloading); - } + if(!readXMLString(p, "file", file) || !readXMLString(p, "name", name)) + continue; - p = p->next; + file = getFilePath(FILE_TYPE_OTHER, "monster/" + file); + loadMonster(file, name, reloading); } xmlFreeDoc(doc); @@ -260,12 +270,12 @@ ConditionDamage* Monsters::getDamageCondition(ConditionType_t conditionType, bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::string& description) { - sb.chance = 100; - sb.speed = 2000; sb.range = sb.minCombatValue = sb.maxCombatValue = 0; sb.combatSpell = sb.isMelee = false; + sb.chance = 100; + sb.speed = 2000; - std::string name = "", scriptName = ""; + std::string name, scriptName; bool isScripted = false; if(readXMLString(node, "script", scriptName)) isScripted = true; @@ -290,8 +300,8 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st if(intValue < 0) intValue = 0; - if(intValue > Map::maxViewportX * 2) - intValue = Map::maxViewportX * 2; + if(intValue > Map::maxViewportX << 1) + intValue = Map::maxViewportX << 1; sb.range = intValue; } @@ -300,12 +310,11 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st sb.minCombatValue = intValue; if(readXMLInteger(node, "max", intValue)) - { sb.maxCombatValue = intValue; - //normalize values - if(std::abs(sb.minCombatValue) > std::abs(sb.maxCombatValue)) - std::swap(sb.minCombatValue, sb.maxCombatValue); - } + + //normalize values + if(std::abs(sb.minCombatValue) > std::abs(sb.maxCombatValue)) + std::swap(sb.minCombatValue, sb.maxCombatValue); if((sb.spell = g_spells->getSpellByName(name))) return true; @@ -371,7 +380,7 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st } std::string tmpName = asLowerCaseString(name); - if(tmpName == "melee") + if(tmpName == "melee" || tmpName == "distance") { int32_t attack = 0, skill = 0; if(readXMLInteger(node, "attack", attack) && readXMLInteger(node, "skill", skill)) @@ -380,32 +389,48 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st sb.maxCombatValue = -Weapons::getMaxMeleeDamage(skill, attack); } - uint32_t tickInterval = 10000; + uint32_t tickInterval = 2000; ConditionType_t conditionType = CONDITION_NONE; - if(readXMLInteger(node, "physical", intValue)) - conditionType = CONDITION_PHYSICAL; + if(readXMLInteger(node, "bleed", intValue) || readXMLInteger(node, "physical", intValue)) + { + conditionType = CONDITION_BLEEDING; + tickInterval = 5000; + } else if(readXMLInteger(node, "fire", intValue)) + { conditionType = CONDITION_FIRE; + tickInterval = 9000; + } else if(readXMLInteger(node, "energy", intValue)) + { conditionType = CONDITION_ENERGY; - else if(readXMLInteger(node, "earth", intValue)) + tickInterval = 10000; + } + else if(readXMLInteger(node, "poison", intValue) || readXMLInteger(node, "earth", intValue)) + { conditionType = CONDITION_POISON; - else if(readXMLInteger(node, "freeze", intValue)) + tickInterval = 5000; + } + else if(readXMLInteger(node, "freeze", intValue) || readXMLInteger(node, "ice", intValue)) + { conditionType = CONDITION_FREEZING; - else if(readXMLInteger(node, "dazzle", intValue)) + tickInterval = 8000; + } + else if(readXMLInteger(node, "dazzle", intValue) || readXMLInteger(node, "holy", intValue)) + { conditionType = CONDITION_DAZZLED; - else if(readXMLInteger(node, "curse", intValue)) + tickInterval = 10000; + } + else if(readXMLInteger(node, "curse", intValue) || readXMLInteger(node, "death", intValue)) + { conditionType = CONDITION_CURSED; + tickInterval = 4000; + } else if(readXMLInteger(node, "drown", intValue)) { conditionType = CONDITION_DROWN; tickInterval = 5000; } - else if(readXMLInteger(node, "poison", intValue)) - { - conditionType = CONDITION_POISON; - tickInterval = 5000; - } uint32_t damage = std::abs(intValue); if(readXMLInteger(node, "tick", intValue) && intValue > 0) @@ -430,6 +455,8 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_PHYSICALDAMAGE); combat->setParam(COMBATPARAM_BLOCKEDBYARMOR, 1); } + else if(tmpName == "bleed") + combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_PHYSICALDAMAGE); else if(tmpName == "drown") combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_DROWNDAMAGE); else if(tmpName == "fire") @@ -452,7 +479,7 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st { bool aggressive = false; if(readXMLInteger(node, "self", intValue)) - aggressive = intValue; + aggressive = (intValue != 0); combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_HEALING); combat->setParam(COMBATPARAM_AGGRESSIVE, aggressive); @@ -501,6 +528,9 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st if(readXMLInteger(tmpNode, "addons", intValue)) outfit.lookAddons = intValue; + if(readXMLInteger(tmpNode, "mount", intValue)) + outfit.lookMount = intValue; + outfits.push_back(outfit); } @@ -566,6 +596,9 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st if(readXMLInteger(tmpNode, "addons", intValue)) outfit.lookAddons = intValue; + if(readXMLInteger(tmpNode, "mount", intValue)) + outfit.lookMount = intValue; + outfits.push_back(outfit); } @@ -604,6 +637,9 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st if(readXMLInteger(node, "addons", intValue)) outfit.lookAddons = intValue; + if(readXMLInteger(node, "mount", intValue)) + outfit.lookMount = intValue; + outfits.push_back(outfit); } @@ -629,7 +665,7 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st bool aggressive = false; if(readXMLInteger(node, "self", intValue)) - aggressive = intValue; + aggressive = (intValue != 0); if(ConditionOutfit* condition = dynamic_cast(Condition::createCondition( CONDITIONID_COMBAT, CONDITION_OUTFIT, duration))) @@ -648,7 +684,7 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st bool aggressive = false; if(readXMLInteger(node, "self", intValue)) - aggressive = intValue; + aggressive = (intValue != 0); if(Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_INVISIBLE, duration)) { @@ -658,11 +694,14 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st } else if(tmpName == "drunk") { - int32_t duration = 10000; + int32_t duration = 10000, subId = 0; if(readXMLInteger(node, "duration", intValue)) duration = intValue; - if(Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_DRUNK, duration)) + if(readXMLInteger(node, "subid", intValue)) + subId = intValue; + + if(Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_DRUNK, duration, 0, false, subId)) combat->setCondition(condition); } else if(tmpName == "skills" || tmpName == "attributes") @@ -728,7 +767,7 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st if(param != CONDITIONPARAM_BUFF) { if(ConditionAttributes* condition = dynamic_cast(Condition::createCondition( - CONDITIONID_COMBAT, CONDITION_ATTRIBUTES, duration, false, subId))) + CONDITIONID_COMBAT, CONDITION_ATTRIBUTES, duration, 0, false, subId))) { condition->setParam(param, intValue); combat->setCondition(condition); @@ -736,48 +775,52 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st } } else if(tmpName == "firefield") - combat->setParam(COMBATPARAM_CREATEITEM, 1492); + combat->setParam(COMBATPARAM_CREATEITEM, ITEM_FIREFIELD); else if(tmpName == "poisonfield") - combat->setParam(COMBATPARAM_CREATEITEM, 1496); + combat->setParam(COMBATPARAM_CREATEITEM, ITEM_POISONFIELD); else if(tmpName == "energyfield") - combat->setParam(COMBATPARAM_CREATEITEM, 1495); - else if(tmpName == "firecondition" || tmpName == "energycondition" || tmpName == "drowncondition" || - tmpName == "poisoncondition" || tmpName == "earthcondition" || tmpName == "freezecondition" || - tmpName == "cursecondition" || tmpName == "dazzlecondition") + combat->setParam(COMBATPARAM_CREATEITEM, ITEM_ENERGYFIELD); + else if(tmpName == "physicalcondition" || tmpName == "bleedcondition" || + tmpName == "firecondition" || tmpName == "energycondition" || + tmpName == "earthcondition" || tmpName == "poisoncondition" || + tmpName == "icecondition" || tmpName == "freezecondition" || + tmpName == "deathcondition" || tmpName == "cursecondition" || + tmpName == "holycondition" || tmpName == "dazzlecondition" || + tmpName == "drowncondition") { ConditionType_t conditionType = CONDITION_NONE; uint32_t tickInterval = 2000; - if(tmpName == "physicalcondition") + if(tmpName == "physicalcondition" || tmpName == "bleedcondition") { - conditionType = CONDITION_PHYSICAL; + conditionType = CONDITION_BLEEDING; tickInterval = 5000; } else if(tmpName == "firecondition") { conditionType = CONDITION_FIRE; - tickInterval = 10000; + tickInterval = 9000; } else if(tmpName == "energycondition") { conditionType = CONDITION_ENERGY; tickInterval = 10000; } - else if(tmpName == "earthcondition") + else if(tmpName == "earthcondition" || tmpName == "poisoncondition") { conditionType = CONDITION_POISON; - tickInterval = 10000; + tickInterval = 5000; } - else if(tmpName == "freezecondition") + else if(tmpName == "icecondition" || tmpName == "freezecondition") { conditionType = CONDITION_FREEZING; - tickInterval = 10000; + tickInterval = 8000; } - else if(tmpName == "cursecondition") + else if(tmpName == "deathcondition" || tmpName == "cursecondition") { conditionType = CONDITION_CURSED; - tickInterval = 10000; + tickInterval = 4000; } - else if(tmpName == "dazzlecondition") + else if(tmpName == "holycondition" || tmpName == "dazzlecondition") { conditionType = CONDITION_DAZZLED; tickInterval = 10000; @@ -787,11 +830,6 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st conditionType = CONDITION_DROWN; tickInterval = 5000; } - else if(tmpName == "poisoncondition") - { - conditionType = CONDITION_POISON; - tickInterval = 5000; - } if(readXMLInteger(node, "tick", intValue) && intValue > 0) tickInterval = intValue; @@ -812,15 +850,14 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st else { delete combat; - std::cout << "[Error - Monsters::deserializeSpell] " << description << " - Unknown spell name: " << name << std::endl; + std::clog << "[Error - Monsters::deserializeSpell] " << description << " - Unknown spell name: " << name << std::endl; return false; } combat->setPlayerCombatValues(FORMULA_VALUE, sb.minCombatValue, 0, sb.maxCombatValue, 0, 0, 0, 0, 0, 0, 0); combatSpell = new CombatSpell(combat, needTarget, needDirection); - xmlNodePtr attributeNode = node->children; - while(attributeNode) + for(xmlNodePtr attributeNode = node->children; attributeNode; attributeNode = attributeNode->next) { if(!xmlStrcmp(attributeNode->name, (const xmlChar*)"attribute")) { @@ -835,7 +872,7 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st if(shoot != SHOOT_EFFECT_UNKNOWN) combat->setParam(COMBATPARAM_DISTANCEEFFECT, shoot); else - std::cout << "[Warning - Monsters::deserializeSpell] " << description << " - Unknown shootEffect: " << strValue << std::endl; + std::clog << "[Warning - Monsters::deserializeSpell] " << description << " - Unknown shootEffect: " << strValue << std::endl; } } else if(tmpStrValue == "areaeffect") @@ -846,14 +883,13 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st if(effect != MAGIC_EFFECT_UNKNOWN) combat->setParam(COMBATPARAM_EFFECT, effect); else - std::cout << "[Warning - Monsters::deserializeSpell] " << description << " - Unknown areaEffect: " << strValue << std::endl; + std::clog << "[Warning - Monsters::deserializeSpell] " << description << " - Unknown areaEffect: " << strValue << std::endl; } } else - std::cout << "[Warning - Monsters::deserializeSpells] Effect type \"" << strValue << "\" does not exist." << std::endl; + std::clog << "[Warning - Monsters::deserializeSpells] Effect type \"" << strValue << "\" does not exist." << std::endl; } } - attributeNode = attributeNode->next; } } @@ -861,14 +897,14 @@ bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::st return true; } -#define SHOW_XML_WARNING(desc) std::cout << "[Warning - Monsters::loadMonster] " << desc << ". (" << file << ")" << std::endl; -#define SHOW_XML_ERROR(desc) std::cout << "[Error - Monsters::loadMonster] " << desc << ". (" << file << ")" << std::endl; +#define SHOW_XML_WARNING(desc) std::clog << "[Warning - Monsters::loadMonster] " << desc << " (" << file << ")." << std::endl; +#define SHOW_XML_ERROR(desc) std::clog << "[Error - Monsters::loadMonster] " << desc << " (" << file << ")." << std::endl; bool Monsters::loadMonster(const std::string& file, const std::string& monsterName, bool reloading/* = false*/) { if(getIdByName(monsterName) && !reloading) { - std::cout << "[Warning - Monsters::loadMonster] Duplicate registered monster with name: " << monsterName << std::endl; + std::clog << "[Warning - Monsters::loadMonster] Duplicate registered monster with name: " << monsterName << std::endl; return true; } @@ -894,16 +930,16 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa xmlDocPtr doc = xmlParseFile(file.c_str()); if(!doc) { - std::cout << "[Warning - Monsters::loadMonster] Cannot load monster (" << monsterName << ") file (" << file << ")." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Monsters::loadMonster] Cannot load monster (" << monsterName << ") file (" << file << ")." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } monsterLoad = true; - xmlNodePtr p, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"monster")) { - std::cout << "[Error - Monsters::loadMonster] Malformed monster (" << monsterName << ") file (" << file << ")." << std::endl; + std::clog << "[Error - Monsters::loadMonster] Malformed monster (" << monsterName << ") file (" << file << ")." << std::endl; xmlFreeDoc(doc); return false; } @@ -915,6 +951,7 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa else monsterLoad = false; + mType->file = file; if(readXMLString(root, "nameDescription", strValue)) mType->nameDescription = strValue; else @@ -926,18 +963,26 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa if(readXMLString(root, "race", strValue)) { std::string tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "venom" || atoi(strValue.c_str()) == 1) + if(tmpStrValue == "venom") mType->race = RACE_VENOM; - else if(tmpStrValue == "blood" || atoi(strValue.c_str()) == 2) + else if(tmpStrValue == "blood") mType->race = RACE_BLOOD; - else if(tmpStrValue == "undead" || atoi(strValue.c_str()) == 3) + else if(tmpStrValue == "undead") mType->race = RACE_UNDEAD; - else if(tmpStrValue == "fire" || atoi(strValue.c_str()) == 4) + else if(tmpStrValue == "fire") mType->race = RACE_FIRE; - else if(tmpStrValue == "energy" || atoi(strValue.c_str()) == 5) + else if(tmpStrValue == "energy") mType->race = RACE_ENERGY; else - SHOW_XML_WARNING("Unknown race type " << strValue); + { + int32_t race = atoi(strValue.c_str()); + if(race < RACE_VENOM || race > RACE_ENERGY) + { + SHOW_XML_WARNING("Unknown race type " << strValue); + } + else + mType->race = (RaceType_t)race; + } } if(readXMLInteger(root, "experience", intValue)) @@ -950,44 +995,42 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa mType->manaCost = intValue; if(readXMLString(root, "skull", strValue)) - mType->skull = getSkull(strValue); + mType->skull = getSkulls(strValue); if(readXMLString(root, "shield", strValue)) - mType->partyShield = getPartyShield(strValue); + mType->partyShield = getShields(strValue); + + if(readXMLString(root, "emblem", strValue)) + mType->guildEmblem = getEmblems(strValue); - p = root->children; - while(p && monsterLoad) + for(xmlNodePtr p = root->children; p; p = p->next) { if(p->type != XML_ELEMENT_NODE) - { - p = p->next; continue; - } if(!xmlStrcmp(p->name, (const xmlChar*)"health")) { - if(readXMLInteger(p, "now", intValue)) - mType->health = intValue; - else - { - SHOW_XML_ERROR("Missing health.now"); - monsterLoad = false; - } + if(readXMLInteger(p, "min", intValue)) + mType->healthMin = intValue; - if(readXMLInteger(p, "max", intValue)) - mType->healthMax = intValue; - else + if(!readXMLInteger(p, "max", intValue)) { SHOW_XML_ERROR("Missing health.max"); monsterLoad = false; + break; } + + mType->healthMax = intValue; + if(!readXMLInteger(p, "now", intValue)) + mType->health = mType->healthMax; + else + mType->health = intValue; } else if(!xmlStrcmp(p->name, (const xmlChar*)"flags")) { - xmlNodePtr tmpNode = p->children; - while(tmpNode) + for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { - if(xmlStrcmp(tmpNode->name, (const xmlChar*)"flag") == 0) + if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"flag")) { if(readXMLString(tmpNode, "summonable", strValue)) mType->isSummonable = booleanString(strValue); @@ -1057,13 +1100,17 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa mType->isWalkable = booleanString(strValue); if(readXMLString(tmpNode, "skull", strValue)) - mType->skull = getSkull(strValue); + mType->skull = getSkulls(strValue); if(readXMLString(tmpNode, "shield", strValue)) - mType->partyShield = getPartyShield(strValue); - } + mType->partyShield = getShields(strValue); + + if(readXMLString(tmpNode, "emblem", strValue)) + mType->guildEmblem = getEmblems(strValue); - tmpNode = tmpNode->next; + if(readXMLString(tmpNode, "eliminable", strValue)) + mType->eliminable = booleanString(strValue); + } } //if a monster can push creatures, it should not be pushable @@ -1109,6 +1156,9 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa if(readXMLInteger(p, "addons", intValue)) mType->outfit.lookAddons = intValue; + + if(readXMLInteger(p, "mount", intValue)) + mType->outfit.lookMount = intValue; } else if(readXMLInteger(p, "typeex", intValue)) mType->outfit.lookTypeEx = intValue; @@ -1126,8 +1176,7 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa } else if(!xmlStrcmp(p->name, (const xmlChar*)"attacks")) { - xmlNodePtr tmpNode = p->children; - while(tmpNode) + for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"attack")) { @@ -1137,8 +1186,6 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa else SHOW_XML_WARNING("Cant load spell"); } - - tmpNode = tmpNode->next; } } else if(!xmlStrcmp(p->name, (const xmlChar*)"defenses")) @@ -1149,8 +1196,7 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa if(readXMLInteger(p, "armor", intValue)) mType->armor = intValue; - xmlNodePtr tmpNode = p->children; - while(tmpNode) + for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"defense")) { @@ -1160,14 +1206,11 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa else SHOW_XML_WARNING("Cant load spell"); } - - tmpNode = tmpNode->next; } } else if(!xmlStrcmp(p->name, (const xmlChar*)"immunities")) { - xmlNodePtr tmpNode = p->children; - while(tmpNode) + for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"immunity")) { @@ -1177,7 +1220,7 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa if(tmpStrValue == "physical") { mType->damageImmunities |= COMBAT_PHYSICALDAMAGE; - mType->conditionImmunities |= CONDITION_PHYSICAL; + mType->conditionImmunities |= CONDITION_BLEEDING; } else if(tmpStrValue == "energy") { @@ -1226,13 +1269,15 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa mType->conditionImmunities |= CONDITION_DRUNK; else if(tmpStrValue == "invisible") mType->conditionImmunities |= CONDITION_INVISIBLE; + else if(tmpStrValue == "bleed") + mType->conditionImmunities |= CONDITION_BLEEDING; else SHOW_XML_WARNING("Unknown immunity name " << strValue); } else if(readXMLString(tmpNode, "physical", strValue) && booleanString(strValue)) { mType->damageImmunities |= COMBAT_PHYSICALDAMAGE; - //mType->conditionImmunities |= CONDITION_PHYSICAL; + mType->conditionImmunities |= CONDITION_BLEEDING; } else if(readXMLString(tmpNode, "energy", strValue) && booleanString(strValue)) { @@ -1282,9 +1327,9 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa mType->conditionImmunities |= CONDITION_DRUNK; else if(readXMLString(tmpNode, "invisible", strValue) && booleanString(strValue)) mType->conditionImmunities |= CONDITION_INVISIBLE; + else if(readXMLString(tmpNode, "bleed", strValue) && booleanString(strValue)) + mType->conditionImmunities |= CONDITION_BLEEDING; } - - tmpNode = tmpNode->next; } } else if(!xmlStrcmp(p->name, (const xmlChar*)"voices")) @@ -1299,8 +1344,7 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa else SHOW_XML_WARNING("Missing voices.chance"); - xmlNodePtr tmpNode = p->children; - while(tmpNode) + for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"voice")) { @@ -1318,34 +1362,25 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa mType->voiceVector.push_back(vb); } - - tmpNode = tmpNode->next; } } else if(!xmlStrcmp(p->name, (const xmlChar*)"loot")) { - xmlNodePtr tmpNode = p->children; - while(tmpNode) + for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { if(tmpNode->type != XML_ELEMENT_NODE) - { - tmpNode = tmpNode->next; continue; - } LootBlock rootBlock; if(loadLoot(tmpNode, rootBlock)) mType->lootItems.push_back(rootBlock); else SHOW_XML_WARNING("Cant load loot"); - - tmpNode = tmpNode->next; } } else if(!xmlStrcmp(p->name, (const xmlChar*)"elements")) { - xmlNodePtr tmpNode = p->children; - while(tmpNode) + for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"element")) { @@ -1374,8 +1409,6 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa else if(readXMLInteger(tmpNode, "undefinedPercent", intValue)) mType->elementMap[COMBAT_UNDEFINEDDAMAGE] = intValue; } - - tmpNode = tmpNode->next; } } else if(!xmlStrcmp(p->name, (const xmlChar*)"summons")) @@ -1383,8 +1416,7 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa if(readXMLInteger(p, "maxSummons", intValue) || readXMLInteger(p, "max", intValue)) mType->maxSummons = intValue; - xmlNodePtr tmpNode = p->children; - while(tmpNode) + for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"summon")) { @@ -1411,14 +1443,11 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa else SHOW_XML_WARNING("Missing summon.name"); } - - tmpNode = tmpNode->next; } } else if(!xmlStrcmp(p->name, (const xmlChar*)"script")) { - xmlNodePtr tmpNode = p->children; - while(tmpNode) + for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"event")) { @@ -1427,34 +1456,29 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa else SHOW_XML_WARNING("Missing name for script event"); } - - tmpNode = tmpNode->next; } } else SHOW_XML_WARNING("Unknown attribute type - " << p->name); - - p = p->next; } xmlFreeDoc(doc); - if(monsterLoad) + if(!monsterLoad) { - static uint32_t id = 0; if(new_mType) - { - id++; - monsterNames[asLowerCaseString(monsterName)] = id; - monsters[id] = mType; - } + delete mType; - return true; + return false; } + static uint32_t id = 0; if(new_mType) - delete mType; + { + monsterNames[asLowerCaseString(monsterName)] = ++id; + monsters[id] = mType; + } - return false; + return true; } bool Monsters::loadLoot(xmlNodePtr node, LootBlock& lootBlock) @@ -1491,12 +1515,12 @@ bool Monsters::loadLoot(xmlNodePtr node, LootBlock& lootBlock) int32_t intValue; if(readXMLInteger(node, "count", intValue) || readXMLInteger(node, "countmax", intValue)) - lootBlock.count = std::max(1, std::min(100, intValue)); + lootBlock.count = std::max((int32_t)1, intValue); else lootBlock.count = 1; if(readXMLInteger(node, "chance", intValue) || readXMLInteger(node, "chance1", intValue)) - lootBlock.chance = std::min(MAX_LOOTCHANCE, intValue); + lootBlock.chance = std::min(MAX_LOOTCHANCE, std::abs(intValue)); else lootBlock.chance = MAX_LOOTCHANCE; @@ -1522,30 +1546,23 @@ bool Monsters::loadChildLoot(xmlNodePtr node, LootBlock& parentBlock) if(!node) return false; - xmlNodePtr p = node->children, insideNode; - while(p) + for(xmlNodePtr p = node->children; p; p = p->next) { - if(!xmlStrcmp(p->name, (const xmlChar*)"inside")) + if(xmlStrcmp(p->name, (const xmlChar*)"inside")) { - insideNode = p->children; - while(insideNode) - { - LootBlock childBlock; - if(loadLoot(insideNode, childBlock)) - parentBlock.childLoot.push_back(childBlock); + LootBlock childBlock; + if(loadLoot(p, childBlock)) + parentBlock.childLoot.push_back(childBlock); - insideNode = insideNode->next; - } - - p = p->next; continue; } - LootBlock childBlock; - if(loadLoot(p, childBlock)) - parentBlock.childLoot.push_back(childBlock); - - p = p->next; + for(xmlNodePtr insideNode = p->children; insideNode; insideNode = insideNode->next) + { + LootBlock childBlock; + if(loadLoot(insideNode, childBlock)) + parentBlock.childLoot.push_back(childBlock); + } } return true; @@ -1582,6 +1599,8 @@ uint32_t Monsters::getIdByName(const std::string& name) Monsters::~Monsters() { loaded = false; - for(MonsterMap::iterator it = monsters.begin(); it != monsters.end(); it++) + for(MonsterMap::iterator it = monsters.begin(); it != monsters.end(); ++it) delete it->second; + + monsters.clear(); } diff --git a/monsters.h b/monsters.h index 5a397f7..4a8ad7d 100644 --- a/monsters.h +++ b/monsters.h @@ -46,7 +46,8 @@ struct LootBlock LootBlock() { - count = chance = 0; + count = 1; + chance = 0; subType = actionId = uniqueId = -1; } }; @@ -87,25 +88,26 @@ class MonsterType void reset(); void dropLoot(Container* corpse); - Item* createLoot(const LootBlock& lootBlock); + ItemList createLoot(const LootBlock& lootBlock); bool createChildLoot(Container* parent, const LootBlock& lootBlock); bool isSummonable, isIllusionable, isConvinceable, isAttackable, isHostile, isLureable, - isWalkable, canPushItems, canPushCreatures, pushable, hideName, hideHealth; + isWalkable, canPushItems, canPushCreatures, pushable, hideName, hideHealth, eliminable; Outfit_t outfit; RaceType_t race; Skulls_t skull; PartyShields_t partyShield; + GuildEmblems_t guildEmblem; LootMessage_t lootMessage; - int32_t defense, armor, health, healthMax, baseSpeed, lookCorpse, corpseUnique, corpseAction, + int32_t defense, armor, health, healthMin, healthMax, baseSpeed, lookCorpse, corpseUnique, corpseAction, maxSummons, targetDistance, runAwayHealth, conditionImmunities, damageImmunities, lightLevel, lightColor, changeTargetSpeed, changeTargetChance; uint32_t yellChance, yellSpeedTicks, staticAttackChance, manaCost; uint64_t experience; - std::string name, nameDescription; + std::string name, nameDescription, file; SummonList summonList; LootItems lootItems; diff --git a/mounts.cpp b/mounts.cpp new file mode 100644 index 0000000..75c1769 --- /dev/null +++ b/mounts.cpp @@ -0,0 +1,204 @@ +//////////////////////////////////////////////////////////////////////// +// OpenTibia - an opensource roleplaying game +//////////////////////////////////////////////////////////////////////// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +//////////////////////////////////////////////////////////////////////// + +#include "otpch.h" +#include "mounts.h" + +bool Mount::isTamed(Player* player) const +{ + if(!player) + return false; + + if(player->hasCustomFlag(PlayerCustomFlag_CanUseAllMounts)) + return true; + + if(premium && !player->isPremium()) + return false; + + uint8_t tmpId = id - 1; + std::string value; + + int32_t key = PSTRG_MOUNTS_RANGE_START + (tmpId / 31); + if(player->getStorage(asString(key), value)) + { + int32_t tmp = (int32_t)std::pow(2., tmpId % 31); + return (tmp & atoi(value.c_str())) == tmp; + } + + if(storageId.empty()) + return false; + + player->getStorage(storageId, value); + if(value == storageValue) + return true; + + int32_t intValue = atoi(value.c_str()); + if(!intValue && value != "0") + return false; + + int32_t tmp = atoi(storageValue.c_str()); + if(!tmp && storageValue != "0") + return false; + + return intValue >= tmp; +} + +void Mounts::clear() +{ + for(MountList::iterator it = mounts.begin(); it != mounts.end(); ++it) + delete *it; + + mounts.clear(); +} + +bool Mounts::reload() +{ + clear(); + return loadFromXml(); +} + +bool Mounts::loadFromXml() +{ + xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "mounts.xml").c_str()); + if(!doc) + { + std::clog << "[Warning - Mounts::loadFromXml] Cannot load mounts file." << std::endl; + std::clog << getLastXMLError() << std::endl; + return false; + } + + xmlNodePtr root = xmlDocGetRootElement(doc); + if(xmlStrcmp(root->name, (const xmlChar*)"mounts")) + { + std::clog << "[Error - Mounts::loadFromXml] Malformed mounts file." << std::endl; + xmlFreeDoc(doc); + return false; + } + + for(xmlNodePtr p = root->children; p; p = p->next) + parseMountNode(p); + + xmlFreeDoc(doc); + return true; +} + +bool Mounts::parseMountNode(xmlNodePtr p) +{ + if(xmlStrcmp(p->name, (const xmlChar*)"mount")) + return false; + + int32_t intValue; + std::string strValue; + + uint8_t mountId = 0; + if(readXMLInteger(p, "id", intValue)) + mountId = intValue; + + std::string name; + if(readXMLString(p, "name", strValue)) + name = strValue; + + uint16_t clientId = 0; + if(readXMLInteger(p, "clientid", intValue) || readXMLInteger(p, "clientId", intValue) || readXMLInteger(p, "cid", intValue)) + clientId = intValue; + + int32_t speed = 0; + if(readXMLInteger(p, "speed", intValue)) + speed = intValue; + + int32_t attackSpeed = 0; + if(readXMLInteger(p, "attackspeed", intValue) || readXMLInteger(p, "attackSpeed", intValue)) + attackSpeed = intValue; + + bool premium = true; + if(readXMLString(p, "premium", strValue)) + premium = booleanString(strValue); + + std::string storageId, storageValue; + if(readXMLString(p, "quest", strValue)) + { + storageId = strValue; + storageValue = "1"; + } + else + { + if(readXMLString(p, "storageId", strValue)) + storageId = strValue; + + if(readXMLString(p, "storageValue", strValue)) + storageValue = strValue; + } + + if(!mountId) + { + std::clog << "[Error - Mounts::parseMountNode] Entry without mountId" << std::endl; + return false; + } + + if(!clientId) + { + std::clog << "[Error - Mounts::parseMountNode] Entry without clientId" << std::endl; + return false; + } + + Mount* mount = new Mount(name, mountId, clientId, + speed, attackSpeed, premium, storageId, storageValue); + if(!mount) + return false; + + mounts.push_back(mount); + return true; +} + +Mount* Mounts::getMountById(uint16_t id) const +{ + if(!id) + return NULL; + + for(MountList::const_iterator it = mounts.begin(); it != mounts.end(); ++it) + { + if((*it)->getId() == id) + return (*it); + } + + return NULL; +} + +Mount* Mounts::getMountByCid(uint16_t id) const +{ + if(!id) + return NULL; + + for(MountList::const_iterator it = mounts.begin(); it != mounts.end(); ++it) + { + if((*it)->getClientId() == id) + return (*it); + } + + return NULL; +} + +bool Mounts::isPremium() const +{ + for(MountList::const_iterator it = mounts.begin(); it != mounts.end(); ++it) + { + if(!(*it)->isPremium()) + return false; + } + + return true; +} \ No newline at end of file diff --git a/mounts.h b/mounts.h new file mode 100644 index 0000000..d7ecdcd --- /dev/null +++ b/mounts.h @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////////////////// +// OpenTibia - an opensource roleplaying game +//////////////////////////////////////////////////////////////////////// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +//////////////////////////////////////////////////////////////////////// + +#ifndef __MOUNTS__ +#define __MOUNTS__ +#include "otsystem.h" + +#include "networkmessage.h" +#include "player.h" + +class Mount +{ + public: + Mount(std::string _name, uint16_t _id, uint16_t _clientId, int32_t _speed, int32_t _attackSpeed, + bool _premium, std::string _storageId, std::string _storageValue) + { + name = _name; + storageId = _storageId; + storageValue = _storageValue; + + speed = _speed; + attackSpeed = _attackSpeed; + + clientId = _clientId; + id = _id; + premium = _premium; + } + + bool isTamed(Player* player) const; + + uint16_t getId() const {return id;} + uint16_t getClientId() const {return clientId;} + + const std::string& getName() const {return name;} + bool isPremium() const {return premium;} + + uint32_t getSpeed() const {return speed;} + int32_t getAttackSpeed() const {return attackSpeed;} + + private: + std::string name, storageId, storageValue; + int32_t speed, attackSpeed; + uint16_t clientId; + uint8_t id; + bool premium; +}; + +typedef std::list MountList; +class Mounts +{ + public: + virtual ~Mounts() {clear();} + static Mounts* getInstance() + { + static Mounts instance; + return &instance; + } + + void clear(); + bool reload(); + + bool loadFromXml(); + bool parseMountNode(xmlNodePtr p); + + inline MountList::const_iterator getFirstMount() const {return mounts.begin();} + inline MountList::const_iterator getLastMount() const {return mounts.end();} + + Mount* getMountById(uint16_t id) const; + Mount* getMountByCid(uint16_t id) const; + + uint8_t getMountCount() const {return (uint8_t)mounts.size();} + bool isPremium() const; + + private: + MountList mounts; +}; +#endif diff --git a/movement.cpp b/movement.cpp index fc1fe83..070d428 100644 --- a/movement.cpp +++ b/movement.cpp @@ -33,15 +33,100 @@ extern Game g_game; extern MoveEvents* g_moveEvents; -MoveEvents::MoveEvents(): - m_lastCacheTile(NULL), m_interface("MoveEvents Interface") +MoveEvent* MoveEventScript::event = NULL; + +void MoveEventScript::registerFunctions() { - m_interface.initState(); + LuaInterface::registerFunctions(); + lua_register(m_luaState, "callFunction", MoveEventScript::luaCallFunction); +} + +int32_t MoveEventScript::luaCallFunction(lua_State* L) +{ + //callFunction(...) + MoveEvent* event = MoveEventScript::event; + if(!event) + { + error(__FUNCTION__, "MoveEvent not set!"); + lua_pushboolean(L, false); + return 1; + } + + if(event->getEventType() == MOVE_EVENT_EQUIP || event->getEventType() == MOVE_EVENT_DE_EQUIP) + { + ScriptEnviroment* env = getEnv(); + bool boolean = popBoolean(L); + slots_t slot = (slots_t)popNumber(L); + + Item* item = env->getItemByUID(popNumber(L)); + if(!item) + { + error(__FUNCTION__, getError(LUA_ERROR_ITEM_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) + { + error(__FUNCTION__, getError(LUA_ERROR_PLAYER_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + if(event->getEventType() != MOVE_EVENT_EQUIP) + lua_pushboolean(L, MoveEvent::DeEquipItem(event, player, item, slot, boolean)); + else + lua_pushboolean(L, MoveEvent::EquipItem(event, player, item, slot, boolean)); + + return 1; + } + else if(event->getEventType() == MOVE_EVENT_STEP_IN) + { + ScriptEnviroment* env = getEnv(); + Item* item = env->getItemByUID(popNumber(L)); + if(!item) + { + error(__FUNCTION__, getError(LUA_ERROR_ITEM_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + Creature* creature = env->getCreatureByUID(popNumber(L)); + if(!creature) + { + error(__FUNCTION__, getError(LUA_ERROR_CREATURE_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + lua_pushboolean(L, MoveEvent::StepInField(creature, item)); + return 1; + } + else if(event->getEventType() == MOVE_EVENT_ADD_ITEM) + { + ScriptEnviroment* env = getEnv(); + Item* item = env->getItemByUID(popNumber(L)); + if(!item) + { + error(__FUNCTION__, getError(LUA_ERROR_ITEM_NOT_FOUND)); + lua_pushboolean(L, false); + return 1; + } + + lua_pushboolean(L, MoveEvent::AddItemField(item)); + return 1; + } + + error(__FUNCTION__, "callFunction not available for current event."); + lua_pushboolean(L, false); + return 1; } -MoveEvents::~MoveEvents() +MoveEvents::MoveEvents(): + m_lastCacheTile(NULL) { - clear(); + m_interface.initState(); } inline void MoveEvents::clearMap(MoveListMap& map) @@ -109,10 +194,10 @@ bool MoveEvents::registerEvent(Event* event, xmlNodePtr p, bool override) switch(eventType) { case MOVE_EVENT_ADD_ITEM: - moveEvent->setEventType(MOVE_EVENT_ADD_ITEM_ITEMTILE); + moveEvent->setEventType(MOVE_EVENT_ADD_TILEITEM); break; case MOVE_EVENT_REMOVE_ITEM: - moveEvent->setEventType(MOVE_EVENT_REMOVE_ITEM_ITEMTILE); + moveEvent->setEventType(MOVE_EVENT_REMOVE_TILEITEM); break; default: break; @@ -195,7 +280,7 @@ bool MoveEvents::registerEvent(Event* event, xmlNodePtr p, bool override) } } else - std::cout << "[Warning - MoveEvents::registerEvent] Malformed entry (from item: \"" << strValue << "\", to item: \"" << endStrValue << "\")" << std::endl; + std::clog << "[Warning - MoveEvents::registerEvent] Malformed entry (from item: \"" << strValue << "\", to item: \"" << endStrValue << "\")" << std::endl; } if(readXMLString(p, "uniqueid", strValue)) @@ -230,10 +315,10 @@ bool MoveEvents::registerEvent(Event* event, xmlNodePtr p, bool override) } } else - std::cout << "[Warning - MoveEvents::registerEvent] Malformed entry (from unique: \"" << strValue << "\", to unique: \"" << endStrValue << "\")" << std::endl; + std::clog << "[Warning - MoveEvents::registerEvent] Malformed entry (from unique: \"" << strValue << "\", to unique: \"" << endStrValue << "\")" << std::endl; } - if(readXMLString(p, "actionid", strValue)) + if(readXMLString(p, "actionid", strValue) || readXMLString(p, "aid", strValue)) { strVector = explodeString(strValue, ";"); for(StringVec::iterator it = strVector.begin(); it != strVector.end(); ++it) @@ -265,7 +350,7 @@ bool MoveEvents::registerEvent(Event* event, xmlNodePtr p, bool override) } } else - std::cout << "[Warning - MoveEvents::registerEvent] Malformed entry (from action: \"" << strValue << "\", to action: \"" << endStrValue << "\")" << std::endl; + std::clog << "[Warning - MoveEvents::registerEvent] Malformed entry (from action: \"" << strValue << "\", to action: \"" << endStrValue << "\")" << std::endl; } if(readXMLString(p, "pos", strValue) || readXMLString(p, "position", strValue)) @@ -301,7 +386,7 @@ void MoveEvents::addEvent(MoveEvent* moveEvent, int32_t id, MoveListMap& map, bo *it = moveEvent; } else - std::cout << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << id << std::endl; + std::clog << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << id << std::endl; return; } @@ -415,7 +500,7 @@ void MoveEvents::addEvent(MoveEvent* moveEvent, Position pos, MovePosListMap& ma { if(!override) { - std::cout << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << pos << std::endl; + std::clog << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << pos << std::endl; add = false; } else @@ -448,13 +533,15 @@ MoveEvent* MoveEvents::getEvent(const Tile* tile, MoveEvent_t eventType) bool MoveEvents::hasEquipEvent(Item* item) { - return getEvent(item, MOVE_EVENT_EQUIP) && getEvent(item, MOVE_EVENT_DEEQUIP); + MoveEvent* event = NULL; + return (event = getEvent(item, MOVE_EVENT_EQUIP)) && !event->isScripted() + && (event = getEvent(item, MOVE_EVENT_DE_EQUIP)) && !event->isScripted(); } bool MoveEvents::hasTileEvent(Item* item) { - return (getEvent(item, MOVE_EVENT_STEP_IN) || getEvent(item, MOVE_EVENT_STEP_OUT) || getEvent(item, - MOVE_EVENT_ADD_ITEM_ITEMTILE) || getEvent(item, MOVE_EVENT_REMOVE_ITEM_ITEMTILE)); + return getEvent(item, MOVE_EVENT_STEP_IN) || getEvent(item, MOVE_EVENT_STEP_OUT) || getEvent( + item, MOVE_EVENT_ADD_TILEITEM) || getEvent(item, MOVE_EVENT_REMOVE_TILEITEM); } uint32_t MoveEvents::onCreatureMove(Creature* actor, Creature* creature, const Tile* fromTile, const Tile* toTile, bool isStepping) @@ -480,6 +567,9 @@ uint32_t MoveEvents::onCreatureMove(Creature* actor, Creature* creature, const T if((moveEvent = getEvent(tile, eventType))) ret &= moveEvent->fireStepEvent(actor, creature, NULL, Position(), fromPos, toPos); + if(!tile) + return ret; + Item* tileItem = NULL; if(m_lastCacheTile == tile) { @@ -487,7 +577,7 @@ uint32_t MoveEvents::onCreatureMove(Creature* actor, Creature* creature, const T return ret; //We cannot use iterators here since the scripts can invalidate the iterator - for(int32_t i = 0, j = m_lastCacheItemVector.size(); i < j; ++i) + for(uint32_t i = 0; i < m_lastCacheItemVector.size(); ++i) { if((tileItem = m_lastCacheItemVector[i]) && (moveEvent = getEvent(tileItem, eventType))) ret &= moveEvent->fireStepEvent(actor, creature, tileItem, tile->getPosition(), fromPos, toPos); @@ -518,40 +608,43 @@ uint32_t MoveEvents::onCreatureMove(Creature* actor, Creature* creature, const T return ret; } -uint32_t MoveEvents::onPlayerEquip(Player* player, Item* item, slots_t slot, bool isCheck) +bool MoveEvents::onPlayerEquip(Player* player, Item* item, slots_t slot, bool isCheck) { if(MoveEvent* moveEvent = getEvent(item, MOVE_EVENT_EQUIP, slot)) return moveEvent->fireEquip(player, item, slot, isCheck); - return 1; + return true; } -uint32_t MoveEvents::onPlayerDeEquip(Player* player, Item* item, slots_t slot, bool isRemoval) +bool MoveEvents::onPlayerDeEquip(Player* player, Item* item, slots_t slot, bool isRemoval) { - if(MoveEvent* moveEvent = getEvent(item, MOVE_EVENT_DEEQUIP, slot)) + if(MoveEvent* moveEvent = getEvent(item, MOVE_EVENT_DE_EQUIP, slot)) return moveEvent->fireEquip(player, item, slot, isRemoval); - return 1; + return true; } uint32_t MoveEvents::onItemMove(Creature* actor, Item* item, Tile* tile, bool isAdd) { - MoveEvent_t eventType1 = MOVE_EVENT_REMOVE_ITEM, eventType2 = MOVE_EVENT_REMOVE_ITEM_ITEMTILE; + MoveEvent_t eventType = MOVE_EVENT_REMOVE_ITEM, tileEventType = MOVE_EVENT_REMOVE_TILEITEM; if(isAdd) { - eventType1 = MOVE_EVENT_ADD_ITEM; - eventType2 = MOVE_EVENT_ADD_ITEM_ITEMTILE; + eventType = MOVE_EVENT_ADD_ITEM; + tileEventType = MOVE_EVENT_ADD_TILEITEM; } uint32_t ret = 1; - MoveEvent* moveEvent = getEvent(tile, eventType1); + MoveEvent* moveEvent = getEvent(tile, eventType); if(moveEvent) ret &= moveEvent->fireAddRemItem(actor, item, NULL, tile->getPosition()); - moveEvent = getEvent(item, eventType1); + moveEvent = getEvent(item, eventType); if(moveEvent) ret &= moveEvent->fireAddRemItem(actor, item, NULL, tile->getPosition()); + if(!tile) + return ret; + Item* tileItem = NULL; if(m_lastCacheTile == tile) { @@ -559,12 +652,11 @@ uint32_t MoveEvents::onItemMove(Creature* actor, Item* item, Tile* tile, bool is return ret; //We cannot use iterators here since the scripts can invalidate the iterator - for(int32_t i = 0, j = m_lastCacheItemVector.size(); i < j; ++i) + for(uint32_t i = 0; i < m_lastCacheItemVector.size(); ++i) { if((tileItem = m_lastCacheItemVector[i]) && tileItem != item - && (moveEvent = getEvent(tileItem, eventType2))) - ret &= moveEvent->fireAddRemItem(actor, item, - tileItem, tile->getPosition()); + && (moveEvent = getEvent(tileItem, tileEventType))) + ret &= moveEvent->fireAddRemItem(actor, item, tileItem, tile->getPosition()); } return ret; @@ -580,7 +672,7 @@ uint32_t MoveEvents::onItemMove(Creature* actor, Item* item, Tile* tile, bool is if(!(thing = tile->__getThing(i)) || !(tileItem = thing->getItem()) || tileItem == item) continue; - if((moveEvent = getEvent(tileItem, eventType2))) + if((moveEvent = getEvent(tileItem, tileEventType))) { m_lastCacheItemVector.push_back(tileItem); ret &= moveEvent->fireAddRemItem(actor, item, tileItem, tile->getPosition()); @@ -617,14 +709,15 @@ void MoveEvents::onRemoveTileItem(const Tile* tile, Item* item) } } -MoveEvent::MoveEvent(LuaScriptInterface* _interface): +MoveEvent::MoveEvent(LuaInterface* _interface): Event(_interface) { - m_eventType = MOVE_EVENT_NONE; stepFunction = NULL; moveFunction = NULL; equipFunction = NULL; slot = SLOTP_WHEREEVER; + + m_eventType = MOVE_EVENT_NONE; wieldInfo = 0; reqLevel = 0; reqMagLevel = 0; @@ -634,11 +727,12 @@ Event(_interface) MoveEvent::MoveEvent(const MoveEvent* copy): Event(copy) { - m_eventType = copy->m_eventType; stepFunction = copy->stepFunction; moveFunction = copy->moveFunction; equipFunction = copy->equipFunction; slot = copy->slot; + + m_eventType = copy->m_eventType; if(copy->m_eventType == MOVE_EVENT_EQUIP) { wieldInfo = copy->wieldInfo; @@ -650,11 +744,6 @@ Event(copy) } } -MoveEvent::~MoveEvent() -{ - // -} - std::string MoveEvent::getScriptEventName() const { switch(m_eventType) @@ -668,7 +757,7 @@ std::string MoveEvent::getScriptEventName() const case MOVE_EVENT_EQUIP: return "onEquip"; - case MOVE_EVENT_DEEQUIP: + case MOVE_EVENT_DE_EQUIP: return "onDeEquip"; case MOVE_EVENT_ADD_ITEM: @@ -681,7 +770,7 @@ std::string MoveEvent::getScriptEventName() const break; } - std::cout << "[Error - MoveEvent::getScriptEventName] No valid event type." << std::endl; + std::clog << "[Error - MoveEvent::getScriptEventName] No valid event type." << std::endl; return ""; } @@ -694,8 +783,8 @@ std::string MoveEvent::getScriptEventParams() const return "cid, item, position, lastPosition, fromPosition, toPosition, actor"; case MOVE_EVENT_EQUIP: - case MOVE_EVENT_DEEQUIP: - return "cid, item, slot"; + case MOVE_EVENT_DE_EQUIP: + return "cid, item, slot, boolean"; case MOVE_EVENT_ADD_ITEM: case MOVE_EVENT_REMOVE_ITEM: @@ -705,7 +794,7 @@ std::string MoveEvent::getScriptEventParams() const break; } - std::cout << "[Error - MoveEvent::getScriptEventParams] No valid event type." << std::endl; + std::clog << "[Error - MoveEvent::getScriptEventParams] No valid event type." << std::endl; return ""; } @@ -723,18 +812,22 @@ bool MoveEvent::configureEvent(xmlNodePtr p) else if(tmpStrValue == "equip") m_eventType = MOVE_EVENT_EQUIP; else if(tmpStrValue == "deequip") - m_eventType = MOVE_EVENT_DEEQUIP; + m_eventType = MOVE_EVENT_DE_EQUIP; else if(tmpStrValue == "additem") m_eventType = MOVE_EVENT_ADD_ITEM; else if(tmpStrValue == "removeitem") m_eventType = MOVE_EVENT_REMOVE_ITEM; else { - std::cout << "[Error - MoveEvent::configureMoveEvent] Unknown event type \"" << strValue << "\"" << std::endl; + if(tmpStrValue == "function" || tmpStrValue == "buffer" || tmpStrValue == "script") + std::clog << "[Error - MoveEvent::configureMoveEvent] No event type found." << std::endl; + else + std::clog << "[Error - MoveEvent::configureMoveEvent] Unknown event type \"" << strValue << "\"" << std::endl; + return false; } - if(m_eventType == MOVE_EVENT_EQUIP || m_eventType == MOVE_EVENT_DEEQUIP) + if(m_eventType == MOVE_EVENT_EQUIP || m_eventType == MOVE_EVENT_DE_EQUIP) { if(readXMLString(p, "slot", strValue)) { @@ -765,8 +858,10 @@ bool MoveEvent::configureEvent(xmlNodePtr p) slot = SLOTP_AMMO; else if(tmpStrValue == "pickupable") slot = SLOTP_RIGHT | SLOTP_LEFT | SLOTP_AMMO; + else if(tmpStrValue == "wherever" || tmpStrValue == "any") + slot = SLOTP_WHEREEVER; else - std::cout << "[Warning - MoveEvent::configureMoveEvent] Unknown slot type \"" << strValue << "\"" << std::endl; + std::clog << "[Warning - MoveEvent::configureMoveEvent] Unknown slot type \"" << strValue << "\"" << std::endl; } wieldInfo = 0; @@ -793,13 +888,10 @@ bool MoveEvent::configureEvent(xmlNodePtr p) StringVec vocStringVec; std::string error = ""; - xmlNodePtr vocationNode = p->children; - while(vocationNode) + for(xmlNodePtr vocationNode = p->children; vocationNode; vocationNode = vocationNode->next) { if(!parseVocationNode(vocationNode, vocEquipMap, vocStringVec, error)) - std::cout << "[Warning - MoveEvent::configureEvent] " << error << std::endl; - - vocationNode = vocationNode->next; + std::clog << "[Warning - MoveEvent::configureEvent] " << error << std::endl; } if(!vocEquipMap.empty()) @@ -810,7 +902,7 @@ bool MoveEvent::configureEvent(xmlNodePtr p) } else { - std::cout << "[Error - MoveEvent::configureMoveEvent] No event found." << std::endl; + std::clog << "[Error - MoveEvent::configureMoveEvent] No event type found." << std::endl; return false; } @@ -830,7 +922,7 @@ bool MoveEvent::loadFunction(const std::string& functionName) equipFunction = DeEquipItem; else { - std::cout << "[Warning - MoveEvent::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; + std::clog << "[Warning - MoveEvent::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; return false; } @@ -841,10 +933,7 @@ bool MoveEvent::loadFunction(const std::string& functionName) MoveEvent_t MoveEvent::getEventType() const { if(m_eventType == MOVE_EVENT_NONE) - { - std::cout << "[Error - MoveEvent::getEventType] MOVE_EVENT_NONE" << std::endl; - return (MoveEvent_t)0; - } + std::clog << "[Error - MoveEvent::getEventType] MOVE_EVENT_NONE" << std::endl; return m_eventType; } @@ -858,7 +947,7 @@ uint32_t MoveEvent::StepInField(Creature* creature, Item* item) { if(MagicField* field = item->getMagicField()) { - field->onStepInField(creature, creature->getPlayer()); + field->onStepInField(creature, creature->getPlayer() != NULL); return 1; } @@ -884,25 +973,25 @@ uint32_t MoveEvent::AddItemField(Item* item) return LUA_ERROR_ITEM_NOT_FOUND; } -uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool isCheck) +bool MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool isCheck) { if(player->isItemAbilityEnabled(slot)) - return 1; + return true; if(!player->hasFlag(PlayerFlag_IgnoreEquipCheck) && moveEvent->getWieldInfo() != 0) { if(player->getLevel() < (uint32_t)moveEvent->getReqLevel() || player->getMagicLevel() < (uint32_t)moveEvent->getReqMagLv()) - return 0; + return false; if(moveEvent->isPremium() && !player->isPremium()) - return 0; + return false; if(!moveEvent->getVocEquipMap().empty() && moveEvent->getVocEquipMap().find(player->getVocationId()) == moveEvent->getVocEquipMap().end()) - return 0; + return false; } if(isCheck) - return 1; + return true; const ItemType& it = Item::items[item->getID()]; if(it.transformEquipTo) @@ -910,44 +999,46 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, Item* newItem = g_game.transformItem(item, it.transformEquipTo); g_game.startDecay(newItem); } - else - player->setItemAbility(slot, true); - if(it.abilities.invisible) + player->setItemAbility(slot, true); + if(!it.hasAbilities()) + return true; + + if(it.abilities->invisible) { Condition* condition = Condition::createCondition((ConditionId_t)slot, CONDITION_INVISIBLE, -1, 0); player->addCondition(condition); } - if(it.abilities.manaShield) + if(it.abilities->manaShield) { Condition* condition = Condition::createCondition((ConditionId_t)slot, CONDITION_MANASHIELD, -1, 0); player->addCondition(condition); } - if(it.abilities.speed) - g_game.changeSpeed(player, it.abilities.speed); + if(it.abilities->speed) + g_game.changeSpeed(player, it.abilities->speed); - if(it.abilities.conditionSuppressions) + if(it.abilities->conditionSuppressions) { - player->setConditionSuppressions(it.abilities.conditionSuppressions, false); + player->setConditionSuppressions(it.abilities->conditionSuppressions, false); player->sendIcons(); } - if(it.abilities.regeneration) + if(it.abilities->regeneration) { Condition* condition = Condition::createCondition((ConditionId_t)slot, CONDITION_REGENERATION, -1, 0); - if(it.abilities.healthGain) - condition->setParam(CONDITIONPARAM_HEALTHGAIN, it.abilities.healthGain); + if(it.abilities->healthGain) + condition->setParam(CONDITIONPARAM_HEALTHGAIN, it.abilities->healthGain); - if(it.abilities.healthTicks) - condition->setParam(CONDITIONPARAM_HEALTHTICKS, it.abilities.healthTicks); + if(it.abilities->healthTicks) + condition->setParam(CONDITIONPARAM_HEALTHTICKS, it.abilities->healthTicks); - if(it.abilities.manaGain) - condition->setParam(CONDITIONPARAM_MANAGAIN, it.abilities.manaGain); + if(it.abilities->manaGain) + condition->setParam(CONDITIONPARAM_MANAGAIN, it.abilities->manaGain); - if(it.abilities.manaTicks) - condition->setParam(CONDITIONPARAM_MANATICKS, it.abilities.manaTicks); + if(it.abilities->manaTicks) + condition->setParam(CONDITIONPARAM_MANATICKS, it.abilities->manaTicks); player->addCondition(condition); } @@ -955,16 +1046,16 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, bool needUpdateSkills = false; for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { - if(it.abilities.skills[i]) + if(it.abilities->skills[i]) { - player->setVarSkill((skills_t)i, it.abilities.skills[i]); + player->setVarSkill((skills_t)i, it.abilities->skills[i]); if(!needUpdateSkills) needUpdateSkills = true; } - if(it.abilities.skillsPercent[i]) + if(it.abilities->skillsPercent[i]) { - player->setVarSkill((skills_t)i, (int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((it.abilities.skillsPercent[i] - 100) / 100.f))); + player->setVarSkill((skills_t)i, (int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((it.abilities->skillsPercent[i] - 100) / 100.f))); if(!needUpdateSkills) needUpdateSkills = true; } @@ -976,16 +1067,16 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, bool needUpdateStats = false; for(uint32_t s = STAT_FIRST; s <= STAT_LAST; ++s) { - if(it.abilities.stats[s]) + if(it.abilities->stats[s]) { - player->setVarStats((stats_t)s, it.abilities.stats[s]); + player->setVarStats((stats_t)s, it.abilities->stats[s]); if(!needUpdateStats) needUpdateStats = true; } - if(it.abilities.statsPercent[s]) + if(it.abilities->statsPercent[s]) { - player->setVarStats((stats_t)s, (int32_t)(player->getDefaultStats((stats_t)s) * ((it.abilities.statsPercent[s] - 100) / 100.f))); + player->setVarStats((stats_t)s, (int32_t)(player->getDefaultStats((stats_t)s) * ((it.abilities->statsPercent[s] - 100) / 100.f))); if(!needUpdateStats) needUpdateStats = true; } @@ -994,15 +1085,14 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, if(needUpdateStats) player->sendStats(); - return 1; + return true; } -uint32_t MoveEvent::DeEquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool isRemoval) +bool MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, slots_t slot, bool isRemoval) { if(!player->isItemAbilityEnabled(slot)) - return 1; + return true; - player->setItemAbility(slot, false); const ItemType& it = Item::items[item->getID()]; if(isRemoval && it.transformDeEquipTo) { @@ -1010,37 +1100,41 @@ uint32_t MoveEvent::DeEquipItem(MoveEvent* moveEvent, Player* player, Item* item g_game.startDecay(item); } - if(it.abilities.invisible) + player->setItemAbility(slot, false); + if(!it.hasAbilities()) + return true; + + if(it.abilities->invisible) player->removeCondition(CONDITION_INVISIBLE, (ConditionId_t)slot); - if(it.abilities.manaShield) + if(it.abilities->manaShield) player->removeCondition(CONDITION_MANASHIELD, (ConditionId_t)slot); - if(it.abilities.speed) - g_game.changeSpeed(player, -it.abilities.speed); + if(it.abilities->speed) + g_game.changeSpeed(player, -it.abilities->speed); - if(it.abilities.conditionSuppressions) + if(it.abilities->conditionSuppressions) { - player->setConditionSuppressions(it.abilities.conditionSuppressions, true); + player->setConditionSuppressions(it.abilities->conditionSuppressions, true); player->sendIcons(); } - if(it.abilities.regeneration) + if(it.abilities->regeneration) player->removeCondition(CONDITION_REGENERATION, (ConditionId_t)slot); bool needUpdateSkills = false; for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { - if(it.abilities.skills[i]) + if(it.abilities->skills[i]) { needUpdateSkills = true; - player->setVarSkill((skills_t)i, -it.abilities.skills[i]); + player->setVarSkill((skills_t)i, -it.abilities->skills[i]); } - if(it.abilities.skillsPercent[i]) + if(it.abilities->skillsPercent[i]) { needUpdateSkills = true; - player->setVarSkill((skills_t)i, -(int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((it.abilities.skillsPercent[i] - 100) / 100.f))); + player->setVarSkill((skills_t)i, -(int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((it.abilities->skillsPercent[i] - 100) / 100.f))); } } @@ -1050,23 +1144,23 @@ uint32_t MoveEvent::DeEquipItem(MoveEvent* moveEvent, Player* player, Item* item bool needUpdateStats = false; for(uint32_t s = STAT_FIRST; s <= STAT_LAST; ++s) { - if(it.abilities.stats[s]) + if(it.abilities->stats[s]) { needUpdateStats = true; - player->setVarStats((stats_t)s, -it.abilities.stats[s]); + player->setVarStats((stats_t)s, -it.abilities->stats[s]); } - if(it.abilities.statsPercent[s]) + if(it.abilities->statsPercent[s]) { needUpdateStats = true; - player->setVarStats((stats_t)s, -(int32_t)(player->getDefaultStats((stats_t)s) * ((it.abilities.statsPercent[s] - 100) / 100.f))); + player->setVarStats((stats_t)s, -(int32_t)(player->getDefaultStats((stats_t)s) * ((it.abilities->statsPercent[s] - 100) / 100.f))); } } if(needUpdateStats) player->sendStats(); - return 1; + return true; } uint32_t MoveEvent::fireStepEvent(Creature* actor, Creature* creature, Item* item, const Position& pos, const Position& fromPos, const Position& toPos) @@ -1083,6 +1177,7 @@ uint32_t MoveEvent::executeStep(Creature* actor, Creature* creature, Item* item, //onStepOut(cid, item, position, lastPosition, fromPosition, toPosition, actor) if(m_interface->reserveEnv()) { + MoveEventScript::event = this; ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { @@ -1097,7 +1192,9 @@ uint32_t MoveEvent::executeStep(Creature* actor, Creature* creature, Item* item, env->streamPosition(scriptstream, "toPosition", toPos, 0); scriptstream << "local actor = " << env->addThing(actor) << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1113,7 +1210,7 @@ uint32_t MoveEvent::executeStep(Creature* actor, Creature* creature, Item* item, #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << creature->getName() << " itemid: " << item->getID() << " - " << pos; - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); @@ -1123,11 +1220,11 @@ uint32_t MoveEvent::executeStep(Creature* actor, Creature* creature, Item* item, m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(creature)); - LuaScriptInterface::pushThing(L, item, env->addThing(item)); - LuaScriptInterface::pushPosition(L, pos, 0); - LuaScriptInterface::pushPosition(L, creature->getLastPosition(), 0); - LuaScriptInterface::pushPosition(L, fromPos, 0); - LuaScriptInterface::pushPosition(L, toPos, 0); + LuaInterface::pushThing(L, item, env->addThing(item)); + LuaInterface::pushPosition(L, pos, 0); + LuaInterface::pushPosition(L, creature->getLastPosition(), 0); + LuaInterface::pushPosition(L, fromPos, 0); + LuaInterface::pushPosition(L, toPos, 0); lua_pushnumber(L, env->addThing(actor)); bool result = m_interface->callFunction(7); @@ -1138,25 +1235,26 @@ uint32_t MoveEvent::executeStep(Creature* actor, Creature* creature, Item* item, } else { - std::cout << "[Error - MoveEvent::executeStep] Call stack overflow." << std::endl; + std::clog << "[Error - MoveEvent::executeStep] Call stack overflow." << std::endl; return 0; } } -uint32_t MoveEvent::fireEquip(Player* player, Item* item, slots_t slot, bool boolean) +bool MoveEvent::fireEquip(Player* player, Item* item, slots_t slot, bool boolean) { if(isScripted()) - return executeEquip(player, item, slot); + return executeEquip(player, item, slot, boolean); return equipFunction(this, player, item, slot, boolean); } -uint32_t MoveEvent::executeEquip(Player* player, Item* item, slots_t slot) +bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot, bool boolean) { - //onEquip(cid, item, slot) - //onDeEquip(cid, item, slot) + //onEquip(cid, item, slot, boolean) + //onDeEquip(cid, item, slot, boolean) if(m_interface->reserveEnv()) { + MoveEventScript::event = this; ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { @@ -1166,8 +1264,11 @@ uint32_t MoveEvent::executeEquip(Player* player, Item* item, slots_t slot) scriptstream << "local cid = " << env->addThing(player) << std::endl; env->streamThing(scriptstream, "item", item, env->addThing(item)); scriptstream << "local slot = " << slot << std::endl; + scriptstream << "local boolean = " << (boolean ? "true" : "false") << std::endl; + + if(m_scriptData) + scriptstream << *m_scriptData; - scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1183,7 +1284,7 @@ uint32_t MoveEvent::executeEquip(Player* player, Item* item, slots_t slot) #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << player->getName() << " itemid: " << item->getID() << " slot: " << slot; - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); @@ -1193,18 +1294,19 @@ uint32_t MoveEvent::executeEquip(Player* player, Item* item, slots_t slot) m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(player)); - LuaScriptInterface::pushThing(L, item, env->addThing(item)); + LuaInterface::pushThing(L, item, env->addThing(item)); lua_pushnumber(L, slot); + lua_pushboolean(L, boolean); - bool result = m_interface->callFunction(3); + bool result = m_interface->callFunction(4); m_interface->releaseEnv(); return result; } } else { - std::cout << "[Error - MoveEvent::executeEquip] Call stack overflow." << std::endl; - return 0; + std::clog << "[Error - MoveEvent::executeEquip] Call stack overflow." << std::endl; + return false; } } @@ -1222,6 +1324,7 @@ uint32_t MoveEvent::executeAddRemItem(Creature* actor, Item* item, Item* tileIte //onRemoveItem(moveItem, tileItem, position, cid) if(m_interface->reserveEnv()) { + MoveEventScript::event = this; ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { @@ -1234,7 +1337,9 @@ uint32_t MoveEvent::executeAddRemItem(Creature* actor, Item* item, Item* tileIte env->streamPosition(scriptstream, "position", pos, 0); scriptstream << "local cid = " << env->addThing(actor) << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1253,7 +1358,7 @@ uint32_t MoveEvent::executeAddRemItem(Creature* actor, Item* item, Item* tileIte desc << "tileid: " << tileItem->getID(); desc << " itemid: " << item->getID() << " - " << pos; - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); @@ -1262,9 +1367,9 @@ uint32_t MoveEvent::executeAddRemItem(Creature* actor, Item* item, Item* tileIte lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); - LuaScriptInterface::pushThing(L, item, env->addThing(item)); - LuaScriptInterface::pushThing(L, tileItem, env->addThing(tileItem)); - LuaScriptInterface::pushPosition(L, pos, 0); + LuaInterface::pushThing(L, item, env->addThing(item)); + LuaInterface::pushThing(L, tileItem, env->addThing(tileItem)); + LuaInterface::pushPosition(L, pos, 0); lua_pushnumber(L, env->addThing(actor)); bool result = m_interface->callFunction(4); @@ -1275,7 +1380,7 @@ uint32_t MoveEvent::executeAddRemItem(Creature* actor, Item* item, Item* tileIte } else { - std::cout << "[Error - MoveEvent::executeAddRemItem] Call stack overflow." << std::endl; + std::clog << "[Error - MoveEvent::executeAddRemItem] Call stack overflow." << std::endl; return 0; } } diff --git a/movement.h b/movement.h index 7ab578b..c27e982 100644 --- a/movement.h +++ b/movement.h @@ -21,38 +21,45 @@ #include "baseevents.h" #include "creature.h" +class MoveEvent; +class MoveEventScript : public LuaInterface +{ + public: + MoveEventScript() : LuaInterface("MoveEvents Interface") {} + virtual ~MoveEventScript() {} + + static MoveEvent* event; + + protected: + virtual void registerFunctions(); + static int32_t luaCallFunction(lua_State* L); +}; + enum MoveEvent_t { MOVE_EVENT_FIRST = 0, MOVE_EVENT_STEP_IN = MOVE_EVENT_FIRST, MOVE_EVENT_STEP_OUT = 1, MOVE_EVENT_EQUIP = 2, - MOVE_EVENT_DEEQUIP = 3, + MOVE_EVENT_DE_EQUIP = 3, MOVE_EVENT_ADD_ITEM = 4, MOVE_EVENT_REMOVE_ITEM = 5, - MOVE_EVENT_ADD_ITEM_ITEMTILE = 6, - MOVE_EVENT_REMOVE_ITEM_ITEMTILE = 7, + MOVE_EVENT_ADD_TILEITEM = 6, + MOVE_EVENT_REMOVE_TILEITEM = 7, MOVE_EVENT_NONE = 8, - MOVE_EVENT_LAST = MOVE_EVENT_REMOVE_ITEM_ITEMTILE + MOVE_EVENT_LAST = MOVE_EVENT_REMOVE_TILEITEM }; -class MoveEvent; typedef std::list EventList; - -struct MoveEventList -{ - EventList moveEvent[MOVE_EVENT_NONE]; -}; - class MoveEvents : public BaseEvents { public: MoveEvents(); - virtual ~MoveEvents(); + virtual ~MoveEvents() {clear();} uint32_t onCreatureMove(Creature* actor, Creature* creature, const Tile* fromTile, const Tile* toTile, bool isStepping); - uint32_t onPlayerEquip(Player* player, Item* item, slots_t slot, bool isCheck); - uint32_t onPlayerDeEquip(Player* player, Item* item, slots_t slot, bool isRemoval); + bool onPlayerEquip(Player* player, Item* item, slots_t slot, bool isCheck); + bool onPlayerDeEquip(Player* player, Item* item, slots_t slot, bool isRemoval); uint32_t onItemMove(Creature* actor, Item* item, Tile* tile, bool isAdd); MoveEvent* getEvent(Item* item, MoveEvent_t eventType); @@ -63,8 +70,10 @@ class MoveEvents : public BaseEvents void onAddTileItem(const Tile* tile, Item* item); protected: - const Tile* m_lastCacheTile; - std::vector m_lastCacheItemVector; + struct MoveEventList + { + EventList moveEvent[MOVE_EVENT_NONE]; + }; virtual std::string getScriptBaseName() const {return "movements";} virtual void clear(); @@ -72,8 +81,8 @@ class MoveEvents : public BaseEvents virtual Event* getEvent(const std::string& nodeName); virtual bool registerEvent(Event* event, xmlNodePtr p, bool override); - virtual LuaScriptInterface& getInterface() {return m_interface;} - LuaScriptInterface m_interface; + virtual LuaInterface& getInterface() {return m_interface;} + MoveEventScript m_interface; void registerItemID(int32_t itemId, MoveEvent_t eventType); void registerActionID(int32_t actionId, MoveEvent_t eventType); @@ -93,18 +102,21 @@ class MoveEvents : public BaseEvents void addEvent(MoveEvent* moveEvent, Position pos, MovePosListMap& map, bool override); MoveEvent* getEvent(const Tile* tile, MoveEvent_t eventType); + + const Tile* m_lastCacheTile; + std::vector m_lastCacheItemVector; }; typedef uint32_t (MoveFunction)(Item* item); typedef uint32_t (StepFunction)(Creature* creature, Item* item); -typedef uint32_t (EquipFunction)(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean); +typedef bool (EquipFunction)(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean); class MoveEvent : public Event { public: - MoveEvent(LuaScriptInterface* _interface); + MoveEvent(LuaInterface* _interface); MoveEvent(const MoveEvent* copy); - virtual ~MoveEvent(); + virtual ~MoveEvent() {} MoveEvent_t getEventType() const; void setEventType(MoveEvent_t type); @@ -114,16 +126,19 @@ class MoveEvent : public Event uint32_t fireStepEvent(Creature* actor, Creature* creature, Item* item, const Position& pos, const Position& fromPos, const Position& toPos); uint32_t fireAddRemItem(Creature* actor, Item* item, Item* tileItem, const Position& pos); - uint32_t fireEquip(Player* player, Item* item, slots_t slot, bool boolean); + bool fireEquip(Player* player, Item* item, slots_t slot, bool boolean); - //scripting uint32_t executeStep(Creature* actor, Creature* creature, Item* item, const Position& pos, const Position& fromPos, const Position& toPos); - uint32_t executeEquip(Player* player, Item* item, slots_t slot); + bool executeEquip(Player* player, Item* item, slots_t slot, bool boolean); uint32_t executeAddRemItem(Creature* actor, Item* item, Item* tileItem, const Position& pos); + static StepFunction StepInField; + static MoveFunction AddItemField; + static EquipFunction EquipItem; + static EquipFunction DeEquipItem; + uint32_t getWieldInfo() const {return wieldInfo;} uint32_t getSlot() const {return slot;} - int32_t getReqLevel() const {return reqLevel;} int32_t getReqMagLv() const {return reqMagLevel;} bool isPremium() const {return premium;} @@ -132,24 +147,17 @@ class MoveEvent : public Event const std::string& getVocationString() const {return vocationString;} protected: + MoveEvent_t m_eventType; + virtual std::string getScriptEventName() const; virtual std::string getScriptEventParams() const; - static StepFunction StepInField; - static MoveFunction AddItemField; - static EquipFunction EquipItem; - static EquipFunction DeEquipItem; - - MoveEvent_t m_eventType; - StepFunction* stepFunction; MoveFunction* moveFunction; + StepFunction* stepFunction; EquipFunction* equipFunction; - uint32_t wieldInfo; - uint32_t slot; - - int32_t reqLevel; - int32_t reqMagLevel; + uint32_t wieldInfo, slot; + int32_t reqLevel, reqMagLevel; bool premium; VocationMap vocEquipMap; diff --git a/networkmessage.cpp b/networkmessage.cpp index e5a61f6..917e36a 100644 --- a/networkmessage.cpp +++ b/networkmessage.cpp @@ -12,119 +12,236 @@ // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" #include #include "networkmessage.h" #include "position.h" -#include "rsa.h" +#include "item.h" -#include "container.h" -#include "creature.h" -#include "player.h" +SocketCode_t NetworkMessage::read(SOCKET socket, bool ignoreLength, int32_t timeout/* = NETWORK_RETRY_TIME*/) +{ + int32_t waiting = 0, data = NETWORK_DEFAULT_SIZE; + if(!ignoreLength) + { + do + { + // just read the size to avoid reading 2 messages at once + int32_t ret = recv(socket, (char*)m_buffer, NETWORK_HEADER_SIZE, 0); + if(ret <= 0) + { + if(errno == EWOULDBLOCK) + { + ret = 0; + OTSYS_SLEEP(10); + + waiting += 10; + if(waiting > timeout) + { + reset(NETWORK_HEADER_SIZE); + return SOCKET_CODE_TIMEOUT; + } + } + else + { + reset(NETWORK_HEADER_SIZE); + return SOCKET_CODE_ERROR; + } + } + + m_size += ret; + } + while(m_size < NETWORK_HEADER_SIZE); + + // for now we expect 2 bytes at once, it should not be splitted + data = (int32_t)(m_buffer[0] | m_buffer[1] << 8); + if(m_size != NETWORK_HEADER_SIZE || data > NETWORK_MAX_SIZE - NETWORK_HEADER_SIZE) + { + reset(NETWORK_HEADER_SIZE); + return SOCKET_CODE_ERROR; + } + } -int32_t NetworkMessage::decodeHeader() + int32_t recvd = 0; + do + { + // read the real data + int32_t ret = recv(socket, (char*)m_buffer + recvd + NETWORK_HEADER_SIZE, data - recvd, 0); + if(ret <= 0) + { + if(errno == EWOULDBLOCK) + { + ret = 0; + OTSYS_SLEEP(100); + + waiting += 100; + if(waiting > timeout) + { + reset(NETWORK_HEADER_SIZE); + return SOCKET_CODE_TIMEOUT; + } + } + else if(data == NETWORK_DEFAULT_SIZE) + break; + else + { + reset(NETWORK_HEADER_SIZE); + return SOCKET_CODE_ERROR; + } + } + + recvd += ret; + } + while(recvd < data); + m_size += recvd; + + // we got something unexpected/incomplete + if(m_size <= NETWORK_HEADER_SIZE || (!ignoreLength && m_size - NETWORK_HEADER_SIZE != data)) + { + reset(NETWORK_HEADER_SIZE); + return SOCKET_CODE_ERROR; + } + + m_position = NETWORK_HEADER_SIZE; + return SOCKET_CODE_OK; +} + +SocketCode_t NetworkMessage::write(SOCKET socket, int32_t timeout/* = NETWORK_RETRY_TIME*/) { - int32_t size = (int32_t)(m_MsgBuf[0] | m_MsgBuf[1] << 8); - m_MsgSize = size; - return size; + if(!m_size) + return SOCKET_CODE_OK; + + m_buffer[2] = (uint8_t)(m_size); + m_buffer[3] = (uint8_t)(m_size >> 8); + + int32_t sent = 0, waiting = 0; + do + { + int32_t ret = send(socket, (char*)m_buffer + sent + NETWORK_HEADER_SIZE, + std::min(m_size - sent + NETWORK_HEADER_SIZE, 1000), 0); + if(ret <= 0) + { + if(errno == EWOULDBLOCK) + { + ret = 0; + OTSYS_SLEEP(100); + + waiting += 100; + if(waiting > timeout) + return SOCKET_CODE_TIMEOUT; + } + else + return SOCKET_CODE_ERROR; + } + + sent += ret; + } + while(sent < m_size + NETWORK_HEADER_SIZE); + return SOCKET_CODE_OK; } -/******************************************************************************/ -std::string NetworkMessage::GetString(uint16_t size/* = 0*/) +std::string NetworkMessage::getString(bool peek/* = false*/, uint16_t size/* = 0*/) { if(!size) - size = GetU16(); + size = get(peek); + + uint16_t position = m_position; + if(peek) + position += 2; - if(size >= (NETWORKMESSAGE_MAXSIZE - m_ReadPos)) - return std::string(); + if(size >= (16384 - position)) + return std :: string(); - char* v = (char*)(m_MsgBuf + m_ReadPos); - m_ReadPos += size; + char* v = (char*)(m_buffer + position); + if(peek) + return std::string(v, size); + + m_position += size; return std::string(v, size); } -Position NetworkMessage::GetPosition() +Position NetworkMessage::getPosition() { Position pos; - pos.x = GetU16(); - pos.y = GetU16(); - pos.z = GetByte(); + pos.x = get(); + pos.y = get(); + pos.z = get(); return pos; } -/******************************************************************************/ -void NetworkMessage::AddString(const char* value) +void NetworkMessage::putString(const char* value, bool addSize/* = true*/) { - uint32_t stringLen = (uint32_t)strlen(value); - if(!canAdd(stringLen + 2) || stringLen > 8192) + uint32_t size = (uint32_t)strlen(value); + if(!hasSpace(size + (addSize ? 2 : 0)) || size > 8192) return; - AddU16(stringLen); - strcpy((char*)(m_MsgBuf + m_ReadPos), value); - m_ReadPos += stringLen; - m_MsgSize += stringLen; -} - -void NetworkMessage::AddBytes(const char* bytes, uint32_t size) -{ - if(!canAdd(size) || size > 8192) - return; + if(addSize) + put(size); - memcpy(m_MsgBuf + m_ReadPos, bytes, size); - m_ReadPos += size; - m_MsgSize += size; + strcpy((char*)(m_buffer + m_position), value); + m_position += size; + m_size += size; } -void NetworkMessage::AddPaddingBytes(uint32_t n) +void NetworkMessage::putPadding(uint32_t amount) { - if(!canAdd(n)) + if(!hasSpace(amount)) return; - memset((void*)&m_MsgBuf[m_ReadPos], 0x33, n); - m_MsgSize = m_MsgSize + n; + memset((void*)&m_buffer[m_position], 0x33, amount); + m_size += amount; } -void NetworkMessage::AddPosition(const Position& pos) +void NetworkMessage::putPosition(const Position& pos) { - AddU16(pos.x); - AddU16(pos.y); - AddByte(pos.z); + put(pos.x); + put(pos.y); + put(pos.z); } -void NetworkMessage::AddItem(uint16_t id, uint8_t count) +void NetworkMessage::putItem(uint16_t id, uint8_t count) { const ItemType &it = Item::items[id]; - AddU16(it.clientId); - if(it.stackable || it.isRune()) - AddByte(count); + put(it.clientId); + if(it.stackable) + put(count); else if(it.isSplash() || it.isFluidContainer()) - { - uint32_t fluidIndex = (count % 8); - AddByte(fluidMap[fluidIndex]); - } + put(fluidMap[count % 8]); + + if(it.isAnimation) + put(0xFE); } -void NetworkMessage::AddItem(const Item* item) +void NetworkMessage::putItem(const Item* item) { - const ItemType &it = Item::items[item->getID()]; - AddU16(it.clientId); - if(it.stackable || it.isRune()) - AddByte(item->getSubType()); + const ItemType& it = Item::items[item->getID()]; + put(it.clientId); + if(it.stackable) + put(item->getSubType()); else if(it.isSplash() || it.isFluidContainer()) - AddByte(fluidMap[item->getSubType() % 8]); + put(fluidMap[item->getSubType() % 8]); + + if(it.isAnimation) + put(0xFE); +} + +void NetworkMessage::putItemId(const Item* item) +{ + const ItemType& it = Item::items[item->getID()]; + put(it.clientId); } -void NetworkMessage::AddItemId(const Item *item) +void NetworkMessage::putItemId(uint16_t itemId) { - const ItemType &it = Item::items[item->getID()]; - AddU16(it.clientId); + const ItemType& it = Item::items[itemId]; + put(it.clientId); } -void NetworkMessage::AddItemId(uint16_t itemId) +int32_t NetworkMessage::decodeHeader() { - const ItemType &it = Item::items[itemId]; - AddU16(it.clientId); + int32_t size = (int32_t)(m_buffer[0] | m_buffer[1] << 8); + m_size = size; + return size; } diff --git a/networkmessage.h b/networkmessage.h index 81fdcf0..6cf8ee1 100644 --- a/networkmessage.h +++ b/networkmessage.h @@ -12,136 +12,118 @@ // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// along with this program. If not, see . //////////////////////////////////////////////////////////////////////// -#ifndef __NETWORK_MESSAGE__ -#define __NETWORK_MESSAGE__ - +#ifndef __NETWORKMESSAGE__ +#define __NETWORKMESSAGE__ #include "otsystem.h" #include "const.h" +enum SocketCode_t +{ + SOCKET_CODE_OK, + SOCKET_CODE_TIMEOUT, + SOCKET_CODE_ERROR, +}; + class Item; -class Creature; -class Player; class Position; -class RSA; class NetworkMessage { public: - enum {headerLength = 2}; - enum {bodyLength = NETWORKMESSAGE_MAXSIZE - headerLength}; - - // constructor/destructor - NetworkMessage() {Reset();} + NetworkMessage() {reset();} virtual ~NetworkMessage() {} - protected: // resets the internal buffer to an empty message - void Reset() + void reset(uint16_t size = NETWORK_CRYPTOHEADER_SIZE) { - m_MsgSize = 0; - m_ReadPos = 8; + m_size = 0; + m_position = size; } - public: - // skips count unknown/unused bytes in an incoming message - void SkipBytes(int32_t count) {m_ReadPos += count;} + // socket functions + SocketCode_t read(SOCKET socket, bool ignoreLength, int32_t timeout = NETWORK_RETRY_TIMEOUT); + SocketCode_t write(SOCKET socket, int32_t timeout = NETWORK_RETRY_TIMEOUT); - // simply read functions for incoming message - uint8_t GetByte() {return m_MsgBuf[m_ReadPos++];} - uint8_t PeekByte() {return m_MsgBuf[m_ReadPos];} - uint16_t GetU16() + // simple read functions for incoming message + template + T get(bool peek = false) { - uint16_t v = *(uint16_t*)(m_MsgBuf + m_ReadPos); - m_ReadPos += 2; - return v; - } - uint16_t PeekU16() - { - uint16_t v = *(uint16_t*)(m_MsgBuf + m_ReadPos); - return v; - } - uint32_t GetU32() - { - uint32_t v = *(uint32_t*)(m_MsgBuf + m_ReadPos); - m_ReadPos += 4; - return v; - } - uint32_t PeekU32() - { - uint32_t v = *(uint32_t*)(m_MsgBuf + m_ReadPos); - return v; + T value = *(T*)(m_buffer + m_position); + if(peek) + return value; + + m_position += sizeof(T); + return value; } - std::string GetString(uint16_t size = 0); - std::string GetRaw() {return GetString(m_MsgSize - m_ReadPos);} + std::string getString(bool peek = false, uint16_t size = 0); + std::string getRaw(bool peek = false) {return getString(peek, m_size - m_position);} - Position GetPosition(); - uint16_t GetSpriteId() {return GetU16();} + // read for complex types + Position getPosition(); - // simply write functions for outgoing message - void AddByte(uint8_t value) - { - if(!canAdd(1)) - return; - - m_MsgBuf[m_ReadPos++] = value; - m_MsgSize++; - } - void AddU16(uint16_t value) - { - if(!canAdd(2)) - return; + // skips count unknown/unused bytes in an incoming message + void skip(int32_t count) {m_position += count;} - *(uint16_t*)(m_MsgBuf + m_ReadPos) = value; - m_ReadPos += 2; m_MsgSize += 2; - } - void AddU32(uint32_t value) + // simple write functions for outgoing message + template + void put(T value) { - if(!canAdd(4)) + if(!hasSpace(sizeof(T))) return; - *(uint32_t*)(m_MsgBuf + m_ReadPos) = value; - m_ReadPos += 4; m_MsgSize += 4; + *(T*)(m_buffer + m_position) = value; + m_position += sizeof(T); + m_size += sizeof(T); } - void AddBytes(const char* bytes, uint32_t size); - void AddPaddingBytes(uint32_t n); + void putString(const std::string& value, bool addSize = true) {putString(value.c_str(), addSize);} + void putString(const char* value, bool addSize = true); - void AddString(const std::string &value) {AddString(value.c_str());} - void AddString(const char* value); + void putPadding(uint32_t amount); - // write functions for complex types - void AddPosition(const Position &pos); - void AddItem(uint16_t id, uint8_t count); - void AddItem(const Item *item); - void AddItemId(const Item *item); - void AddItemId(uint16_t itemId); + // write for complex types + void putPosition(const Position& pos); + void putItem(uint16_t id, uint8_t count); + void putItem(const Item* item); + void putItemId(const Item* item); + void putItemId(uint16_t itemId); - int32_t getMessageLength() const {return m_MsgSize;} - void setMessageLength(int32_t newSize) {m_MsgSize = newSize;} + int32_t decodeHeader(); - int32_t getReadPos() const {return m_ReadPos;} - void setReadPos(int32_t newPos) {m_ReadPos = newPos;} + // message propeties functions + uint16_t size() const {return m_size;} + void setSize(uint16_t size) {m_size = size;} - char* getBuffer() {return (char*)&m_MsgBuf[0];} - char* getBodyBuffer() {m_ReadPos = 2; return (char*)&m_MsgBuf[headerLength];} + uint16_t position() const {return m_position;} + void setPosition(uint16_t position) {m_position = position;} - int32_t decodeHeader(); + char* buffer() {return (char*)&m_buffer[0];} + char* bodyBuffer() + { + m_position = NETWORK_HEADER_SIZE; + return (char*)&m_buffer[NETWORK_HEADER_SIZE]; + } #ifdef __TRACK_NETWORK__ - virtual void Track(std::string file, long line, std::string func) {} + virtual void track(std::string file, int32_t line, std::string func) {} virtual void clearTrack() {} -#endif +#endif protected: - inline bool canAdd(int32_t size) {return (size + m_ReadPos < NETWORKMESSAGE_MAXSIZE - 16);} + // used to check available space while writing + inline bool hasSpace(int32_t size) const {return (size + m_position < NETWORK_MAX_SIZE - 16);} + + // message propeties + uint16_t m_size; + uint16_t m_position; - uint8_t m_MsgBuf[NETWORKMESSAGE_MAXSIZE]; - int32_t m_MsgSize, m_ReadPos; + // message data + uint8_t m_buffer[NETWORK_MAX_SIZE]; }; typedef boost::shared_ptr NetworkMessage_ptr; -#endif // #ifndef __NETWORK_MESSAGE__ +#endif diff --git a/npc.cpp b/npc.cpp index e73046c..827094c 100644 --- a/npc.cpp +++ b/npc.cpp @@ -37,12 +37,131 @@ extern ConfigManager g_config; extern Game g_game; extern Spells* g_spells; +extern Npcs g_npcs; AutoList Npc::autoList; #ifdef __ENABLE_SERVER_DIAGNOSTIC__ uint32_t Npc::npcCount = 0; #endif -NpcScriptInterface* Npc::m_interface = NULL; +NpcScript* Npc::m_interface = NULL; + +Npcs::~Npcs() +{ + for(DataMap::iterator it = data.begin(); it != data.end(); ++it) + delete it->second; + + data.clear(); +} + +bool Npcs::loadFromXml(bool reloading/* = false*/) +{ + xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_OTHER, "npc/npcs.xml").c_str()); + if(!doc) + { + std::clog << "[Warning - Npcs::loadFromXml] Cannot load npcs file." << std::endl; + std::clog << getLastXMLError() << std::endl; + return false; + } + + xmlNodePtr root = xmlDocGetRootElement(doc); + if(xmlStrcmp(root->name,(const xmlChar*)"npcs")) + { + std::clog << "[Error - Npcs::loadFromXml] Malformed npcs file." << std::endl; + return false; + } + + for(xmlNodePtr p = root->children; p; p = p->next) + { + if(p->type != XML_ELEMENT_NODE) + continue; + + if(xmlStrcmp(p->name, (const xmlChar*)"npc")) + { + std::clog << "[Warning - Npcs::loadFromXml] Unknown node name: " << p->name << "." << std::endl; + continue; + } + else + parseNpcNode(p, FILE_TYPE_OTHER, reloading); + } + + return true; +} + +bool Npcs::parseNpcNode(xmlNodePtr node, FileType_t path, bool reloading/* = false*/) +{ + std::string name; + if(!readXMLString(node, "name", name)) + { + std::clog << "[Warning - Npcs::parseNpcNode] Missing npc name!" << std::endl; + return false; + } + + bool new_nType = false; + NpcType* nType = NULL; + if(!(nType = getType(name))) + new_nType = true; + else if(reloading) + { + std::clog << "[Warning - Npcs::parseNpcNode] Duplicate registered npc with name: " << name << "." << std::endl; + return false; + } + + std::string strValue; + if(!readXMLString(node, "file", strValue) && !readXMLString(node, "path", strValue)) + { + std::clog << "[Warning - Npcs::loadFromXml] Missing file path for npc with name " << name << "." << std::endl; + return false; + } + + if(new_nType) + nType = new NpcType(); + + nType->name = name; + toLowerCaseString(name); + + nType->file = getFilePath(path, "npc/" + strValue); + if(readXMLString(node, "nameDescription", strValue) || readXMLString(node, "namedescription", strValue)) + nType->nameDescription = strValue; + + if(readXMLString(node, "script", strValue)) + nType->script = strValue; + + for(xmlNodePtr q = node->children; q; q = q->next) + { + if(!xmlStrcmp(q->name, (const xmlChar*)"look")) + { + int32_t intValue; + if(readXMLInteger(q, "type", intValue)) + { + nType->outfit.lookType = intValue; + if(readXMLInteger(q, "head", intValue)) + nType->outfit.lookHead = intValue; + + if(readXMLInteger(q, "body", intValue)) + nType->outfit.lookBody = intValue; + + if(readXMLInteger(q, "legs", intValue)) + nType->outfit.lookLegs = intValue; + + if(readXMLInteger(q, "feet", intValue)) + nType->outfit.lookFeet = intValue; + + if(readXMLInteger(q, "addons", intValue)) + nType->outfit.lookAddons = intValue; + + if(readXMLInteger(q, "mount", intValue)) + nType->outfit.lookMount = intValue; + } + else if(readXMLInteger(q, "typeex", intValue)) + nType->outfit.lookTypeEx = intValue; + } + } + + if(new_nType) + data[name] = nType; + + return true; +} void Npcs::reload() { @@ -51,13 +170,43 @@ void Npcs::reload() delete Npc::m_interface; Npc::m_interface = NULL; + + if(fileExists(getFilePath(FILE_TYPE_OTHER, "npc/npcs.xml").c_str())) + { + DataMap tmp = data; + if(!loadFromXml()) + data = tmp; + + tmp.clear(); + } + for(AutoList::iterator it = Npc::autoList.begin(); it != Npc::autoList.end(); ++it) it->second->reload(); } -Npc* Npc::createNpc(const std::string& name) +NpcType* Npcs::getType(const std::string& name) const +{ + DataMap::const_iterator it = data.find(asLowerCaseString(name)); + if(it == data.end()) + return NULL; + + return it->second; +} + +bool Npcs::setType(std::string name, NpcType* nType) { - Npc* npc = new Npc(name); + toLowerCaseString(name); + DataMap::const_iterator it = data.find(name); + if(it != data.end()) + return false; + + data[name] = nType; + return true; +} + +Npc* Npc::createNpc(NpcType* nType) +{ + Npc* npc = new Npc(nType); if(!npc) return NULL; @@ -68,47 +217,47 @@ Npc* Npc::createNpc(const std::string& name) return NULL; } -Npc::Npc(const std::string& _name): - Creature() +Npc* Npc::createNpc(const std::string& name) { -#ifdef __ENABLE_SERVER_DIAGNOSTIC__ - npcCount++; -#endif - m_filename = getFilePath(FILE_TYPE_OTHER, "npc/" + _name + ".xml"); - if(!fileExists(m_filename.c_str())) + NpcType* nType = NULL; + if(!(nType = g_npcs.getType(name))) { - std::string tmp = getFilePath(FILE_TYPE_MOD, "npc/" + _name + ".xml"); - if(fileExists(tmp.c_str())) - m_filename = tmp; + nType = new NpcType(); + nType->file = getFilePath(FILE_TYPE_OTHER, "npc/" + name + ".xml"); + if(!fileExists(nType->file.c_str())) + { + nType->file = getFilePath(FILE_TYPE_MOD, "npc/" + name + ".xml"); + if(!fileExists(nType->file.c_str())) + { + std::clog << "[Warning - Npc::createNpc] Cannot find npc with name: " << name << "." << std::endl; + return NULL; + } + } + + nType->name = name; + g_npcs.setType(name, nType); } - m_npcEventHandler = NULL; - loaded = false; - reset(); + return createNpc(nType); } -Npc::~Npc() +Npc::Npc(NpcType* _nType) : Creature(), m_npcEventHandler(NULL) { - reset(); #ifdef __ENABLE_SERVER_DIAGNOSTIC__ - npcCount--; + ++npcCount; #endif + nType = _nType; + + m_npcEventHandler = NULL; + reset(); } -bool Npc::load() +Npc::~Npc() { - if(isLoaded()) - return true; - reset(); - if(!m_interface) - { - m_interface = new NpcScriptInterface(); - m_interface->loadNpcLib(getFilePath(FILE_TYPE_OTHER, "npc/lib/npc.lua")); - } - - loaded = loadFromXml(m_filename); - return isLoaded(); +#ifdef __ENABLE_SERVER_DIAGNOSTIC__ + --npcCount; +#endif } void Npc::reset() @@ -117,6 +266,7 @@ void Npc::reset() walkTicks = 1500; floorChange = false; attackable = false; + walkable = false; hasBusyReply = false; hasScriptedFocus = false; focusCreature = 0; @@ -126,8 +276,10 @@ void Npc::reset() idleInterval = 5 * 60; lastVoice = OTSYS_TIME(); defaultPublic = true; + baseDirection = SOUTH; + if(m_npcEventHandler) + delete m_npcEventHandler; - delete m_npcEventHandler; m_npcEventHandler = NULL; for(ResponseList::iterator it = responseList.begin(); it != responseList.end(); ++it) delete *it; @@ -145,6 +297,28 @@ void Npc::reset() voiceList.clear(); } +bool Npc::load() +{ + if(isLoaded()) + return true; + + if(nType->file.empty()) + { + std::clog << "[Warning - Npc::load] Cannot load npc with name: " << nType->name << "." << std::endl; + return false; + } + + if(!m_interface) + { + m_interface = new NpcScript(); + m_interface->loadDirectory(getFilePath(FILE_TYPE_OTHER, "npc/lib"), false, true, this); + } + + loaded = loadFromXml(); + defaultOutfit = currentOutfit = nType->outfit; + return isLoaded(); +} + void Npc::reload() { reset(); @@ -153,41 +327,43 @@ void Npc::reload() if(m_npcEventHandler) m_npcEventHandler->onCreatureAppear(this); - if(walkTicks > 0) + if(walkTicks) addEventWalk(); } -bool Npc::loadFromXml(const std::string& filename) +bool Npc::loadFromXml() { - xmlDocPtr doc = xmlParseFile(filename.c_str()); + xmlDocPtr doc = xmlParseFile(nType->file.c_str()); if(!doc) { - std::cout << "[Warning - Npc::loadFromXml] Cannot load npc file (" << filename << ")." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Npc::loadFromXml] Cannot load npc file: " << nType->file << "." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr p, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"npc")) { - std::cout << "[Error - Npc::loadFromXml] Malformed npc file (" << filename << ")." << std::endl; + std::clog << "[Warning - Npc::loadFromXml] Malformed npc file: " << nType->file << "." << std::endl; xmlFreeDoc(doc); return false; } int32_t intValue; std::string strValue; + if(readXMLString(root, "name", strValue)) + nType->name = strValue; - std::string scriptfile = ""; if(readXMLString(root, "script", strValue)) - scriptfile = strValue; + nType->script = strValue; - if(readXMLString(root, "name", strValue)) - name = strValue; - - nameDescription = name; if(readXMLString(root, "namedescription", strValue) || readXMLString(root, "nameDescription", strValue)) - nameDescription = strValue; + nType->nameDescription = strValue; + + if(!nType->nameDescription.empty()) + replaceString(nType->nameDescription, "|NAME|", nType->name); + else + nType->nameDescription = nType->name; if(readXMLString(root, "hidename", strValue) || readXMLString(root, "hideName", strValue)) hideName = booleanString(strValue); @@ -206,63 +382,32 @@ bool Npc::loadFromXml(const std::string& filename) walkable = booleanString(strValue); if(readXMLInteger(root, "autowalk", intValue)) - { - std::cout << "[Notice - Npc::Npc] NPC Name: " << name << " - autowalk has been deprecated, use walkinterval." << std::endl; - walkTicks = 2000; - } + std::clog << "[Notice - Npc::Npc] NPC: " << nType->name << " - autowalk attribute has been deprecated, use walkinterval instead." << std::endl; if(readXMLInteger(root, "walkinterval", intValue)) walkTicks = intValue; + if(readXMLInteger(root, "direction", intValue) && intValue >= NORTH && intValue <= WEST) + { + direction = (Direction)intValue; + baseDirection = direction; + } + if(readXMLString(root, "floorchange", strValue)) floorChange = booleanString(strValue); if(readXMLString(root, "skull", strValue)) - { - std::string tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "red" || tmpStrValue == "4") - setSkull(SKULL_RED); - else if(tmpStrValue == "white" || tmpStrValue == "3") - setSkull(SKULL_WHITE); - else if(tmpStrValue == "green" || tmpStrValue == "2") - setSkull(SKULL_GREEN); - else if(tmpStrValue == "yellow" || tmpStrValue == "1") - setSkull(SKULL_YELLOW); - else - setSkull(SKULL_NONE); - } + setSkull(getSkulls(strValue)); if(readXMLString(root, "shield", strValue)) - { - std::string tmpStrValue = asLowerCaseString(strValue); - if(tmpStrValue == "whitenoshareoff" || tmpStrValue == "10") - setShield(SHIELD_YELLOW_NOSHAREDEXP); - else if(tmpStrValue == "blueshareoff" || tmpStrValue == "9") - setShield(SHIELD_BLUE_NOSHAREDEXP); - else if(tmpStrValue == "yellowshareblink" || tmpStrValue == "8") - setShield(SHIELD_YELLOW_NOSHAREDEXP_BLINK); - else if(tmpStrValue == "blueshareblink" || tmpStrValue == "7") - setShield(SHIELD_BLUE_NOSHAREDEXP_BLINK); - else if(tmpStrValue == "yellowshareon" || tmpStrValue == "6") - setShield(SHIELD_YELLOW_SHAREDEXP); - else if(tmpStrValue == "blueshareon" || tmpStrValue == "5") - setShield(SHIELD_BLUE_SHAREDEXP); - else if(tmpStrValue == "yellow" || tmpStrValue == "4") - setShield(SHIELD_YELLOW); - else if(tmpStrValue == "blue" || tmpStrValue == "3") - setShield(SHIELD_BLUE); - else if(tmpStrValue == "whiteyellow" || tmpStrValue == "2") - setShield(SHIELD_WHITEYELLOW); - else if(tmpStrValue == "whiteblue" || tmpStrValue == "1") - setShield(SHIELD_WHITEBLUE); - else - setShield(SHIELD_NONE); - } + setShield(getShields(strValue)); + + if(readXMLString(root, "emblem", strValue)) + setEmblem(getEmblems(strValue)); - p = root->children; - while(p) + for(xmlNodePtr p = root->children; p; p = p->next) { - if(xmlStrcmp(p->name, (const xmlChar*)"health") == 0) + if(!xmlStrcmp(p->name, (const xmlChar*)"health")) { if(readXMLInteger(p, "now", intValue)) health = intValue; @@ -274,36 +419,37 @@ bool Npc::loadFromXml(const std::string& filename) else healthMax = 100; } - else if(xmlStrcmp(p->name, (const xmlChar*)"look") == 0) + else if(!xmlStrcmp(p->name, (const xmlChar*)"look")) { if(readXMLInteger(p, "type", intValue)) { - defaultOutfit.lookType = intValue; + nType->outfit.lookType = intValue; if(readXMLInteger(p, "head", intValue)) - defaultOutfit.lookHead = intValue; + nType->outfit.lookHead = intValue; if(readXMLInteger(p, "body", intValue)) - defaultOutfit.lookBody = intValue; + nType->outfit.lookBody = intValue; if(readXMLInteger(p, "legs", intValue)) - defaultOutfit.lookLegs = intValue; + nType->outfit.lookLegs = intValue; if(readXMLInteger(p, "feet", intValue)) - defaultOutfit.lookFeet = intValue; + nType->outfit.lookFeet = intValue; if(readXMLInteger(p, "addons", intValue)) - defaultOutfit.lookAddons = intValue; + nType->outfit.lookAddons = intValue; + + if(readXMLInteger(p, "mount", intValue)) + nType->outfit.lookMount = intValue; } else if(readXMLInteger(p, "typeex", intValue)) - defaultOutfit.lookTypeEx = intValue; - - currentOutfit = defaultOutfit; + nType->outfit.lookTypeEx = intValue; } - else if(xmlStrcmp(p->name, (const xmlChar*)"voices") == 0) + else if(!xmlStrcmp(p->name, (const xmlChar*)"voices")) { for(xmlNodePtr q = p->children; q != NULL; q = q->next) { - if(xmlStrcmp(q->name, (const xmlChar*)"voice") == 0) + if(!xmlStrcmp(q->name, (const xmlChar*)"voice")) { if(!readXMLString(q, "text", strValue)) continue; @@ -320,11 +466,11 @@ bool Npc::loadFromXml(const std::string& filename) else voice.margin = 0; - voice.type = SPEAK_SAY; + voice.type = MSG_SPEAK_SAY; if(readXMLInteger(q, "type", intValue)) - voice.type = (SpeakClasses)intValue; + voice.type = (MessageClasses)intValue; else if(readXMLString(q, "yell", strValue) && booleanString(strValue)) - voice.type = SPEAK_YELL; + voice.type = MSG_SPEAK_YELL; if(readXMLString(q, "randomspectator", strValue) || readXMLString(q, "randomSpectator", strValue)) voice.randomSpectator = booleanString(strValue); @@ -335,11 +481,11 @@ bool Npc::loadFromXml(const std::string& filename) } } } - else if(xmlStrcmp(p->name, (const xmlChar*)"parameters") == 0) + else if(!xmlStrcmp(p->name, (const xmlChar*)"parameters")) { for(xmlNodePtr q = p->children; q != NULL; q = q->next) { - if(xmlStrcmp(q->name, (const xmlChar*)"parameter") == 0) + if(!xmlStrcmp(q->name, (const xmlChar*)"parameter")) { std::string paramKey, paramValue; if(!readXMLString(q, "key", paramKey)) @@ -352,7 +498,7 @@ bool Npc::loadFromXml(const std::string& filename) } } } - else if(xmlStrcmp(p->name, (const xmlChar*)"interaction") == 0) + else if(!xmlStrcmp(p->name, (const xmlChar*)"interaction")) { if(readXMLInteger(p, "talkradius", intValue)) talkRadius = intValue; @@ -366,24 +512,27 @@ bool Npc::loadFromXml(const std::string& filename) if(readXMLInteger(p, "defaultpublic", intValue)) defaultPublic = intValue != 0; - responseList = loadInteraction(p->children); + responseList = parseInteractionNode(p->children); } - - p = p->next; } xmlFreeDoc(doc); - if(scriptfile.empty()) + if(nType->script.empty()) return true; - if(scriptfile.find("/") == std::string::npos) - scriptfile = getFilePath(FILE_TYPE_OTHER, "npc/scripts/" + scriptfile); + if(nType->script.find("/") != std::string::npos) + { + replaceString(nType->script, "|DATA|", getFilePath(FILE_TYPE_OTHER, "npc/scripts")); + replaceString(nType->script, "|MODS|", getFilePath(FILE_TYPE_MOD, "scripts")); + } + else + nType->script = getFilePath(FILE_TYPE_OTHER, "npc/scripts/" + nType->script); - m_npcEventHandler = new NpcScript(scriptfile, this); + m_npcEventHandler = new NpcEvents(nType->script, this); return m_npcEventHandler->isLoaded(); } -uint32_t Npc::loadParams(xmlNodePtr node) +uint32_t Npc::parseParamsNode(xmlNodePtr node) { std::string strValue; uint32_t params = RESPOND_DEFAULT; @@ -418,22 +567,22 @@ uint32_t Npc::loadParams(xmlNodePtr node) else if(tmpParam == "lowlevel") params |= RESPOND_LOWLEVEL; else - std::cout << "[Warning - Npc::loadParams] NPC Name: " << name << " - Unknown param " << (*it) << std::endl; + std::clog << "[Warning - Npc::parseParamsNode] NPC Name: " << nType->name << " - Unknown param " << (*it) << std::endl; } } return params; } -ResponseList Npc::loadInteraction(xmlNodePtr node) +ResponseList Npc::parseInteractionNode(xmlNodePtr node) { std::string strValue; int32_t intValue; ResponseList _responseList; - while(node) + for(; node; node = node->next) { - if(xmlStrcmp(node->name, (const xmlChar*)"include") == 0) + if(!xmlStrcmp(node->name, (const xmlChar*)"include")) { if(readXMLString(node, "file", strValue)) { @@ -442,18 +591,18 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) xmlNodePtr root = xmlDocGetRootElement(doc); if(!xmlStrcmp(root->name,(const xmlChar*)"interaction")) { - ResponseList includedResponses = loadInteraction(root->children); + ResponseList includedResponses = parseInteractionNode(root->children); _responseList.insert(_responseList.end(), includedResponses.begin(), includedResponses.end()); } else - std::cout << "[Error - Npc::loadInteraction] Malformed interaction file (" << strValue << ")." << std::endl; + std::clog << "[Error - Npc::parseInteractionNode] Malformed interaction file (" << strValue << ")." << std::endl; xmlFreeDoc(doc); } else { - std::cout << "[Warning - Npc::loadInteraction] Cannot load interaction file (" << strValue << ")." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Npc::parseInteractionNode] Cannot load interaction file (" << strValue << ")." << std::endl; + std::clog << getLastXMLError() << std::endl; } } } @@ -475,12 +624,14 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) ListItem li; if(!readXMLInteger(tmpNode, "id", intValue)) { - std::cout << "[Warning - Npc::loadInteraction] NPC Name: " << name << " - Missing list item itemId" << std::endl; + std::clog << "[Warning - Npc::parseInteractionNode] NPC Name: " << nType->name << " - Missing list item itemId" << std::endl; tmpNode = tmpNode->next; continue; } li.itemId = intValue; + const ItemType& it = Item::items[li.itemId]; + if(readXMLInteger(tmpNode, "sellprice", intValue)) li.sellPrice = intValue; @@ -491,7 +642,7 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) li.keywords = strValue; else { - std::cout << "[Warning - Npc::loadInteraction] NPC Name: " << name << " - Missing list item keywords" << std::endl; + std::clog << "[Warning - Npc::parseInteractionNode] NPC Name: " << nType->name << " - Missing list item keywords" << std::endl; tmpNode = tmpNode->next; continue; } @@ -499,6 +650,13 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) //optional if(readXMLInteger(tmpNode, "subtype", intValue)) li.subType = intValue; + else + { + if(it.stackable) + li.subType = 1; + else if(it.isFluidContainer() || it.isSplash()) + li.subType = 0; + } if(readXMLString(tmpNode, "name", strValue)) li.name = strValue; @@ -513,7 +671,7 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) } } else - std::cout << "[Warning - Npc::loadInteraction] NPC Name: " << name << " - Duplicate listId found: " << strValue << std::endl; + std::clog << "[Warning - Npc::parseInteractionNode] NPC Name: " << nType->name << " - Duplicate listId found: " << strValue << std::endl; } } else if(!xmlStrcmp(node->name, (const xmlChar*)"interact")) @@ -538,13 +696,13 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) if(readXMLInteger(node, "focus", intValue)) prop.focusStatus = intValue; - if(readXMLInteger(node, "storageId", intValue)) - prop.storageId = intValue; + if(readXMLString(node, "storageId", strValue)) + prop.storageId = strValue; if(readXMLString(node, "storageValue", strValue)) prop.storageValue = strValue; - uint32_t interactParams = loadParams(node); + uint32_t interactParams = parseParamsNode(node); if(readXMLString(node, "storageComp", strValue)) { std::string tmpStrValue = asLowerCaseString(strValue); @@ -592,7 +750,7 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) if(it != itemListMap.end()) prop.itemList.insert(prop.itemList.end(), it->second.begin(), it->second.end()); else - std::cout << "[Warning - Npc::loadInteraction] NPC Name: " << name << " - Could not find a list id called: " << strValue << std::endl; + std::clog << "[Warning - Npc::parseInteractionNode] NPC Name: " << nType->name << " - Could not find a list id called: " << strValue << std::endl; } } @@ -603,13 +761,12 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) tmpNode = tmpNode->next; } - tmpNode = node->children; - while(tmpNode) + for(tmpNode = node->children; tmpNode; tmpNode = tmpNode->next) { if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"response")) { prop.output = prop.knowSpell = ""; - prop.params = interactParams | loadParams(tmpNode); + prop.params = interactParams | parseParamsNode(tmpNode); ScriptVars scriptVars; if(readXMLString(tmpNode, "knowspell", strValue)) @@ -639,8 +796,7 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) scriptVars.b3 = (intValue == 1); ResponseList subResponseList; - xmlNodePtr subNode = tmpNode->children; - while(subNode) + for(xmlNodePtr subNode = tmpNode->children; subNode; subNode = subNode->next) { if(!xmlStrcmp(subNode->name, (const xmlChar*)"action")) { @@ -697,7 +853,7 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) action.actionType = ACTION_SETSPELL; action.strValue = strValue; if(strValue != "|SPELL|" && !g_spells->getInstantSpellByName(strValue)) - std::cout << "[Warning - Npc::loadInteraction] NPC Name: " << name << " - Could not find an instant spell called: " << strValue << std::endl; + std::clog << "[Warning - Npc::parseInteractionNode] NPC Name: " << nType->name << " - Could not find an instant spell called: " << strValue << std::endl; } } else if(tmpStrValue == "listname") @@ -723,7 +879,7 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) action.actionType = ACTION_TEACHSPELL; action.strValue = strValue; if(strValue != "|SPELL|" && !g_spells->getInstantSpellByName(strValue)) - std::cout << "[Warning - Npc::loadInteraction] NPC Name: " << name << " - Could not find an instant spell called: " << strValue << std::endl; + std::clog << "[Warning - Npc::parseInteractionNode] NPC Name: " << nType->name << " - Could not find an instant spell called: " << strValue << std::endl; } } else if(tmpStrValue == "unteachspell") @@ -733,7 +889,7 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) action.actionType = ACTION_UNTEACHSPELL; action.strValue = strValue; if(strValue != "|SPELL|" && !g_spells->getInstantSpellByName(strValue)) - std::cout << "[Warning - Npc::loadInteraction] NPC Name: " << name << " - Could not find an instant spell called: " << strValue << std::endl; + std::clog << "[Warning - Npc::parseInteractionNode] NPC Name: " << nType->name << " - Could not find an instant spell called: " << strValue << std::endl; } } else if(tmpStrValue == "sell") @@ -872,11 +1028,11 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) } } else - std::cout << "[Warning - Npc::loadInteraction] Unknown action " << strValue << std::endl; + std::clog << "[Warning - Npc::parseInteractionNode] Unknown action " << strValue << std::endl; } - if(readXMLInteger(subNode, "key", intValue)) - action.key = intValue; + if(readXMLString(subNode, "key", strValue)) + action.key = strValue; if(action.actionType != ACTION_NONE) prop.actionList.push_back(action); @@ -885,13 +1041,11 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) { if(subResponseList.empty()) { - ResponseList nodeResponseList = loadInteraction(subNode); + ResponseList nodeResponseList = parseInteractionNode(subNode); subResponseList.insert(subResponseList.end(), nodeResponseList.begin(), nodeResponseList.end()); } } - - subNode = subNode->next; } //Check if this interaction has a |list| keyword @@ -984,18 +1138,14 @@ ResponseList Npc::loadInteraction(xmlNodePtr node) _responseList.push_back(response); } } - - tmpNode = tmpNode->next; } } - - node = node->next; } return _responseList; } -NpcState* Npc::getState(const Player* player, bool makeNew /*= true*/) +NpcState* Npc::getState(const Player* player, bool makeNew/* = true*/) { for(StateList::iterator it = stateList.begin(); it != stateList.end(); ++it) { @@ -1012,7 +1162,7 @@ NpcState* Npc::getState(const Player* player, bool makeNew /*= true*/) state->amount = 1; state->itemId = 0; state->subType = -1; - state->ignoreCap = state->inBackpacks = false; + state->ignore = state->inBackpacks = false; state->spellName = state->listName = ""; state->listPluralName = ""; state->level = state->topic = -1; @@ -1029,57 +1179,62 @@ NpcState* Npc::getState(const Player* player, bool makeNew /*= true*/) bool Npc::canSee(const Position& pos) const { - if(pos.z != getPosition().z) + Position tmp = getPosition(); + if(pos.z != tmp.z) return false; - return Creature::canSee(getPosition(), pos, Map::maxClientViewportX, Map::maxClientViewportY); + return Creature::canSee(tmp, pos, Map::maxClientViewportX, Map::maxClientViewportY); } void Npc::onCreatureAppear(const Creature* creature) { Creature::onCreatureAppear(creature); - if(creature == this && walkTicks > 0) - addEventWalk(); - if(creature == this) { + if(walkTicks) + addEventWalk(); + if(m_npcEventHandler) m_npcEventHandler->onCreatureAppear(creature); return; } + if(m_npcEventHandler) + m_npcEventHandler->onCreatureAppear(creature); + //only players for script events - if(Player* player = const_cast(creature->getPlayer())) - { - if(m_npcEventHandler) - m_npcEventHandler->onCreatureAppear(creature); + Player* player = const_cast(creature->getPlayer()); + if(!player) + return; - NpcState* npcState = getState(player); - if(npcState && canSee(player->getPosition())) - { - npcState->respondToCreature = player->getID(); - onPlayerEnter(player, npcState); - } + if(NpcState* npcState = getState(player)) + { + npcState->respondToCreature = player->getID(); + onPlayerEnter(player, npcState); } } void Npc::onCreatureDisappear(const Creature* creature, bool isLogout) { Creature::onCreatureDisappear(creature, isLogout); - if(creature == this) //Close all open shop window's - closeAllShopWindows(); - else if(Player* player = const_cast(creature->getPlayer())) + if(creature == this) { - if(m_npcEventHandler) - m_npcEventHandler->onCreatureDisappear(creature); + closeAllShopWindows(); + return; + } - NpcState* npcState = getState(player); - if(npcState) - { - npcState->respondToCreature = player->getID(); - onPlayerLeave(player, npcState); - } + if(m_npcEventHandler) + m_npcEventHandler->onCreatureDisappear(creature); + + Player* player = const_cast(creature->getPlayer()); + if(!player) + return; + + if(NpcState* npcState = getState(player)) + { + npcState->respondToCreature = player->getID(); + onPlayerLeave(player, npcState); } } @@ -1087,69 +1242,58 @@ void Npc::onCreatureMove(const Creature* creature, const Tile* newTile, const Po const Tile* oldTile, const Position& oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); - if(creature == this) - { - if(m_npcEventHandler) - m_npcEventHandler->onCreatureMove(creature, oldPos, newPos); - } - else if(Player* player = const_cast(creature->getPlayer())) - { - if(m_npcEventHandler) - m_npcEventHandler->onCreatureMove(creature, oldPos, newPos); + if(m_npcEventHandler) + m_npcEventHandler->onCreatureMove(creature, oldPos, newPos); + + Player* player = const_cast(creature->getPlayer()); + if(!player) + return; - NpcState* npcState = getState(player); - if(npcState) + if(NpcState* npcState = getState(player)) + { + bool canSeeNewPos = canSee(newPos), canSeeOldPos = canSee(oldPos); + if(canSeeNewPos && !canSeeOldPos) { - bool canSeeNewPos = canSee(newPos), canSeeOldPos = canSee(oldPos); - if(canSeeNewPos && !canSeeOldPos) - { - npcState->respondToCreature = player->getID(); - onPlayerEnter(player, npcState); - } - else if(!canSeeNewPos && canSeeOldPos) - { - npcState->respondToCreature = player->getID(); - onPlayerLeave(player, npcState); - } - else if(canSeeNewPos && canSeeOldPos) - { - npcState->respondToCreature = player->getID(); - const NpcResponse* response = getResponse(player, npcState, EVENT_PLAYER_MOVE); - executeResponse(player, npcState, response); - } + npcState->respondToCreature = player->getID(); + onPlayerEnter(player, npcState); + } + else if(!canSeeNewPos && canSeeOldPos) + { + npcState->respondToCreature = player->getID(); + onPlayerLeave(player, npcState); + } + else if(canSeeNewPos && canSeeOldPos) + { + npcState->respondToCreature = player->getID(); + const NpcResponse* response = getResponse(player, npcState, EVENT_PLAYER_MOVE); + executeResponse(player, npcState, response); } } } -void Npc::onCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, Position* pos/* = NULL*/) +void Npc::onCreatureSay(const Creature* creature, MessageClasses type, const std::string& text, Position* pos/* = NULL*/) { - if(creature->getID() == this->getID()) + if(m_npcEventHandler) + m_npcEventHandler->onCreatureSay(creature, type, text, pos); + + const Player* player = creature->getPlayer(); + if(!player) return; - //only players for script events - if(const Player* player = creature->getPlayer()) + if(type == MSG_SPEAK_SAY || type == MSG_NPC_TO) { - if(m_npcEventHandler) - m_npcEventHandler->onCreatureSay(player, type, text, pos); + Position destPos = creature->getPosition(); + if(pos) + destPos = (*pos); - if(type == SPEAK_SAY || type == SPEAK_PRIVATE_PN) + const Position& myPos = getPosition(); + if(canSee(myPos) && (destPos.x >= myPos.x - talkRadius) && (destPos.x <= myPos.x + talkRadius) + && (destPos.y >= myPos.y - talkRadius) && (destPos.y <= myPos.y + talkRadius)) { - Position destPos = creature->getPosition(); - if(pos) - destPos = (*pos); - - const Position& myPos = getPosition(); - if(canSee(myPos)) + if(NpcState* npcState = getState(player)) { - if((destPos.x >= myPos.x - talkRadius) && (destPos.x <= myPos.x + talkRadius) && - (destPos.y >= myPos.y - talkRadius) && (destPos.y <= myPos.y + talkRadius)) - { - if(NpcState* npcState = getState(player)) - { - npcState->respondToText = text; - npcState->respondToCreature = player->getID(); - } - } + npcState->respondToText = text; + npcState->respondToCreature = player->getID(); } } } @@ -1157,6 +1301,12 @@ void Npc::onCreatureSay(const Creature* creature, SpeakClasses type, const std:: void Npc::onPlayerCloseChannel(const Player* player) { + if(NpcState* npcState = getState(player, true)) + { + const NpcResponse* response = getResponse(player, npcState, EVENT_PLAYER_CHATCLOSE); + executeResponse(const_cast(player), npcState, response); + } + if(m_npcEventHandler) m_npcEventHandler->onPlayerCloseChannel(player); } @@ -1169,7 +1319,9 @@ void Npc::onPlayerEnter(Player* player, NpcState* state) void Npc::onPlayerLeave(Player* player, NpcState* state) { - player->closeShopWindow(); + if(player->getShopOwner() == this) + player->closeShopWindow(); + const NpcResponse* response = getResponse(player, state, EVENT_PLAYER_LEAVE); executeResponse(player, state, response); } @@ -1222,14 +1374,17 @@ void Npc::onThink(uint32_t interval) if((uint32_t)(MAX_RAND_RANGE / idleInterval) >= (uint32_t)random_range(0, MAX_RAND_RANGE)) idleResponse = true; + if(getTimeSinceLastMove() >= walkTicks) + addEventWalk(); + isIdle = true; for(StateList::iterator it = stateList.begin(); it != stateList.end();) { NpcState* npcState = *it; - Player* player = g_game.getPlayerByID(npcState->respondToCreature); - const NpcResponse* response = NULL; bool closeConversation = false, idleTimeout = false; + + Player* player = g_game.getPlayerByID(npcState->respondToCreature); if(!npcState->isQueued) { if(!npcState->prevInteraction) @@ -1257,15 +1412,15 @@ void Npc::onThink(uint32_t interval) } else { - Player* nextPlayer = NULL; + Player* tmpPlayer = NULL; while(!queueList.empty()) { - if((nextPlayer = g_game.getPlayerByID(*queueList.begin()))) + if((tmpPlayer = g_game.getPlayerByID(*queueList.begin()))) { - if(NpcState* nextPlayerState = getState(nextPlayer, false)) + if(NpcState* tmpPlayerState = getState(tmpPlayer, false)) { - nextPlayerState->respondToText = nextPlayerState->prevRespondToText; - nextPlayerState->isQueued = false; + tmpPlayerState->respondToText = tmpPlayerState->prevRespondToText; + tmpPlayerState->isQueued = false; break; } } @@ -1275,7 +1430,7 @@ void Npc::onThink(uint32_t interval) } delete *it; - stateList.erase(it++); + it = stateList.erase(it); continue; } @@ -1331,7 +1486,7 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* if(response) { npcState->lastResponse = response; - npcState->isIdle = response->getFocusState() == 0; + npcState->isIdle = !response->getFocusState(); bool resetTopic = true; if(response->getAmount() != -1) @@ -1378,13 +1533,13 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* if(it->strValue == "|TEMPLE|") teleportTo = player->getMasterPosition(); - g_game.internalTeleport(player, teleportTo, true); + g_game.internalTeleport(player, teleportTo, false); break; } case ACTION_SETIDLE: { - npcState->isIdle = (it->intValue == 1); + npcState->isIdle = (it->intValue != 0); break; } @@ -1455,7 +1610,7 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* case ACTION_SETSTORAGE: { - if(it->key > 0) + if(!it->key.empty()) player->setStorage(it->key, it->strValue); break; @@ -1500,7 +1655,7 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* const ItemType& iit = Item::items[npcState->itemId]; if(iit.id != 0) { - uint32_t moneyCount = it->intValue; + uint64_t moneyCount = it->intValue; if(it->strValue == "|PRICE|") moneyCount = npcState->price * npcState->amount; @@ -1619,7 +1774,8 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* case ACTION_SCRIPT: { - NpcScriptInterface interface; + NpcScript interface; + interface.loadDirectory(getFilePath(FILE_TYPE_OTHER, "npc/lib"), false, false, this); if(interface.reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); @@ -1652,7 +1808,9 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* scriptstream << "topic = " << npcState->topic << ',' << std::endl; scriptstream << "itemid = " << npcState->itemId << ',' << std::endl; scriptstream << "subtype = " << npcState->subType << ',' << std::endl; - scriptstream << "ignorecap = " << npcState->ignoreCap << ',' << std::endl; + scriptstream << "ignore = " << npcState->ignore << ',' << std::endl; + scriptstream << "ignorecapacity = ignore," << std::endl; + scriptstream << "ignoreequipped = ignore," << std::endl; scriptstream << "inbackpacks = " << npcState->inBackpacks << ',' << std::endl; scriptstream << "amount = " << npcState->amount << ',' << std::endl; scriptstream << "price = " << npcState->price << ',' << std::endl; @@ -1679,7 +1837,7 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* { lua_State* L = interface.getState(); lua_getglobal(L, "_state"); - NpcScriptInterface::popState(L, npcState); + NpcScript::popState(L, npcState); } interface.releaseEnv(); @@ -1699,9 +1857,9 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* if(!responseString.empty()) { if(!response->publicize()) - doSay(responseString, SPEAK_PRIVATE_NP, player); + doSay(responseString, MSG_NPC_FROM, player); else - doSay(responseString, SPEAK_SAY, NULL); + doSay(responseString, MSG_SPEAK_SAY, NULL); } } else @@ -1724,11 +1882,12 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* lua_State* L = m_interface->getState(); env->setScriptId(functionId, m_interface); - Npc* prevNpc = env->getNpc(); env->setRealPos(getPosition()); - env->setNpc(this); + Npc* prevNpc = env->getNpc(); + env->setNpc(this); m_interface->pushFunction(functionId); + int32_t paramCount = 0; for(ActionList::const_iterator it = response->getFirstAction(); it != response->getEndAction(); ++it) { @@ -1740,7 +1899,7 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* lua_pushstring(L, npcState->respondToText.c_str()); else { - std::cout << "[Warning - Npc::executeResponse] Unknown script param: " << it->strValue << std::endl; + std::clog << "[Warning - Npc::executeResponse] Unknown script param: " << it->strValue << std::endl; break; } @@ -1748,12 +1907,12 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* } } - NpcScriptInterface::pushState(L, npcState); + NpcScript::pushState(L, npcState); lua_setglobal(L, "_state"); m_interface->callFunction(paramCount); lua_getglobal(L, "_state"); - NpcScriptInterface::popState(L, npcState); + NpcScript::popState(L, npcState); if(prevNpc) { env->setRealPos(prevNpc->getPosition()); @@ -1763,7 +1922,7 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* m_interface->releaseEnv(); } else - std::cout << "[Error] Call stack overflow." << std::endl; + std::clog << "[Error] Call stack overflow." << std::endl; } } @@ -1774,15 +1933,14 @@ void Npc::executeResponse(Player* player, NpcState* npcState, const NpcResponse* } } -void Npc::doSay(const std::string& text, SpeakClasses type, Player* player) +void Npc::doSay(const std::string& text, MessageClasses type, Player* player) { if(!player) { std::string tmp = text; replaceString(tmp, "{", ""); replaceString(tmp, "}", ""); - - g_game.internalCreatureSay(this, type, tmp, false); + g_game.internalCreatureSay(this, type, tmp, player && player->isGhost()); } else { @@ -1791,39 +1949,22 @@ void Npc::doSay(const std::string& text, SpeakClasses type, Player* player) } } -void Npc::doTurn(Direction dir) -{ - g_game.internalCreatureTurn(this, dir); -} - -void Npc::doMove(Direction dir) -{ - g_game.internalMoveCreature(this, dir); -} - -void Npc::doMoveTo(Position target) -{ - std::list listDir; - if(!g_game.getPathToEx(this, target, listDir, 1, 1, true, true)) - return; - - startAutoWalk(listDir); -} - uint32_t Npc::getListItemPrice(uint16_t itemId, ShopEvent_t type) { + std::list itemList; for(ItemListMap::iterator it = itemListMap.begin(); it != itemListMap.end(); ++it) { - std::list& itemList = it->second; + itemList = it->second; for(std::list::iterator iit = itemList.begin(); iit != itemList.end(); ++iit) { - if((*iit).itemId == itemId) - { - if(type == SHOPEVENT_BUY) - return (*iit).buyPrice; - else if(type == SHOPEVENT_SELL) - return (*iit).sellPrice; - } + if(iit->itemId != itemId) + continue; + + if(type == SHOPEVENT_BUY) + return iit->buyPrice; + + if(type == SHOPEVENT_SELL) + return iit->sellPrice; } } @@ -1831,7 +1972,7 @@ uint32_t Npc::getListItemPrice(uint16_t itemId, ShopEvent_t type) } void Npc::onPlayerTrade(Player* player, ShopEvent_t type, int32_t callback, uint16_t itemId, uint8_t count, - uint8_t amount, bool ignoreCap/* = false*/, bool inBackpacks/* = false*/) + uint8_t amount, bool ignore/* = false*/, bool inBackpacks/* = false*/) { if(type == SHOPEVENT_BUY) { @@ -1841,7 +1982,7 @@ void Npc::onPlayerTrade(Player* player, ShopEvent_t type, int32_t callback, uint npcState->subType = count; npcState->itemId = itemId; npcState->buyPrice = getListItemPrice(itemId, SHOPEVENT_BUY); - npcState->ignoreCap = ignoreCap; + npcState->ignore = ignore; npcState->inBackpacks = inBackpacks; const NpcResponse* response = getResponse(player, npcState, EVENT_PLAYER_SHOPBUY); @@ -1856,6 +1997,7 @@ void Npc::onPlayerTrade(Player* player, ShopEvent_t type, int32_t callback, uint npcState->subType = count; npcState->itemId = itemId; npcState->sellPrice = getListItemPrice(itemId, SHOPEVENT_SELL); + npcState->ignore = ignore; const NpcResponse* response = getResponse(player, npcState, EVENT_PLAYER_SHOPSELL); executeResponse(player, npcState, response); @@ -1863,13 +2005,12 @@ void Npc::onPlayerTrade(Player* player, ShopEvent_t type, int32_t callback, uint } if(m_npcEventHandler) - m_npcEventHandler->onPlayerTrade(player, callback, itemId, count, amount, ignoreCap, inBackpacks); + m_npcEventHandler->onPlayerTrade(player, callback, itemId, count, amount, ignore, inBackpacks); player->sendGoods(); } -void Npc::onPlayerEndTrade(Player* player, int32_t buyCallback, - int32_t sellCallback) +void Npc::onPlayerEndTrade(Player* player, int32_t buyCallback, int32_t sellCallback) { lua_State* L = getInterface()->getState(); if(buyCallback != -1) @@ -1878,8 +2019,7 @@ void Npc::onPlayerEndTrade(Player* player, int32_t buyCallback, luaL_unref(L, LUA_REGISTRYINDEX, sellCallback); removeShopPlayer(player); - NpcState* npcState = getState(player, true); - if(npcState) + if(NpcState* npcState = getState(player, true)) { const NpcResponse* response = getResponse(player, npcState, EVENT_PLAYER_SHOPCLOSE); executeResponse(player, npcState, response); @@ -1894,7 +2034,7 @@ bool Npc::getNextStep(Direction& dir, uint32_t& flags) if(Creature::getNextStep(dir, flags)) return true; - if(walkTicks <= 0 || !isIdle || focusCreature || getTimeSinceLastMove() < walkTicks) + if(!walkTicks || !isIdle || focusCreature || getTimeSinceLastMove() < walkTicks) return false; return getRandomStep(dir); @@ -1902,42 +2042,29 @@ bool Npc::getNextStep(Direction& dir, uint32_t& flags) bool Npc::canWalkTo(const Position& fromPos, Direction dir) { - if(getNoMove()) + if(cannotMove) return false; - Position toPos = fromPos; - toPos = getNextPosition(dir, toPos); + Position toPos = getNextPosition(dir, fromPos); if(!Spawns::getInstance()->isInZone(masterPosition, masterRadius, toPos)) return false; Tile* tile = g_game.getTile(toPos); - if(!tile) - return true; - - if(getTile()->isSwimmingPool() != tile->isSwimmingPool()) // prevent npc entering/exiting to swimming pool + if(!tile || g_game.isSwimmingPool(NULL, getTile(), false) != g_game.isSwimmingPool(NULL, tile, + false) || (!floorChange && (tile->floorChange() || tile->positionChange()))) return false; - if(floorChange && (tile->floorChange() || tile->positionChange())) - return true; - return tile->__queryAdd(0, this, 1, FLAG_PATHFINDING) == RET_NOERROR; } bool Npc::getRandomStep(Direction& dir) { std::vector dirList; - const Position& creaturePos = getPosition(); - if(canWalkTo(creaturePos, NORTH)) - dirList.push_back(NORTH); - - if(canWalkTo(creaturePos, SOUTH)) - dirList.push_back(SOUTH); - - if(canWalkTo(creaturePos, EAST)) - dirList.push_back(EAST); - - if(canWalkTo(creaturePos, WEST)) - dirList.push_back(WEST); + for(int32_t i = NORTH; i < SOUTHWEST; ++i) + { + if(canWalkTo(getPosition(), (Direction)i)) + dirList.push_back((Direction)i); + } if(dirList.empty()) return false; @@ -1951,13 +2078,15 @@ void Npc::setCreatureFocus(Creature* creature) { if(!creature) { + if(!walkTicks) + g_game.internalCreatureTurn(this, baseDirection); + focusCreature = 0; return; } - const Position& creaturePos = creature->getPosition(); - const Position& myPos = getPosition(); - int32_t dx = myPos.x - creaturePos.x, dy = myPos.y - creaturePos.y; + Position pos = creature->getPosition(), _pos = getPosition(); + int32_t dx = _pos.x - pos.x, dy = _pos.y - pos.y; float tan = 10; if(dx != 0) @@ -1974,8 +2103,8 @@ void Npc::setCreatureFocus(Creature* creature) else if(dy > 0) dir = NORTH; - focusCreature = creature->getID(); g_game.internalCreatureTurn(this, dir); + focusCreature = creature->getID(); } const NpcResponse* Npc::getResponse(const ResponseList& list, const Player* player, @@ -2027,7 +2156,7 @@ const NpcResponse* Npc::getResponse(const ResponseList& list, const Player* play if(hasBitSet(RESPOND_DRUID, params)) { Vocation* tmpVoc = player->vocation; - for(uint32_t i = 0; i <= player->promotionLevel; i++) + for(uint32_t i = 0; i <= player->promotionLevel; ++i) tmpVoc = Vocations::getInstance()->getVocation(tmpVoc->getFromVocation()); if(tmpVoc->getId() != 2) @@ -2039,7 +2168,7 @@ const NpcResponse* Npc::getResponse(const ResponseList& list, const Player* play if(hasBitSet(RESPOND_KNIGHT, params)) { Vocation* tmpVoc = player->vocation; - for(uint32_t i = 0; i <= player->promotionLevel; i++) + for(uint32_t i = 0; i <= player->promotionLevel; ++i) tmpVoc = Vocations::getInstance()->getVocation(tmpVoc->getFromVocation()); if(tmpVoc->getId() != 4) @@ -2051,7 +2180,7 @@ const NpcResponse* Npc::getResponse(const ResponseList& list, const Player* play if(hasBitSet(RESPOND_PALADIN, params)) { Vocation* tmpVoc = player->vocation; - for(uint32_t i = 0; i <= player->promotionLevel; i++) + for(uint32_t i = 0; i <= player->promotionLevel; ++i) tmpVoc = Vocations::getInstance()->getVocation(tmpVoc->getFromVocation()); if(tmpVoc->getId() != 3) @@ -2063,7 +2192,7 @@ const NpcResponse* Npc::getResponse(const ResponseList& list, const Player* play if(hasBitSet(RESPOND_SORCERER, params)) { Vocation* tmpVoc = player->vocation; - for(uint32_t i = 0; i <= player->promotionLevel; i++) + for(uint32_t i = 0; i <= player->promotionLevel; ++i) tmpVoc = Vocations::getInstance()->getVocation(tmpVoc->getFromVocation()); if(tmpVoc->getId() != 1) @@ -2082,7 +2211,7 @@ const NpcResponse* Npc::getResponse(const ResponseList& list, const Player* play if(hasBitSet(RESPOND_LOWMONEY, params)) { - if((signed)g_game.getMoney(player) >= (npcState->price * npcState->amount)) + if(g_game.getMoney(player) >= (uint64_t)(npcState->price * npcState->amount)) continue; ++matchCount; @@ -2147,7 +2276,7 @@ const NpcResponse* Npc::getResponse(const ResponseList& list, const Player* play ++matchCount; } - if((*it)->getStorageId() != -1) + if(!(*it)->getStorageId().empty()) { std::string value, storageValue = (*it)->getStorage(); player->getStorage((*it)->getStorageId(), value); @@ -2328,7 +2457,7 @@ const NpcResponse* Npc::getResponse(const Player* player, NpcState* npcState, co return getResponse(responseList, player, npcState, text); } -const NpcResponse* Npc::getResponse(const Player* player, NpcEvent_t eventType) +const NpcResponse* Npc::getResponse(const Player*, NpcEvent_t eventType) { std::string eventName = getEventResponseName(eventType); if(eventName.empty()) @@ -2337,11 +2466,11 @@ const NpcResponse* Npc::getResponse(const Player* player, NpcEvent_t eventType) std::vector result; for(ResponseList::const_iterator it = responseList.begin(); it != responseList.end(); ++it) { - if((*it)->getInteractType() == INTERACT_EVENT) - { - if((*it)->getInputText() == asLowerCaseString(eventName)) - result.push_back(*it); - } + if((*it)->getInteractType() != INTERACT_EVENT) + continue; + + if((*it)->getInputText() == asLowerCaseString(eventName)) + result.push_back(*it); } if(result.empty()) @@ -2381,6 +2510,8 @@ std::string Npc::getEventResponseName(NpcEvent_t eventType) return "onPlayerShopBuy"; case EVENT_PLAYER_SHOPCLOSE: return "onPlayerShopClose"; + case EVENT_PLAYER_CHATCLOSE: + return "onPlayerChatClose"; default: break; } @@ -2437,7 +2568,7 @@ std::string Npc::formatResponse(Creature* creature, const NpcState* npcState, co } replaceString(responseString, "|NAME|", creature->getName()); - replaceString(responseString, "|NPCNAME|", getName()); + replaceString(responseString, "|NPCNAME|", nType->name); return responseString; } @@ -2462,77 +2593,39 @@ void Npc::closeAllShopWindows() (*it)->closeShopWindow(); } -NpcScriptInterface* Npc::getInterface() +NpcScript* Npc::getInterface() { return m_interface; } -NpcScriptInterface::NpcScriptInterface() : - LuaScriptInterface("Npc interface") +NpcScript::NpcScript(): + LuaInterface("NpcScript Interface") { - m_libLoaded = false; initState(); } - -NpcScriptInterface::~NpcScriptInterface() -{ - // -} - -bool NpcScriptInterface::initState() +void NpcScript::registerFunctions() { - return LuaScriptInterface::initState(); -} - -bool NpcScriptInterface::closeState() -{ - m_libLoaded = false; - return LuaScriptInterface::closeState(); -} + LuaInterface::registerFunctions(); + lua_register(m_luaState, "selfFocus", NpcScript::luaActionFocus); + lua_register(m_luaState, "selfSay", NpcScript::luaActionSay); + lua_register(m_luaState, "selfFollow", NpcScript::luaActionFollow); -bool NpcScriptInterface::loadNpcLib(std::string file) -{ - if(m_libLoaded) - return true; + lua_register(m_luaState, "getNpcId", NpcScript::luaGetNpcId); + lua_register(m_luaState, "getNpcParameter", NpcScript::luaGetNpcParameter); - if(!loadFile(file)) - { - std::cout << "Warning: [NpcScriptInterface::loadNpcLib] Cannot load " << file << std::endl; - return false; - } + lua_register(m_luaState, "getNpcState", NpcScript::luaGetNpcState); + lua_register(m_luaState, "setNpcState", NpcScript::luaSetNpcState); - m_libLoaded = true; - return true; + lua_register(m_luaState, "openShopWindow", NpcScript::luaOpenShopWindow); + lua_register(m_luaState, "closeShopWindow", NpcScript::luaCloseShopWindow); + lua_register(m_luaState, "getShopOwner", NpcScript::luaGetShopOwner); } -void NpcScriptInterface::registerFunctions() -{ - LuaScriptInterface::registerFunctions(); - lua_register(m_luaState, "selfFocus", NpcScriptInterface::luaActionFocus); - lua_register(m_luaState, "selfSay", NpcScriptInterface::luaActionSay); - - lua_register(m_luaState, "selfTurn", NpcScriptInterface::luaActionTurn); - lua_register(m_luaState, "selfMove", NpcScriptInterface::luaActionMove); - lua_register(m_luaState, "selfMoveTo", NpcScriptInterface::luaActionMoveTo); - lua_register(m_luaState, "selfFollow", NpcScriptInterface::luaActionFollow); - - lua_register(m_luaState, "getNpcId", NpcScriptInterface::luaGetNpcId); - lua_register(m_luaState, "getNpcDistanceTo", NpcScriptInterface::luaGetNpcDistanceTo); - lua_register(m_luaState, "getNpcParameter", NpcScriptInterface::luaGetNpcParameter); - - lua_register(m_luaState, "getNpcState", NpcScriptInterface::luaGetNpcState); - lua_register(m_luaState, "setNpcState", NpcScriptInterface::luaSetNpcState); - - lua_register(m_luaState, "openShopWindow", NpcScriptInterface::luaOpenShopWindow); - lua_register(m_luaState, "closeShopWindow", NpcScriptInterface::luaCloseShopWindow); -} - -int32_t NpcScriptInterface::luaActionFocus(lua_State* L) +int32_t NpcScript::luaActionFocus(lua_State* L) { //selfFocus(cid) ScriptEnviroment* env = getEnv(); - Npc* npc = env->getNpc(); if(!npc) return 0; @@ -2547,13 +2640,13 @@ int32_t NpcScriptInterface::luaActionFocus(lua_State* L) return 0; } -int32_t NpcScriptInterface::luaActionSay(lua_State* L) +int32_t NpcScript::luaActionSay(lua_State* L) { //selfSay(words[, target[, type]]) int32_t params = lua_gettop(L), target = 0; - SpeakClasses type = SPEAK_CLASS_NONE; + MessageClasses type = MSG_NONE; if(params > 2) - type = (SpeakClasses)popNumber(L); + type = (MessageClasses)popNumber(L); if(params > 1) target = popNumber(L); @@ -2564,61 +2657,26 @@ int32_t NpcScriptInterface::luaActionSay(lua_State* L) return 0; Player* player = env->getPlayerByUID(target); - if(type == SPEAK_CLASS_NONE) + if(type == MSG_NONE) { if(player) - type = SPEAK_PRIVATE_NP; + type = MSG_NPC_FROM; else - type = SPEAK_SAY; + type = MSG_SPEAK_SAY; } - npc->doSay(popString(L), (SpeakClasses)type, player); - return 0; -} - -int32_t NpcScriptInterface::luaActionTurn(lua_State* L) -{ - //selfTurn(direction) - ScriptEnviroment* env = getEnv(); - if(Npc* npc = env->getNpc()) - npc->doTurn((Direction)popNumber(L)); - + npc->doSay(popString(L), (MessageClasses)type, player); return 0; } -int32_t NpcScriptInterface::luaActionMove(lua_State* L) -{ - //selfMove(direction) - ScriptEnviroment* env = getEnv(); - if(Npc* npc = env->getNpc()) - npc->doMove((Direction)popNumber(L)); - - return 0; -} - -int32_t NpcScriptInterface::luaActionMoveTo(lua_State* L) -{ - //selfMoveTo(x, y, z) - Position pos; - pos.z = (uint16_t)popNumber(L); - pos.y = (uint16_t)popNumber(L); - pos.x = (uint16_t)popNumber(L); - - ScriptEnviroment* env = getEnv(); - if(Npc* npc = env->getNpc()) - npc->doMoveTo(pos); - - return 0; -} - -int32_t NpcScriptInterface::luaActionFollow(lua_State* L) +int32_t NpcScript::luaActionFollow(lua_State* L) { //selfFollow(cid) uint32_t cid = popNumber(L); ScriptEnviroment* env = getEnv(); - Player* player = env->getPlayerByUID(cid); - if(cid && !player) + Creature* creature = env->getCreatureByUID(cid); + if(cid && !creature) { lua_pushboolean(L, false); return 1; @@ -2631,11 +2689,11 @@ int32_t NpcScriptInterface::luaActionFollow(lua_State* L) return 1; } - lua_pushboolean(L, npc->setFollowCreature(player, true)); + lua_pushboolean(L, npc->setFollowCreature(creature, true)); return 1; } -int32_t NpcScriptInterface::luaGetNpcId(lua_State* L) +int32_t NpcScript::luaGetNpcId(lua_State* L) { //getNpcId() ScriptEnviroment* env = getEnv(); @@ -2647,34 +2705,9 @@ int32_t NpcScriptInterface::luaGetNpcId(lua_State* L) return 1; } -int32_t NpcScriptInterface::luaGetNpcDistanceTo(lua_State* L) -{ - //getNpcDistanceTo(uid) - ScriptEnviroment* env = getEnv(); - Npc* npc = env->getNpc(); - - Thing* thing = env->getThingByUID(popNumber(L)); - if(thing && npc) - { - Position thingPos = thing->getPosition(); - Position npcPos = npc->getPosition(); - if(npcPos.z == thingPos.z) - lua_pushnumber(L, std::max(std::abs(npcPos.x - thingPos.x), std::abs(npcPos.y - thingPos.y))); - else - lua_pushnumber(L, -1); - } - else - { - errorEx(getError(LUA_ERROR_THING_NOT_FOUND)); - lua_pushnil(L); - } - - return 1; -} - -int32_t NpcScriptInterface::luaGetNpcParameter(lua_State* L) +int32_t NpcScript::luaGetNpcParameter(lua_State* L) { - //getNpcParameter(paramKey) + //getNpcParameter(key) ScriptEnviroment* env = getEnv(); if(Npc* npc = env->getNpc()) { @@ -2690,7 +2723,7 @@ int32_t NpcScriptInterface::luaGetNpcParameter(lua_State* L) return 1; } -int32_t NpcScriptInterface::luaGetNpcState(lua_State* L) +int32_t NpcScript::luaGetNpcState(lua_State* L) { //getNpcState(cid) ScriptEnviroment* env = getEnv(); @@ -2698,14 +2731,14 @@ int32_t NpcScriptInterface::luaGetNpcState(lua_State* L) const Player* player = env->getPlayerByUID(popNumber(L)); if(player && npc) - NpcScriptInterface::pushState(L, npc->getState(player)); + NpcScript::pushState(L, npc->getState(player)); else lua_pushnil(L); return 1; } -int32_t NpcScriptInterface::luaSetNpcState(lua_State* L) +int32_t NpcScript::luaSetNpcState(lua_State* L) { //setNpcState(state, cid) ScriptEnviroment* env = getEnv(); @@ -2715,7 +2748,7 @@ int32_t NpcScriptInterface::luaSetNpcState(lua_State* L) if(player && npc) { NpcState* tmp = npc->getState(player); - NpcScriptInterface::popState(L, tmp); + NpcScript::popState(L, tmp); lua_pushboolean(L, true); } else @@ -2724,14 +2757,16 @@ int32_t NpcScriptInterface::luaSetNpcState(lua_State* L) return 1; } -void NpcScriptInterface::pushState(lua_State* L, NpcState* state) +void NpcScript::pushState(lua_State* L, NpcState* state) { lua_newtable(L); setField(L, "price", state->price); setField(L, "amount", state->amount); setField(L, "itemid", state->itemId); setField(L, "subtype", state->subType); - setFieldBool(L, "ignorecap", state->ignoreCap); + setFieldBool(L, "ignore", state->ignore); + setFieldBool(L, "ignorecapacity", state->ignore); + setFieldBool(L, "ignoreequipped", state->ignore); setFieldBool(L, "inbackpacks", state->inBackpacks); setField(L, "topic", state->topic); setField(L, "level", state->level); @@ -2753,13 +2788,14 @@ void NpcScriptInterface::pushState(lua_State* L, NpcState* state) setField(L, "s3", state->scriptVars.s3); } -void NpcScriptInterface::popState(lua_State* L, NpcState* &state) +void NpcScript::popState(lua_State* L, NpcState* &state) { state->price = getField(L, "price"); state->amount = getField(L, "amount"); state->itemId = getField(L, "itemid"); state->subType = getField(L, "subtype"); - state->ignoreCap = getFieldBool(L, "ignorecap"); + state->ignore = getFieldBool(L, "ignore") || getFieldBool(L, + "ignorecapacity") || getFieldBool(L, "ignoreequipped"); state->inBackpacks = getFieldBool(L, "inbackpacks"); state->topic = getField(L, "topic"); state->level = getField(L, "level"); @@ -2781,7 +2817,7 @@ void NpcScriptInterface::popState(lua_State* L, NpcState* &state) state->scriptVars.s3 = getFieldString(L, "s3"); } -int32_t NpcScriptInterface::luaOpenShopWindow(lua_State* L) +int32_t NpcScript::luaOpenShopWindow(lua_State* L) { //openShopWindow(cid, items, onBuy callback, onSell callback) ScriptEnviroment* env = getEnv(); @@ -2794,20 +2830,20 @@ int32_t NpcScriptInterface::luaOpenShopWindow(lua_State* L) } int32_t sellCallback = -1; - if(lua_isfunction(L, -1) == 0) + if(!lua_isfunction(L, -1)) lua_pop(L, 1); // skip it - use default value else sellCallback = popCallback(L); int32_t buyCallback = -1; - if(lua_isfunction(L, -1) == 0) + if(!lua_isfunction(L, -1)) lua_pop(L, 1); else buyCallback = popCallback(L); if(!lua_istable(L, -1)) { - error(__FUNCTION__, "item list is not a table."); + error(__FUNCTION__, "Item list is not a table"); lua_pushboolean(L, false); return 1; } @@ -2836,21 +2872,26 @@ int32_t NpcScriptInterface::luaOpenShopWindow(lua_State* L) return 1; } - player->closeShopWindow(); + if(player->getShopOwner() == npc) + { + lua_pushboolean(L, true); + return 1; + } + + player->closeShopWindow(false); npc->addShopPlayer(player); player->setShopOwner(npc, buyCallback, sellCallback, itemList); - player->openShopWindow(); + player->openShopWindow(npc); lua_pushboolean(L, true); return 1; } -int32_t NpcScriptInterface::luaCloseShopWindow(lua_State* L) +int32_t NpcScript::luaCloseShopWindow(lua_State* L) { //closeShopWindow(cid) ScriptEnviroment* env = getEnv(); - Player* player = env->getPlayerByUID(popNumber(L)); if(!player) { @@ -2859,42 +2900,38 @@ int32_t NpcScriptInterface::luaCloseShopWindow(lua_State* L) return 1; } - Npc* npc = env->getNpc(); - if(!npc) + player->closeShopWindow(); + lua_pushboolean(L, true); + return 1; +} + +int32_t NpcScript::luaGetShopOwner(lua_State* L) +{ + //getShopOwner(cid) + ScriptEnviroment* env = getEnv(); + Player* player = env->getPlayerByUID(popNumber(L)); + if(!player) { - errorEx(getError(LUA_ERROR_CREATURE_NOT_FOUND)); + errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); return 1; } - int32_t onBuy, onSell; - Npc* merchant = player->getShopOwner(onBuy, onSell); - if(merchant == npc) - player->closeShopWindow(npc, onBuy, onSell); + if(Npc* npc = player->getShopOwner()) + lua_pushnumber(L, env->addThing(npc)); + else + lua_pushnil(L); return 1; } -NpcEventsHandler::NpcEventsHandler(Npc* npc) -{ - m_npc = npc; - m_loaded = false; -} - -bool NpcEventsHandler::isLoaded() -{ - return m_loaded; -} - -NpcScript::NpcScript(std::string file, Npc* npc): - NpcEventsHandler(npc) +NpcEvents::NpcEvents(std::string file, Npc* npc): + m_npc(npc), m_interface(npc->getInterface()) { - m_interface = npc->getInterface(); if(!m_interface->loadFile(file, npc)) { - std::cout << "[Warning - NpcScript::NpcScript] Cannot load script: " << file << std::endl; - std::cout << m_interface->getLastError() << std::endl; - + std::clog << "[Warning - NpcEvents::NpcEvents] Cannot load script: " << file + << std::endl << m_interface->getLastError() << std::endl; m_loaded = false; return; } @@ -2909,7 +2946,7 @@ NpcScript::NpcScript(std::string file, Npc* npc): m_loaded = true; } -void NpcScript::onCreatureAppear(const Creature* creature) +void NpcEvents::onCreatureAppear(const Creature* creature) { if(m_onCreatureAppear == -1) return; @@ -2923,7 +2960,7 @@ void NpcScript::onCreatureAppear(const Creature* creature) #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << "npc " << m_npc->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_onCreatureAppear, m_interface); @@ -2937,10 +2974,10 @@ void NpcScript::onCreatureAppear(const Creature* creature) m_interface->releaseEnv(); } else - std::cout << "[Error - NpcScript::onCreatureAppear] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; + std::clog << "[Error - NpcEvents::onCreatureAppear] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; } -void NpcScript::onCreatureDisappear(const Creature* creature) +void NpcEvents::onCreatureDisappear(const Creature* creature) { if(m_onCreatureDisappear == -1) return; @@ -2954,7 +2991,7 @@ void NpcScript::onCreatureDisappear(const Creature* creature) #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << "npc " << m_npc->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_onCreatureDisappear, m_interface); @@ -2968,10 +3005,10 @@ void NpcScript::onCreatureDisappear(const Creature* creature) m_interface->releaseEnv(); } else - std::cout << "[Error - NpcScript::onCreatureDisappear] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; + std::clog << "[Error - NpcEvents::onCreatureDisappear] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; } -void NpcScript::onCreatureMove(const Creature* creature, const Position& oldPos, const Position& newPos) +void NpcEvents::onCreatureMove(const Creature* creature, const Position& oldPos, const Position& newPos) { if(m_onCreatureMove == -1) return; @@ -2985,7 +3022,7 @@ void NpcScript::onCreatureMove(const Creature* creature, const Position& oldPos, #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << "npc " << m_npc->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_onCreatureMove, m_interface); @@ -2995,17 +3032,17 @@ void NpcScript::onCreatureMove(const Creature* creature, const Position& oldPos, m_interface->pushFunction(m_onCreatureMove); lua_pushnumber(L, env->addThing(const_cast(creature))); - LuaScriptInterface::pushPosition(L, oldPos, 0); - LuaScriptInterface::pushPosition(L, newPos, 0); + LuaInterface::pushPosition(L, oldPos, 0); + LuaInterface::pushPosition(L, newPos, 0); m_interface->callFunction(3); m_interface->releaseEnv(); } else - std::cout << "[Error - NpcScript::onCreatureMove] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; + std::clog << "[Error - NpcEvents::onCreatureMove] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; } -void NpcScript::onCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, Position* pos/* = NULL*/) +void NpcEvents::onCreatureSay(const Creature* creature, MessageClasses type, const std::string& text, Position* /*pos = NULL*/) { if(m_onCreatureSay == -1) return; @@ -3019,7 +3056,7 @@ void NpcScript::onCreatureSay(const Creature* creature, SpeakClasses type, const #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << "npc " << m_npc->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_onCreatureSay, m_interface); @@ -3036,16 +3073,16 @@ void NpcScript::onCreatureSay(const Creature* creature, SpeakClasses type, const m_interface->releaseEnv(); } else - std::cout << "[Error - NpcScript::onCreatureSay] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; + std::clog << "[Error - NpcEvents::onCreatureSay] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; } -void NpcScript::onPlayerTrade(const Player* player, int32_t callback, uint16_t itemid, - uint8_t count, uint8_t amount, bool ignoreCap, bool inBackpacks) +void NpcEvents::onPlayerTrade(const Player* player, int32_t callback, uint16_t itemid, + uint8_t count, uint8_t amount, bool ignore, bool inBackpacks) { if(callback == -1) return; - //on"Buy/Sell"(cid, itemid, count, amount, ignoreCap, inBackpacks) + //on"Buy/Sell"(cid, itemid, count, amount, "ignore", inBackpacks) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); @@ -3054,7 +3091,7 @@ void NpcScript::onPlayerTrade(const Player* player, int32_t callback, uint16_t i #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << "npc " << m_npc->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(-1, m_interface); @@ -3062,74 +3099,86 @@ void NpcScript::onPlayerTrade(const Player* player, int32_t callback, uint16_t i env->setNpc(m_npc); uint32_t cid = env->addThing(const_cast(player)); - LuaScriptInterface::pushCallback(L, callback); + LuaInterface::pushCallback(L, callback); lua_pushnumber(L, cid); lua_pushnumber(L, itemid); lua_pushnumber(L, count); lua_pushnumber(L, amount); - lua_pushboolean(L, ignoreCap); + lua_pushboolean(L, ignore); lua_pushboolean(L, inBackpacks); m_interface->callFunction(6); m_interface->releaseEnv(); } else - std::cout << "[Error - NpcScript::onPlayerTrade] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; + std::clog << "[Error - NpcEvents::onPlayerTrade] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; } -void NpcScript::onPlayerCloseChannel(const Player* player) +void NpcEvents::onPlayerEndTrade(const Player* player) { - if(m_onPlayerCloseChannel == -1) + if(m_onPlayerEndTrade == -1) return; - //onPlayerCloseChannel(cid) + //onPlayerEndTrade(cid) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); lua_State* L = m_interface->getState(); - env->setScriptId(m_onPlayerCloseChannel, m_interface); + #ifdef __DEBUG_LUASCRIPTS__ + std::stringstream desc; + desc << "npc " << m_npc->getName(); + env->setEvent(desc.str()); + #endif + + env->setScriptId(m_onPlayerEndTrade, m_interface); env->setRealPos(m_npc->getPosition()); env->setNpc(m_npc); - m_interface->pushFunction(m_onPlayerCloseChannel); + m_interface->pushFunction(m_onPlayerEndTrade); lua_pushnumber(L, env->addThing(const_cast(player))); m_interface->callFunction(1); m_interface->releaseEnv(); } else - std::cout << "[Error - NpcScript::onPlayerCloseChannel] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; + std::clog << "[Error - NpcEvents::onPlayerEndTrade] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; } -void NpcScript::onPlayerEndTrade(const Player* player) +void NpcEvents::onPlayerCloseChannel(const Player* player) { if(m_onPlayerCloseChannel == -1) return; - //onPlayerEndTrade(cid) + //onPlayerCloseChannel(cid) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); lua_State* L = m_interface->getState(); - env->setScriptId(m_onPlayerEndTrade, m_interface); + #ifdef __DEBUG_LUASCRIPTS__ + std::stringstream desc; + desc << "npc " << m_npc->getName(); + env->setEvent(desc.str()); + #endif + + env->setScriptId(m_onPlayerCloseChannel, m_interface); env->setRealPos(m_npc->getPosition()); env->setNpc(m_npc); - m_interface->pushFunction(m_onPlayerEndTrade); + m_interface->pushFunction(m_onPlayerCloseChannel); lua_pushnumber(L, env->addThing(const_cast(player))); m_interface->callFunction(1); m_interface->releaseEnv(); } else - std::cout << "[Error - NpcScript::onPlayerEndTrade] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; + std::clog << "[Error - NpcEvents::onPlayerCloseChannel] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; } -void NpcScript::onThink() +void NpcEvents::onThink() { if(m_onThink == -1) return; @@ -3142,7 +3191,7 @@ void NpcScript::onThink() #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << "npc " << m_npc->getName(); - env->setEventDesc(desc.str()); + env->setEvent(desc.str()); #endif env->setScriptId(m_onThink, m_interface); @@ -3155,6 +3204,5 @@ void NpcScript::onThink() m_interface->releaseEnv(); } else - std::cout << "[Error - NpcScript::onThink] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; + std::clog << "[Error - NpcEvents::onThink] NPC Name: " << m_npc->getName() << " - Call stack overflow" << std::endl; } - diff --git a/npc.h b/npc.h index 92ceb6b..8380d8f 100644 --- a/npc.h +++ b/npc.h @@ -24,44 +24,48 @@ #include "luascript.h" class Npc; -class Player; -class NpcResponse; - -struct NpcState; -typedef std::list NpcList; +struct NpcType +{ + std::string name, file, nameDescription, script; + Outfit_t outfit; +}; class Npcs { public: Npcs() {} - virtual ~Npcs() {} - + virtual ~Npcs(); void reload(); + + bool loadFromXml(bool reloading = false); + bool parseNpcNode(xmlNodePtr node, FileType_t path, bool reloading = false); + + NpcType* getType(const std::string& name) const; + bool setType(std::string name, NpcType* nType); + + private: + typedef std::map DataMap; + DataMap data; }; -class NpcScriptInterface : public LuaScriptInterface +struct NpcState; +class NpcScript : public LuaInterface { public: - NpcScriptInterface(); - virtual ~NpcScriptInterface(); - - bool loadNpcLib(std::string file); + NpcScript(); + virtual ~NpcScript() {} - static void pushState(lua_State *L, NpcState* state); - static void popState(lua_State *L, NpcState* &state); + static void pushState(lua_State* L, NpcState* state); + static void popState(lua_State* L, NpcState* &state); protected: virtual void registerFunctions(); + static int32_t luaActionFocus(lua_State* L); static int32_t luaActionSay(lua_State* L); - - static int32_t luaActionTurn(lua_State* L); - static int32_t luaActionMove(lua_State* L); - static int32_t luaActionMoveTo(lua_State* L); static int32_t luaActionFollow(lua_State* L); static int32_t luaGetNpcId(lua_State* L); - static int32_t luaGetNpcDistanceTo(lua_State* L); static int32_t luaGetNpcParameter(lua_State* L); static int32_t luaGetNpcState(lua_State* L); @@ -69,60 +73,34 @@ class NpcScriptInterface : public LuaScriptInterface static int32_t luaOpenShopWindow(lua_State* L); static int32_t luaCloseShopWindow(lua_State* L); - - private: - bool m_libLoaded; - - virtual bool initState(); - virtual bool closeState(); + static int32_t luaGetShopOwner(lua_State* L); }; -class NpcEventsHandler +class Player; +class NpcEvents { public: - NpcEventsHandler(Npc* npc); - virtual ~NpcEventsHandler() {} - - virtual void onCreatureAppear(const Creature* creature) {} - virtual void onCreatureDisappear(const Creature* creature) {} - - virtual void onCreatureMove(const Creature* creature, const Position& oldPos, const Position& newPos) {} - virtual void onCreatureSay(const Creature* creature, SpeakClasses, const std::string& text, Position* pos = NULL) {} - - virtual void onPlayerTrade(const Player* player, int32_t callback, uint16_t itemid, - uint8_t count, uint8_t amount, bool ignoreCap, bool inBackpacks) {} - virtual void onPlayerEndTrade(const Player* player) {} - virtual void onPlayerCloseChannel(const Player* player) {} - - virtual void onThink() {} - bool isLoaded(); - - protected: - Npc* m_npc; - bool m_loaded; -}; - -class NpcScript : public NpcEventsHandler -{ - public: - NpcScript(std::string file, Npc* npc); - virtual ~NpcScript() {} + NpcEvents(std::string file, Npc* npc); + virtual ~NpcEvents() {} virtual void onCreatureAppear(const Creature* creature); virtual void onCreatureDisappear(const Creature* creature); virtual void onCreatureMove(const Creature* creature, const Position& oldPos, const Position& newPos); - virtual void onCreatureSay(const Creature* creature, SpeakClasses, const std::string& text, Position* pos = NULL); + virtual void onCreatureSay(const Creature* creature, MessageClasses, const std::string& text, Position* pos = NULL); virtual void onPlayerTrade(const Player* player, int32_t callback, uint16_t itemid, - uint8_t count, uint8_t amount, bool ignoreCap, bool inBackpacks); + uint8_t count, uint8_t amount, bool ignore, bool inBackpacks); virtual void onPlayerEndTrade(const Player* player); virtual void onPlayerCloseChannel(const Player* player); virtual void onThink(); + bool isLoaded() const {return m_loaded;} private: - NpcScriptInterface* m_interface; + Npc* m_npc; + bool m_loaded; + NpcScript* m_interface; int32_t m_onCreatureAppear, m_onCreatureDisappear, m_onCreatureMove, m_onCreatureSay, m_onPlayerCloseChannel, m_onPlayerEndTrade, m_onThink; }; @@ -144,7 +122,8 @@ enum NpcEvent_t EVENT_PLAYER_LEAVE, EVENT_PLAYER_SHOPSELL, EVENT_PLAYER_SHOPBUY, - EVENT_PLAYER_SHOPCLOSE + EVENT_PLAYER_SHOPCLOSE, + EVENT_PLAYER_CHATCLOSE }; enum ResponseType_t @@ -224,14 +203,15 @@ struct ResponseAction ResponseAction() { actionType = ACTION_NONE; - key = intValue = 0; + intValue = 0; + key = ""; strValue = ""; pos = Position(); } ReponseActionParam_t actionType; - int32_t key, intValue; - std::string strValue; + int32_t intValue; + std::string key, strValue; Position pos; }; @@ -263,9 +243,11 @@ struct ScriptVars }; typedef std::list ActionList; + +class NpcResponse; typedef std::list ResponseList; -typedef std::map ResponseScriptMap; +typedef std::map ResponseScriptMap; class NpcResponse { public: @@ -274,13 +256,10 @@ class NpcResponse ResponseProperties() { topic = amount = focusStatus = -1; - output = ""; interactType = INTERACT_TEXT; responseType = RESPONSE_DEFAULT; params = 0; - storageId = -1; storageComp = STORAGE_EQUAL; - knowSpell = ""; publicize = true; } @@ -288,9 +267,9 @@ class NpcResponse InteractType_t interactType; ResponseType_t responseType; StorageComparision_t storageComp; - int32_t topic, amount, focusStatus, storageId; + int32_t topic, amount, focusStatus; uint32_t params; - std::string output, knowSpell, storageValue; + std::string output, knowSpell, storageId, storageValue; ActionList actionList; std::list inputList; std::list itemList; @@ -328,7 +307,7 @@ class NpcResponse std::string getInputText() const {return (prop.inputList.empty() ? "" : *prop.inputList.begin());} int32_t getTopic() const {return prop.topic;} int32_t getFocusState() const {return prop.focusStatus;} - int32_t getStorageId() const {return prop.storageId;} + std::string getStorageId() const {return prop.storageId;} std::string getStorage() const {return prop.storageValue;} ResponseType_t getResponseType() const {return prop.responseType;} InteractType_t getInteractType() const {return prop.interactType;} @@ -356,7 +335,7 @@ class NpcResponse struct NpcState { - bool isIdle, isQueued, ignoreCap, inBackpacks; + bool isIdle, isQueued, ignore, inBackpacks; int32_t topic, price, sellPrice, buyPrice, amount, itemId, subType, level; uint32_t respondToCreature; uint64_t prevInteraction; @@ -369,12 +348,11 @@ struct NpcState struct Voice { bool randomSpectator; - SpeakClasses type; + MessageClasses type; uint32_t interval, margin; std::string text; }; -#define MAX_RAND_RANGE 10000000 class Npc : public Creature { public: @@ -382,12 +360,15 @@ class Npc : public Creature static uint32_t npcCount; #endif virtual ~Npc(); + + static Npc* createNpc(NpcType* nType); static Npc* createNpc(const std::string& name); virtual Npc* getNpc() {return this;} virtual const Npc* getNpc() const {return this;} + virtual CreatureType_t getType() const {return CREATURETYPE_NPC;} - virtual uint32_t rangeId() {return 0x80000000;} + virtual uint32_t rangeId() {return NPC_ID_RANGE;} static AutoList autoList; void addList() {autoList[id] = this;} @@ -398,48 +379,49 @@ class Npc : public Creature virtual bool isWalkable() const {return walkable;} virtual bool canSee(const Position& pos) const; + virtual bool canSeeInvisibility() const {return true;} bool isLoaded() {return loaded;} bool load(); void reload(); - virtual const std::string& getName() const {return name;} - virtual const std::string& getNameDescription() const {return nameDescription;} + void setNpcPath(const std::string& _name, bool fromXmlFile = false); - void doSay(const std::string& text, SpeakClasses type, Player* player); - void doTurn(Direction dir); - void doMove(Direction dir); - void doMoveTo(Position pos); + virtual const std::string& getName() const {return nType->name;} + virtual const std::string& getNameDescription() const {return nType->nameDescription;} + + void doSay(const std::string& text, MessageClasses type, Player* player); void onPlayerTrade(Player* player, ShopEvent_t type, int32_t callback, uint16_t itemId, uint8_t count, - uint8_t amount, bool ignoreCap = false, bool inBackpacks = false); + uint8_t amount, bool ignore = false, bool inBackpacks = false); void onPlayerEndTrade(Player* player, int32_t buyCallback, int32_t sellCallback); void onPlayerCloseChannel(const Player* player); void setCreatureFocus(Creature* creature); - NpcScriptInterface* getInterface(); + NpcScript* getInterface(); protected: - Npc(const std::string& _name); + Npc(NpcType* _nType); + NpcType* nType; bool loaded; + void reset(); + bool loadFromXml(); + virtual void onCreatureAppear(const Creature* creature); virtual void onCreatureDisappear(const Creature* creature, bool isLogout); virtual void onCreatureMove(const Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport); - virtual void onCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, Position* pos = NULL); + virtual void onCreatureSay(const Creature* creature, MessageClasses type, const std::string& text, Position* pos = NULL); virtual void onThink(uint32_t interval); - bool isImmune(CombatType_t type) const {return true;} - bool isImmune(ConditionType_t type) const {return true;} + bool isImmune(CombatType_t) const {return true;} + bool isImmune(ConditionType_t) const {return true;} - virtual std::string getDescription(int32_t lookDistance) const {return nameDescription + ".";} + virtual std::string getDescription(int32_t) const {return nType->nameDescription + ".";} virtual bool getNextStep(Direction& dir, uint32_t& flags); bool getRandomStep(Direction& dir); - - void reset(); - bool loadFromXml(const std::string& name); bool canWalkTo(const Position& fromPos, Direction dir); const NpcResponse* getResponse(const ResponseList& list, const Player* player, @@ -457,25 +439,29 @@ class Npc : public Creature std::string formatResponse(Creature* creature, const NpcState* npcState, const NpcResponse* response) const; void executeResponse(Player* player, NpcState* npcState, const NpcResponse* response); + uint32_t parseParamsNode(xmlNodePtr node); + ResponseList parseInteractionNode(xmlNodePtr node); + void onPlayerEnter(Player* player, NpcState* state); void onPlayerLeave(Player* player, NpcState* state); - typedef std::map ParametersMap; - ParametersMap m_parameters; - - uint32_t loadParams(xmlNodePtr node); - ResponseList loadInteraction(xmlNodePtr node); - void addShopPlayer(Player* player); void removeShopPlayer(const Player* player); void closeAllShopWindows(); - uint32_t walkTicks; - std::string name, nameDescription, m_filename; - int32_t talkRadius, idleTime, idleInterval, focusCreature; bool floorChange, attackable, walkable, isIdle, hasBusyReply, hasScriptedFocus, defaultPublic; + Direction baseDirection; + + int32_t talkRadius, idleTime, idleInterval, focusCreature; + uint32_t walkTicks; int64_t lastVoice; + typedef std::map > ItemListMap; + ItemListMap itemListMap; + + typedef std::map ParametersMap; + ParametersMap m_parameters; + typedef std::list ShopPlayerList; ShopPlayerList shopPlayerList; @@ -488,16 +474,13 @@ class Npc : public Creature typedef std::list VoiceList; VoiceList voiceList; - typedef std::map > ItemListMap; - ItemListMap itemListMap; - ResponseScriptMap responseScriptMap; ResponseList responseList; - NpcEventsHandler* m_npcEventHandler; - static NpcScriptInterface* m_interface; + NpcEvents* m_npcEventHandler; + static NpcScript* m_interface; friend class Npcs; - friend class NpcScriptInterface; + friend class NpcScript; }; #endif diff --git a/rsa.h b/otpch.cpp similarity index 66% rename from rsa.h rename to otpch.cpp index ac35d93..b7ff133 100644 --- a/rsa.h +++ b/otpch.cpp @@ -15,31 +15,4 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// -#ifndef __RSA__ -#define __RSA__ - -#include "otsystem.h" -#include "gmp.h" - -class RSA -{ - public: - RSA(); - virtual ~RSA(); - - void setKey(const char* p, const char* q, const char* d); - bool setKey(const std::string& file); - - void decrypt(char* msg, int32_t size); - - int32_t getKeySize(); - void getPublicKey(char* buffer); - - protected: - boost::recursive_mutex rsaLock; - - bool m_keySet; - //use only GMP - mpz_t m_p, m_q, m_u, m_d, m_dp, m_dq, m_mod; -}; -#endif +#include "otpch.h" \ No newline at end of file diff --git a/otpch.h b/otpch.h index 3203cf0..2c3467b 100644 --- a/otpch.h +++ b/otpch.h @@ -20,6 +20,7 @@ #endif #define __OTPCH__ + #if defined WINDOWS #include #endif @@ -34,7 +35,7 @@ #include #include #include -#include //otserv +#include "configmanager.h" #include "thing.h" diff --git a/otserv.cpp b/otserv.cpp index 48d9d5f..b473e70 100644 --- a/otserv.cpp +++ b/otserv.cpp @@ -14,17 +14,28 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// + #include "otpch.h" #include "otsystem.h" +#include #include #include #include + #ifndef WINDOWS #include +#include +#else +#include #endif + #include +#include +#include +#include + #include "server.h" #ifdef __LOGIN_SERVER__ #include "gameservers.h" @@ -34,14 +45,15 @@ #include "game.h" #include "chat.h" #include "tools.h" -#include "rsa.h" -#include "textlogger.h" #include "protocollogin.h" #include "protocolgame.h" #include "protocolold.h" +#include "protocolhttp.h" + #include "status.h" -#ifdef __REMOTE_CONTROL__ +#include "manager.h" +#ifdef __OTADMIN__ #include "admin.h" #endif @@ -53,9 +65,13 @@ #include "ioban.h" #include "outfit.h" +#include "mounts.h" #include "vocation.h" #include "group.h" +#include "quests.h" +#include "raids.h" + #include "monsters.h" #ifdef __OTSERV_ALLOCATOR__ #include "allocator.h" @@ -63,47 +79,30 @@ #ifdef __EXCEPTION_TRACER__ #include "exception.h" #endif - -#if defined(WINDOWS) && !defined(__CONSOLE__) -#include "shellapi.h" -#include "gui.h" -#include "inputbox.h" -#include "commctrl.h" -#else -#include "resources.h" +#ifndef __OTADMIN__ +#include "textlogger.h" #endif #ifdef __NO_BOOST_EXCEPTIONS__ #include -void boost::throw_exception(std::exception const & e) +inline void boost::throw_exception(std::exception const & e) { - std::cout << "Boost exception: " << e.what() << std::endl; + std::clog << "Boost exception: " << e.what() << std::endl; } #endif +RSA* g_RSA; ConfigManager g_config; Game g_game; +Chat g_chat; Monsters g_monsters; Npcs g_npcs; -RSA g_RSA; -Chat g_chat; -#if defined(WINDOWS) && !defined(__CONSOLE__) -GUILogger g_logger; -NOTIFYICONDATA NID; -#endif - -IpList serverIps; boost::mutex g_loaderLock; boost::condition_variable g_loaderSignal; - boost::unique_lock g_loaderUniqueLock(g_loaderLock); -#ifdef __REMOTE_CONTROL__ -extern Admin* g_admin; -#endif -#if !defined(WINDOWS) || defined(__CONSOLE__) bool argumentsHandler(StringVec args) { StringVec tmp; @@ -111,32 +110,33 @@ bool argumentsHandler(StringVec args) { if((*it) == "--help") { - std::cout << "Usage:\n" + std::clog << "Usage:\n" "\n" "\t--config=$1\t\tAlternate configuration file path.\n" "\t--data-directory=$1\tAlternate data directory path.\n" - "\t--ip=$1\t\t\tIP address of gameworld server.\n" + "\t--ip=$1\t\t\tIP address of the server.\n" "\t\t\t\tShould be equal to the global IP.\n" "\t--login-port=$1\tPort for login server to listen on.\n" "\t--game-port=$1\tPort for game server to listen on.\n" "\t--admin-port=$1\tPort for admin server to listen on.\n" + "\t--manager-port=$1\tPort for manager server to listen on.\n" "\t--status-port=$1\tPort for status server to listen on.\n"; #ifndef WINDOWS - std::cout << "\t--runfile=$1\t\tSpecifies run file. Will contain the pid\n" - "\t\t\t\tof the server process as long as it is running.\n"; + std::clog << "\t--runfile=$1\t\tSpecifies run file. Will contain the pid\n" + "\t\t\t\tof the server process as long as run status.\n"; #endif - std::cout << "\t--output-log=$1\t\tAll standard output will be logged to\n" + std::clog << "\t--log=$1\t\tWhole standard output will be logged to\n" "\t\t\t\tthis file.\n" - "\t--error-log=$1\t\tAll standard errors will be logged to\n" - "\t\t\t\tthis file.\n"; + "\t--closed\t\t\tStarts the server as closed.\n" + "\t--no-script\t\t\tStarts the server without script system.\n"; return false; } - if((*it) == "--version") + if((*it) == "--version" || (*it) == "-v") { - std::cout << STATUS_SERVER_NAME << ", version " << STATUS_SERVER_VERSION << " (" << STATUS_SERVER_CODENAME << ")\n" - "Compiled with " << BOOST_COMPILER << " at " << __DATE__ << ", " << __TIME__ << ".\n" - "A server developed by Elf, slawkens, Talaturen, Lithium, KaczooH, Kiper, Kornholijo.\n" + std::clog << SOFTWARE_NAME << ", version " << SOFTWARE_VERSION << " (" << SOFTWARE_CODENAME << ")\n" + "Compiled with " << BOOST_COMPILER << " (x86_64: " << __x86_64__ << ") at " << __DATE__ << ", " << __TIME__ << ".\n" + "A server developed by " << SOFTWARE_DEVELOPERS << ".\n" "Visit our forum for updates, support and resources: http://otland.net.\n"; return false; } @@ -146,6 +146,8 @@ bool argumentsHandler(StringVec args) g_config.setString(ConfigManager::CONFIG_FILE, tmp[1]); else if(tmp[0] == "--data-directory") g_config.setString(ConfigManager::DATA_DIRECTORY, tmp[1]); + else if(tmp[0] == "--logs-directory") + g_config.setString(ConfigManager::LOGS_DIRECTORY, tmp[1]); else if(tmp[0] == "--ip") g_config.setString(ConfigManager::IP, tmp[1]); else if(tmp[0] == "--login-port") @@ -154,35 +156,55 @@ bool argumentsHandler(StringVec args) g_config.setNumber(ConfigManager::GAME_PORT, atoi(tmp[1].c_str())); else if(tmp[0] == "--admin-port") g_config.setNumber(ConfigManager::ADMIN_PORT, atoi(tmp[1].c_str())); + else if(tmp[0] == "--manager-port") + g_config.setNumber(ConfigManager::MANAGER_PORT, atoi(tmp[1].c_str())); else if(tmp[0] == "--status-port") g_config.setNumber(ConfigManager::STATUS_PORT, atoi(tmp[1].c_str())); #ifndef WINDOWS - else if(tmp[0] == "--runfile") + else if(tmp[0] == "--runfile" || tmp[0] == "--run-file" || tmp[0] == "--pidfile" || tmp[0] == "--pid-file") g_config.setString(ConfigManager::RUNFILE, tmp[1]); #endif - else if(tmp[0] == "--output-log") - g_config.setString(ConfigManager::OUT_LOG, tmp[1]); - else if(tmp[0] == "--error-log") - g_config.setString(ConfigManager::ERROR_LOG, tmp[1]); + else if(tmp[0] == "--log") + g_config.setString(ConfigManager::OUTPUT_LOG, tmp[1]); +#ifndef WINDOWS + else if(tmp[0] == "--daemon" || tmp[0] == "-d") + g_config.setBool(ConfigManager::DAEMONIZE, true); +#endif + else if(tmp[0] == "--closed") + g_config.setBool(ConfigManager::START_CLOSED, true); + else if(tmp[0] == "--no-script" || tmp[0] == "--noscript") + g_config.setBool(ConfigManager::SCRIPT_SYSTEM, false); } return true; } -#endif #ifndef WINDOWS +int32_t OTSYS_getch() +{ + struct termios oldt; + tcgetattr(STDIN_FILENO, &oldt); + + struct termios newt = oldt; + newt.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + + int32_t ch = getchar(); + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + return ch; +} + void signalHandler(int32_t sig) { - uint32_t tmp = 0; switch(sig) { case SIGHUP: Dispatcher::getInstance().addTask(createTask( - boost::bind(&Game::saveGameState, &g_game, false))); + boost::bind(&Game::saveGameState, &g_game, (uint8_t)SAVE_PLAYERS | (uint8_t)SAVE_MAP | (uint8_t)SAVE_STATE))); break; case SIGTRAP: - g_game.cleanMap(tmp); + g_game.cleanMap(); break; case SIGCHLD: @@ -191,25 +213,29 @@ void signalHandler(int32_t sig) case SIGUSR1: Dispatcher::getInstance().addTask(createTask( - boost::bind(&Game::setGameState, &g_game, GAME_STATE_CLOSED))); + boost::bind(&Game::setGameState, &g_game, GAMESTATE_CLOSED))); break; case SIGUSR2: - g_game.setGameState(GAME_STATE_NORMAL); + g_game.setGameState(GAMESTATE_NORMAL); break; case SIGCONT: - g_game.reloadInfo(RELOAD_ALL); + Dispatcher::getInstance().addTask(createTask( + boost::bind(&Game::reloadInfo, &g_game, RELOAD_ALL, 0, false))); break; case SIGQUIT: Dispatcher::getInstance().addTask(createTask( - boost::bind(&Game::setGameState, &g_game, GAME_STATE_SHUTDOWN))); + boost::bind(&Game::setGameState, &g_game, GAMESTATE_SHUTDOWN))); break; case SIGTERM: Dispatcher::getInstance().addTask(createTask( boost::bind(&Game::shutdown, &g_game))); + + Dispatcher::getInstance().stop(); + Scheduler::getInstance().stop(); break; default: @@ -222,68 +248,57 @@ void runfileHandler(void) std::ofstream runfile(g_config.getString(ConfigManager::RUNFILE).c_str(), std::ios::trunc | std::ios::out); runfile.close(); } +#else +int32_t OTSYS_getch() +{ + return (int32_t)getchar(); +} #endif void allocationHandler() { puts("Allocation failed, server out of memory!\nDecrease size of your map or compile in a 64-bit mode."); - char buffer[1024]; - fgets(buffer, 1024, stdin); - exit(-1); + OTSYS_getch(); + std::exit(-1); } -void startupErrorMessage(const std::string& error) +void startupErrorMessage(std::string error = "") { + // we will get a crash here as the threads aren't going down smoothly if(error.length() > 0) - std::cout << std::endl << "> ERROR: " << error << std::endl; + std::clog << std::endl << "> ERROR: " << error << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - MessageBox(GUI::getInstance()->m_mainWindow, error.c_str(), "Error", MB_OK); - system("pause"); - #else - getchar(); - #endif - exit(-1); + OTSYS_getch(); + std::exit(-1); } -void otserv( -#if !defined(WINDOWS) || defined(__CONSOLE__) -StringVec args, -#endif -ServiceManager* services); - -#if !defined(WINDOWS) || defined(__CONSOLE__) -int main(int argc, char *argv[]) +void otserv(StringVec args, ServiceManager* services); +int main(int argc, char* argv[]) { + std::srand((uint32_t)OTSYS_TIME()); StringVec args = StringVec(argv, argv + argc); if(argc > 1 && !argumentsHandler(args)) return 0; -#else -void serverMain(void* param) -{ - std::cout.rdbuf(&g_logger); - std::cerr.rdbuf(&g_logger); - -#endif std::set_new_handler(allocationHandler); ServiceManager servicer; g_config.startup(); - #ifdef __OTSERV_ALLOCATOR_STATS__ - boost::thread(boost::bind(&allocatorStatsThread, (void*)NULL)); - // TODO: destruct this thread... - #endif - #ifdef __EXCEPTION_TRACER__ +#ifdef __OTSERV_ALLOCATOR_STATS__ + //boost::thread(boost::bind(&allocatorStatsThread, (void*)NULL)); + // TODO: this thread needs a shutdown (timed_lock + interrupt? .interrupt + .unlock) +#endif +#ifdef __EXCEPTION_TRACER__ ExceptionHandler mainExceptionHandler; mainExceptionHandler.InstallHandler(); - #endif - #ifndef WINDOWS +#endif +#ifndef WINDOWS // ignore sigpipe... struct sigaction sigh; sigh.sa_handler = SIG_IGN; sigh.sa_flags = 0; + sigemptyset(&sigh.sa_mask); sigaction(SIGPIPE, &sigh, NULL); @@ -296,178 +311,128 @@ void serverMain(void* param) signal(SIGCONT, signalHandler); //reload all signal(SIGQUIT, signalHandler); //save & shutdown signal(SIGTERM, signalHandler); //shutdown - #endif +#endif - Dispatcher::getInstance().addTask(createTask(boost::bind(otserv, - #if !defined(WINDOWS) || defined(__CONSOLE__) - args, - #endif - &servicer))); + OutputHandler::getInstance(); + Dispatcher::getInstance().addTask(createTask(boost::bind(otserv, args, &servicer))); g_loaderSignal.wait(g_loaderUniqueLock); - #if !defined(WINDOWS) || defined(__CONSOLE__) - std::string outPath = g_config.getString(ConfigManager::OUT_LOG), errPath = g_config.getString(ConfigManager::ERROR_LOG); - if(outPath.length() < 3) - outPath = ""; - else if(outPath[0] != '/' && outPath[1] != ':') - { - outPath = getFilePath(FILE_TYPE_LOG, outPath); - std::cout << "> Logging output to file: " << outPath << std::endl; - } - - if(errPath.length() < 3) - errPath = ""; - else if(errPath[0] != '/' && errPath[1] != ':') - { - errPath = getFilePath(FILE_TYPE_LOG, errPath); - std::cout << "> Logging errors to file: " << errPath << std::endl; - } - - if(outPath != "") - { - boost::shared_ptr outFile; - outFile.reset(new std::ofstream(outPath.c_str(), (g_config.getBool(ConfigManager::TRUNCATE_LOGS) ? - std::ios::trunc : std::ios::app) | std::ios::out)); - if(!outFile->is_open()) - startupErrorMessage("Could not open output log file for writing!"); - - std::cout.rdbuf(outFile->rdbuf()); - } - - if(errPath != "") - { - boost::shared_ptr errFile; - errFile.reset(new std::ofstream(errPath.c_str(), (g_config.getBool(ConfigManager::TRUNCATE_LOGS) ? - std::ios::trunc : std::ios::app) | std::ios::out)); - if(!errFile->is_open()) - startupErrorMessage("Could not open error log file for writing!"); - - std::cerr.rdbuf(errFile->rdbuf()); - } - #endif - + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); if(servicer.isRunning()) { - std::cout << ">> " << g_config.getString(ConfigManager::SERVER_NAME) << " server Online!" << std::endl << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Status: Online!"); - GUI::getInstance()->m_connections = true; - #endif + Status::getInstance(); + std::clog << ">> " << g_config.getString(ConfigManager::SERVER_NAME) << " server Online!" << std::endl << std::endl; servicer.run(); } else - { - std::cout << ">> " << g_config.getString(ConfigManager::SERVER_NAME) << " server Offline! No services available..." << std::endl << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Status: Offline!"); - GUI::getInstance()->m_connections = true; - #endif - } + std::clog << ">> " << g_config.getString(ConfigManager::SERVER_NAME) << " server Offline! No services available..." << std::endl << std::endl; + + Dispatcher::getInstance().exit(); + Scheduler::getInstance().exit(); #ifdef __EXCEPTION_TRACER__ mainExceptionHandler.RemoveHandler(); #endif -#if !defined(WINDOWS) || defined(__CONSOLE__) return 0; -#endif } -void otserv( -#if !defined(WINDOWS) || defined(__CONSOLE__) -StringVec args, -#endif -ServiceManager* services) +void otserv(StringVec, ServiceManager* services) { - srand((uint32_t)OTSYS_TIME()); - #if defined(WINDOWS) - #if defined(__CONSOLE__) - SetConsoleTitle(STATUS_SERVER_NAME); - #else - GUI::getInstance()->m_connections = false; - #endif - #endif + std::srand((uint32_t)OTSYS_TIME()); +#if defined(WINDOWS) + SetConsoleTitle(SOFTWARE_NAME); - g_game.setGameState(GAME_STATE_STARTUP); - #if !defined(WINDOWS) && !defined(__ROOT_PERMISSION__) +#endif + g_game.setGameState(GAMESTATE_STARTUP); +#if !defined(WINDOWS) && !defined(__ROOT_PERMISSION__) if(!getuid() || !geteuid()) { - std::cout << "> WARNING: " << STATUS_SERVER_NAME << " has been executed as root user! It is recommended to execute as a normal user." << std::endl; - std::cout << "Continue? (y/N)" << std::endl; - - char buffer = getchar(); - if(buffer == 10 || (buffer != 121 && buffer != 89)) + std::clog << "> WARNING: " << SOFTWARE_NAME << " has been executed as super user! It is " + << "recommended to run as a normal user." << std::endl << "Continue? (y/N)" << std::endl; + char buffer = OTSYS_getch(); + if(buffer != 121 && buffer != 89) startupErrorMessage("Aborted."); } - #endif - - std::cout << STATUS_SERVER_NAME << ", version " << STATUS_SERVER_VERSION << " (" << STATUS_SERVER_CODENAME << ")" << std::endl; - std::cout << "Compiled with " << BOOST_COMPILER << " at " << __DATE__ << ", " << __TIME__ << "." << std::endl; - std::cout << "A server developed by Elf, slawkens, Talaturen, KaczooH, Lithium, Kiper, Kornholijo." << std::endl; - std::cout << "Visit our forum for updates, support and resources: http://otland.net." << std::endl << std::endl; +#endif + std::clog << SOFTWARE_NAME << ", version " << SOFTWARE_VERSION << " (" << SOFTWARE_CODENAME << ")" << std::endl + << "Compiled with " << BOOST_COMPILER << " (x86_64: " << __x86_64__ << ") at " << __DATE__ << ", " << __TIME__ << "." << std::endl + << "A server developed by " << SOFTWARE_DEVELOPERS << "." << std::endl + << "Visit our forum for updates, support and resources: http://otland.net." << std::endl << std::endl; std::stringstream ss; - #ifdef __DEBUG__ +#ifdef __DEBUG__ ss << " GLOBAL"; - #endif - #ifdef __DEBUG_MOVESYS__ +#endif +#ifdef __DEBUG_MOVESYS__ ss << " MOVESYS"; - #endif - #ifdef __DEBUG_CHAT__ +#endif +#ifdef __DEBUG_CHAT__ ss << " CHAT"; - #endif - #ifdef __DEBUG_EXCEPTION_REPORT__ - ss << " EXCEPTION-REPORT"; - #endif - #ifdef __DEBUG_HOUSES__ +#endif +#ifdef __DEBUG_HOUSES__ ss << " HOUSES"; - #endif - #ifdef __DEBUG_LUASCRIPTS__ +#endif +#ifdef __DEBUG_LUASCRIPTS__ ss << " LUA-SCRIPTS"; - #endif - #ifdef __DEBUG_MAILBOX__ +#endif +#ifdef __DEBUG_MAILBOX__ ss << " MAILBOX"; - #endif - #ifdef __DEBUG_NET__ +#endif +#ifdef __DEBUG_NET__ ss << " NET"; - #endif - #ifdef __DEBUG_NET_DETAIL__ +#endif +#ifdef __DEBUG_NET_DETAIL__ ss << " NET-DETAIL"; - #endif - #ifdef __DEBUG_RAID__ +#endif +#ifdef __DEBUG_RAID__ ss << " RAIDS"; - #endif - #ifdef __DEBUG_SCHEDULER__ +#endif +#ifdef __DEBUG_SCHEDULER__ ss << " SCHEDULER"; - #endif - #ifdef __DEBUG_SPAWN__ +#endif +#ifdef __DEBUG_SPAWN__ ss << " SPAWNS"; - #endif - #ifdef __SQL_QUERY_DEBUG__ +#endif +#ifdef __SQL_QUERY_DEBUG__ ss << " SQL-QUERIES"; - #endif +#endif std::string debug = ss.str(); if(!debug.empty()) - { - std::cout << ">> Debugging:"; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Displaying debugged components"); - #endif - std::cout << debug << "." << std::endl; - } + std::clog << ">> Debugging:" << debug << "." << std::endl; - std::cout << ">> Loading config (" << g_config.getString(ConfigManager::CONFIG_FILE) << ")" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading config"); - #endif + std::clog << ">> Loading config (" << g_config.getString(ConfigManager::CONFIG_FILE) << ")" << std::endl; if(!g_config.load()) startupErrorMessage("Unable to load " + g_config.getString(ConfigManager::CONFIG_FILE) + "!"); +#ifndef WINDOWS + if(g_config.getBool(ConfigManager::DAEMONIZE)) + { + std::clog << "> Daemonization... "; + if(fork()) + { + std::clog << "succeed, bye!" << std::endl; + exit(0); + } + else + std::clog << "failed, continuing." << std::endl; + } + +#endif + // silently append trailing slash + std::string path = g_config.getString(ConfigManager::DATA_DIRECTORY); + g_config.setString(ConfigManager::DATA_DIRECTORY, path.erase(path.find_last_not_of("/") + 1) + "/"); + + path = g_config.getString(ConfigManager::LOGS_DIRECTORY); + g_config.setString(ConfigManager::LOGS_DIRECTORY, path.erase(path.find_last_not_of("/") + 1) + "/"); + + std::clog << "> Opening logs" << std::endl; Logger::getInstance()->open(); + IntegerVec cores = vectorAtoi(explodeString(g_config.getString(ConfigManager::CORES_USED), ",")); if(cores[0] != -1) { - #ifdef WINDOWS +#ifdef WINDOWS int32_t mask = 0; for(IntegerVec::iterator it = cores.begin(); it != cores.end(); ++it) mask += 1 << (*it); @@ -490,7 +455,8 @@ ServiceManager* services) else if(defaultPriority == "higher") SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS); - #else +#else +#ifndef __APPLE__ cpu_set_t mask; CPU_ZERO(&mask); for(IntegerVec::iterator it = cores.begin(); it != cores.end(); ++it) @@ -498,9 +464,10 @@ ServiceManager* services) sched_setaffinity(getpid(), (int32_t)sizeof(mask), &mask); } +#endif std::string runPath = g_config.getString(ConfigManager::RUNFILE); - if(runPath != "" && runPath.length() > 2) + if(!runPath.empty() && runPath.length() > 2) { std::ofstream runFile(runPath.c_str(), std::ios::trunc | std::ios::out); runFile << getpid(); @@ -509,315 +476,348 @@ ServiceManager* services) } if(!nice(g_config.getNumber(ConfigManager::NICE_LEVEL))) {} - #endif +#endif std::string encryptionType = asLowerCaseString(g_config.getString(ConfigManager::ENCRYPTION_TYPE)); if(encryptionType == "md5") { g_config.setNumber(ConfigManager::ENCRYPTION, ENCRYPTION_MD5); - std::cout << "> Using MD5 encryption" << std::endl; + std::clog << "> Using MD5 encryption" << std::endl; } else if(encryptionType == "sha1") { g_config.setNumber(ConfigManager::ENCRYPTION, ENCRYPTION_SHA1); - std::cout << "> Using SHA1 encryption" << std::endl; + std::clog << "> Using SHA1 encryption" << std::endl; + } + else if(encryptionType == "sha256") + { + g_config.setNumber(ConfigManager::ENCRYPTION, ENCRYPTION_SHA256); + std::clog << "> Using SHA256 encryption" << std::endl; + } + else if(encryptionType == "sha512") + { + g_config.setNumber(ConfigManager::ENCRYPTION, ENCRYPTION_SHA512); + std::clog << "> Using SHA512 encryption" << std::endl; } else { g_config.setNumber(ConfigManager::ENCRYPTION, ENCRYPTION_PLAIN); - std::cout << "> Using plaintext encryption" << std::endl; + std::clog << "> Using plaintext encryption" << std::endl << std::endl + << "> WARNING: This method is completely unsafe!" << std::endl + << "> Please set encryptionType = \"sha1\" (or any other available method) in config.lua" << std::endl; + boost::this_thread::sleep(boost::posix_time::seconds(15)); } - - std::cout << ">> Checking software version... "; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Checking software version"); - #endif - if(xmlDocPtr doc = xmlParseFile(VERSION_CHECK)) + + std::clog << ">> Checking software version..."; + if(VERSION_BUILD) { - xmlNodePtr p, root = xmlDocGetRootElement(doc); - if(!xmlStrcmp(root->name, (const xmlChar*)"versions")) + if(xmlDocPtr doc = xmlParseFile(VERSION_CHECK)) { - p = root->children->next; - if(!xmlStrcmp(p->name, (const xmlChar*)"entry")) + xmlNodePtr p, root = xmlDocGetRootElement(doc); + if(!xmlStrcmp(root->name, (const xmlChar*)"versions")) { - std::string version; - int32_t patch, build, timestamp; + p = root->children->next; + if(!xmlStrcmp(p->name, (const xmlChar*)"entry")) + { + std::string version; + int32_t patch, build, timestamp; - bool tmp = false; - if(readXMLString(p, "version", version) && version != STATUS_SERVER_VERSION) - tmp = true; + bool tmp = false; + if(readXMLString(p, "version", version) && version != SOFTWARE_VERSION) + tmp = true; - if(readXMLInteger(p, "patch", patch) && patch > VERSION_PATCH) - tmp = true; + if(readXMLInteger(p, "patch", patch) && patch > VERSION_PATCH) + tmp = true; - if(readXMLInteger(p, "build", build) && build > VERSION_BUILD) - tmp = true; + if(readXMLInteger(p, "build", build) && build > VERSION_BUILD) + tmp = true; - if(readXMLInteger(p, "timestamp", timestamp) && timestamp > VERSION_TIMESTAMP) - tmp = true; + if(readXMLInteger(p, "timestamp", timestamp) && timestamp > VERSION_TIMESTAMP) + tmp = true; - if(tmp) - { - std::cout << "outdated, please consider updating!" << std::endl; - std::cout << "> Current version information - version: " << STATUS_SERVER_VERSION << ", patch: " << VERSION_PATCH; - std::cout << ", build: " << VERSION_BUILD << ", timestamp: " << VERSION_TIMESTAMP << "." << std::endl; - std::cout << "> Latest version information - version: " << version << ", patch: " << patch; - std::cout << ", build: " << build << ", timestamp: " << timestamp << "." << std::endl; - if(g_config.getBool(ConfigManager::CONFIM_OUTDATED_VERSION) && version.find("_SVN") == std::string::npos) + if(tmp) { - #if defined(WINDOWS) && !defined(__CONSOLE__) - if(MessageBox(GUI::getInstance()->m_mainWindow, "Continue?", "Outdated software", MB_YESNO) == IDNO) - #else - std::cout << "Continue? (y/N)" << std::endl; - - char buffer = getchar(); - if(buffer == 10 || (buffer != 121 && buffer != 89)) - #endif - startupErrorMessage("Aborted."); + std::clog << " "; + if(version.find("_SVN") == std::string::npos) + std::clog << "running sub version, please mind it's unstable and only for testing purposes!"; + else + std::clog << "outdated, please consider upgrading!"; + + std::clog << std::endl << "> Current version information - version: " + << SOFTWARE_VERSION << ", patch: " << VERSION_PATCH + << ", build: " << VERSION_BUILD << ", timestamp: " << VERSION_TIMESTAMP + << "." << std::endl << "> Latest version information - version: " + << version << ", patch: " << patch << ", build: " << build + << ", timestamp: " << timestamp << "." << std::endl; + if(g_config.getBool(ConfigManager::CONFIRM_OUTDATED_VERSION) && + asLowerCaseString(version).find("_svn") == std::string::npos) + { + std::clog << "Continue? (y/N)" << std::endl; + char buffer = OTSYS_getch(); + if(buffer != 121 && buffer != 89) + startupErrorMessage("Aborted."); + } } + else + std::clog << "up to date!" << std::endl; } else - std::cout << "up to date!" << std::endl; + std::clog << "failed checking - malformed entry." << std::endl; } else - std::cout << "failed checking - malformed entry." << std::endl; + std::clog << "failed checking - malformed file." << std::endl; + + xmlFreeDoc(doc); } else - std::cout << "failed checking - malformed file." << std::endl; - - xmlFreeDoc(doc); + std::clog << "failed - could not parse remote file (are you connected to any network?)" << std::endl; } else - std::cout << "failed - could not parse remote file (are you connected to the internet?)" << std::endl; + std::clog << std::endl << "> Ignoring version check, using SVN" << std::endl; - std::cout << ">> Fetching blacklist" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Fetching blacklist"); - #endif - if(!g_game.fetchBlacklist()) + std::clog << ">> Loading RSA key"; + g_RSA = RSA_new(); + + BN_dec2bn(&g_RSA->p, g_config.getString(ConfigManager::RSA_PRIME1).c_str()); + BN_dec2bn(&g_RSA->q, g_config.getString(ConfigManager::RSA_PRIME2).c_str()); + BN_dec2bn(&g_RSA->d, g_config.getString(ConfigManager::RSA_PRIVATE).c_str()); + BN_dec2bn(&g_RSA->n, g_config.getString(ConfigManager::RSA_MODULUS).c_str()); + BN_dec2bn(&g_RSA->e, g_config.getString(ConfigManager::RSA_PUBLIC).c_str()); + + // This check will verify keys set in config.lua + if(RSA_check_key(g_RSA)) { - #if defined(WINDOWS) && !defined(__CONSOLE__) - if(MessageBox(GUI::getInstance()->m_mainWindow, "Unable to fetch blacklist! Continue?", "Blacklist", MB_YESNO) == IDNO) - #else - std::cout << "Unable to fetch blacklist! Continue? (y/N)" << std::endl; - char buffer = getchar(); - if(buffer == 10 || (buffer != 121 && buffer != 89)) - #endif - startupErrorMessage("Unable to fetch blacklist!"); - } + std::clog << std::endl << "> Calculating dmp1, dmq1 and iqmp for RSA..."; - std::cout << ">> Loading RSA key" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading RSA Key"); - #endif + // Ok, now we calculate a few things, dmp1, dmq1 and iqmp + BN_CTX* ctx = BN_CTX_new(); + BN_CTX_start(ctx); - const char* p("14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113"); - const char* q("7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101"); - const char* d("46730330223584118622160180015036832148732986808519344675210555262940258739805766860224610646919605860206328024326703361630109888417839241959507572247284807035235569619173792292786907845791904955103601652822519121908367187885509270025388641700821735345222087940578381210879116823013776808975766851829020659073"); - g_RSA.setKey(p, q, d); + BIGNUM *r1 = BN_CTX_get(ctx), *r2 = BN_CTX_get(ctx); + BN_mod(g_RSA->dmp1, g_RSA->d, r1, ctx); + BN_mod(g_RSA->dmq1, g_RSA->d, r2, ctx); - std::cout << ">> Starting SQL connection" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Starting SQL connection"); - #endif + BN_mod_inverse(g_RSA->iqmp, g_RSA->q, g_RSA->p, ctx); + std::clog << " done" << std::endl; + } + else + { + ERR_load_crypto_strings(); + std::stringstream s; + + s << std::endl << "> OpenSSL failed - " << ERR_error_string(ERR_get_error(), NULL); + startupErrorMessage(s.str()); + } + + std::clog << ">> Starting SQL connection" << std::endl; Database* db = Database::getInstance(); if(db && db->isConnected()) { - std::cout << ">> Running Database Manager" << std::endl; - if(!DatabaseManager::getInstance()->isDatabaseSetup()) - startupErrorMessage("The database you specified in config.lua is empty, please import schemas/.sql to the database (if you are using MySQL, please read doc/MYSQL_HELP for more information)."); - else + std::clog << ">> Running Database Manager" << std::endl; + if(DatabaseManager::getInstance()->isDatabaseSetup()) { uint32_t version = 0; do { version = DatabaseManager::getInstance()->updateDatabase(); - if(version == 0) + if(!version) break; - std::cout << "> Database has been updated to version: " << version << "." << std::endl; + std::clog << "> Database has been updated to version: " << version << "." << std::endl; } while(version < VERSION_DATABASE); } + else + startupErrorMessage("The database you have specified in config.lua is empty, please import schemas/.sql to the database."); DatabaseManager::getInstance()->checkTriggers(); DatabaseManager::getInstance()->checkEncryption(); - if(g_config.getBool(ConfigManager::OPTIMIZE_DB_AT_STARTUP) && !DatabaseManager::getInstance()->optimizeTables()) - std::cout << "> No tables were optimized." << std::endl; + if(g_config.getBool(ConfigManager::OPTIMIZE_DATABASE) && !DatabaseManager::getInstance()->optimizeTables()) + std::clog << "> No tables were optimized." << std::endl; } else startupErrorMessage("Couldn't estabilish connection to SQL database!"); - std::cout << ">> Loading items" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading items"); - #endif + std::clog << ">> Loading items (OTB)" << std::endl; if(Item::items.loadFromOtb(getFilePath(FILE_TYPE_OTHER, "items/items.otb"))) startupErrorMessage("Unable to load items (OTB)!"); + std::clog << ">> Loading items (XML)" << std::endl; if(!Item::items.loadFromXml()) { - #if defined(WINDOWS) && !defined(__CONSOLE__) - if(MessageBox(GUI::getInstance()->m_mainWindow, "Unable to load items (XML)! Continue?", "Items (XML)", MB_YESNO) == IDNO) - #else - std::cout << "Unable to load items (XML)! Continue? (y/N)" << std::endl; - char buffer = getchar(); - if(buffer == 10 || (buffer != 121 && buffer != 89)) - #endif + std::clog << "Unable to load items (XML)! Continue? (y/N)" << std::endl; + char buffer = OTSYS_getch(); + if(buffer != 121 && buffer != 89) startupErrorMessage("Unable to load items (XML)!"); } - std::cout << ">> Loading groups" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading groups"); - #endif + std::clog << ">> Loading groups" << std::endl; if(!Groups::getInstance()->loadFromXml()) startupErrorMessage("Unable to load groups!"); - std::cout << ">> Loading vocations" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading vocations"); - #endif + std::clog << ">> Loading vocations" << std::endl; if(!Vocations::getInstance()->loadFromXml()) startupErrorMessage("Unable to load vocations!"); - std::cout << ">> Loading script systems" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading script systems"); - #endif - if(!ScriptingManager::getInstance()->load()) - startupErrorMessage(""); + std::clog << ">> Loading outfits" << std::endl; + if(!Outfits::getInstance()->loadFromXml()) + startupErrorMessage("Unable to load outfits!"); - std::cout << ">> Loading chat channels" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading chat channels"); - #endif + std::clog << ">> Loading mounts" << std::endl; + if(!Mounts::getInstance()->loadFromXml()) + startupErrorMessage("Unable to load mounts!"); + + std::clog << ">> Loading chat channels" << std::endl; if(!g_chat.loadFromXml()) startupErrorMessage("Unable to load chat channels!"); - std::cout << ">> Loading outfits" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading outfits"); - #endif - if(!Outfits::getInstance()->loadFromXml()) - startupErrorMessage("Unable to load outfits!"); + if(g_config.getBool(ConfigManager::SCRIPT_SYSTEM)) + { + std::clog << ">> Loading script systems" << std::endl; + if(!ScriptManager::getInstance()->loadSystem()) + startupErrorMessage(); + } + else + ScriptManager::getInstance(); + + std::clog << ">> Loading mods..." << std::endl; + if(!ScriptManager::getInstance()->loadMods()) + startupErrorMessage(); + + #ifdef __LOGIN_SERVER__ + std::clog << ">> Loading game servers" << std::endl; + if(!GameServers::getInstance()->loadFromXml(true)) + startupErrorMessage("Unable to load game servers!"); - std::cout << ">> Loading experience stages" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading experience stages"); #endif + std::clog << ">> Loading experience stages" << std::endl; if(!g_game.loadExperienceStages()) startupErrorMessage("Unable to load experience stages!"); - std::cout << ">> Loading monsters" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading monsters"); - #endif + std::clog << ">> Loading quests" << std::endl; + Quests::getInstance()->loadFromXml(); + + std::clog << ">> Loading monsters" << std::endl; if(!g_monsters.loadFromXml()) { - #if defined(WINDOWS) && !defined(__CONSOLE__) - if(MessageBox(GUI::getInstance()->m_mainWindow, "Unable to load monsters! Continue?", "Monsters", MB_YESNO) == IDNO) - #else - std::cout << "Unable to load monsters! Continue? (y/N)" << std::endl; - char buffer = getchar(); - if(buffer == 10 || (buffer != 121 && buffer != 89)) - #endif + std::clog << "Unable to load monsters! Continue? (y/N)" << std::endl; + char buffer = OTSYS_getch(); + if(buffer != 121 && buffer != 89) startupErrorMessage("Unable to load monsters!"); } - std::cout << ">> Loading mods..." << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading mods..."); - #endif - if(!ScriptingManager::getInstance()->loadMods()) - startupErrorMessage("Unable to load mods!"); - - std::cout << ">> Loading map and spawns..." << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading map and spawns..."); - #endif - if(!g_game.loadMap(g_config.getString(ConfigManager::MAP_NAME))) - startupErrorMessage(""); - - #ifdef __LOGIN_SERVER__ - std::cout << ">> Loading game servers" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading game servers"); - #endif - if(!GameServers::getInstance()->loadFromXml(true)) - startupErrorMessage("Unable to load game servers!"); - #endif - - #ifdef __REMOTE_CONTROL__ - std::cout << ">> Loading administration protocol" << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Loading administration protocol"); - #endif + if(fileExists(getFilePath(FILE_TYPE_OTHER, "npc/npcs.xml").c_str())) + { + std::clog << ">> Loading npcs" << std::endl; + if(!g_npcs.loadFromXml()) + { + std::clog << "Unable to load npcs! Continue? (y/N)" << std::endl; + char buffer = OTSYS_getch(); + if(buffer != 121 && buffer != 89) + startupErrorMessage("Unable to load npcs!"); + } + } - g_admin = new Admin(); - if(!g_admin->loadFromXml()) - startupErrorMessage("Unable to load administration protocol!"); + std::clog << ">> Loading raids" << std::endl; + Raids::getInstance()->loadFromXml(); - services->add(g_config.getNumber(ConfigManager::ADMIN_PORT)); - #endif + std::clog << ">> Loading map and spawns..." << std::endl; + if(!g_game.loadMap(g_config.getString(ConfigManager::MAP_NAME))) + startupErrorMessage(); - std::cout << ">> Checking world type... "; + std::clog << ">> Checking world type... "; std::string worldType = asLowerCaseString(g_config.getString(ConfigManager::WORLD_TYPE)); - if(worldType == "pvp" || worldType == "2" || worldType == "normal") + if(worldType == "open" || worldType == "2" || worldType == "openpvp") { - g_game.setWorldType(WORLD_TYPE_PVP); - std::cout << "PvP" << std::endl; + g_game.setWorldType(WORLDTYPE_OPEN); + std::clog << "Open PvP" << std::endl; } - else if(worldType == "no-pvp" || worldType == "nopvp" || worldType == "non-pvp" || worldType == "nonpvp" || worldType == "1" || worldType == "safe") + else if(worldType == "optional" || worldType == "1" || worldType == "optionalpvp") { - g_game.setWorldType(WORLD_TYPE_NO_PVP); - std::cout << "NoN-PvP" << std::endl; + g_game.setWorldType(WORLDTYPE_OPTIONAL); + std::clog << "Optional PvP" << std::endl; } - else if(worldType == "pvp-enforced" || worldType == "pvpenforced" || worldType == "pvp-enfo" || worldType == "pvpenfo" || worldType == "pvpe" || worldType == "enforced" || worldType == "enfo" || worldType == "3" || worldType == "war") + else if(worldType == "hardcore" || worldType == "3" || worldType == "hardcorepvp") { - g_game.setWorldType(WORLD_TYPE_PVP_ENFORCED); - std::cout << "PvP-Enforced" << std::endl; + g_game.setWorldType(WORLDTYPE_HARDCORE); + std::clog << "Hardcore PvP" << std::endl; } else { - std::cout << std::endl; + std::clog << std::endl; startupErrorMessage("Unknown world type: " + g_config.getString(ConfigManager::WORLD_TYPE)); } - std::cout << ">> Initializing game state modules and registering services..." << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> Initializing game state and registering services..."); - #endif - g_game.setGameState(GAME_STATE_INIT); + std::clog << ">> Starting to dominate the world... done." << std::endl + << ">> Initializing game state and binding services..." << std::endl; + g_game.setGameState(GAMESTATE_INIT); + IPAddressList ipList; - std::string ip = g_config.getString(ConfigManager::IP); - std::cout << "> Global address: " << ip << std::endl; - serverIps.push_back(std::make_pair(LOCALHOST, 0xFFFFFFFF)); + StringVec ip = explodeString(g_config.getString(ConfigManager::IP), ","); + if(asLowerCaseString(ip[0]) == "auto") + { + // TODO: automatic shit + } - char hostName[128]; - hostent* host = NULL; - if(!gethostname(hostName, 128) && (host = gethostbyname(hostName))) + IPAddress m_ip; + std::clog << "> Global IP address: "; + for(StringVec::iterator it = ip.begin(); it != ip.end(); ++it) { - uint8_t** address = (uint8_t**)host->h_addr_list; - while(address[0] != NULL) + uint32_t resolvedIp = inet_addr(it->c_str()); + if(resolvedIp == INADDR_NONE) { - serverIps.push_back(std::make_pair(*(uint32_t*)(*address), 0x0000FFFF)); - address++; + struct hostent* host = gethostbyname(it->c_str()); + if(!host) + { + std::clog << "..." << std::endl; + startupErrorMessage("Cannot resolve " + (*it) + "!"); + } + + resolvedIp = *(uint32_t*)host->h_addr; } + + m_ip = boost::asio::ip::address_v4(swap_uint32(resolvedIp)); + ipList.push_back(m_ip); + std::clog << m_ip.to_string() << std::endl; } - uint32_t resolvedIp = inet_addr(ip.c_str()); - if(resolvedIp == INADDR_NONE) + ipList.push_back(boost::asio::ip::address_v4(INADDR_LOOPBACK)); + if(!g_config.getBool(ConfigManager::BIND_ONLY_GLOBAL_ADDRESS)) { - if((host = gethostbyname(ip.c_str()))) - resolvedIp = *(uint32_t*)host->h_addr; - else - startupErrorMessage("Cannot resolve " + ip + "!"); + char hostName[128]; + if(!gethostname(hostName, 128)) + { + if(hostent* host = gethostbyname(hostName)) + { + std::stringstream s; + for(uint8_t** addr = (uint8_t**)host->h_addr_list; addr[0]; addr++) + { + uint32_t resolved = swap_uint32(*(uint32_t*)(*addr)); + if(m_ip.to_v4().to_ulong() == resolved) + continue; + + ipList.push_back(boost::asio::ip::address_v4(resolved)); + s << (int32_t)(addr[0][0]) << "." << (int32_t)(addr[0][1]) << "." + << (int32_t)(addr[0][2]) << "." << (int32_t)(addr[0][3]) << "\t"; + } + + if(s.str().size()) + std::clog << "> Local IP address(es): " << s.str() << std::endl; + } + } + + if(m_ip.to_v4().to_ulong() != LOCALHOST) + ipList.push_back(boost::asio::ip::address_v4(LOCALHOST)); } + else if(ipList.size() < 2) + startupErrorMessage("Unable to bind any IP address! You may want to disable \"bindOnlyGlobalAddress\" setting in config.lua"); - serverIps.push_back(std::make_pair(resolvedIp, 0)); - Status::getInstance()->setMapName(g_config.getString(ConfigManager::MAP_NAME)); + services->add(g_config.getNumber(ConfigManager::STATUS_PORT), ipList); + services->add(g_config.getNumber(ConfigManager::MANAGER_PORT), ipList); + #ifdef __OTADMIN__ + services->add(g_config.getNumber(ConfigManager::ADMIN_PORT), ipList); + #endif - services->add(g_config.getNumber(ConfigManager::STATUS_PORT)); + //services->add(8080, ipList); if( #ifdef __LOGIN_SERVER__ true @@ -826,621 +826,22 @@ ServiceManager* services) #endif ) { - services->add(g_config.getNumber(ConfigManager::LOGIN_PORT)); - services->add(g_config.getNumber(ConfigManager::LOGIN_PORT)); + services->add(g_config.getNumber(ConfigManager::LOGIN_PORT), ipList); + services->add(g_config.getNumber(ConfigManager::LOGIN_PORT), ipList); } - services->add(g_config.getNumber(ConfigManager::GAME_PORT)); - services->add(g_config.getNumber(ConfigManager::LOGIN_PORT)); - std::cout << "> Local ports: "; + services->add(g_config.getNumber(ConfigManager::LOGIN_PORT), ipList); + IntegerVec games = vectorAtoi(explodeString(g_config.getString(ConfigManager::GAME_PORT), ",")); + for(IntegerVec::const_iterator it = games.begin(); it != games.end(); ++it) + services->add(*it, ipList); + std::clog << "> Bound ports: "; std::list ports = services->getPorts(); for(std::list::iterator it = ports.begin(); it != ports.end(); ++it) - std::cout << (*it) << "\t"; - - std::cout << std::endl << ">> All modules were loaded, server is starting up..." << std::endl; - #if defined(WINDOWS) && !defined(__CONSOLE__) - SendMessage(GUI::getInstance()->m_statusBar, WM_SETTEXT, 0, (LPARAM)">> All modules were loaded, server is starting up..."); - #endif - g_game.setGameState(GAME_STATE_NORMAL); + std::clog << (*it) << "\t"; + std::clog << std::endl << ">> Everything smells good, server is starting up..." << std::endl; g_game.start(services); + g_game.setGameState(g_config.getBool(ConfigManager::START_CLOSED) ? GAMESTATE_CLOSED : GAMESTATE_NORMAL); g_loaderSignal.notify_all(); } - -#if defined(WINDOWS) && !defined(__CONSOLE__) -LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - CInputBox iBox(hwnd); - switch(message) - { - case WM_CREATE: - { - GUI::getInstance()->m_logWindow = CreateWindow("edit", NULL, - WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_VISIBLE | ES_MULTILINE | DS_CENTER, 0, 0, 640, 450, hwnd, (HMENU)ID_LOG, NULL, NULL); - GUI::getInstance()->m_statusBar = CreateWindowEx(0, STATUSCLASSNAME, NULL, - WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0, hwnd, (HMENU)ID_STATUS_BAR, GetModuleHandle(NULL), NULL); - - int32_t statusBarWidthLine[] = {150, -1}; - GUI::getInstance()->m_lineCount = 0; - - SendMessage(GUI::getInstance()->m_statusBar, SB_SETPARTS, sizeof(statusBarWidthLine) / sizeof(int32_t), (LPARAM)statusBarWidthLine); - SendMessage(GUI::getInstance()->m_statusBar, SB_SETTEXT, 0, (LPARAM)"Not loaded"); - - GUI::getInstance()->m_minimized = false; - GUI::getInstance()->m_pBox.setParent(hwnd); - SendMessage(GUI::getInstance()->m_logWindow, WM_SETFONT, (WPARAM)GUI::getInstance()->m_font, 0); - - NID.hWnd = hwnd; - NID.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON)); - NID.uCallbackMessage = WM_USER + 1; - NID.uFlags = NIF_TIP | NIF_ICON | NIF_MESSAGE; - - strcpy(NID.szTip, STATUS_SERVER_NAME); - Shell_NotifyIcon(NIM_ADD, &NID); - - boost::thread(boost::bind(&serverMain, (void*)hwnd)); - break; - } - - case WM_SIZE: - { - if(wParam == SIZE_MINIMIZED) - { - GUI::getInstance()->m_minimized = true; - ShowWindow(hwnd, SW_HIDE); - ModifyMenu(GUI::getInstance()->m_trayMenu, ID_TRAY_HIDE, MF_STRING, ID_TRAY_HIDE, "&Show window"); - } - else - { - RECT rcStatus; - int32_t iStatusHeight; - int32_t iEditHeight; - RECT rcClient; - GUI::getInstance()->m_statusBar = GetDlgItem(hwnd, ID_STATUS_BAR); - SendMessage(GUI::getInstance()->m_statusBar, WM_SIZE, 0, 0); - GetWindowRect(GUI::getInstance()->m_statusBar, &rcStatus); - iStatusHeight = rcStatus.bottom - rcStatus.top; - GetClientRect(hwnd, &rcClient); - iEditHeight = rcClient.bottom - iStatusHeight; - GUI::getInstance()->m_logWindow = GetDlgItem(hwnd, ID_LOG); - SetWindowPos(GUI::getInstance()->m_logWindow, NULL, 0, rcClient.top, rcClient.right, iEditHeight, SWP_NOZORDER); - } - - break; - } - - case WM_COMMAND: - { - switch(LOWORD(wParam)) - { - case ID_TRAY_HIDE: - { - if(GUI::getInstance()->m_minimized) - { - ShowWindow(hwnd, SW_SHOW); - ShowWindow(hwnd, SW_RESTORE); - ModifyMenu(GUI::getInstance()->m_trayMenu, ID_TRAY_HIDE, MF_STRING, ID_TRAY_HIDE, "&Hide window"); - GUI::getInstance()->m_minimized = false; - } - else - { - ShowWindow(hwnd, SW_HIDE); - ModifyMenu(GUI::getInstance()->m_trayMenu, ID_TRAY_HIDE, MF_STRING, ID_TRAY_HIDE, "&Show window"); - GUI::getInstance()->m_minimized = true; - } - - break; - } - - case ID_MENU_MAIN_ACCEPT: - { - if(g_game.getGameState() != GAME_STATE_STARTUP && !GUI::getInstance()->m_connections) - { - GUI::getInstance()->m_connections = true; - ModifyMenu(GetMenu(hwnd), ID_MENU_MAIN_ACCEPT, MF_STRING, ID_MENU_MAIN_REJECT, "&Reject connections"); - } - - break; - } - - case ID_MENU_MAIN_REJECT: - { - if(g_game.getGameState() != GAME_STATE_STARTUP && GUI::getInstance()->m_connections) - { - GUI::getInstance()->m_connections = false; - ModifyMenu(GetMenu(hwnd), ID_MENU_MAIN_REJECT, MF_STRING, ID_MENU_MAIN_ACCEPT, "&Accept connections"); - } - - break; - } - - case ID_MENU_MAIN_CLEARLOG: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - GUI::getInstance()->m_logText = ""; - GUI::getInstance()->m_lineCount = 0; - std::cout << STATUS_SERVER_NAME << ", version " << STATUS_SERVER_VERSION << " (" << STATUS_SERVER_CODENAME << ")" << std::endl; - std::cout << "Compiled with " << BOOST_COMPILER << " at " << __DATE__ << ", " << __TIME__ << "." << std::endl; - std::cout << "A server developed by Elf, slawkens, Talaturen, Lithium, KaczooH, Kiper, Kornholijo." << std::endl; - std::cout << "Visit our forum for updates, support and resources: http://otland.net." << std::endl; - std::cout << std::endl; - } - - break; - } - - case ID_TRAY_SHUTDOWN: - case ID_MENU_MAIN_SHUTDOWN: - { - if(MessageBox(hwnd, "Are you sure you want to shutdown the server?", "Shutdown", MB_YESNO) == IDYES) - { - Dispatcher::getInstance().addTask( - createTask(boost::bind(&Game::setGameState, &g_game, GAME_STATE_SHUTDOWN))); - Shell_NotifyIcon(NIM_DELETE, &NID); - } - - break; - } - - case ID_MENU_SERVER_WORLDTYPE_PVP: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - g_game.setWorldType(WORLD_TYPE_PVP); - std::cout << "WorldType set to 'PVP'." << std::endl; - } - - break; - } - - case ID_MENU_SERVER_WORLDTYPE_NOPVP: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - g_game.setWorldType(WORLD_TYPE_NO_PVP); - std::cout << "WorldType set to 'Non-PVP'." << std::endl; - } - - break; - } - - case ID_MENU_SERVER_WORLDTYPE_PVPENFORCED: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - g_game.setWorldType(WORLD_TYPE_PVP_ENFORCED); - std::cout << "WorldType set to 'PVP-Enforced'." << std::endl; - } - - break; - } - - case ID_MENU_SERVER_BROADCAST: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(iBox.DoModal("Broadcast message", "What would you like to broadcast?")) - g_game.broadcastMessage(iBox.Text, MSG_STATUS_WARNING); - } - - break; - } - - case ID_MENU_SERVER_SAVE: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - Dispatcher::getInstance().addTask(createTask( - boost::bind(&Game::saveGameState, &g_game, false))); - MessageBox(NULL, "Server has been saved.", "Server save", MB_OK); - } - - break; - } - - case ID_MENU_SERVER_CLEAN: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - uint32_t count = 0; - g_game.cleanMap(count); - - char buffer[100]; - sprintf(buffer, "Map has been cleaned, collected %u items.", count); - MessageBox(NULL, buffer, "Map clean", MB_OK); - } - - break; - } - - case ID_MENU_SERVER_REFRESH: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - g_game.proceduralRefresh(); - MessageBox(NULL, "Map will now refresh in a while.", "Map refresh", MB_OK); - } - - break; - } - - case ID_MENU_SERVER_OPEN: - { - if(g_game.getGameState() != GAME_STATE_STARTUP && GUI::getInstance()->m_connections) - { - g_game.setGameState(GAME_STATE_NORMAL); - ModifyMenu(GetMenu(hwnd), ID_MENU_SERVER_OPEN, MF_STRING, ID_MENU_SERVER_CLOSE, "&Close server"); - } - - break; - } - - case ID_MENU_SERVER_CLOSE: - { - if(g_game.getGameState() != GAME_STATE_STARTUP && GUI::getInstance()->m_connections) - { - Dispatcher::getInstance().addTask(createTask( - boost::bind(&Game::setGameState, &g_game, GAME_STATE_CLOSED))); - ModifyMenu(GetMenu(hwnd), ID_MENU_SERVER_CLOSE, MF_STRING, ID_MENU_SERVER_OPEN, "&Open server"); - } - - break; - } - - case ID_MENU_SERVER_PLAYERBOX: - { - if(g_game.getGameState() != GAME_STATE_STARTUP && GUI::getInstance()->m_connections) - { - if(g_game.getPlayersOnline() == 0) - MessageBox(NULL, "No players online.", "Player management", MB_OK); - else - GUI::getInstance()->m_pBox.popUp("Player management"); - } - - break; - } - - case ID_MENU_RELOAD_ACTIONS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_ACTIONS)) - std::cout << "Reloaded actions." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_CHAT: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_CHAT)) - std::cout << "Reloaded chat channels." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_CONFIG: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_CONFIG)) - std::cout << "Reloaded config." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_CREATUREEVENTS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_CREATUREEVENTS)) - std::cout << "Reloaded creature events." << std::endl; - } - - break; - } - - #ifdef __LOGIN_SERVER__ - case ID_MENU_RELOAD_GAMESERVERS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_GAMESERVERS)) - std::cout << "Reloaded game servers." << std::endl; - } - - break; - } - - #endif - case ID_MENU_RELOAD_GLOBALEVENTS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_GLOBALEVENTS)) - std::cout << "Reloaded global events." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_GROUPS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_GROUPS)) - std::cout << "Reloaded groups." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_HIGHSCORES: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_HIGHSCORES)) - std::cout << "Reloaded highscores." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_HOUSEPRICES: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_HOUSEPRICES)) - std::cout << "Reloaded house prices." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_ITEMS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_ITEMS)) - std::cout << "Reloaded items." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_MODS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_MODS)) - std::cout << "Reloaded mods." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_MONSTERS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_MONSTERS)) - std::cout << "Reloaded monsters." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_MOVEMENTS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_MOVEEVENTS)) - std::cout << "Reloaded movements." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_NPCS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_NPCS)) - std::cout << "Reloaded npcs." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_OUTFITS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_OUTFITS)) - std::cout << "Reloaded outfits." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_QUESTS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_QUESTS)) - std::cout << "Reloaded quests." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_RAIDS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_RAIDS)) - std::cout << "Reloaded raids." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_SPELLS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_SPELLS)) - std::cout << "Reloaded spells." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_STAGES: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_STAGES)) - std::cout << "Reloaded stages." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_TALKACTIONS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_TALKACTIONS)) - std::cout << "Reloaded talk actions." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_VOCATIONS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_VOCATIONS)) - std::cout << "Reloaded vocations." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_WEAPONS: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_WEAPONS)) - std::cout << "Reloaded weapons." << std::endl; - } - - break; - } - - case ID_MENU_RELOAD_ALL: - { - if(g_game.getGameState() != GAME_STATE_STARTUP) - { - if(g_game.reloadInfo(RELOAD_ALL)) - std::cout << "Reloaded all." << std::endl; - } - - break; - } - - default: - break; - } - - break; - } - - case WM_CLOSE: - case WM_DESTROY: - { - if(MessageBox(hwnd, "Are you sure you want to shutdown the server?", "Shutdown", MB_YESNO) == IDYES) - { - Shell_NotifyIcon(NIM_DELETE, &NID); - Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::setGameState, &g_game, GAME_STATE_SHUTDOWN))); - } - - break; - } - - case WM_USER + 1: // tray icon messages - { - switch(lParam) - { - case WM_RBUTTONUP: // right click - { - POINT mp; - GetCursorPos(&mp); - TrackPopupMenu(GetSubMenu(GUI::getInstance()->m_trayMenu, 0), 0, mp.x, mp.y, 0, hwnd, 0); - break; - } - - case WM_LBUTTONUP: // left click - { - if(GUI::getInstance()->m_minimized) - { - ShowWindow(hwnd, SW_SHOW); - ShowWindow(hwnd, SW_RESTORE); - ModifyMenu(GUI::getInstance()->m_trayMenu, ID_TRAY_HIDE, MF_STRING, ID_TRAY_HIDE, "&Hide window"); - GUI::getInstance()->m_minimized = false; - } - - break; - } - } - - break; - } - - default: - return DefWindowProc(hwnd, message, wParam, lParam); - } - - return 0; -} - -int32_t WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int32_t WindowStyle) -{ - MSG messages; - WNDCLASSEX wincl; - GUI::getInstance()->initTrayMenu(); - GUI::getInstance()->initFont(); - wincl.hInstance = hInstance; - wincl.lpszClassName = "forgottenserver_gui"; - wincl.lpfnWndProc = WindowProcedure; - wincl.style = CS_DBLCLKS; - wincl.cbSize = sizeof(WNDCLASSEX); - wincl.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON)); - wincl.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON), IMAGE_ICON, 16, 16, 0); - wincl.hCursor = LoadCursor(NULL, IDC_ARROW); - wincl.lpszMenuName = MAKEINTRESOURCE(ID_MENU); - wincl.cbClsExtra = 0; - wincl.cbWndExtra = 0; - wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND; - if(!RegisterClassEx(&wincl)) - return 0; - - GUI::getInstance()->m_mainWindow = CreateWindowEx(0, "forgottenserver_gui", STATUS_SERVER_NAME, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 450, HWND_DESKTOP, NULL, hInstance, NULL); - ShowWindow(GUI::getInstance()->m_mainWindow, 1); - while(GetMessage(&messages, NULL, 0, 0)) - { - TranslateMessage(&messages); - DispatchMessage(&messages); - } - - return messages.wParam; -} -#endif diff --git a/otsystem.h b/otsystem.h index b077328..859b7a0 100644 --- a/otsystem.h +++ b/otsystem.h @@ -34,54 +34,103 @@ #include #include #include +#include -#include -#include -#include +#include +#include +#ifdef __GXX_EXPERIMENTAL_CXX0X__ + #include +#else + #include +#endif + +#ifndef __x86_64__ + #ifdef _M_X64 // msvc + #define __x86_64__ 1 + #else + #define __x86_64__ 0 + #endif +#endif -#include -#include +#include +#include #ifdef WINDOWS -#include -#include + #include + #include -#define OTSERV_ACCESS(file, mode) _access(file, mode); -inline int64_t OTSYS_TIME() -{ - _timeb t; - _ftime(&t); - return ((int64_t)t.millitm) + ((int64_t)t.time) * 1000; -} + #ifndef access + #define access _access + #endif + + #ifndef timeb + #define timeb _timeb + #endif + + #ifndef ftime + #define ftime _ftime + #endif + + #ifndef EWOULDBLOCK + #define EWOULDBLOCK WSAEWOULDBLOCK + #endif + + #ifndef errno + #define errno WSAGetLastError() + #endif + + #ifndef OTSYS_SLEEP + #define OTSYS_SLEEP(n) Sleep(n) + #endif #else -#include -#include -#include + #include + #include + #include + + #include + #include + #include + + #include + #include + + #ifndef SOCKET + #define SOCKET int32_t + #endif + + #ifndef closesocket + #define closesocket close + #endif + + #ifndef SOCKADDR + #define SOCKADDR sockaddr + #endif -#include -#include -#include -#include + #ifndef SOCKET_ERROR + #define SOCKET_ERROR -1 + #endif -#include -#include + inline void OTSYS_SLEEP(int32_t n) + { + timespec tv; + tv.tv_sec = n / 1000; + tv.tv_nsec = (n % 1000) * 1000000; + nanosleep(&tv, NULL); + } +#endif -#define OTSERV_ACCESS(file, mode) access(file, mode); inline int64_t OTSYS_TIME() { timeb t; ftime(&t); return ((int64_t)t.millitm) + ((int64_t)t.time) * 1000; } -#endif -#ifdef __GNUC__ - #define __OTSERV_FUNCTION__ __PRETTY_FUNCTION__ -#elif defined(_MSC_VER) - #define __OTSERV_FUNCTION__ __FUNCDNAME__ -#endif +inline uint32_t swap_uint32(uint32_t val) +{ + val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); + return (val << 16) | (val >> 16); +} #define foreach BOOST_FOREACH #define reverse_foreach BOOST_REVERSE_FOREACH - -typedef std::vector > IpList; #endif diff --git a/outfit.cpp b/outfit.cpp index b0c20da..b1d5f4f 100644 --- a/outfit.cpp +++ b/outfit.cpp @@ -35,14 +35,14 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) int32_t intValue; if(!readXMLInteger(p, "id", intValue)) { - std::cout << "[Error - Outfits::parseOutfitNode] Missing outfit id, skipping" << std::endl; + std::clog << "[Error - Outfits::parseOutfitNode] Missing outfit id, skipping" << std::endl; return false; } Outfit newOutfit; newOutfit.outfitId = intValue; - std::string name, strValue; + std::string strValue; if(readXMLString(p, "default", strValue)) newOutfit.isDefault = booleanString(strValue); @@ -50,10 +50,10 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) { std::stringstream ss; ss << "Outfit #" << newOutfit.outfitId; - ss >> name; + ss >> newOutfit.name; } else - name = strValue; + newOutfit.name = strValue; bool override = false; if(readXMLString(p, "override", strValue) && booleanString(strValue)) @@ -62,15 +62,22 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) if(readXMLInteger(p, "access", intValue)) newOutfit.accessLevel = intValue; - if(readXMLInteger(p, "quest", intValue)) + if(readXMLString(p, "group", strValue) || readXMLString(p, "groups", strValue)) { - newOutfit.storageId = intValue; + newOutfit.groups.clear(); + if(!parseIntegerVec(strValue, newOutfit.groups)) + std::clog << "[Warning - Outfits::parseOutfitNode] Invalid group(s) for an outfit with id " << newOutfit.outfitId << std::endl; + } + + if(readXMLString(p, "quest", strValue)) + { + newOutfit.storageId = strValue; newOutfit.storageValue = "1"; } else { - if(readXMLInteger(p, "storageId", intValue)) - newOutfit.storageId = intValue; + if(readXMLString(p, "storageId", strValue)) + newOutfit.storageId = strValue; if(readXMLString(p, "storageValue", strValue)) newOutfit.storageValue = strValue; @@ -87,14 +94,14 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) Outfit outfit = newOutfit; if(!readXMLInteger(listNode, "looktype", intValue) && !readXMLInteger(listNode, "lookType", intValue)) { - std::cout << "[Error - Outfits::parseOutfitNode] Missing looktype for an outfit with id " << outfit.outfitId << std::endl; + std::clog << "[Error - Outfits::parseOutfitNode] Missing looktype for an outfit with id " << outfit.outfitId << std::endl; continue; } outfit.lookType = intValue; if(!readXMLString(listNode, "gender", strValue) && !readXMLString(listNode, "type", strValue) && !readXMLString(listNode, "sex", strValue)) { - std::cout << "[Error - Outfits::parseOutfitNode] Missing gender(s) for an outfit with id " << outfit.outfitId + std::clog << "[Error - Outfits::parseOutfitNode] Missing gender(s) for an outfit with id " << outfit.outfitId << " and looktype " << outfit.lookType << std::endl; continue; } @@ -102,7 +109,7 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) IntegerVec intVector; if(!parseIntegerVec(strValue, intVector)) { - std::cout << "[Error - Outfits::parseOutfitNode] Invalid gender(s) for an outfit with id " << outfit.outfitId + std::clog << "[Error - Outfits::parseOutfitNode] Invalid gender(s) for an outfit with id " << outfit.outfitId << " and looktype " << outfit.lookType << std::endl; continue; } @@ -112,8 +119,9 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) if(readXMLString(listNode, "name", strValue)) outfit.name = strValue; - else - outfit.name = name; + + if(readXMLString(listNode, "premium", strValue)) + outfit.isPremium = booleanString(strValue); if(readXMLString(listNode, "requirement", strValue)) { @@ -127,7 +135,7 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) else if(tmpStrValue == "any") outfit.requirement = REQUIREMENT_ANY; else if(tmpStrValue != "both") - std::cout << "[Warning - Outfits::loadFromXml] Unknown requirement tag value, using default (both)" << std::endl; + std::clog << "[Warning - Outfits::loadFromXml] Unknown requirement tag value, using default (both)" << std::endl; } if(readXMLString(listNode, "manaShield", strValue)) @@ -163,14 +171,17 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) if(readXMLInteger(listNode, "speed", intValue)) outfit.speed = intValue; + if(readXMLInteger(listNode, "attackspeed", intValue) || readXMLInteger(listNode, "attackSpeed", intValue)) + outfit.attackSpeed = intValue; + for(xmlNodePtr configNode = listNode->children; configNode != NULL; configNode = configNode->next) { if(!xmlStrcmp(configNode->name, (const xmlChar*)"reflect")) { if(readXMLInteger(configNode, "percentAll", intValue)) { - for(uint32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) - outfit.reflect[REFLECT_PERCENT][(CombatType_t)i] += intValue; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) + outfit.reflect[REFLECT_PERCENT][i] += intValue; } if(readXMLInteger(configNode, "percentElements", intValue)) @@ -229,8 +240,8 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) if(readXMLInteger(configNode, "chanceAll", intValue)) { - for(uint32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) - outfit.reflect[REFLECT_CHANCE][(CombatType_t)i] += intValue; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) + outfit.reflect[REFLECT_CHANCE][i] += intValue; } if(readXMLInteger(configNode, "chanceElements", intValue)) @@ -291,8 +302,8 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) { if(readXMLInteger(configNode, "percentAll", intValue)) { - for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) - outfit.absorb[(CombatType_t)i] += intValue; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) + outfit.absorb[i] += intValue; } if(readXMLInteger(configNode, "percentElements", intValue)) @@ -471,7 +482,7 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) outfit.conditionSuppressions |= CONDITION_ENERGY; if(readXMLString(configNode, "physical", strValue) && booleanString(strValue)) - outfit.conditionSuppressions |= CONDITION_PHYSICAL; + outfit.conditionSuppressions |= CONDITION_BLEEDING; if(readXMLString(configNode, "haste", strValue) && booleanString(strValue)) outfit.conditionSuppressions |= CONDITION_HASTE; @@ -546,7 +557,7 @@ bool Outfits::parseOutfitNode(xmlNodePtr p) add = true; } else - std::cout << "[Warning - Outfits::parseOutfitNode] Duplicated outfit for gender " << (*it) << " with lookType " << outfit.outfitId << std::endl; + std::clog << "[Warning - Outfits::parseOutfitNode] Duplicated outfit for gender " << (*it) << " with lookType " << outfit.outfitId << std::endl; } else { @@ -568,25 +579,21 @@ bool Outfits::loadFromXml() xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "outfits.xml").c_str()); if(!doc) { - std::cout << "[Warning - Outfits::loadFromXml] Cannot load outfits file, using defaults." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Outfits::loadFromXml] Cannot load outfits file, using defaults." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr p, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"outfits")) { - std::cout << "[Error - Outfits::loadFromXml] Malformed outfits file." << std::endl; + std::clog << "[Error - Outfits::loadFromXml] Malformed outfits file." << std::endl; xmlFreeDoc(doc); return false; } - p = root->children; - while(p) - { + for(xmlNodePtr p = root->children; p; p = p->next) parseOutfitNode(p); - p = p->next; - } xmlFreeDoc(doc); return true; @@ -820,7 +827,7 @@ int16_t Outfits::getOutfitReflect(uint32_t lookType, uint16_t sex, CombatType_t if(it->second.lookType != lookType) continue; - if(it->second.reflect[REFLECT_CHANCE][combat] < random_range(0, 100)) + if(it->second.reflect[REFLECT_PERCENT][combat] && it->second.reflect[REFLECT_CHANCE][combat] >= random_range(1, 100)) return it->second.reflect[REFLECT_PERCENT][combat]; } diff --git a/outfit.h b/outfit.h index 141f07e..36e4fa7 100644 --- a/outfit.h +++ b/outfit.h @@ -20,8 +20,8 @@ #include "otsystem.h" #include "enums.h" - -#define OUTFITS_MAX_NUMBER 25 +#include "const.h" +#include "tools.h" enum AddonRequirement_t { @@ -48,8 +48,8 @@ struct Outfit isDefault = true; requirement = REQUIREMENT_BOTH; isPremium = manaShield = invisible = regeneration = false; - outfitId = lookType = addons = accessLevel = storageId = 0; - speed = healthGain = healthTicks = manaGain = manaTicks = conditionSuppressions = 0; + outfitId = lookType = addons = accessLevel = speed = attackSpeed = 0; + healthGain = healthTicks = manaGain = manaTicks = conditionSuppressions = 0; } bool isDefault, isPremium, manaShield, invisible, regeneration; @@ -58,10 +58,11 @@ struct Outfit uint16_t accessLevel, addons; int32_t skills[SKILL_LAST + 1], skillsPercent[SKILL_LAST + 1], stats[STAT_LAST + 1], statsPercent[STAT_LAST + 1], - speed, healthGain, healthTicks, manaGain, manaTicks, conditionSuppressions; + speed, attackSpeed, healthGain, healthTicks, manaGain, manaTicks, conditionSuppressions; - uint32_t outfitId, lookType, storageId; - std::string name, storageValue; + uint32_t outfitId, lookType; + std::string name, storageId, storageValue; + IntegerVec groups; }; typedef std::list OutfitList; diff --git a/outputmessage.cpp b/outputmessage.cpp index 07228e3..7834497 100644 --- a/outputmessage.cpp +++ b/outputmessage.cpp @@ -62,7 +62,7 @@ void OutputMessagePool::send(OutputMessage_ptr msg) if(state == OutputMessage::STATE_ALLOCATED_NO_AUTOSEND) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Sending message - SINGLE" << std::endl; + std::clog << "Sending message - SINGLE" << std::endl; #endif if(msg->getConnection()) { @@ -71,12 +71,12 @@ void OutputMessagePool::send(OutputMessage_ptr msg) } #ifdef __DEBUG_NET__ else - std::cout << "Error: [OutputMessagePool::send] NULL connection." << std::endl; + std::clog << "[Error - OutputMessagePool::send] NULL connection." << std::endl; #endif } #ifdef __DEBUG_NET__ else - std::cout << "Warning: [OutputMessagePool::send] State != STATE_ALLOCATED_NO_AUTOSEND" << std::endl; + std::clog << "[Warning - OutputMessagePool::send] State != STATE_ALLOCATED_NO_AUTOSEND" << std::endl; #endif } @@ -110,11 +110,11 @@ void OutputMessagePool::sendAll() if(true) #else //It will send only messages bigger then 1 kb or with a lifetime greater than 10 ms - if(omsg->getMessageLength() > 1024 || (m_frameTime - omsg->getFrame() > 10)) + if(omsg->size() > 1024 || (m_frameTime - omsg->getFrame() > 10)) #endif { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Sending message - ALL" << std::endl; + std::clog << "Sending message - ALL" << std::endl; #endif if(omsg->getConnection()) { @@ -123,7 +123,7 @@ void OutputMessagePool::sendAll() } #ifdef __DEBUG_NET__ else - std::cout << "Error: [OutputMessagePool::send] NULL connection." << std::endl; + std::clog << "[Error - OutputMessagePool::send] NULL connection." << std::endl; #endif it = m_autoSend.erase(it); @@ -144,12 +144,12 @@ void OutputMessagePool::internalReleaseMessage(OutputMessage* msg) if(msg->getProtocol()) msg->getProtocol()->unRef(); else - std::cout << "[Warning - OutputMessagePool::internalReleaseMessage] protocol not found." << std::endl; + std::clog << "[Warning - OutputMessagePool::internalReleaseMessage] protocol not found." << std::endl; if(msg->getConnection()) msg->getConnection()->unRef(); else - std::cout << "[Warning - OutputMessagePool::internalReleaseMessage] connection not found." << std::endl; + std::clog << "[Warning - OutputMessagePool::internalReleaseMessage] connection not found." << std::endl; msg->freeMessage(); #ifdef __TRACK_NETWORK__ @@ -161,10 +161,10 @@ void OutputMessagePool::internalReleaseMessage(OutputMessage* msg) m_outputPoolLock.unlock(); } -OutputMessage_ptr OutputMessagePool::getOutputMessage(Protocol* protocol, bool autoSend /*= true*/) +OutputMessage_ptr OutputMessagePool::getOutputMessage(Protocol* protocol, bool autoSend/* = true*/) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "request output message - auto = " << autoSend << std::endl; + std::clog << "request output message - auto = " << autoSend << std::endl; #endif if(m_shutdown) return OutputMessage_ptr(); @@ -197,7 +197,7 @@ OutputMessage_ptr OutputMessagePool::getOutputMessage(Protocol* protocol, bool a void OutputMessagePool::configureOutputMessage(OutputMessage_ptr msg, Protocol* protocol, bool autoSend) { TRACK_MESSAGE(msg); - msg->Reset(); + msg->reset(); if(autoSend) { msg->setState(OutputMessage::STATE_ALLOCATED); diff --git a/outputmessage.h b/outputmessage.h index 44907fd..98326df 100644 --- a/outputmessage.h +++ b/outputmessage.h @@ -42,20 +42,20 @@ class OutputMessage : public NetworkMessage, boost::noncopyable Protocol* getProtocol() const {return m_protocol;} Connection_ptr getConnection() const {return m_connection;} - char* getOutputBuffer() {return (char*)&m_MsgBuf[m_outputBufferStart];} + char* getOutputBuffer() {return (char*)&m_buffer[m_outputBufferStart];} uint64_t getFrame() const {return m_frame;} - void writeMessageLength() {addHeader((uint16_t)(m_MsgSize));} + void writeMessageLength() {addHeader((uint16_t)(m_size));} void addCryptoHeader(bool addChecksum) { if(addChecksum) - addHeader((uint32_t)(adlerChecksum((uint8_t*)(m_MsgBuf + m_outputBufferStart), m_MsgSize))); + addHeader((adlerChecksum((uint8_t*)(m_buffer + m_outputBufferStart), m_size))); - addHeader((uint16_t)(m_MsgSize)); + addHeader((uint16_t)(m_size)); } #ifdef __TRACK_NETWORK__ - virtual void Track(std::string file, int64_t line, std::string func) + virtual void track(std::string file, int32_t line, std::string func) { if(lastUses.size() >= 25) lastUses.pop_front(); @@ -70,11 +70,11 @@ class OutputMessage : public NetworkMessage, boost::noncopyable lastUses.clear(); } - void PrintTrace() + void printTrace() { uint32_t n = 1; for(std::list::const_reverse_iterator it = lastUses.rbegin(); it != lastUses.rend(); ++it, ++n) - std::cout << "\t" << n << ".\t" << (*it) << std::endl; + std::clog << "\t" << n << ".\t" << (*it) << std::endl; } #endif @@ -92,13 +92,13 @@ class OutputMessage : public NetworkMessage, boost::noncopyable { if((int32_t)m_outputBufferStart - (int32_t)sizeof(T) < 0) { - std::cout << "[Error - OutputMessage::addHeader] m_outputBufferStart(" << m_outputBufferStart << ") < " << sizeof(T) << std::endl; + std::clog << "[Error - OutputMessage::addHeader] m_outputBufferStart(" << m_outputBufferStart << ") < " << sizeof(T) << std::endl; return; } m_outputBufferStart -= sizeof(T); - *(T*)(m_MsgBuf + m_outputBufferStart) = value; - m_MsgSize += sizeof(T); + *(T*)(m_buffer + m_outputBufferStart) = value; + m_size += sizeof(T); } void freeMessage() @@ -136,8 +136,6 @@ class OutputMessage : public NetworkMessage, boost::noncopyable uint32_t m_outputBufferStart; }; -typedef boost::shared_ptr OutputMessage_ptr; - class OutputMessagePool { private: @@ -192,7 +190,7 @@ class OutputMessagePool }; #ifdef __TRACK_NETWORK__ - #define TRACK_MESSAGE(omsg) (omsg)->Track(__FILE__, __LINE__, __FUNCTION__) + #define TRACK_MESSAGE(omsg) (omsg)->track(__FILE__, __LINE__, __FUNCTION__) #else #define TRACK_MESSAGE(omsg) #endif diff --git a/party.cpp b/party.cpp index ae26ec9..fb17a13 100644 --- a/party.cpp +++ b/party.cpp @@ -33,7 +33,7 @@ Party::Party(Player* _leader) { leader = _leader; leader->setParty(this); - leader->sendPlayerPartyIcons(leader); + leader->sendPlayerIcons(leader); } } @@ -41,15 +41,15 @@ void Party::disband() { leader->sendClosePrivate(CHANNEL_PARTY); leader->setParty(NULL); - leader->sendTextMessage(MSG_INFO_DESCR, "Your party has been disbanded."); + leader->sendTextMessage(MSG_PARTY, "Your party has been disbanded."); - leader->sendPlayerPartyIcons(leader); + leader->sendPlayerIcons(leader); for(PlayerVector::iterator it = inviteList.begin(); it != inviteList.end(); ++it) { (*it)->removePartyInvitation(this); - (*it)->sendPlayerPartyIcons(leader); - (*it)->sendPlayerPartyIcons(*it); - leader->sendPlayerPartyIcons(*it); + (*it)->sendPlayerIcons(leader); + (*it)->sendPlayerIcons(*it); + leader->sendPlayerIcons(*it); } inviteList.clear(); @@ -57,11 +57,11 @@ void Party::disband() { (*it)->sendClosePrivate(CHANNEL_PARTY); (*it)->setParty(NULL); - (*it)->sendTextMessage(MSG_INFO_DESCR, "Your party has been disbanded."); + (*it)->sendTextMessage(MSG_PARTY, "Your party has been disbanded."); - (*it)->sendPlayerPartyIcons(*it); - (*it)->sendPlayerPartyIcons(leader); - leader->sendPlayerPartyIcons(*it); + (*it)->sendPlayerIcons(*it); + (*it)->sendPlayerIcons(leader); + leader->sendPlayerIcons(*it); } memberList.clear(); @@ -100,8 +100,8 @@ bool Party::leave(Player* player) player->setParty(NULL); player->sendClosePrivate(CHANNEL_PARTY); - player->sendTextMessage(MSG_INFO_DESCR, "You have left the party."); - player->sendPlayerPartyIcons(player); + player->sendTextMessage(MSG_PARTY, "You have left the party."); + player->sendPlayerIcons(player); updateSharedExperience(); updateIcons(player); @@ -110,7 +110,7 @@ bool Party::leave(Player* player) char buffer[105]; sprintf(buffer, "%s has left the party.", player->getName().c_str()); - broadcastMessage(MSG_INFO_DESCR, buffer); + broadcastMessage(MSG_PARTY, buffer); if(missingLeader || canDisband()) disband(); @@ -133,9 +133,9 @@ bool Party::passLeadership(Player* player) char buffer[125]; sprintf(buffer, "%s is now the leader of the party.", player->getName().c_str()); - broadcastMessage(MSG_INFO_DESCR, buffer, true); + broadcastMessage(MSG_PARTY, buffer, true); - player->sendTextMessage(MSG_INFO_DESCR, "You are now the leader of the party."); + player->sendTextMessage(MSG_PARTY, "You are now the leader of the party."); updateSharedExperience(); updateIcons(oldLeader); @@ -158,10 +158,10 @@ bool Party::join(Player* player) char buffer[200]; sprintf(buffer, "%s has joined the party.", player->getName().c_str()); - broadcastMessage(MSG_INFO_DESCR, buffer); + broadcastMessage(MSG_PARTY, buffer); sprintf(buffer, "You have joined %s'%s party. Open the party channel to communicate with your companions.", leader->getName().c_str(), (leader->getName()[leader->getName().length() - 1] == 's' ? "" : "s")); - player->sendTextMessage(MSG_INFO_DESCR, buffer); + player->sendTextMessage(MSG_PARTY, buffer); updateSharedExperience(); updateIcons(player); @@ -177,8 +177,8 @@ bool Party::removeInvite(Player* player) if(it != inviteList.end()) inviteList.erase(it); - leader->sendPlayerPartyIcons(player); - player->sendPlayerPartyIcons(leader); + leader->sendPlayerIcons(player); + player->sendPlayerIcons(leader); player->removePartyInvitation(this); if(canDisband()) @@ -194,10 +194,10 @@ void Party::revokeInvitation(Player* player) char buffer[150]; sprintf(buffer, "%s has revoked %s invitation.", leader->getName().c_str(), (leader->getSex(false) ? "his" : "her")); - player->sendTextMessage(MSG_INFO_DESCR, buffer); + player->sendTextMessage(MSG_PARTY, buffer); sprintf(buffer, "Invitation for %s has been revoked.", player->getName().c_str()); - leader->sendTextMessage(MSG_INFO_DESCR, buffer); + leader->sendTextMessage(MSG_PARTY, buffer); removeInvite(player); } @@ -211,13 +211,13 @@ bool Party::invitePlayer(Player* player) char buffer[150]; sprintf(buffer, "%s has been invited.%s", player->getName().c_str(), (!memberList.size() ? " Open the party channel to communicate with your members." : "")); - leader->sendTextMessage(MSG_INFO_DESCR, buffer); + leader->sendTextMessage(MSG_PARTY, buffer); sprintf(buffer, "%s has invited you to %s party.", leader->getName().c_str(), (leader->getSex(false) ? "his" : "her")); - player->sendTextMessage(MSG_INFO_DESCR, buffer); + player->sendTextMessage(MSG_PARTY, buffer); - leader->sendPlayerPartyIcons(player); - player->sendPlayerPartyIcons(leader); + leader->sendPlayerIcons(player); + player->sendPlayerIcons(leader); return true; } @@ -229,18 +229,18 @@ void Party::updateIcons(Player* player) PlayerVector::iterator it; for(it = memberList.begin(); it != memberList.end(); ++it) { - (*it)->sendPlayerPartyIcons(player); - player->sendPlayerPartyIcons((*it)); + (*it)->sendPlayerIcons(player); + player->sendPlayerIcons((*it)); } for(it = inviteList.begin(); it != inviteList.end(); ++it) { - (*it)->sendPlayerPartyIcons(player); - player->sendPlayerPartyIcons((*it)); + (*it)->sendPlayerIcons(player); + player->sendPlayerIcons((*it)); } - leader->sendPlayerPartyIcons(player); - player->sendPlayerPartyIcons(leader); + leader->sendPlayerIcons(player); + player->sendPlayerIcons(leader); } void Party::updateAllIcons() @@ -249,15 +249,15 @@ void Party::updateAllIcons() for(it = memberList.begin(); it != memberList.end(); ++it) { for(PlayerVector::iterator iit = memberList.begin(); iit != memberList.end(); ++iit) - (*it)->sendPlayerPartyIcons((*iit)); + (*it)->sendPlayerIcons((*iit)); - (*it)->sendPlayerPartyIcons(leader); - leader->sendPlayerPartyIcons((*it)); + (*it)->sendPlayerIcons(leader); + leader->sendPlayerIcons((*it)); } - leader->sendPlayerPartyIcons(leader); + leader->sendPlayerIcons(leader); for(it = inviteList.begin(); it != inviteList.end(); ++it) - (*it)->sendPlayerPartyIcons(leader); + (*it)->sendPlayerIcons(leader); } void Party::broadcastMessage(MessageClasses messageClass, const std::string& text, bool sendToInvitations/* = false*/) @@ -303,35 +303,37 @@ bool Party::setSharedExperience(Player* player, bool _sharedExpActive) { sharedExpEnabled = canEnableSharedExperience(); if(sharedExpEnabled) - leader->sendTextMessage(MSG_INFO_DESCR, "Shared Experience is now active."); + leader->sendTextMessage(MSG_PARTY_MANAGEMENT, "Shared Experience is now active."); else - leader->sendTextMessage(MSG_INFO_DESCR, "Shared Experience has been activated, but some members of your party are inactive."); + leader->sendTextMessage(MSG_PARTY_MANAGEMENT, "Shared Experience has been activated, but some members of your party are inactive."); } else - leader->sendTextMessage(MSG_INFO_DESCR, "Shared Experience has been deactivated."); + leader->sendTextMessage(MSG_PARTY_MANAGEMENT, "Shared Experience has been deactivated."); updateAllIcons(); return true; } -void Party::shareExperience(double experience, bool fromMonster, bool multiplied) +void Party::shareExperience(double experience, Creature* target, bool multiplied) { - double shareExperience = (experience / (double)(memberList.size() + 1)); - if(experience > (double)g_config.getNumber(ConfigManager::EXTRA_PARTY_LIMIT)) - shareExperience += (experience * (double)(g_config.getNumber(ConfigManager::EXTRA_PARTY_PERCENT) / 100)); + double shareExperience = experience; + if(experience >= (double)g_config.getNumber(ConfigManager::EXTRA_PARTY_LIMIT)) + shareExperience += (experience * ((double)g_config.getNumber(ConfigManager::EXTRA_PARTY_PERCENT) / 100)); - double tmpExperience = shareExperience; - leader->onGainSharedExperience(tmpExperience, fromMonster, multiplied); + shareExperience /= memberList.size() + 1; + double tmpExperience = shareExperience; //we need this, as onGainSharedExperience increases the value + + leader->onGainSharedExperience(tmpExperience, target, multiplied); for(PlayerVector::iterator it = memberList.begin(); it != memberList.end(); ++it) { tmpExperience = shareExperience; - (*it)->onGainSharedExperience(tmpExperience, fromMonster, multiplied); + (*it)->onGainSharedExperience(tmpExperience, target, multiplied); } } bool Party::canUseSharedExperience(const Player* player, uint32_t highestLevel/* = 0*/) const { - if(!player || player->isRemoved()) + if(!player || player->isRemoved() || !memberList.size()) return false; if(!highestLevel) @@ -379,9 +381,6 @@ bool Party::canEnableSharedExperience() void Party::addPlayerHealedMember(Player* player, uint32_t points) { - if(points <= 0) - return; - CountMap::iterator it = pointMap.find(player->getID()); if(it != pointMap.end()) { @@ -396,9 +395,6 @@ void Party::addPlayerHealedMember(Player* player, uint32_t points) void Party::addPlayerDamageMonster(Player* player, uint32_t points) { - if(points <= 0) - return; - CountMap::iterator it = pointMap.find(player->getID()); if(it != pointMap.end()) { @@ -439,5 +435,5 @@ bool Party::isPlayerInvited(const Player* player, bool result/* = false*/) const bool Party::canOpenCorpse(uint32_t ownerId) { - return leader->getID() == ownerId || isPlayerMember(g_game.getPlayerByID(ownerId)); + return leader->getGUID() == ownerId || isPlayerMember(g_game.getPlayerByGuid(ownerId)); } diff --git a/party.h b/party.h index 9714fc4..2a75b1e 100644 --- a/party.h +++ b/party.h @@ -20,8 +20,8 @@ #include "player.h" typedef std::vector PlayerVector; -typedef std::vector ItemVector; +class Creature; class Player; class Party; @@ -49,7 +49,7 @@ class Party void updateIcons(Player* player); void broadcastMessage(MessageClasses messageClass, const std::string& text, bool sendToInvitations = false); - void shareExperience(double experience, bool fromMonster, bool multiplied); + void shareExperience(double experience, Creature* target, bool multiplied); bool setSharedExperience(Player* player, bool _sharedExpActive); bool isSharedExperienceActive() const {return sharedExpActive;} bool isSharedExperienceEnabled() const {return sharedExpEnabled;} diff --git a/player.cpp b/player.cpp index f2f9c8c..8cd3f43 100644 --- a/player.cpp +++ b/player.cpp @@ -18,18 +18,18 @@ #include #include "player.h" +#include "manager.h" + #include "iologindata.h" #include "ioban.h" #include "town.h" #include "house.h" #include "beds.h" +#include "mounts.h" +#include "quests.h" #include "combat.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) -#include "gui.h" -#endif - #include "movement.h" #include "weapons.h" #include "creatureevent.h" @@ -55,9 +55,9 @@ Player::Player(const std::string& _name, ProtocolGame* p): Creature(), transferContainer(ITEM_LOCKER), name(_name), nameDescription(_name), client(p) { if(client) - client->setPlayer(this); + p->setPlayer(this); - pzLocked = isConnecting = addAttackSkillPoint = requestedOutfit = false; + pvpBlessing = pzLocked = isConnecting = addAttackSkillPoint = requestedOutfit = mounted = outfitAttributes = sentChat = false; saving = true; lastAttackBlockType = BLOCK_NONE; @@ -68,19 +68,19 @@ Player::Player(const std::string& _name, ProtocolGame* p): guildLevel = GUILDLEVEL_NONE; promotionLevel = walkTaskEvent = actionTaskEvent = nextStepEvent = bloodHitCount = shieldBlockCount = 0; - lastAttack = idleTime = marriage = blessings = balance = premiumDays = mana = manaMax = manaSpent = 0; - soul = guildId = levelPercent = magLevelPercent = magLevel = experience = damageImmunities = 0; - conditionImmunities = conditionSuppressions = groupId = vocation_id = managerNumber2 = town = skullEnd = 0; - lastLogin = lastLogout = lastIP = messageTicks = messageBuffer = nextAction = 0; - editListId = maxWriteLen = windowTextId = rankId = 0; + mailAttempts = idleTime = marriage = blessings = balance = premiumDays = mana = manaMax = manaSpent = 0; + soul = guildId = levelPercent = magLevelPercent = magLevel = experience = damageImmunities = rankId = 0; + conditionImmunities = conditionSuppressions = groupId = vocationId = managerNumber2 = town = skullEnd = 0; + lastLogin = lastLogout = lastIP = messageTicks = messageBuffer = nextAction = editListId = maxWriteLen = 0; + windowTextId = nextExAction = 0; purchaseCallback = saleCallback = -1; - level = shootRange = 1; + level = 1; rates[SKILL__MAGLEVEL] = rates[SKILL__LEVEL] = 1.0f; soulMax = 100; capacity = 400.00; stamina = STAMINA_MAX; - lastLoad = lastPing = lastPong = OTSYS_TIME(); + lastLoad = lastPing = lastPong = lastAttack = lastMail = OTSYS_TIME(); writeItem = NULL; group = NULL; @@ -89,12 +89,13 @@ Player::Player(const std::string& _name, ProtocolGame* p): tradeItem = NULL; tradePartner = NULL; walkTask = NULL; + weapon = NULL; setVocation(0); setParty(NULL); transferContainer.setParent(NULL); - for(int32_t i = 0; i < 11; i++) + for(int32_t i = 0; i < 11; ++i) { inventory[i] = NULL; inventoryAbilities[i] = false; @@ -116,7 +117,7 @@ Player::Player(const std::string& _name, ProtocolGame* p): for(int32_t i = LOSS_FIRST; i <= LOSS_LAST; ++i) lossPercent[i] = 100; - for(int8_t i = 0; i <= 13; i++) + for(int32_t i = 0; i <= 12; ++i) talkState[i] = false; #ifdef __ENABLE_SERVER_DIAGNOSTIC__ @@ -129,29 +130,34 @@ Player::~Player() #ifdef __ENABLE_SERVER_DIAGNOSTIC__ playerCount--; #endif - setWriteItem(NULL); - for(int32_t i = 0; i < 11; i++) + for(int32_t i = 0; i < 11; ++i) { - if(inventory[i]) - { - inventory[i]->setParent(NULL); - inventory[i]->unRef(); + if(!inventory[i]) + continue; - inventory[i] = NULL; - inventoryAbilities[i] = false; - } + inventory[i]->setParent(NULL); + inventory[i]->unRef(); + + inventory[i] = NULL; + inventoryAbilities[i] = false; } + setWriteItem(NULL); setNextWalkActionTask(NULL); + transferContainer.setParent(NULL); - for(DepotMap::iterator it = depots.begin(); it != depots.end(); it++) + for(DepotMap::iterator it = depots.begin(); it != depots.end(); ++it) it->second.first->unRef(); } -void Player::setVocation(uint32_t vocId) +void Player::setVocation(uint32_t id) { - vocation_id = vocId; - vocation = Vocations::getInstance()->getVocation(vocId); + vocationId = id; + if(!(vocation = Vocations::getInstance()->getVocation(id))) + return; + + Creature::setDropLoot((vocation->getDropLoot() ? LOOT_DROP_FULL : LOOT_DROP_PREVENT)); + Creature::setLossSkill(vocation->getLossSkill()); soulMax = vocation->getGain(GAIN_SOUL); if(Condition* condition = getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT)) @@ -163,6 +169,22 @@ void Player::setVocation(uint32_t vocId) } } +void Player::setDropLoot(lootDrop_t _lootDrop) +{ + if(vocation && !vocation->getDropLoot()) + _lootDrop = LOOT_DROP_PREVENT; + + Creature::setDropLoot(_lootDrop); +} + +void Player::setLossSkill(bool _skillLoss) +{ + if(vocation && !vocation->getLossSkill()) + _skillLoss = false; + + Creature::setLossSkill(_skillLoss); +} + bool Player::isPushable() const { return accountManager == MANAGER_NONE && !hasFlag(PlayerFlag_CannotBePushed) && Creature::isPushable(); @@ -176,7 +198,7 @@ std::string Player::getDescription(int32_t lookDistance) const s << "yourself."; if(hasFlag(PlayerFlag_ShowGroupNameInsteadOfVocation)) s << " You are " << group->getName(); - else if(vocation_id != 0) + else if(vocationId != 0) s << " You are " << vocation->getDescription(); else s << " You have no vocation"; @@ -190,19 +212,17 @@ std::string Player::getDescription(int32_t lookDistance) const s << ". " << (sex % 2 ? "He" : "She"); if(hasFlag(PlayerFlag_ShowGroupNameInsteadOfVocation)) s << " is " << group->getName(); - else if(vocation_id != 0) + else if(vocationId != 0) s << " is " << vocation->getDescription(); else s << " has no vocation"; - - s << getSpecialDescription(); } std::string tmp; if(marriage && IOLoginData::getInstance()->getNameByGuid(marriage, tmp)) { s << ", "; - if(vocation_id == 0) + if(vocationId == 0) { if(lookDistance == -1) s << "and you are"; @@ -230,6 +250,7 @@ std::string Player::getDescription(int32_t lookDistance) const s << "."; } + s << getSpecialDescription(); return s.str(); } @@ -254,7 +275,8 @@ Item* Player::getEquippedItem(slots_t slot) const { case SLOT_LEFT: case SLOT_RIGHT: - return item->getWieldPosition() == SLOT_HAND ? item : NULL; + if(item->getWieldPosition() == SLOT_HAND) + return item; default: break; @@ -265,74 +287,95 @@ Item* Player::getEquippedItem(slots_t slot) const void Player::setConditionSuppressions(uint32_t conditions, bool remove) { - if(!remove) - conditionSuppressions |= conditions; - else + if(remove) conditionSuppressions &= ~conditions; + else + conditionSuppressions |= conditions; +} + +Item* Player::getWeapon(bool ignoreAmmo) +{ + if(!ignoreAmmo && weapon) + return weapon; + + Item* item = NULL; + for(int32_t slot = SLOT_RIGHT; slot <= SLOT_LEFT; ++slot) + { + if(!(item = getEquippedItem((slots_t)slot)) || item->getWeaponType() != WEAPON_DIST) + continue; + + if(!ignoreAmmo && item->getAmmoType() != AMMO_NONE) + { + Item* ammoItem = getInventoryItem(SLOT_AMMO); + if(ammoItem && ammoItem->getAmmoType() == item->getAmmoType()) + { + if(g_weapons->getWeapon(ammoItem)) + return ammoItem; + } + } + else if(g_weapons->getWeapon(item)) + return item; + } + + return NULL; } -Item* Player::getWeapon(bool ignoreAmmo /*= false*/) +ItemVector Player::getWeapons() const { - Item* item; - for(uint32_t slot = SLOT_RIGHT; slot <= SLOT_LEFT; slot++) + Item* item = NULL; + ItemVector weapons; + for(int32_t slot = SLOT_RIGHT; slot <= SLOT_LEFT; ++slot) { - item = getEquippedItem((slots_t)slot); - if(!item) + if(!(item = getEquippedItem((slots_t)slot))) continue; switch(item->getWeaponType()) { - case WEAPON_SWORD: - case WEAPON_AXE: - case WEAPON_CLUB: - case WEAPON_WAND: - case WEAPON_FIST: - { - const Weapon* weapon = g_weapons->getWeapon(item); - if(weapon) - return item; + case WEAPON_SHIELD: break; - } case WEAPON_DIST: { - if(!ignoreAmmo && item->getAmmoType() != AMMO_NONE) + if(item->getAmmoType() != AMMO_NONE) { Item* ammoItem = getInventoryItem(SLOT_AMMO); if(ammoItem && ammoItem->getAmmoType() == item->getAmmoType()) - { - const Weapon* weapon = g_weapons->getWeapon(ammoItem); - if(weapon) - { - shootRange = item->getShootRange(); - return ammoItem; - } - } - } - else - { - const Weapon* weapon = g_weapons->getWeapon(item); - if(weapon) - { - shootRange = item->getShootRange(); - return item; - } + item = ammoItem; + else + break; } - break; } default: + { + if(g_weapons->getWeapon(item)) + weapons.push_back(item); + break; + } } } - return NULL; + return weapons; +} + +void Player::updateWeapon() +{ + ItemVector weapons = getWeapons(); + if(weapons.empty()) + weapon = NULL; + else if(!weapon || weapons.size() == 1 || weapons[1] == weapon) + weapon = weapons[0]; + else if(weapons[0] == weapon) + weapon = weapons[1]; + else + weapon = NULL; } WeaponType_t Player::getWeaponType() { - if(Item* item = getWeapon()) - return item->getWeaponType(); + if(weapon) + return weapon->getWeaponType(); return WEAPON_NONE; } @@ -357,6 +400,7 @@ int32_t Player::getWeaponSkill(const Item* item) const return getSkill(SKILL_FIST, SKILL_LEVEL); case WEAPON_DIST: + case WEAPON_AMMO: return getSkill(SKILL_DIST, SKILL_LEVEL); default: @@ -368,8 +412,8 @@ int32_t Player::getWeaponSkill(const Item* item) const int32_t Player::getArmor() const { - int32_t armor = 0; - for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) + int32_t i = SLOT_FIRST, armor = 0; + for(; i < SLOT_LAST; ++i) { if(Item* item = getInventoryItem((slots_t)i)) armor += item->getArmor(); @@ -381,37 +425,20 @@ int32_t Player::getArmor() const return armor; } -void Player::getShieldAndWeapon(const Item* &shield, const Item* &weapon) const +void Player::getShieldAndWeapon(const Item* &_shield, const Item* &_weapon) const { - shield = weapon = NULL; - + _shield = NULL; Item* item = NULL; - for(uint32_t slot = SLOT_RIGHT; slot <= SLOT_LEFT; slot++) + for(uint32_t slot = SLOT_RIGHT; slot <= SLOT_LEFT; ++slot) { - item = getInventoryItem((slots_t)slot); - if(!item) + if(!(item = getInventoryItem((slots_t)slot)) || item->getWeaponType() != WEAPON_SHIELD) continue; - switch(item->getWeaponType()) - { - case WEAPON_NONE: - break; - - case WEAPON_SHIELD: - { - if(!shield || (shield && item->getDefense() > shield->getDefense())) - shield = item; - - break; - } - - default: //weapons that are not shields - { - weapon = item; - break; - } - } + if(!_shield || (_shield && item->getDefense() > _shield->getDefense())) + _shield = item; } + + _weapon = weapon; } int32_t Player::getDefense() const @@ -419,23 +446,21 @@ int32_t Player::getDefense() const int32_t baseDefense = 5, defenseValue = 0, defenseSkill = 0, extraDefense = 0; float defenseFactor = getDefenseFactor(); - const Item* weapon = NULL; - const Item* shield = NULL; - - getShieldAndWeapon(shield, weapon); - if(weapon) + const Item *_weapon = NULL, *_shield = NULL; + getShieldAndWeapon(_shield, _weapon); + if(_weapon) { - extraDefense = weapon->getExtraDefense(); - defenseValue = baseDefense + weapon->getDefense(); - defenseSkill = getWeaponSkill(weapon); + extraDefense = _weapon->getExtraDefense(); + defenseValue = baseDefense + _weapon->getDefense(); + defenseSkill = getWeaponSkill(_weapon); } - if(shield && shield->getDefense() > defenseValue) + if(_shield && _shield->getDefense() > defenseValue) { - if(shield->getExtraDefense() > extraDefense) - extraDefense = shield->getExtraDefense(); + if(_shield->getExtraDefense() > extraDefense) + extraDefense = _shield->getExtraDefense(); - defenseValue = baseDefense + shield->getDefense(); + defenseValue = baseDefense + _shield->getDefense(); defenseSkill = getSkill(SKILL_SHIELD, SKILL_LEVEL); } @@ -476,7 +501,7 @@ float Player::getDefenseFactor() const case FIGHTMODE_DEFENSE: { - if((OTSYS_TIME() - lastAttack) < const_cast(this)->getAttackSpeed()) //attacking will cause us to get into normal defense + if((OTSYS_TIME() - lastAttack) < getAttackSpeed()) //attacking will cause us to get into normal defense return 1.0f; return 2.0f; @@ -495,7 +520,7 @@ void Player::sendIcons() const if(!client) return; - uint32_t icons = 0; + uint32_t icons = ICON_NONE; for(ConditionList::const_iterator it = conditions.begin(); it != conditions.end(); ++it) { if(!isSuppress((*it)->getType())) @@ -503,10 +528,13 @@ void Player::sendIcons() const } if(getZone() == ZONE_PROTECTION) - icons |= ICON_PROTECTIONZONE; + icons |= ICON_PZ; if(pzLocked) - icons |= ICON_PZ; + icons |= ICON_PZBLOCK; + + if(!getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT)) + icons |= ICON_HUNGRY; client->sendIcons(icons); } @@ -514,7 +542,8 @@ void Player::sendIcons() const void Player::updateInventoryWeight() { inventoryWeight = 0.00; - if(hasFlag(PlayerFlag_HasInfiniteCapacity)) + if(hasFlag(PlayerFlag_HasInfiniteCapacity) + || !g_config.getBool(ConfigManager::USE_CAPACITY)) return; for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) @@ -580,19 +609,19 @@ int32_t Player::getSkill(skills_t skilltype, skillsid_t skillinfo) const return std::max((int32_t)0, ret); } -void Player::addSkillAdvance(skills_t skill, uint32_t count, bool useMultiplier/* = true*/) +void Player::addSkillAdvance(skills_t skill, uint64_t count, bool useMultiplier/* = true*/) { if(!count) return; //player has reached max skill - uint32_t currReqTries = vocation->getReqSkillTries(skill, skills[skill][SKILL_LEVEL]), + uint64_t currReqTries = vocation->getReqSkillTries(skill, skills[skill][SKILL_LEVEL]), nextReqTries = vocation->getReqSkillTries(skill, skills[skill][SKILL_LEVEL] + 1); if(currReqTries > nextReqTries) return; if(useMultiplier) - count = uint32_t((double)count * rates[skill] * g_config.getDouble(ConfigManager::RATE_SKILL)); + count = uint64_t((double)count * rates[skill] * g_config.getDouble(ConfigManager::RATE_SKILL)); std::stringstream s; while(skills[skill][SKILL_TRIES] + count >= nextReqTries) @@ -602,11 +631,7 @@ void Player::addSkillAdvance(skills_t skill, uint32_t count, bool useMultiplier/ skills[skill][SKILL_LEVEL]++; s.str(""); - s << "You advanced in " << getSkillName(skill); - if(g_config.getBool(ConfigManager::ADVANCING_SKILL_LEVEL)) - s << " [" << skills[skill][SKILL_LEVEL] << "]"; - - s << "."; + s << "You advanced to " << getSkillName(skill) << " level " << skills[skill][SKILL_LEVEL] << "."; sendTextMessage(MSG_EVENT_ADVANCE, s.str().c_str()); CreatureEventList advanceEvents = getCreatureEvents(CREATURE_EVENT_ADVANCE); @@ -626,7 +651,7 @@ void Player::addSkillAdvance(skills_t skill, uint32_t count, bool useMultiplier/ skills[skill][SKILL_TRIES] += count; //update percent - uint32_t newPercent = Player::getPercentLevel(skills[skill][SKILL_TRIES], nextReqTries); + uint16_t newPercent = Player::getPercentLevel(skills[skill][SKILL_TRIES], nextReqTries); if(skills[skill][SKILL_PERCENT] != newPercent) { skills[skill][SKILL_PERCENT] = newPercent; @@ -708,7 +733,7 @@ int32_t Player::getContainerID(const Container* container) const void Player::addContainer(uint32_t cid, Container* container) { #ifdef __DEBUG__ - std::cout << getName() << ", addContainer: " << (int32_t)cid << std::endl; + std::clog << getName() << ", addContainer: " << (int32_t)cid << std::endl; #endif if(cid > 0xF) return; @@ -737,21 +762,18 @@ void Player::closeContainer(uint32_t cid) } #ifdef __DEBUG__ - std::cout << getName() << ", closeContainer: " << (int32_t)cid << std::endl; + std::clog << getName() << ", closeContainer: " << (int32_t)cid << std::endl; #endif } bool Player::canOpenCorpse(uint32_t ownerId) { - return getID() == ownerId || (party && party->canOpenCorpse(ownerId)) || hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges); + return guid == ownerId || (party && party->canOpenCorpse(ownerId)) || hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges); } uint16_t Player::getLookCorpse() const { - if(sex % 2) - return ITEM_MALE_CORPSE; - - return ITEM_FEMALE_CORPSE; + return (sex % 2) ? ITEM_MALE_CORPSE : ITEM_FEMALE_CORPSE; } void Player::dropLoot(Container* corpse) @@ -759,12 +781,13 @@ void Player::dropLoot(Container* corpse) if(!corpse || lootDrop != LOOT_DROP_FULL) return; - uint32_t start = g_config.getNumber(ConfigManager::BLESS_REDUCTION_BASE), loss = lossPercent[LOSS_CONTAINERS], bless = getBlessings(); + uint32_t loss = lossPercent[LOSS_CONTAINERS], start = g_config.getNumber( + ConfigManager::BLESS_REDUCTION_BASE), bless = getBlessings(); while(bless > 0 && loss > 0) { loss -= start; - start -= g_config.getNumber(ConfigManager::BLESS_REDUCTION_DECREAMENT); - bless--; + start -= g_config.getNumber(ConfigManager::BLESS_REDUCTION_DECREMENT); + --bless; } uint32_t itemLoss = (uint32_t)std::floor((5. + loss) * lossPercent[LOSS_ITEMS] / 1000.); @@ -774,8 +797,8 @@ void Player::dropLoot(Container* corpse) if(!item) continue; - uint32_t rand = random_range(1, 100); - if(skull > SKULL_WHITE || (item->getContainer() && rand < loss) || (!item->getContainer() && rand < itemLoss)) + uint32_t tmp = random_range(1, 100); + if(skull > SKULL_WHITE || (item->getContainer() && tmp < loss) || (!item->getContainer() && tmp < itemLoss)) { g_game.internalMoveItem(NULL, this, corpse, INDEX_WHEREEVER, item, item->getItemCount(), 0); sendRemoveInventoryItem((slots_t)i, inventory[(slots_t)i]); @@ -783,15 +806,23 @@ void Player::dropLoot(Container* corpse) } } -bool Player::setStorage(const uint32_t key, const std::string& value) +bool Player::setStorage(const std::string& key, const std::string& value) { - if(!IS_IN_KEYRANGE(key, RESERVED_RANGE)) - return Creature::setStorage(key, value); + uint32_t numericKey = atol(key.c_str()); + if(!IS_IN_KEYRANGE(numericKey, RESERVED_RANGE) || IS_IN_KEYRANGE(numericKey, MOUNTS_RANGE)) + { + if(!Creature::setStorage(key, value)) + return false; + + if(Quests::getInstance()->isQuestStorage(key, value, true)) + onUpdateQuest(); + + return true; + } - if(IS_IN_KEYRANGE(key, OUTFITS_RANGE)) + if(IS_IN_KEYRANGE(numericKey, OUTFITS_RANGE)) { - uint32_t lookType = atoi(value.c_str()) >> 16; - uint32_t addons = atoi(value.c_str()) & 0xFF; + uint32_t lookType = atoi(value.c_str()) >> 16, addons = atoi(value.c_str()) & 0xFF; if(addons < 4) { Outfit outfit; @@ -799,30 +830,29 @@ bool Player::setStorage(const uint32_t key, const std::string& value) return addOutfit(outfit.outfitId, addons); } else - std::cout << "[Warning - Player::setStorage] Invalid addons value key: " << key + std::clog << "[Warning - Player::setStorage] Invalid addons value key: " << key << ", value: " << value << " for player: " << getName() << std::endl; } - else if(IS_IN_KEYRANGE(key, OUTFITSID_RANGE)) + else if(IS_IN_KEYRANGE(numericKey, OUTFITSID_RANGE)) { - uint32_t outfitId = atoi(value.c_str()) >> 16; - uint32_t addons = atoi(value.c_str()) & 0xFF; + uint32_t outfitId = atoi(value.c_str()) >> 16, addons = atoi(value.c_str()) & 0xFF; if(addons < 4) return addOutfit(outfitId, addons); else - std::cout << "[Warning - Player::setStorage] Invalid addons value key: " << key + std::clog << "[Warning - Player::setStorage] Invalid addons value key: " << key << ", value: " << value << " for player: " << getName() << std::endl; } else - std::cout << "[Warning - Player::setStorage] Unknown reserved key: " << key << " for player: " << getName() << std::endl; + std::clog << "[Warning - Player::setStorage] Unknown reserved key: " << key << " for player: " << getName() << std::endl; return false; } -void Player::eraseStorage(const uint32_t key) +void Player::eraseStorage(const std::string& key) { Creature::eraseStorage(key); - if(IS_IN_KEYRANGE(key, RESERVED_RANGE)) - std::cout << "[Warning - Player::eraseStorage] Unknown reserved key: " << key << " for player: " << name << std::endl; + if(IS_IN_KEYRANGE(atol(key.c_str()), RESERVED_RANGE)) + std::clog << "[Warning - Player::eraseStorage] Unknown reserved key: " << key << " for player: " << name << std::endl; } bool Player::canSee(const Position& pos) const @@ -846,21 +876,43 @@ bool Player::canSeeCreature(const Creature* creature) const bool Player::canWalkthrough(const Creature* creature) const { - if(!creature) + if(creature == this || hasFlag(PlayerFlag_CanPassThroughAllCreatures) || creature->isWalkable() || + std::find(forceWalkthrough.begin(), forceWalkthrough.end(), creature->getID()) != forceWalkthrough.end() + || (creature->getMaster() && creature->getMaster() != this && canWalkthrough(creature->getMaster()))) return true; - if(creature == this) - return false; - const Player* player = creature->getPlayer(); if(!player) return false; - if(g_game.getWorldType() == WORLD_TYPE_NO_PVP && player->getTile()->ground - && player->getTile()->ground->getID() != ITEM_GLOWING_SWITCH) + if(((g_game.getWorldType() == WORLDTYPE_OPTIONAL && !player->isEnemy(this, true) && + !player->isProtected()) || player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) || player->isProtected()) && player->getTile()->ground + && Item::items[player->getTile()->ground->getID()].walkStack && (!player->hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges) + || player->getAccess() <= getAccess())) return true; - return player->isGhost() && getGhostAccess() < player->getGhostAccess(); + return (player->isGhost() && getGhostAccess() < player->getGhostAccess()) + || (isGhost() && getGhostAccess() > player->getGhostAccess()); +} + +void Player::setWalkthrough(const Creature* creature, bool walkthrough) +{ + std::vector::iterator it = std::find(forceWalkthrough.begin(), + forceWalkthrough.end(), creature->getID()); + bool update = false; + if(walkthrough && it == forceWalkthrough.end()) + { + forceWalkthrough.push_back(creature->getID()); + update = true; + } + else if(!walkthrough && it != forceWalkthrough.end()) + { + forceWalkthrough.erase(it); + update = true; + } + + if(update) + sendCreatureWalkthrough(creature, !walkthrough ? canWalkthrough(creature) : walkthrough); } Depot* Player::getDepot(uint32_t depotId, bool autoCreateDepot) @@ -877,14 +929,29 @@ Depot* Player::getDepot(uint32_t depotId, bool autoCreateDepot) { if(Depot* depot = container->getDepot()) { - container->__internalAddThing(Item::CreateItem(ITEM_DEPOT)); - addDepot(depot, depotId); + Item* item = Item::CreateItem(ITEM_MARKET); + if(item) + container->__internalAddThing(item); + + if((item = Item::CreateItem(ITEM_INBOX))) + { + container->__internalAddThing(item); + depot->setInbox(item->getContainer()); + } + + if((item = Item::CreateItem(ITEM_DEPOT))) + { + container->__internalAddThing(item); + depot->setLocker(item->getContainer()); + } + + internalAddDepot(depot, depotId); return depot; } } g_game.freeThing(locker); - std::cout << "Failure: Creating a new depot with id: " << depotId << + std::clog << "Failure: Creating a new depot with id: " << depotId << ", for player: " << getName() << std::endl; } @@ -896,9 +963,63 @@ bool Player::addDepot(Depot* depot, uint32_t depotId) if(getDepot(depotId, false)) return false; + internalAddDepot(depot, depotId); + return true; +} + +void Player::internalAddDepot(Depot* depot, uint32_t depotId) +{ depots[depotId] = std::make_pair(depot, false); + depot->setDepotId(depotId); depot->setMaxDepotLimit((group != NULL ? group->getDepotLimit(isPremium()) : 1000)); - return true; +} + +void Player::updateDepots() +{ + Depot* depot = NULL; + for(DepotMap::iterator it = depots.begin(); it != depots.end(); ++it) + { + depot = it->second.first; + if(depot->__getItemTypeCount(ITEM_INBOX) == 0) // This happens only during upgrade of depots... + { + ItemList::const_reverse_iterator rit = depot->getReversedItems(); + depot->setLocker((*rit)->getContainer()); // Depot is ALWAYS! the last item in the locker + + Item* item = Item::CreateItem(ITEM_MARKET); + if(item) + depot->__internalAddThing(item); + + if((item = Item::CreateItem(ITEM_INBOX))) + { + depot->__internalAddThing(item); + depot->setInbox(item->getContainer()); + } + + // we need to place the depot to be first + depot->__removeThing(depot->getLocker()->getItem(), 1); + depot->__addThing(NULL, depot->getLocker()->getItem()); + + rit = depot->getReversedItems(); + while(rit != depot->getReversedEnd()) + { + item = *rit; + if(item->getID() != ITEM_INBOX && item->getID() != ITEM_MARKET && item->getID() != ITEM_DEPOT) + { + g_game.internalMoveItem(NULL, item->getParent(), depot->getInbox(), + INDEX_WHEREEVER, item, item->getItemCount(), NULL, FLAG_NOLIMIT); + rit = depot->getReversedItems(); + } + else + ++rit; + } + } + else + { + ItemList::const_iterator rit = depot->getItems(); + depot->setLocker((*rit)->getContainer()); + depot->setInbox((*(++rit))->getContainer()); + } + } } void Player::useDepot(uint32_t depotId, bool value) @@ -916,7 +1037,7 @@ void Player::sendCancelMessage(ReturnValue message) const sendCancel("Destination is out of reach."); break; - case RET_NOTMOVEABLE: + case RET_NOTMOVABLE: sendCancel("You cannot move this object."); break; @@ -925,7 +1046,7 @@ void Player::sendCancelMessage(ReturnValue message) const break; case RET_BOTHHANDSNEEDTOBEFREE: - sendCancel("Both hands needs to be free."); + sendCancel("Both hands need to be free."); break; case RET_CANNOTBEDRESSED: @@ -957,7 +1078,7 @@ void Player::sendCancelMessage(ReturnValue message) const break; case RET_NOTENOUGHCAPACITY: - sendCancel("This object is too heavy."); + sendCancel("This object is too heavy for you to carry."); break; case RET_CONTAINERNOTENOUGHROOM: @@ -970,7 +1091,7 @@ void Player::sendCancelMessage(ReturnValue message) const break; case RET_CANNOTPICKUP: - sendCancel("You cannot pickup this object."); + sendCancel("You cannot take this object."); break; case RET_CANNOTTHROW: @@ -998,7 +1119,7 @@ void Player::sendCancelMessage(ReturnValue message) const break; case RET_DEPOTISFULL: - sendCancel("You cannot put more items in this depot."); + sendCancel("Your depot is full. Remove surplus items before storing new ones."); break; case RET_CANNOTUSETHISOBJECT: @@ -1022,7 +1143,7 @@ void Player::sendCancelMessage(ReturnValue message) const break; case RET_YOUMAYNOTLOGOUTDURINGAFIGHT: - sendCancel("You may not logout during or immediately after a fight!"); + sendCancel("You may not logout during or immediately after a fight."); break; case RET_DIRECTPLAYERSHOOT: @@ -1114,7 +1235,7 @@ void Player::sendCancelMessage(ReturnValue message) const break; case RET_ACTIONNOTPERMITTEDINANOPVPZONE: - sendCancel("This action is not permitted in a non-pvp zone."); + sendCancel("This action is not permitted in a safe zone."); break; case RET_YOUCANNOTLOGOUTHERE: @@ -1129,10 +1250,6 @@ void Player::sendCancelMessage(ReturnValue message) const sendCancel("You cannot conjure items here."); break; - case RET_YOUNEEDTOSPLITYOURSPEARS: - sendCancel("You need to split your spears first."); - break; - case RET_NAMEISTOOAMBIGUOUS: sendCancel("Name is too ambiguous."); break; @@ -1153,20 +1270,17 @@ void Player::sendCancelMessage(ReturnValue message) const sendCancel("You cannot add more items on this tile."); break; - case RET_DONTSHOWMESSAGE: + case RET_NOTENOUGHSKILL: + sendCancel("You do not have enough skill."); break; case RET_NOTPOSSIBLE: - default: sendCancel("Sorry, not possible."); break; - } -} -void Player::sendStats() -{ - if(client) - client->sendStats(); + default: + break; + } } Item* Player::getWriteItem(uint32_t& _windowTextId, uint16_t& _maxWriteLen) @@ -1176,7 +1290,7 @@ Item* Player::getWriteItem(uint32_t& _windowTextId, uint16_t& _maxWriteLen) return writeItem; } -void Player::setWriteItem(Item* item, uint16_t _maxWriteLen/* = 0*/) +void Player::setWriteItem(Item* item, uint16_t _maxLen/* = 0*/) { windowTextId++; if(writeItem) @@ -1185,7 +1299,7 @@ void Player::setWriteItem(Item* item, uint16_t _maxWriteLen/* = 0*/) if(item) { writeItem = item; - maxWriteLen = _maxWriteLen; + maxWriteLen = _maxLen; writeItem->addRef(); } else @@ -1246,7 +1360,7 @@ void Player::sendAddContainerItem(const Container* container, const Item* item) } } -void Player::sendUpdateContainerItem(const Container* container, uint8_t slot, const Item* oldItem, const Item* newItem) +void Player::sendUpdateContainerItem(const Container* container, uint8_t slot, const Item*, const Item* newItem) { if(!client) return; @@ -1258,7 +1372,7 @@ void Player::sendUpdateContainerItem(const Container* container, uint8_t slot, c } } -void Player::sendRemoveContainerItem(const Container* container, uint8_t slot, const Item* item) +void Player::sendRemoveContainerItem(const Container* container, uint8_t slot, const Item*) { if(!client) return; @@ -1312,6 +1426,7 @@ void Player::onCreatureAppear(const Creature* creature) g_moveEvents->onPlayerEquip(this, item, (slots_t)slot, false); } + updateWeapon(); if(BedItem* bed = Beds::getInstance()->getBedBySleeper(guid)) bed->wakeUp(); @@ -1353,16 +1468,25 @@ void Player::onCreatureAppear(const Creature* creature) g_game.checkPlayersRecord(this); if(!isGhost()) + { IOLoginData::getInstance()->updateOnlineStatus(guid, true); + for(AutoList::iterator it = autoList.begin(); it != autoList.end(); ++it) + it->second->notifyLogIn(this); + } + else + { + for(AutoList::iterator it = autoList.begin(); it != autoList.end(); ++it) + { + if(it->second->canSeeCreature(this)) + it->second->notifyLogIn(this); + } + } - #if defined(WINDOWS) && !defined(__CONSOLE__) - GUI::getInstance()->m_pBox.addPlayer(this); - #endif if(g_config.getBool(ConfigManager::DISPLAY_LOGGING)) - std::cout << name << " has logged in." << std::endl; + std::clog << name << " has logged in." << std::endl; } -void Player::onAttackedCreatureDisappear(bool isLogout) +void Player::onTargetDisappear(bool isLogout) { sendCancelTarget(); if(!isLogout) @@ -1378,31 +1502,40 @@ void Player::onFollowCreatureDisappear(bool isLogout) void Player::onChangeZone(ZoneType_t zone) { - if(attackedCreature && zone == ZONE_PROTECTION && !hasFlag(PlayerFlag_IgnoreProtectionZone)) + if(zone == ZONE_PROTECTION && !hasFlag(PlayerFlag_IgnoreProtectionZone)) { - setAttackedCreature(NULL); - onAttackedCreatureDisappear(false); + if(attackedCreature) + { + setAttackedCreature(NULL); + onTargetDisappear(false); + } + + if(g_config.getBool(ConfigManager::UNMOUNT_PLAYER_IN_PZ)) + dismount(true); } + + g_game.updateCreatureWalkthrough(this); sendIcons(); } -void Player::onAttackedCreatureChangeZone(ZoneType_t zone) +void Player::onTargetChangeZone(ZoneType_t zone) { if(zone == ZONE_PROTECTION && !hasFlag(PlayerFlag_IgnoreProtectionZone)) { setAttackedCreature(NULL); - onAttackedCreatureDisappear(false); + onTargetDisappear(false); } - else if(zone == ZONE_NOPVP && attackedCreature->getPlayer() && !hasFlag(PlayerFlag_IgnoreProtectionZone)) + else if(zone == ZONE_OPTIONAL && attackedCreature->getPlayer() && !hasFlag(PlayerFlag_IgnoreProtectionZone)) { setAttackedCreature(NULL); - onAttackedCreatureDisappear(false); + onTargetDisappear(false); } - else if(zone == ZONE_NORMAL && g_game.getWorldType() == WORLD_TYPE_NO_PVP && attackedCreature->getPlayer()) + else if(zone == ZONE_OPEN && g_game.getWorldType() == WORLDTYPE_OPTIONAL && attackedCreature->getPlayer() + && !attackedCreature->getPlayer()->isEnemy(this, true)) { //attackedCreature can leave a pvp zone if not pzlocked setAttackedCreature(NULL); - onAttackedCreatureDisappear(false); + onTargetDisappear(false); } } @@ -1418,6 +1551,15 @@ void Player::onCreatureDisappear(const Creature* creature, bool isLogout) lastLogout = time(NULL); } + Item* item = NULL; + for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot) + { + if(!(item = getInventoryItem((slots_t)slot))) + continue; + + g_moveEvents->onPlayerDeEquip(this, item, (slots_t)slot, false); + } + if(eventWalk) setFollowCreature(NULL); @@ -1429,29 +1571,12 @@ void Player::onCreatureDisappear(const Creature* creature, bool isLogout) if(party) party->leave(this); - g_game.cancelRuleViolation(this); - if(hasFlag(PlayerFlag_CanAnswerRuleViolations)) - { - PlayerVector closeReportList; - for(RuleViolationsMap::const_iterator it = g_game.getRuleViolations().begin(); it != g_game.getRuleViolations().end(); ++it) - { - if(it->second->gamemaster == this) - closeReportList.push_back(it->second->reporter); - } - - for(PlayerVector::iterator it = closeReportList.begin(); it != closeReportList.end(); ++it) - g_game.closeRuleViolation(*it); - } - - g_chat.removeUserFromAllChannels(this); + g_chat.removeUserFromChannels(this); if(!isGhost()) IOLoginData::getInstance()->updateOnlineStatus(guid, false); - #if defined(WINDOWS) && !defined(__CONSOLE__) - GUI::getInstance()->m_pBox.removePlayer(this); - #endif if(g_config.getBool(ConfigManager::DISPLAY_LOGGING)) - std::cout << getName() << " has logged out." << std::endl; + std::clog << getName() << " has logged out." << std::endl; bool saved = false; for(uint32_t tries = 0; !saved && tries < 3; ++tries) @@ -1460,33 +1585,37 @@ void Player::onCreatureDisappear(const Creature* creature, bool isLogout) saved = true; #ifdef __DEBUG__ else - std::cout << "Error while saving player: " << getName() << ", strike " << tries << "." << std::endl; + std::clog << "Error while saving player: " << getName() << ", strike " << tries << "." << std::endl; #endif } if(!saved) #ifndef __DEBUG__ - std::cout << "Error while saving player: " << getName() << "." << std::endl; + std::clog << "Error while saving player: " << getName() << "." << std::endl; #else - std::cout << "Player " << getName() << " couldn't be saved." << std::endl; + std::clog << "Player " << getName() << " couldn't be saved." << std::endl; #endif } -void Player::openShopWindow() +void Player::openShopWindow(Npc* npc) { - sendShop(); + sendShop(npc); sendGoods(); } -void Player::closeShopWindow(Npc* npc/* = NULL*/, int32_t onBuy/* = -1*/, int32_t onSell/* = -1*/) +void Player::closeShopWindow(bool send/* = true*/) { - if(npc || (npc = getShopOwner(onBuy, onSell))) + int32_t onBuy = -1, onSell = -1; + if(Npc* npc = getShopOwner(onBuy, onSell)) npc->onPlayerEndTrade(this, onBuy, onSell); if(shopOwner) - sendCloseShop(); + { + shopOwner = NULL; + if(send) + sendCloseShop(); + } - shopOwner = NULL; purchaseCallback = saleCallback = -1; shopOffer.clear(); } @@ -1503,8 +1632,8 @@ bool Player::canShopItem(uint16_t itemId, uint8_t subType, ShopEvent_t event) return true; const ItemType& it = Item::items[id]; - if(it.isFluidContainer() || it.isSplash() || it.isRune()) - return sit->subType == subType; + if(it.isFluidContainer() || it.isSplash()) + return (sit->subType % 8) == subType; return true; } @@ -1526,8 +1655,8 @@ void Player::onCreatureMove(const Creature* creature, const Tile* newTile, const if(creature != this) return; - if(getParty()) - getParty()->updateSharedExperience(); + if(party) + party->updateSharedExperience(); //check if we should close trade if(tradeState != TRADE_TRANSFER && ((tradeItem && !Position::areInRange<1,1,0>(tradeItem->getPosition(), getPosition())) @@ -1539,30 +1668,45 @@ void Player::onCreatureMove(const Creature* creature, const Tile* newTile, const int32_t ticks = g_config.getNumber(ConfigManager::STAIRHOP_DELAY); if(ticks > 0) { - addExhaust(ticks, EXHAUST_COMBAT); + addExhaust(ticks, EXHAUST_SPELLGROUP_ATTACK); + addExhaust(ticks, EXHAUST_MELEE); if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks)) addCondition(condition); } } + + if(getZone() == ZONE_PROTECTION && newTile->ground && oldTile->ground && + Item::items[newTile->ground->getID()].walkStack != Item::items[oldTile->ground->getID()].walkStack) + g_game.updateCreatureWalkthrough(this); } void Player::onAddContainerItem(const Container* container, const Item* item) { checkTradeState(item); + if(backpack.first && (const_cast(container) != backpack.first || backpack.first->full())) + backpack.first = NULL; } -void Player::onUpdateContainerItem(const Container* container, uint8_t slot, - const Item* oldItem, const ItemType& oldType, const Item* newItem, const ItemType& newType) +void Player::onUpdateContainerItem(const Container* container, uint8_t, + const Item* oldItem, const ItemType&, const Item* newItem, const ItemType&) { - if(oldItem != newItem) - onRemoveContainerItem(container, slot, oldItem); + if(tradeState == TRADE_TRANSFER) + return; + + checkTradeState(oldItem); + if(oldItem != newItem && tradeItem) + { + if(tradeItem->getParent() != container && container->isHoldingItem(tradeItem)) + g_game.internalCloseTrade(this); + } if(tradeState != TRADE_TRANSFER) checkTradeState(oldItem); } -void Player::onRemoveContainerItem(const Container* container, uint8_t slot, const Item* item) +void Player::onRemoveContainerItem(const Container* container, uint8_t, const Item* item) { + backpack.first = NULL; if(tradeState == TRADE_TRANSFER) return; @@ -1599,18 +1743,27 @@ void Player::onSendContainer(const Container* container) } } -void Player::onUpdateInventoryItem(slots_t slot, Item* oldItem, const ItemType& oldType, - Item* newItem, const ItemType& newType) +void Player::onUpdateInventoryItem(slots_t, Item* oldItem, const ItemType&, + Item* newItem, const ItemType&) { - if(oldItem != newItem) - onRemoveInventoryItem(slot, oldItem); + if(tradeState == TRADE_TRANSFER) + return; + + checkTradeState(oldItem); + if(oldItem != newItem && tradeItem) + { + const Container* container = oldItem->getContainer(); + if(container && container->isHoldingItem(tradeItem)) + g_game.internalCloseTrade(this); + } if(tradeState != TRADE_TRANSFER) checkTradeState(oldItem); } -void Player::onRemoveInventoryItem(slots_t slot, Item* item) +void Player::onRemoveInventoryItem(slots_t, Item* item) { + backpack.first = NULL; if(tradeState == TRADE_TRANSFER) return; @@ -1689,13 +1842,12 @@ void Player::setNextActionTask(SchedulerTask* task) } } -uint32_t Player::getNextActionTime() const +uint32_t Player::getNextActionTime(bool scheduler/* = true*/) const { - int64_t time = nextAction - OTSYS_TIME(); - if(time < SCHEDULER_MINTICKS) - return SCHEDULER_MINTICKS; + if(!scheduler) + return (uint32_t)std::max((int64_t)0, ((int64_t)nextAction - OTSYS_TIME())); - return time; + return (uint32_t)std::max((int64_t)SCHEDULER_MINTICKS, ((int64_t)nextAction - OTSYS_TIME())); } void Player::onThink(uint32_t interval) @@ -1705,17 +1857,18 @@ void Player::onThink(uint32_t interval) if(timeNow - lastPing >= 5000) { lastPing = timeNow; - if(client) + if(hasClient()) client->sendPing(); else if(g_config.getBool(ConfigManager::STOP_ATTACK_AT_EXIT)) setAttackedCreature(NULL); } - if((timeNow - lastPong) >= 60000 && canLogout(true)) + if((timeNow - lastPong) >= 60000 && !getTile()->hasFlag(TILESTATE_NOLOGOUT) + && !isConnecting && !pzLocked && !hasCondition(CONDITION_INFIGHT)) { - if(client) + if(hasClient()) client->logout(true, true); - else if(g_creatureEvents->playerLogout(this, true)) + else if(g_creatureEvents->playerLogout(this, false)) g_game.removeCreature(this, true); } @@ -1725,9 +1878,12 @@ void Player::onThink(uint32_t interval) messageTicks = 0; addMessageBuffer(); } + + if(lastMail && lastMail < (uint64_t)(OTSYS_TIME() + g_config.getNumber(ConfigManager::MAIL_ATTEMPTS_FADE))) + mailAttempts = lastMail = 0; } -bool Player::isMuted(uint16_t channelId, SpeakClasses type, uint32_t& time) +bool Player::isMuted(uint16_t channelId, MessageClasses type, int32_t& time) { time = 0; if(hasFlag(PlayerFlag_CannotBeMuted)) @@ -1736,69 +1892,74 @@ bool Player::isMuted(uint16_t channelId, SpeakClasses type, uint32_t& time) int32_t muteTicks = 0; for(ConditionList::iterator it = conditions.begin(); it != conditions.end(); ++it) { - if((*it)->getType() == CONDITION_MUTED && (*it)->getSubId() == 0 && (*it)->getTicks() > muteTicks) - muteTicks = (*it)->getTicks(); + if((*it)->getType() == CONDITION_MUTED && (*it)->getSubId() == 0) + { + if((*it)->getTicks() == -1) + { + time = -1; + break; + } + + if((*it)->getTicks() > muteTicks) + muteTicks = (*it)->getTicks(); + } } - time = (uint32_t)muteTicks / 1000; - return time > 0 && type != SPEAK_PRIVATE_PN && (type != SPEAK_CHANNEL_Y || (channelId != CHANNEL_GUILD && !g_chat.isPrivateChannel(channelId))); + if(muteTicks) + time = (uint32_t)muteTicks / 1000; + + return type != MSG_NPC_TO && (type != MSG_CHANNEL || (channelId != CHANNEL_GUILD && !g_chat.isPrivateChannel(channelId))); } void Player::addMessageBuffer() { - if(!hasFlag(PlayerFlag_CannotBeMuted) && g_config.getNumber( - ConfigManager::MAX_MESSAGEBUFFER) != 0 && messageBuffer > 0) + if(!hasFlag(PlayerFlag_CannotBeMuted) && g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER) && messageBuffer) messageBuffer--; } void Player::removeMessageBuffer() { + if(hasFlag(PlayerFlag_CannotBeMuted)) + return; + int32_t maxBuffer = g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER); - if(!hasFlag(PlayerFlag_CannotBeMuted) && maxBuffer != 0 && messageBuffer <= maxBuffer + 1) - { - if(++messageBuffer > maxBuffer) - { - uint32_t muteCount = 1; - MuteCountMap::iterator it = muteCountMap.find(guid); - if(it != muteCountMap.end()) - muteCount = it->second; - - uint32_t muteTime = 5 * muteCount * muteCount; - muteCountMap[guid] = muteCount + 1; - if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, muteTime * 1000)) - addCondition(condition); + if(!maxBuffer || messageBuffer > maxBuffer + 1 || ++messageBuffer <= maxBuffer) + return; - char buffer[50]; - sprintf(buffer, "You are muted for %d seconds.", muteTime); - sendTextMessage(MSG_STATUS_SMALL, buffer); - } - } + uint32_t muteCount = 1; + MuteCountMap::iterator it = muteCountMap.find(guid); + if(it != muteCountMap.end()) + muteCount = it->second; + + uint32_t muteTime = 5 * muteCount * muteCount; + muteCountMap[guid] = muteCount + 1; + if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, muteTime * 1000)) + addCondition(condition); + + char buffer[50]; + sprintf(buffer, "You are muted for %d seconds.", muteTime); + sendTextMessage(MSG_STATUS_SMALL, buffer); +} + +double Player::getFreeCapacity() const +{ + if(hasFlag(PlayerFlag_HasInfiniteCapacity) + || !g_config.getBool(ConfigManager::USE_CAPACITY)) + return 10000.00; + + return std::max(0.00, capacity - inventoryWeight); } void Player::drainHealth(Creature* attacker, CombatType_t combatType, int32_t damage) { Creature::drainHealth(attacker, combatType, damage); - char buffer[150]; - if(attacker) - sprintf(buffer, "You lose %d hitpoint%s due to an attack by %s.", damage, (damage != 1 ? "s" : ""), attacker->getNameDescription().c_str()); - else - sprintf(buffer, "You lose %d hitpoint%s.", damage, (damage != 1 ? "s" : "")); - sendStats(); - sendTextMessage(MSG_EVENT_DEFAULT, buffer); } void Player::drainMana(Creature* attacker, CombatType_t combatType, int32_t damage) { Creature::drainMana(attacker, combatType, damage); - char buffer[150]; - if(attacker) - sprintf(buffer, "You lose %d mana blocking an attack by %s.", damage, attacker->getNameDescription().c_str()); - else - sprintf(buffer, "You lose %d mana.", damage); - sendStats(); - sendTextMessage(MSG_EVENT_DEFAULT, buffer); } void Player::addManaSpent(uint64_t amount, bool useMultiplier/* = true*/) @@ -1807,24 +1968,22 @@ void Player::addManaSpent(uint64_t amount, bool useMultiplier/* = true*/) return; uint64_t currReqMana = vocation->getReqMana(magLevel), nextReqMana = vocation->getReqMana(magLevel + 1); - if(currReqMana > nextReqMana) //player has reached max magic level + if(magLevel > 0 && currReqMana > nextReqMana) //player has reached max magic level return; if(useMultiplier) amount = uint64_t((double)amount * rates[SKILL__MAGLEVEL] * g_config.getDouble(ConfigManager::RATE_MAGIC)); - bool advance = false; + std::stringstream s; while(manaSpent + amount >= nextReqMana) { amount -= nextReqMana - manaSpent; manaSpent = 0; - magLevel++; - char advMsg[50]; - sprintf(advMsg, "You advanced to magic level %d.", magLevel); - sendTextMessage(MSG_EVENT_ADVANCE, advMsg); + s.str(""); + s << "You advanced to magic level " << ++magLevel << "."; + sendTextMessage(MSG_EVENT_ADVANCE, s.str()); - advance = true; CreatureEventList advanceEvents = getCreatureEvents(CREATURE_EVENT_ADVANCE); for(CreatureEventList::iterator it = advanceEvents.begin(); it != advanceEvents.end(); ++it) (*it)->executeAdvance(this, SKILL__MAGLEVEL, (magLevel - 1), magLevel); @@ -1841,19 +2000,21 @@ void Player::addManaSpent(uint64_t amount, bool useMultiplier/* = true*/) if(amount) manaSpent += amount; - uint32_t newPercent = Player::getPercentLevel(manaSpent, nextReqMana); + uint16_t newPercent = Player::getPercentLevel(manaSpent, nextReqMana); if(magLevelPercent != newPercent) { magLevelPercent = newPercent; sendStats(); } - else if(advance) + else if(!s.str().empty()) sendStats(); } void Player::addExperience(uint64_t exp) { + bool attackable = isProtected(); uint32_t prevLevel = level; + uint64_t nextLevelExp = Player::getExpForLevel(level + 1); if(Player::getExpForLevel(level) > nextLevelExp) { @@ -1866,13 +2027,21 @@ void Player::addExperience(uint64_t exp) experience += exp; while(experience >= nextLevelExp) { - healthMax += vocation->getGain(GAIN_HEALTH); - health += vocation->getGain(GAIN_HEALTH); - manaMax += vocation->getGain(GAIN_MANA); - mana += vocation->getGain(GAIN_MANA); - capacity += vocation->getGainCap(); - ++level; + Vocation* voc = vocation; + if(voc->getId() > 0 && g_config.getBool(ConfigManager::ROOK_SYSTEM) && + level <= (uint32_t)g_config.getNumber(ConfigManager::ROOK_TOLEVEL)) + { + if(Vocation* tmp = Vocations::getInstance()->getVocation(0)) + voc = tmp; + } + + healthMax += voc->getGain(GAIN_HEALTH); + health += voc->getGain(GAIN_HEALTH); + manaMax += voc->getGain(GAIN_MANA); + mana += voc->getGain(GAIN_MANA); + capacity += voc->getGainCap(); + nextLevelExp = Player::getExpForLevel(level + 1); if(Player::getExpForLevel(level) > nextLevelExp) //player has reached max level break; @@ -1881,20 +2050,20 @@ void Player::addExperience(uint64_t exp) if(prevLevel != level) { updateBaseSpeed(); - setBaseSpeed(getBaseSpeed()); - g_game.changeSpeed(this, 0); - g_game.addCreatureHealth(this); - if(getParty()) - getParty()->updateSharedExperience(); - - char advMsg[60]; - sprintf(advMsg, "You advanced from Level %d to Level %d.", prevLevel, level); - sendTextMessage(MSG_EVENT_ADVANCE, advMsg); + if(party) + party->updateSharedExperience(); CreatureEventList advanceEvents = getCreatureEvents(CREATURE_EVENT_ADVANCE); for(CreatureEventList::iterator it = advanceEvents.begin(); it != advanceEvents.end(); ++it) (*it)->executeAdvance(this, SKILL__LEVEL, prevLevel, level); + + std::stringstream s; + s << "You advanced from Level " << prevLevel << " to Level " << level << "."; + + sendTextMessage(MSG_EVENT_ADVANCE, s.str()); + if(isProtected() != attackable) + g_game.updateCreatureWalkthrough(this); } uint64_t currLevelExp = Player::getExpForLevel(level); @@ -1909,13 +2078,23 @@ void Player::addExperience(uint64_t exp) void Player::removeExperience(uint64_t exp, bool updateStats/* = true*/) { uint32_t prevLevel = level; + bool attackable = isProtected(); + experience -= std::min(exp, experience); while(level > 1 && experience < Player::getExpForLevel(level)) { - level--; - healthMax = std::max((int32_t)0, (healthMax - (int32_t)vocation->getGain(GAIN_HEALTH))); - manaMax = std::max((int32_t)0, (manaMax - (int32_t)vocation->getGain(GAIN_MANA))); - capacity = std::max((double)0, (capacity - (double)vocation->getGainCap())); + --level; + Vocation* voc = vocation; + if(voc->getId() > 0 && g_config.getBool(ConfigManager::ROOK_SYSTEM) && + level < (uint32_t)g_config.getNumber(ConfigManager::ROOK_TOLEVEL)) + { + if(Vocation* tmp = Vocations::getInstance()->getVocation(0)) + voc = tmp; + } + + healthMax = std::max((int32_t)0, (healthMax - (int32_t)voc->getGain(GAIN_HEALTH))); + manaMax = std::max((int32_t)0, (manaMax - (int32_t)voc->getGain(GAIN_MANA))); + capacity = std::max((double)0, (capacity - (double)voc->getGainCap())); } if(prevLevel != level) @@ -1923,19 +2102,24 @@ void Player::removeExperience(uint64_t exp, bool updateStats/* = true*/) if(updateStats) { updateBaseSpeed(); - setBaseSpeed(getBaseSpeed()); - g_game.changeSpeed(this, 0); g_game.addCreatureHealth(this); } - char advMsg[90]; - sprintf(advMsg, "You were downgraded from Level %d to Level %d.", prevLevel, level); - sendTextMessage(MSG_EVENT_ADVANCE, advMsg); + CreatureEventList advanceEvents = getCreatureEvents(CREATURE_EVENT_ADVANCE); + for(CreatureEventList::iterator it = advanceEvents.begin(); it != advanceEvents.end(); ++it) + (*it)->executeAdvance(this, SKILL__LEVEL, prevLevel, level); + + std::stringstream s; + s << "You were downgraded from Level " << prevLevel << " to Level " << level << "."; + + sendTextMessage(MSG_EVENT_ADVANCE, s.str()); + if(!isProtected() != attackable) + g_game.updateCreatureWalkthrough(this); } - uint64_t currLevelExp = Player::getExpForLevel(level); - uint64_t nextLevelExp = Player::getExpForLevel(level + 1); + uint64_t currLevelExp = Player::getExpForLevel(level), + nextLevelExp = Player::getExpForLevel(level + 1); if(nextLevelExp > currLevelExp) levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp); else @@ -1945,7 +2129,7 @@ void Player::removeExperience(uint64_t exp, bool updateStats/* = true*/) sendStats(); } -uint32_t Player::getPercentLevel(uint64_t count, uint64_t nextLevelCount) +uint16_t Player::getPercentLevel(uint64_t count, uint64_t nextLevelCount) { if(nextLevelCount > 0) return std::min((uint32_t)100, std::max((uint32_t)0, uint32_t(count * 100 / nextLevelCount))); @@ -1953,7 +2137,7 @@ uint32_t Player::getPercentLevel(uint64_t count, uint64_t nextLevelCount) return 0; } -void Player::onBlockHit(BlockType_t blockType) +void Player::onBlockHit(BlockType_t) { if(shieldBlockCount > 0) { @@ -1963,17 +2147,16 @@ void Player::onBlockHit(BlockType_t blockType) } } -void Player::onAttackedCreatureBlockHit(Creature* target, BlockType_t blockType) +void Player::onTargetBlockHit(Creature* target, BlockType_t blockType) { - Creature::onAttackedCreatureBlockHit(target, blockType); + Creature::onTargetBlockHit(target, blockType); lastAttackBlockType = blockType; switch(blockType) { case BLOCK_NONE: { + bloodHitCount = shieldBlockCount = 30; addAttackSkillPoint = true; - bloodHitCount = 30; - shieldBlockCount = 30; break; } @@ -2002,97 +2185,114 @@ void Player::onAttackedCreatureBlockHit(Creature* target, BlockType_t blockType) bool Player::hasShield() const { - bool result = false; Item* item = getInventoryItem(SLOT_LEFT); - if(item && item->getWeaponType() == WEAPON_SHIELD) - result = true; - - item = getInventoryItem(SLOT_RIGHT); - if(item && item->getWeaponType() == WEAPON_SHIELD) - result = true; - - return result; + return (item && item->getWeaponType() == WEAPON_SHIELD) || ((item = getInventoryItem(SLOT_RIGHT)) && item->getWeaponType() == WEAPON_SHIELD); } BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, - bool checkDefense/* = false*/, bool checkArmor/* = false*/) + bool checkDefense/* = false*/, bool checkArmor/* = false*/, bool reflect/* = true*/, bool field/* = false*/, bool element/* = false*/) { - BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor); - if(attacker) + BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor, reflect, field); + if(attacker && !element) { int16_t color = g_config.getNumber(ConfigManager::SQUARE_COLOR); if(color < 0) - color = random_range(0, 255); + color = random_range(0, 254); - sendCreatureSquare(attacker, (SquareColor_t)color); + sendCreatureSquare(attacker, color); } if(blockType != BLOCK_NONE) return blockType; - if(vocation->getMultiplier(MULTIPLIER_MAGICDEFENSE) != 1.0 && combatType != COMBAT_NONE && - combatType != COMBAT_PHYSICALDAMAGE && combatType != COMBAT_UNDEFINEDDAMAGE && - combatType != COMBAT_DROWNDAMAGE) + if(vocation->getMultiplier(MULTIPLIER_MAGICDEFENSE) != 1.0 && combatType != COMBAT_PHYSICALDAMAGE && + combatType != COMBAT_NONE && combatType != COMBAT_UNDEFINEDDAMAGE && combatType != COMBAT_DROWNDAMAGE) damage -= (int32_t)std::ceil((double)(damage * vocation->getMultiplier(MULTIPLIER_MAGICDEFENSE)) / 100.); - if(damage > 0) + if(damage <= 0) + return blockType; + + int32_t blocked = 0, reflected = 0; + if(reflect) + reflect = attacker && !attacker->isRemoved() && attacker->getHealth() > 0; + + Item* item = NULL; + for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot) { - Item* item = NULL; - int32_t blocked = 0, reflected = 0; - for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot) + if(!(item = getInventoryItem((slots_t)slot)) || item->isRemoved() || + (g_moveEvents->hasEquipEvent(item) && !isItemAbilityEnabled((slots_t)slot))) + continue; + + const ItemType& it = Item::items[item->getID()]; + if(!it.hasAbilities()) + continue; + + bool transform = false; + if(it.abilities->absorb[combatType]) { - if(!(item = getInventoryItem((slots_t)slot)) || (g_moveEvents->hasEquipEvent(item) - && !isItemAbilityEnabled((slots_t)slot))) - continue; + blocked += (int32_t)std::ceil((double)(damage * it.abilities->absorb[combatType]) / 100.); + if(item->hasCharges()) + transform = true; - const ItemType& it = Item::items[item->getID()]; - if(it.abilities.absorb[combatType]) - { - blocked += (int32_t)std::ceil((double)(damage * it.abilities.absorb[combatType]) / 100.); - if(item->hasCharges()) - g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1)); - } + } - if(it.abilities.reflect[REFLECT_PERCENT][combatType] && it.abilities.reflect[REFLECT_CHANCE][combatType] < random_range(0, 100)) - { - reflected += (int32_t)std::ceil((double)(damage * it.abilities.reflect[REFLECT_PERCENT][combatType]) / 100.); - if(item->hasCharges() && !it.abilities.absorb[combatType]) - g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1)); - } + if(field && it.abilities->fieldAbsorb[combatType]) + { + blocked += (int32_t)std::ceil((double)(damage * it.abilities->fieldAbsorb[combatType]) / 100.); + if(item->hasCharges()) + transform = true; } - if(outfitAttributes) + if(reflect && it.abilities->reflect[REFLECT_PERCENT][combatType] && it.abilities->reflect[REFLECT_CHANCE][combatType] >= random_range(1, 100)) { - uint32_t tmp = Outfits::getInstance()->getOutfitAbsorb(defaultOutfit.lookType, sex, combatType); - if(tmp) - blocked += (int32_t)std::ceil((double)(damage * tmp) / 100.); + reflected += (int32_t)std::ceil((double)(damage * it.abilities->reflect[REFLECT_PERCENT][combatType]) / 100.); + if(item->hasCharges()) + transform = true; + } + + if(!element && transform) + g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1)); + } + + if(outfitAttributes) + { + uint32_t tmp = Outfits::getInstance()->getOutfitAbsorb(defaultOutfit.lookType, sex, combatType); + if(tmp) + blocked += (int32_t)std::ceil((double)(damage * tmp) / 100.); + if(reflect) + { tmp = Outfits::getInstance()->getOutfitReflect(defaultOutfit.lookType, sex, combatType); if(tmp) reflected += (int32_t)std::ceil((double)(damage * tmp) / 100.); } + } - if(vocation->getAbsorb(combatType)) - blocked += (int32_t)std::ceil((double)(damage * vocation->getAbsorb(combatType)) / 100.); + if(mounted) + { + // TODO: mount attributes + } - if(vocation->getReflect(combatType)) - reflected += (int32_t)std::ceil((double)(damage * vocation->getReflect(combatType)) / 100.); + if(vocation->getAbsorb(combatType)) + blocked += (int32_t)std::ceil((double)(damage * vocation->getAbsorb(combatType)) / 100.); - damage -= blocked; - if(damage <= 0) - { - damage = 0; - blockType = BLOCK_DEFENSE; - } + if(reflect && vocation->getReflect(combatType)) + reflected += (int32_t)std::ceil((double)(damage * vocation->getReflect(combatType)) / 100.); - if(reflected) - { - CombatType_t reflectType = combatType; - if(reflected <= 0) - reflectType = COMBAT_HEALING; + damage -= blocked; + if(damage <= 0) + { + damage = 0; + blockType = BLOCK_DEFENSE; + } - g_game.combatChangeHealth(reflectType, NULL, attacker, -reflected); - } + if(reflected && !element) + { + if(combatType != COMBAT_HEALING) + reflected = -reflected; + + if(!g_game.combatBlockHit(combatType, this, attacker, reflected, false, false, true, false)) + g_game.combatChangeHealth(combatType, NULL, attacker, reflected); } return blockType; @@ -2108,30 +2308,32 @@ uint32_t Player::getIP() const bool Player::onDeath() { - Item* preventLoss = NULL; - Item* preventDrop = NULL; - if(getZone() == ZONE_PVP) + Item *preventLoss = NULL, *preventDrop = NULL; + if(getZone() == ZONE_HARDCORE) { setDropLoot(LOOT_DROP_NONE); setLossSkill(false); } - else if(skull < SKULL_RED && g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) + else if(skull < SKULL_RED) { Item* item = NULL; - for(int32_t i = SLOT_FIRST; ((skillLoss || lootDrop == LOOT_DROP_FULL) && i < SLOT_LAST); ++i) + for(int32_t i = SLOT_FIRST; ((!preventDrop || !preventLoss) && i < SLOT_LAST); ++i) { - if(!(item = getInventoryItem((slots_t)i)) || (g_moveEvents->hasEquipEvent(item) - && !isItemAbilityEnabled((slots_t)i))) + if(!(item = getInventoryItem((slots_t)i)) || item->isRemoved() || + (g_moveEvents->hasEquipEvent(item) && !isItemAbilityEnabled((slots_t)i))) continue; const ItemType& it = Item::items[item->getID()]; - if(lootDrop == LOOT_DROP_FULL && it.abilities.preventDrop) + if(!it.hasAbilities()) + continue; + + if(lootDrop == LOOT_DROP_FULL && it.abilities->preventDrop) { setDropLoot(LOOT_DROP_PREVENT); preventDrop = item; } - if(skillLoss && !preventLoss && it.abilities.preventLoss) + if(skillLoss && !preventLoss && it.abilities->preventLoss) preventLoss = item; } } @@ -2144,38 +2346,59 @@ bool Player::onDeath() return false; } - if(preventLoss) + uint32_t totalDamage = 0, pvpDamage = 0, opponents = 0; + for(CountMap::iterator it = damageMap.begin(); it != damageMap.end(); ++it) { - setLossSkill(false); - if(preventLoss->getCharges() > 1) //weird, but transform failed to remove for some hosters - g_game.transformItem(preventLoss, preventLoss->getID(), std::max(0, ((int32_t)preventLoss->getCharges() - 1))); - else - g_game.internalRemoveItem(NULL, preventDrop); + if(((OTSYS_TIME() - it->second.ticks) / 1000) > g_config.getNumber( + ConfigManager::FAIRFIGHT_TIMERANGE)) + continue; + + totalDamage += it->second.total; + if(Creature* creature = g_game.getCreatureByID(it->first)) + { + Player* player = creature->getPlayer(); + if(!player) + player = creature->getPlayerMaster(); + + if(!player) + continue; + + opponents += player->getLevel(); + pvpDamage += it->second.total; + } } - if(preventDrop && preventDrop != preventLoss) + bool usePVPBlessing = false; + if(preventLoss) { - if(preventDrop->getCharges() > 1) //weird, but transform failed to remove for some hosters - g_game.transformItem(preventDrop, preventDrop->getID(), std::max(0, ((int32_t)preventDrop->getCharges() - 1))); - else - g_game.internalRemoveItem(NULL, preventDrop); + setLossSkill(false); + g_game.transformItem(preventLoss, preventLoss->getID(), std::max(0, (int32_t)preventLoss->getCharges() - 1)); } + else if(pvpBlessing && (int32_t)std::floor((100. * pvpDamage) / std::max( + 1U, totalDamage)) >= g_config.getNumber(ConfigManager::PVP_BLESSING_THRESHOLD)) + usePVPBlessing = true; + + if(preventDrop && preventDrop != preventLoss && !usePVPBlessing) + g_game.transformItem(preventDrop, preventDrop->getID(), std::max(0, (int32_t)preventDrop->getCharges() - 1)); removeConditions(CONDITIONEND_DEATH); if(skillLoss) { - uint64_t lossExperience = getLostExperience(); + double reduction = 1.; + if(g_config.getBool(ConfigManager::FAIRFIGHT_REDUCTION) && opponents > level) + reduction -= (double)level / opponents; + + uint64_t lossExperience = (uint64_t)std::floor(reduction * getLostExperience()), currExperience = experience; removeExperience(lossExperience, false); - double percent = 1. - ((double)(experience - lossExperience) / experience); + double percent = 1. - ((double)(currExperience - lossExperience) / std::max((uint64_t)1, currExperience)); - //Magic level loss - uint32_t sumMana = 0; - uint64_t lostMana = 0; + // magic level loss + uint64_t sumMana = 0, lostMana = 0; for(uint32_t i = 1; i <= magLevel; ++i) sumMana += vocation->getReqMana(i); sumMana += manaSpent; - lostMana = (uint64_t)std::ceil(sumMana * ((double)(percent * lossPercent[LOSS_MANA]) / 100.)); + lostMana = (uint64_t)std::ceil((percent * lossPercent[LOSS_MANA] / 100.) * sumMana); while(lostMana > manaSpent && magLevel > 0) { lostMana -= manaSpent; @@ -2183,51 +2406,81 @@ bool Player::onDeath() magLevel--; } - manaSpent -= std::max((int32_t)0, (int32_t)lostMana); + manaSpent -= lostMana; uint64_t nextReqMana = vocation->getReqMana(magLevel + 1); if(nextReqMana > vocation->getReqMana(magLevel)) magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana); else magLevelPercent = 0; - //Skill loss - uint32_t lostSkillTries, sumSkillTries; - for(int16_t i = 0; i < 7; ++i) //for each skill + // skill loss + uint64_t lostSkillTries, sumSkillTries; + for(int16_t i = 0; i < 7; ++i) // for each skill { lostSkillTries = sumSkillTries = 0; - for(uint32_t c = 11; c <= skills[i][SKILL_LEVEL]; ++c) //sum up all required tries for all skill levels + for(uint32_t c = 11; c <= skills[i][SKILL_LEVEL]; ++c) // sum up all required tries for all skill levels sumSkillTries += vocation->getReqSkillTries(i, c); sumSkillTries += skills[i][SKILL_TRIES]; - lostSkillTries = (uint32_t)std::ceil(sumSkillTries * ((double)(percent * lossPercent[LOSS_SKILLS]) / 100.)); - while(lostSkillTries > skills[i][SKILL_TRIES]) + lostSkillTries = (uint64_t)std::ceil((percent * lossPercent[LOSS_SKILLS] / 100.) * sumSkillTries); + while(lostSkillTries > skills[i][SKILL_TRIES] && skills[i][SKILL_LEVEL] > 10) { lostSkillTries -= skills[i][SKILL_TRIES]; skills[i][SKILL_TRIES] = vocation->getReqSkillTries(i, skills[i][SKILL_LEVEL]); - if(skills[i][SKILL_LEVEL] < 11) - { - skills[i][SKILL_LEVEL] = 10; - skills[i][SKILL_TRIES] = lostSkillTries = 0; - break; - } - else - skills[i][SKILL_LEVEL]--; + skills[i][SKILL_LEVEL]--; } - skills[i][SKILL_TRIES] = std::max((int32_t)0, (int32_t)(skills[i][SKILL_TRIES] - lostSkillTries)); + skills[i][SKILL_TRIES] -= lostSkillTries; } - blessings = 0; + if(usePVPBlessing) + pvpBlessing = false; + else + blessings = 0; + loginPosition = masterPosition; - if(!inventory[SLOT_BACKPACK]) + if(vocationId > 0 && g_config.getBool(ConfigManager::ROOK_SYSTEM) && + level <= (uint32_t)g_config.getNumber(ConfigManager::ROOK_LEVELTO)) + { + if(Town* rook = Towns::getInstance()->getTown(g_config.getNumber(ConfigManager::ROOK_TOWN))) + { + level = 1; + soulMax = soul = 100; + capacity = 400; + stamina = STAMINA_MAX; + health = healthMax = 150; + loginPosition = masterPosition = rook->getPosition(); + experience = magLevel = manaSpent = mana = manaMax = balance = marriage = 0; + promotionLevel = defaultOutfit.lookAddons = defaultOutfit.lookMount = 0; + + setTown(rook->getID()); + setVocation(0); + leaveGuild(); + + storageMap.clear(); + for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) + { + skills[i][SKILL_LEVEL] = 10; + skills[i][SKILL_TRIES] = 0; + } + + for(uint32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) + { + if(inventory[i]) + g_game.internalRemoveItem(NULL, inventory[i]); + } + } + } + else if(!inventory[SLOT_BACKPACK]) // FIXME: you should receive the bag after you login back... __internalAddThing(SLOT_BACKPACK, Item::CreateItem(g_config.getNumber(ConfigManager::DEATH_CONTAINER))); sendIcons(); sendStats(); sendSkills(); - sendReLoginWindow(); + g_creatureEvents->playerLogout(this, true); g_game.removeCreature(this, false); + sendReLoginWindow((uint8_t)std::floor((1. - reduction) * 100.)); } else { @@ -2235,8 +2488,10 @@ bool Player::onDeath() if(preventLoss) { loginPosition = masterPosition; - sendReLoginWindow(); + g_creatureEvents->playerLogout(this, true); + g_game.removeCreature(this, false); + sendReLoginWindow(0); } } @@ -2251,7 +2506,8 @@ void Player::dropCorpse(DeathList deathList) if(health <= 0) { health = healthMax; - mana = manaMax; + if(getZone() != ZONE_HARDCORE || g_config.getBool(ConfigManager::PVPZONE_RECOVERMANA)) + mana = manaMax; } setDropLoot(LOOT_DROP_FULL); @@ -2260,7 +2516,7 @@ void Player::dropCorpse(DeathList deathList) onIdleStatus(); g_game.addCreatureHealth(this); - g_game.internalTeleport(this, masterPosition, true); + g_game.internalTeleport(this, masterPosition, false); } else { @@ -2277,7 +2533,7 @@ Item* Player::createCorpse(DeathList deathList) return NULL; std::stringstream ss; - ss << "You recognize " << getNameDescription() << ". " << (sex % 2 ? "He" : "She") << " was killed by "; + ss << "You recognize " << nameDescription << ". " << (sex % 2 ? "He" : "She") << " was killed by "; if(deathList[0].isCreatureKill()) { ss << deathList[0].getKillerCreature()->getNameDescription(); @@ -2318,22 +2574,35 @@ Item* Player::createCorpse(DeathList deathList) return corpse; } -void Player::addExhaust(uint32_t ticks, Exhaust_t type) +void Player::addCooldown(uint32_t ticks, uint16_t spellId) { - if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, ticks, 0, false, type)) + if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, + CONDITION_SPELLCOOLDOWN, ticks, 0, false, spellId)) addCondition(condition); } -void Player::addInFightTicks(bool pzLock/* = false*/) +void Player::addExhaust(uint32_t ticks, Exhaust_t exhaust) +{ + if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, + CONDITION_EXHAUST, ticks, 0, false, (int32_t)exhaust)) + addCondition(condition); +} + +void Player::addInFightTicks(bool pzLock, int32_t ticks/* = 0*/) { if(hasFlag(PlayerFlag_NotGainInFight)) return; + if(!ticks) + ticks = g_config.getNumber(ConfigManager::PZ_LOCKED); + else + ticks = std::max(-1, ticks); + if(pzLock) pzLocked = true; if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, - CONDITION_INFIGHT, g_config.getNumber(ConfigManager::PZ_LOCKED))) + CONDITION_INFIGHT, ticks)) addCondition(condition); } @@ -2354,6 +2623,7 @@ void Player::addDefaultRegeneration(uint32_t addTicks) void Player::removeList() { + Manager::getInstance()->removeUser(id); autoList.erase(id); if(!isGhost()) { @@ -2372,26 +2642,13 @@ void Player::removeList() void Player::addList() { - if(!isGhost()) - { - for(AutoList::iterator it = autoList.begin(); it != autoList.end(); ++it) - it->second->notifyLogIn(this); - } - else - { - for(AutoList::iterator it = autoList.begin(); it != autoList.end(); ++it) - { - if(it->second->canSeeCreature(this)) - it->second->notifyLogIn(this); - } - } - autoList[id] = this; + Manager::getInstance()->addUser(this); } -void Player::kickPlayer(bool displayEffect, bool forceLogout) +void Player::kick(bool displayEffect, bool forceLogout) { - if(!client) + if(!hasClient()) { if(g_creatureEvents->playerLogout(this, forceLogout)) g_game.removeCreature(this); @@ -2405,7 +2662,7 @@ void Player::notifyLogIn(Player* loginPlayer) if(!client) return; - VIPListSet::iterator it = VIPList.find(loginPlayer->getGUID()); + VIPSet::iterator it = VIPList.find(loginPlayer->getGUID()); if(it != VIPList.end()) client->sendVIPLogIn(loginPlayer->getGUID()); } @@ -2415,14 +2672,14 @@ void Player::notifyLogOut(Player* logoutPlayer) if(!client) return; - VIPListSet::iterator it = VIPList.find(logoutPlayer->getGUID()); + VIPSet::iterator it = VIPList.find(logoutPlayer->getGUID()); if(it != VIPList.end()) client->sendVIPLogOut(logoutPlayer->getGUID()); } bool Player::removeVIP(uint32_t _guid) { - VIPListSet::iterator it = VIPList.find(_guid); + VIPSet::iterator it = VIPList.find(_guid); if(it == VIPList.end()) return false; @@ -2430,36 +2687,34 @@ bool Player::removeVIP(uint32_t _guid) return true; } -bool Player::addVIP(uint32_t _guid, std::string& name, bool isOnline, bool internal/* = false*/) +bool Player::addVIP(uint32_t _guid, const std::string& name, bool online, bool loading/* = false*/) { if(guid == _guid) { - if(!internal) + if(!loading) sendTextMessage(MSG_STATUS_SMALL, "You cannot add yourself."); return false; } - if(VIPList.size() > (group ? group->getMaxVips(isPremium()) : 20)) + if(!loading && VIPList.size() > (size_t)(group ? group->getMaxVips(isPremium()) : g_config.getNumber(ConfigManager::VIPLIST_DEFAULT_LIMIT))) { - if(!internal) - sendTextMessage(MSG_STATUS_SMALL, "You cannot add more buddies."); - + sendTextMessage(MSG_STATUS_SMALL, "You cannot add more buddies."); return false; } - VIPListSet::iterator it = VIPList.find(_guid); + VIPSet::iterator it = VIPList.find(_guid); if(it != VIPList.end()) { - if(!internal) + if(!loading) sendTextMessage(MSG_STATUS_SMALL, "This player is already in your list."); return false; } VIPList.insert(_guid); - if(client && !internal) - client->sendVIP(_guid, name, isOnline); + if(!loading && client) + client->sendVIP(_guid, name, online); return true; } @@ -2494,9 +2749,6 @@ void Player::autoCloseContainers(const Container* container) bool Player::hasCapacity(const Item* item, uint32_t count) const { - if(hasFlag(PlayerFlag_CannotPickupItem)) - return false; - if(hasFlag(PlayerFlag_HasInfiniteCapacity) || item->getTopParent() == this) return true; @@ -2509,14 +2761,18 @@ bool Player::hasCapacity(const Item* item, uint32_t count) const return (itemWeight < getFreeCapacity()); } -ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count, uint32_t flags) const +ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count, uint32_t flags, Creature*) const { const Item* item = thing->getItem(); if(!item) return RET_NOTPOSSIBLE; - bool childIsOwner = ((flags & FLAG_CHILDISOWNER) == FLAG_CHILDISOWNER), skipLimit = ((flags & FLAG_NOLIMIT) == FLAG_NOLIMIT); - if(childIsOwner) + if(!item->isPickupable() || (hasFlag(PlayerFlag_CannotPickupItem) && + item->getParent() && item->getParent() != VirtualCylinder::virtualCylinder)) + return RET_CANNOTPICKUP; + + bool childOwner = ((flags & FLAG_CHILDISOWNER) == FLAG_CHILDISOWNER), skipLimit = ((flags & FLAG_NOLIMIT) == FLAG_NOLIMIT); + if(childOwner) { //a child container is querying the player, just check if enough capacity if(skipLimit || hasCapacity(item, count)) @@ -2525,14 +2781,11 @@ ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count return RET_NOTENOUGHCAPACITY; } - if(!item->isPickupable()) - return RET_CANNOTPICKUP; - ReturnValue ret = RET_NOERROR; if((item->getSlotPosition() & SLOTP_HEAD) || (item->getSlotPosition() & SLOTP_NECKLACE) || (item->getSlotPosition() & SLOTP_BACKPACK) || (item->getSlotPosition() & SLOTP_ARMOR) || (item->getSlotPosition() & SLOTP_LEGS) || (item->getSlotPosition() & SLOTP_FEET) || - (item->getSlotPosition() & SLOTP_RING)) + (item->getSlotPosition() & SLOTP_RING) || (item->getSlotPosition() & SLOTP_AMMO)) ret = RET_CANNOTBEDRESSED; else if(item->getSlotPosition() & SLOTP_TWO_HAND) ret = RET_PUTTHISOBJECTINBOTHHANDS; @@ -2560,8 +2813,16 @@ ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count case SLOT_RIGHT: if(item->getSlotPosition() & SLOTP_RIGHT) { - //check if we already carry an item in the other hand - if(item->getSlotPosition() & SLOTP_TWO_HAND) + if(!g_config.getBool(ConfigManager::TIBIA_SLOTS)) + { + if(!item->isWeapon() || (item->getWeaponType() != WEAPON_SHIELD && !item->isDualWield())) + ret = RET_NOTPOSSIBLE; + else if(inventory[SLOT_LEFT] && inventory[SLOT_LEFT]->getSlotPosition() & SLOTP_TWO_HAND) + ret = RET_DROPTWOHANDEDITEM; + else + ret = RET_NOERROR; + } + else if(item->getSlotPosition() & SLOTP_TWO_HAND) { if(inventory[SLOT_LEFT] && inventory[SLOT_LEFT] != item) ret = RET_BOTHHANDSNEEDTOBEFREE; @@ -2574,13 +2835,14 @@ ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count WeaponType_t type = item->getWeaponType(), leftType = leftItem->getWeaponType(); if(leftItem->getSlotPosition() & SLOTP_TWO_HAND) ret = RET_DROPTWOHANDEDITEM; - else if(item == leftItem && count == item->getItemCount()) + else if(item == leftItem && item->getItemCount() == count) ret = RET_NOERROR; else if(leftType == WEAPON_SHIELD && type == WEAPON_SHIELD) ret = RET_CANONLYUSEONESHIELD; else if(!leftItem->isWeapon() || !item->isWeapon() || - leftType == WEAPON_SHIELD || leftType == WEAPON_AMMO - || type == WEAPON_SHIELD || type == WEAPON_AMMO) + leftType == WEAPON_AMMO || type == WEAPON_AMMO || + leftType == WEAPON_SHIELD || type == WEAPON_SHIELD || + (leftItem->isDualWield() && item->isDualWield())) ret = RET_NOERROR; else ret = RET_CANONLYUSEONEWEAPON; @@ -2592,8 +2854,16 @@ ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count case SLOT_LEFT: if(item->getSlotPosition() & SLOTP_LEFT) { - //check if we already carry an item in the other hand - if(item->getSlotPosition() & SLOTP_TWO_HAND) + if(!g_config.getBool(ConfigManager::TIBIA_SLOTS)) + { + if(!item->isWeapon() || item->getWeaponType() == WEAPON_SHIELD) + ret = RET_NOTPOSSIBLE; + else if(inventory[SLOT_RIGHT] && item->getSlotPosition() & SLOTP_TWO_HAND) + ret = RET_BOTHHANDSNEEDTOBEFREE; + else + ret = RET_NOERROR; + } + else if(item->getSlotPosition() & SLOTP_TWO_HAND) { if(inventory[SLOT_RIGHT] && inventory[SLOT_RIGHT] != item) ret = RET_BOTHHANDSNEEDTOBEFREE; @@ -2606,13 +2876,14 @@ ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count WeaponType_t type = item->getWeaponType(), rightType = rightItem->getWeaponType(); if(rightItem->getSlotPosition() & SLOTP_TWO_HAND) ret = RET_DROPTWOHANDEDITEM; - else if(item == rightItem && count == item->getItemCount()) + else if(item == rightItem && item->getItemCount() == count) ret = RET_NOERROR; else if(rightType == WEAPON_SHIELD && type == WEAPON_SHIELD) ret = RET_CANONLYUSEONESHIELD; else if(!rightItem->isWeapon() || !item->isWeapon() || - rightType == WEAPON_SHIELD || rightType == WEAPON_AMMO - || type == WEAPON_SHIELD || type == WEAPON_AMMO) + rightType == WEAPON_AMMO || type == WEAPON_AMMO || + rightType == WEAPON_SHIELD || type == WEAPON_SHIELD || + (rightItem->isDualWield() && item->isDualWield())) ret = RET_NOERROR; else ret = RET_CANONLYUSEONEWEAPON; @@ -2634,7 +2905,7 @@ ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count ret = RET_NOERROR; break; case SLOT_AMMO: - if(item->getSlotPosition() & SLOTP_AMMO) + if(item->getSlotPosition() & SLOTP_AMMO || g_config.getBool(ConfigManager::TIBIA_SLOTS)) ret = RET_NOERROR; break; case SLOT_WHEREEVER: @@ -2646,19 +2917,37 @@ ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count break; } - if(ret == RET_NOERROR || ret == RET_NOTENOUGHROOM) + Player* self = const_cast(this); + if(ret == RET_NOERROR) { //need an exchange with source? - if(getInventoryItem((slots_t)index) != NULL && (!getInventoryItem((slots_t)index)->isStackable() - || getInventoryItem((slots_t)index)->getID() != item->getID())) + Item* tmpItem = NULL; + if((tmpItem = getInventoryItem((slots_t)index)) && (!tmpItem->isStackable() || tmpItem->getID() != item->getID())) return RET_NEEDEXCHANGE; - if(!g_moveEvents->onPlayerEquip(const_cast(this), const_cast(item), (slots_t)index, true)) + if(!g_moveEvents->onPlayerEquip(self, const_cast(item), (slots_t)index, true)) return RET_CANNOTBEDRESSED; + } - //check if enough capacity - if(!hasCapacity(item, count)) - return RET_NOTENOUGHCAPACITY; + if((ret == RET_NOERROR || ret == RET_NOTENOUGHROOM) && !hasCapacity(item, count)) //check if enough capacity + return RET_NOTENOUGHCAPACITY; + + if(index == SLOT_LEFT || index == SLOT_RIGHT) + { + if(ret == RET_NOERROR && item->getWeaponType() != WEAPON_NONE) + self->setLastAttack(OTSYS_TIME()); + + Item* tmpItem = inventory[(slots_t)index]; + if(ret == RET_BOTHHANDSNEEDTOBEFREE && g_game.internalAddItem( + NULL, self, tmpItem, INDEX_WHEREEVER) == RET_NOERROR) + { + self->sendRemoveInventoryItem((slots_t)index, tmpItem); + self->onRemoveInventoryItem((slots_t)index, tmpItem); + + self->inventory[(slots_t)index] = NULL; + self->inventoryWeight -= tmpItem->getWeight(); + sendStats(); + } } return ret; @@ -2674,26 +2963,71 @@ ReturnValue Player::__queryMaxCount(int32_t index, const Thing* thing, uint32_t return RET_NOTPOSSIBLE; } - const Thing* destThing = __getThing(index); - const Item* destItem = NULL; - if(destThing) - destItem = destThing->getItem(); - - if(destItem) + if(index == INDEX_WHEREEVER) { - if(destItem->isStackable() && item->getID() == destItem->getID()) - maxQueryCount = 100 - destItem->getItemCount(); - else - maxQueryCount = 0; + uint32_t n = 0; + for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) + { + if(Item* inventoryItem = inventory[i]) + { + if(Container* subContainer = inventoryItem->getContainer()) + { + uint32_t queryCount = 0; + subContainer->__queryMaxCount(INDEX_WHEREEVER, item, item->getItemCount(), queryCount, flags); + + //iterate through all items, including sub-containers (deep search) + n += queryCount; + for(ContainerIterator cit = subContainer->begin(); cit != subContainer->end(); ++cit) + { + if(Container* tmpContainer = (*cit)->getContainer()) + { + queryCount = 0; + tmpContainer->__queryMaxCount(INDEX_WHEREEVER, item, item->getItemCount(), queryCount, flags); + n += queryCount; + } + } + } + else if(inventoryItem->isStackable() && item->getID() == inventoryItem->getID() && inventoryItem->getItemCount() < 100) + { + uint32_t remainder = (100 - inventoryItem->getItemCount()); + if(__queryAdd(i, item, remainder, flags) == RET_NOERROR) + n += remainder; + } + } + else if(__queryAdd(i, item, item->getItemCount(), flags) == RET_NOERROR) + { + if(item->isStackable()) + n += 100; + else + n += 1; + } + } + + maxQueryCount = n; } else { - if(item->isStackable()) - maxQueryCount = 100; - else - maxQueryCount = 1; + const Thing* destThing = __getThing(index); + const Item* destItem = NULL; + if(destThing) + destItem = destThing->getItem(); + + if(destItem) + { + if(destItem->isStackable() && item->getID() == destItem->getID() && destItem->getItemCount() < 100) + maxQueryCount = 100 - destItem->getItemCount(); + else + maxQueryCount = 0; + } + else if(__queryAdd(index, item, count, flags) == RET_NOERROR) + { + if(item->isStackable()) + maxQueryCount = 100; + else + maxQueryCount = 1; - return RET_NOERROR; + return RET_NOERROR; + } } if(maxQueryCount < count) @@ -2702,7 +3036,7 @@ ReturnValue Player::__queryMaxCount(int32_t index, const Thing* thing, uint32_t return RET_NOERROR; } -ReturnValue Player::__queryRemove(const Thing* thing, uint32_t count, uint32_t flags) const +ReturnValue Player::__queryRemove(const Thing* thing, uint32_t count, uint32_t flags, Creature*) const { int32_t index = __getIndexOfThing(thing); if(index == -1) @@ -2712,11 +3046,11 @@ ReturnValue Player::__queryRemove(const Thing* thing, uint32_t count, uint32_t f if(!item) return RET_NOTPOSSIBLE; - if(count == 0 || (item->isStackable() && count > item->getItemCount())) + if(!count || (item->isStackable() && count > item->getItemCount())) return RET_NOTPOSSIBLE; - if(item->isNotMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags)) - return RET_NOTMOVEABLE; + if(!item->isMovable() && !hasBitSet(FLAG_IGNORENOTMOVABLE, flags)) + return RET_NOTMOVABLE; return RET_NOERROR; } @@ -2724,68 +3058,139 @@ ReturnValue Player::__queryRemove(const Thing* thing, uint32_t count, uint32_t f Cylinder* Player::__queryDestination(int32_t& index, const Thing* thing, Item** destItem, uint32_t& flags) { - if(index == 0 /*drop to capacity window*/ || index == INDEX_WHEREEVER) + if(!index /*drop to capacity window*/ || index == INDEX_WHEREEVER) { *destItem = NULL; const Item* item = thing->getItem(); if(!item) return this; - //find a appropiate slot + bool autoStack = (flags & FLAG_IGNOREAUTOSTACK) != FLAG_IGNOREAUTOSTACK; + if((!autoStack || !item->isStackable()) && backpack.first && + backpack.first->__queryAdd(backpack.second, item, item->getItemCount(), flags)) + { + index = backpack.second; + if(backpack.second != INDEX_WHEREEVER) + ++backpack.second; + + return backpack.first; + } + + std::list > containers; + std::list > freeSlots; for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) { - if(!inventory[i] && __queryAdd(i, item, item->getItemCount(), 0) == RET_NOERROR) + if(Item* invItem = inventory[i]) { - index = i; - return this; + if(invItem == item || invItem == tradeItem) + continue; + + if(autoStack && item->isStackable() && __queryAdd(i, item, item->getItemCount(), 0) + == RET_NOERROR && invItem->getID() == item->getID() && invItem->getItemCount() < 100) + { + *destItem = invItem; + index = i; + return this; + } + + if(Container* container = invItem->getContainer()) + { + if(!autoStack && container->__queryAdd( + INDEX_WHEREEVER, item, item->getItemCount(), flags) == RET_NOERROR) + { + index = INDEX_WHEREEVER; + backpack = std::make_pair(container, index + 1); + return container; + } + + containers.push_back(std::make_pair(container, 0)); + } + } + else if(!autoStack) + { + if(__queryAdd(i, item, item->getItemCount(), 0) == RET_NOERROR) + { + index = i; + return this; + } } + else + freeSlots.push_back(std::make_pair(this, i)); } - //try containers - std::list > deepList; - for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) + int32_t deepness = g_config.getNumber(ConfigManager::PLAYER_DEEPNESS); + while(!containers.empty()) { - if(inventory[i] == tradeItem) + Container* tmpContainer = containers.front().first; + int32_t level = containers.front().second; + + containers.pop_front(); + if(!tmpContainer) continue; - if(Container* container = dynamic_cast(inventory[i])) + for(uint32_t n = 0; n < tmpContainer->capacity(); ++n) { - if(container->__queryAdd(-1, item, item->getItemCount(), 0) == RET_NOERROR) + if(Item* tmpItem = tmpContainer->getItem(n)) { - index = INDEX_WHEREEVER; - *destItem = NULL; - return container; + if(tmpItem == item || tmpItem == tradeItem) + continue; + + if(autoStack && item->isStackable() && tmpContainer->__queryAdd(n, item, item->getItemCount(), + 0) == RET_NOERROR && tmpItem->getID() == item->getID() && tmpItem->getItemCount() < 100) + { + index = n; + *destItem = tmpItem; + return tmpContainer; + } + + if(Container* container = tmpItem->getContainer()) + { + if(!autoStack && container->__queryAdd(INDEX_WHEREEVER, + item, item->getItemCount(), flags) == RET_NOERROR) + { + index = INDEX_WHEREEVER; + backpack = std::make_pair(container, index + 1); + return container; + } + + if(deepness < 0 || level < deepness) + containers.push_back(std::make_pair(container, level + 1)); + } } + else + { + if(!autoStack) + { + if(tmpContainer->__queryAdd(n, item, item->getItemCount(), 0) == RET_NOERROR) + { + index = n; + backpack = std::make_pair(tmpContainer, index + 1); + return tmpContainer; + } + } + else + freeSlots.push_back(std::make_pair(tmpContainer, n)); - deepList.push_back(std::make_pair(container, 0)); + break; // one slot to check is definitely enough. + } } } - //check deeper in the containers - int32_t deepness = g_config.getNumber(ConfigManager::PLAYER_DEEPNESS); - for(std::list >::iterator dit = deepList.begin(); dit != deepList.end(); ++dit) + if(autoStack) { - Container* c = (*dit).first; - if(!c || c->empty()) - continue; - - int32_t level = (*dit).second; - for(ItemList::const_iterator it = c->getItems(); it != c->getEnd(); ++it) + while(!freeSlots.empty()) { - if((*it) == tradeItem) + Cylinder* tmpCylinder = freeSlots.front().first; + int32_t i = freeSlots.front().second; + + freeSlots.pop_front(); + if(!tmpCylinder) continue; - if(Container* subContainer = dynamic_cast(*it)) + if(tmpCylinder->__queryAdd(i, item, item->getItemCount(), flags) == RET_NOERROR) { - if(subContainer->__queryAdd(-1, item, item->getItemCount(), 0) == RET_NOERROR) - { - index = INDEX_WHEREEVER; - *destItem = NULL; - return subContainer; - } - - if(deepness < 0 || level < deepness) - deepList.push_back(std::make_pair(subContainer, (level + 1))); + index = i; + return tmpCylinder; } } } @@ -2812,22 +3217,20 @@ void Player::__addThing(Creature* actor, Thing* thing) __addThing(actor, 0, thing); } -void Player::__addThing(Creature* actor, int32_t index, Thing* thing) +void Player::__addThing(Creature*, int32_t index, Thing* thing) { if(index < 0 || index > 11) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__addThing], " << "player: " << getName() << ", index: " << index << ", index < 0 || index > 11" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Player::__addThing], " << "player: " << getName() << ", index: " << index << ", index < 0 || index > 11" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } - if(index == 0) + if(!index) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__addThing], " << "player: " << getName() << ", index == 0" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Player::__addThing], " << "player: " << getName() << ", index == 0" << std::endl; #endif return /*RET_NOTENOUGHROOM*/; } @@ -2836,8 +3239,7 @@ void Player::__addThing(Creature* actor, int32_t index, Thing* thing) if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__addThing], " << "player: " << getName() << ", item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Player::__addThing], " << "player: " << getName() << ", item == NULL" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -2847,7 +3249,6 @@ void Player::__addThing(Creature* actor, int32_t index, Thing* thing) //send to client sendAddInventoryItem((slots_t)index, item); - //event methods onAddInventoryItem((slots_t)index, item); } @@ -2858,18 +3259,16 @@ void Player::__updateThing(Thing* thing, uint16_t itemId, uint32_t count) if(index == -1) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__updateThing], " << "player: " << getName() << ", index == -1" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Player::__updateThing], " << "player: " << getName() << ", index == -1" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } Item* item = thing->getItem(); - if(item == NULL) + if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__updateThing], " << "player: " << getName() << ", item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Player::__updateThing], " << "player: " << getName() << ", item == NULL" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -2888,11 +3287,10 @@ void Player::__updateThing(Thing* thing, uint16_t itemId, uint32_t count) void Player::__replaceThing(uint32_t index, Thing* thing) { - if(index < 0 || index > 11) + if(index > 11) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__replaceThing], " << "player: " << getName() << ", index: " << index << ", index < 0 || index > 11" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Player::__replaceThing], " << "player: " << getName() << ", index: " << index << ", index < 0 || index > 11" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -2901,8 +3299,7 @@ void Player::__replaceThing(uint32_t index, Thing* thing) if(!oldItem) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__updateThing], " << "player: " << getName() << ", oldItem == NULL" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Player::__updateThing], " << "player: " << getName() << ", oldItem == NULL" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -2911,8 +3308,7 @@ void Player::__replaceThing(uint32_t index, Thing* thing) if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__updateThing], " << "player: " << getName() << ", item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Player::__updateThing], " << "player: " << getName() << ", item == NULL" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -2935,8 +3331,7 @@ void Player::__removeThing(Thing* thing, uint32_t count) if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__removeThing], " << "player: " << getName() << ", item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Player::__removeThing], " << "player: " << getName() << ", item == NULL" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -2945,8 +3340,7 @@ void Player::__removeThing(Thing* thing, uint32_t count) if(index == -1) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__removeThing], " << "player: " << getName() << ", index == -1" << std::endl; - DEBUG_REPORT + std::clog << "Failure: [Player::__removeThing], " << "player: " << getName() << ", index == -1" << std::endl; #endif return /*RET_NOTPOSSIBLE*/; } @@ -3015,7 +3409,7 @@ int32_t Player::__getLastIndex() const return SLOT_LAST; } -uint32_t Player::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/, bool itemCount /*= true*/) const +uint32_t Player::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) const { Item* item = NULL; Container* container = NULL; @@ -3027,7 +3421,7 @@ uint32_t Player::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/, b continue; if(item->getID() == itemId) - count += Item::countByType(item, subType, itemCount); + count += Item::countByType(item, subType); if(!(container = item->getContainer())) continue; @@ -3035,7 +3429,7 @@ uint32_t Player::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/, b for(ContainerIterator it = container->begin(), end = container->end(); it != end; ++it) { if((*it)->getID() == itemId) - count += Item::countByType(*it, subType, itemCount); + count += Item::countByType(*it, subType); } } @@ -3043,8 +3437,7 @@ uint32_t Player::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/, b } -std::map& Player::__getAllItemTypeCount(std::map& countMap, bool itemCount/* = true*/) const +std::map& Player::__getAllItemTypeCount(std::map& countMap) const { Item* item = NULL; Container* container = NULL; @@ -3053,19 +3446,19 @@ std::map& Player::__getAllItemTypeCount(std::mapgetID()] += Item::countByType(item, -1, itemCount); + countMap[item->getID()] += Item::countByType(item, -1); if(!(container = item->getContainer())) continue; for(ContainerIterator it = container->begin(), end = container->end(); it != end; ++it) - countMap[(*it)->getID()] += Item::countByType(*it, -1, itemCount); + countMap[(*it)->getID()] += Item::countByType(*it, -1); } return countMap; } -void Player::postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link /*= LINK_OWNER*/) +void Player::postAddNotification(Creature*, Thing* thing, const Cylinder* oldParent, + int32_t index, CylinderLink_t link /*= LINK_OWNER*/) { if(link == LINK_OWNER) //calling movement scripts g_moveEvents->onPlayerEquip(this, thing->getItem(), (slots_t)index, false); @@ -3083,6 +3476,7 @@ void Player::postAddNotification(Creature* actor, Thing* thing, const Cylinder* updateInventoryWeight(); updateItemsLight(); + updateWeapon(); sendStats(); } @@ -3099,21 +3493,20 @@ void Player::postAddNotification(Creature* actor, Thing* thing, const Cylinder* if(creature != this) return; - typedef std::vector Containers; - Containers containers; + std::vector containers; for(ContainerVector::iterator it = containerVec.begin(); it != containerVec.end(); ++it) { if(!Position::areInRange<1,1,0>(it->second->getPosition(), getPosition())) containers.push_back(it->second); } - for(Containers::const_iterator it = containers.begin(); it != containers.end(); ++it) + for(std::vector::const_iterator it = containers.begin(); it != containers.end(); ++it) autoCloseContainers(*it); } } -void Player::postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link /*= LINK_OWNER*/) +void Player::postRemoveNotification(Creature*, Thing* thing, const Cylinder* newParent, + int32_t index, bool isCompleteRemoval, CylinderLink_t link/* = LINK_OWNER*/) { if(link == LINK_OWNER) //calling movement scripts g_moveEvents->onPlayerDeEquip(this, thing->getItem(), (slots_t)index, isCompleteRemoval); @@ -3124,13 +3517,14 @@ void Player::postRemoveNotification(Creature* actor, Thing* thing, const Cylinde if(const Item* item = (newParent ? newParent->getItem() : NULL)) { assert(item->getContainer() != NULL); - requireListUpdate = item->getContainer()->getHoldingPlayer() != this; + requireListUpdate = item->getContainer()->getHoldingPlayer() == this; } else - requireListUpdate = newParent != this; + requireListUpdate = newParent == this; updateInventoryWeight(); updateItemsLight(); + updateWeapon(); sendStats(); } @@ -3179,42 +3573,36 @@ void Player::__internalAddThing(Thing* thing) void Player::__internalAddThing(uint32_t index, Thing* thing) { #ifdef __DEBUG_MOVESYS__ - std::cout << "[Player::__internalAddThing] index: " << index << std::endl; -#endif + std::clog << "[Player::__internalAddThing] index: " << index << std::endl; - Item* item = thing->getItem(); - if(!item) +#endif + if(!index || index > 11) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__internalAddThing] item == NULL" << std::endl; + std::clog << "Failure: [Player::__internalAddThing] index == 0 || index > 11" << std::endl; #endif return; } - //index == 0 means we should equip this item at the most appropiate slot - if(index == 0) + if(inventory[index]) { #ifdef __DEBUG_MOVESYS__ - std::cout << "Failure: [Player::__internalAddThing] index == 0" << std::endl; - DEBUG_REPORT + std::clog << "Warning: [Player::__internalAddThing], player: " << getName() << ", items[index] is not empty." << std::endl; #endif return; } - if(index > 0 && index < 11) + Item* item = thing->getItem(); + if(!item) { - if(inventory[index]) - { #ifdef __DEBUG_MOVESYS__ - std::cout << "Warning: [Player::__internalAddThing], player: " << getName() << ", items[index] is not empty." << std::endl; - //DEBUG_REPORT + std::clog << "Failure: [Player::__internalAddThing] item == NULL" << std::endl; #endif - return; - } - - inventory[index] = item; - item->setParent(this); + return; } + + inventory[index] = item; + item->setParent(this); } bool Player::setFollowCreature(Creature* creature, bool fullPathSearch /*= false*/) @@ -3223,23 +3611,21 @@ bool Player::setFollowCreature(Creature* creature, bool fullPathSearch /*= false CreatureEventList followEvents = getCreatureEvents(CREATURE_EVENT_FOLLOW); for(CreatureEventList::iterator it = followEvents.begin(); it != followEvents.end(); ++it) { - if(creature && !(*it)->executeFollow(this, creature)) + if(!(*it)->executeAction(this, creature) && !deny) deny = true; } - if(deny || !Creature::setFollowCreature(creature, fullPathSearch)) - { - setFollowCreature(NULL); - setAttackedCreature(NULL); - if(!deny) - sendCancelMessage(RET_THEREISNOWAY); + if(deny || Creature::setFollowCreature(creature, fullPathSearch)) + return true; - sendCancelTarget(); - stopEventWalk(); - return false; - } + setFollowCreature(NULL); + setAttackedCreature(NULL); + if(!deny) + sendCancelMessage(RET_THEREISNOWAY); - return true; + sendCancelTarget(); + cancelNextWalk = true; + return false; } bool Player::setAttackedCreature(Creature* creature) @@ -3250,7 +3636,7 @@ bool Player::setAttackedCreature(Creature* creature) return false; } - if(chaseMode == CHASEMODE_FOLLOW && creature) + if(chaseMode == CHASEMODE_FOLLOW && creature && !getNoMove()) { if(followCreature != creature) //chase opponent setFollowCreature(creature); @@ -3264,35 +3650,46 @@ bool Player::setAttackedCreature(Creature* creature) return true; } +void Player::goToFollowCreature() +{ + if(!walkTask) + Creature::goToFollowCreature(); +} + void Player::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const { Creature::getPathSearchParams(creature, fpp); fpp.fullPathSearch = true; } -void Player::doAttacking(uint32_t interval) +void Player::doAttacking(uint32_t) { - if(!lastAttack) - lastAttack = OTSYS_TIME() - getAttackSpeed() - 1; - else if((OTSYS_TIME() - lastAttack) < getAttackSpeed()) - return; - if(hasCondition(CONDITION_PACIFIED) && !hasCustomFlag(PlayerCustomFlag_IgnorePacification)) { lastAttack = OTSYS_TIME(); return; } - Item* tool = getWeapon(); - if(const Weapon* weapon = g_weapons->getWeapon(tool)) + if(!lastAttack) + lastAttack = OTSYS_TIME() - getAttackSpeed() - 1; + else if((OTSYS_TIME() - lastAttack) < getAttackSpeed()) + return; + + if(const Weapon* _weapon = g_weapons->getWeapon(weapon)) { - if(weapon->interruptSwing() && !canDoAction()) + if(_weapon->interruptSwing() && !canDoAction()) { - SchedulerTask* task = createSchedulerTask(getNextActionTime(), boost::bind(&Game::checkCreatureAttack, &g_game, getID())); + SchedulerTask* task = createSchedulerTask(getNextActionTime(), + boost::bind(&Game::checkCreatureAttack, &g_game, getID())); setNextActionTask(task); } - else if((!weapon->hasExhaustion() || !hasCondition(CONDITION_EXHAUST, EXHAUST_COMBAT)) && weapon->useWeapon(this, tool, attackedCreature)) - lastAttack = OTSYS_TIME(); + else + { + if((!_weapon->hasExhaustion() || !hasCondition(CONDITION_EXHAUST)) && _weapon->useWeapon(this, weapon, attackedCreature)) + lastAttack = OTSYS_TIME(); + + updateWeapon(); + } } else if(Weapon::useFist(this, attackedCreature)) lastAttack = OTSYS_TIME(); @@ -3313,7 +3710,7 @@ double Player::getGainedExperience(Creature* attacker) const double attackerLevel = (double)attackerPlayer->getLevel(), min = g_config.getDouble( ConfigManager::EFP_MIN_THRESHOLD), max = g_config.getDouble(ConfigManager::EFP_MAX_THRESHOLD); - if((min > 0 && level < (uint32_t)std::floor(attackerLevel * min)) || (max > 0 && + if((min > 0.0 && level < (uint32_t)std::floor(attackerLevel * min)) || (max > 0.0 && level > (uint32_t)std::floor(attackerLevel * max))) return 0; @@ -3335,26 +3732,24 @@ double Player::getGainedExperience(Creature* attacker) const void Player::onFollowCreature(const Creature* creature) { if(!creature) - stopEventWalk(); + cancelNextWalk = true; } void Player::setChaseMode(chaseMode_t mode) { - chaseMode_t prevChaseMode = chaseMode; - chaseMode = mode; - - if(prevChaseMode == chaseMode) + if(chaseMode == mode) return; + chaseMode = mode; if(chaseMode == CHASEMODE_FOLLOW) { - if(!followCreature && attackedCreature) //chase opponent + if(!followCreature && attackedCreature && !getNoMove()) //chase opponent setFollowCreature(attackedCreature); } else if(attackedCreature) { setFollowCreature(NULL); - stopEventWalk(); + cancelNextWalk = true; } } @@ -3373,36 +3768,34 @@ void Player::onWalkComplete() walkTask = NULL; } -void Player::stopWalk() -{ - if(listWalkDir.empty()) - return; - - stopEventWalk(); -} - void Player::getCreatureLight(LightInfo& light) const { - if(internalLight.level > itemsLight.level) + if(hasCustomFlag(PlayerCustomFlag_HasFullLight)) + { + light.level = 0xFF; + light.color = 215; + } + else if(internalLight.level > itemsLight.level) light = internalLight; else light = itemsLight; } -void Player::updateItemsLight(bool internal /*=false*/) +void Player::updateItemsLight(bool internal/* = false*/) { - LightInfo maxLight; - LightInfo curLight; + LightInfo maxLight, curLight; + Item* item = NULL; for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) { - if(Item* item = getInventoryItem((slots_t)i)) - { - item->getLight(curLight); - if(curLight.level > maxLight.level) - maxLight = curLight; - } + if(!(item = getInventoryItem((slots_t)i))) + continue; + + item->getLight(curLight); + if(curLight.level > maxLight.level) + maxLight = curLight; } - if(itemsLight.level != maxLight.level || itemsLight.color != maxLight.color) + + if(maxLight.level != itemsLight.level || maxLight.color != itemsLight.color) { itemsLight = maxLight; if(!internal) @@ -3413,11 +3806,17 @@ void Player::updateItemsLight(bool internal /*=false*/) void Player::onAddCondition(ConditionType_t type, bool hadCondition) { Creature::onAddCondition(type, hadCondition); - if(getLastPosition().x && type != CONDITION_GAMEMASTER) // don't send if player have just logged in (its already done in protocolgame), or condition have no icons + if(type == CONDITION_GAMEMASTER) + return; + + if(type == CONDITION_INVISIBLE && !hadCondition && mounted) + dismount(false); + + if(getLastPosition().x) // don't send if player have just logged in (its already done in protocolgame), or condition have no icons sendIcons(); } -void Player::onAddCombatCondition(ConditionType_t type, bool hadCondition) +void Player::onAddCombatCondition(ConditionType_t type, bool) { std::string tmp; switch(type) @@ -3447,17 +3846,11 @@ void Player::onAddCombatCondition(ConditionType_t type, bool hadCondition) case CONDITION_DRUNK: tmp = "drunk"; break; - case CONDITION_MANASHIELD: - tmp = "protected by a magic shield"; - break; case CONDITION_PARALYZE: tmp = "paralyzed"; break; - case CONDITION_HASTE: - tmp = "hasted"; - break; - case CONDITION_ATTRIBUTES: - tmp = "strengthened"; + case CONDITION_BLEEDING: + tmp = "bleeding"; break; default: break; @@ -3485,7 +3878,7 @@ void Player::onEndCondition(ConditionType_t type) sendIcons(); } -void Player::onCombatRemoveCondition(const Creature* attacker, Condition* condition) +void Player::onCombatRemoveCondition(const Creature*, Condition* condition) { //Creature::onCombatRemoveCondition(attacker, condition); bool remove = true; @@ -3493,12 +3886,12 @@ void Player::onCombatRemoveCondition(const Creature* attacker, Condition* condit { remove = false; //Means the condition is from an item, id == slot - if(g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) + if(g_game.getWorldType() == WORLDTYPE_HARDCORE) { if(Item* item = getInventoryItem((slots_t)condition->getId())) { //25% chance to destroy the item - if(25 >= random_range(0, 100)) + if(random_range(1, 100) < 26) g_game.internalRemoveItem(NULL, item); } } @@ -3508,7 +3901,7 @@ void Player::onCombatRemoveCondition(const Creature* attacker, Condition* condit { if(!canDoAction()) { - uint32_t delay = getNextActionTime(); + int32_t delay = getNextActionTime(false); delay -= (delay % EVENT_CREATURE_THINK_INTERVAL); if(delay < 0) removeCondition(condition); @@ -3527,26 +3920,21 @@ void Player::onTickCondition(ConditionType_t type, int32_t interval, bool& _remo useStamina(-(interval * g_config.getNumber(ConfigManager::RATE_STAMINA_LOSS))); } -void Player::onAttackedCreature(Creature* target) +void Player::onTarget(Creature* target) { - Creature::onAttackedCreature(target); + Creature::onTarget(target); if(hasFlag(PlayerFlag_NotGainInFight)) return; - addInFightTicks(); + addInFightTicks(false); Player* targetPlayer = target->getPlayer(); if(!targetPlayer) return; addAttacked(targetPlayer); - if(targetPlayer == this && targetPlayer->getZone() != ZONE_PVP) - { - targetPlayer->sendCreatureSkull(this); - return; - } - - if(Combat::isInPvpZone(this, targetPlayer) || isPartner(targetPlayer) || (g_config.getBool( - ConfigManager::ALLOW_FIGHTBACK) && targetPlayer->hasAttacked(this))) + if(Combat::isInPvpZone(this, targetPlayer) || isPartner(targetPlayer) || isAlly(targetPlayer) + || (g_config.getBool(ConfigManager::ALLOW_FIGHTBACK) && targetPlayer->hasAttacked(this) + && !targetPlayer->isEnemy(this, false))) return; if(!pzLocked) @@ -3555,31 +3943,29 @@ void Player::onAttackedCreature(Creature* target) sendIcons(); } - if(getZone() != target->getZone()) + if(getZone() != target->getZone() || skull != SKULL_NONE || targetPlayer->isEnemy(this, true) + || canRevenge(targetPlayer->getGUID()) || g_game.getWorldType() != WORLDTYPE_OPEN) return; - if(skull == SKULL_NONE) + if(target->getSkull() != SKULL_NONE || (targetPlayer->canRevenge(guid) && targetPlayer->hasAttacked(this))) + targetPlayer->sendCreatureSkull(this); + else if(!hasCustomFlag(PlayerCustomFlag_NotGainSkull)) { - if(targetPlayer->getSkull() != SKULL_NONE) - targetPlayer->sendCreatureSkull(this); - else if(!hasCustomFlag(PlayerCustomFlag_NotGainSkull)) - { - setSkull(SKULL_WHITE); - g_game.updateCreatureSkull(this); - } + setSkull(SKULL_WHITE); + g_game.updateCreatureSkull(this); } } -void Player::onSummonAttackedCreature(Creature* summon, Creature* target) +void Player::onSummonTarget(Creature* summon, Creature* target) { - Creature::onSummonAttackedCreature(summon, target); - onAttackedCreature(target); + Creature::onSummonTarget(summon, target); + onTarget(target); } void Player::onAttacked() { Creature::onAttacked(); - addInFightTicks(); + addInFightTicks(false); } bool Player::checkLoginDelay(uint32_t playerId) const @@ -3591,57 +3977,111 @@ bool Player::checkLoginDelay(uint32_t playerId) const void Player::onIdleStatus() { Creature::onIdleStatus(); - if(getParty()) - getParty()->clearPlayerPoints(this); + if(party) + party->clearPlayerPoints(this); } void Player::onPlacedCreature() { //scripting event - onLogin if(!g_creatureEvents->playerLogin(this)) - kickPlayer(true, true); + kick(true, true); +} + +void Player::onTargetDrain(Creature* target, int32_t points) +{ + if(points < 0) + return; + + Creature::onTargetDrain(target, points); + if(party && target && (!target->getMaster() || !target->getMaster()->getPlayer()) + && target->getMonster() && target->getMonster()->isHostile()) //we have fulfilled a requirement for shared experience + party->addPlayerDamageMonster(this, points); } -void Player::onAttackedCreatureDrain(Creature* target, int32_t points) +void Player::onSummonTargetDrain(Creature* summon, Creature* target, int32_t points) { - Creature::onAttackedCreatureDrain(target, points); + if(points < 0) + return; + + Creature::onSummonTargetDrain(summon, target, points); if(party && target && (!target->getMaster() || !target->getMaster()->getPlayer()) && target->getMonster() && target->getMonster()->isHostile()) //we have fulfilled a requirement for shared experience - getParty()->addPlayerDamageMonster(this, points); + party->addPlayerDamageMonster(this, points); +} + +void Player::onTargetGain(Creature* target, int32_t points) +{ + Creature::onTargetGain(target, points); + if(!target || !party) + return; - char buffer[100]; - sprintf(buffer, "You deal %d damage to %s.", points, target->getNameDescription().c_str()); - sendTextMessage(MSG_STATUS_DEFAULT, buffer); + Player* tmpPlayer = NULL; + if(target->getPlayer()) + tmpPlayer = target->getPlayer(); + else if(target->getMaster() && target->getMaster()->getPlayer()) + tmpPlayer = target->getMaster()->getPlayer(); + + if(isPartner(tmpPlayer)) + party->addPlayerHealedMember(this, points); +} + +void Player::onUpdateQuest() +{ + sendTextMessage(MSG_EVENT_ADVANCE, "Your quest log has been updated."); } -void Player::onSummonAttackedCreatureDrain(Creature* summon, Creature* target, int32_t points) +GuildEmblems_t Player::getGuildEmblem(const Creature* creature) const { - Creature::onSummonAttackedCreatureDrain(summon, target, points); + const Player* player = creature->getPlayer(); + if(!player || !player->hasEnemy()) + return Creature::getGuildEmblem(creature); + + if(player->isEnemy(this, false)) + return EMBLEM_RED; - char buffer[100]; - sprintf(buffer, "Your %s deals %d damage to %s.", summon->getName().c_str(), points, target->getNameDescription().c_str()); - sendTextMessage(MSG_EVENT_DEFAULT, buffer); + return player->getGuildId() == guildId ? EMBLEM_GREEN : EMBLEM_BLUE; } -void Player::onTargetCreatureGainHealth(Creature* target, int32_t points) +bool Player::getEnemy(const Player* player, War_t& data) const { - Creature::onTargetCreatureGainHealth(target, points); - if(target && getParty()) - { - Player* tmpPlayer = NULL; - if(target->getPlayer()) - tmpPlayer = target->getPlayer(); - else if(target->getMaster() && target->getMaster()->getPlayer()) - tmpPlayer = target->getMaster()->getPlayer(); + if(!guildId || !player || player->isRemoved()) + return false; - if(isPartner(tmpPlayer)) - getParty()->addPlayerHealedMember(this, points); - } + uint32_t guild = player->getGuildId(); + if(!guild) + return false; + + WarMap::const_iterator it = warMap.find(guild); + if(it == warMap.end()) + return false; + + data = it->second; + return true; +} + +bool Player::isEnemy(const Player* player, bool allies) const +{ + if(!guildId || !player || player->isRemoved()) + return false; + + uint32_t guild = player->getGuildId(); + if(!guild) + return false; + + return !warMap.empty() && (((g_game.getWorldType() != WORLDTYPE_OPTIONAL || g_config.getBool( + ConfigManager::OPTIONAL_WAR_ATTACK_ALLY)) && allies && guildId == guild) || + warMap.find(guild) != warMap.end()); +} + +bool Player::isAlly(const Player* player) const +{ + return !warMap.empty() && player && player->getGuildId() == guildId; } -bool Player::onKilledCreature(Creature* target, uint32_t& flags) +bool Player::onKilledCreature(Creature* target, DeathEntry& entry) { - if(!Creature::onKilledCreature(target, flags)) + if(!Creature::onKilledCreature(target, entry)) return false; if(hasFlag(PlayerFlag_NotGenerateLoot)) @@ -3653,29 +4093,41 @@ bool Player::onKilledCreature(Creature* target, uint32_t& flags) g_config.getNumber(ConfigManager::HUNTING_DURATION)))) addCondition(condition); - if(hasFlag(PlayerFlag_NotGainInFight) || !hasBitSet((uint32_t)KILLFLAG_JUSTIFY, flags) || getZone() != target->getZone()) + if(hasFlag(PlayerFlag_NotGainInFight) || getZone() != target->getZone()) return true; Player* targetPlayer = target->getPlayer(); - if(!targetPlayer || Combat::isInPvpZone(this, targetPlayer) || !hasCondition(CONDITION_INFIGHT) || isPartner(targetPlayer)) + if(!targetPlayer || Combat::isInPvpZone(this, targetPlayer) + || isPartner(targetPlayer) || isAlly(targetPlayer)) return true; - if(!targetPlayer->hasAttacked(this) && target->getSkull() == SKULL_NONE && targetPlayer != this - && ((g_config.getBool(ConfigManager::USE_FRAG_HANDLER) && addUnjustifiedKill( - targetPlayer)) || hasBitSet((uint32_t)KILLFLAG_LASTHIT, flags))) - flags |= (uint32_t)KILLFLAG_UNJUSTIFIED; + War_t enemy; + if(targetPlayer->getEnemy(this, enemy)) + { + if(entry.isLast()) + IOGuild::getInstance()->updateWar(enemy); - pzLocked = true; - if((condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, - g_config.getNumber(ConfigManager::WHITE_SKULL_TIME)))) - addCondition(condition); + entry.setWar(enemy); + } + + if(!entry.isJustify() || !hasCondition(CONDITION_INFIGHT)) + return true; + + std::vector::iterator it = std::find(revengeList.begin(), revengeList.end(), targetPlayer->getGUID()); + if(!targetPlayer->hasAttacked(this) && target->getSkull() == SKULL_NONE && it == revengeList.end() + && targetPlayer != this && (addUnjustifiedKill(targetPlayer, !enemy.war) || entry.isLast())) + entry.setUnjustified(); + if(it != revengeList.end()) + revengeList.erase(it); + + addInFightTicks(true, g_config.getNumber(ConfigManager::WHITE_SKULL_TIME)); return true; } -bool Player::gainExperience(double& gainExp, bool fromMonster) +bool Player::gainExperience(double& gainExp, Creature* target) { - if(!rateExperience(gainExp, fromMonster)) + if(!rateExperience(gainExp, target)) return false; //soul regeneration @@ -3696,12 +4148,12 @@ bool Player::gainExperience(double& gainExp, bool fromMonster) return true; } -bool Player::rateExperience(double& gainExp, bool fromMonster) +bool Player::rateExperience(double& gainExp, Creature* target) { if(hasFlag(PlayerFlag_NotGainExperience) || gainExp <= 0) return false; - if(!fromMonster) + if(target->getPlayer()) return true; gainExp *= rates[SKILL__LEVEL] * g_game.getExperienceStage(level, @@ -3711,7 +4163,7 @@ bool Player::rateExperience(double& gainExp, bool fromMonster) int32_t minutes = getStaminaMinutes(); if(minutes >= g_config.getNumber(ConfigManager::STAMINA_LIMIT_TOP)) { - if(isPremium() || !g_config.getNumber(ConfigManager::STAMINA_BONUS_PREMIUM)) + if(isPremium() || !g_config.getBool(ConfigManager::STAMINA_BONUS_PREMIUM)) gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_ABOVE); } else if(minutes < (g_config.getNumber(ConfigManager::STAMINA_LIMIT_BOTTOM)) && minutes > 0) @@ -3719,29 +4171,34 @@ bool Player::rateExperience(double& gainExp, bool fromMonster) else if(minutes <= 0) gainExp = 0; } - else if(isPremium() || !g_config.getNumber(ConfigManager::STAMINA_BONUS_PREMIUM)) + else if(isPremium() || !g_config.getBool(ConfigManager::STAMINA_BONUS_PREMIUM)) gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_ABOVE); return true; } -void Player::onGainExperience(double& gainExp, bool fromMonster, bool multiplied) +void Player::onGainExperience(double& gainExp, Creature* target, bool multiplied) { + uint64_t tmp = experience; if(party && party->isSharedExperienceEnabled() && party->isSharedExperienceActive()) { - party->shareExperience(gainExp, fromMonster, multiplied); - rateExperience(gainExp, fromMonster); + party->shareExperience(gainExp, target, multiplied); + rateExperience(gainExp, target); return; //we will get a share of the experience through the sharing mechanism } - if(gainExperience(gainExp, fromMonster)) - Creature::onGainExperience(gainExp, fromMonster, true); + if(gainExperience(gainExp, target)) + Creature::onGainExperience(gainExp, target, true); + + CreatureEventList advanceEvents = getCreatureEvents(CREATURE_EVENT_ADVANCE); + for(CreatureEventList::iterator it = advanceEvents.begin(); it != advanceEvents.end(); ++it) + (*it)->executeAdvance(this, SKILL__EXPERIENCE, tmp, experience); } -void Player::onGainSharedExperience(double& gainExp, bool fromMonster, bool multiplied) +void Player::onGainSharedExperience(double& gainExp, Creature* target, bool) { - if(gainExperience(gainExp, fromMonster)) - Creature::onGainSharedExperience(gainExp, fromMonster, true); + if(gainExperience(gainExp, target)) + Creature::onGainSharedExperience(gainExp, target, true); } bool Player::isImmune(CombatType_t type) const @@ -3754,9 +4211,14 @@ bool Player::isImmune(ConditionType_t type) const return hasCustomFlag(PlayerCustomFlag_IsImmune) || Creature::isImmune(type); } +bool Player::isProtected() const +{ + return (vocation && !vocation->isAttackable()) || hasCustomFlag(PlayerCustomFlag_IsProtected) || level < g_config.getNumber(ConfigManager::PROTECTION_LEVEL); +} + bool Player::isAttackable() const { - return (!hasFlag(PlayerFlag_CannotBeAttacked) && !isAccountManager()); + return !hasFlag(PlayerFlag_CannotBeAttacked) && !isAccountManager(); } void Player::changeHealth(int32_t healthChange) @@ -3781,20 +4243,19 @@ void Player::changeSoul(int32_t soulChange) sendStats(); } -bool Player::canLogout(bool checkInfight) -{ - if(checkInfight && hasCondition(CONDITION_INFIGHT)) - return false; - - return !isConnecting && !pzLocked && !getTile()->hasFlag(TILESTATE_NOLOGOUT); -} - bool Player::changeOutfit(Outfit_t outfit, bool checkList) { uint32_t outfitId = Outfits::getInstance()->getOutfitId(outfit.lookType); if(checkList && (!canWearOutfit(outfitId, outfit.lookAddons) || !requestedOutfit)) return false; + if(outfit.lookMount && outfit.lookMount != getDefaultOutfit().lookMount) + { + Mount* mount = Mounts::getInstance()->getMountByCid(outfit.lookMount); + if(!mount || !mount->isTamed(this)) + return false; + } + requestedOutfit = false; if(outfitAttributes) { @@ -3811,14 +4272,27 @@ bool Player::canWearOutfit(uint32_t outfitId, uint32_t addons) { OutfitMap::iterator it = outfits.find(outfitId); if(it == outfits.end() || (it->second.isPremium && !isPremium()) || getAccess() < it->second.accessLevel - || ((it->second.addons & addons) != addons && !hasCustomFlag(PlayerCustomFlag_CanWearAllAddons))) + || (!it->second.groups.empty() && std::find(it->second.groups.begin(), it->second.groups.end(), groupId) + == it->second.groups.end()) || ((it->second.addons & addons) != addons && !hasCustomFlag(PlayerCustomFlag_CanWearAllAddons))) return false; - if(!it->second.storageId) + if(it->second.storageId.empty()) return true; std::string value; - return getStorage(it->second.storageId, value) && value == it->second.storageValue; + getStorage(it->second.storageId, value); + if(value == it->second.storageValue) + return true; + + int32_t intValue = atoi(value.c_str()); + if(!intValue && value != "0") + return false; + + int32_t tmp = atoi(it->second.storageValue.c_str()); + if(!tmp && it->second.storageValue != "0") + return false; + + return intValue >= tmp; } bool Player::addOutfit(uint32_t outfitId, uint32_t addons) @@ -3842,17 +4316,35 @@ bool Player::removeOutfit(uint32_t outfitId, uint32_t addons) if(it == outfits.end()) return false; + bool update = false; if(addons == 0xFF) //remove outfit - outfits.erase(it); + { + if(it->second.lookType == defaultOutfit.lookType) + { + outfits.erase(it); + if((it = outfits.begin()) != outfits.end()) + defaultOutfit.lookType = it->second.lookType; + + update = true; + } + else + outfits.erase(it); + } else //remove addons - outfits[outfitId].addons = it->second.addons & (~addons); + { + update = it->second.lookType == defaultOutfit.lookType; + it->second.addons &= ~addons; + } + + if(update) + g_game.internalCreatureChangeOutfit(this, defaultOutfit, true); return true; } void Player::generateReservedStorage() { - uint32_t baseKey = PSTRG_OUTFITSID_RANGE_START + 1; + uint32_t key = PSTRG_OUTFITSID_RANGE_START + 1; const OutfitMap& defaultOutfits = Outfits::getInstance()->getOutfits(sex); for(OutfitMap::const_iterator it = outfits.begin(); it != outfits.end(); ++it) { @@ -3861,15 +4353,15 @@ void Player::generateReservedStorage() & it->second.addons) == it->second.addons)) continue; - std::stringstream ss; - ss << ((it->first << 16) | (it->second.addons & 0xFF)); - storageMap[baseKey] = ss.str(); + std::stringstream k, v; + k << key++; // this may not work as intended, revalidate it + v << ((it->first << 16) | (it->second.addons & 0xFF)); - baseKey++; - if(baseKey <= PSTRG_OUTFITSID_RANGE_START + PSTRG_OUTFITSID_RANGE_SIZE) + storageMap[k.str()] = v.str(); + if(key <= PSTRG_OUTFITSID_RANGE_START + PSTRG_OUTFITSID_RANGE_SIZE) continue; - std::cout << "[Warning - Player::genReservedStorageRange] Player " << getName() << " with more than 500 outfits!" << std::endl; + std::clog << "[Warning - Player::genReservedStorageRange] Player " << getName() << " with more than 500 outfits!" << std::endl; break; } } @@ -3893,21 +4385,27 @@ Skulls_t Player::getSkull() const return skull; } -Skulls_t Player::getSkullClient(const Creature* creature) const +Skulls_t Player::getSkullType(const Creature* creature) const { - if(const Player* player = creature->getPlayer()) + const Player* player = creature->getPlayer(); + if(player && player->getSkull() == SKULL_NONE) { - if(g_game.getWorldType() != WORLD_TYPE_PVP) + if(g_game.getWorldType() != WORLDTYPE_OPEN) return SKULL_NONE; - if((player == this || (skull != SKULL_NONE && player->getSkull() < SKULL_RED)) && player->hasAttacked(this)) + if(canRevenge(player->getGUID())) + return SKULL_ORANGE; + + if((skull != SKULL_NONE || player->canRevenge(guid)) && + player->hasAttacked(this) && !player->isEnemy(this, false)) return SKULL_YELLOW; - if(player->getSkull() == SKULL_NONE && isPartner(player) && g_game.getWorldType() != WORLD_TYPE_NO_PVP) + if((isPartner(player) || isAlly(player)) && + g_game.getWorldType() != WORLDTYPE_OPTIONAL) return SKULL_GREEN; } - return Creature::getSkullClient(creature); + return Creature::getSkullType(creature); } bool Player::hasAttacked(const Player* attacked) const @@ -3928,7 +4426,9 @@ void Player::addAttacked(const Player* attacked) void Player::setSkullEnd(time_t _time, bool login, Skulls_t _skull) { - if(g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) + if(g_game.getWorldType() != WORLDTYPE_OPEN + || hasFlag(PlayerFlag_NotGainInFight) || + hasCustomFlag(PlayerCustomFlag_NotGainSkull)) return; bool requireUpdate = false; @@ -3952,50 +4452,54 @@ void Player::setSkullEnd(time_t _time, bool login, Skulls_t _skull) } } -bool Player::addUnjustifiedKill(const Player* attacked) +bool Player::addUnjustifiedKill(const Player* attacked, bool countNow) { - if(g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED || attacked == this || hasFlag( - PlayerFlag_NotGainInFight) || hasCustomFlag(PlayerCustomFlag_NotGainSkull)) + if(!g_config.getBool(ConfigManager::USE_FRAG_HANDLER) || hasFlag( + PlayerFlag_NotGainInFight) || g_game.getWorldType() != WORLDTYPE_OPEN + || hasCustomFlag(PlayerCustomFlag_NotGainUnjustified) || hasCustomFlag( + PlayerCustomFlag_NotGainSkull)) return false; - if(client) + if(countNow) { char buffer[90]; - sprintf(buffer, "Warning! The murder of %s was not justified.", - attacked->getName().c_str()); - client->sendTextMessage(MSG_STATUS_WARNING, buffer); + sprintf(buffer, "Warning! The murder of %s was not justified.", attacked->getName().c_str()); + sendTextMessage(MSG_STATUS_WARNING, buffer); } - time_t now = time(NULL), today = (now - 84600), week = (now - (7 * 84600)); + time_t now = time(NULL), first = (now - g_config.getNumber(ConfigManager::FRAG_LIMIT)), + second = (now - g_config.getNumber(ConfigManager::FRAG_SECOND_LIMIT)); std::vector dateList; + IOLoginData::getInstance()->getUnjustifiedDates(guid, dateList, now); + if(countNow) + dateList.push_back(now); - dateList.push_back(now); - uint32_t tc = 0, wc = 0, mc = dateList.size(); + uint32_t fc = 0, sc = 0, tc = dateList.size(); for(std::vector::iterator it = dateList.begin(); it != dateList.end(); ++it) { - if((*it) > week) - wc++; + if(second > 0 && (*it) > second) + sc++; - if((*it) > today) - tc++; + if(first > 0 && (*it) > first) + fc++; } - uint32_t d = g_config.getNumber(ConfigManager::RED_DAILY_LIMIT), w = g_config.getNumber( - ConfigManager::RED_WEEKLY_LIMIT), m = g_config.getNumber(ConfigManager::RED_MONTHLY_LIMIT); - if(skull < SKULL_RED && ((d > 0 && tc >= d) || (w > 0 && wc >= w) || (m > 0 && mc >= m))) + uint32_t f = g_config.getNumber(ConfigManager::RED_LIMIT), s = g_config.getNumber( + ConfigManager::RED_SECOND_LIMIT), t = g_config.getNumber(ConfigManager::RED_THIRD_LIMIT); + if(skull < SKULL_RED && ((f > 0 && fc >= f) || (s > 0 && sc >= s) || (t > 0 && tc >= t))) setSkullEnd(now + g_config.getNumber(ConfigManager::RED_SKULL_LENGTH), false, SKULL_RED); if(!g_config.getBool(ConfigManager::USE_BLACK_SKULL)) { - d += g_config.getNumber(ConfigManager::BAN_DAILY_LIMIT); - w += g_config.getNumber(ConfigManager::BAN_WEEKLY_LIMIT); - m += g_config.getNumber(ConfigManager::BAN_MONTHLY_LIMIT); - if((d <= 0 || tc < d) && (w <= 0 || wc < w) && (m <= 0 || mc < m)) + f += g_config.getNumber(ConfigManager::BAN_LIMIT); + s += g_config.getNumber(ConfigManager::BAN_SECOND_LIMIT); + t += g_config.getNumber(ConfigManager::BAN_THIRD_LIMIT); + if((f <= 0 || fc < f) && (s <= 0 || sc < s) && (t <= 0 || tc < t)) return true; if(!IOBan::getInstance()->addAccountBanishment(accountId, (now + g_config.getNumber( - ConfigManager::KILLS_BAN_LENGTH)), 20, ACTION_BANISHMENT, "Unjustified player killing.", 0, guid)) + ConfigManager::KILLS_BAN_LENGTH)), "Unjustified player killing.", 0, guid)) return true; sendTextMessage(MSG_INFO_DESCR, "You have been banished."); @@ -4005,10 +4509,10 @@ bool Player::addUnjustifiedKill(const Player* attacked) } else { - d += g_config.getNumber(ConfigManager::BLACK_DAILY_LIMIT); - w += g_config.getNumber(ConfigManager::BLACK_WEEKLY_LIMIT); - m += g_config.getNumber(ConfigManager::BLACK_MONTHLY_LIMIT); - if(skull < SKULL_BLACK && ((d > 0 && tc >= d) || (w > 0 && wc >= w) || (m > 0 && mc >= m))) + f += g_config.getNumber(ConfigManager::BLACK_LIMIT); + s += g_config.getNumber(ConfigManager::BLACK_SECOND_LIMIT); + t += g_config.getNumber(ConfigManager::BLACK_THIRD_LIMIT); + if(skull < SKULL_BLACK && ((f > 0 && fc >= f) || (s > 0 && sc >= s) || (t > 0 && tc >= t))) { setSkullEnd(now + g_config.getNumber(ConfigManager::BLACK_SKULL_LENGTH), false, SKULL_BLACK); setAttackedCreature(NULL); @@ -4023,7 +4527,7 @@ void Player::setPromotionLevel(uint32_t pLevel) { if(pLevel > promotionLevel) { - uint32_t tmpLevel = 0, currentVoc = vocation_id; + uint32_t tmpLevel = 0, currentVoc = vocationId; for(uint32_t i = promotionLevel; i < pLevel; ++i) { currentVoc = Vocations::getInstance()->getPromotedVocation(currentVoc); @@ -4035,14 +4539,14 @@ void Player::setPromotionLevel(uint32_t pLevel) if(voc->isPremiumNeeded() && !isPremium() && g_config.getBool(ConfigManager::PREMIUM_FOR_PROMOTION)) continue; - vocation_id = currentVoc; + vocationId = currentVoc; } promotionLevel += tmpLevel; } else if(pLevel < promotionLevel) { - uint32_t tmpLevel = 0, currentVoc = vocation_id; + uint32_t tmpLevel = 0, currentVoc = vocationId; for(uint32_t i = pLevel; i < promotionLevel; ++i) { Vocation* voc = Vocations::getInstance()->getVocation(currentVoc); @@ -4054,18 +4558,19 @@ void Player::setPromotionLevel(uint32_t pLevel) if(voc->isPremiumNeeded() && !isPremium() && g_config.getBool(ConfigManager::PREMIUM_FOR_PROMOTION)) continue; - vocation_id = currentVoc; + vocationId = currentVoc; } promotionLevel -= tmpLevel; } - setVocation(vocation_id); + setVocation(vocationId); } uint16_t Player::getBlessings() const { - if(!isPremium() && g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM)) + if(!g_config.getBool(ConfigManager::BLESSINGS) || (!isPremium() && + g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM))) return 0; uint16_t count = 0; @@ -4080,13 +4585,10 @@ uint16_t Player::getBlessings() const uint64_t Player::getLostExperience() const { - if(!skillLoss) - return 0; - double percent = (double)(lossPercent[LOSS_EXPERIENCE] - vocation->getLessLoss() - (getBlessings() * g_config.getNumber( ConfigManager::BLESS_REDUCTION))) / 100.; if(level <= 25) - return (uint64_t)std::floor((double)(experience * percent) / 10.); + return (uint64_t)std::floor(percent * experience / 10.); int32_t base = level; double levels = (double)(base + 50) / 100.; @@ -4100,18 +4602,42 @@ uint64_t Player::getLostExperience() const } if(levels > 0.) - lost += (uint64_t)std::floor((double)(getExpForLevel(base) - getExpForLevel(base - 1)) * levels); + lost += (uint64_t)std::floor(levels * (getExpForLevel(base) - getExpForLevel(base - 1))); - return (uint64_t)std::floor((double)(lost * percent)); + return (uint64_t)std::floor(percent * lost); } -uint32_t Player::getAttackSpeed() +uint32_t Player::getAttackSpeed() const { - Item* weapon = getWeapon(); - if(weapon && weapon->getAttackSpeed() != 0) - return weapon->getAttackSpeed(); + int32_t modifiers = 0; + if(mounted) + { + if(Mount* tmp = Mounts::getInstance()->getMountByCid(defaultOutfit.lookMount)) + { + if(tmp->getAttackSpeed() == -1) + return 0; + + modifiers += tmp->getAttackSpeed(); + } + } + + if(outfitAttributes) + { + Outfit outfit; + if(Outfits::getInstance()->getOutfit(defaultOutfit.lookType, outfit)) + { + if(outfit.attackSpeed == -1) + return 0; + + modifiers += outfit.attackSpeed; + } + } - return vocation->getAttackSpeed(); + Item* _weapon = weapon; + if(!weapon || weapon->getWeaponType() == WEAPON_AMMO) + _weapon = const_cast(this)->getWeapon(true); + + return (((_weapon && _weapon->getAttackSpeed() != 0) ? _weapon->getAttackSpeed() : (vocation->getAttackSpeed() / std::max((size_t)1, getWeapons().size()))) + modifiers); } void Player::learnInstantSpell(const std::string& name) @@ -4140,7 +4666,7 @@ bool Player::hasLearnedInstantSpell(const std::string& name) const for(LearnedInstantSpellList::const_iterator it = learnedInstantSpellList.begin(); it != learnedInstantSpellList.end(); ++it) { - if(!strcasecmp((*it).c_str(), name.c_str())) + if(boost::algorithm::iequals(*it, name)) return true; } @@ -4150,8 +4676,6 @@ bool Player::hasLearnedInstantSpell(const std::string& name) const void Player::manageAccount(const std::string &text) { std::stringstream msg; - msg << "Account Manager: "; - bool noSwap = true; switch(accountManager) { @@ -4161,31 +4685,30 @@ void Player::manageAccount(const std::string &text) { managerString = text; trimString(managerString); - if(managerString.length() < 4) - msg << "Your name you want is too short, please select a longer name."; - else if(managerString.length() > 20) - msg << "The name you want is too long, please select a shorter name."; + if(managerString.length() < 3) + msg << "The name is too short, please select a longer one."; + else if(managerString.length() > 30) + msg << "The name is too long, please select a shorter one."; else if(!isValidName(managerString)) - msg << "That name seems to contain invalid symbols, please choose another name."; + msg << "Your name seems to contain invalid symbols, please choose another one."; else if(IOLoginData::getInstance()->playerExists(managerString, true)) - msg << "A player with that name already exists, please choose another name."; + msg << "Player with that name already exists, please choose another one."; else { std::string tmp = asLowerCaseString(managerString); if(tmp.substr(0, 4) != "god " && tmp.substr(0, 3) != "cm " && tmp.substr(0, 3) != "gm ") { - talkState[1] = true; - talkState[2] = true; - msg << managerString << ", are you sure?"; + talkState[1] = talkState[2] = true; + msg << "{" << managerString << "}, are you sure? {yes} or {no}?"; } else - msg << "Your character is not a staff member, please tell me another name!"; + msg << "Your character is not a staff member, please choose another name."; } } else if(checkText(text, "no") && talkState[2]) { talkState[1] = talkState[2] = false; - msg << "What else would you like to name your character?"; + msg << "What new name would you like have then?"; } else if(checkText(text, "yes") && talkState[2]) { @@ -4196,27 +4719,27 @@ void Player::manageAccount(const std::string &text) IOLoginData::getInstance()->changeName(tmp, managerString, managerString2) && IOBan::getInstance()->removePlayerBanishment(tmp, PLAYERBAN_LOCK)) { + msg << "Your character {" << managerString << "} has been successfully renamed to {" << managerString2 << "}, you should be able to login now."; if(House* house = Houses::getInstance()->getHouseByPlayerId(tmp)) house->updateDoorDescription(managerString); talkState[1] = true; talkState[2] = false; - msg << "Your character has been successfully renamed, you should now be able to login at it without any problems."; } else { talkState[1] = talkState[2] = false; - msg << "Failed to change your name, please try again."; + msg << "Failed to change your name, please contact with staff."; } } else { talkState[1] = talkState[2] = false; - msg << "A player with that name already exists, please choose another name."; + msg << "Player with that name already exists, please choose another one."; } } else - msg << "Sorry, but I can't understand you, please try to repeat that!"; + msg << "Sorry, but I can't understand you, please try to repeat."; break; } @@ -4226,10 +4749,10 @@ void Player::manageAccount(const std::string &text) if(checkText(text, "cancel") || (checkText(text, "account") && !talkState[1])) { talkState[1] = true; - for(int8_t i = 2; i <= 12; i++) + for(int8_t i = 2; i <= 12; ++i) talkState[i] = false; - msg << "Do you want to change your 'password', request a 'recovery key', add a 'character', or 'delete' a character?"; + msg << "Do you want to change your {password}, generate a {recovery key}, create a {character}, or {delete} an existing character?"; } else if(checkText(text, "delete") && talkState[1]) { @@ -4242,13 +4765,13 @@ void Player::manageAccount(const std::string &text) std::string tmp = text; trimString(tmp); if(!isValidName(tmp, false)) - msg << "That name contains invalid characters, try to say your name again, you might have typed it wrong."; + msg << "That name to contain invalid symbols, please try again."; else { talkState[2] = false; talkState[3] = true; managerString = tmp; - msg << "Do you really want to delete the character named " << managerString << "?"; + msg << "Do you really want to delete the character {" << managerString << "}? {yes} or {no}"; } } else if(checkText(text, "yes") && talkState[3]) @@ -4264,54 +4787,54 @@ void Player::manageAccount(const std::string &text) break; case DELETE_HOUSE: - msg << "Your character owns a house. To make sure you really want to lose your house by deleting your character, you have to login and leave the house or pass it to someone else first."; + msg << "Your character owns a house. You have to login and leave the house or pass it to someone else to complete."; break; case DELETE_LEADER: - msg << "Your character is the leader of a guild. You need to disband or pass the leadership someone else to delete your character."; + msg << "Your character is leader of a guild. You have to disband the guild or pass the leadership to someone else to complete."; break; case DELETE_ONLINE: - msg << "A character with that name is currently online, to delete a character it has to be offline."; + msg << "Character with that name is currently online, to delete a character it has to be offline."; break; } talkState[1] = true; - for(int8_t i = 2; i <= 12; i++) + for(int8_t i = 2; i <= 12; ++i) talkState[i] = false; } else if(checkText(text, "no") && talkState[3]) { talkState[1] = true; talkState[3] = false; - msg << "Tell me what character you want to delete."; + msg << "Which character would you like to delete then?"; } else if(checkText(text, "password") && talkState[1]) { talkState[1] = false; talkState[4] = true; - msg << "Tell me your new password please."; + msg << "What would you like your password to be?"; } else if(talkState[4]) { std::string tmp = text; trimString(tmp); if(tmp.length() < 6) - msg << "That password is too short, at least 6 digits are required. Please select a longer password."; + msg << "That password is too short, please select a longer one."; else if(!isValidPassword(tmp)) - msg << "Your password contains invalid characters... please tell me another one."; + msg << "Your password seems to contain invalid symbols, please choose another one."; else { talkState[4] = false; talkState[5] = true; managerString = tmp; - msg << "Should '" << managerString << "' be your new password?"; + msg << "{" << managerString << "} is it? {yes} or {no}?"; } } else if(checkText(text, "yes") && talkState[5]) { talkState[1] = true; - for(int8_t i = 2; i <= 12; i++) + for(int8_t i = 2; i <= 12; ++i) talkState[i] = false; IOLoginData::getInstance()->setPassword(managerNumber, managerString); @@ -4320,10 +4843,10 @@ void Player::manageAccount(const std::string &text) else if(checkText(text, "no") && talkState[5]) { talkState[1] = true; - for(int8_t i = 2; i <= 12; i++) + for(int8_t i = 2; i <= 12; ++i) talkState[i] = false; - msg << "Then not."; + msg << "Ok, then not."; } else if(checkText(text, "character") && talkState[1]) { @@ -4336,24 +4859,24 @@ void Player::manageAccount(const std::string &text) else { talkState[1] = true; - for(int8_t i = 2; i <= 12; i++) + for(int8_t i = 2; i <= 12; ++i) talkState[i] = false; - msg << "Your account reach the limit of 15 players, you can 'delete' a character if you want to create a new one."; + msg << "Your account has reached the limit of 15 characters, you should {delete} a character if you want to create a new one."; } } else if(talkState[6]) { managerString = text; trimString(managerString); - if(managerString.length() < 4) - msg << "Your name you want is too short, please select a longer name."; - else if(managerString.length() > 20) - msg << "The name you want is too long, please select a shorter name."; + if(managerString.length() < 3) + msg << "That name is too short, please select a longer one."; + else if(managerString.length() > 30) + msg << "That name is too long, please select a shorter one."; else if(!isValidName(managerString)) - msg << "That name seems to contain invalid symbols, please choose another name."; + msg << "Your name seems to contain invalid symbols, please choose another one."; else if(IOLoginData::getInstance()->playerExists(managerString, true)) - msg << "A player with that name already exists, please choose another name."; + msg << "Player with that name already exists, please choose another one."; else { std::string tmp = asLowerCaseString(managerString); @@ -4361,23 +4884,23 @@ void Player::manageAccount(const std::string &text) { talkState[6] = false; talkState[7] = true; - msg << managerString << ", are you sure?"; + msg << "{" << managerString << "}, are you sure? {yes} or {no}"; } else - msg << "Your character is not a staff member, please tell me another name!"; + msg << "Your character is not a staff member, please choose another name."; } } else if(checkText(text, "no") && talkState[7]) { talkState[6] = true; talkState[7] = false; - msg << "What else would you like to name your character?"; + msg << "What would you like your character name to be then?"; } else if(checkText(text, "yes") && talkState[7]) { talkState[7] = false; talkState[8] = true; - msg << "Should your character be a 'male' or a 'female'."; + msg << "Would you like to be a {male} or a {female}."; } else if(talkState[8] && (checkText(text, "female") || checkText(text, "male"))) { @@ -4385,12 +4908,12 @@ void Player::manageAccount(const std::string &text) talkState[9] = true; if(checkText(text, "female")) { - msg << "A female, are you sure?"; + msg << "A female, are you sure? {yes} or {no}"; managerSex = PLAYERSEX_FEMALE; } else { - msg << "A male, are you sure?"; + msg << "A male, are you sure? {yes} or {no}"; managerSex = PLAYERSEX_MALE; } } @@ -4398,7 +4921,7 @@ void Player::manageAccount(const std::string &text) { talkState[8] = true; talkState[9] = false; - msg << "Tell me... would you like to be a 'male' or a 'female'?"; + msg << "Tell me then, would you like to be a {male} or a {female}?"; } else if(checkText(text, "yes") && talkState[9]) { @@ -4407,114 +4930,112 @@ void Player::manageAccount(const std::string &text) talkState[9] = false; talkState[11] = true; - bool firstPart = true; + std::vector vocations; for(VocationsMap::iterator it = Vocations::getInstance()->getFirstVocation(); it != Vocations::getInstance()->getLastVocation(); ++it) { if(it->first == it->second->getFromVocation() && it->first != 0) - { - if(firstPart) - { - msg << "What do you want to be... " << it->second->getDescription(); - firstPart = false; - } - else if(it->first - 1 != 0) - msg << ", " << it->second->getDescription(); - else - msg << " or " << it->second->getDescription() << "."; - } + vocations.push_back(it->second->getName()); + } + + msg << "What would you like to be... "; + for(std::vector::const_iterator it = vocations.begin(); it != vocations.end(); ++it) + { + if(it == vocations.begin()) + msg << "{" << *it << "}"; + else if(*it == *(vocations.rbegin())) + msg << " or {" << *it << "}."; + else + msg << ", {" << *it << "}"; } } else if(!IOLoginData::getInstance()->playerExists(managerString, true)) { talkState[1] = true; - for(int8_t i = 2; i <= 12; i++) + for(int8_t i = 2; i <= 12; ++i) talkState[i] = false; if(IOLoginData::getInstance()->createCharacter(managerNumber, managerString, managerNumber2, (uint16_t)managerSex)) - msg << "Your character has been created."; + msg << "Your character {" << managerString << "} has been created."; else - msg << "Your character couldn't be created, please try again."; + msg << "Your character couldn't be created, please contact with staff."; } else { talkState[6] = true; talkState[9] = false; - msg << "A player with that name already exists, please choose another name."; + msg << "Player with that name already exists, please choose another one."; } } else if(talkState[11]) { for(VocationsMap::iterator it = Vocations::getInstance()->getFirstVocation(); it != Vocations::getInstance()->getLastVocation(); ++it) { - std::string tmp = asLowerCaseString(it->second->getName()); - if(checkText(text, tmp) && it != Vocations::getInstance()->getLastVocation() && it->first == it->second->getFromVocation() && it->first != 0) + if(checkText(text, asLowerCaseString(it->second->getName())) && + it->first == it->second->getFromVocation() && it->first != 0) { - msg << "So you would like to be " << it->second->getDescription() << "... are you sure?"; + msg << "So you would like to be " << it->second->getDescription() << ", {yes} or {no}?"; managerNumber2 = it->first; talkState[11] = false; talkState[12] = true; } } - - if(msg.str().length() == 17) - msg << "I don't understand what vocation you would like to be... could you please repeat it?"; } else if(checkText(text, "yes") && talkState[12]) { if(!IOLoginData::getInstance()->playerExists(managerString, true)) { talkState[1] = true; - for(int8_t i = 2; i <= 12; i++) + for(int8_t i = 2; i <= 12; ++i) talkState[i] = false; if(IOLoginData::getInstance()->createCharacter(managerNumber, managerString, managerNumber2, (uint16_t)managerSex)) - msg << "Your character has been created."; + msg << "Your character {" << managerString << "} has been created."; else - msg << "Your character couldn't be created, please try again."; + msg << "Your character couldn't be created, please contact with staff."; } else { talkState[6] = true; talkState[9] = false; - msg << "A player with that name already exists, please choose another name."; + msg << "Player with that name already exists, please choose another one."; } } else if(checkText(text, "no") && talkState[12]) { talkState[11] = true; talkState[12] = false; - msg << "No? Then what would you like to be?"; + msg << "What would you like to be then?"; } else if(checkText(text, "recovery key") && talkState[1]) { talkState[1] = false; talkState[10] = true; - msg << "Would you like a recovery key?"; + msg << "Would you like to generate a recovery key? {yes} or {no}"; } else if(checkText(text, "yes") && talkState[10]) { if(account.recoveryKey != "0") - msg << "Sorry, you already have a recovery key, for security reasons I may not give you a new one."; + msg << "Sorry, but you already have a recovery key. For security reasons I may not generate for you you a new one."; else { managerString = generateRecoveryKey(4, 4); IOLoginData::getInstance()->setRecoveryKey(managerNumber, managerString); - msg << "Your recovery key is: " << managerString << "."; + msg << "Your recovery key is {" << managerString << "}."; } talkState[1] = true; - for(int8_t i = 2; i <= 12; i++) + for(int8_t i = 2; i <= 12; ++i) talkState[i] = false; } else if(checkText(text, "no") && talkState[10]) { - msg << "Then not."; + msg << "Ok, then not."; talkState[1] = true; - for(int8_t i = 2; i <= 12; i++) + for(int8_t i = 2; i <= 12; ++i) talkState[i] = false; } else - msg << "Please read the latest message that I have specified, I don't understand the current requested action."; + msg << "Sorry, but I can't understand you, please try to repeat."; break; } @@ -4531,15 +5052,15 @@ void Player::manageAccount(const std::string &text) std::string tmp = text; trimString(tmp); if(tmp.length() < 6) - msg << "That password is too short, at least 6 digits are required. Please select a longer password."; + msg << "That password is too short, please select a longer one."; else if(!isValidPassword(tmp)) - msg << "Your password contains invalid characters... please tell me another one."; + msg << "Your password seems to contain invalid symbols, please choose another one."; else { talkState[3] = true; talkState[2] = false; managerString = tmp; - msg << managerString << " is it? 'yes' or 'no'?"; + msg << "{" << managerString << "} is it? {yes} or {no}?"; } } else if(checkText(text, "yes") && talkState[3]) @@ -4557,14 +5078,13 @@ void Player::manageAccount(const std::string &text) managerNumber = id; noSwap = talkState[1] = false; - msg << "Your account has been created, you may manage it now, but remember your account name: '" - << managerChar << "' and password: '" << managerString - << "'! If the account name is too hard to remember, please note it somewhere."; + msg << "Your account has been created, you may manage it now, but please remember your account name {" + << managerChar << "} and password {" << managerString << "}!"; } else - msg << "Your account could not be created, please try again."; + msg << "Your account could not be created, please contact with staff."; - for(int8_t i = 2; i <= 5; i++) + for(int8_t i = 2; i <= 5; ++i) talkState[i] = false; } else @@ -4585,17 +5105,17 @@ void Player::manageAccount(const std::string &text) std::string tmp = text; trimString(tmp); if(tmp.length() < 3) - msg << "That account name is too short, at least 3 digits are required. Please select a longer account name."; - else if(tmp.length() > 25) - msg << "That account name is too long, not more than 25 digits are required. Please select a shorter account name."; + msg << "That account name is too short, please select a longer one."; + else if(tmp.length() > 32) + msg << "That account name is too long, please select a shorter one."; else if(!isValidAccountName(tmp)) - msg << "Your account name contains invalid characters, please choose another one."; + msg << "Your account name seems to contain invalid symbols, please choose another one."; else if(asLowerCaseString(tmp) == asLowerCaseString(managerString)) msg << "Your account name cannot be same as password, please choose another one."; else { sprintf(managerChar, "%s", tmp.c_str()); - msg << managerChar << ", are you sure?"; + msg << "{" << managerChar << "}, is it? {yes} or {no}?"; talkState[4] = false; talkState[5] = true; } @@ -4611,18 +5131,18 @@ void Player::manageAccount(const std::string &text) managerNumber = id; noSwap = talkState[1] = false; - msg << "Your account has been created, you may manage it now, but remember your account name: '" - << managerChar << "' and password: '" << managerString << "'!"; + msg << "Your account has been created, you may manage it now, but please remember your account name {" + << managerChar << "} and password {" << managerString << "}!"; } else - msg << "Your account could not be created, please try again."; + msg << "Your account could not be created, please contact with staff."; - for(int8_t i = 2; i <= 5; i++) + for(int8_t i = 2; i <= 5; ++i) talkState[i] = false; } else { - msg << "An account with that name already exists, please try another account name."; + msg << "Account with that name already exists, please choose another one."; talkState[4] = true; talkState[5] = false; } @@ -4631,7 +5151,7 @@ void Player::manageAccount(const std::string &text) { talkState[5] = false; talkState[4] = true; - msg << "What else would you like as your account name?"; + msg << "What would you like your account name to be then?"; } else if(checkText(text, "recover") && !talkState[6]) { @@ -4650,7 +5170,7 @@ void Player::manageAccount(const std::string &text) } else { - msg << "Sorry, but account with such name doesn't exists."; + msg << "Sorry, but account with name {" << managerString << "} does not exists."; talkState[6] = talkState[7] = false; } } @@ -4661,15 +5181,15 @@ void Player::manageAccount(const std::string &text) { sprintf(managerChar, "%s%d", g_config.getString(ConfigManager::SERVER_NAME).c_str(), random_range(100, 999)); IOLoginData::getInstance()->setPassword(managerNumber, managerChar); - msg << "Correct! Your new password is: " << managerChar << "."; + msg << "Correct! Your new password is {" << managerChar << "}."; } else - msg << "Sorry, but this key doesn't match to account you gave me."; + msg << "Sorry, but this key does not match to specified account."; talkState[7] = talkState[8] = false; } else - msg << "Sorry, but I can't understand you, please try to repeat that."; + msg << "Sorry, but I can't understand you, please try to repeat."; break; } @@ -4678,14 +5198,14 @@ void Player::manageAccount(const std::string &text) break; } - sendTextMessage(MSG_STATUS_CONSOLE_BLUE, msg.str().c_str()); + sendCreatureSay(this, MSG_NPC_FROM, msg.str()); if(!noSwap) - sendTextMessage(MSG_STATUS_CONSOLE_ORANGE, "Hint: Type 'account' to manage your account and if you want to start over then type 'cancel'."); + sendCreatureSay(this, MSG_NPC_FROM, "Hint: Type {account} to manage your account and if you want to start over then type {cancel}."); } bool Player::isGuildInvited(uint32_t guildId) const { - for(InvitedToGuildsList::const_iterator it = invitedToGuildsList.begin(); it != invitedToGuildsList.end(); ++it) + for(InvitationsList::const_iterator it = invitationsList.begin(); it != invitationsList.end(); ++it) { if((*it) == guildId) return true; @@ -4696,10 +5216,13 @@ bool Player::isGuildInvited(uint32_t guildId) const void Player::leaveGuild() { + warMap.clear(); + g_game.updateCreatureEmblem(this); sendClosePrivate(CHANNEL_GUILD); + guildLevel = GUILDLEVEL_NONE; guildId = rankId = 0; - guildName = rankName = guildNick = ""; + guildName = rankName = guildNick = std::string(); } bool Player::isPremium() const @@ -4707,7 +5230,7 @@ bool Player::isPremium() const if(g_config.getBool(ConfigManager::FREE_PREMIUM) || hasFlag(PlayerFlag_IsAlwaysPremium)) return true; - return premiumDays; + return (premiumDays != 0); } bool Player::setGuildLevel(GuildLevel_t newLevel, uint32_t rank/* = 0*/) @@ -4733,11 +5256,11 @@ void Player::setGroupId(int32_t newId) void Player::setGroup(Group* newGroup) { - if(newGroup) - { - group = newGroup; - groupId = group->getId(); - } + if(!newGroup) + return; + + group = newGroup; + groupId = group->getId(); } PartyShields_t Player::getPartyShield(const Creature* creature) const @@ -4746,38 +5269,34 @@ PartyShields_t Player::getPartyShield(const Creature* creature) const if(!player) return Creature::getPartyShield(creature); - if(Party* party = getParty()) + if(party) { if(party->getLeader() == player) { - if(party->isSharedExperienceActive()) - { - if(party->isSharedExperienceEnabled()) - return SHIELD_YELLOW_SHAREDEXP; + if(!party->isSharedExperienceActive()) + return SHIELD_YELLOW; - if(party->canUseSharedExperience(player)) - return SHIELD_YELLOW_NOSHAREDEXP; + if(party->isSharedExperienceEnabled()) + return SHIELD_YELLOW_SHAREDEXP; - return SHIELD_YELLOW_NOSHAREDEXP_BLINK; - } + if(party->canUseSharedExperience(player)) + return SHIELD_YELLOW_NOSHAREDEXP; - return SHIELD_YELLOW; + return SHIELD_YELLOW_NOSHAREDEXP_BLINK; } if(party->isPlayerMember(player)) { - if(party->isSharedExperienceActive()) - { - if(party->isSharedExperienceEnabled()) - return SHIELD_BLUE_SHAREDEXP; + if(!party->isSharedExperienceActive()) + return SHIELD_BLUE; - if(party->canUseSharedExperience(player)) - return SHIELD_BLUE_NOSHAREDEXP; + if(party->isSharedExperienceEnabled()) + return SHIELD_BLUE_SHAREDEXP; - return SHIELD_BLUE_NOSHAREDEXP_BLINK; - } + if(party->canUseSharedExperience(player)) + return SHIELD_BLUE_NOSHAREDEXP; - return SHIELD_BLUE; + return SHIELD_BLUE_NOSHAREDEXP_BLINK; } if(isInviting(player)) @@ -4792,21 +5311,26 @@ PartyShields_t Player::getPartyShield(const Creature* creature) const bool Player::isInviting(const Player* player) const { - if(!player || !getParty() || getParty()->getLeader() != this) + if(!player || player->isRemoved() || !party || party->getLeader() != this) return false; - return getParty()->isPlayerInvited(player); + return party->isPlayerInvited(player); } bool Player::isPartner(const Player* player) const { - if(!player || !getParty() || !player->getParty()) - return false; + return player && player->getParty() && player->getParty() == party; +} - return (getParty() == player->getParty()); +bool Player::getHideHealth() const +{ + if(hasFlag(PlayerFlag_HideHealth)) + return true; + + return hideHealth; } -void Player::sendPlayerPartyIcons(Player* player) +void Player::sendPlayerIcons(Player* player) { sendCreatureShield(player); sendCreatureSkull(player); @@ -4865,56 +5389,55 @@ void Player::increaseCombatValues(int32_t& min, int32_t& max, bool useCharges, b else max = (int32_t)(max * vocation->getMultiplier(MULTIPLIER_MAGIC)); - Item* weapon = NULL; - if(!countWeapon) - weapon = getWeapon(); - Item* item = NULL; - int32_t minValue = 0, maxValue = 0; - for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) + int32_t minValue = 0, maxValue = 0, i = SLOT_FIRST; + for(; i < SLOT_LAST; ++i) { - if(!(item = getInventoryItem((slots_t)i)) || (g_moveEvents->hasEquipEvent(item) - && !isItemAbilityEnabled((slots_t)i))) + if(!(item = getInventoryItem((slots_t)i)) || item->isRemoved() || + (g_moveEvents->hasEquipEvent(item) && !isItemAbilityEnabled((slots_t)i))) continue; const ItemType& it = Item::items[item->getID()]; + if(!it.hasAbilities()) + continue; + if(min > 0) { - minValue += it.abilities.increment[HEALING_VALUE]; - if(it.abilities.increment[HEALING_PERCENT]) - min = (int32_t)std::ceil((double)(min * it.abilities.increment[HEALING_PERCENT]) / 100.); + minValue += it.abilities->increment[HEALING_VALUE]; + if(it.abilities->increment[HEALING_PERCENT]) + min = (int32_t)std::ceil((double)(min * it.abilities->increment[HEALING_PERCENT]) / 100.); } else { - minValue -= it.abilities.increment[MAGIC_VALUE]; - if(it.abilities.increment[MAGIC_PERCENT]) - min = (int32_t)std::ceil((double)(min * it.abilities.increment[MAGIC_PERCENT]) / 100.); + minValue -= it.abilities->increment[MAGIC_VALUE]; + if(it.abilities->increment[MAGIC_PERCENT]) + min = (int32_t)std::ceil((double)(min * it.abilities->increment[MAGIC_PERCENT]) / 100.); } if(max > 0) { - maxValue += it.abilities.increment[HEALING_VALUE]; - if(it.abilities.increment[HEALING_PERCENT]) - max = (int32_t)std::ceil((double)(max * it.abilities.increment[HEALING_PERCENT]) / 100.); + maxValue += it.abilities->increment[HEALING_VALUE]; + if(it.abilities->increment[HEALING_PERCENT]) + max = (int32_t)std::ceil((double)(max * it.abilities->increment[HEALING_PERCENT]) / 100.); } else { - maxValue -= it.abilities.increment[MAGIC_VALUE]; - if(it.abilities.increment[MAGIC_PERCENT]) - max = (int32_t)std::ceil((double)(max * it.abilities.increment[MAGIC_PERCENT]) / 100.); + maxValue -= it.abilities->increment[MAGIC_VALUE]; + if(it.abilities->increment[MAGIC_PERCENT]) + max = (int32_t)std::ceil((double)(max * it.abilities->increment[MAGIC_PERCENT]) / 100.); } bool removeCharges = false; for(int32_t j = INCREMENT_FIRST; j <= INCREMENT_LAST; ++j) { - if(!it.abilities.increment[(Increment_t)j]) + if(!it.abilities->increment[(Increment_t)j]) continue; removeCharges = true; break; } - if(useCharges && removeCharges && item != weapon && item->hasCharges()) + if(useCharges && removeCharges && (countWeapon || item != weapon) && item->hasCharges()) g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1)); } @@ -4945,5 +5468,101 @@ bool Player::transferMoneyTo(const std::string& name, uint64_t amount) void Player::sendCritical() const { if(g_config.getBool(ConfigManager::DISPLAY_CRITICAL_HIT)) - g_game.addAnimatedText(getPosition(), TEXTCOLOR_DARKRED, "CRITICAL!"); + sendTextMessage(MSG_STATUS_CONSOLE_RED, "You strike a critical hit!"); +} + +void Player::setMounted(bool mounting) +{ + if(!mounting) + { + dismount(true); + return; + } + + if(_tile->hasFlag(TILESTATE_PROTECTIONZONE) && g_config.getBool(ConfigManager::UNMOUNT_PLAYER_IN_PZ) && !hasFlag(PlayerFlag_IgnoreProtectionZone)) + sendCancelMessage(RET_ACTIONNOTPERMITTEDINPROTECTIONZONE); + else if(Mounts::getInstance()->isPremium() && !isPremium()) + sendCancelMessage(RET_YOUNEEDPREMIUMACCOUNT); + else if(!defaultOutfit.lookMount) + sendOutfitWindow(); + else if(!mounted) + { + if(Mount* mount = Mounts::getInstance()->getMountByCid(defaultOutfit.lookMount)) + { + mounted = true; + if(mount->getSpeed()) + g_game.changeSpeed(this, mount->getSpeed()); + + // TODO: mount attributes + g_game.internalCreatureChangeOutfit(this, defaultOutfit, true); + } + } +} + +void Player::dismount(bool update) +{ + if(!mounted) + return; + + mounted = false; + if(!defaultOutfit.lookMount) + return; + + if(Mount* mount = Mounts::getInstance()->getMountByCid(defaultOutfit.lookMount)) + { + if(mount->getSpeed() > 0) + g_game.changeSpeed(this, -(int32_t)mount->getSpeed()); + + // TODO: mount attributes + } + + if(update) + g_game.internalCreatureChangeOutfit(this, defaultOutfit, true); +} + +bool Player::tameMount(uint8_t mountId) +{ + if(!Mounts::getInstance()->getMountById(mountId)) + return false; + + --mountId; + int32_t key = PSTRG_MOUNTS_RANGE_START + (mountId / 31), value = 0; + + std::string tmp; + if(getStorage(asString(key), tmp)) + { + value = atoi(tmp.c_str()); + value |= (int32_t)pow(2., mountId % 31); + } + else + value = (int32_t)pow(2., mountId % 31); + + setStorage(asString(key), asString(value)); + return true; +} + +bool Player::untameMount(uint8_t mountId) +{ + if(!Mounts::getInstance()->getMountById(mountId)) + return false; + + --mountId; + int32_t key = PSTRG_MOUNTS_RANGE_START + (mountId / 31), value = 0; + + std::string tmp; + if(!getStorage(asString(key), tmp)) + return true; + + value = atoi(tmp.c_str()); + value ^= (int32_t)std::pow(2., mountId % 31); + + Mount* mount = Mounts::getInstance()->getMountByCid(defaultOutfit.lookMount); + if(mount && mount->getId() == (mountId + 1)) + { + dismount(true); + defaultOutfit.lookMount = 0; + } + + setStorage(asString(key), asString(value)); + return true; } diff --git a/player.h b/player.h index b7d66f6..6cb507c 100644 --- a/player.h +++ b/player.h @@ -17,7 +17,6 @@ #ifndef __PLAYER__ #define __PLAYER__ - #include "otsystem.h" #include "enums.h" @@ -37,9 +36,7 @@ #include "npc.h" class House; -class NetworkMessage; class Weapon; -class ProtocolGame; class Npc; class Party; class SchedulerTask; @@ -115,19 +112,15 @@ enum GamemasterCondition_t GAMEMASTER_TELEPORT = 2 }; -enum Exhaust_t -{ - EXHAUST_COMBAT = 1, - EXHAUST_HEALING = 2 -}; - -typedef std::set VIPListSet; +typedef std::set VIPSet; +typedef std::list > ChannelsList; typedef std::vector > ContainerVector; typedef std::map > DepotMap; typedef std::map MuteCountMap; typedef std::list LearnedInstantSpellList; -typedef std::list InvitedToGuildsList; +typedef std::list InvitationsList; typedef std::list PartyList; +typedef std::map WarMap; #define SPEED_MAX 1500 #define SPEED_MIN 10 @@ -145,6 +138,7 @@ class Player : public Creature, public Cylinder virtual Player* getPlayer() {return this;} virtual const Player* getPlayer() const {return this;} + virtual CreatureType_t getType() const {return CREATURETYPE_PLAYER;} static MuteCountMap muteCountMap; @@ -157,21 +151,30 @@ class Player : public Creature, public Cylinder void manageAccount(const std::string& text); bool isAccountManager() const {return (accountManager != MANAGER_NONE);} - void kickPlayer(bool displayEffect, bool forceLogout); + void kick(bool displayEffect, bool forceLogout); void setGUID(uint32_t _guid) {guid = _guid;} uint32_t getGUID() const {return guid;} static AutoList autoList; - virtual uint32_t rangeId() {return 0x10000000;} + virtual uint32_t rangeId() {return PLAYER_ID_RANGE;} + static bool sort(Player* lhs, Player* rhs) {return lhs->getName() < rhs->getName();} void addList(); void removeList(); static uint64_t getExpForLevel(uint32_t lv) { + static std::map cache; lv--; - return ((50ULL * lv * lv * lv) - (150ULL * lv * lv) + (400ULL * lv)) / 3ULL; + + std::map::iterator it = cache.find(lv); + if(it != cache.end()) + return it->second; + + uint64_t exp = ((50ULL * lv * lv * lv) - (150ULL * lv * lv) + (400ULL * lv)) / 3ULL; + cache[lv] = exp; + return exp; } uint32_t getPromotionLevel() const {return promotionLevel;} @@ -185,10 +188,12 @@ class Player : public Creature, public Cylinder void setParty(Party* _party) {party = _party;} Party* getParty() const {return party;} - PartyShields_t getPartyShield(const Creature* creature) const; + bool isInviting(const Player* player) const; bool isPartner(const Player* player) const; - void sendPlayerPartyIcons(Player* player); + + bool getHideHealth() const; + bool addPartyInvitation(Party* party); bool removePartyInvitation(Party* party); void clearPartyInvitations(); @@ -218,17 +223,18 @@ class Player : public Creature, public Cylinder bool hasCustomFlag(PlayerCustomFlags value) const {return group != NULL && group->hasCustomFlag(value);} void addBlessing(int16_t blessing) {blessings += blessing;} - bool hasBlessing(int16_t value) const {return (blessings & ((int16_t)1 << value));} + bool hasBlessing(int16_t blessing) const {return ((blessings & ((int16_t)1 << blessing)) != 0);} + void setPVPBlessing(bool value) {pvpBlessing = value;} + bool hasPVPBlessing() const {return pvpBlessing;} uint16_t getBlessings() const; OperatingSystem_t getOperatingSystem() const {return operatingSystem;} - void setOperatingSystem(OperatingSystem_t clientOs) {operatingSystem = clientOs;} + void setOperatingSystem(OperatingSystem_t os) {operatingSystem = os;} uint32_t getClientVersion() const {return clientVersion;} void setClientVersion(uint32_t version) {clientVersion = version;} - bool hasClient() const {return client;} + bool hasClient() const {return (client != NULL);} bool isVirtual() const {return (getID() == 0);} - void disconnect() {if(client) client->disconnect();} uint32_t getIP() const; bool canOpenCorpse(uint32_t ownerId); @@ -238,8 +244,8 @@ class Player : public Creature, public Cylinder void addContainer(uint32_t cid, Container* container); void closeContainer(uint32_t cid); - virtual bool setStorage(const uint32_t key, const std::string& value); - virtual void eraseStorage(const uint32_t key); + virtual bool setStorage(const std::string& key, const std::string& value); + virtual void eraseStorage(const std::string& key); void generateReservedStorage(); bool transferMoneyTo(const std::string& name, uint64_t amount); @@ -250,7 +256,11 @@ class Player : public Creature, public Cylinder void setGroup(Group* newGroup); Group* getGroup() const {return group;} + void setMarketDepotId(int16_t newId) { marketDepotId = newId; } + int16_t getMarketDepotId() const { return marketDepotId; } + virtual bool isGhost() const {return hasCondition(CONDITION_GAMEMASTER, GAMEMASTER_INVISIBLE) || hasFlag(PlayerFlag_CannotBeSeen);} + virtual bool isWalkable() const {return hasCustomFlag(PlayerCustomFlag_IsWalkable);} void switchSaving() {saving = !saving;} bool isSaving() const {return saving;} @@ -259,26 +269,40 @@ class Player : public Creature, public Cylinder void setIdleTime(uint32_t amount) {idleTime = amount;} bool checkLoginDelay(uint32_t playerId) const; - bool isTrading() const {return tradePartner;} + bool isTrading() const {return (tradePartner != NULL);} uint32_t getAccount() const {return accountId;} std::string getAccountName() const {return account;} uint16_t getAccess() const {return group ? group->getAccess() : 0;} uint16_t getGhostAccess() const {return group ? group->getGhostAccess() : 0;} - bool isPremium() const; - int32_t getPremiumDays() const {return premiumDays;} - uint32_t getLevel() const {return level;} uint64_t getExperience() const {return experience;} uint32_t getMagicLevel() const {return getPlayerInfo(PLAYERINFO_MAGICLEVEL);} + uint32_t getBaseMagicLevel() const {return magLevel;} uint64_t getSpentMana() const {return manaSpent;} - uint32_t getVocationId() const {return vocation_id;} - void setVocation(uint32_t vocId); + bool isPremium() const; + int32_t getPremiumDays() const {return premiumDays;} + + bool hasEnemy() const {return !warMap.empty();} + bool getEnemy(const Player* player, War_t& data) const; + + bool isEnemy(const Player* player, bool allies) const; + bool isAlly(const Player* player) const; + + void addEnemy(uint32_t guild, War_t war) + {warMap[guild] = war;} + void removeEnemy(uint32_t guild) {warMap.erase(guild);} + + uint32_t getVocationId() const {return vocationId;} + void setVocation(uint32_t id); uint16_t getSex(bool full) const {return full ? sex : sex % 2;} void setSex(uint16_t); + virtual void setDropLoot(lootDrop_t _lootDrop); + virtual void setLossSkill(bool _skillLoss); + uint64_t getStamina() const {return hasFlag(PlayerFlag_HasInfiniteStamina) ? STAMINA_MAX : stamina;} void setStamina(uint64_t value) {stamina = std::min((uint64_t)STAMINA_MAX, (uint64_t)std::max((uint64_t)0, value));} uint32_t getStaminaMinutes() const {return (uint32_t)(getStamina() / (uint64_t)STAMINA_MULTIPLIER);} @@ -297,23 +321,15 @@ class Player : public Creature, public Cylinder virtual bool isPushable() const; virtual int32_t getThrowRange() const {return 1;} + virtual double getGainedExperience(Creature* attacker) const; - bool isMuted(uint16_t channelId, SpeakClasses type, uint32_t& time); + bool isMuted(uint16_t channelId, MessageClasses type, int32_t& time); void addMessageBuffer(); void removeMessageBuffer(); double getCapacity() const {return capacity;} void setCapacity(double newCapacity) {capacity = newCapacity;} - - double getFreeCapacity() const - { - if(hasFlag(PlayerFlag_CannotPickupItem)) - return 0.00; - else if(hasFlag(PlayerFlag_HasInfiniteCapacity)) - return 10000.00; - - return std::max(0.00, capacity - inventoryWeight); - } + double getFreeCapacity() const; virtual int32_t getSoul() const {return getPlayerInfo(PLAYERINFO_SOUL);} virtual int32_t getMaxHealth() const {return getPlayerInfo(PLAYERINFO_MAXHEALTH);} @@ -326,6 +342,7 @@ class Player : public Creature, public Cylinder bool isItemAbilityEnabled(slots_t slot) const {return inventoryAbilities[slot];} void setItemAbility(slots_t slot, bool enabled) {inventoryAbilities[slot] = enabled;} + int32_t getBaseSkill(skills_t skill) const {return skills[skill][SKILL_LEVEL];} int32_t getVarSkill(skills_t skill) const {return varSkills[skill];} void setVarSkill(skills_t skill, int32_t modifier) {varSkills[skill] += modifier;} @@ -340,14 +357,19 @@ class Player : public Creature, public Cylinder Depot* getDepot(uint32_t depotId, bool autoCreateDepot); bool addDepot(Depot* depot, uint32_t depotId); + void updateDepots(); void useDepot(uint32_t depotId, bool value); virtual bool canSee(const Position& pos) const; virtual bool canSeeCreature(const Creature* creature) const; virtual bool canWalkthrough(const Creature* creature) const; + void setWalkthrough(const Creature* creature, bool walkthrough); virtual bool canSeeInvisibility() const {return hasFlag(PlayerFlag_CanSenseInvisibility);} + bool hasSentChat() const {return sentChat;} + void setSentChat(bool sending) {sentChat = sending;} + virtual RaceType_t getRace() const {return RACE_BLOOD;} //safe-trade functions @@ -364,6 +386,7 @@ class Player : public Creature, public Cylinder shopOffer = offer; } + Npc* getShopOwner() const {return shopOwner;} Npc* getShopOwner(int32_t& onBuy, int32_t& onSell) { onBuy = purchaseCallback; @@ -377,15 +400,19 @@ class Player : public Creature, public Cylinder onSell = saleCallback; return shopOwner; } + + //Quest functions + void onUpdateQuest(); //V.I.P. functions void notifyLogIn(Player* loginPlayer); void notifyLogOut(Player* logoutPlayer); bool removeVIP(uint32_t guid); - bool addVIP(uint32_t guid, std::string& name, bool isOnline, bool internal = false); + bool addVIP(uint32_t guid, const std::string& name, bool online, bool loading = false); //follow functions virtual bool setFollowCreature(Creature* creature, bool fullPathSearch = false); + virtual void goToFollowCreature(); //follow events virtual void onFollowCreature(const Creature* creature); @@ -395,21 +422,24 @@ class Player : public Creature, public Cylinder virtual void onWalkAborted(); virtual void onWalkComplete(); - void stopWalk(); - void openShopWindow(); - void closeShopWindow(Npc* npc = NULL, int32_t onBuy = -1, int32_t onSell = -1); + void openShopWindow(Npc* npc); + void closeShopWindow(bool send = true); bool canShopItem(uint16_t itemId, uint8_t subType, ShopEvent_t event); + chaseMode_t getChaseMode() const {return chaseMode;} void setChaseMode(chaseMode_t mode); + fightMode_t getFightMode() const {return fightMode;} void setFightMode(fightMode_t mode) {fightMode = mode;} - void setSecureMode(secureMode_t mode) {secureMode = mode;} secureMode_t getSecureMode() const {return secureMode;} + void setSecureMode(secureMode_t mode) {secureMode = mode;} //combat functions virtual bool setAttackedCreature(Creature* creature); + bool hasShield() const; + bool isImmune(CombatType_t type) const; bool isImmune(ConditionType_t type) const; - bool hasShield() const; + bool isProtected() const; virtual bool isAttackable() const; virtual void changeHealth(int32_t healthChange); @@ -418,20 +448,24 @@ class Player : public Creature, public Cylinder bool isPzLocked() const {return pzLocked;} void setPzLocked(bool v) {pzLocked = v;} + virtual BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, - bool checkDefense = false, bool checkArmor = false); + bool checkDefense = false, bool checkArmor = false, bool reflect = true, bool field = false, bool element = false); + virtual void doAttacking(uint32_t interval); virtual bool hasExtraSwing() {return lastAttack > 0 && ((OTSYS_TIME() - lastAttack) >= getAttackSpeed());} - int32_t getShootRange() const {return shootRange;} + void setLastAttack(uint64_t time) {lastAttack = time;} int32_t getSkill(skills_t skilltype, skillsid_t skillinfo) const; bool getAddAttackSkill() const {return addAttackSkillPoint;} BlockType_t getLastAttackBlockType() const {return lastAttackBlockType;} - Item* getWeapon(bool ignoreAmmo = false); + Item* getWeapon(bool ignoreAmmo); + ItemVector getWeapons() const; + virtual WeaponType_t getWeaponType(); int32_t getWeaponSkill(const Item* item) const; - void getShieldAndWeapon(const Item* &shield, const Item* &weapon) const; + void getShieldAndWeapon(const Item* &_shield, const Item* &_weapon) const; virtual void drainHealth(Creature* attacker, CombatType_t combatType, int32_t damage); virtual void drainMana(Creature* attacker, CombatType_t combatType, int32_t damage); @@ -439,19 +473,24 @@ class Player : public Creature, public Cylinder void addExperience(uint64_t exp); void removeExperience(uint64_t exp, bool updateStats = true); void addManaSpent(uint64_t amount, bool useMultiplier = true); - void addSkillAdvance(skills_t skill, uint32_t count, bool useMultiplier = true); - bool addUnjustifiedKill(const Player* attacked); + void addSkillAdvance(skills_t skill, uint64_t count, bool useMultiplier = true); + bool addUnjustifiedKill(const Player* attacked, bool countNow); virtual int32_t getArmor() const; virtual int32_t getDefense() const; virtual float getAttackFactor() const; virtual float getDefenseFactor() const; - void addExhaust(uint32_t ticks, Exhaust_t type); - void addInFightTicks(bool pzLock = false); - void addDefaultRegeneration(uint32_t addTicks); + void addRevenge(uint32_t playerGUID) {revengeList.push_back(playerGUID);} + bool canRevenge(uint32_t playerGUID) const + { + return std::find(revengeList.begin(), revengeList.end(), playerGUID) != revengeList.end(); + } - virtual double getGainedExperience(Creature* attacker) const; + void addCooldown(uint32_t ticks, uint16_t spellId); + void addExhaust(uint32_t ticks, Exhaust_t exhaust); + void addInFightTicks(bool pzLock, int32_t ticks = 0); + void addDefaultRegeneration(uint32_t addTicks); //combat event functions virtual void onAddCondition(ConditionType_t type, bool hadCondition); @@ -459,25 +498,28 @@ class Player : public Creature, public Cylinder virtual void onEndCondition(ConditionType_t type); virtual void onCombatRemoveCondition(const Creature* attacker, Condition* condition); virtual void onTickCondition(ConditionType_t type, int32_t interval, bool& _remove); - virtual void onAttackedCreature(Creature* target); - virtual void onSummonAttackedCreature(Creature* summon, Creature* target); + virtual void onTarget(Creature* target); + virtual void onSummonTarget(Creature* summon, Creature* target); virtual void onAttacked(); - virtual void onAttackedCreatureDrain(Creature* target, int32_t points); - virtual void onSummonAttackedCreatureDrain(Creature* summon, Creature* target, int32_t points); - virtual void onTargetCreatureGainHealth(Creature* target, int32_t points); - virtual bool onKilledCreature(Creature* target, uint32_t& flags); - virtual void onGainExperience(double& gainExp, bool fromMonster, bool multiplied); - virtual void onGainSharedExperience(double& gainExp, bool fromMonster, bool multiplied); - virtual void onAttackedCreatureBlockHit(Creature* target, BlockType_t blockType); + virtual void onTargetDrain(Creature* target, int32_t points); + virtual void onSummonTargetDrain(Creature* summon, Creature* target, int32_t points); + virtual void onTargetGain(Creature* target, int32_t points); + virtual bool onKilledCreature(Creature* target, DeathEntry& entry); + virtual void onGainExperience(double& gainExp, Creature* target, bool multiplied); + virtual void onGainSharedExperience(double& gainExp, Creature* taraget, bool multiplied); + virtual void onTargetBlockHit(Creature* target, BlockType_t blockType); virtual void onBlockHit(BlockType_t blockType); virtual void onChangeZone(ZoneType_t zone); - virtual void onAttackedCreatureChangeZone(ZoneType_t zone); + virtual void onTargetChangeZone(ZoneType_t zone); virtual void onIdleStatus(); virtual void onPlacedCreature(); virtual void getCreatureLight(LightInfo& light) const; Skulls_t getSkull() const; - Skulls_t getSkullClient(const Creature* creature) const; + + Skulls_t getSkullType(const Creature* creature) const; + PartyShields_t getPartyShield(const Creature* creature) const; + GuildEmblems_t getGuildEmblem(const Creature* creature) const; bool hasAttacked(const Player* attacked) const; void addAttacked(const Player* attacked); @@ -490,7 +532,6 @@ class Player : public Creature, public Cylinder bool removeOutfit(uint32_t outfitId, uint32_t addons); bool canWearOutfit(uint32_t outfitId, uint32_t addons); - bool canLogout(bool checkInfight); //tile //send methods @@ -498,28 +539,30 @@ class Player : public Creature, public Cylinder {if(client) client->sendAddTileItem(tile, pos, tile->getClientIndexOfThing(this, item), item);} void sendUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem, const Item* newItem) {if(client) client->sendUpdateTileItem(tile, pos, tile->getClientIndexOfThing(this, oldItem), newItem);} - void sendRemoveTileItem(const Tile* tile, const Position& pos, uint32_t stackpos, const Item* item) + void sendRemoveTileItem(const Tile* tile, const Position& pos, uint32_t stackpos, const Item*) {if(client) client->sendRemoveTileItem(tile, pos, stackpos);} void sendUpdateTile(const Tile* tile, const Position& pos) {if(client) client->sendUpdateTile(tile, pos);} - void sendChannelMessage(std::string author, std::string text, SpeakClasses type, uint8_t channel) + void sendChannelMessage(std::string author, std::string text, MessageClasses type, uint16_t channel) {if(client) client->sendChannelMessage(author, text, type, channel);} + void sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent) + {if(client) client->sendChannelEvent(channelId, playerName, channelEvent);} void sendCreatureAppear(const Creature* creature) - {if(client) client->sendAddCreature(creature, creature->getPosition(), creature->getTile()->getClientIndexOfThing( - this, creature));} + {if(client) client->sendAddCreature(creature, creature->getPosition(), creature->getTile()->getClientIndexOfThing(this, creature));} void sendCreatureDisappear(const Creature* creature, uint32_t stackpos) {if(client) client->sendRemoveCreature(creature, creature->getPosition(), stackpos);} void sendCreatureMove(const Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, uint32_t oldStackpos, bool teleport) - {if(client) client->sendMoveCreature(creature, newTile, newPos, newTile->getClientIndexOfThing( - this, creature), oldTile, oldPos, oldStackpos, teleport);} + {if(client) client->sendMoveCreature(creature, newTile, newPos, newTile->getClientIndexOfThing(this, creature), oldTile, oldPos, oldStackpos, teleport);} void sendCreatureTurn(const Creature* creature) {if(client) client->sendCreatureTurn(creature, creature->getTile()->getClientIndexOfThing(this, creature));} - void sendCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, Position* pos = NULL) - {if(client) client->sendCreatureSay(creature, type, text, pos);} - void sendCreatureSquare(const Creature* creature, SquareColor_t color) + void sendCreatureSay(const Creature* creature, MessageClasses type, const std::string& text, Position* pos = NULL, uint32_t statementId = 0) + {if(client) client->sendCreatureSay(creature, type, text, pos, statementId);} + void sendCreatureChannelSay(Creature* creature, MessageClasses type, const std::string& text, uint16_t channelId, uint32_t statementId = 0) const + {if(client) client->sendCreatureChannelSay(creature, type, text, channelId, statementId);} + void sendCreatureSquare(const Creature* creature, uint8_t color) {if(client) client->sendCreatureSquare(creature, color);} void sendCreatureChangeOutfit(const Creature* creature, const Outfit_t& outfit) {if(client) client->sendCreatureOutfit(creature, outfit);} @@ -528,6 +571,14 @@ class Player : public Creature, public Cylinder {if(client) client->sendCreatureLight(creature);} void sendCreatureShield(const Creature* creature) {if(client) client->sendCreatureShield(creature);} + void sendCreatureEmblem(const Creature* creature) + {if(client) client->sendCreatureEmblem(creature);} + void sendCreatureWalkthrough(const Creature* creature, bool walkthrough) + {if(client) client->sendCreatureWalkthrough(creature, walkthrough);} + void sendSpellCooldown(Spells_t icon, uint32_t cooldown) + {if(client) client->sendSpellCooldown(icon, cooldown);} + void sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t cooldown) + {if(client) client->sendSpellGroupCooldown(groupId, cooldown);} //container void sendAddContainerItem(const Container* container, const Item* item); @@ -539,11 +590,19 @@ class Player : public Creature, public Cylinder //inventory void sendAddInventoryItem(slots_t slot, const Item* item) {if(client) client->sendAddInventoryItem(slot, item);} - void sendUpdateInventoryItem(slots_t slot, const Item* oldItem, const Item* newItem) + void sendUpdateInventoryItem(slots_t slot, const Item*, const Item* newItem) {if(client) client->sendUpdateInventoryItem(slot, newItem);} - void sendRemoveInventoryItem(slots_t slot, const Item* item) + void sendRemoveInventoryItem(slots_t slot, const Item*) {if(client) client->sendRemoveInventoryItem(slot);} + //mount + bool isMounted() const {return mounted;} + void setMounted(bool mounting); + void dismount(bool update); + + bool tameMount(uint8_t mountId); + bool untameMount(uint8_t mountId); + //event methods virtual void onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem, const ItemType& oldType, const Item* newItem, const ItemType& newType); @@ -555,7 +614,7 @@ class Player : public Creature, public Cylinder virtual void onCreatureMove(const Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport); - virtual void onAttackedCreatureDisappear(bool isLogout); + virtual void onTargetDisappear(bool isLogout); virtual void onFollowCreatureDisappear(bool isLogout); //cylinder implementations @@ -581,13 +640,11 @@ class Player : public Creature, public Cylinder void autoCloseContainers(const Container* container); //inventory - void onAddInventoryItem(slots_t slot, Item* item) {} + void onAddInventoryItem(slots_t, Item*) {} void onUpdateInventoryItem(slots_t slot, Item* oldItem, const ItemType& oldType, Item* newItem, const ItemType& newType); void onRemoveInventoryItem(slots_t slot, Item* item); - void sendAnimatedText(const Position& pos, uint8_t color, std::string text) const - {if(client) client->sendAnimatedText(pos,color,text);} void sendCancel(const std::string& msg) const {if(client) client->sendCancel(msg);} void sendCancelMessage(ReturnValue message) const; @@ -616,33 +673,48 @@ class Player : public Creature, public Cylinder void sendIcons() const; void sendMagicEffect(const Position& pos, uint8_t type) const {if(client) client->sendMagicEffect(pos, type);} - void sendStats(); + void sendStats() const + {if(client) client->sendStats();} void sendSkills() const {if(client) client->sendSkills();} void sendTextMessage(MessageClasses type, const std::string& message) const {if(client) client->sendTextMessage(type, message);} - void sendReLoginWindow() const - {if(client) client->sendReLoginWindow();} + void sendStatsMessage(MessageClasses type, const std::string& message, Position pos, MessageDetails* details = NULL) const + {if(client) client->sendStatsMessage(type, message, pos, details);} + void sendReLoginWindow(uint8_t pvpPercent) const + {if(client) client->sendReLoginWindow(pvpPercent);} void sendTextWindow(Item* item, uint16_t maxLen, bool canWrite) const {if(client) client->sendTextWindow(windowTextId, item, maxLen, canWrite);} - void sendTextWindow(uint32_t itemId, const std::string& text) const - {if(client) client->sendTextWindow(windowTextId, itemId, text);} - void sendToChannel(Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId, uint32_t time = 0) const - {if(client) client->sendToChannel(creature, type, text, channelId, time);} - void sendShop() const - {if(client) client->sendShop(shopOffer);} + void sendShop(Npc* npc) const + {if(client) client->sendShop(npc, shopOffer);} void sendGoods() const {if(client) client->sendGoods(shopOffer);} void sendCloseShop() const {if(client) client->sendCloseShop();} + void sendMarketEnter(uint32_t depotId) const + {if(client) client->sendMarketEnter(depotId);} + void sendMarketLeave() + {marketDepotId = -1; if(client) client->sendMarketLeave();} + void sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers) const + {if(client) client->sendMarketBrowseItem(itemId, buyOffers, sellOffers);} + void sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers) const + {if(client) client->sendMarketBrowseOwnOffers(buyOffers, sellOffers);} + void sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers) const + {if(client) client->sendMarketBrowseOwnHistory(buyOffers, sellOffers);} + void sendMarketDetail(uint16_t itemId) const + {if(client) client->sendMarketDetail(itemId);} + void sendMarketAcceptOffer(MarketOfferEx offer) const + {if(client) client->sendMarketAcceptOffer(offer);} + void sendMarketCancelOffer(MarketOfferEx offer) const + {if(client) client->sendMarketCancelOffer(offer);} void sendTradeItemRequest(const Player* player, const Item* item, bool ack) const {if(client) client->sendTradeItemRequest(player, item, ack);} void sendTradeClose() const {if(client) client->sendCloseTrade();} void sendWorldLight(LightInfo& lightInfo) {if(client) client->sendWorldLight(lightInfo);} - void sendChannelsDialog() - {if(client) client->sendChannelsDialog();} + void sendChannelsDialog(const ChannelsList& channels) + {if(client) client->sendChannelsDialog(channels);} void sendOpenPrivateChannel(const std::string& receiver) {if(client) client->sendOpenPrivateChannel(receiver);} void sendOutfitWindow() @@ -651,35 +723,35 @@ class Player : public Creature, public Cylinder {if(client) client->sendCloseContainer(cid);} void sendChannel(uint16_t channelId, const std::string& channelName) {if(client) client->sendChannel(channelId, channelName);} - void sendRuleViolationsChannel(uint16_t channelId) - {if(client) client->sendRuleViolationsChannel(channelId);} - void sendRemoveReport(const std::string& name) - {if(client) client->sendRemoveReport(name);} - void sendLockRuleViolation() - {if(client) client->sendLockRuleViolation();} - void sendRuleViolationCancel(const std::string& name) - {if(client) client->sendRuleViolationCancel(name);} void sendTutorial(uint8_t tutorialId) {if(client) client->sendTutorial(tutorialId);} void sendAddMarker(const Position& pos, MapMarks_t markType, const std::string& desc) {if (client) client->sendAddMarker(pos, markType, desc);} void sendCritical() const; + void sendPlayerIcons(Player* player); void receivePing() {lastPong = OTSYS_TIME();} virtual void onThink(uint32_t interval); - uint32_t getAttackSpeed(); + uint32_t getAttackSpeed() const; + + void setLastMail(uint64_t v) {lastMail = v;} + uint16_t getMailAttempts() const {return mailAttempts;} + void addMailAttempt() {++mailAttempts;} virtual void postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link = LINK_OWNER); + int32_t index, CylinderLink_t link = LINK_OWNER); virtual void postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link = LINK_OWNER); + int32_t index, bool isCompleteRemoval, CylinderLink_t link = LINK_OWNER); - void setNextAction(int64_t time) {if(time > nextAction) {nextAction = time;}} + void setNextAction(int64_t time) {if(time > nextAction) nextAction = time;} bool canDoAction() const {return nextAction <= OTSYS_TIME();} - uint32_t getNextActionTime() const; + uint32_t getNextActionTime(bool scheduler = true) const; + + void setNextExAction(int64_t time) {if(time > nextExAction) nextExAction = time;} + bool canDoExAction() const {return nextExAction <= OTSYS_TIME();} Item* getWriteItem(uint32_t& _windowTextId, uint16_t& _maxWriteLen); - void setWriteItem(Item* item, uint16_t _maxWriteLen = 0); + void setWriteItem(Item* item, uint16_t _maxLen = 0); House* getEditHouse(uint32_t& _windowTextId, uint32_t& _listId); void setEditHouse(House* house, uint32_t listId = 0); @@ -688,22 +760,29 @@ class Player : public Creature, public Cylinder void unlearnInstantSpell(const std::string& name); bool hasLearnedInstantSpell(const std::string& name) const; - VIPListSet VIPList; + VIPSet VIPList; ContainerVector containerVec; - InvitedToGuildsList invitedToGuildsList; + InvitationsList invitationsList; ConditionList storedConditionList; DepotMap depots; + Container transferContainer; + // TODO: make it private? uint32_t marriage; uint64_t balance; double rates[SKILL__LAST + 1]; - Container transferContainer; protected: void checkTradeState(const Item* item); + void internalAddDepot(Depot* depot, uint32_t depotId); + + bool gainExperience(double& gainExp, Creature* target); + bool rateExperience(double& gainExp, Creature* target); - bool gainExperience(double& gainExp, bool fromMonster); - bool rateExperience(double& gainExp, bool fromMonster); + void updateInventoryWeight(); + void updateInventoryGoods(uint32_t itemId); + void updateItemsLight(bool internal = false); + void updateWeapon(); void updateBaseSpeed() { if(!hasFlag(PlayerFlag_SetMaxSpeed)) @@ -712,10 +791,6 @@ class Player : public Creature, public Cylinder baseSpeed = SPEED_MAX; } - void updateInventoryWeight(); - void updateInventoryGoods(uint32_t itemId); - void updateItemsLight(bool internal = false); - void setNextWalkActionTask(SchedulerTask* task); void setNextWalkTask(SchedulerTask* task); void setNextActionTask(SchedulerTask* task); @@ -728,10 +803,10 @@ class Player : public Creature, public Cylinder //cylinder implementations virtual ReturnValue __queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const; + uint32_t flags, Creature* actor = NULL) const; virtual ReturnValue __queryMaxCount(int32_t index, const Thing* thing, uint32_t count, uint32_t& maxQueryCount, uint32_t flags) const; - virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, uint32_t flags) const; + virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, uint32_t flags, Creature* actor = NULL) const; virtual Cylinder* __queryDestination(int32_t& index, const Thing* thing, Item** destItem, uint32_t& flags); @@ -747,10 +822,8 @@ class Player : public Creature, public Cylinder virtual int32_t __getIndexOfThing(const Thing* thing) const; virtual int32_t __getFirstIndex() const; virtual int32_t __getLastIndex() const; - virtual uint32_t __getItemTypeCount(uint16_t itemId, int32_t subType = -1, - bool itemCount = true) const; - virtual std::map& __getAllItemTypeCount(std::map& countMap, bool itemCount = true) const; + virtual uint32_t __getItemTypeCount(uint16_t itemId, int32_t subType = -1) const; + virtual std::map& __getAllItemTypeCount(std::map& countMap) const; virtual void __internalAddThing(Thing* thing); virtual void __internalAddThing(uint32_t index, Thing* thing); @@ -775,7 +848,7 @@ class Player : public Creature, public Cylinder virtual uint64_t getLostExperience() const; virtual void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const; - static uint32_t getPercentLevel(uint64_t count, uint64_t nextLevelCount); + static uint16_t getPercentLevel(uint64_t count, uint64_t nextLevelCount); bool isPromoted(uint32_t pLevel = 1) const {return promotionLevel >= pLevel;} bool hasCapacity(const Item* item, uint32_t count) const; @@ -789,6 +862,9 @@ class Player : public Creature, public Cylinder bool requestedOutfit; bool outfitAttributes; bool addAttackSkillPoint; + bool mounted; + bool pvpBlessing; + bool sentChat; OperatingSystem_t operatingSystem; AccountManager_t accountManager; @@ -801,13 +877,15 @@ class Player : public Creature, public Cylinder GuildLevel_t guildLevel; int16_t blessings; + int16_t marketDepotId; uint16_t maxWriteLen; uint16_t sex; + uint16_t mailAttempts; int32_t premiumDays; int32_t soul; int32_t soulMax; - int32_t vocation_id; + int32_t vocationId; int32_t groupId; int32_t managerNumber, managerNumber2; int32_t purchaseCallback; @@ -817,7 +895,6 @@ class Player : public Creature, public Cylinder int32_t messageBuffer; int32_t bloodHitCount; int32_t shieldBlockCount; - int32_t shootRange; uint32_t clientVersion; uint32_t messageTicks; @@ -831,12 +908,10 @@ class Player : public Creature, public Cylinder uint32_t damageImmunities; uint32_t conditionImmunities; uint32_t conditionSuppressions; - uint32_t condition; //? uint32_t nextStepEvent; uint32_t actionTaskEvent; uint32_t walkTaskEvent; uint32_t lossPercent[LOSS_LAST + 1]; - uint32_t skills[SKILL_LAST + 1][3]; uint32_t guid; uint32_t editListId; uint32_t windowTextId; @@ -848,14 +923,18 @@ class Player : public Creature, public Cylinder time_t skullEnd; time_t lastLogin; time_t lastLogout; + int64_t lastLoad; int64_t lastPong; int64_t lastPing; int64_t nextAction; + int64_t nextExAction; uint64_t stamina; uint64_t experience; uint64_t manaSpent; uint64_t lastAttack; + uint64_t lastMail; + uint64_t skills[SKILL_LAST + 1][3]; double inventoryWeight; double capacity; @@ -868,6 +947,7 @@ class Player : public Creature, public Cylinder Position loginPosition; LightInfo itemsLight; + std::pair backpack; Vocation* vocation; ProtocolGame* client; @@ -880,6 +960,10 @@ class Player : public Creature, public Cylinder Item* writeItem; House* editHouse; Npc* shopOwner; + Item* weapon; + + std::vector forceWalkthrough; + std::vector revengeList; typedef std::set AttackedSet; AttackedSet attackedSet; @@ -887,11 +971,11 @@ class Player : public Creature, public Cylinder PartyList invitePartyList; OutfitMap outfits; LearnedInstantSpellList learnedInstantSpellList; + WarMap warMap; friend class Game; - friend class LuaScriptInterface; + friend class LuaInterface; friend class Npc; - friend class Map; friend class Actions; friend class IOLoginData; friend class ProtocolGame; diff --git a/playerbox.cpp b/playerbox.cpp deleted file mode 100644 index bb116c3..0000000 --- a/playerbox.cpp +++ /dev/null @@ -1,194 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// -#include "otpch.h" -#include "playerbox.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) - -#include "gui.h" -#include "player.h" -#include "ioban.h" - -#include "game.h" -extern Game g_game; - -HWND PlayerBox::playerBox = NULL; -HWND PlayerBox::parent = NULL; - -HWND PlayerBox::ban = NULL; -HWND PlayerBox::kick = NULL; -HWND PlayerBox::list = NULL; -HWND PlayerBox::online = NULL; - -HINSTANCE PlayerBox::m_instance = NULL; - -PlayerBox::PlayerBox() -{ - HINSTANCE hInst = GetModuleHandle(NULL); - WNDCLASSEX wcex; - if(!GetClassInfoEx(hInst, "PlayerBox", &wcex)) - { - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = (WNDPROC)WndProc; - - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = hInst; - - wcex.lpszMenuName = NULL; - wcex.hIcon = NULL; - wcex.hIconSm = NULL; - - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); - - wcex.lpszClassName = "PlayerBox"; - if(!RegisterClassEx(&wcex)) - MessageBox(NULL, "Cannot create PlayerBox!", "Error", MB_OK); - } -} - -void PlayerBox::updatePlayersOnline() -{ - int32_t playersOnline = SendMessage(list, CB_GETCOUNT, 0, 0); - char playersOnlineBuffer[50]; - - sprintf(playersOnlineBuffer, "%d player%s online", playersOnline, (playersOnline != 1 ? "s" : "")); - SendMessage(online, WM_SETTEXT, 0, (LPARAM)playersOnlineBuffer); -} - -void PlayerBox::addPlayer(Player* player) -{ - SendMessage(list, CB_ADDSTRING, 0, (LPARAM)player->getName().c_str()); - updatePlayersOnline(); -} - -void PlayerBox::removePlayer(Player* player) -{ - DWORD index = SendMessage(list, CB_FINDSTRING, 0,(LPARAM)player->getName().c_str()); - if((signed)index != CB_ERR) - SendMessage(list, CB_DELETESTRING, index, 0); - - updatePlayersOnline(); -} - -LRESULT CALLBACK PlayerBox::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch(message) - { - case WM_CREATE: - { - int32_t playersOnline = g_game.getPlayersOnline(); - char playersOnlineBuffer[50]; - sprintf(playersOnlineBuffer, "%d player%s online", playersOnline, (playersOnline != 1 ? "s" : "")); - m_instance = GetModuleHandle(NULL); - - ban = CreateWindowEx(0, "button", "Ban permamently", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 5, 35, 115, 25, hWnd, NULL, m_instance, NULL); - kick = CreateWindowEx(0, "button", "Kick", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 125, 35, 90, 25, hWnd, NULL, m_instance, NULL); - list = CreateWindowEx(0, "combobox", "", WS_CHILD | WS_VISIBLE | WS_VSCROLL/* | CBS_DROPDOWNLIST*/ | CBS_SORT, 5, 5, 210, 25, hWnd, NULL, m_instance, NULL); - online = CreateWindowEx(WS_EX_STATICEDGE, "static", playersOnlineBuffer, WS_VISIBLE | WS_CHILD | WS_TABSTOP, 5, 65, 210, 20, hWnd, NULL, m_instance, NULL); - - SendMessage(ban, WM_SETFONT, (WPARAM)GUI::getInstance()->m_font, 0); - SendMessage(kick, WM_SETFONT, (WPARAM)GUI::getInstance()->m_font, 0); - SendMessage(list, WM_SETFONT, (WPARAM)GUI::getInstance()->m_font, 0); - SendMessage(online, WM_SETFONT, (WPARAM)GUI::getInstance()->m_font, 0); - for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) - SendMessage(list, CB_ADDSTRING, 0, (LPARAM)it->second->getName().c_str()); - - break; - } - case WM_COMMAND: - { - switch(HIWORD(wParam)) - { - case BN_CLICKED: - { - char name[30]; - GetWindowText(list, name, sizeof(name)); - if(Player* player = g_game.getPlayerByName(name)) - { - char buffer[150]; - sprintf(buffer, "Are you sure you want to %s %s?", ((HWND)lParam == kick ? "kick" : "ban permamently"), player->getName().c_str()); - if(MessageBox(hWnd, buffer, "Player management", MB_YESNO) == IDYES) - { - if((HWND)lParam == ban) - IOBan::getInstance()->addPlayerBanishment(player->getID(), -1, 21, ACTION_DELETION, "Permament banishment.", 0, PLAYERBAN_BANISHMENT); - - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_GREEN); - Scheduler::getInstance().addEvent(createSchedulerTask(1000, boost::bind( - &Game::kickPlayer, &g_game, player->getID(), false))); - } - } - else - MessageBox(hWnd, "A player with this name is not online", "Player management", MB_OK); - - break; - } - default: - break; - } - break; - } - case WM_DESTROY: - { - EnableWindow(parent, true); - SetForegroundWindow(parent); - DestroyWindow(hWnd); - - PostQuitMessage(0); - break; - } - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - - return 0; -} - -bool PlayerBox::popUp(LPCTSTR szCaption) -{ - RECT r; - GetWindowRect(GetDesktopWindow(), &r); - - playerBox = CreateWindowEx(WS_EX_TOOLWINDOW, "PlayerBox", szCaption, WS_POPUPWINDOW|WS_CAPTION|WS_TABSTOP, (r.right-200)/2, (r.bottom-115)/2, 225, 115, parent, NULL, m_instance, NULL); - if(!playerBox) - return FALSE; - - SetForegroundWindow(playerBox); - EnableWindow(parent, FALSE); - ShowWindow(playerBox, SW_SHOW); - UpdateWindow(playerBox); - - BOOL ret = 0; - MSG msg; - - SendMessage(list, WM_KEYDOWN, VK_DOWN, 0); - while(GetMessage(&msg, NULL, 0, 0)) - { - if(msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) - { - SendMessage(playerBox, WM_DESTROY, 0, 0); - ret = 0; - } - - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - return ret; -} -#endif diff --git a/playerbox.h b/playerbox.h deleted file mode 100644 index 1f41f8d..0000000 --- a/playerbox.h +++ /dev/null @@ -1,49 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// - -#ifndef __PLAYERBOX__ -#define __PLAYERBOX__ -#include "otsystem.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) - -class Player; -class PlayerBox -{ - static HWND playerBox; - static HWND parent; - - static HWND kick; - static HWND ban; - static HWND online; - - static HINSTANCE m_instance; - static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - - public: - PlayerBox(); - virtual ~PlayerBox() {} - - void updatePlayersOnline(); - void addPlayer(Player* player); - void removePlayer(Player* player); - - void setParent(HWND _parent) {parent = _parent;} - bool popUp(LPCTSTR szCaption); - static HWND list; -}; -#endif -#endif diff --git a/position.h b/position.h index 6750068..f244d07 100644 --- a/position.h +++ b/position.h @@ -93,22 +93,22 @@ class Position return !(*this < p); } - bool operator==(const Position p) const + bool operator==(const Position& p) const { return (p.x == x && p.y == y && p.z == z); } - bool operator!=(const Position p) const + bool operator!=(const Position& p) const { return !(*this == p); } - Position operator+(const Position p1) + Position operator+(const Position& p1) { return Position(x + p1.x, y + p1.y, z + p1.z); } - Position operator-(const Position p1) + Position operator-(const Position& p1) { return Position(x - p1.x, y - p1.y, z - p1.z); } @@ -131,12 +131,12 @@ class PositionEx : public Position int16_t stackpos; - bool operator==(const PositionEx p) const + bool operator==(const PositionEx& p) const { return (p.x == x && p.y == y && p.z == z && p.stackpos == stackpos); } - bool operator!=(const PositionEx p) const + bool operator!=(const PositionEx& p) const { return !(p.x == x && p.y == y && p.z == z && p.stackpos != stackpos); } diff --git a/protocol.cpp b/protocol.cpp index d12c283..50aa840 100644 --- a/protocol.cpp +++ b/protocol.cpp @@ -20,20 +20,19 @@ #endif #include "protocol.h" -#include "scheduler.h" +#include "tools.h" +#include "scheduler.h" #include "connection.h" #include "outputmessage.h" -#include "tools.h" -#include "rsa.h" - -extern RSA g_RSA; +#include +extern RSA* g_RSA; void Protocol::onSendMessage(OutputMessage_ptr msg) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Protocol::onSendMessage" << std::endl; + std::clog << "Protocol::onSendMessage" << std::endl; #endif if(!m_rawMessages) { @@ -41,7 +40,7 @@ void Protocol::onSendMessage(OutputMessage_ptr msg) if(m_encryptionEnabled) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Protocol::onSendMessage - encrypt" << std::endl; + std::clog << "Protocol::onSendMessage - encrypt" << std::endl; #endif XTEA_encrypt(*msg); } @@ -49,7 +48,7 @@ void Protocol::onSendMessage(OutputMessage_ptr msg) if(m_checksumEnabled) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Protocol::onSendMessage - crypto header" << std::endl; + std::clog << "Protocol::onSendMessage - crypto header" << std::endl; #endif msg->addCryptoHeader(m_checksumEnabled); } @@ -62,14 +61,15 @@ void Protocol::onSendMessage(OutputMessage_ptr msg) void Protocol::onRecvMessage(NetworkMessage& msg) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Protocol::onRecvMessage" << std::endl; + std::clog << "Protocol::onRecvMessage" << std::endl; #endif if(m_encryptionEnabled) { #ifdef __DEBUG_NET_DETAIL__ - std::cout << "Protocol::onRecvMessage - decrypt" << std::endl; + std::clog << "Protocol::onRecvMessage - decrypt" << std::endl; #endif - XTEA_decrypt(msg); + if(!XTEA_decrypt(msg)) + return; } parsePacket(msg); @@ -80,13 +80,11 @@ OutputMessage_ptr Protocol::getOutputBuffer() if(m_outputBuffer) return m_outputBuffer; - if(m_connection) - { - m_outputBuffer = OutputMessagePool::getInstance()->getOutputMessage(this); - return m_outputBuffer; - } + if(!m_connection) + return OutputMessage_ptr(); - return OutputMessage_ptr(); + m_outputBuffer = OutputMessagePool::getInstance()->getOutputMessage(this); + return m_outputBuffer; } void Protocol::releaseProtocol() @@ -107,114 +105,111 @@ void Protocol::deleteProtocolTask() void Protocol::XTEA_encrypt(OutputMessage& msg) { - uint32_t k[4]; - for(uint8_t i = 0; i < 4; i++) - k[i] = m_key[i]; - - int32_t messageLength = msg.getMessageLength(); //add bytes until reach 8 multiple - uint32_t n; - if((messageLength % 8) != 0) + uint16_t messageLength = msg.size(); + if(messageLength % 8) { - n = 8 - (messageLength % 8); - msg.AddPaddingBytes(n); - messageLength = messageLength + n; + uint16_t n = 8 - (messageLength % 8); + msg.putPadding(n); + messageLength += n; } - int32_t readPos = 0; - uint32_t* buffer = (uint32_t*)msg.getOutputBuffer(); - while(readPos < messageLength / 4) + int32_t readPos = -1; + uint32_t *buffer = (uint32_t*)msg.getOutputBuffer(), delta = 0x61C88647; + while(++readPos < messageLength >> 2) { - uint32_t v0 = buffer[readPos], v1 = buffer[readPos + 1], delta = 0x61C88647, sum = 0; - for(int32_t i = 0; i < 32; i++) + uint32_t v0 = buffer[readPos], v1 = buffer[readPos + 1], sum = 0; + for(int32_t i = 0; i < 32; ++i) { - v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + m_key[sum & 3]); sum -= delta; - v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[sum>>11 & 3]); + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + m_key[sum >> 11 & 3]); } buffer[readPos] = v0; - buffer[readPos + 1] = v1; - readPos += 2; + buffer[++readPos] = v1; } } bool Protocol::XTEA_decrypt(NetworkMessage& msg) { - if((msg.getMessageLength() - 6) % 8 != 0) + if((msg.size() - 6) % 8) { - std::cout << "[Failure - Protocol::XTEA_decrypt] Not valid encrypted message size"; + std::clog << "[Failure - Protocol::XTEA_decrypt] Not valid encrypted message size"; int32_t ip = getIP(); if(ip) - std::cout << " (IP: " << convertIPAddress(ip) << ")"; + std::clog << " (IP: " << convertIPAddress(ip) << ")"; - std::cout << std::endl; + std::clog << std::endl; return false; } - uint32_t k[4]; - for(uint8_t i = 0; i < 4; i++) - k[i] = m_key[i]; - - int32_t messageLength = msg.getMessageLength() - 6, readPos = 0; - uint32_t* buffer = (uint32_t*)(msg.getBuffer() + msg.getReadPos()); - while(readPos < messageLength / 4) + int32_t messageLength = msg.size() - 6, readPos = -1; + uint32_t *buffer = (uint32_t*)(msg.buffer() + msg.position()), delta = 0x61C88647; + while(++readPos < messageLength >> 2) { - uint32_t v0 = buffer[readPos], v1 = buffer[readPos + 1], delta = 0x61C88647, sum = 0xC6EF3720; - for(int32_t i = 0; i < 32; i++) + uint32_t v0 = buffer[readPos], v1 = buffer[readPos + 1], sum = 0xC6EF3720; + for(int32_t i = 0; i < 32; ++i) { - v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[sum>>11 & 3]); + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + m_key[sum >> 11 & 3]); sum += delta; - v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + m_key[sum & 3]); } buffer[readPos] = v0; - buffer[readPos + 1] = v1; - readPos += 2; + buffer[++readPos] = v1; } - // - int32_t tmp = msg.GetU16(); - if(tmp > msg.getMessageLength() - 8) + int32_t tmp = msg.get(); + if(tmp > msg.size() - 8) { - std::cout << "[Failure - Protocol::XTEA_decrypt] Not valid unencrypted message size"; + std::clog << "[Failure - Protocol::XTEA_decrypt] Not valid unencrypted message size"; uint32_t ip = getIP(); if(ip) - std::cout << " (IP: " << convertIPAddress(ip) << ")"; + std::clog << " (IP: " << convertIPAddress(ip) << ")"; - std::cout << std::endl; + std::clog << std::endl; return false; } - msg.setMessageLength(tmp); + msg.setSize(tmp); return true; } bool Protocol::RSA_decrypt(NetworkMessage& msg) { - return RSA_decrypt(&g_RSA, msg); -} - -bool Protocol::RSA_decrypt(RSA* rsa, NetworkMessage& msg) -{ - if(msg.getMessageLength() - msg.getReadPos() != 128) + if(msg.size() - msg.position() != 128) { - std::cout << "[Warning - Protocol::RSA_decrypt] Not valid packet size" << std::endl; + std::clog << "[Warning - Protocol::RSA_decrypt] Not valid packet size"; + int32_t ip = getIP(); + if(ip) + std::clog << " (IP: " << convertIPAddress(ip) << ")"; + + std::clog << std::endl; return false; } - rsa->decrypt((char*)(msg.getBuffer() + msg.getReadPos()), 128); - if(!msg.GetByte()) + uint16_t size = msg.size(); + RSA_private_decrypt(128, (uint8_t*)(msg.buffer() + msg.position()), (uint8_t*)msg.buffer(), g_RSA, RSA_NO_PADDING); + msg.setSize(size); + + msg.setPosition(0); + if(!msg.get()) return true; - std::cout << "[Warning - Protocol::RSA_decrypt] First byte != 0" << std::endl; + std::clog << "[Warning - Protocol::RSA_decrypt] First byte != 0"; + int32_t ip = getIP(); + if(ip) + std::clog << " (IP: " << convertIPAddress(ip) << ")"; + + std::clog << std::endl; return false; } uint32_t Protocol::getIP() const { - if(getConnection()) - return getConnection()->getIP(); + if(Connection_ptr connection = getConnection()) + return connection->getIP(); return 0; } diff --git a/protocol.h b/protocol.h index adfb7a7..a40d3d3 100644 --- a/protocol.h +++ b/protocol.h @@ -18,15 +18,10 @@ #ifndef __PROTOCOL__ #define __PROTOCOL__ #include "otsystem.h" -#include "rsa.h" +#include "connection.h" class OutputMessage; -typedef boost::shared_ptr OutputMessage_ptr; - class Connection; -typedef boost::shared_ptr Connection_ptr; - -class RSA; class NetworkMessage; class Protocol : boost::noncopyable @@ -49,7 +44,7 @@ class Protocol : boost::noncopyable void onRecvMessage(NetworkMessage& msg); void onSendMessage(OutputMessage_ptr msg); - virtual void parsePacket(NetworkMessage& msg) {} + virtual void parsePacket(NetworkMessage&) {} uint32_t getIP() const; Connection_ptr getConnection() {return m_connection;} @@ -74,7 +69,6 @@ class Protocol : boost::noncopyable void XTEA_encrypt(OutputMessage& msg); bool XTEA_decrypt(NetworkMessage& msg); bool RSA_decrypt(NetworkMessage& msg); - bool RSA_decrypt(RSA* rsa, NetworkMessage& msg); virtual void releaseProtocol(); virtual void deleteProtocolTask(); diff --git a/protocolgame.cpp b/protocolgame.cpp index 7a2b136..46bdfdc 100644 --- a/protocolgame.cpp +++ b/protocolgame.cpp @@ -15,10 +15,9 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" -#include "resources.h" - #include #include +#include #include "protocolgame.h" #include "textlogger.h" @@ -40,14 +39,20 @@ #include "actions.h" #include "creatureevent.h" #include "quests.h" +#include "mounts.h" #include "chat.h" #include "configmanager.h" #include "game.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) -#include "gui.h" -#endif +#include "iomarket.h" + +/* +Bytes not yet added: + 0x87 -> Position swapping (http://www.tibia.com/support/?subtopic=faq&question=swapping) + 0xF4 -> Flash client objects cache? + 0xF5 -> Flash client inventory? +*/ extern Game g_game; extern ConfigManager g_config; @@ -66,8 +71,8 @@ void ProtocolGame::addGameTaskInternal(uint32_t delay, const FunctionType& func) #ifdef __ENABLE_SERVER_DIAGNOSTIC__ uint32_t ProtocolGame::protocolGameCount = 0; -#endif +#endif void ProtocolGame::setPlayer(Player* p) { player = p; @@ -92,7 +97,7 @@ void ProtocolGame::deleteProtocolTask() Protocol::deleteProtocolTask(); } -bool ProtocolGame::login(const std::string& name, uint32_t id, const std::string& password, +bool ProtocolGame::login(const std::string& name, uint32_t id, const std::string&, OperatingSystem_t operatingSystem, uint16_t version, bool gamemaster) { //dispatcher thread @@ -114,7 +119,7 @@ bool ProtocolGame::login(const std::string& name, uint32_t id, const std::string } Ban ban; - ban.value = player->getID(); + ban.value = player->getGUID(); ban.param = PLAYERBAN_BANISHMENT; ban.type = BAN_PLAYER; @@ -127,14 +132,12 @@ bool ProtocolGame::login(const std::string& name, uint32_t id, const std::string else IOLoginData::getInstance()->getNameByGuid(ban.adminId, name_, true); - char buffer[500 + ban.comment.length()]; - sprintf(buffer, "Your character has been %s at:\n%s by: %s,\nfor the following reason:\n%s.\nThe action taken was:\n%s.\nThe comment given was:\n%s.\nYour %s%s.", - (deletion ? "deleted" : "banished"), formatDateShort(ban.added).c_str(), name_.c_str(), - getReason(ban.reason).c_str(), getAction(ban.action, false).c_str(), ban.comment.c_str(), - (deletion ? "character won't be undeleted" : "banishment will be lifted at:\n"), - (deletion ? "." : formatDateShort(ban.expires, true).c_str())); + std::stringstream stream; + stream << "Your character has been " << (deletion ? "deleted" : "banished") << " at:\n" << formatDateEx(ban.added, "%d %b %Y").c_str() << " by: " << name_.c_str() + << ".\nThe comment given was:\n" << ban.comment.c_str() << ".\nYour " << (deletion ? "character won't be undeleted" : "banishment will be lifted at:\n") + << (deletion ? "" : formatDateEx(ban.expires).c_str()) << "."; - disconnectClient(0x14, buffer); + disconnectClient(0x14, stream.str().c_str()); return false; } @@ -154,8 +157,14 @@ bool ProtocolGame::login(const std::string& name, uint32_t id, const std::string return false; } } - else if(player->getName() == "Account Manager" && g_config.getBool(ConfigManager::ACCOUNT_MANAGER)) + else if(player->getName() == "Account Manager") { + if(!g_config.getBool(ConfigManager::ACCOUNT_MANAGER)) + { + disconnectClient(0x14, "Account Manager is disabled."); + return false; + } + if(id != 1) { player->accountManager = MANAGER_ACCOUNT; @@ -173,13 +182,13 @@ bool ProtocolGame::login(const std::string& name, uint32_t id, const std::string if(!player->hasFlag(PlayerFlag_CanAlwaysLogin)) { - if(g_game.getGameState() == GAME_STATE_CLOSING) + if(g_game.getGameState() == GAMESTATE_CLOSING) { disconnectClient(0x14, "Gameworld is just going down, please come back later."); return false; } - if(g_game.getGameState() == GAME_STATE_CLOSED) + if(g_game.getGameState() == GAMESTATE_CLOSED) { disconnectClient(0x14, "Gameworld is currently closed, please come back later."); return false; @@ -229,9 +238,9 @@ bool ProtocolGame::login(const std::string& name, uint32_t id, const std::string else ss << "awaiting connection..."; - output->AddByte(0x16); - output->AddString(ss.str()); - output->AddByte(WaitingList::getTime(slot)); + output->put(0x16); + output->putString(ss.str()); + output->put(WaitingList::getTime(slot)); OutputMessagePool::getInstance()->send(output); } @@ -245,13 +254,12 @@ bool ProtocolGame::login(const std::string& name, uint32_t id, const std::string return false; } - player->setOperatingSystem(operatingSystem); player->setClientVersion(version); + player->setOperatingSystem(operatingSystem); if(!g_game.placeCreature(player, player->getLoginPosition()) && !g_game.placeCreature(player, player->getMasterPosition(), false, true)) { disconnectClient(0x14, "Temple position is wrong. Contact with the administration."); return false; - } player->lastIP = player->getIP(); @@ -261,17 +269,23 @@ bool ProtocolGame::login(const std::string& name, uint32_t id, const std::string m_acceptPackets = true; return true; } - else if(_player->client) + + if(gamemaster && !_player->hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges)) + { + disconnectClient(0x14, "You are not a gamemaster! Turn off the gamemaster mode in your IP changer."); + return false; + } + + if(_player->hasClient()) { if(m_eventConnect || !g_config.getBool(ConfigManager::REPLACE_KICK_ON_LOGIN)) { - //A task has already been scheduled just bail out (should not be overriden) + // task has already been scheduled just bail out (should not be overriden) disconnectClient(0x14, "You are already logged in."); return false; } - g_chat.removeUserFromAllChannels(_player); - _player->disconnect(); + _player->client->disconnect(); _player->isConnecting = true; addRef(); @@ -312,19 +326,18 @@ bool ProtocolGame::logout(bool displayEffect, bool forceLogout) return false; } else - g_creatureEvents->playerLogout(player, false); + g_creatureEvents->playerLogout(player, true); } else if(!g_creatureEvents->playerLogout(player, true)) return false; - } - else - displayEffect = false; - if(displayEffect && !player->isGhost()) - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + if(displayEffect && !player->isGhost()) + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + } - if(Connection_ptr connection = getConnection()) - connection->close(); + disconnect(); + if(player->isRemoved()) + return true; return g_game.removeCreature(player); } @@ -335,7 +348,7 @@ bool ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem, m_eventConnect = 0; Player* _player = g_game.getPlayerByID(playerId); - if(!_player || _player->isRemoved() || _player->client) + if(!_player || _player->isRemoved() || _player->hasClient()) { disconnectClient(0x14, "You are already logged in."); return false; @@ -343,26 +356,25 @@ bool ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem, player = _player; player->addRef(); + player->client = this; player->isConnecting = false; - player->client = this; player->sendCreatureAppear(player); - player->setOperatingSystem(operatingSystem); player->setClientVersion(version); player->lastIP = player->getIP(); player->lastLoad = OTSYS_TIME(); - player->lastLogin = std::max(time(NULL), player->lastLogin + 1); + g_chat.reOpenChannels(player); m_acceptPackets = true; return true; } void ProtocolGame::disconnect() { - if(getConnection()) - getConnection()->close(); + if(Connection_ptr connection = getConnection()) + connection->close(); } void ProtocolGame::disconnectClient(uint8_t error, const char* message) @@ -370,8 +382,8 @@ void ProtocolGame::disconnectClient(uint8_t error, const char* message) if(OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false)) { TRACK_MESSAGE(output); - output->AddByte(error); - output->AddString(message); + output->put(error); + output->putString(message); OutputMessagePool::getInstance()->send(output); } @@ -385,10 +397,10 @@ void ProtocolGame::onConnect() TRACK_MESSAGE(output); enableChecksum(); - output->AddByte(0x1F); - output->AddU16(random_range(0, 0xFFFF)); - output->AddU16(0x00); - output->AddByte(random_range(0, 0xFF)); + output->put(0x1F); + output->put(random_range(0, 0xFFFF)); + output->put(0x00); + output->put(random_range(0, 0xFF)); OutputMessagePool::getInstance()->send(output); } @@ -396,41 +408,32 @@ void ProtocolGame::onConnect() void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) { - parseFirstPacket(msg); -} - -bool ProtocolGame::parseFirstPacket(NetworkMessage& msg) -{ - if( -#if defined(WINDOWS) && !defined(__CONSOLE__) - !GUI::getInstance()->m_connections || -#endif - g_game.getGameState() == GAME_STATE_SHUTDOWN) + if(g_game.getGameState() == GAMESTATE_SHUTDOWN) { getConnection()->close(); - return false; + return; } - OperatingSystem_t operatingSystem = (OperatingSystem_t)msg.GetU16(); - uint16_t version = msg.GetU16(); + OperatingSystem_t operatingSystem = (OperatingSystem_t)msg.get(); + uint16_t version = msg.get(); if(!RSA_decrypt(msg)) { getConnection()->close(); - return false; + return; } - uint32_t key[4] = {msg.GetU32(), msg.GetU32(), msg.GetU32(), msg.GetU32()}; + uint32_t key[4] = {msg.get(), msg.get(), msg.get(), msg.get()}; enableXTEAEncryption(); setXTEAKey(key); - bool gamemaster = msg.GetByte(); - std::string name = msg.GetString(), character = msg.GetString(), password = msg.GetString(); + bool gamemaster = (msg.get() != (char)0); + std::string name = msg.getString(), character = msg.getString(), password = msg.getString(); - msg.SkipBytes(6); //841- wtf? + msg.skip(6); //841- wtf? if(version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { disconnectClient(0x14, CLIENT_VERSION_STRING); - return false; + return; } if(name.empty()) @@ -438,35 +441,35 @@ bool ProtocolGame::parseFirstPacket(NetworkMessage& msg) if(!g_config.getBool(ConfigManager::ACCOUNT_MANAGER)) { disconnectClient(0x14, "Invalid account name."); - return false; + return; } name = "1"; password = "1"; } - if(g_game.getGameState() < GAME_STATE_NORMAL) + if(g_game.getGameState() < GAMESTATE_NORMAL) { disconnectClient(0x14, "Gameworld is just starting up, please wait."); - return false; + return; } - if(g_game.getGameState() == GAME_STATE_MAINTAIN) + if(g_game.getGameState() == GAMESTATE_MAINTAIN) { disconnectClient(0x14, "Gameworld is under maintenance, please re-connect in a while."); - return false; + return; } if(ConnectionManager::getInstance()->isDisabled(getIP(), protocolId)) { disconnectClient(0x14, "Too many connections attempts from your IP address, please try again later."); - return false; + return; } if(IOBan::getInstance()->isIpBanished(getIP())) { disconnectClient(0x14, "Your IP is banished!"); - return false; + return; } uint32_t id = 1; @@ -474,15 +477,15 @@ bool ProtocolGame::parseFirstPacket(NetworkMessage& msg) { ConnectionManager::getInstance()->addAttempt(getIP(), protocolId, false); disconnectClient(0x14, "Invalid account name."); - return false; + return; } - std::string hash; - if(!IOLoginData::getInstance()->getPassword(id, hash, character) || !encryptTest(password, hash)) + std::string hash, salt; + if(!IOLoginData::getInstance()->getPassword(id, hash, salt, character) || !encryptTest(salt + password, hash)) { ConnectionManager::getInstance()->addAttempt(getIP(), protocolId, false); disconnectClient(0x14, "Invalid password."); - return false; + return; } Ban ban; @@ -498,32 +501,27 @@ bool ProtocolGame::parseFirstPacket(NetworkMessage& msg) else IOLoginData::getInstance()->getNameByGuid(ban.adminId, name_, true); - char buffer[500 + ban.comment.length()]; - sprintf(buffer, "Your account has been %s at:\n%s by: %s,\nfor the following reason:\n%s.\nThe action taken was:\n%s.\nThe comment given was:\n%s.\nYour %s%s.", - (deletion ? "deleted" : "banished"), formatDateShort(ban.added).c_str(), name_.c_str(), - getReason(ban.reason).c_str(), getAction(ban.action, false).c_str(), ban.comment.c_str(), - (deletion ? "account won't be undeleted" : "banishment will be lifted at:\n"), - (deletion ? "." : formatDateShort(ban.expires, true).c_str())); + std::stringstream stream; + stream << "Your account has been " << (deletion ? "deleted" : "banished") << " at:\n" << formatDateEx(ban.added, "%d %b %Y").c_str() << " by: " << name_.c_str() + << ".\nThe comment given was:\n" << ban.comment.c_str() << ".\nYour " << (deletion ? "account won't be undeleted" : "banishment will be lifted at:\n") + << (deletion ? "" : formatDateEx(ban.expires).c_str()) << "."; - disconnectClient(0x14, buffer); - return false; + disconnectClient(0x14, stream.str().c_str()); + return; } ConnectionManager::getInstance()->addAttempt(getIP(), protocolId, true); Dispatcher::getInstance().addTask(createTask(boost::bind( &ProtocolGame::login, this, character, id, password, operatingSystem, version, gamemaster))); - return true; } void ProtocolGame::parsePacket(NetworkMessage &msg) { - if(!player || !m_acceptPackets || g_game.getGameState() == GAME_STATE_SHUTDOWN - || msg.getMessageLength() <= 0) + if(!player || !m_acceptPackets || g_game.getGameState() == GAMESTATE_SHUTDOWN || !msg.size()) return; - uint8_t recvbyte = msg.GetByte(); - //a dead player cannot performs actions - if(player->isRemoved() && recvbyte != 0x14) + uint8_t recvbyte = msg.get(); + if(player->isRemoved() && recvbyte != 0x14) //a dead player cannot performs actions return; if(player->isAccountManager()) @@ -538,8 +536,24 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) parseSay(msg); break; + case 0x1E: + parseReceivePing(msg); + break; + + case 0xC9: + parseUpdateTile(msg); + break; + + case 0xE8: + parseDebugAssert(msg); + break; + + case 0xA1: + parseCancelTarget(msg); + break; + default: - sendCancelWalk(); + parseCancelWalk(msg); break; } } @@ -682,19 +696,7 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) break; case 0x9A: // open priv - parseOpenPriv(msg); - break; - - case 0x9B: //process report - parseProcessRuleViolation(msg); - break; - - case 0x9C: //gm closes report - parseCloseRuleViolation(msg); - break; - - case 0x9D: //player cancels report - parseCancelRuleViolation(msg); + parseOpenPrivate(msg); break; case 0x9E: // close NPC @@ -771,9 +773,14 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) case 0xD3: // set outfit if((!player->hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges) || !g_config.getBool(ConfigManager::DISABLE_OUTFITS_PRIVILEGED)) && (g_config.getBool(ConfigManager::ALLOW_CHANGECOLORS) || g_config.getBool(ConfigManager::ALLOW_CHANGEOUTFIT))) - parseSetOutfit(msg); + parseSetOutfit(msg); break; + case 0xD4: // set mount + if(g_config.getBool(ConfigManager::ALLOW_MOUNTS)) + parseMountStatus(msg); + + break; case 0xDC: parseAddVip(msg); break; @@ -787,7 +794,7 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) break; case 0xE7: - parseViolationWindow(msg); + parseThankYou(msg); break; case 0xE8: @@ -802,44 +809,35 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) parseQuestInfo(msg); break; - default: - { - if(g_config.getBool(ConfigManager::BAN_UNKNOWN_BYTES)) - { - int64_t banTime = -1; - ViolationAction_t action = ACTION_BANISHMENT; - Account tmp = IOLoginData::getInstance()->loadAccount(player->getAccount(), true); - - tmp.warnings++; - if(tmp.warnings >= g_config.getNumber(ConfigManager::WARNINGS_TO_DELETION)) - action = ACTION_DELETION; - else if(tmp.warnings >= g_config.getNumber(ConfigManager::WARNINGS_TO_FINALBAN)) - { - banTime = time(NULL) + g_config.getNumber(ConfigManager::FINALBAN_LENGTH); - action = ACTION_BANFINAL; - } - else - banTime = time(NULL) + g_config.getNumber(ConfigManager::BAN_LENGTH); + case 0xF2: + parseViolationReport(msg); + break; - if(IOBan::getInstance()->addAccountBanishment(tmp.number, banTime, 13, action, - "Sending unknown packets to the server.", 0, player->getGUID())) - { - IOLoginData::getInstance()->saveAccount(tmp); - player->sendTextMessage(MSG_INFO_DESCR, "You have been banished."); + case 0xF4: + parseMarketLeave(); + break; - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_GREEN); - Scheduler::getInstance().addEvent(createSchedulerTask(1000, boost::bind( - &Game::kickPlayer, &g_game, player->getID(), false))); - } - } + case 0xF5: + parseMarketBrowse(msg); + break; + + case 0xF6: + parseMarketCreateOffer(msg); + break; + + case 0xF7: + parseMarketCancelOffer(msg); + break; - std::stringstream hex, s; - hex << "0x" << std::hex << (int16_t)recvbyte << std::dec; - s << player->getName() << " sent unknown byte: " << hex << std::endl; + case 0xF8: + parseMarketAcceptOffer(msg); + break; - LOG_MESSAGE(LOGTYPE_NOTICE, s.str(), "PLAYER") - Logger::getInstance()->eFile(getFilePath(FILE_TYPE_LOG, "bots/" + player->getName() + ".log").c_str(), - "[" + formatDate() + "] Received byte " + hex.str(), false); + default: + { + std::stringstream s; + s << "Sent unknown byte: 0x" << std::hex << (int16_t)recvbyte << std::dec; + Logger::getInstance()->eFile("bots/" + player->getName() + ".log", s.str(), true); break; } } @@ -848,14 +846,12 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage_ptr msg) { - if(!tile) - return; - + msg->put(0x00); // enviromental effects, flash only int32_t count = 0; if(tile->ground) { - msg->AddItem(tile->ground); - count++; + msg->putItem(tile->ground); + ++count; } const TileItemVector* items = tile->getItemList(); @@ -865,7 +861,7 @@ void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage_ptr msg) if(items) { for(it = items->getBeginTopItem(); (it != items->getEndTopItem() && count < 10); ++it, ++count) - msg->AddItem(*it); + msg->putItem(*it); } if(creatures) @@ -880,14 +876,14 @@ void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage_ptr msg) checkCreatureAsKnown((*cit)->getID(), known, removedKnown); AddCreature(msg, (*cit), known, removedKnown); - count++; + ++count; } } if(items) { for(it = items->getBeginDownItem(); (it != items->getEndDownItem() && count < 10); ++it, ++count) - msg->AddItem(*it); + msg->putItem(*it); } } @@ -913,8 +909,8 @@ void ProtocolGame::GetMapDescription(int32_t x, int32_t y, int32_t z, if(skip >= 0) { - msg->AddByte(skip); - msg->AddByte(0xFF); + msg->put(skip); + msg->put(0xFF); //cc += skip; } } @@ -923,30 +919,26 @@ void ProtocolGame::GetFloorDescription(NetworkMessage_ptr msg, int32_t x, int32_ int32_t width, int32_t height, int32_t offset, int32_t& skip) { Tile* tile = NULL; - for(int32_t nx = 0; nx < width; nx++) + for(int32_t nx = 0; nx < width; ++nx) { - for(int32_t ny = 0; ny < height; ny++) + for(int32_t ny = 0; ny < height; ++ny) { if((tile = g_game.getTile(Position(x + nx + offset, y + ny + offset, z)))) { if(skip >= 0) { - msg->AddByte(skip); - msg->AddByte(0xFF); + msg->put(skip); + msg->put(0xFF); } skip = 0; GetTileDescription(tile, msg); } - else + else if(++skip == 0xFF) { - ++skip; - if(skip == 0xFF) - { - msg->AddByte(0xFF); - msg->AddByte(0xFF); - skip = -1; - } + msg->put(0xFF); + msg->put(0xFF); + skip = -1; } } } @@ -973,11 +965,11 @@ void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool& known, uint32_t& remo // ... but not in future knownCreatureList.push_back(id); // too many known creatures? - if(knownCreatureList.size() > 250) + if(knownCreatureList.size() > 1300) { // lets try to remove one from the end of the list Creature* c = NULL; - for(int32_t n = 0; n < 250; n++) + for(int16_t n = 0; n < 1300; ++n) { removedKnown = knownCreatureList.front(); if(!(c = g_game.getCreatureByID(removedKnown)) || !canSee(c)) @@ -1008,11 +1000,6 @@ bool ProtocolGame::canSee(const Position& pos) const bool ProtocolGame::canSee(uint16_t x, uint16_t y, uint16_t z) const { -#ifdef __DEBUG__ - if(z < 0 || z >= MAP_MAX_LAYERS) - std::cout << "[Warning - ProtocolGame::canSee] Z-value is out of range!" << std::endl; -#endif - const Position& myPos = player->getPosition(); if(myPos.z <= 7) { @@ -1030,93 +1017,95 @@ bool ProtocolGame::canSee(uint16_t x, uint16_t y, uint16_t z) const } //********************** Parse methods *******************************// -void ProtocolGame::parseLogout(NetworkMessage& msg) +void ProtocolGame::parseLogout(NetworkMessage&) { Dispatcher::getInstance().addTask(createTask(boost::bind(&ProtocolGame::logout, this, true, false))); } -void ProtocolGame::parseCreatePrivateChannel(NetworkMessage& msg) +void ProtocolGame::parseCancelWalk(NetworkMessage&) +{ + Dispatcher::getInstance().addTask(createTask(boost::bind(&ProtocolGame::sendCancelWalk, this))); +} + +void ProtocolGame::parseCancelTarget(NetworkMessage&) +{ + Dispatcher::getInstance().addTask(createTask(boost::bind(&ProtocolGame::sendCancelTarget, this))); +} + +void ProtocolGame::parseCreatePrivateChannel(NetworkMessage&) { addGameTask(&Game::playerCreatePrivateChannel, player->getID()); } void ProtocolGame::parseChannelInvite(NetworkMessage& msg) { - const std::string name = msg.GetString(); + const std::string name = msg.getString(); addGameTask(&Game::playerChannelInvite, player->getID(), name); } void ProtocolGame::parseChannelExclude(NetworkMessage& msg) { - const std::string name = msg.GetString(); + const std::string name = msg.getString(); addGameTask(&Game::playerChannelExclude, player->getID(), name); } -void ProtocolGame::parseGetChannels(NetworkMessage& msg) +void ProtocolGame::parseGetChannels(NetworkMessage&) { addGameTask(&Game::playerRequestChannels, player->getID()); } void ProtocolGame::parseOpenChannel(NetworkMessage& msg) { - uint16_t channelId = msg.GetU16(); + uint16_t channelId = msg.get(); addGameTask(&Game::playerOpenChannel, player->getID(), channelId); } void ProtocolGame::parseCloseChannel(NetworkMessage& msg) { - uint16_t channelId = msg.GetU16(); + uint16_t channelId = msg.get(); addGameTask(&Game::playerCloseChannel, player->getID(), channelId); } -void ProtocolGame::parseOpenPriv(NetworkMessage& msg) +void ProtocolGame::parseOpenPrivate(NetworkMessage& msg) { - const std::string receiver = msg.GetString(); + const std::string receiver = msg.getString(); addGameTask(&Game::playerOpenPrivateChannel, player->getID(), receiver); } -void ProtocolGame::parseProcessRuleViolation(NetworkMessage& msg) -{ - const std::string reporter = msg.GetString(); - addGameTask(&Game::playerProcessRuleViolation, player->getID(), reporter); -} - -void ProtocolGame::parseCloseRuleViolation(NetworkMessage& msg) -{ - const std::string reporter = msg.GetString(); - addGameTask(&Game::playerCloseRuleViolation, player->getID(), reporter); -} - -void ProtocolGame::parseCancelRuleViolation(NetworkMessage& msg) -{ - addGameTask(&Game::playerCancelRuleViolation, player->getID()); -} - -void ProtocolGame::parseCloseNpc(NetworkMessage& msg) +void ProtocolGame::parseCloseNpc(NetworkMessage&) { addGameTask(&Game::playerCloseNpcChannel, player->getID()); } -void ProtocolGame::parseCancelMove(NetworkMessage& msg) +void ProtocolGame::parseCancelMove(NetworkMessage&) { addGameTask(&Game::playerCancelAttackAndFollow, player->getID()); } -void ProtocolGame::parseReceivePing(NetworkMessage& msg) +void ProtocolGame::parseReceivePing(NetworkMessage&) { addGameTask(&Game::playerReceivePing, player->getID()); } void ProtocolGame::parseAutoWalk(NetworkMessage& msg) { - // first we get all directions... + uint8_t dirCount = msg.get(); + if(dirCount > 128) //client limit + { + for(uint8_t i = 0; i < dirCount; ++i) + msg.get(); + + std::stringstream s; + s << "Attempt to auto walk for " << (uint16_t)dirCount << " steps - client is limited to 128 steps."; + Logger::getInstance()->eFile("bots/" + player->getName() + ".log", s.str(), true); + return; + } + std::list path; - size_t dirCount = msg.GetByte(); - for(size_t i = 0; i < dirCount; ++i) + for(uint8_t i = 0; i < dirCount; ++i) { - uint8_t rawDir = msg.GetByte(); Direction dir = SOUTH; - switch(rawDir) + switch(msg.get()) { case 1: dir = EAST; @@ -1137,7 +1126,6 @@ void ProtocolGame::parseAutoWalk(NetworkMessage& msg) dir = SOUTHWEST; break; case 7: - dir = SOUTH; break; case 8: dir = SOUTHEAST; @@ -1152,17 +1140,17 @@ void ProtocolGame::parseAutoWalk(NetworkMessage& msg) addGameTask(&Game::playerAutoWalk, player->getID(), path); } -void ProtocolGame::parseMove(NetworkMessage& msg, Direction dir) +void ProtocolGame::parseMove(NetworkMessage&, Direction dir) { addGameTask(&Game::playerMove, player->getID(), dir); } -void ProtocolGame::parseTurn(NetworkMessage& msg, Direction dir) +void ProtocolGame::parseTurn(NetworkMessage&, Direction dir) { addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), dir); } -void ProtocolGame::parseRequestOutfit(NetworkMessage& msg) +void ProtocolGame::parseRequestOutfit(NetworkMessage&) { addGameTask(&Game::playerRequestOutfit, player->getID()); } @@ -1171,46 +1159,57 @@ void ProtocolGame::parseSetOutfit(NetworkMessage& msg) { Outfit_t newOutfit = player->defaultOutfit; if(g_config.getBool(ConfigManager::ALLOW_CHANGEOUTFIT)) - newOutfit.lookType = msg.GetU16(); + newOutfit.lookType = msg.get(); else - msg.SkipBytes(2); + msg.skip(2); if(g_config.getBool(ConfigManager::ALLOW_CHANGECOLORS)) { - newOutfit.lookHead = msg.GetByte(); - newOutfit.lookBody = msg.GetByte(); - newOutfit.lookLegs = msg.GetByte(); - newOutfit.lookFeet = msg.GetByte(); + newOutfit.lookHead = msg.get(); + newOutfit.lookBody = msg.get(); + newOutfit.lookLegs = msg.get(); + newOutfit.lookFeet = msg.get(); } else - msg.SkipBytes(4); + msg.skip(4); if(g_config.getBool(ConfigManager::ALLOW_CHANGEADDONS)) - newOutfit.lookAddons = msg.GetByte(); + newOutfit.lookAddons = msg.get(); else - msg.SkipBytes(1); + msg.skip(1); + + if(g_config.getBool(ConfigManager::ALLOW_MOUNTS)) + newOutfit.lookMount = msg.get(); + else + msg.skip(2); addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit); } +void ProtocolGame::parseMountStatus(NetworkMessage& msg) +{ + bool status = msg.get() != (char)0; + addGameTask(&Game::playerChangeMountStatus, player->getID(), status); +} + void ProtocolGame::parseUseItem(NetworkMessage& msg) { - Position pos = msg.GetPosition(); - uint16_t spriteId = msg.GetSpriteId(); - int16_t stackpos = msg.GetByte(); - uint8_t index = msg.GetByte(); + Position pos = msg.getPosition(); + uint16_t spriteId = msg.get(); + int16_t stackpos = msg.get(); + uint8_t index = msg.get(); bool isHotkey = (pos.x == 0xFFFF && !pos.y && !pos.z); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItem, player->getID(), pos, stackpos, index, spriteId, isHotkey); } void ProtocolGame::parseUseItemEx(NetworkMessage& msg) { - Position fromPos = msg.GetPosition(); - uint16_t fromSpriteId = msg.GetSpriteId(); - int16_t fromStackpos = msg.GetByte(); - Position toPos = msg.GetPosition(); - uint16_t toSpriteId = msg.GetU16(); - int16_t toStackpos = msg.GetByte(); + Position fromPos = msg.getPosition(); + uint16_t fromSpriteId = msg.get(); + int16_t fromStackpos = msg.get(); + Position toPos = msg.getPosition(); + uint16_t toSpriteId = msg.get(); + int16_t toStackpos = msg.get(); bool isHotkey = (fromPos.x == 0xFFFF && !fromPos.y && !fromPos.z); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItemEx, player->getID(), fromPos, fromStackpos, fromSpriteId, toPos, toStackpos, toSpriteId, isHotkey); @@ -1218,45 +1217,45 @@ void ProtocolGame::parseUseItemEx(NetworkMessage& msg) void ProtocolGame::parseBattleWindow(NetworkMessage& msg) { - Position fromPos = msg.GetPosition(); - uint16_t spriteId = msg.GetSpriteId(); - int16_t fromStackpos = msg.GetByte(); - uint32_t creatureId = msg.GetU32(); + Position fromPos = msg.getPosition(); + uint16_t spriteId = msg.get(); + int16_t fromStackpos = msg.get(); + uint32_t creatureId = msg.get(); bool isHotkey = (fromPos.x == 0xFFFF && !fromPos.y && !fromPos.z); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseBattleWindow, player->getID(), fromPos, fromStackpos, creatureId, spriteId, isHotkey); } void ProtocolGame::parseCloseContainer(NetworkMessage& msg) { - uint8_t cid = msg.GetByte(); + uint8_t cid = msg.get(); addGameTask(&Game::playerCloseContainer, player->getID(), cid); } void ProtocolGame::parseUpArrowContainer(NetworkMessage& msg) { - uint8_t cid = msg.GetByte(); + uint8_t cid = msg.get(); addGameTask(&Game::playerMoveUpContainer, player->getID(), cid); } void ProtocolGame::parseUpdateTile(NetworkMessage& msg) { - Position pos = msg.GetPosition(); - //addGameTask(&Game::playerUpdateTile, player->getID(), pos); + Position pos = msg.getPosition(); + addGameTask(&Game::playerUpdateTile, player->getID(), pos); } void ProtocolGame::parseUpdateContainer(NetworkMessage& msg) { - uint8_t cid = msg.GetByte(); + uint8_t cid = msg.get(); addGameTask(&Game::playerUpdateContainer, player->getID(), cid); } void ProtocolGame::parseThrow(NetworkMessage& msg) { - Position fromPos = msg.GetPosition(); - uint16_t spriteId = msg.GetSpriteId(); - int16_t fromStackpos = msg.GetByte(); - Position toPos = msg.GetPosition(); - uint8_t count = msg.GetByte(); + Position fromPos = msg.getPosition(); + uint16_t spriteId = msg.get(); + int16_t fromStackpos = msg.get(); + Position toPos = msg.getPosition(); + uint8_t count = msg.get(); if(toPos != fromPos) addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerMoveThing, player->getID(), fromPos, spriteId, fromStackpos, toPos, count); @@ -1264,9 +1263,9 @@ void ProtocolGame::parseThrow(NetworkMessage& msg) void ProtocolGame::parseLookAt(NetworkMessage& msg) { - Position pos = msg.GetPosition(); - uint16_t spriteId = msg.GetSpriteId(); - int16_t stackpos = msg.GetByte(); + Position pos = msg.getPosition(); + uint16_t spriteId = msg.get(); + int16_t stackpos = msg.get(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookAt, player->getID(), pos, spriteId, stackpos); } @@ -1275,32 +1274,30 @@ void ProtocolGame::parseSay(NetworkMessage& msg) std::string receiver; uint16_t channelId = 0; - SpeakClasses type = (SpeakClasses)msg.GetByte(); + MessageClasses type = (MessageClasses)msg.get(); switch(type) { - case SPEAK_PRIVATE: - case SPEAK_PRIVATE_RED: - case SPEAK_RVR_ANSWER: - receiver = msg.GetString(); + case MSG_PRIVATE_TO: + case MSG_GAMEMASTER_PRIVATE_TO: + receiver = msg.getString(); break; - case SPEAK_CHANNEL_Y: - case SPEAK_CHANNEL_RN: - case SPEAK_CHANNEL_RA: - channelId = msg.GetU16(); + case MSG_CHANNEL: + case MSG_CHANNEL_HIGHLIGHT: + case MSG_GAMEMASTER_CHANNEL: + channelId = msg.get(); break; default: break; } - const std::string text = msg.GetString(); + const std::string text = msg.getString(); if(text.length() > 255) //client limit { std::stringstream s; - s << text.length(); - - Logger::getInstance()->eFile("bots/" + player->getName() + ".log", "Attempt to send message with size " + s.str() + " - client is limited to 255 characters.", true); + s << "Attempt to send message with size " << text.length() << " - client is limited to 255 characters."; + Logger::getInstance()->eFile("bots/" + player->getName() + ".log", s.str(), true); return; } @@ -1309,9 +1306,9 @@ void ProtocolGame::parseSay(NetworkMessage& msg) void ProtocolGame::parseFightModes(NetworkMessage& msg) { - uint8_t rawFightMode = msg.GetByte(); //1 - offensive, 2 - balanced, 3 - defensive - uint8_t rawChaseMode = msg.GetByte(); //0 - stand while fightning, 1 - chase opponent - uint8_t rawSecureMode = msg.GetByte(); //0 - can't attack unmarked, 1 - can attack unmarked + uint8_t rawFightMode = msg.get(); //1 - offensive, 2 - balanced, 3 - defensive + uint8_t rawChaseMode = msg.get(); //0 - stand while fightning, 1 - chase opponent + uint8_t rawSecureMode = msg.get(); //0 - can't attack unmarked, 1 - can attack unmarked chaseMode_t chaseMode = CHASEMODE_STANDSTILL; if(rawChaseMode == 1) @@ -1332,79 +1329,83 @@ void ProtocolGame::parseFightModes(NetworkMessage& msg) void ProtocolGame::parseAttack(NetworkMessage& msg) { - uint32_t creatureId = msg.GetU32(); + uint32_t creatureId = msg.get(); + msg.get(); //? + msg.get(); //? + addGameTask(&Game::playerSetAttackedCreature, player->getID(), creatureId); } void ProtocolGame::parseFollow(NetworkMessage& msg) { - uint32_t creatureId = msg.GetU32(); + uint32_t creatureId = msg.get(); addGameTask(&Game::playerFollowCreature, player->getID(), creatureId); } void ProtocolGame::parseTextWindow(NetworkMessage& msg) { - uint32_t windowTextId = msg.GetU32(); - const std::string newText = msg.GetString(); + uint32_t windowTextId = msg.get(); + const std::string newText = msg.getString(); addGameTask(&Game::playerWriteItem, player->getID(), windowTextId, newText); } void ProtocolGame::parseHouseWindow(NetworkMessage &msg) { - uint8_t doorId = msg.GetByte(); - uint32_t id = msg.GetU32(); - const std::string text = msg.GetString(); + uint8_t doorId = msg.get(); + uint32_t id = msg.get(); + const std::string text = msg.getString(); addGameTask(&Game::playerUpdateHouseWindow, player->getID(), doorId, id, text); } void ProtocolGame::parseLookInShop(NetworkMessage &msg) { - uint16_t id = msg.GetU16(); - uint16_t count = msg.GetByte(); + uint16_t id = msg.get(); + uint8_t count = msg.get(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInShop, player->getID(), id, count); } void ProtocolGame::parsePlayerPurchase(NetworkMessage &msg) { - uint16_t id = msg.GetU16(); - uint16_t count = msg.GetByte(); - uint16_t amount = msg.GetByte(); - bool ignoreCap = msg.GetByte(); - bool inBackpacks = msg.GetByte(); + uint16_t id = msg.get(); + uint8_t count = msg.get(); + uint8_t amount = msg.get(); + bool ignoreCap = (msg.get() != (char)0); + bool inBackpacks = (msg.get() != (char)0); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerPurchaseItem, player->getID(), id, count, amount, ignoreCap, inBackpacks); } void ProtocolGame::parsePlayerSale(NetworkMessage &msg) { - uint16_t id = msg.GetU16(); - uint16_t count = msg.GetByte(); - uint16_t amount = msg.GetByte(); - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerSellItem, player->getID(), id, count, amount); + uint16_t id = msg.get(); + uint8_t count = msg.get(); + uint8_t amount = msg.get(); + bool ignoreEquipped = (msg.get() != (char)0); + addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerSellItem, player->getID(), id, count, amount, ignoreEquipped); } -void ProtocolGame::parseCloseShop(NetworkMessage &msg) +void ProtocolGame::parseCloseShop(NetworkMessage&) { addGameTask(&Game::playerCloseShop, player->getID()); } void ProtocolGame::parseRequestTrade(NetworkMessage& msg) { - Position pos = msg.GetPosition(); - uint16_t spriteId = msg.GetSpriteId(); - int16_t stackpos = msg.GetByte(); - uint32_t playerId = msg.GetU32(); + Position pos = msg.getPosition(); + uint16_t spriteId = msg.get(); + int16_t stackpos = msg.get(); + uint32_t playerId = msg.get(); addGameTask(&Game::playerRequestTrade, player->getID(), pos, stackpos, playerId, spriteId); } -void ProtocolGame::parseAcceptTrade(NetworkMessage& msg) +void ProtocolGame::parseAcceptTrade(NetworkMessage&) { addGameTask(&Game::playerAcceptTrade, player->getID()); } void ProtocolGame::parseLookInTrade(NetworkMessage& msg) { - bool counter = msg.GetByte(); - int32_t index = msg.GetByte(); + bool counter = (msg.get() != (char)0); + int32_t index = msg.get(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInTrade, player->getID(), counter, index); } @@ -1415,7 +1416,7 @@ void ProtocolGame::parseCloseTrade() void ProtocolGame::parseAddVip(NetworkMessage& msg) { - const std::string name = msg.GetString(); + const std::string name = msg.getString(); if(name.size() > 32) return; @@ -1424,15 +1425,15 @@ void ProtocolGame::parseAddVip(NetworkMessage& msg) void ProtocolGame::parseRemoveVip(NetworkMessage& msg) { - uint32_t guid = msg.GetU32(); + uint32_t guid = msg.get(); addGameTask(&Game::playerRequestRemoveVip, player->getID(), guid); } void ProtocolGame::parseRotateItem(NetworkMessage& msg) { - Position pos = msg.GetPosition(); - uint16_t spriteId = msg.GetSpriteId(); - int16_t stackpos = msg.GetByte(); + Position pos = msg.getPosition(); + uint16_t spriteId = msg.get(); + int16_t stackpos = msg.get(); addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerRotateItem, player->getID(), pos, stackpos, spriteId); } @@ -1443,77 +1444,130 @@ void ProtocolGame::parseDebugAssert(NetworkMessage& msg) std::stringstream s; s << "----- " << formatDate() << " - " << player->getName() << " (" << convertIPAddress(getIP()) - << ") -----" << std::endl << msg.GetString() << std::endl << msg.GetString() - << std::endl << msg.GetString() << std::endl << msg.GetString() - << std::endl << std::endl; + << ") -----" << std::endl + << msg.getString() << std::endl + << msg.getString() << std::endl + << msg.getString() << std::endl + << msg.getString() << std::endl + << std::endl; m_debugAssertSent = true; - Logger::getInstance()->iFile(LOGFILE_CLIENT_ASSERTION, s.str(), false); + Logger::getInstance()->iFile(LOGFILE_ASSERTIONS, s.str(), false); } void ProtocolGame::parseBugReport(NetworkMessage& msg) { - std::string comment = msg.GetString(); + std::string comment = msg.getString(); addGameTask(&Game::playerReportBug, player->getID(), comment); } +void ProtocolGame::parseThankYou(NetworkMessage& msg) +{ + uint32_t statementId = msg.get(); + addGameTask(&Game::playerThankYou, player->getID(), statementId); +} + void ProtocolGame::parseInviteToParty(NetworkMessage& msg) { - uint32_t targetId = msg.GetU32(); + uint32_t targetId = msg.get(); addGameTask(&Game::playerInviteToParty, player->getID(), targetId); } void ProtocolGame::parseJoinParty(NetworkMessage& msg) { - uint32_t targetId = msg.GetU32(); + uint32_t targetId = msg.get(); addGameTask(&Game::playerJoinParty, player->getID(), targetId); } void ProtocolGame::parseRevokePartyInvite(NetworkMessage& msg) { - uint32_t targetId = msg.GetU32(); + uint32_t targetId = msg.get(); addGameTask(&Game::playerRevokePartyInvitation, player->getID(), targetId); } void ProtocolGame::parsePassPartyLeadership(NetworkMessage& msg) { - uint32_t targetId = msg.GetU32(); + uint32_t targetId = msg.get(); addGameTask(&Game::playerPassPartyLeadership, player->getID(), targetId); } -void ProtocolGame::parseLeaveParty(NetworkMessage& msg) +void ProtocolGame::parseLeaveParty(NetworkMessage&) { - addGameTask(&Game::playerLeaveParty, player->getID()); + addGameTask(&Game::playerLeaveParty, player->getID(), false); } void ProtocolGame::parseSharePartyExperience(NetworkMessage& msg) { - bool activate = msg.GetByte(); - uint8_t unknown = msg.GetByte(); //TODO: find out what is this byte - addGameTask(&Game::playerSharePartyExperience, player->getID(), activate, unknown); + bool activate = (msg.get() != (char)0); + addGameTask(&Game::playerSharePartyExperience, player->getID(), activate); } -void ProtocolGame::parseQuests(NetworkMessage& msg) +void ProtocolGame::parseQuests(NetworkMessage&) { addGameTask(&Game::playerQuests, player->getID()); } void ProtocolGame::parseQuestInfo(NetworkMessage& msg) { - uint16_t questId = msg.GetU16(); + uint16_t questId = msg.get(); addGameTask(&Game::playerQuestInfo, player->getID(), questId); } -void ProtocolGame::parseViolationWindow(NetworkMessage& msg) +void ProtocolGame::parseViolationReport(NetworkMessage& msg) +{ + ReportType_t type = (ReportType_t)msg.get(); + uint8_t reason = msg.get(); + + std::string name = msg.getString(), comment = msg.getString(), translation = ""; + if(type != REPORT_BOT) + translation = msg.getString(); + + uint32_t statementId = 0; + if(type == REPORT_STATEMENT) + statementId = msg.get(); + + addGameTask(&Game::playerReportViolation, player->getID(), type, reason, name, comment, translation, statementId); +} + +void ProtocolGame::parseMarketLeave() +{ + addGameTask(&Game::playerLeaveMarket, player->getID()); +} + +void ProtocolGame::parseMarketBrowse(NetworkMessage& msg) +{ + uint16_t browseId = msg.get(); + if(browseId == MARKETREQUEST_OWN_OFFERS) + addGameTask(&Game::playerBrowseMarketOwnOffers, player->getID()); + else if(browseId == MARKETREQUEST_OWN_HISTORY) + addGameTask(&Game::playerBrowseMarketOwnHistory, player->getID()); + else + addGameTask(&Game::playerBrowseMarket, player->getID(), browseId); +} + +void ProtocolGame::parseMarketCreateOffer(NetworkMessage& msg) +{ + uint8_t type = msg.get(); + uint16_t spriteId = msg.get(); + uint16_t amount = msg.get(); + uint32_t price = msg.get(); + bool anonymous = (msg.get() != 0); + addGameTask(&Game::playerCreateMarketOffer, player->getID(), type, spriteId, amount, price, anonymous); +} + +void ProtocolGame::parseMarketCancelOffer(NetworkMessage& msg) +{ + uint32_t timestamp = msg.get(); + uint16_t counter = msg.get(); + addGameTask(&Game::playerCancelMarketOffer, player->getID(), timestamp, counter); +} + +void ProtocolGame::parseMarketAcceptOffer(NetworkMessage& msg) { - std::string target = msg.GetString(); - uint8_t reason = msg.GetByte(); - ViolationAction_t action = (ViolationAction_t)msg.GetByte(); - std::string comment = msg.GetString(); - std::string statement = msg.GetString(); - uint32_t statementId = (uint32_t)msg.GetU16(); - bool ipBanishment = msg.GetByte(); - addGameTask(&Game::playerViolationWindow, player->getID(), target, reason, action, comment, statement, statementId, ipBanishment); + uint32_t timestamp = msg.get(); + uint16_t counter = msg.get(); + uint16_t amount = msg.get(); + addGameTask(&Game::playerAcceptMarketOffer, player->getID(), timestamp, counter, amount); } //********************** Send methods *******************************// @@ -1523,8 +1577,21 @@ void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xAD); - msg->AddString(receiver); + msg->put(0xAD); + msg->putString(receiver); + } +} + +void ProtocolGame::sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(msg) + { + TRACK_MESSAGE(msg); + msg->put(0xF3); + msg->put(channelId); + msg->putString(playerName); + msg->put(channelEvent); } } @@ -1537,8 +1604,8 @@ void ProtocolGame::sendCreatureOutfit(const Creature* creature, const Outfit_t& if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x8E); - msg->AddU32(creature->getID()); + msg->put(0x8E); + msg->put(creature->getID()); AddCreatureOutfit(msg, creature, outfit); } } @@ -1566,6 +1633,21 @@ void ProtocolGame::sendWorldLight(const LightInfo& lightInfo) } } +void ProtocolGame::sendCreatureWalkthrough(const Creature* creature, bool walkthrough) +{ + if(!canSee(creature)) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(msg) + { + TRACK_MESSAGE(msg); + msg->put(0x92); + msg->put(creature->getID()); + msg->put(!walkthrough); + } +} + void ProtocolGame::sendCreatureShield(const Creature* creature) { if(!canSee(creature)) @@ -1575,9 +1657,9 @@ void ProtocolGame::sendCreatureShield(const Creature* creature) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x91); - msg->AddU32(creature->getID()); - msg->AddByte(player->getPartyShield(creature)); + msg->put(0x91); + msg->put(creature->getID()); + msg->put(player->getPartyShield(creature)); } } @@ -1590,13 +1672,13 @@ void ProtocolGame::sendCreatureSkull(const Creature* creature) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x90); - msg->AddU32(creature->getID()); - msg->AddByte(player->getSkullClient(creature)); + msg->put(0x90); + msg->put(creature->getID()); + msg->put(player->getSkullType(creature)); } } -void ProtocolGame::sendCreatureSquare(const Creature* creature, SquareColor_t color) +void ProtocolGame::sendCreatureSquare(const Creature* creature, uint8_t color) { if(!canSee(creature)) return; @@ -1605,9 +1687,9 @@ void ProtocolGame::sendCreatureSquare(const Creature* creature, SquareColor_t co if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x86); - msg->AddU32(creature->getID()); - msg->AddByte((uint8_t)color); + msg->put(0x86); + msg->put(creature->getID()); + msg->put(color); } } @@ -1617,8 +1699,8 @@ void ProtocolGame::sendTutorial(uint8_t tutorialId) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xDC); - msg->AddByte(tutorialId); + msg->put(0xDC); + msg->put(tutorialId); } } @@ -1628,20 +1710,21 @@ void ProtocolGame::sendAddMarker(const Position& pos, MapMarks_t markType, const if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xDD); - msg->AddPosition(pos); - msg->AddByte(markType); - msg->AddString(desc); + msg->put(0xDD); + msg->putPosition(pos); + msg->put(markType); + msg->putString(desc); } } -void ProtocolGame::sendReLoginWindow() +void ProtocolGame::sendReLoginWindow(uint8_t pvpPercent) { NetworkMessage_ptr msg = getOutputBuffer(); if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x28); + msg->put(0x28); + msg->put(pvpPercent); } } @@ -1674,8 +1757,8 @@ void ProtocolGame::sendClosePrivate(uint16_t channelId) if(channelId == CHANNEL_GUILD || channelId == CHANNEL_PARTY) g_chat.removeUserFromChannel(player, channelId); - msg->AddByte(0xB3); - msg->AddU16(channelId); + msg->put(0xB3); + msg->put(channelId); } } @@ -1685,28 +1768,29 @@ void ProtocolGame::sendCreatePrivateChannel(uint16_t channelId, const std::strin if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xB2); - msg->AddU16(channelId); - msg->AddString(channelName); + msg->put(0xB2); + msg->put(channelId); + msg->putString(channelName); + + msg->put(0x01); + msg->putString(player->getName()); + msg->put(0x00); } } -void ProtocolGame::sendChannelsDialog() +void ProtocolGame::sendChannelsDialog(const ChannelsList& channels) { NetworkMessage_ptr msg = getOutputBuffer(); if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xAB); - ChannelList list = g_chat.getChannelList(player); - msg->AddByte(list.size()); - for(ChannelList::iterator it = list.begin(); it != list.end(); ++it) + msg->put(0xAB); + + msg->put(channels.size()); + for(ChannelsList::const_iterator it = channels.begin(); it != channels.end(); ++it) { - if(ChatChannel* channel = (*it)) - { - msg->AddU16(channel->getId()); - msg->AddString(channel->getName()); - } + msg->put(it->first); + msg->putString(it->second); } } } @@ -1717,58 +1801,38 @@ void ProtocolGame::sendChannel(uint16_t channelId, const std::string& channelNam if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xAC); - msg->AddU16(channelId); - msg->AddString(channelName); - } -} + msg->put(0xAC); -void ProtocolGame::sendRuleViolationsChannel(uint16_t channelId) -{ - NetworkMessage_ptr msg = getOutputBuffer(); - if(msg) - { - TRACK_MESSAGE(msg); - msg->AddByte(0xAE); - msg->AddU16(channelId); - for(RuleViolationsMap::const_iterator it = g_game.getRuleViolations().begin(); it != g_game.getRuleViolations().end(); ++it) + msg->put(channelId); + msg->putString(channelName); + if(channelId == CHANNEL_PARTY || channelId == CHANNEL_GUILD || channelId == CHANNEL_PRIVATE) { - RuleViolation& rvr = *it->second; - if(rvr.isOpen && rvr.reporter) - AddCreatureSpeak(msg, rvr.reporter, SPEAK_RVR_CHANNEL, rvr.text, channelId, rvr.time); - } - } -} + if(ChatChannel* channel = g_chat.getChannelById(channelId)) + { + const UsersMap& users = channel->getUsers(); + msg->put(users.size()); + for(UsersMap::const_iterator itt = users.begin(); itt != users.end(); ++itt) + msg->putString(itt->second->getName()); -void ProtocolGame::sendRemoveReport(const std::string& name) -{ - NetworkMessage_ptr msg = getOutputBuffer(); - if(msg) - { - TRACK_MESSAGE(msg); - msg->AddByte(0xAF); - msg->AddString(name); - } -} + if(PrivateChatChannel* privateChannel = dynamic_cast(channel)) + { + const InviteList& invitedUsers = privateChannel->getInvitedUsers(); + msg->put(invitedUsers.size()); + for(InviteList::const_iterator it = invitedUsers.begin(); it != invitedUsers.end(); ++it) + { + if(Player* _player = g_game.getPlayerByID(*it)) + msg->putString(_player->getName()); + } + } + else + msg->put(0x00); -void ProtocolGame::sendRuleViolationCancel(const std::string& name) -{ - NetworkMessage_ptr msg = getOutputBuffer(); - if(msg) - { - TRACK_MESSAGE(msg); - msg->AddByte(0xB0); - msg->AddString(name); - } -} + return; + } + } -void ProtocolGame::sendLockRuleViolation() -{ - NetworkMessage_ptr msg = getOutputBuffer(); - if(msg) - { - TRACK_MESSAGE(msg); - msg->AddByte(0xB1); + msg->put(0x00); + msg->put(0x00); } } @@ -1778,8 +1842,8 @@ void ProtocolGame::sendIcons(int32_t icons) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xA2); - msg->AddU16(icons); + msg->put(0xA2); + msg->put(icons); } } @@ -1789,33 +1853,34 @@ void ProtocolGame::sendContainer(uint32_t cid, const Container* container, bool if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x6E); - msg->AddByte(cid); + msg->put(0x6E); + msg->put(cid); - msg->AddItemId(container); - msg->AddString(container->getName()); - msg->AddByte(container->capacity()); + msg->putItem(container); + msg->putString(container->getName()); + msg->put(container->capacity()); - msg->AddByte(hasParent ? 0x01 : 0x00); - msg->AddByte(std::min(container->size(), (uint32_t)255)); + msg->put(hasParent ? 0x01 : 0x00); + msg->put(std::min(container->size(), 255U)); ItemList::const_iterator cit = container->getItems(); for(uint32_t i = 0; cit != container->getEnd() && i < 255; ++cit, ++i) - msg->AddItem(*cit); + msg->putItem(*cit); } } -void ProtocolGame::sendShop(const ShopInfoList& shop) +void ProtocolGame::sendShop(Npc* npc, const ShopInfoList& shop) { NetworkMessage_ptr msg = getOutputBuffer(); if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x7A); - msg->AddByte(std::min(shop.size(), (size_t)255)); + msg->put(0x7A); + msg->putString(npc->getName()); + msg->put(std::min(shop.size(), (size_t)65535)); ShopInfoList::const_iterator it = shop.begin(); - for(uint32_t i = 0; it != shop.end() && i < 255; ++it, ++i) + for(uint16_t i = 0; it != shop.end() && i < 65535; ++it, ++i) AddShopItem(msg, (*it)); } } @@ -1826,7 +1891,7 @@ void ProtocolGame::sendCloseShop() if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x7C); + msg->put(0x7C); } } @@ -1836,8 +1901,8 @@ void ProtocolGame::sendGoods(const ShopInfoList& shop) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x7B); - msg->AddU32(g_game.getMoney(player)); + msg->put(0x7B); + msg->put((uint32_t)g_game.getMoney(player)); std::map goodsMap; if(shop.size() >= 5) @@ -1870,81 +1935,515 @@ void ProtocolGame::sendGoods(const ShopInfoList& shop) continue; int8_t subType = -1; - if(sit->subType) - { - const ItemType& it = Item::items[sit->itemId]; - if(it.hasSubType() && !it.stackable) - subType = sit->subType; - } + const ItemType& it = Item::items[sit->itemId]; + if(sit->subType && it.hasSubType() && !it.stackable) + subType = sit->subType; if(subType != -1) { - uint32_t count = player->__getItemTypeCount(sit->itemId, subType); + uint32_t count = subType; + if(!it.isFluidContainer() && !it.isSplash()) + count = player->__getItemTypeCount(sit->itemId, subType); + if(count > 0) goodsMap[sit->itemId] = count; + else + goodsMap[sit->itemId] = 0; } else goodsMap[sit->itemId] = tmpMap[sit->itemId]; } } - msg->AddByte(std::min(goodsMap.size(), (size_t)255)); + msg->put(std::min(goodsMap.size(), (size_t)255)); std::map::const_iterator it = goodsMap.begin(); for(uint32_t i = 0; it != goodsMap.end() && i < 255; ++it, ++i) { - msg->AddItemId(it->first); - msg->AddByte(std::min(it->second, (uint32_t)255)); + msg->putItemId(it->first); + msg->put(std::min(it->second, (uint32_t)255)); } } } -void ProtocolGame::sendTradeItemRequest(const Player* player, const Item* item, bool ack) +void ProtocolGame::sendMarketEnter(uint32_t depotId) { NetworkMessage_ptr msg = getOutputBuffer(); - if(msg) - { - TRACK_MESSAGE(msg); - if(ack) - msg->AddByte(0x7D); - else - msg->AddByte(0x7E); + if(!msg) + return; - msg->AddString(player->getName()); - if(const Container* container = item->getContainer()) - { - msg->AddByte(container->getItemHoldingCount() + 1); - msg->AddItem(item); - for(ContainerIterator it = container->begin(); it != container->end(); ++it) - msg->AddItem(*it); - } - else - { - msg->AddByte(1); - msg->AddItem(item); - } - } -} + TRACK_MESSAGE(msg); + msg->put(0xF6); + msg->put(std::min((uint64_t)0xFFFFFFFF, player->balance)); + if(Vocation* vocation = player->getVocation()) + msg->put(vocation->getClientId()); + else + msg->put(0x00); -void ProtocolGame::sendCloseTrade() -{ - NetworkMessage_ptr msg = getOutputBuffer(); - if(msg) + msg->put(std::min((int32_t)0xFF, IOMarket::getInstance()->getPlayerOfferCount(player->getGUID()))); + + Depot* depot = player->getDepot(depotId, false); + if(!depot) { - TRACK_MESSAGE(msg); - msg->AddByte(0x7F); + msg->put(0x00); + return; } -} -void ProtocolGame::sendCloseContainer(uint32_t cid) -{ - NetworkMessage_ptr msg = getOutputBuffer(); - if(msg) + player->setMarketDepotId(depotId); + + std::map depotItems; + std::list containerList; + containerList.push_back(depot->getLocker()); + do { - TRACK_MESSAGE(msg); - msg->AddByte(0x6F); - msg->AddByte(cid); - } -} + Container* container = containerList.front(); + containerList.pop_front(); + for(ItemList::const_iterator it = container->getItems(), end = container->getEnd(); it != end; ++it) + { + Item* item = *it; + + Container* c = item->getContainer(); + if(c && !c->empty()) + { + containerList.push_back(c); + continue; + } + + const ItemType& itemType = Item::items[item->getID()]; + if(itemType.wareId == 0) + continue; + + if(!itemType.isRune() && item->getCharges() != itemType.charges) + continue; + + if(item->getDuration() != (int32_t)itemType.decayTime) + continue; + + depotItems[item->getID()] += Item::countByType(item, -1); + } + } + while(!containerList.empty()); + + msg->put(std::min((size_t)0xFFFF, depotItems.size())); + + uint16_t i = 0; + for(std::map::const_iterator it = depotItems.begin(), end = depotItems.end(); it != end && i < 65535; ++it, ++i) + { + msg->putItemId(it->first); + msg->put(std::min((uint32_t)0xFFFF, it->second)); + } +} + +void ProtocolGame::sendMarketLeave() +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(0xF7); +} + +void ProtocolGame::sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(0xF9); + msg->putItemId(itemId); + + msg->put(buyOffers.size()); + for(MarketOfferList::const_iterator it = buyOffers.begin(), end = buyOffers.end(); it != end; ++it) + { + msg->put(it->timestamp); + msg->put(it->counter); + msg->put(it->amount); + msg->put(it->price); + msg->putString(it->playerName); + } + + msg->put(sellOffers.size()); + for(MarketOfferList::const_iterator it = sellOffers.begin(), end = sellOffers.end(); it != end; ++it) + { + msg->put(it->timestamp); + msg->put(it->counter); + msg->put(it->amount); + msg->put(it->price); + msg->putString(it->playerName); + } +} + +void ProtocolGame::sendMarketAcceptOffer(MarketOfferEx offer) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(0xF9); + msg->putItemId(offer.itemId); + + if(offer.type == MARKETACTION_BUY) + { + msg->put(0x01); + msg->put(offer.timestamp); + msg->put(offer.counter); + msg->put(offer.amount); + msg->put(offer.price); + msg->putString(offer.playerName); + msg->put(0x00); + } + else + { + msg->put(0x00); + msg->put(0x01); + msg->put(offer.timestamp); + msg->put(offer.counter); + msg->put(offer.amount); + msg->put(offer.price); + msg->putString(offer.playerName); + } +} + +void ProtocolGame::sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(0xF9); + msg->put(MARKETREQUEST_OWN_OFFERS); + + msg->put(buyOffers.size()); + for(MarketOfferList::const_iterator it = buyOffers.begin(), end = buyOffers.end(); it != end; ++it) + { + msg->put(it->timestamp); + msg->put(it->counter); + msg->putItemId(it->itemId); + msg->put(it->amount); + msg->put(it->price); + } + + msg->put(sellOffers.size()); + for(MarketOfferList::const_iterator it = sellOffers.begin(), end = sellOffers.end(); it != end; ++it) + { + msg->put(it->timestamp); + msg->put(it->counter); + msg->putItemId(it->itemId); + msg->put(it->amount); + msg->put(it->price); + } +} + +void ProtocolGame::sendMarketCancelOffer(MarketOfferEx offer) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(0xF9); + msg->put(MARKETREQUEST_OWN_OFFERS); + + if(offer.type == MARKETACTION_BUY) + { + msg->put(0x01); + msg->put(offer.timestamp); + msg->put(offer.counter); + msg->putItemId(offer.itemId); + msg->put(offer.amount); + msg->put(offer.price); + msg->put(0x00); + } + else + { + msg->put(0x00); + msg->put(0x01); + msg->put(offer.timestamp); + msg->put(offer.counter); + msg->putItemId(offer.itemId); + msg->put(offer.amount); + msg->put(offer.price); + } +} + +void ProtocolGame::sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(0xF9); + msg->put(MARKETREQUEST_OWN_HISTORY); + + std::map counterMap; + + msg->put(buyOffers.size()); + for(HistoryMarketOfferList::const_iterator it = buyOffers.begin(), end = buyOffers.end(); it != end; ++it) + { + msg->put(it->timestamp); + msg->put(counterMap[it->timestamp]++); + msg->putItemId(it->itemId); + msg->put(it->amount); + msg->put(it->price); + msg->put(it->state); + } + + counterMap.clear(); + + msg->put(sellOffers.size()); + for(HistoryMarketOfferList::const_iterator it = sellOffers.begin(), end = sellOffers.end(); it != end; ++it) + { + msg->put(it->timestamp); + msg->put(counterMap[it->timestamp]++); + msg->putItemId(it->itemId); + msg->put(it->amount); + msg->put(it->price); + msg->put(it->state); + } +} + +void ProtocolGame::sendMarketDetail(uint16_t itemId) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(!msg) + return; + + TRACK_MESSAGE(msg); + msg->put(0xF8); + msg->putItemId(itemId); + + const ItemType& it = Item::items[itemId]; + std::stringstream ss; + if(it.armor != 0) + { + ss << it.armor; + msg->putString(ss.str()); + } + else + msg->put(0x00); + + if(it.attack != 0) + { + ss.str(""); + ss << it.attack; + msg->putString(ss.str()); + } + else + msg->put(0x00); + + if(it.isContainer()) + { + ss.str(""); + ss << it.maxItems; + msg->putString(ss.str()); + } + else + msg->put(0x00); + + if(it.defense != 0) + { + ss.str(""); + ss << it.defense; + if(it.extraDefense != 0) + ss << " +" << it.extraDefense; + + msg->putString(ss.str()); + } + else + msg->put(0x00); + + if(!it.description.empty()) + { + std::string descr = it.description; + if(descr[descr.length() - 1] == '.') + descr.erase(descr.length() - 1); + + msg->putString(descr); + } + else + msg->put(0x00); + + if(it.decayTime != 0) + { + ss.str(""); + ss << it.decayTime << " seconds"; + msg->putString(ss.str()); + } + else + msg->put(0x00); + + ss.str(""); + if(it.hasAbilities()) + { + bool separator = false; + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) + { + if(!it.abilities->absorb[i]) + continue; + + if(separator) + ss << ", "; + else + separator = true; + + ss << getCombatName((CombatType_t)i) << " " << std::showpos << it.abilities->absorb[i] << std::noshowpos << "%"; + } + } + msg->putString(ss.str()); + + if(it.minReqLevel != 0) + { + ss.str(""); + ss << it.minReqLevel; + msg->putString(ss.str()); + } + else + msg->put(0x00); + + if(it.minReqMagicLevel != 0) + { + ss.str(""); + ss << it.minReqMagicLevel; + msg->putString(ss.str()); + } + else + msg->put(0x00); + + msg->putString(it.vocationString); + + msg->putString(it.runeSpellName); + + ss.str(""); + if(it.hasAbilities()) + { + bool separator = false; + for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; i++) + { + if(!it.abilities->skills[i]) + continue; + + if(separator) + ss << ", "; + else + separator = true; + + ss << getSkillName(i) << " " << std::showpos << it.abilities->skills[i] << std::noshowpos; + } + + if(it.abilities->speed != 0) + { + if(separator) + ss << ", "; + + ss << "speed" << " " << std::showpos << (int32_t)(it.abilities->speed / 2) << std::noshowpos; + } + } + msg->putString(ss.str()); + + if(it.charges != 0) + { + ss.str(""); + ss << it.charges; + msg->putString(ss.str()); + } + else + msg->put(0x00); + + std::string weaponName = getWeaponName(it.weaponType); + if(it.slotPosition & SLOTP_TWO_HAND) + { + if(!weaponName.empty()) + weaponName += ", two-handed"; + else + weaponName = "two-handed"; + } + msg->putString(weaponName); + + if(it.weight > 0) + { + ss.str(""); + ss << std::fixed << std::setprecision(2) << it.weight << " oz"; + msg->putString(ss.str()); + } + else + msg->put(0x00); + + MarketStatistics* statistics = IOMarket::getInstance()->getPurchaseStatistics(itemId); + if(statistics) + { + msg->put(0x01); + msg->put(statistics->numTransactions); + msg->put(std::min((uint64_t)0xFFFFFFFF, statistics->totalPrice)); + msg->put(statistics->highestPrice); + msg->put(statistics->lowestPrice); + } + else + msg->put(0x00); + + statistics = IOMarket::getInstance()->getSaleStatistics(itemId); + if(statistics) + { + msg->put(0x01); + msg->put(statistics->numTransactions); + msg->put(std::min((uint64_t)0xFFFFFFFF, statistics->totalPrice)); + msg->put(statistics->highestPrice); + msg->put(statistics->lowestPrice); + } + else + msg->put(0x00); +} + +void ProtocolGame::sendTradeItemRequest(const Player* _player, const Item* item, bool ack) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(msg) + { + TRACK_MESSAGE(msg); + if(ack) + msg->put(0x7D); + else + msg->put(0x7E); + + msg->putString(_player->getName()); + if(const Container* container = item->getContainer()) + { + msg->put(container->getItemHoldingCount() + 1); + msg->putItem(item); + for(ContainerIterator it = container->begin(); it != container->end(); ++it) + msg->putItem(*it); + } + else + { + msg->put(1); + msg->putItem(item); + } + } +} + +void ProtocolGame::sendCloseTrade() +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(msg) + { + TRACK_MESSAGE(msg); + msg->put(0x7F); + } +} + +void ProtocolGame::sendCloseContainer(uint32_t cid) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(msg) + { + TRACK_MESSAGE(msg); + msg->put(0x6F); + msg->put(cid); + } +} void ProtocolGame::sendCreatureTurn(const Creature* creature, int16_t stackpos) { @@ -1955,32 +2454,43 @@ void ProtocolGame::sendCreatureTurn(const Creature* creature, int16_t stackpos) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x6B); - msg->AddPosition(creature->getPosition()); - msg->AddByte(stackpos); - msg->AddU16(0x63); /*99*/ - msg->AddU32(creature->getID()); - msg->AddByte(creature->getDirection()); + msg->put(0x6B); + msg->putPosition(creature->getPosition()); + msg->put(stackpos); + msg->put(0x63); + msg->put(creature->getID()); + msg->put(creature->getDirection()); } } -void ProtocolGame::sendCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, Position* pos/* = NULL*/) +void ProtocolGame::sendCreatureSay(const Creature* creature, MessageClasses type, const std::string& text, Position* pos, uint32_t statementId) { NetworkMessage_ptr msg = getOutputBuffer(); if(msg) { TRACK_MESSAGE(msg); - AddCreatureSpeak(msg, creature, type, text, 0, 0, pos); + AddCreatureSpeak(msg, creature, type, text, 0, pos, statementId); } } -void ProtocolGame::sendToChannel(const Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId, uint32_t time /*= 0*/) +void ProtocolGame::sendCreatureChannelSay(const Creature* creature, MessageClasses type, const std::string& text, uint16_t channelId, uint32_t statementId) { NetworkMessage_ptr msg = getOutputBuffer(); if(msg) { TRACK_MESSAGE(msg); - AddCreatureSpeak(msg, creature, type, text, channelId, time); + AddCreatureSpeak(msg, creature, type, text, channelId, NULL, statementId); + } +} + +void ProtocolGame::sendStatsMessage(MessageClasses type, const std::string& message, + Position pos, MessageDetails* details/* = NULL*/) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(msg) + { + TRACK_MESSAGE(msg); + AddTextMessage(msg, type, message, &pos, details); } } @@ -2000,7 +2510,8 @@ void ProtocolGame::sendCancelTarget() if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xA3); + msg->put(0xA3); + msg->put(0); //? creatureId? } } @@ -2013,9 +2524,9 @@ void ProtocolGame::sendChangeSpeed(const Creature* creature, uint32_t speed) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x8F); - msg->AddU32(creature->getID()); - msg->AddU16(speed); + msg->put(0x8F); + msg->put(creature->getID()); + msg->put(speed); } } @@ -2025,8 +2536,8 @@ void ProtocolGame::sendCancelWalk() if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xB5); - msg->AddByte(player->getDirection()); + msg->put(0xB5); + msg->put(player->getDirection()); } } @@ -2046,7 +2557,7 @@ void ProtocolGame::sendPing() if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x1E); + msg->put(0x1E); } } @@ -2076,19 +2587,6 @@ void ProtocolGame::sendMagicEffect(const Position& pos, uint8_t type) } } -void ProtocolGame::sendAnimatedText(const Position& pos, uint8_t color, std::string text) -{ - if(!canSee(pos)) - return; - - NetworkMessage_ptr msg = getOutputBuffer(); - if(msg) - { - TRACK_MESSAGE(msg); - AddAnimatedText(msg, pos, color, text); - } -} - void ProtocolGame::sendCreatureHealth(const Creature* creature) { if(!canSee(creature)) @@ -2106,7 +2604,7 @@ void ProtocolGame::sendFYIBox(const std::string& message) { if(message.empty() || message.length() > 1018) //Prevent client debug when message is empty or length is > 1018 (not confirmed) { - std::cout << "[Warning - ProtocolGame::sendFYIBox] Trying to send an empty or too huge message." << std::endl; + std::clog << "[Warning - ProtocolGame::sendFYIBox] Trying to send an empty or too huge message." << std::endl; return; } @@ -2114,13 +2612,13 @@ void ProtocolGame::sendFYIBox(const std::string& message) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x15); - msg->AddString(message); + msg->put(0x15); + msg->putString(message); } } //tile -void ProtocolGame::sendAddTileItem(const Tile* tile, const Position& pos, uint32_t stackpos, const Item* item) +void ProtocolGame::sendAddTileItem(const Tile*, const Position& pos, uint32_t stackpos, const Item* item) { if(!canSee(pos)) return; @@ -2133,7 +2631,7 @@ void ProtocolGame::sendAddTileItem(const Tile* tile, const Position& pos, uint32 } } -void ProtocolGame::sendUpdateTileItem(const Tile* tile, const Position& pos, uint32_t stackpos, const Item* item) +void ProtocolGame::sendUpdateTileItem(const Tile*, const Position& pos, uint32_t stackpos, const Item* item) { if(!canSee(pos)) return; @@ -2146,7 +2644,7 @@ void ProtocolGame::sendUpdateTileItem(const Tile* tile, const Position& pos, uin } } -void ProtocolGame::sendRemoveTileItem(const Tile* tile, const Position& pos, uint32_t stackpos) +void ProtocolGame::sendRemoveTileItem(const Tile*, const Position& pos, uint32_t stackpos) { if(!canSee(pos)) return; @@ -2168,18 +2666,18 @@ void ProtocolGame::sendUpdateTile(const Tile* tile, const Position& pos) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x69); - msg->AddPosition(pos); + msg->put(0x69); + msg->putPosition(pos); if(tile) { GetTileDescription(tile, msg); - msg->AddByte(0x00); - msg->AddByte(0xFF); + msg->put(0x00); + msg->put(0xFF); } else { - msg->AddByte(0x01); - msg->AddByte(0xFF); + msg->put(0x01); + msg->put(0xFF); } } } @@ -2200,28 +2698,11 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos return; } - msg->AddByte(0x0A); - msg->AddU32(player->getID()); - msg->AddU16(0x32); + msg->put(0x0A); + msg->put(player->getID()); + msg->put(0x32); - msg->AddByte(player->hasFlag(PlayerFlag_CanReportBugs)); - if(Group* group = player->getGroup()) - { - int32_t reasons = group->getViolationReasons(); - if(reasons > 1) - { - msg->AddByte(0x0B); - for(int32_t i = 0; i < 20; ++i) - { - if(i < 4) - msg->AddByte(group->getNameViolationFlags()); - else if(i < reasons) - msg->AddByte(group->getStatementViolationFlags()); - else - msg->AddByte(0x00); - } - } - } + msg->put(player->hasFlag(PlayerFlag_CanReportBugs)); AddMapDescription(msg, pos); for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) @@ -2230,15 +2711,14 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos AddPlayerStats(msg); AddPlayerSkills(msg); - //gameworld light-settings LightInfo lightInfo; g_game.getWorldLightInfo(lightInfo); - AddWorldLight(msg, lightInfo); - //player light level + AddWorldLight(msg, lightInfo); AddCreatureLight(msg, creature); + player->sendIcons(); - for(VIPListSet::iterator it = player->VIPList.begin(); it != player->VIPList.end(); it++) + for(VIPSet::iterator it = player->VIPList.begin(); it != player->VIPList.end(); ++it) { std::string vipName; if(IOLoginData::getInstance()->getNameByGuid((*it), vipName)) @@ -2249,7 +2729,7 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos } } -void ProtocolGame::sendRemoveCreature(const Creature* creature, const Position& pos, uint32_t stackpos) +void ProtocolGame::sendRemoveCreature(const Creature*, const Position& pos, uint32_t stackpos) { if(!canSee(pos)) return; @@ -2262,8 +2742,8 @@ void ProtocolGame::sendRemoveCreature(const Creature* creature, const Position& } } -void ProtocolGame::sendMoveCreature(const Creature* creature, const Tile* newTile, const Position& newPos, - uint32_t newStackpos, const Tile* oldTile, const Position& oldPos, uint32_t oldStackpos, bool teleport) +void ProtocolGame::sendMoveCreature(const Creature* creature, const Tile*, const Position& newPos, + uint32_t newStackpos, const Tile*, const Position& oldPos, uint32_t oldStackpos, bool teleport) { if(creature == player) { @@ -2280,10 +2760,10 @@ void ProtocolGame::sendMoveCreature(const Creature* creature, const Tile* newTil { if(oldPos.z != 7 || newPos.z < 8) { - msg->AddByte(0x6D); - msg->AddPosition(oldPos); - msg->AddByte(oldStackpos); - msg->AddPosition(newPos); + msg->put(0x6D); + msg->putPosition(oldPos); + msg->put(oldStackpos); + msg->putPosition(newPos); } else RemoveTileItem(msg, oldPos, oldStackpos); @@ -2295,23 +2775,23 @@ void ProtocolGame::sendMoveCreature(const Creature* creature, const Tile* newTil if(oldPos.y > newPos.y) // north, for old x { - msg->AddByte(0x65); + msg->put(0x65); GetMapDescription(oldPos.x - 8, newPos.y - 6, newPos.z, 18, 1, msg); } else if(oldPos.y < newPos.y) // south, for old x { - msg->AddByte(0x67); + msg->put(0x67); GetMapDescription(oldPos.x - 8, newPos.y + 7, newPos.z, 18, 1, msg); } if(oldPos.x < newPos.x) // east, [with new y] { - msg->AddByte(0x66); + msg->put(0x66); GetMapDescription(newPos.x + 9, newPos.y - 6, newPos.z, 1, 14, msg); } else if(oldPos.x > newPos.x) // west, [with new y] { - msg->AddByte(0x68); + msg->put(0x68); GetMapDescription(newPos.x - 8, newPos.y - 6, newPos.z, 1, 14, msg); } } @@ -2328,10 +2808,10 @@ void ProtocolGame::sendMoveCreature(const Creature* creature, const Tile* newTil TRACK_MESSAGE(msg); if(!teleport && (oldPos.z != 7 || newPos.z < 8) && oldStackpos < 10) { - msg->AddByte(0x6D); - msg->AddPosition(oldPos); - msg->AddByte(oldStackpos); - msg->AddPosition(newPos); + msg->put(0x6D); + msg->putPosition(oldPos); + msg->put(oldStackpos); + msg->putPosition(newPos); } else { @@ -2431,63 +2911,45 @@ void ProtocolGame::sendTextWindow(uint32_t windowTextId, Item* item, uint16_t ma if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x96); - msg->AddU32(windowTextId); - msg->AddItemId(item); + msg->put(0x96); + msg->put(windowTextId); + msg->putItemId(item); if(canWrite) { - msg->AddU16(maxLen); - msg->AddString(item->getText()); + msg->put(maxLen); + msg->putString(item->getText()); } else { - msg->AddU16(item->getText().size()); - msg->AddString(item->getText()); + msg->put(item->getText().size()); + msg->putString(item->getText()); } const std::string& writer = item->getWriter(); if(writer.size()) - msg->AddString(writer); + msg->putString(writer); else - msg->AddString(""); + msg->putString(""); time_t writtenDate = item->getDate(); if(writtenDate > 0) - msg->AddString(formatDate(writtenDate)); + msg->putString(formatDate(writtenDate)); else - msg->AddString(""); - } -} - -void ProtocolGame::sendTextWindow(uint32_t windowTextId, uint32_t itemId, const std::string& text) -{ - NetworkMessage_ptr msg = getOutputBuffer(); - if(msg) - { - TRACK_MESSAGE(msg); - msg->AddByte(0x96); - msg->AddU32(windowTextId); - msg->AddItemId(itemId); - - msg->AddU16(text.size()); - msg->AddString(text); - - msg->AddString(""); - msg->AddString(""); + msg->putString(""); } } -void ProtocolGame::sendHouseWindow(uint32_t windowTextId, House* _house, - uint32_t listId, const std::string& text) +void ProtocolGame::sendHouseWindow(uint32_t windowTextId, House*, + uint32_t, const std::string& text) { NetworkMessage_ptr msg = getOutputBuffer(); if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0x97); - msg->AddByte(0x00); - msg->AddU32(windowTextId); - msg->AddString(text); + msg->put(0x97); + msg->put(0x00); + msg->put(windowTextId); + msg->putString(text); } } @@ -2497,7 +2959,7 @@ void ProtocolGame::sendOutfitWindow() if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xC8); + msg->put(0xC8); AddCreatureOutfit(msg, player, player->getDefaultOutfit(), true); std::list outfitList; @@ -2509,28 +2971,52 @@ void ProtocolGame::sendOutfitWindow() if(outfitList.size()) { - msg->AddByte((size_t)std::min((size_t)OUTFITS_MAX_NUMBER, outfitList.size())); - std::list::iterator it = outfitList.begin(); - for(int32_t i = 0; it != outfitList.end() && i < OUTFITS_MAX_NUMBER; ++it, ++i) + msg->put(outfitList.size()); + for(std::list::iterator it = outfitList.begin(); it != outfitList.end(); ++it) { - msg->AddU16(it->lookType); - msg->AddString(it->name); + msg->put(it->lookType); + msg->putString(it->name); if(player->hasCustomFlag(PlayerCustomFlag_CanWearAllAddons)) - msg->AddByte(0x03); + msg->put(0x03); else if(!g_config.getBool(ConfigManager::ADDONS_PREMIUM) || player->isPremium()) - msg->AddByte(it->addons); + msg->put(it->addons); else - msg->AddByte(0x00); + msg->put(0x00); } } else { - msg->AddByte(1); - msg->AddU16(player->getDefaultOutfit().lookType); - msg->AddString("Outfit"); - msg->AddByte(player->getDefaultOutfit().lookAddons); + msg->put(1); + msg->put(player->getDefaultOutfit().lookType); + msg->putString("Your outfit"); + msg->put(player->getDefaultOutfit().lookAddons); } + if(g_config.getBool(ConfigManager::ALLOW_MOUNTS)) + { + std::list mountList; + for(MountList::const_iterator it = Mounts::getInstance()->getFirstMount(); + it != Mounts::getInstance()->getLastMount(); ++it) + { + if((*it)->isTamed(player)) + mountList.push_back((*it)); + } + + if(mountList.size()) + { + msg->put(mountList.size()); + for(std::list::iterator it = mountList.begin(); it != mountList.end(); ++it) + { + msg->put((*it)->getClientId()); + msg->putString((*it)->getName()); + } + } + else + msg->put(0); + } + else + msg->put(0); + player->hasRequestedOutfit(true); } } @@ -2541,17 +3027,17 @@ void ProtocolGame::sendQuests() if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xF0); + msg->put(0xF0); - msg->AddU16(Quests::getInstance()->getQuestCount(player)); + msg->put(Quests::getInstance()->getQuestCount(player)); for(QuestList::const_iterator it = Quests::getInstance()->getFirstQuest(); it != Quests::getInstance()->getLastQuest(); ++it) { if(!(*it)->isStarted(player)) continue; - msg->AddU16((*it)->getId()); - msg->AddString((*it)->getName()); - msg->AddByte((*it)->isCompleted(player)); + msg->put((*it)->getId()); + msg->putString((*it)->getName()); + msg->put((*it)->isCompleted(player)); } } } @@ -2562,17 +3048,17 @@ void ProtocolGame::sendQuestInfo(Quest* quest) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xF1); - msg->AddU16(quest->getId()); + msg->put(0xF1); + msg->put(quest->getId()); - msg->AddByte(quest->getMissionCount(player)); + msg->put(quest->getMissionCount(player)); for(MissionList::const_iterator it = quest->getFirstMission(); it != quest->getLastMission(); ++it) { if(!(*it)->isStarted(player)) continue; - msg->AddString((*it)->getName(player)); - msg->AddString((*it)->getDescription(player)); + msg->putString((*it)->getName(player)); + msg->putString((*it)->getDescription(player)); } } } @@ -2583,8 +3069,8 @@ void ProtocolGame::sendVIPLogIn(uint32_t guid) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xD3); - msg->AddU32(guid); + msg->put(0xD3); + msg->put(guid); } } @@ -2594,276 +3080,362 @@ void ProtocolGame::sendVIPLogOut(uint32_t guid) if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xD4); - msg->AddU32(guid); + msg->put(0xD4); + msg->put(guid); } } -void ProtocolGame::sendVIP(uint32_t guid, const std::string& name, bool isOnline) +void ProtocolGame::sendVIP(uint32_t guid, const std::string& name, bool online) { NetworkMessage_ptr msg = getOutputBuffer(); if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xD2); - msg->AddU32(guid); - msg->AddString(name); - msg->AddByte(isOnline ? 1 : 0); + msg->put(0xD2); + msg->put(guid); + msg->putString(name); + msg->put(online ? 1 : 0); } } -////////////// Add common messages -void ProtocolGame::AddMapDescription(NetworkMessage_ptr msg, const Position& pos) +void ProtocolGame::sendSpellCooldown(Spells_t icon, uint32_t cooldown) { - msg->AddByte(0x64); - msg->AddPosition(player->getPosition()); - GetMapDescription(pos.x - 8, pos.y - 6, pos.z, 18, 14, msg); + NetworkMessage_ptr msg = getOutputBuffer(); + if(msg) + { + TRACK_MESSAGE(msg); + msg->put(0xA4); + msg->put(icon); + msg->put(cooldown); + } +} + +void ProtocolGame::sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t cooldown) +{ + NetworkMessage_ptr msg = getOutputBuffer(); + if(msg) + { + TRACK_MESSAGE(msg); + msg->put(0xA5); + msg->put(groupId); + msg->put(cooldown); + } } -void ProtocolGame::AddTextMessage(NetworkMessage_ptr msg, MessageClasses mclass, const std::string& message) +void ProtocolGame::reloadCreature(const Creature* creature) { - msg->AddByte(0xB4); - msg->AddByte(mclass); - msg->AddString(message); + if(!canSee(creature)) + return; + + // we are cheating the client in here! + uint32_t stackpos = creature->getTile()->getClientIndexOfThing(player, creature); + if(stackpos >= 10) + return; + + NetworkMessage_ptr msg = getOutputBuffer(); + if(msg) + { + TRACK_MESSAGE(msg); + if(std::find(knownCreatureList.begin(), knownCreatureList.end(), + creature->getID()) != knownCreatureList.end()) + { + RemoveTileItem(msg, creature->getPosition(), stackpos); + msg->put(0x6A); + + msg->putPosition(creature->getPosition()); + msg->put(stackpos); + AddCreature(msg, creature, false, creature->getID()); + } + else + AddTileCreature(msg, creature->getPosition(), stackpos, creature); + } } -void ProtocolGame::AddAnimatedText(NetworkMessage_ptr msg, const Position& pos, - uint8_t color, const std::string& text) +void ProtocolGame::AddMapDescription(NetworkMessage_ptr msg, const Position& pos) { - msg->AddByte(0x84); - msg->AddPosition(pos); - msg->AddByte(color); - msg->AddString(text); + msg->put(0x64); + msg->putPosition(player->getPosition()); + GetMapDescription(pos.x - 8, pos.y - 6, pos.z, 18, 14, msg); } -void ProtocolGame::AddMagicEffect(NetworkMessage_ptr msg,const Position& pos, uint8_t type) +void ProtocolGame::AddTextMessage(NetworkMessage_ptr msg, MessageClasses mClass, const std::string& message, + Position* pos/* = NULL*/, MessageDetails* details/* = NULL*/) { - msg->AddByte(0x83); - msg->AddPosition(pos); - msg->AddByte(type + 1); + msg->put(0xB4); + msg->put(mClass); + switch(mClass) + { + case MSG_DAMAGE_DEALT: + case MSG_DAMAGE_RECEIVED: + case MSG_DAMAGE_OTHERS: + { + if(pos) + msg->putPosition(*pos); + else + msg->putPosition(player->getPosition()); + + if(!details) + { + msg->put(0x00); + msg->put(0x00); + msg->put(0x00); + msg->put(0x00); + break; + } + + msg->put(details->value); + msg->put(details->color); + if(details->sub) + { + msg->put(details->sub->value); + msg->put(details->sub->color); + } + else + { + msg->put(0x00); + msg->put(0x00); + } + + break; + } + + case MSG_EXPERIENCE: + case MSG_EXPERIENCE_OTHERS: + case MSG_HEALED: + case MSG_HEALED_OTHERS: + { + if(pos) + msg->putPosition(*pos); + else + msg->putPosition(player->getPosition()); + + if(details) + { + msg->put(details->value); + msg->put(details->color); + } + else + { + msg->put(0x00); + msg->put(0x00); + } + + break; + } + + default: + break; + } + + msg->putString(message); +} + +void ProtocolGame::AddMagicEffect(NetworkMessage_ptr msg, const Position& pos, uint8_t type) +{ + msg->put(0x83); + msg->putPosition(pos); + msg->put(type + 1); } void ProtocolGame::AddDistanceShoot(NetworkMessage_ptr msg, const Position& from, const Position& to, uint8_t type) { - msg->AddByte(0x85); - msg->AddPosition(from); - msg->AddPosition(to); - msg->AddByte(type + 1); + msg->put(0x85); + msg->putPosition(from); + msg->putPosition(to); + msg->put(type + 1); } void ProtocolGame::AddCreature(NetworkMessage_ptr msg, const Creature* creature, bool known, uint32_t remove) { if(!known) { - msg->AddU16(0x61); - msg->AddU32(remove); - msg->AddU32(creature->getID()); - msg->AddString(creature->getHideName() ? "" : creature->getName()); + msg->put(0x61); + msg->put(remove); + msg->put(creature->getID()); + msg->put(creature->getType()); + msg->putString(creature->getHideName() ? "" : creature->getName()); } else { - msg->AddU16(0x62); - msg->AddU32(creature->getID()); + msg->put(0x62); + msg->put(creature->getID()); } if(!creature->getHideHealth()) - msg->AddByte((int32_t)std::ceil(((float)creature->getHealth()) * 100 / std::max(creature->getMaxHealth(), (int32_t)1))); + msg->put(std::ceil(creature->getHealth() * 100. / std::max(creature->getMaxHealth(), 1))); else - msg->AddByte(0x00); + msg->put(0x00); - msg->AddByte((uint8_t)creature->getDirection()); + msg->put((uint8_t)creature->getDirection()); AddCreatureOutfit(msg, creature, creature->getCurrentOutfit()); LightInfo lightInfo; creature->getCreatureLight(lightInfo); - msg->AddByte(player->hasCustomFlag(PlayerCustomFlag_HasFullLight) ? 0xFF : lightInfo.level); - msg->AddByte(lightInfo.color); - msg->AddU16(creature->getStepSpeed()); - msg->AddByte(player->getSkullClient(creature)); - msg->AddByte(player->getPartyShield(creature)); + msg->put(lightInfo.level); + msg->put(lightInfo.color); + + msg->put(creature->getStepSpeed()); + msg->put(player->getSkullType(creature)); + msg->put(player->getPartyShield(creature)); if(!known) - msg->AddByte(0x00); // war emblem + msg->put(player->getGuildEmblem(creature)); - msg->AddByte(!player->canWalkthrough(creature)); + msg->put(!player->canWalkthrough(creature)); } void ProtocolGame::AddPlayerStats(NetworkMessage_ptr msg) { - msg->AddByte(0xA0); - msg->AddU16(player->getHealth()); - msg->AddU16(player->getPlayerInfo(PLAYERINFO_MAXHEALTH)); - msg->AddU32(uint32_t(player->getFreeCapacity() * 100)); - uint64_t experience = player->getExperience(); - if(experience > 0x7FFFFFFF) // client debugs after 2,147,483,647 exp - msg->AddU32(0x7FFFFFFF); - else - msg->AddU32(experience); - - msg->AddU16(player->getPlayerInfo(PLAYERINFO_LEVEL)); - msg->AddByte(player->getPlayerInfo(PLAYERINFO_LEVELPERCENT)); - msg->AddU16(player->getPlayerInfo(PLAYERINFO_MANA)); - msg->AddU16(player->getPlayerInfo(PLAYERINFO_MAXMANA)); - msg->AddByte(player->getPlayerInfo(PLAYERINFO_MAGICLEVEL)); - msg->AddByte(player->getPlayerInfo(PLAYERINFO_MAGICLEVELPERCENT)); - msg->AddByte(player->getPlayerInfo(PLAYERINFO_SOUL)); - msg->AddU16(player->getStaminaMinutes()); + msg->put(0xA0); + msg->put(player->getHealth()); + msg->put(player->getPlayerInfo(PLAYERINFO_MAXHEALTH)); + msg->put(uint32_t(player->getFreeCapacity() * 100)); + msg->put(uint32_t(player->getCapacity() * 100)); + msg->put(player->getExperience()); + msg->put(player->getPlayerInfo(PLAYERINFO_LEVEL)); + msg->put(player->getPlayerInfo(PLAYERINFO_LEVELPERCENT)); + msg->put(player->getPlayerInfo(PLAYERINFO_MANA)); + msg->put(player->getPlayerInfo(PLAYERINFO_MAXMANA)); + msg->put(player->getMagicLevel()); + msg->put(player->getBaseMagicLevel()); + msg->put(player->getPlayerInfo(PLAYERINFO_MAGICLEVELPERCENT)); + msg->put(player->getPlayerInfo(PLAYERINFO_SOUL)); + msg->put(player->getStaminaMinutes()); + msg->put(player->getSpeed()); + + Condition* condition = player->getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT); + msg->put(condition ? condition->getTicks() / 1000 : 0x00); } void ProtocolGame::AddPlayerSkills(NetworkMessage_ptr msg) { - msg->AddByte(0xA1); - msg->AddByte(player->getSkill(SKILL_FIST, SKILL_LEVEL)); - msg->AddByte(player->getSkill(SKILL_FIST, SKILL_PERCENT)); - msg->AddByte(player->getSkill(SKILL_CLUB, SKILL_LEVEL)); - msg->AddByte(player->getSkill(SKILL_CLUB, SKILL_PERCENT)); - msg->AddByte(player->getSkill(SKILL_SWORD, SKILL_LEVEL)); - msg->AddByte(player->getSkill(SKILL_SWORD, SKILL_PERCENT)); - msg->AddByte(player->getSkill(SKILL_AXE, SKILL_LEVEL)); - msg->AddByte(player->getSkill(SKILL_AXE, SKILL_PERCENT)); - msg->AddByte(player->getSkill(SKILL_DIST, SKILL_LEVEL)); - msg->AddByte(player->getSkill(SKILL_DIST, SKILL_PERCENT)); - msg->AddByte(player->getSkill(SKILL_SHIELD, SKILL_LEVEL)); - msg->AddByte(player->getSkill(SKILL_SHIELD, SKILL_PERCENT)); - msg->AddByte(player->getSkill(SKILL_FISH, SKILL_LEVEL)); - msg->AddByte(player->getSkill(SKILL_FISH, SKILL_PERCENT)); -} - -void ProtocolGame::AddCreatureSpeak(NetworkMessage_ptr msg, const Creature* creature, SpeakClasses type, - std::string text, uint16_t channelId, uint32_t time/*= 0*/, Position* pos/* = NULL*/) -{ - msg->AddByte(0xAA); - if(creature) + msg->put(0xA1); + for(uint8_t i = 0; i <= SKILL_LAST; ++i) { - const Player* speaker = creature->getPlayer(); - if(speaker) - { - msg->AddU32(++g_chat.statement); - g_chat.statementMap[g_chat.statement] = text; - } - else - msg->AddU32(0x00); + msg->put(player->getSkill((skills_t)i, SKILL_LEVEL)); + msg->put(player->getBaseSkill((skills_t)i)); + msg->put(player->getSkill((skills_t)i, SKILL_PERCENT)); + } +} - if(creature->getSpeakType() != SPEAK_CLASS_NONE) +void ProtocolGame::AddCreatureSpeak(NetworkMessage_ptr msg, const Creature* creature, MessageClasses type, + std::string text, uint16_t channelId, Position* pos, uint32_t statementId) +{ + msg->put(0xAA); + if(creature) + { + msg->put(statementId); + msg->putString(!creature->getHideName() ? creature->getName() : ""); + if(creature->getSpeakType() != MSG_NONE) type = creature->getSpeakType(); - switch(type) - { - case SPEAK_CHANNEL_RA: - msg->AddString(""); - break; - case SPEAK_RVR_ANSWER: - msg->AddString("Gamemaster"); - break; - default: - msg->AddString(!creature->getHideName() ? creature->getName() : ""); - break; - } - - if(speaker && type != SPEAK_RVR_ANSWER && !speaker->isAccountManager() - && !speaker->hasCustomFlag(PlayerCustomFlag_HideLevel)) - msg->AddU16(speaker->getPlayerInfo(PLAYERINFO_LEVEL)); + const Player* speaker = creature->getPlayer(); + if(speaker && !speaker->isAccountManager() && !speaker->hasCustomFlag(PlayerCustomFlag_HideLevel)) + msg->put(speaker->getPlayerInfo(PLAYERINFO_LEVEL)); else - msg->AddU16(0x00); - + msg->put(0x00); } else { - msg->AddU32(0x00); - msg->AddString(""); - msg->AddU16(0x00); + msg->put(0x00); + msg->putString(""); + msg->put(0x00); } - msg->AddByte(type); + msg->put(type); switch(type) { - case SPEAK_SAY: - case SPEAK_WHISPER: - case SPEAK_YELL: - case SPEAK_MONSTER_SAY: - case SPEAK_MONSTER_YELL: - case SPEAK_PRIVATE_NP: + case MSG_SPEAK_SAY: + case MSG_SPEAK_WHISPER: + case MSG_SPEAK_YELL: + case MSG_SPEAK_MONSTER_SAY: + case MSG_SPEAK_MONSTER_YELL: + case MSG_SPEAK_SPELL: + case MSG_NPC_FROM: { if(pos) - msg->AddPosition(*pos); + msg->putPosition(*pos); else if(creature) - msg->AddPosition(creature->getPosition()); + msg->putPosition(creature->getPosition()); else - msg->AddPosition(Position(0,0,7)); + msg->putPosition(Position(0,0,7)); break; } - case SPEAK_CHANNEL_Y: - case SPEAK_CHANNEL_RN: - case SPEAK_CHANNEL_RA: - case SPEAK_CHANNEL_O: - case SPEAK_CHANNEL_W: - msg->AddU16(channelId); - break; - - case SPEAK_RVR_CHANNEL: - { - msg->AddU32(uint32_t(OTSYS_TIME() / 1000 & 0xFFFFFFFF) - time); + case MSG_CHANNEL: + case MSG_CHANNEL_HIGHLIGHT: + case MSG_GAMEMASTER_CHANNEL: + msg->put(channelId); break; - } default: break; } - msg->AddString(text); + msg->putString(text); } void ProtocolGame::AddCreatureHealth(NetworkMessage_ptr msg,const Creature* creature) { - msg->AddByte(0x8C); - msg->AddU32(creature->getID()); + msg->put(0x8C); + msg->put(creature->getID()); if(!creature->getHideHealth()) - msg->AddByte((int32_t)std::ceil(((float)creature->getHealth()) * 100 / std::max(creature->getMaxHealth(), (int32_t)1))); + msg->put(std::ceil(creature->getHealth() * 100. / std::max(creature->getMaxHealth(), (int32_t)1))); else - msg->AddByte(0x00); + msg->put(0x00); } void ProtocolGame::AddCreatureOutfit(NetworkMessage_ptr msg, const Creature* creature, const Outfit_t& outfit, bool outfitWindow/* = false*/) { - if(outfitWindow || !creature->getPlayer() || (!creature->isInvisible() && (!creature->isGhost() + if(outfitWindow || (!creature->isInvisible() && (!creature->isGhost() || !g_config.getBool(ConfigManager::GHOST_INVISIBLE_EFFECT)))) { - msg->AddU16(outfit.lookType); + msg->put(outfit.lookType); if(outfit.lookType) { - msg->AddByte(outfit.lookHead); - msg->AddByte(outfit.lookBody); - msg->AddByte(outfit.lookLegs); - msg->AddByte(outfit.lookFeet); - msg->AddByte(outfit.lookAddons); + msg->put(outfit.lookHead); + msg->put(outfit.lookBody); + msg->put(outfit.lookLegs); + msg->put(outfit.lookFeet); + msg->put(outfit.lookAddons); } else if(outfit.lookTypeEx) - msg->AddItemId(outfit.lookTypeEx); + msg->putItemId(outfit.lookTypeEx); else - msg->AddU16(outfit.lookTypeEx); + msg->put(outfit.lookTypeEx); + + const Player* _player = creature->getPlayer(); + if(!_player || _player->isMounted()) + msg->put(outfit.lookMount); + else + msg->put(0x00); } else - msg->AddU32(0x00); + { + msg->put(0x00); + msg->put(0x00); + } } void ProtocolGame::AddWorldLight(NetworkMessage_ptr msg, const LightInfo& lightInfo) { - msg->AddByte(0x82); - msg->AddByte((player->hasCustomFlag(PlayerCustomFlag_HasFullLight) ? 0xFF : lightInfo.level)); - msg->AddByte(lightInfo.color); + msg->put(0x82); + msg->put(lightInfo.level); + msg->put(lightInfo.color); } void ProtocolGame::AddCreatureLight(NetworkMessage_ptr msg, const Creature* creature) { + msg->put(0x8D); + msg->put(creature->getID()); + LightInfo lightInfo; creature->getCreatureLight(lightInfo); - msg->AddByte(0x8D); - msg->AddU32(creature->getID()); - msg->AddByte((player->hasCustomFlag(PlayerCustomFlag_HasFullLight) ? 0xFF : lightInfo.level)); - msg->AddByte(lightInfo.color); + + msg->put(lightInfo.level); + msg->put(lightInfo.color); } //tile @@ -2872,10 +3444,10 @@ void ProtocolGame::AddTileItem(NetworkMessage_ptr msg, const Position& pos, uint if(stackpos >= 10) return; - msg->AddByte(0x6A); - msg->AddPosition(pos); - msg->AddByte(stackpos); - msg->AddItem(item); + msg->put(0x6A); + msg->putPosition(pos); + msg->put(stackpos); + msg->putItem(item); } void ProtocolGame::AddTileCreature(NetworkMessage_ptr msg, const Position& pos, uint32_t stackpos, const Creature* creature) @@ -2883,9 +3455,9 @@ void ProtocolGame::AddTileCreature(NetworkMessage_ptr msg, const Position& pos, if(stackpos >= 10) return; - msg->AddByte(0x6A); - msg->AddPosition(pos); - msg->AddByte(stackpos); + msg->put(0x6A); + msg->putPosition(pos); + msg->put(stackpos); bool known; uint32_t removedKnown; @@ -2898,10 +3470,10 @@ void ProtocolGame::UpdateTileItem(NetworkMessage_ptr msg, const Position& pos, u if(stackpos >= 10) return; - msg->AddByte(0x6B); - msg->AddPosition(pos); - msg->AddByte(stackpos); - msg->AddItem(item); + msg->put(0x6B); + msg->putPosition(pos); + msg->put(stackpos); + msg->putItem(item); } void ProtocolGame::RemoveTileItem(NetworkMessage_ptr msg, const Position& pos, uint32_t stackpos) @@ -2909,18 +3481,18 @@ void ProtocolGame::RemoveTileItem(NetworkMessage_ptr msg, const Position& pos, u if(stackpos >= 10) return; - msg->AddByte(0x6C); - msg->AddPosition(pos); - msg->AddByte(stackpos); + msg->put(0x6C); + msg->putPosition(pos); + msg->put(stackpos); } void ProtocolGame::MoveUpCreature(NetworkMessage_ptr msg, const Creature* creature, - const Position& newPos, const Position& oldPos, uint32_t oldStackpos) + const Position& newPos, const Position& oldPos, uint32_t) { if(creature != player) return; - msg->AddByte(0xBE); //floor change up + msg->put(0xBE); //floor change up if(newPos.z == 7) //going to surface { int32_t skip = -1; @@ -2932,8 +3504,8 @@ void ProtocolGame::MoveUpCreature(NetworkMessage_ptr msg, const Creature* creatu GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 0, 18, 14, 8, skip); if(skip >= 0) { - msg->AddByte(skip); - msg->AddByte(0xFF); + msg->put(skip); + msg->put(0xFF); } } else if(newPos.z > 7) //underground, going one floor up (still underground) @@ -2942,28 +3514,28 @@ void ProtocolGame::MoveUpCreature(NetworkMessage_ptr msg, const Creature* creatu GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, oldPos.z - 3, 18, 14, 3, skip); if(skip >= 0) { - msg->AddByte(skip); - msg->AddByte(0xFF); + msg->put(skip); + msg->put(0xFF); } } //moving up a floor up makes us out of sync //west - msg->AddByte(0x68); + msg->put(0x68); GetMapDescription(oldPos.x - 8, oldPos.y + 1 - 6, newPos.z, 1, 14, msg); //north - msg->AddByte(0x65); + msg->put(0x65); GetMapDescription(oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 1, msg); } void ProtocolGame::MoveDownCreature(NetworkMessage_ptr msg, const Creature* creature, - const Position& newPos, const Position& oldPos, uint32_t oldStackpos) + const Position& newPos, const Position& oldPos, uint32_t) { if(creature != player) return; - msg->AddByte(0xBF); //floor change down + msg->put(0xBF); //floor change down if(newPos.z == 8) //going from surface to underground { int32_t skip = -1; @@ -2972,8 +3544,8 @@ void ProtocolGame::MoveDownCreature(NetworkMessage_ptr msg, const Creature* crea GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip); if(skip >= 0) { - msg->AddByte(skip); - msg->AddByte(0xFF); + msg->put(skip); + msg->put(0xFF); } } else if(newPos.z > oldPos.z && newPos.z > 8 && newPos.z < 14) //going further down @@ -2982,18 +3554,18 @@ void ProtocolGame::MoveDownCreature(NetworkMessage_ptr msg, const Creature* crea GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip); if(skip >= 0) { - msg->AddByte(skip); - msg->AddByte(0xFF); + msg->put(skip); + msg->put(0xFF); } } //moving down a floor makes us out of sync //east - msg->AddByte(0x66); + msg->put(0x66); GetMapDescription(oldPos.x + 9, oldPos.y - 1 - 6, newPos.z, 1, 14, msg); //south - msg->AddByte(0x67); + msg->put(0x67); GetMapDescription(oldPos.x - 8, oldPos.y + 7, newPos.z, 18, 1, msg); } @@ -3002,9 +3574,9 @@ void ProtocolGame::AddInventoryItem(NetworkMessage_ptr msg, slots_t slot, const { if(item) { - msg->AddByte(0x78); - msg->AddByte(slot); - msg->AddItem(item); + msg->put(0x78); + msg->put(slot); + msg->putItem(item); } else RemoveInventoryItem(msg, slot); @@ -3012,8 +3584,8 @@ void ProtocolGame::AddInventoryItem(NetworkMessage_ptr msg, slots_t slot, const void ProtocolGame::RemoveInventoryItem(NetworkMessage_ptr msg, slots_t slot) { - msg->AddByte(0x79); - msg->AddByte(slot); + msg->put(0x79); + msg->put(slot); } void ProtocolGame::UpdateInventoryItem(NetworkMessage_ptr msg, slots_t slot, const Item* item) @@ -3024,55 +3596,55 @@ void ProtocolGame::UpdateInventoryItem(NetworkMessage_ptr msg, slots_t slot, con //containers void ProtocolGame::AddContainerItem(NetworkMessage_ptr msg, uint8_t cid, const Item* item) { - msg->AddByte(0x70); - msg->AddByte(cid); - msg->AddItem(item); + msg->put(0x70); + msg->put(cid); + msg->putItem(item); } void ProtocolGame::UpdateContainerItem(NetworkMessage_ptr msg, uint8_t cid, uint8_t slot, const Item* item) { - msg->AddByte(0x71); - msg->AddByte(cid); - msg->AddByte(slot); - msg->AddItem(item); + msg->put(0x71); + msg->put(cid); + msg->put(slot); + msg->putItem(item); } void ProtocolGame::RemoveContainerItem(NetworkMessage_ptr msg, uint8_t cid, uint8_t slot) { - msg->AddByte(0x72); - msg->AddByte(cid); - msg->AddByte(slot); + msg->put(0x72); + msg->put(cid); + msg->put(slot); } -void ProtocolGame::sendChannelMessage(std::string author, std::string text, SpeakClasses type, uint8_t channel) +void ProtocolGame::sendChannelMessage(std::string author, std::string text, MessageClasses type, uint16_t channel) { NetworkMessage_ptr msg = getOutputBuffer(); if(msg) { TRACK_MESSAGE(msg); - msg->AddByte(0xAA); - msg->AddU32(0x00); - msg->AddString(author); - msg->AddU16(0x00); - msg->AddByte(type); - msg->AddU16(channel); - msg->AddString(text); + msg->put(0xAA); + msg->put(0x00); + msg->putString(author); + msg->put(0x00); + msg->put(type); + msg->put(channel); + msg->putString(text); } } -void ProtocolGame::AddShopItem(NetworkMessage_ptr msg, const ShopInfo item) +void ProtocolGame::AddShopItem(NetworkMessage_ptr msg, const ShopInfo& item) { const ItemType& it = Item::items[item.itemId]; - msg->AddU16(it.clientId); + msg->put(it.clientId); if(it.isSplash() || it.isFluidContainer()) - msg->AddByte(fluidMap[item.subType % 8]); + msg->put(fluidMap[item.subType % 8]); else if(it.stackable || it.charges) - msg->AddByte(item.subType); + msg->put(item.subType); else - msg->AddByte(0x01); + msg->put(0x00); - msg->AddString(item.itemName); - msg->AddU32(uint32_t(it.weight * 100)); - msg->AddU32(item.buyPrice); - msg->AddU32(item.sellPrice); + msg->putString(item.itemName); + msg->put(uint32_t(it.weight * 100)); + msg->put(item.buyPrice); + msg->put(item.sellPrice); } diff --git a/protocolgame.h b/protocolgame.h index 5846ced..711b415 100644 --- a/protocolgame.h +++ b/protocolgame.h @@ -32,7 +32,9 @@ class Container; class Tile; class Connection; class Quest; +class Depot; +typedef std::list > ChannelsList; typedef boost::shared_ptr NetworkMessage_ptr; class ProtocolGame : public Protocol { @@ -40,6 +42,7 @@ class ProtocolGame : public Protocol #ifdef __ENABLE_SERVER_DIAGNOSTIC__ static uint32_t protocolGameCount; #endif + ProtocolGame(Connection_ptr connection): Protocol(connection) { #ifdef __ENABLE_SERVER_DIAGNOSTIC__ @@ -49,13 +52,11 @@ class ProtocolGame : public Protocol m_eventConnect = 0; m_debugAssertSent = m_acceptPackets = false; } - virtual ~ProtocolGame() { #ifdef __ENABLE_SERVER_DIAGNOSTIC__ protocolGameCount--; #endif - player = NULL; } enum {protocolId = 0x0A}; @@ -88,20 +89,22 @@ class ProtocolGame : public Protocol virtual void onConnect(); virtual void onRecvFirstMessage(NetworkMessage& msg); - bool parseFirstPacket(NetworkMessage& msg); virtual void parsePacket(NetworkMessage& msg); //Parse methods void parseLogout(NetworkMessage& msg); - void parseCancelMove(NetworkMessage& msg); + void parseCancelWalk(NetworkMessage& msg); + void parseCancelTarget(NetworkMessage& msg); void parseReceivePing(NetworkMessage& msg); void parseAutoWalk(NetworkMessage& msg); void parseMove(NetworkMessage& msg, Direction dir); void parseTurn(NetworkMessage& msg, Direction dir); + void parseCancelMove(NetworkMessage& msg); void parseRequestOutfit(NetworkMessage& msg); void parseSetOutfit(NetworkMessage& msg); + void parseMountStatus(NetworkMessage& msg); void parseSay(NetworkMessage& msg); void parseLookAt(NetworkMessage& msg); void parseFightModes(NetworkMessage& msg); @@ -109,6 +112,7 @@ class ProtocolGame : public Protocol void parseFollow(NetworkMessage& msg); void parseBugReport(NetworkMessage& msg); + void parseThankYou(NetworkMessage& msg); void parseDebugAssert(NetworkMessage& msg); void parseThrow(NetworkMessage& msg); @@ -143,6 +147,13 @@ class ProtocolGame : public Protocol void parseAcceptTrade(NetworkMessage& msg); void parseCloseTrade(); + //market methods + void parseMarketLeave(); + void parseMarketBrowse(NetworkMessage& msg); + void parseMarketCreateOffer(NetworkMessage& msg); + void parseMarketCancelOffer(NetworkMessage& msg); + void parseMarketAcceptOffer(NetworkMessage& msg); + //VIP methods void parseAddVip(NetworkMessage& msg); void parseRemoveVip(NetworkMessage& msg); @@ -155,36 +166,32 @@ class ProtocolGame : public Protocol void parseChannelExclude(NetworkMessage& msg); void parseGetChannels(NetworkMessage& msg); void parseOpenChannel(NetworkMessage& msg); - void parseOpenPriv(NetworkMessage& msg); + void parseOpenPrivate(NetworkMessage& msg); void parseCloseChannel(NetworkMessage& msg); void parseCloseNpc(NetworkMessage& msg); - void parseProcessRuleViolation(NetworkMessage& msg); - void parseCloseRuleViolation(NetworkMessage& msg); - void parseCancelRuleViolation(NetworkMessage& msg); + + //rule violation + void parseViolationReport(NetworkMessage& msg); //Send functions - void sendChannelMessage(std::string author, std::string text, SpeakClasses type, uint8_t channel); + void sendChannelMessage(std::string author, std::string text, MessageClasses type, uint16_t channel); + void sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent); void sendClosePrivate(uint16_t channelId); void sendCreatePrivateChannel(uint16_t channelId, const std::string& channelName); - void sendChannelsDialog(); + void sendChannelsDialog(const ChannelsList& channels); void sendChannel(uint16_t channelId, const std::string& channelName); - void sendRuleViolationsChannel(uint16_t channelId); void sendOpenPrivateChannel(const std::string& receiver); - void sendToChannel(const Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId, uint32_t time = 0); - void sendRemoveReport(const std::string& name); - void sendLockRuleViolation(); - void sendRuleViolationCancel(const std::string& name); void sendIcons(int32_t icons); void sendFYIBox(const std::string& message); void sendDistanceShoot(const Position& from, const Position& to, uint8_t type); void sendMagicEffect(const Position& pos, uint8_t type); - void sendAnimatedText(const Position& pos, uint8_t color, std::string text); void sendCreatureHealth(const Creature* creature); void sendSkills(); void sendPing(); void sendCreatureTurn(const Creature* creature, int16_t stackpos); - void sendCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, Position* pos = NULL); + void sendCreatureSay(const Creature* creature, MessageClasses type, const std::string& text, Position* pos, uint32_t statementId); + void sendCreatureChannelSay(const Creature* creature, MessageClasses type, const std::string& text, uint16_t channelId, uint32_t statementId); void sendCancel(const std::string& message); void sendCancelWalk(); @@ -193,22 +200,33 @@ class ProtocolGame : public Protocol void sendCreatureOutfit(const Creature* creature, const Outfit_t& outfit); void sendStats(); void sendTextMessage(MessageClasses mclass, const std::string& message); - void sendReLoginWindow(); + void sendStatsMessage(MessageClasses mclass, const std::string& message, + Position pos, MessageDetails* details = NULL); + void sendReLoginWindow(uint8_t pvpPercent); void sendTutorial(uint8_t tutorialId); void sendAddMarker(const Position& pos, MapMarks_t markType, const std::string& desc); void sendCreatureSkull(const Creature* creature); void sendCreatureShield(const Creature* creature); + void sendCreatureEmblem(const Creature* creature) {reloadCreature(creature);} + void sendCreatureWalkthrough(const Creature* creature, bool walkthrough); - void sendShop(const ShopInfoList& shop); + void sendShop(Npc* npc, const ShopInfoList& shop); void sendCloseShop(); void sendGoods(const ShopInfoList& shop); - void sendTradeItemRequest(const Player* player, const Item* item, bool ack); + void sendMarketEnter(uint32_t depotId); + void sendMarketLeave(); + void sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers); + void sendMarketAcceptOffer(MarketOfferEx offer); + void sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers); + void sendMarketCancelOffer(MarketOfferEx offer); + void sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers); + void sendMarketDetail(uint16_t itemId); + void sendTradeItemRequest(const Player* _player, const Item* item, bool ack); void sendCloseTrade(); void sendTextWindow(uint32_t windowTextId, Item* item, uint16_t maxLen, bool canWrite); - void sendTextWindow(uint32_t windowTextId, uint32_t itemId, const std::string& text); void sendHouseWindow(uint32_t windowTextId, House* house, uint32_t listId, const std::string& text); void sendOutfitWindow(); @@ -217,12 +235,15 @@ class ProtocolGame : public Protocol void sendVIPLogIn(uint32_t guid); void sendVIPLogOut(uint32_t guid); - void sendVIP(uint32_t guid, const std::string& name, bool isOnline); + void sendVIP(uint32_t guid, const std::string& name, bool online); + + void sendSpellCooldown(Spells_t icon, uint32_t cooldown); + void sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t cooldown); void sendCreatureLight(const Creature* creature); void sendWorldLight(const LightInfo& lightInfo); - void sendCreatureSquare(const Creature* creature, SquareColor_t color); + void sendCreatureSquare(const Creature* creature, uint8_t color); //tiles void sendAddTileItem(const Tile* tile, const Position& pos, uint32_t stackpos, const Item* item); @@ -248,28 +269,29 @@ class ProtocolGame : public Protocol void sendUpdateInventoryItem(slots_t slot, const Item* item); void sendRemoveInventoryItem(slots_t slot); - //Help functions + // help functions + void reloadCreature(const Creature* creature); - // translate a tile to clientreadable format + //translate a tile to clientreadable format void GetTileDescription(const Tile* tile, NetworkMessage_ptr msg); - // translate a floor to clientreadable format + //translate a floor to clientreadable format void GetFloorDescription(NetworkMessage_ptr msg, int32_t x, int32_t y, int32_t z, int32_t width, int32_t height, int32_t offset, int32_t& skip); - // translate a map area to clientreadable format + //translate a map area to clientreadable format void GetMapDescription(int32_t x, int32_t y, int32_t z, int32_t width, int32_t height, NetworkMessage_ptr msg); void AddMapDescription(NetworkMessage_ptr msg, const Position& pos); - void AddTextMessage(NetworkMessage_ptr msg, MessageClasses mclass, const std::string& message); - void AddAnimatedText(NetworkMessage_ptr msg, const Position& pos, uint8_t color, const std::string& text); + void AddTextMessage(NetworkMessage_ptr msg, MessageClasses mclass, const std::string& message, + Position* pos = NULL, MessageDetails* details = NULL); void AddMagicEffect(NetworkMessage_ptr msg, const Position& pos, uint8_t type); void AddDistanceShoot(NetworkMessage_ptr msg, const Position& from, const Position& to, uint8_t type); void AddCreature(NetworkMessage_ptr msg, const Creature* creature, bool known, uint32_t remove); void AddPlayerStats(NetworkMessage_ptr msg); - void AddCreatureSpeak(NetworkMessage_ptr msg, const Creature* creature, SpeakClasses type, - std::string text, uint16_t channelId, uint32_t time = 0, Position* pos = NULL); + void AddCreatureSpeak(NetworkMessage_ptr msg, const Creature* creature, MessageClasses type, + std::string text, uint16_t channelId, Position* pos, uint32_t statementId); void AddCreatureHealth(NetworkMessage_ptr msg, const Creature* creature); void AddCreatureOutfit(NetworkMessage_ptr msg, const Creature* creature, const Outfit_t& outfit, bool outfitWindow = false); void AddPlayerSkills(NetworkMessage_ptr msg); @@ -297,11 +319,8 @@ class ProtocolGame : public Protocol void UpdateInventoryItem(NetworkMessage_ptr msg, slots_t slot, const Item* item); void RemoveInventoryItem(NetworkMessage_ptr msg, slots_t slot); - //rule violation window - void parseViolationWindow(NetworkMessage& msg); - //shop - void AddShopItem(NetworkMessage_ptr msg, const ShopInfo item); + void AddShopItem(NetworkMessage_ptr msg, const ShopInfo& item); #define addGameTask(f, ...) addGameTaskInternal(0, boost::bind(f, &g_game, __VA_ARGS__)) #define addGameTaskTimed(delay, f, ...) addGameTaskInternal(delay, boost::bind(f, &g_game, __VA_ARGS__)) @@ -311,7 +330,7 @@ class ProtocolGame : public Protocol friend class Player; Player* player; - uint32_t m_eventConnect; + uint32_t m_eventConnect, m_maxSizeCount; bool m_debugAssertSent, m_acceptPackets; }; #endif diff --git a/protocolhttp.cpp b/protocolhttp.cpp index 2de16ae..82bf8e3 100644 --- a/protocolhttp.cpp +++ b/protocolhttp.cpp @@ -22,45 +22,39 @@ #ifdef __ENABLE_SERVER_DIAGNOSTIC__ uint32_t ProtocolHTTP::protocolHTTPCount = 0; -#endif +#endif +#ifdef __DEBUG_NET_DETAIL__ void ProtocolHTTP::deleteProtocolTask() { -#ifdef __DEBUG_NET_DETAIL__ - std::cout << "Deleting ProtocolHTTP" << std::endl; -#endif + std::clog << "Deleting ProtocolHTTP" << std::endl; Protocol::deleteProtocolTask(); } -void ProtocolHTTP::disconnectClient() -{ - getConnection()->close(); -} - -bool ProtocolHTTP::parseFirstPacket(NetworkMessage& msg) +#endif +void ProtocolHTTP::onRecvFirstMessage(NetworkMessage&) { if(OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false)) { TRACK_MESSAGE(output); - output->AddString("HTTP/1.1 200 OK"); - output->AddString("Date: Fri, 27 Mar 2009 17:28.13 GMT\r\n"); - output->AddString("Server: The Forgotten Server httpd/0.4\r\n"); - output->AddString("Content-Location: filename.html\r\n"); + output->putString("HTTP/1.1 200 OK"); + output->putString("Date: Fri, 27 Mar 2009 17:28.13 GMT\r\n"); + output->putString("Server: The Forgotten Server httpd/0.4\r\n"); + output->putString("Content-Location: index.html\r\n"); //Vary: negotiate\r\n //TCN: choice\r\n - output->AddString("Last-Modified: Fri, 27 Mar 2009 17:28.13 GMT\r\n"); - output->AddString("Accept-Ranges: bytes\r\n"); - output->AddString("Content-Length: 1234\r\n"); - output->AddString("Expires: Fri, 27 Mar 2009 17:28.13 GMT\r\n"); - output->AddString("Connection: close\r\n"); - output->AddString("Content-Type: text/html qs=0.7\r\n"); - output->AddString("\r\n"); - output->AddString("The Forgotten Server httpdIt works (apache ripoff ;D)!"); + output->putString("Last-Modified: Fri, 27 Mar 2009 17:28.13 GMT\r\n"); + output->putString("Accept-Ranges: bytes\r\n"); + output->putString("Content-Length: 1234\r\n"); + output->putString("Expires: Fri, 27 Mar 2009 17:28.13 GMT\r\n"); + output->putString("Connection: close\r\n"); + output->putString("Content-Type: text/html qs=0.7\r\n"); + output->putString("\r\n"); + output->putString("The Forgotten Server httpdIt works (apache ripoff ;D)!"); OutputMessagePool::getInstance()->send(output); } getConnection()->close(); - return true; } diff --git a/protocolhttp.h b/protocolhttp.h index ec65200..f791ec1 100644 --- a/protocolhttp.h +++ b/protocolhttp.h @@ -20,23 +20,20 @@ #include "protocol.h" class NetworkMessage; -class OutputMessage; - class ProtocolHTTP : public Protocol { public: #ifdef __ENABLE_SERVER_DIAGNOSTIC__ static uint32_t protocolHTTPCount; #endif - virtual void onRecvFirstMessage(NetworkMessage& msg) {parseFirstPacket(msg);} + virtual void onRecvFirstMessage(NetworkMessage& msg); - ProtocolHTTP(Connection* connection) : Protocol(connection) + ProtocolHTTP(Connection_ptr connection) : Protocol(connection) { #ifdef __ENABLE_SERVER_DIAGNOSTIC__ protocolHTTPCount++; #endif - disableXTEAEncryption(); - disableChecksum(); + setRawMessages(true); } virtual ~ProtocolHTTP() { @@ -48,12 +45,12 @@ class ProtocolHTTP : public Protocol enum {protocolId = 0x00}; enum {isSingleSocket = true}; enum {hasChecksum = false}; + static const char* protocolName() {return "http protocol";} +#ifdef __DEBUG_NET_DETAIL__ protected: virtual void deleteProtocolTask(); - - void disconnectClient(); - bool parseFirstPacket(NetworkMessage& msg); +#endif }; #endif diff --git a/protocollogin.cpp b/protocollogin.cpp index 8b71d9d..8ddec65 100644 --- a/protocollogin.cpp +++ b/protocollogin.cpp @@ -15,21 +15,20 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" -#include "resources.h" #include #include "protocollogin.h" #include "tools.h" -#include "rsa.h" +#include "const.h" #include "iologindata.h" #include "ioban.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) -#include "gui.h" -#endif #include "outputmessage.h" #include "connection.h" +#ifdef __LOGIN_SERVER__ +#include "gameservers.h" +#endif #include "configmanager.h" #include "game.h" @@ -37,68 +36,69 @@ extern ConfigManager g_config; extern Game g_game; -extern IpList serverIps; - #ifdef __ENABLE_SERVER_DIAGNOSTIC__ uint32_t ProtocolLogin::protocolLoginCount = 0; -#endif +#endif +#ifdef __DEBUG_NET_DETAIL__ void ProtocolLogin::deleteProtocolTask() { -#ifdef __DEBUG_NET_DETAIL__ - std::cout << "Deleting ProtocolLogin" << std::endl; -#endif + std::clog << "Deleting ProtocolLogin" << std::endl; Protocol::deleteProtocolTask(); } +#endif void ProtocolLogin::disconnectClient(uint8_t error, const char* message) { OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false); if(output) { TRACK_MESSAGE(output); - output->AddByte(error); - output->AddString(message); + output->put(error); + output->putString(message); OutputMessagePool::getInstance()->send(output); } getConnection()->close(); } -bool ProtocolLogin::parseFirstPacket(NetworkMessage& msg) +void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) { - if( -#if defined(WINDOWS) && !defined(__CONSOLE__) - !GUI::getInstance()->m_connections || -#endif - g_game.getGameState() == GAME_STATE_SHUTDOWN) + if(g_game.getGameState() == GAMESTATE_SHUTDOWN) { getConnection()->close(); - return false; + return; } uint32_t clientIp = getConnection()->getIP(); - /*uint16_t operatingSystem = msg.GetU16();*/msg.SkipBytes(2); - uint16_t version = msg.GetU16(); + msg.skip(2); // client platform + uint16_t version = msg.get(); + +#ifdef CLIENT_VERSION_DATA + uint32_t datSignature = msg.get(); + uint32_t sprSignature = msg.get(); - msg.SkipBytes(12); + uint32_t picSignature = msg.get(); +#else + msg.skip(12); +#endif if(!RSA_decrypt(msg)) { getConnection()->close(); - return false; + return; } - uint32_t key[4] = {msg.GetU32(), msg.GetU32(), msg.GetU32(), msg.GetU32()}; + uint32_t key[4] = {msg.get(), msg.get(), msg.get(), msg.get()}; enableXTEAEncryption(); setXTEAKey(key); - std::string name = msg.GetString(), password = msg.GetString(); + std::string name = msg.getString(), password = msg.getString(); if(name.empty()) { if(!g_config.getBool(ConfigManager::ACCOUNT_MANAGER)) { disconnectClient(0x0A, "Invalid account name."); - return false; + return; } name = "1"; @@ -108,47 +108,59 @@ bool ProtocolLogin::parseFirstPacket(NetworkMessage& msg) if(version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { disconnectClient(0x0A, CLIENT_VERSION_STRING); - return false; + return; + } +#ifdef CLIENT_VERSION_DATA + + if(sprSignature != CLIENT_VERSION_SPR) + { + disconnectClient(0x0A, CLIENT_VERSION_DATA); + return; + } + + if(datSignature != CLIENT_VERSION_DAT) + { + disconnectClient(0x0A, CLIENT_VERSION_DATA); + return; + } + + if(picSignature != CLIENT_VERSION_PIC) + { + disconnectClient(0x0A, CLIENT_VERSION_DATA); + return; } +#endif - if(g_game.getGameState() < GAME_STATE_NORMAL) + if(g_game.getGameState() < GAMESTATE_NORMAL) { disconnectClient(0x0A, "Server is just starting up, please wait."); - return false; + return; } - if(g_game.getGameState() == GAME_STATE_MAINTAIN) + if(g_game.getGameState() == GAMESTATE_MAINTAIN) { disconnectClient(0x0A, "Server is under maintenance, please re-connect in a while."); - return false; + return; } if(ConnectionManager::getInstance()->isDisabled(clientIp, protocolId)) { disconnectClient(0x0A, "Too many connections attempts from your IP address, please try again later."); - return false; + return; } if(IOBan::getInstance()->isIpBanished(clientIp)) { disconnectClient(0x0A, "Your IP is banished!"); - return false; - } - - uint32_t id = 1; - if(!IOLoginData::getInstance()->getAccountId(name, id)) - { - ConnectionManager::getInstance()->addAttempt(clientIp, protocolId, false); - disconnectClient(0x0A, "Invalid account name."); - return false; + return; } - Account account = IOLoginData::getInstance()->loadAccount(id); - if(!encryptTest(password, account.password)) + Account account; + if(!IOLoginData::getInstance()->loadAccount(account, name) || !encryptTest(account.salt + password, account.password)) { ConnectionManager::getInstance()->addAttempt(clientIp, protocolId, false); - disconnectClient(0x0A, "Invalid password."); - return false; + disconnectClient(0x0A, "Invalid account name or password."); + return; } Ban ban; @@ -164,95 +176,111 @@ bool ProtocolLogin::parseFirstPacket(NetworkMessage& msg) else IOLoginData::getInstance()->getNameByGuid(ban.adminId, name_, true); - char buffer[500 + ban.comment.length()]; - sprintf(buffer, "Your account has been %s at:\n%s by: %s,\nfor the following reason:\n%s.\nThe action taken was:\n%s.\nThe comment given was:\n%s.\nYour %s%s.", - (deletion ? "deleted" : "banished"), formatDateShort(ban.added).c_str(), name_.c_str(), - getReason(ban.reason).c_str(), getAction(ban.action, false).c_str(), ban.comment.c_str(), - (deletion ? "account won't be undeleted" : "banishment will be lifted at:\n"), - (deletion ? "." : formatDateShort(ban.expires, true).c_str())); + std::stringstream ss; + ss << "Your account has been " << (deletion ? "deleted" : "banished") << " at:\n" << formatDateEx(ban.added, "%d %b %Y").c_str() + << " by: " << name_.c_str() << ".\nThe comment given was:\n" << ban.comment.c_str() << ".\nYour " << (deletion ? + "account won't be undeleted" : "banishment will be lifted at:\n") << (deletion ? "" : formatDateEx(ban.expires).c_str()) << "."; - disconnectClient(0x0A, buffer); - return false; + disconnectClient(0x0A, ss.str().c_str()); + return; } - //Remove premium days + // remove premium days + #ifndef __LOGIN_SERVER__ IOLoginData::getInstance()->removePremium(account); if(!g_config.getBool(ConfigManager::ACCOUNT_MANAGER) && !account.charList.size()) { disconnectClient(0x0A, std::string("This account does not contain any character yet.\nCreate a new character on the " + g_config.getString(ConfigManager::SERVER_NAME) + " website at " + g_config.getString(ConfigManager::URL) + ".").c_str()); - return false; + return; + } + #else + Characters charList; + for(Characters::iterator it = account.charList.begin(); it != account.charList.end(); ++it) + { + if(version >= it->second.server->getVersionMin() && version <= it->second.server->getVersionMax()) + charList[it->first] = it->second; } + IOLoginData::getInstance()->removePremium(account); + if(!g_config.getBool(ConfigManager::ACCOUNT_MANAGER) && !charList.size()) + { + disconnectClient(0x0A, std::string("This account does not contain any character on this client yet.\nCreate a new character on the " + + g_config.getString(ConfigManager::SERVER_NAME) + " website at " + g_config.getString(ConfigManager::URL) + ".").c_str()); + return; + } + #endif + ConnectionManager::getInstance()->addAttempt(clientIp, protocolId, true); if(OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false)) { TRACK_MESSAGE(output); - output->AddByte(0x14); + output->put(0x14); + uint32_t serverIp = getConnection()->getEndpoint(); char motd[1300]; sprintf(motd, "%d\n%s", g_game.getMotdId(), g_config.getString(ConfigManager::MOTD).c_str()); - output->AddString(motd); + output->putString(motd); - uint32_t serverIp = serverIps[0].first; - for(IpList::iterator it = serverIps.begin(); it != serverIps.end(); ++it) + //Add char list + output->put(0x64); + if(g_config.getBool(ConfigManager::ACCOUNT_MANAGER) && account.number != 1) { - if((it->first & it->second) != (clientIp & it->second)) - continue; + output->put(account.charList.size() + 1); + output->putString("Account Manager"); - serverIp = it->first; - break; - } + output->putString(g_config.getString(ConfigManager::SERVER_NAME)); + output->put(serverIp); - //Add char list - output->AddByte(0x64); - if(g_config.getBool(ConfigManager::ACCOUNT_MANAGER) && id != 1) - { - output->AddByte(account.charList.size() + 1); - output->AddString("Account Manager"); - output->AddString(g_config.getString(ConfigManager::SERVER_NAME)); - output->AddU32(serverIp); - output->AddU16(g_config.getNumber(ConfigManager::GAME_PORT)); + IntegerVec games = vectorAtoi(explodeString(g_config.getString(ConfigManager::GAME_PORT), ",")); + output->put(games[random_range(0, games.size() - 1)]); } else - output->AddByte((uint8_t)account.charList.size()); + output->put((uint8_t)account.charList.size()); - for(Characters::iterator it = account.charList.begin(); it != account.charList.end(); it++) + #ifndef __LOGIN_SERVER__ + for(Characters::iterator it = account.charList.begin(); it != account.charList.end(); ++it) { - #ifndef __LOGIN_SERVER__ - output->AddString((*it)); + output->putString((*it)); if(g_config.getBool(ConfigManager::ON_OR_OFF_CHARLIST)) { if(g_game.getPlayerByName((*it))) - output->AddString("Online"); + output->putString("Online"); else - output->AddString("Offline"); + output->putString("Offline"); } else - output->AddString(g_config.getString(ConfigManager::SERVER_NAME)); - - output->AddU32(serverIp); - output->AddU16(g_config.getNumber(ConfigManager::GAME_PORT)); - #else - if(version < it->second->getVersionMin() || version > it->second->getVersionMax()) - continue; - - output->AddString(it->first); - output->AddString(it->second->getName()); - output->AddU32(it->second->getAddress()); - output->AddU16(it->second->getPort()); - #endif + output->putString(g_config.getString(ConfigManager::SERVER_NAME)); + + output->put(serverIp); + IntegerVec games = vectorAtoi(explodeString(g_config.getString(ConfigManager::GAME_PORT), ",")); + output->put(games[random_range(0, games.size() - 1)]); + } + #else + for(Characters::iterator it = charList.begin(); it != charList.end(); ++it) + { + output->putString(it->second.name); + if(!g_config.getBool(ConfigManager::ON_OR_OFF_CHARLIST) || it->second.status < 0) + output->putString(it->second.server->getName()); + else if(it->second.status) + output->putString("Online"); + else + output->putString("Offline"); + + output->put(it->second.server->getAddress()); + IntegerVec games = it->second.server->getPorts(); + output->put(games[random_range(0, games.size() - 1)]); } + #endif //Add premium days if(g_config.getBool(ConfigManager::FREE_PREMIUM)) - output->AddU16(65535); //client displays free premium + output->put(GRATIS_PREMIUM); else - output->AddU16(account.premiumDays); + output->put(account.premiumDays); OutputMessagePool::getInstance()->send(output); } getConnection()->close(); - return true; } diff --git a/protocollogin.h b/protocollogin.h index cc299c2..6011320 100644 --- a/protocollogin.h +++ b/protocollogin.h @@ -20,15 +20,13 @@ #include "protocol.h" class NetworkMessage; -class OutputMessage; - class ProtocolLogin : public Protocol { public: #ifdef __ENABLE_SERVER_DIAGNOSTIC__ static uint32_t protocolLoginCount; #endif - virtual void onRecvFirstMessage(NetworkMessage& msg) {parseFirstPacket(msg);} + virtual void onRecvFirstMessage(NetworkMessage& msg); ProtocolLogin(Connection_ptr connection) : Protocol(connection) { @@ -47,12 +45,13 @@ class ProtocolLogin : public Protocol enum {protocolId = 0x01}; enum {isSingleSocket = false}; enum {hasChecksum = true}; + static const char* protocolName() {return "login protocol";} protected: + #ifdef __DEBUG_NET_DETAIL__ virtual void deleteProtocolTask(); - + #endif void disconnectClient(uint8_t error, const char* message); - bool parseFirstPacket(NetworkMessage& msg); }; #endif diff --git a/protocolold.cpp b/protocolold.cpp index 3678344..0154e17 100644 --- a/protocolold.cpp +++ b/protocolold.cpp @@ -15,70 +15,61 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" -#include "resources.h" - #include "protocolold.h" -#include "rsa.h" #include "outputmessage.h" #include "connection.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) -#include "gui.h" -#endif - #include "game.h" + extern Game g_game; #ifdef __ENABLE_SERVER_DIAGNOSTIC__ uint32_t ProtocolOld::protocolOldCount = 0; -#endif +#endif #ifdef __DEBUG_NET_DETAIL__ void ProtocolOld::deleteProtocolTask() { - std::cout << "Deleting ProtocolOld" << std::endl; + std::clog << "Deleting ProtocolOld" << std::endl; Protocol::deleteProtocolTask(); } -#endif +#endif void ProtocolOld::disconnectClient(uint8_t error, const char* message) { if(OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false)) { TRACK_MESSAGE(output); - output->AddByte(error); - output->AddString(message); + output->put(error); + output->putString(message); OutputMessagePool::getInstance()->send(output); } getConnection()->close(); } -bool ProtocolOld::parseFirstPacket(NetworkMessage& msg) +void ProtocolOld::onRecvFirstMessage(NetworkMessage& msg) { - if( -#if defined(WINDOWS) && !defined(__CONSOLE__) - !GUI::getInstance()->m_connections || -#endif - g_game.getGameState() == GAME_STATE_SHUTDOWN) + if(g_game.getGameState() == GAMESTATE_SHUTDOWN) { getConnection()->close(); - return false; + return; } - /*uint16_t operatingSystem = */msg.GetU16(); - uint16_t version = msg.GetU16(); - msg.SkipBytes(12); + msg.skip(2); + uint16_t version = msg.get(); + + msg.skip(12); if(version <= 760) disconnectClient(0x0A, CLIENT_VERSION_STRING); if(!RSA_decrypt(msg)) { getConnection()->close(); - return false; + return; } - uint32_t key[4] = {msg.GetU32(), msg.GetU32(), msg.GetU32(), msg.GetU32()}; + uint32_t key[4] = {msg.get(), msg.get(), msg.get(), msg.get()}; enableXTEAEncryption(); setXTEAKey(key); @@ -86,11 +77,4 @@ bool ProtocolOld::parseFirstPacket(NetworkMessage& msg) disableChecksum(); disconnectClient(0x0A, CLIENT_VERSION_STRING); - return false; -} - -void ProtocolOld::onRecvFirstMessage(NetworkMessage& msg) -{ - parseFirstPacket(msg); -} - +} \ No newline at end of file diff --git a/protocolold.h b/protocolold.h index 8bf05b5..1a2a2d7 100644 --- a/protocolold.h +++ b/protocolold.h @@ -20,8 +20,6 @@ #include "protocol.h" class NetworkMessage; -class OutputMessage; - class ProtocolOld : public Protocol { public: @@ -36,7 +34,6 @@ class ProtocolOld : public Protocol protocolOldCount++; #endif } - virtual ~ProtocolOld() { #ifdef __ENABLE_SERVER_DIAGNOSTIC__ @@ -51,9 +48,7 @@ class ProtocolOld : public Protocol #ifdef __DEBUG_NET_DETAIL__ virtual void deleteProtocolTask(); #endif - void disconnectClient(uint8_t error, const char* message); - bool parseFirstPacket(NetworkMessage& msg); }; class ProtocolOldLogin : public ProtocolOld diff --git a/quests.cpp b/quests.cpp index 88e6f35..4848650 100644 --- a/quests.cpp +++ b/quests.cpp @@ -25,7 +25,8 @@ bool Mission::isStarted(Player* player) return false; std::string value; - return player->getStorage(storageId, value) && atoi(value.c_str()) >= startValue; + player->getStorage(storageId, value); + return atoi(value.c_str()) >= startValue; } bool Mission::isCompleted(Player* player) @@ -34,42 +35,59 @@ bool Mission::isCompleted(Player* player) return false; std::string value; - return player->getStorage(storageId, value) && atoi(value.c_str()) >= endValue; + player->getStorage(storageId, value); + return atoi(value.c_str()) >= endValue; } -std::string Mission::getDescription(Player* player) +std::string Mission::parseStorages(std::string state, std::string value, Player* player) { - std::string value; - if(!player->getStorage(storageId, value)) - return "Couldn't retrieve a valid player storage, please report to a gamemaster."; - - if(state.size()) + std::string::size_type start = 0, end = 0, nStart = 0, length = 0; + while((start = state.find("|STORAGE:", end)) != std::string::npos) { - std::string ret = state; - replaceString(ret, "|STATE|", value); - return ret; + nStart = start + 9; + if((end = state.find("|", nStart)) == std::string::npos) + break; + + length = end - nStart; + std::string value, storage = state.substr(nStart, length); + player->getStorage(storage, value); + state.replace(start, (end - start + 1), value); } - if(atoi(value.c_str()) >= endValue) - return states.rbegin()->second; + replaceString(state, "|STATE|", value); + return state; +} - for(int32_t i = endValue; i >= startValue; --i) +std::string Mission::getDescription(Player* player) +{ + std::string value; + player->getStorage(storageId, value); + if(!states.empty()) { - if(!player->getStorage(storageId, value) || atoi(value.c_str()) != i) - continue; + int32_t cmp = atoi(value.c_str()); + if(cmp >= endValue) + return parseStorages(states.rbegin()->second, value, player); - std::string ret = states[i - startValue]; - replaceString(ret, "|STATE|", value); - return ret; + for(int32_t i = (endValue - 1); i >= startValue; --i) + { + if(cmp != i) + continue; + std::string tmp = states[i]; + if(!tmp.empty()) + return parseStorages(tmp, value, player); + } } + if(state.size()) + return parseStorages(state, value, player); + return "Couldn't retrieve any mission description, please report to a gamemaster."; } Quest::~Quest() { - for(MissionList::iterator it = missions.begin(); it != missions.end(); it++) + for(MissionList::iterator it = missions.begin(); it != missions.end(); ++it) delete (*it); missions.clear(); @@ -77,16 +95,20 @@ Quest::~Quest() bool Quest::isStarted(Player* player) { - if(!player) - return false; + for(MissionList::const_iterator it = missions.begin(); it != missions.end(); ++it) + { + if((*it)->isStarted(player)) + return true; + } std::string value; - return player->getStorage(storageId, value) && atoi(value.c_str()) >= storageValue; + player->getStorage(storageId, value); + return atoi(value.c_str()) >= storageValue; } bool Quest::isCompleted(Player* player) const { - for(MissionList::const_iterator it = missions.begin(); it != missions.end(); it++) + for(MissionList::const_iterator it = missions.begin(); it != missions.end(); ++it) { if(!(*it)->isCompleted(player)) return false; @@ -98,7 +120,7 @@ bool Quest::isCompleted(Player* player) const uint16_t Quest::getMissionCount(Player* player) { uint16_t count = 0; - for(MissionList::iterator it = missions.begin(); it != missions.end(); it++) + for(MissionList::iterator it = missions.begin(); it != missions.end(); ++it) { if((*it)->isStarted(player)) count++; @@ -109,7 +131,7 @@ uint16_t Quest::getMissionCount(Player* player) void Quests::clear() { - for(QuestList::iterator it = quests.begin(); it != quests.end(); it++) + for(QuestList::iterator it = quests.begin(); it != quests.end(); ++it) delete (*it); quests.clear(); @@ -126,25 +148,21 @@ bool Quests::loadFromXml() xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "quests.xml").c_str()); if(!doc) { - std::cout << "[Warning - Quests::loadFromXml] Cannot load quests file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Quests::loadFromXml] Cannot load quests file." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr p, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"quests")) { - std::cout << "[Error - Quests::loadFromXml] Malformed quests file." << std::endl; + std::clog << "[Error - Quests::loadFromXml] Malformed quests file." << std::endl; xmlFreeDoc(doc); return false; } - p = root->children; - while(p) - { + for(xmlNodePtr p = root->children; p; p = p->next) parseQuestNode(p, false); - p = p->next; - } xmlFreeDoc(doc); return true; @@ -170,9 +188,9 @@ bool Quests::parseQuestNode(xmlNodePtr p, bool checkDuplicate) if(readXMLString(p, "name", strValue)) name = strValue; - uint32_t startStorageId = 0; - if(readXMLInteger(p, "startstorageid", intValue) || readXMLInteger(p, "storageId", intValue)) - startStorageId = intValue; + std::string startStorageId; + if(readXMLString(p, "startstorageid", strValue) || readXMLString(p, "storageId", strValue)) + startStorageId = strValue; int32_t startStorageValue = 0; if(readXMLInteger(p, "startstoragevalue", intValue) || readXMLInteger(p, "storageValue", intValue)) @@ -187,50 +205,63 @@ bool Quests::parseQuestNode(xmlNodePtr p, bool checkDuplicate) if(xmlStrcmp(missionNode->name, (const xmlChar*)"mission")) continue; - std::string missionName, missionState; + std::string missionName, missionState, storageId; if(readXMLString(missionNode, "name", strValue)) missionName = strValue; if(readXMLString(missionNode, "state", strValue) || readXMLString(missionNode, "description", strValue)) missionState = strValue; - uint32_t storageId = 0; - if(readXMLInteger(missionNode, "storageid", intValue) || readXMLInteger(p, "storageId", intValue)) - storageId = intValue; + if(readXMLString(missionNode, "storageid", strValue) || readXMLString(missionNode, "storageId", strValue)) + storageId = strValue; int32_t startValue = 0, endValue = 0; - if(readXMLInteger(missionNode, "startvalue", intValue) || readXMLInteger(p, "startValue", intValue)) + if(readXMLInteger(missionNode, "startvalue", intValue) || readXMLInteger(missionNode, "startValue", intValue)) startValue = intValue; - if(readXMLInteger(missionNode, "endvalue", intValue) || readXMLInteger(p, "endValue", intValue)) + if(readXMLInteger(missionNode, "endvalue", intValue) || readXMLInteger(missionNode, "endValue", intValue)) endValue = intValue; - if(Mission* mission = new Mission(missionName, missionState, storageId, startValue, endValue)) + bool notify = true; + if(readXMLString(missionNode, "notify", strValue)) + notify = booleanString(strValue); + + if(Mission* mission = new Mission(missionName, missionState, storageId, startValue, endValue, notify)) { - if(missionState.empty()) + // parse sub-states only if main is not set + for(xmlNodePtr stateNode = missionNode->children; stateNode; stateNode = stateNode->next) { - // parse sub-states only if main is not set - for(xmlNodePtr stateNode = missionNode->children; stateNode; stateNode = stateNode->next) + if(xmlStrcmp(stateNode->name, (const xmlChar*)"missionstate")) + continue; + + if(!readXMLString(stateNode, "id", strValue)) { - if(xmlStrcmp(stateNode->name, (const xmlChar*)"missionstate")) - continue; + std::clog << "[Warning - Quests::parseQuestNode] Missing missionId for mission state" << std::endl; + continue; + } + + std::string description, strDesc; + if(readXMLString(stateNode, "description", strDesc)) + description = strDesc; - uint32_t missionId; - if(!readXMLInteger(stateNode, "id", intValue)) + StringVec strVector = explodeString(strValue, "-"); + if(strVector.size() > 1) + { + IntegerVec intVector = vectorAtoi(strVector); + if(intVector[0] && intVector[1]) { - std::cout << "[Warning - Quests::parseQuestNode] Missing missionId for mission state" << std::endl; - continue; + for(int32_t i = intVector[0]; i <= intVector[1]; i++) + mission->newState(i, description); } + else + std::clog << "Invalid mission state id '" << strValue << "' for mission '" << mission->getName(NULL) << "'" << std::endl; - missionId = intValue; - std::string description; - if(readXMLString(stateNode, "description", strValue)) - description = strValue; - - mission->newState(missionId, description); + continue; } + else + mission->newState(atoi(strValue.c_str()), description); } - + quest->newMission(mission); } } @@ -249,10 +280,41 @@ bool Quests::parseQuestNode(xmlNodePtr p, bool checkDuplicate) return true; } +bool Quests::isQuestStorage(const std::string& key, const std::string& value, bool notification) const +{ + int32_t _value = atoi(value.c_str()); + for(QuestList::const_iterator it = quests.begin(); it != quests.end(); ++it) + { + if(value.empty()) + { + if((*it)->getStorageId() == key) + return true; + } + else if((*it)->getStorageId() == key && (_value && _value == (*it)->getStorageValue())) + return true; + + for(MissionList::const_iterator mit = (*it)->getFirstMission(); mit != (*it)->getLastMission(); ++mit) + { + if(notification && !(*mit)->isNotifying()) + continue; + + if(value.empty()) + { + if((*mit)->getStorageId() == key) + return true; + } + else if((*mit)->getStorageId() == key && (_value && _value >= (*mit)->getStartValue() && _value <= (*mit)->getEndValue())) + return true; + } + } + + return false; +} + uint16_t Quests::getQuestCount(Player* player) { uint16_t count = 0; - for(QuestList::iterator it = quests.begin(); it != quests.end(); it++) + for(QuestList::iterator it = quests.begin(); it != quests.end(); ++it) { if((*it)->isStarted(player)) count++; @@ -263,11 +325,11 @@ uint16_t Quests::getQuestCount(Player* player) Quest* Quests::getQuestById(uint16_t id) const { - for(QuestList::const_iterator it = quests.begin(); it != quests.end(); it++) + for(QuestList::const_iterator it = quests.begin(); it != quests.end(); ++it) { if((*it)->getId() == id) return (*it); } return NULL; -} +} \ No newline at end of file diff --git a/quests.h b/quests.h index 26cc522..ae51a28 100644 --- a/quests.h +++ b/quests.h @@ -26,13 +26,14 @@ typedef std::map StateMap; class Mission { public: - Mission(std::string _name, std::string _state, uint32_t _storageId, int32_t _startValue, int32_t _endValue) + Mission(std::string _name, std::string _state, std::string _storageId, int32_t _startValue, int32_t _endValue, bool _notify) { name = _name; state = _state; - endValue = _endValue; startValue = _startValue; + endValue = _endValue; storageId = _storageId; + notify = _notify; } virtual ~Mission() {states.clear();} @@ -40,28 +41,37 @@ class Mission bool isStarted(Player* player); bool isCompleted(Player* player); + bool isNotifying() const {return notify;} std::string getName(Player* player) {return (isCompleted(player) ? (name + " (completed)") : name);} std::string getDescription(Player* player); + int32_t getStartValue() {return startValue;} + int32_t getEndValue() {return endValue;} + + const std::string& getStorageId() const {return storageId;} + private: + std::string parseStorages(std::string state, std::string value, Player* player); + std::string name, state; StateMap states; + bool notify; int32_t startValue, endValue; - uint32_t storageId; + std::string storageId; }; typedef std::list MissionList; class Quest { public: - Quest(std::string _name, uint16_t _id, uint32_t _storageId, int32_t _storageValue) + Quest(std::string _name, uint16_t _id, std::string _storageId, int32_t _storageValue) { name = _name; id = _id; - storageId = _storageId; storageValue = _storageValue; + storageId = _storageId; } virtual ~Quest(); @@ -72,6 +82,8 @@ class Quest uint16_t getId() const {return id;} const std::string& getName() const {return name;} + const std::string& getStorageId() const {return storageId;} + int32_t getStorageValue() const {return storageValue;} uint16_t getMissionCount(Player* player); inline MissionList::const_iterator getFirstMission() const {return missions.begin();} @@ -83,7 +95,7 @@ class Quest uint16_t id; int32_t storageValue; - uint32_t storageId; + std::string storageId; }; typedef std::list QuestList; @@ -103,6 +115,7 @@ class Quests bool loadFromXml(); bool parseQuestNode(xmlNodePtr p, bool checkDuplicate); + bool isQuestStorage(const std::string& key, const std::string& value, bool notification) const; uint16_t getQuestCount(Player* player); inline QuestList::const_iterator getFirstQuest() const {return quests.begin();} diff --git a/raids.cpp b/raids.cpp index c0ac571..25e07dc 100644 --- a/raids.cpp +++ b/raids.cpp @@ -25,6 +25,8 @@ extern Game g_game; extern ConfigManager g_config; +LuaInterface ScriptEvent::m_interface("Raid Interface"); + Raids::Raids() { running = NULL; @@ -41,14 +43,14 @@ bool Raids::parseRaidNode(xmlNodePtr raidNode, bool checkDuplicate, FileType_t p std::string strValue; if(!readXMLString(raidNode, "name", strValue)) { - std::cout << "[Error - Raids::parseRaidNode] name tag missing for raid." << std::endl; + std::clog << "[Error - Raids::parseRaidNode] name tag missing for raid." << std::endl; return false; } std::string name = strValue; if(!readXMLInteger(raidNode, "interval2", intValue) || intValue <= 0) { - std::cout << "[Error - Raids::parseRaidNode] interval2 tag missing or divided by 0 for raid " << name << std::endl; + std::clog << "[Error - Raids::parseRaidNode] interval2 tag missing or divided by 0 for raid " << name << std::endl; return false; } @@ -57,7 +59,7 @@ bool Raids::parseRaidNode(xmlNodePtr raidNode, bool checkDuplicate, FileType_t p if(!readXMLString(raidNode, "file", strValue)) { file = name + ".xml"; - std::cout << "[Warning - Raids::parseRaidNode] file tag missing for raid " << name << ", using default: " << file << std::endl; + std::clog << "[Warning - Raids::parseRaidNode] file tag missing for raid " << name << ", using default: " << file << std::endl; } else file = strValue; @@ -65,7 +67,7 @@ bool Raids::parseRaidNode(xmlNodePtr raidNode, bool checkDuplicate, FileType_t p file = getFilePath(pathing, "raids/" + file); uint64_t margin = 0; if(!readXMLInteger(raidNode, "margin", intValue)) - std::cout << "[Warning - Raids::parseRaidNode] margin tag missing for raid " << name << ", using default: " << margin << std::endl; + std::clog << "[Warning - Raids::parseRaidNode] margin tag missing for raid " << name << ", using default: " << margin << std::endl; else margin = intValue * 60 * 1000; @@ -78,7 +80,7 @@ bool Raids::parseRaidNode(xmlNodePtr raidNode, bool checkDuplicate, FileType_t p else if(tmpStrValue == "block") refType = REF_BLOCK; else if(tmpStrValue != "none") - std::cout << "[Warning - Raids::parseRaidNode] Unknown reftype \"" << strValue << "\" for raid " << name << std::endl; + std::clog << "[Warning - Raids::parseRaidNode] Unknown reftype \"" << strValue << "\" for raid " << name << std::endl; } bool ref = false; @@ -93,7 +95,7 @@ bool Raids::parseRaidNode(xmlNodePtr raidNode, bool checkDuplicate, FileType_t p if(!raid || !raid->loadFromXml(file)) { delete raid; - std::cout << "[Fatal - Raids::parseRaidNode] failed to load raid " << name << std::endl; + std::clog << "[Fatal - Raids::parseRaidNode] failed to load raid " << name << std::endl; return false; } @@ -118,25 +120,21 @@ bool Raids::loadFromXml() xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_OTHER, "raids/raids.xml").c_str()); if(!doc) { - std::cout << "[Warning - Raids::loadFromXml] Could not load raids file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Raids::loadFromXml] Could not load raids file." + << std::endl << getLastXMLError() << std::endl; return false; } - xmlNodePtr raidNode, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"raids")) { - std::cout << "[Error - Raids::loadFromXml] Malformed raids file." << std::endl; + std::clog << "[Error - Raids::loadFromXml] Malformed raids file." << std::endl; xmlFreeDoc(doc); return false; } - raidNode = root->children; - while(raidNode) - { - parseRaidNode(raidNode, false, FILE_TYPE_OTHER); - raidNode = raidNode->next; - } + for(xmlNodePtr p = root->children; p; p = p->next) + parseRaidNode(p, false, FILE_TYPE_OTHER); xmlFreeDoc(doc); loaded = true; @@ -160,7 +158,7 @@ void Raids::checkRaids() { checkRaidsEvent = Scheduler::getInstance().addEvent(createSchedulerTask( CHECK_RAIDS_INTERVAL * 1000, boost::bind(&Raids::checkRaids, this))); - if(getRunning()) + if(running) return; uint64_t now = OTSYS_TIME(); @@ -196,10 +194,10 @@ bool Raids::reload() Raid* Raids::getRaidByName(const std::string& name) { RaidList::iterator it; - for(it = raidList.begin(); it != raidList.end(); it++) + for(it = raidList.begin(); it != raidList.end(); ++it) { - if(!strcasecmp((*it)->getName().c_str(), name.c_str())) - return (*it); + if(boost::algorithm::iequals((*it)->getName(), name)) + return *it; } return NULL; @@ -222,8 +220,8 @@ Raid::Raid(const std::string& _name, uint32_t _interval, uint64_t _margin, Raid::~Raid() { stopEvents(); - for(RaidEventVector::iterator it = raidEvents.begin(); it != raidEvents.end(); it++) - delete (*it); + for(RaidEventVector::iterator it = raidEvents.begin(); it != raidEvents.end(); ++it) + delete *it; raidEvents.clear(); } @@ -236,25 +234,23 @@ bool Raid::loadFromXml(const std::string& _filename) xmlDocPtr doc = xmlParseFile(_filename.c_str()); if(!doc) { - std::cout << "[Error - Raid::loadFromXml] Could not load raid file (" << _filename << ")." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Error - Raid::loadFromXml] Could not load raid file " << _filename + << std::endl << std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr root, eventNode; - root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"raid")) { - std::cout << "[Error - Raid::loadFromXml] Malformed raid file (" << _filename << ")." << std::endl; + std::clog << "[Error - Raid::loadFromXml] Malformed raid file " << _filename << std::endl; xmlFreeDoc(doc); return false; } std::string strValue; - eventNode = root->children; - while(eventNode) + for(xmlNodePtr eventNode = root->children; eventNode; eventNode = eventNode->next) { - RaidEvent* event; + RaidEvent* event = NULL; if(!xmlStrcmp(eventNode->name, (const xmlChar*)"announce")) event = new AnnounceEvent(this, ref); else if(!xmlStrcmp(eventNode->name, (const xmlChar*)"effect")) @@ -268,20 +264,15 @@ bool Raid::loadFromXml(const std::string& _filename) else if(!xmlStrcmp(eventNode->name, (const xmlChar*)"script")) event = new ScriptEvent(this, ref); else - { - eventNode = eventNode->next; continue; - } if(!event->configureRaidEvent(eventNode)) { - std::cout << "[Error - Raid::loadFromXml] Could not configure raid in file: " << _filename << ", eventNode: " << eventNode->name << std::endl; + std::clog << "[Error - Raid::loadFromXml] Could not configure raid in file: " << _filename << ", eventNode: " << eventNode->name << std::endl; delete event; } else raidEvents.push_back(event); - - eventNode = eventNode->next; } //sort by delay time @@ -309,7 +300,7 @@ bool Raid::startRaid() bool Raid::executeRaidEvent(RaidEvent* raidEvent) { - if(!raidEvent->executeEvent()) + if(!raidEvent->executeEvent(name)) return !resetRaid(false); RaidEvent* newRaidEvent = getNextRaidEvent(); @@ -381,7 +372,7 @@ bool AnnounceEvent::configureRaidEvent(xmlNodePtr eventNode) std::string strValue; if(!readXMLString(eventNode, "message", strValue)) { - std::cout << "[Error - AnnounceEvent::configureRaidEvent] Message tag missing for announce event." << std::endl; + std::clog << "[Error - AnnounceEvent::configureRaidEvent] Message tag missing for announce event." << std::endl; return false; } @@ -404,17 +395,17 @@ bool AnnounceEvent::configureRaidEvent(xmlNodePtr eventNode) else if(tmpStrValue == "red") m_messageType = MSG_STATUS_CONSOLE_RED; else - std::cout << "[Notice - AnnounceEvent::configureRaidEvent] Unknown type tag for announce event, using default: " + std::clog << "[Notice - AnnounceEvent::configureRaidEvent] Unknown type tag for announce event, using default: " << (int32_t)m_messageType << std::endl; } else - std::cout << "[Notice - AnnounceEvent::configureRaidEvent] Missing type tag for announce event. Using default: " + std::clog << "[Notice - AnnounceEvent::configureRaidEvent] Missing type tag for announce event. Using default: " << (int32_t)m_messageType << std::endl; return true; } -bool AnnounceEvent::executeEvent() const +bool AnnounceEvent::executeEvent(const std::string&) const { g_game.broadcastMessage(m_message, m_messageType); return true; @@ -431,7 +422,7 @@ bool EffectEvent::configureRaidEvent(xmlNodePtr eventNode) { if(!readXMLString(eventNode, "name", strValue)) { - std::cout << "[Error - EffectEvent::configureRaidEvent] id (or name) tag missing for effect event." << std::endl; + std::clog << "[Error - EffectEvent::configureRaidEvent] id (or name) tag missing for effect event." << std::endl; return false; } else @@ -444,21 +435,21 @@ bool EffectEvent::configureRaidEvent(xmlNodePtr eventNode) { if(!readXMLInteger(eventNode, "x", intValue)) { - std::cout << "[Error - EffectEvent::configureRaidEvent] x tag missing for effect event." << std::endl; + std::clog << "[Error - EffectEvent::configureRaidEvent] x tag missing for effect event." << std::endl; return false; } m_position.x = intValue; if(!readXMLInteger(eventNode, "y", intValue)) { - std::cout << "[Error - EffectEvent::configureRaidEvent] y tag missing for effect event." << std::endl; + std::clog << "[Error - EffectEvent::configureRaidEvent] y tag missing for effect event." << std::endl; return false; } m_position.y = intValue; if(!readXMLInteger(eventNode, "z", intValue)) { - std::cout << "[Error - EffectEvent::configureRaidEvent] z tag missing for effect event." << std::endl; + std::clog << "[Error - EffectEvent::configureRaidEvent] z tag missing for effect event." << std::endl; return false; } @@ -469,7 +460,7 @@ bool EffectEvent::configureRaidEvent(xmlNodePtr eventNode) IntegerVec posList = vectorAtoi(explodeString(strValue, ";")); if(posList.size() < 3) { - std::cout << "[Error - EffectEvent::configureRaidEvent] Malformed pos tag for effect event." << std::endl; + std::clog << "[Error - EffectEvent::configureRaidEvent] Malformed pos tag for effect event." << std::endl; return false; } @@ -479,7 +470,7 @@ bool EffectEvent::configureRaidEvent(xmlNodePtr eventNode) return true; } -bool EffectEvent::executeEvent() const +bool EffectEvent::executeEvent(const std::string&) const { g_game.addMagicEffect(m_position, m_effect); return true; @@ -496,7 +487,7 @@ bool ItemSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) { if(!readXMLString(eventNode, "name", strValue)) { - std::cout << "[Error - ItemSpawnEvent::configureRaidEvent] id (or name) tag missing for itemspawn event." << std::endl; + std::clog << "[Error - ItemSpawnEvent::configureRaidEvent] id (or name) tag missing for itemspawn event." << std::endl; return false; } else @@ -515,21 +506,21 @@ bool ItemSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) { if(!readXMLInteger(eventNode, "x", intValue)) { - std::cout << "[Error - ItemSpawnEvent::configureRaidEvent] x tag missing for itemspawn event." << std::endl; + std::clog << "[Error - ItemSpawnEvent::configureRaidEvent] x tag missing for itemspawn event." << std::endl; return false; } m_position.x = intValue; if(!readXMLInteger(eventNode, "y", intValue)) { - std::cout << "[Error - ItemSpawnEvent::configureRaidEvent] y tag missing for itemspawn event." << std::endl; + std::clog << "[Error - ItemSpawnEvent::configureRaidEvent] y tag missing for itemspawn event." << std::endl; return false; } m_position.y = intValue; if(!readXMLInteger(eventNode, "z", intValue)) { - std::cout << "[Error - ItemSpawnEvent::configureRaidEvent] z tag missing for itemspawn event." << std::endl; + std::clog << "[Error - ItemSpawnEvent::configureRaidEvent] z tag missing for itemspawn event." << std::endl; return false; } @@ -540,7 +531,7 @@ bool ItemSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) IntegerVec posList = vectorAtoi(explodeString(strValue, ";")); if(posList.size() < 3) { - std::cout << "[Error - ItemSpawnEvent::configureRaidEvent] Malformed pos tag for itemspawn event." << std::endl; + std::clog << "[Error - ItemSpawnEvent::configureRaidEvent] Malformed pos tag for itemspawn event." << std::endl; return false; } @@ -550,7 +541,7 @@ bool ItemSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) return true; } -bool ItemSpawnEvent::executeEvent() const +bool ItemSpawnEvent::executeEvent(const std::string&) const { if(m_chance < (uint32_t)random_range(0, (int32_t)MAX_ITEM_CHANCE)) return true; @@ -558,7 +549,7 @@ bool ItemSpawnEvent::executeEvent() const Tile* tile = g_game.getTile(m_position); if(!tile) { - std::cout << "[Fatal - ItemSpawnEvent::executeEvent] Missing tile at position " << m_position << std::endl; + std::clog << "[Fatal - ItemSpawnEvent::executeEvent] Missing tile at position " << m_position << std::endl; return false; } @@ -574,14 +565,14 @@ bool ItemSpawnEvent::executeEvent() const Item* newItem = Item::CreateItem(m_itemId, stackCount); if(!newItem) { - std::cout << "[Error - ItemSpawnEvent::executeEvent] Cannot create item with id " << m_itemId << std::endl; + std::clog << "[Error - ItemSpawnEvent::executeEvent] Cannot create item with id " << m_itemId << std::endl; return false; } ReturnValue ret = g_game.internalAddItem(NULL, tile, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT); if(ret != RET_NOERROR) { - std::cout << "[Error - ItemSpawnEvent::executeEvent] Cannot spawn item with id " << m_itemId << std::endl; + std::clog << "[Error - ItemSpawnEvent::executeEvent] Cannot spawn item with id " << m_itemId << std::endl; return false; } @@ -597,14 +588,14 @@ bool ItemSpawnEvent::executeEvent() const Item* newItem = Item::CreateItem(m_itemId, m_subType); if(!newItem) { - std::cout << "[Error - ItemSpawnEvent::executeEvent] Cannot create item with id " << m_itemId << std::endl; + std::clog << "[Error - ItemSpawnEvent::executeEvent] Cannot create item with id " << m_itemId << std::endl; return false; } ReturnValue ret = g_game.internalAddItem(NULL, tile, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT); if(ret != RET_NOERROR) { - std::cout << "[Error - ItemSpawnEvent::executeEvent] Cannot spawn item with id " << m_itemId << std::endl; + std::clog << "[Error - ItemSpawnEvent::executeEvent] Cannot spawn item with id " << m_itemId << std::endl; return false; } @@ -626,7 +617,7 @@ bool SingleSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) std::string strValue; if(!readXMLString(eventNode, "name", strValue)) { - std::cout << "[Error - SingleSpawnEvent::configureRaidEvent] name tag missing for singlespawn event." << std::endl; + std::clog << "[Error - SingleSpawnEvent::configureRaidEvent] name tag missing for singlespawn event." << std::endl; return false; } @@ -636,21 +627,21 @@ bool SingleSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) int32_t intValue; if(!readXMLInteger(eventNode, "x", intValue)) { - std::cout << "[Error - SingleSpawnEvent::configureRaidEvent] x tag missing for singlespawn event." << std::endl; + std::clog << "[Error - SingleSpawnEvent::configureRaidEvent] x tag missing for singlespawn event." << std::endl; return false; } m_position.x = intValue; if(!readXMLInteger(eventNode, "y", intValue)) { - std::cout << "[Error - SingleSpawnEvent::configureRaidEvent] y tag missing for singlespawn event." << std::endl; + std::clog << "[Error - SingleSpawnEvent::configureRaidEvent] y tag missing for singlespawn event." << std::endl; return false; } m_position.y = intValue; if(!readXMLInteger(eventNode, "z", intValue)) { - std::cout << "[Error - SingleSpawnEvent::configureRaidEvent] z tag missing for singlespawn event." << std::endl; + std::clog << "[Error - SingleSpawnEvent::configureRaidEvent] z tag missing for singlespawn event." << std::endl; return false; } @@ -661,7 +652,7 @@ bool SingleSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) IntegerVec posList = vectorAtoi(explodeString(strValue, ";")); if(posList.size() < 3) { - std::cout << "[Error - SingleSpawnEvent::configureRaidEvent] Malformed pos tag for singlespawn event." << std::endl; + std::clog << "[Error - SingleSpawnEvent::configureRaidEvent] Malformed pos tag for singlespawn event." << std::endl; return false; } @@ -671,19 +662,19 @@ bool SingleSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) return true; } -bool SingleSpawnEvent::executeEvent() const +bool SingleSpawnEvent::executeEvent(const std::string&) const { Monster* monster = Monster::createMonster(m_monsterName); if(!monster) { - std::cout << "[Error - SingleSpawnEvent::executeEvent] Cannot create monster " << m_monsterName << std::endl; + std::clog << "[Error - SingleSpawnEvent::executeEvent] Cannot create monster " << m_monsterName << std::endl; return false; } - if(!g_game.placeCreature(monster, m_position)) + if(!g_game.placeCreature(monster, m_position, false, true)) { delete monster; - std::cout << "[Error - SingleSpawnEvent::executeEvent] Cannot spawn monster " << m_monsterName << std::endl; + std::clog << "[Error - SingleSpawnEvent::executeEvent] Cannot spawn monster " << m_monsterName << std::endl; return false; } @@ -712,7 +703,7 @@ bool AreaSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) IntegerVec posList = vectorAtoi(explodeString(strValue, ";")); if(posList.size() < 3) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] Malformed centerPosition tag for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] Malformed centerPosition tag for areaspawn event." << std::endl; return false; } @@ -722,21 +713,21 @@ bool AreaSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) { if(!readXMLInteger(eventNode, "centerx", intValue)) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] centerx tag missing for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] centerx tag missing for areaspawn event." << std::endl; return false; } centerPos.x = intValue; if(!readXMLInteger(eventNode, "centery", intValue)) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] centery tag missing for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] centery tag missing for areaspawn event." << std::endl; return false; } centerPos.y = intValue; if(!readXMLInteger(eventNode, "centerz", intValue)) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] centerz tag missing for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] centerz tag missing for areaspawn event." << std::endl; return false; } @@ -758,7 +749,7 @@ bool AreaSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) IntegerVec posList = vectorAtoi(explodeString(strValue, ";")); if(posList.size() < 3) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] Malformed fromPosition tag for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] Malformed fromPosition tag for areaspawn event." << std::endl; return false; } @@ -768,21 +759,21 @@ bool AreaSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) { if(!readXMLInteger(eventNode, "fromx", intValue)) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] fromx tag missing for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] fromx tag missing for areaspawn event." << std::endl; return false; } m_fromPos.x = intValue; if(!readXMLInteger(eventNode, "fromy", intValue)) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] fromy tag missing for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] fromy tag missing for areaspawn event." << std::endl; return false; } m_fromPos.y = intValue; if(!readXMLInteger(eventNode, "fromz", intValue)) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] fromz tag missing for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] fromz tag missing for areaspawn event." << std::endl; return false; } @@ -794,7 +785,7 @@ bool AreaSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) IntegerVec posList = vectorAtoi(explodeString(strValue, ";")); if(posList.size() < 3) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] Malformed toPosition tag for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] Malformed toPosition tag for areaspawn event." << std::endl; return false; } @@ -804,21 +795,21 @@ bool AreaSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) { if(!readXMLInteger(eventNode, "tox", intValue)) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] tox tag missing for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] tox tag missing for areaspawn event." << std::endl; return false; } m_toPos.x = intValue; if(!readXMLInteger(eventNode, "toy", intValue)) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] toy tag missing for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] toy tag missing for areaspawn event." << std::endl; return false; } m_toPos.y = intValue; if(!readXMLInteger(eventNode, "toz", intValue)) { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] toz tag missing for areaspawn event." << std::endl; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] toz tag missing for areaspawn event." << std::endl; return false; } @@ -826,40 +817,37 @@ bool AreaSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) } } - xmlNodePtr monsterNode = eventNode->children; - while(monsterNode) + for(xmlNodePtr monsterNode = eventNode->children; monsterNode; monsterNode = monsterNode->next) { - if(!xmlStrcmp(monsterNode->name, (const xmlChar*)"monster")) + if(xmlStrcmp(monsterNode->name, (const xmlChar*)"monster")) + continue; + + if(!readXMLString(monsterNode, "name", strValue)) { - if(!readXMLString(monsterNode, "name", strValue)) - { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] name tag missing for monster node." << std::endl; - return false; - } + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] name tag missing for monster node." << std::endl; + return false; + } - std::string name = strValue; - int32_t min = 0, max = 0; - if(readXMLInteger(monsterNode, "min", intValue) || readXMLInteger(monsterNode, "maxamount", intValue)) - min = intValue; + std::string name = strValue; + int32_t min = 0, max = 0; + if(readXMLInteger(monsterNode, "min", intValue) || readXMLInteger(monsterNode, "minamount", intValue)) + min = intValue; - if(readXMLInteger(monsterNode, "max", intValue) || readXMLInteger(monsterNode, "maxamount", intValue)) - max = intValue; + if(readXMLInteger(monsterNode, "max", intValue) || readXMLInteger(monsterNode, "maxamount", intValue)) + max = intValue; - if(!min && !max) + if(!min && !max) + { + if(!readXMLInteger(monsterNode, "amount", intValue)) { - if(!readXMLInteger(monsterNode, "amount", intValue)) - { - std::cout << "[Error - AreaSpawnEvent::configureRaidEvent] amount tag missing for monster node." << std::endl; - return false; - } - - min = max = intValue; + std::clog << "[Error - AreaSpawnEvent::configureRaidEvent] amount tag missing for monster node." << std::endl; + return false; } - addMonster(name, min, max); + min = max = intValue; } - monsterNode = monsterNode->next; + addMonster(name, min, max); } return true; @@ -867,8 +855,8 @@ bool AreaSpawnEvent::configureRaidEvent(xmlNodePtr eventNode) AreaSpawnEvent::~AreaSpawnEvent() { - for(MonsterSpawnList::iterator it = m_spawnList.begin(); it != m_spawnList.end(); it++) - delete (*it); + for(MonsterSpawnList::iterator it = m_spawnList.begin(); it != m_spawnList.end(); ++it) + delete *it; m_spawnList.clear(); } @@ -888,18 +876,21 @@ void AreaSpawnEvent::addMonster(const std::string& name, uint32_t min, uint32_t addMonster(monsterSpawn); } -bool AreaSpawnEvent::executeEvent() const +bool AreaSpawnEvent::executeEvent(const std::string&) const { - for(MonsterSpawnList::const_iterator it = m_spawnList.begin(); it != m_spawnList.end(); it++) + MonsterSpawn* spawn = NULL; + for(MonsterSpawnList::const_iterator it = m_spawnList.begin(); it != m_spawnList.end(); ++it) { - MonsterSpawn* spawn = *it; + if(!(spawn = *it)) + continue; + uint32_t amount = (uint32_t)random_range(spawn->min, spawn->max); for(uint32_t i = 0; i < amount; ++i) { Monster* monster = Monster::createMonster(spawn->name); if(!monster) { - std::cout << "[Error - AreaSpawnEvent::executeEvent] Cannot create monster " << spawn->name << std::endl; + std::clog << "[Error - AreaSpawnEvent::executeEvent] Cannot create monster " << spawn->name << std::endl; return false; } @@ -907,7 +898,7 @@ bool AreaSpawnEvent::executeEvent() const for(int32_t t = 0; t < MAXIMUM_TRIES_PER_MONSTER; ++t) { if(!g_game.placeCreature(monster, Position(random_range(m_fromPos.x, m_toPos.x), - random_range(m_fromPos.y, m_toPos.y), random_range(m_fromPos.z, m_toPos.z)))) + random_range(m_fromPos.y, m_toPos.y), random_range(m_fromPos.z, m_toPos.z)), true)) continue; if(m_raid->usesRef() && m_ref) @@ -928,45 +919,60 @@ bool AreaSpawnEvent::executeEvent() const return true; } -LuaScriptInterface ScriptEvent::m_interface("Raid Interface"); - bool ScriptEvent::configureRaidEvent(xmlNodePtr eventNode) { if(!RaidEvent::configureRaidEvent(eventNode)) return false; std::string scriptsName = Raids::getInstance()->getScriptBaseName(); - if(!m_interface.loadDirectory(getFilePath(FILE_TYPE_OTHER, std::string(scriptsName + "/lib/")))) - std::cout << "[Warning - ScriptEvent::configureRaidEvent] Cannot load " << scriptsName << "/lib/" << std::endl; + if(!m_interface.getState()) + { + m_interface.initState(); + std::string path = getFilePath(FILE_TYPE_OTHER, std::string(scriptsName + "/lib/")); + if(!m_interface.loadDirectory(path, false, true)) + std::clog << "[Warning - ScriptEvent::configureRaidEvent] Cannot load " << path << std::endl; + } std::string strValue; if(readXMLString(eventNode, "file", strValue)) { - if(!loadScript(getFilePath(FILE_TYPE_OTHER, std::string(scriptsName + "/scripts/" + strValue)), true)) + std::string path = getFilePath(FILE_TYPE_OTHER, std::string(scriptsName + "/scripts/" + strValue)); + if(!fileExists(path.c_str())) + path = getFilePath(FILE_TYPE_MOD, std::string("/scripts/" + strValue)); + + if(!fileExists(path.c_str())) { - std::cout << "[Error - ScriptEvent::configureRaidEvent] Cannot load raid script file (" << strValue << ")." << std::endl; + std::clog << "[Error - ScriptEvent::configureRaidEvent] Cannot find script file " << strValue << std::endl; return false; } - } - else if(!parseXMLContentString(eventNode->children, strValue) && !loadBuffer(strValue)) - { - std::cout << "[Error - ScriptEvent::configureRaidEvent] Cannot load raid script buffer." << std::endl; + + if(checkScript(scriptsName, path, true) && loadScript(path, true)) + return true; + + std::clog << "[Error - ScriptEvent::configureRaidEvent] Cannot load script file " << path << std::endl; return false; } + else if(parseXMLContentString(eventNode->children, strValue) && + checkBuffer(scriptsName, strValue) && loadBuffer(strValue)) + return true; - return true; + std::clog << "[Error - ScriptEvent::configureRaidEvent] Cannot load script buffer." << std::endl; + return false; } -bool ScriptEvent::executeEvent() const +bool ScriptEvent::executeEvent(const std::string& name) const { - //onRaid() + //onRaid(name) if(m_interface.reserveEnv()) { ScriptEnviroment* env = m_interface.getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { + std::stringstream scriptstream; + scriptstream << "local name = \"" << name << "\"" << std::endl; + bool result = true; - if(m_interface.loadBuffer(m_scriptData)) + if(m_scriptData && m_interface.loadBuffer(*m_scriptData)) { lua_State* L = m_interface.getState(); result = m_interface.getGlobalBool(L, "_result", true); @@ -978,19 +984,22 @@ bool ScriptEvent::executeEvent() const else { #ifdef __DEBUG_LUASCRIPTS__ - env->setEventDesc("Raid event"); + env->setEvent("Raid event"); #endif env->setScriptId(m_scriptId, &m_interface); + lua_State* L = m_interface.getState(); + m_interface.pushFunction(m_scriptId); + lua_pushstring(L, name.c_str()); - bool result = m_interface.callFunction(0); + bool result = m_interface.callFunction(1); m_interface.releaseEnv(); return result; } } else { - std::cout << "[Error - ScriptEvent::executeEvent] Call stack overflow." << std::endl; + std::clog << "[Error - ScriptEvent::executeEvent] Call stack overflow." << std::endl; return false; } } diff --git a/raids.h b/raids.h index 4e6b0c4..2ef02cb 100644 --- a/raids.h +++ b/raids.h @@ -145,7 +145,7 @@ class RaidEvent virtual ~RaidEvent() {} virtual bool configureRaidEvent(xmlNodePtr eventNode); - virtual bool executeEvent() const {return false;} + virtual bool executeEvent(const std::string&) const {return false;} uint32_t getDelay() const {return m_delay;} static bool compareEvents(const RaidEvent* lhs, const RaidEvent* rhs) @@ -169,7 +169,7 @@ class AnnounceEvent : public RaidEvent virtual ~AnnounceEvent() {} virtual bool configureRaidEvent(xmlNodePtr eventNode); - virtual bool executeEvent() const; + virtual bool executeEvent(const std::string& name) const; private: std::string m_message; @@ -184,7 +184,7 @@ class EffectEvent : public RaidEvent virtual ~EffectEvent() {} virtual bool configureRaidEvent(xmlNodePtr eventNode); - virtual bool executeEvent() const; + virtual bool executeEvent(const std::string& name) const; private: MagicEffect_t m_effect; @@ -199,7 +199,7 @@ class ItemSpawnEvent : public RaidEvent virtual ~ItemSpawnEvent() {} virtual bool configureRaidEvent(xmlNodePtr eventNode); - virtual bool executeEvent() const; + virtual bool executeEvent(const std::string& name) const; private: int16_t m_itemId; @@ -216,7 +216,7 @@ class SingleSpawnEvent : public RaidEvent virtual ~SingleSpawnEvent() {} virtual bool configureRaidEvent(xmlNodePtr eventNode); - virtual bool executeEvent() const; + virtual bool executeEvent(const std::string& name) const; private: std::string m_monsterName; @@ -230,7 +230,7 @@ class AreaSpawnEvent : public RaidEvent virtual ~AreaSpawnEvent(); virtual bool configureRaidEvent(xmlNodePtr eventNode); - virtual bool executeEvent() const; + virtual bool executeEvent(const std::string& name) const; void addMonster(MonsterSpawn* _spawn); void addMonster(const std::string& name, uint32_t min, uint32_t max); @@ -244,14 +244,14 @@ class ScriptEvent : public RaidEvent, public Event { public: ScriptEvent(Raid* raid, bool ref): RaidEvent(raid, ref), - Event(&m_interface) {m_interface.initState();} + Event(&m_interface) {} virtual ~ScriptEvent() {} virtual bool configureRaidEvent(xmlNodePtr eventNode); - virtual bool executeEvent() const; + virtual bool executeEvent(const std::string& name) const; - virtual bool configureEvent(xmlNodePtr p) {return false;} - static LuaScriptInterface m_interface; + virtual bool configureEvent(xmlNodePtr) {return false;} + static LuaInterface m_interface; protected: virtual std::string getScriptEventName() const {return "onRaid";} diff --git a/resources.h b/resources.h deleted file mode 100644 index 7c72165..0000000 --- a/resources.h +++ /dev/null @@ -1,93 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// - -#ifndef __RESOURCES__ -#define __RESOURCES__ -#include "definitions.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) - #define ID_KICK 101 - #define ID_BAN 102 - #define ID_ABOUT 104 - #define ID_LOG 105 - #define ID_ICON 106 - - #define ID_TRAY_HIDE 118 - #define ID_TRAY_SHUTDOWN 119 - #define ID_STATUS_BAR 120 - - #define ID_MENU 200 - - #define ID_MENU_MAIN_REJECT 201 - #define ID_MENU_MAIN_ACCEPT 202 - #define ID_MENU_MAIN_CLEARLOG 203 - #define ID_MENU_MAIN_SHUTDOWN 204 - - #define ID_MENU_SERVER_WORLDTYPE_NOPVP 205 - #define ID_MENU_SERVER_WORLDTYPE_PVP 206 - #define ID_MENU_SERVER_WORLDTYPE_PVPENFORCED 207 - - #define ID_MENU_SERVER_BROADCAST 208 - #define ID_MENU_SERVER_SAVE 209 - #define ID_MENU_SERVER_CLEAN 210 - #define ID_MENU_SERVER_REFRESH 211 - #define ID_MENU_SERVER_CLOSE 212 - #define ID_MENU_SERVER_OPEN 213 - - #define ID_MENU_SERVER_PLAYERBOX 214 - - #define ID_MENU_RELOAD_ACTIONS 215 - #define ID_MENU_RELOAD_CHAT 216 - #define ID_MENU_RELOAD_CONFIG 217 - #define ID_MENU_RELOAD_CREATUREEVENTS 218 - #ifdef __LOGIN_SERVER__ - #define ID_MENU_RELOAD_GAMESERVERS 219 - #endif - #define ID_MENU_RELOAD_GLOBALEVENTS 220 - #define ID_MENU_RELOAD_GROUPS 221 - #define ID_MENU_RELOAD_HIGHSCORES 222 - #define ID_MENU_RELOAD_HOUSEPRICES 223 - #define ID_MENU_RELOAD_ITEMS 224 - #define ID_MENU_RELOAD_MONSTERS 225 - #define ID_MENU_RELOAD_MODS 226 - #define ID_MENU_RELOAD_MOVEMENTS 227 - #define ID_MENU_RELOAD_NPCS 228 - #define ID_MENU_RELOAD_OUTFITS 229 - #define ID_MENU_RELOAD_QUESTS 230 - #define ID_MENU_RELOAD_RAIDS 231 - #define ID_MENU_RELOAD_SPELLS 232 - #define ID_MENU_RELOAD_STAGES 233 - #define ID_MENU_RELOAD_TALKACTIONS 234 - #define ID_MENU_RELOAD_VOCATIONS 235 - #define ID_MENU_RELOAD_WEAPONS 236 - #define ID_MENU_RELOAD_ALL 237 -#endif - -#define CLIENT_VERSION_MIN 854 -#define CLIENT_VERSION_MAX 854 -#define CLIENT_VERSION_STRING "Only clients with protocol 8.54 allowed!" - -#define STATUS_SERVER_NAME "The Forgotten Server" -#define STATUS_SERVER_VERSION "0.3.6" -#define STATUS_SERVER_CODENAME "Crying Damson" -#define STATUS_SERVER_PROTOCOL "8.54" - -#define VERSION_CHECK "http://forgottenserver.otland.net/version.xml" -#define VERSION_PATCH 1 -#define VERSION_TIMESTAMP 1261647210 -#define VERSION_BUILD 3429 -#define VERSION_DATABASE 23 -#endif diff --git a/rsa.cpp b/rsa.cpp deleted file mode 100644 index f4b6e1e..0000000 --- a/rsa.cpp +++ /dev/null @@ -1,132 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// -#include "otpch.h" -#include "rsa.h" - -RSA::RSA() -{ - m_keySet = false; - mpz_init2(m_p, 1024); - mpz_init2(m_q, 1024); - mpz_init2(m_d, 1024); - mpz_init2(m_u, 1024); - mpz_init2(m_dp, 1024); - mpz_init2(m_dq, 1024); - mpz_init2(m_mod, 1024); -} - -RSA::~RSA() -{ - mpz_clear(m_p); - mpz_clear(m_q); - mpz_clear(m_d); - mpz_clear(m_u); - mpz_clear(m_dp); - mpz_clear(m_dq); - mpz_clear(m_mod); -} - -bool RSA::setKey(const std::string& file) -{ - //loads p, q and d from a file - FILE* f = fopen(file.c_str(), "r"); - if(!f) - return false; - - char p[512], q[512], d[512]; - delete fgets(p, 512, f); - delete fgets(q, 512, f); - delete fgets(d, 512, f); - setKey(p, q, d); - return true; -} - -void RSA::setKey(const char* p, const char* q, const char* d) -{ - boost::recursive_mutex::scoped_lock lockClass(rsaLock); - - mpz_set_str(m_p, p, 10); - mpz_set_str(m_q, q, 10); - mpz_set_str(m_d, d, 10); - - mpz_t pm1,qm1; - mpz_init2(pm1, 520); - mpz_init2(qm1, 520); - - mpz_sub_ui(pm1, m_p, 1); - mpz_sub_ui(qm1, m_q, 1); - mpz_invert(m_u, m_p, m_q); - mpz_mod(m_dp, m_d, pm1); - mpz_mod(m_dq, m_d, qm1); - - mpz_mul(m_mod, m_p, m_q); - - mpz_clear(pm1); - mpz_clear(qm1); -} - -void RSA::decrypt(char* msg, int32_t size) -{ - boost::recursive_mutex::scoped_lock lockClass(rsaLock); - - mpz_t c,v1,v2,u2,tmp; - mpz_init2(c, 1024); - mpz_init2(v1, 1024); - mpz_init2(v2, 1024); - mpz_init2(u2, 1024); - mpz_init2(tmp, 1024); - - mpz_import(c, 128, 1, 1, 0, 0, msg); - - mpz_mod(tmp, c, m_p); - mpz_powm(v1, tmp, m_dp, m_p); - mpz_mod(tmp, c, m_q); - mpz_powm(v2, tmp, m_dq, m_q); - mpz_sub(u2, v2, v1); - mpz_mul(tmp, u2, m_u); - mpz_mod(u2, tmp, m_q); - if(mpz_cmp_si(u2, 0) < 0) - { - mpz_add(tmp, u2, m_q); - mpz_set(u2, tmp); - } - mpz_mul(tmp, u2, m_p); - mpz_set_ui(c, 0); - mpz_add(c, v1, tmp); - - size_t count = (mpz_sizeinbase(c, 2) + 7)/8; - memset(msg, 0, 128 - count); - mpz_export(&msg[128 - count], NULL, 1, 1, 0, 0, c); - - mpz_clear(c); - mpz_clear(v1); - mpz_clear(v2); - mpz_clear(u2); - mpz_clear(tmp); -} - -int32_t RSA::getKeySize() -{ - return (mpz_sizeinbase(m_mod, 2) + 7) / 8; -} - -void RSA::getPublicKey(char* buffer) -{ - size_t count = (mpz_sizeinbase(m_mod, 2) + 7) / 8; - memset(buffer, 0, 128 - count); - mpz_export(&buffer[128 - count], NULL, 1, 1, 0, 0, m_mod); -} diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..07e0235 --- /dev/null +++ b/run.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +#The forgotten server run script + +echo "The forgotten server run script" +VERSION="1.00a" + +case "$1" in + --help|-h) + echo >&2 "Usage $0 " + echo >&2 " Where options are:" + echo >&2 " -h|--help|start|stop|restart|kill|--version"; + if [ -f theforgottenserver ]; + then + ./theforgottenserver --help + fi + ;; + start|s) + if [ -f theforgottenserver ]; + then + echo >&2 "Running The forgotten server v$VERSION" + ./theforgottenserver + echo >&2 "Exited with code $?" + else + echo >&2 "Could not start The forgotten server" + fi + ;; + restart) + echo "Restarting..." + killall -9 theforgottenserver + if [ -f theforgottenserver ]; + then + ./theforgottenserver + echo >&2 "Exited with code $?" + else + echo >&2 "Failed" + fi + ;; + stop|kill) + killall -9 theforgottenserver + ;; + --version) + echo "v$VERSION" + exit + ;; + *) + echo >&2 "Usage $0 " + echo >&2 " Where options are:" + echo >&2 " -h|--help|start|stop|restart|kill"; + ;; +esac diff --git a/scheduler.cpp b/scheduler.cpp index 438a288..69e7530 100644 --- a/scheduler.cpp +++ b/scheduler.cpp @@ -26,7 +26,7 @@ Scheduler::Scheduler() { m_lastEvent = 0; Scheduler::m_threadState = STATE_RUNNING; - boost::thread(boost::bind(&Scheduler::schedulerThread, (void*)this)); + m_thread = boost::thread(boost::bind(&Scheduler::schedulerThread, (void*)this)); } void Scheduler::schedulerThread(void* p) @@ -36,23 +36,22 @@ void Scheduler::schedulerThread(void* p) ExceptionHandler schedulerExceptionHandler; schedulerExceptionHandler.InstallHandler(); #endif - srand((uint32_t)OTSYS_TIME()); boost::unique_lock eventLockUnique(scheduler->m_eventLock, boost::defer_lock); while(Scheduler::m_threadState != Scheduler::STATE_TERMINATED) { SchedulerTask* task = NULL; - bool run = false, ret = false; + bool run = false, action = false; // check if there are events waiting... eventLockUnique.lock(); if(scheduler->m_eventList.empty()) // unlock mutex and wait for signal scheduler->m_eventSignal.wait(eventLockUnique); else // unlock mutex and wait for signal or timeout - ret = scheduler->m_eventSignal.timed_wait(eventLockUnique, scheduler->m_eventList.top()->getCycle()); + action = scheduler->m_eventSignal.timed_wait(eventLockUnique, scheduler->m_eventList.top()->getCycle()); // the mutex is locked again now... - if(!ret && Scheduler::m_threadState != Scheduler::STATE_TERMINATED) + if(!action && Scheduler::m_threadState != Scheduler::STATE_TERMINATED) { // ok we had a timeout, so there has to be an event we have to execute... task = scheduler->m_eventList.top(); @@ -70,17 +69,17 @@ void Scheduler::schedulerThread(void* p) eventLockUnique.unlock(); // add task to dispatcher - if(task) + if(!task) + continue; + + // if it was not stopped + if(run) { - // if it was not stopped - if(run) - { - task->unsetExpiration(); - Dispatcher::getInstance().addTask(task); - } - else - delete task; // was stopped, have to be deleted here + task->unsetExpiration(); + Dispatcher::getInstance().addTask(task); } + else + delete task; // was stopped, have to be deleted here } #if defined __EXCEPTION_TRACER__ @@ -116,7 +115,7 @@ uint32_t Scheduler::addEvent(SchedulerTask* task) } #ifdef __DEBUG_SCHEDULER__ else - std::cout << "[Error - Scheduler::addTask] Scheduler thread is terminated." << std::endl; + std::clog << "[Error - Scheduler::addTask] Scheduler thread is terminated." << std::endl; #endif m_eventLock.unlock(); diff --git a/scheduler.h b/scheduler.h index 82d6915..4b13106 100644 --- a/scheduler.h +++ b/scheduler.h @@ -19,7 +19,7 @@ #define __SCHEDULER__ #include "otsystem.h" -#include "tasks.h" +#include "dispatcher.h" #define SCHEDULER_MINTICKS 50 class SchedulerTask : public Task @@ -43,7 +43,6 @@ class SchedulerTask : public Task inline SchedulerTask* createSchedulerTask(uint32_t delay, const boost::function& f) { - assert(delay); if(delay < SCHEDULER_MINTICKS) delay = SCHEDULER_MINTICKS; @@ -72,6 +71,7 @@ class Scheduler void stop(); void shutdown(); + void exit() {m_thread.join();} static void schedulerThread(void* p); @@ -87,6 +87,7 @@ class Scheduler uint32_t m_lastEvent; EventIds m_eventIds; + boost::thread m_thread; boost::mutex m_eventLock; boost::condition_variable m_eventSignal; diff --git a/schemas/mysql.sql b/schemas/mysql.sql index 203b866..4395c84 100644 --- a/schemas/mysql.sql +++ b/schemas/mysql.sql @@ -6,12 +6,14 @@ DROP TRIGGER IF EXISTS `ondelete_accounts`; DROP TABLE IF EXISTS `player_depotitems`; DROP TABLE IF EXISTS `tile_items`; +DROP TABLE IF EXISTS `tile_store`; DROP TABLE IF EXISTS `tiles`; DROP TABLE IF EXISTS `bans`; DROP TABLE IF EXISTS `house_lists`; DROP TABLE IF EXISTS `houses`; DROP TABLE IF EXISTS `player_items`; DROP TABLE IF EXISTS `player_namelocks`; +DROP TABLE IF EXISTS `player_statements`; DROP TABLE IF EXISTS `player_skills`; DROP TABLE IF EXISTS `player_storage`; DROP TABLE IF EXISTS `player_viplist`; @@ -37,40 +39,42 @@ CREATE TABLE `accounts` `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(32) NOT NULL DEFAULT '', `password` VARCHAR(255) NOT NULL/* VARCHAR(32) NOT NULL COMMENT 'MD5'*//* VARCHAR(40) NOT NULL COMMENT 'SHA1'*/, + `salt` VARCHAR(40) NOT NULL DEFAULT '', `premdays` INT NOT NULL DEFAULT 0, `lastday` INT UNSIGNED NOT NULL DEFAULT 0, `email` VARCHAR(255) NOT NULL DEFAULT '', - `key` VARCHAR(20) NOT NULL DEFAULT '0', + `key` VARCHAR(32) NOT NULL DEFAULT '0', `blocked` TINYINT(1) NOT NULL DEFAULT FALSE COMMENT 'internal usage', `warnings` INT NOT NULL DEFAULT 0, `group_id` INT NOT NULL DEFAULT 1, PRIMARY KEY (`id`), UNIQUE (`name`) ) ENGINE = InnoDB; -INSERT INTO `accounts` VALUES (1, '1', '1', 65535, 0, '', '0', 0, 0, 1); +INSERT INTO `accounts` VALUES (1, '1', '1', '', 65535, 0, '', '0', 0, 0, 1); CREATE TABLE `players` ( `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `group_id` INT NOT NULL DEFAULT 1, `account_id` INT NOT NULL DEFAULT 0, `level` INT NOT NULL DEFAULT 1, `vocation` INT NOT NULL DEFAULT 0, `health` INT NOT NULL DEFAULT 150, `healthmax` INT NOT NULL DEFAULT 150, - `experience` BIGINT NOT NULL DEFAULT 0, + `experience` BIGINT UNSIGNED NOT NULL DEFAULT 0, `lookbody` INT NOT NULL DEFAULT 0, `lookfeet` INT NOT NULL DEFAULT 0, `lookhead` INT NOT NULL DEFAULT 0, `looklegs` INT NOT NULL DEFAULT 0, `looktype` INT NOT NULL DEFAULT 136, `lookaddons` INT NOT NULL DEFAULT 0, + `lookmount` INT NOT NULL DEFAULT 0, `maglevel` INT NOT NULL DEFAULT 0, `mana` INT NOT NULL DEFAULT 0, `manamax` INT NOT NULL DEFAULT 0, - `manaspent` INT NOT NULL DEFAULT 0, + `manaspent` BIGINT UNSIGNED NOT NULL DEFAULT 0, `soul` INT UNSIGNED NOT NULL DEFAULT 0, `town_id` INT NOT NULL DEFAULT 0, `posx` INT NOT NULL DEFAULT 0, @@ -88,8 +92,9 @@ CREATE TABLE `players` `guildnick` VARCHAR(255) NOT NULL DEFAULT '', `lastlogout` BIGINT UNSIGNED NOT NULL DEFAULT 0, `blessings` TINYINT(2) NOT NULL DEFAULT 0, - `balance` BIGINT NOT NULL DEFAULT 0, - `stamina` BIGINT NOT NULL DEFAULT 151200000 COMMENT 'stored in miliseconds', + `pvp_blessing` TINYINT(1) NOT NULL DEFAULT 0, + `balance` BIGINT UNSIGNED NOT NULL DEFAULT 0, + `stamina` BIGINT UNSIGNED NOT NULL DEFAULT 151200000 COMMENT 'stored in miliseconds', `direction` INT NOT NULL DEFAULT 2, `loss_experience` INT NOT NULL DEFAULT 100, `loss_mana` INT NOT NULL DEFAULT 100, @@ -100,7 +105,7 @@ CREATE TABLE `players` `online` TINYINT(1) NOT NULL DEFAULT 0, `marriage` INT UNSIGNED NOT NULL DEFAULT 0, `promotion` INT NOT NULL DEFAULT 0, - `deleted` TINYINT(1) NOT NULL DEFAULT FALSE, + `deleted` INT NOT NULL DEFAULT 0, `description` VARCHAR(255) NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE (`name`, `deleted`), KEY (`account_id`), KEY (`group_id`), @@ -108,12 +113,12 @@ CREATE TABLE `players` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`) ON DELETE CASCADE ) ENGINE = InnoDB; -INSERT INTO `players` VALUES (1, 'Account Manager', 0, 1, 1, 1, 0, 150, 150, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, 50, 50, 7, '', 400, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 201660000, 0, 100, 100, 100, 100, 100, 0, 0, 0, 0, 0, ''); +INSERT INTO `players` VALUES (1, 'Account Manager', 0, 1, 1, 1, 0, 150, 150, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, 0, 853, 921, 7, '', 400, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 0, 201660000, 0, 100, 100, 100, 100, 100, 0, 0, 0, 0, 0, ''); CREATE TABLE `account_viplist` ( `account_id` INT NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `player_id` INT NOT NULL, KEY (`account_id`), KEY (`player_id`), KEY (`world_id`), UNIQUE (`account_id`, `player_id`), FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`) ON DELETE CASCADE, @@ -144,7 +149,7 @@ CREATE TABLE `player_depotitems` CREATE TABLE `player_items` ( - `player_id` INT NOT NULL DEFAULT 0, + `player_id` INT NOT NULL, `pid` INT NOT NULL DEFAULT 0, `sid` INT NOT NULL DEFAULT 0, `itemtype` INT NOT NULL DEFAULT 0, @@ -156,7 +161,7 @@ CREATE TABLE `player_items` CREATE TABLE `player_namelocks` ( - `player_id` INT NOT NULL DEFAULT 0, + `player_id` INT NOT NULL, `name` VARCHAR(255) NOT NULL, `new_name` VARCHAR(255) NOT NULL, `date` BIGINT NOT NULL DEFAULT 0, @@ -164,9 +169,20 @@ CREATE TABLE `player_namelocks` FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE ) ENGINE = InnoDB; +CREATE TABLE `player_statements` +( + `id` INT NOT NULL AUTO_INCREMENT, + `player_id` INT NOT NULL, + `channel_id` INT NOT NULL DEFAULT 0, + `text` VARCHAR (255) NOT NULL, + `date` BIGINT NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), KEY (`player_id`), KEY (`channel_id`), + FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE +) ENGINE = InnoDB; + CREATE TABLE `player_skills` ( - `player_id` INT NOT NULL DEFAULT 0, + `player_id` INT NOT NULL, `skillid` TINYINT(2) NOT NULL DEFAULT 0, `value` INT UNSIGNED NOT NULL DEFAULT 0, `count` INT UNSIGNED NOT NULL DEFAULT 0, @@ -184,9 +200,9 @@ CREATE TABLE `player_spells` CREATE TABLE `player_storage` ( - `player_id` INT NOT NULL DEFAULT 0, - `key` INT UNSIGNED NOT NULL DEFAULT 0, - `value` VARCHAR(255) NOT NULL DEFAULT '0', + `player_id` INT NOT NULL, + `key` VARCHAR(32) NOT NULL DEFAULT '0', + `value` TEXT NOT NULL, KEY (`player_id`), UNIQUE (`player_id`, `key`), FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE ) ENGINE = InnoDB; @@ -206,6 +222,7 @@ CREATE TABLE `killers` `death_id` INT NOT NULL, `final_hit` TINYINT(1) UNSIGNED NOT NULL DEFAULT FALSE, `unjustified` TINYINT(1) UNSIGNED NOT NULL DEFAULT FALSE, + `war` INT NOT NULL DEFAULT 0, PRIMARY KEY (`id`), FOREIGN KEY (`death_id`) REFERENCES `player_deaths`(`id`) ON DELETE CASCADE ) ENGINE = InnoDB; @@ -228,7 +245,7 @@ CREATE TABLE `environment_killers` CREATE TABLE `houses` ( `id` INT UNSIGNED NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `owner` INT NOT NULL, `paid` INT UNSIGNED NOT NULL DEFAULT 0, `warnings` INT NOT NULL DEFAULT 0, @@ -246,10 +263,18 @@ CREATE TABLE `houses` UNIQUE (`id`, `world_id`) ) ENGINE = InnoDB; +CREATE TABLE `tile_store` +( + `house_id` INT UNSIGNED NOT NULL, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, + `data` LONGBLOB NOT NULL, + FOREIGN KEY (`house_id`) REFERENCES `houses` (`id`) ON DELETE CASCADE +) ENGINE = InnoDB; + CREATE TABLE `house_auctions` ( `house_id` INT UNSIGNED NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `player_id` INT NOT NULL, `bid` INT UNSIGNED NOT NULL DEFAULT 0, `limit` INT UNSIGNED NOT NULL DEFAULT 0, @@ -262,7 +287,7 @@ CREATE TABLE `house_auctions` CREATE TABLE `house_lists` ( `house_id` INT UNSIGNED NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `listid` INT NOT NULL, `list` TEXT NOT NULL, UNIQUE (`house_id`, `world_id`, `listid`), @@ -272,7 +297,7 @@ CREATE TABLE `house_lists` CREATE TABLE `house_data` ( `house_id` INT UNSIGNED NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `data` LONGBLOB NOT NULL, UNIQUE (`house_id`, `world_id`), FOREIGN KEY (`house_id`, `world_id`) REFERENCES `houses`(`id`, `world_id`) ON DELETE CASCADE @@ -281,7 +306,7 @@ CREATE TABLE `house_data` CREATE TABLE `tiles` ( `id` INT UNSIGNED NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `house_id` INT UNSIGNED NOT NULL, `x` INT(5) UNSIGNED NOT NULL, `y` INT(5) UNSIGNED NOT NULL, @@ -294,7 +319,7 @@ CREATE TABLE `tiles` CREATE TABLE `tile_items` ( `tile_id` INT UNSIGNED NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `sid` INT NOT NULL, `pid` INT NOT NULL DEFAULT 0, `itemtype` INT NOT NULL, @@ -307,11 +332,13 @@ CREATE TABLE `tile_items` CREATE TABLE `guilds` ( `id` INT NOT NULL AUTO_INCREMENT, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `name` VARCHAR(255) NOT NULL, `ownerid` INT NOT NULL, `creationdata` INT NOT NULL, + `checkdata` INT NOT NULL, `motd` VARCHAR(255) NOT NULL, + `balance` BIGINT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE (`name`, `world_id`) ) ENGINE = InnoDB; @@ -335,20 +362,46 @@ CREATE TABLE `guild_ranks` FOREIGN KEY (`guild_id`) REFERENCES `guilds`(`id`) ON DELETE CASCADE ) ENGINE = InnoDB; +CREATE TABLE `guild_wars` +( + `id` INT NOT NULL AUTO_INCREMENT, + `guild_id` INT NOT NULL, + `enemy_id` INT NOT NULL, + `begin` BIGINT NOT NULL DEFAULT 0, + `end` BIGINT NOT NULL DEFAULT 0, + `frags` INT UNSIGNED NOT NULL DEFAULT 0, + `payment` BIGINT UNSIGNED NOT NULL DEFAULT 0, + `guild_kills` INT UNSIGNED NOT NULL DEFAULT 0, + `enemy_kills` INT UNSIGNED NOT NULL DEFAULT 0, + `status` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), KEY `status` (`status`), + KEY `guild_id` (`guild_id`), KEY `enemy_id` (`enemy_id`), + FOREIGN KEY (`guild_id`) REFERENCES `guilds`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`enemy_id`) REFERENCES `guilds`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB; + +CREATE TABLE `guild_kills` +( + `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `guild_id` INT NOT NULL, + `war_id` INT NOT NULL, + `death_id` INT NOT NULL, + FOREIGN KEY (`guild_id`) REFERENCES `guilds`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`war_id`) REFERENCES `guild_wars`(`id`) ON DELETE CASCADE, + FOREIGN KEY (`death_id`) REFERENCES `player_deaths`(`id`) ON DELETE CASCADE +) ENGINE = InnoDB; + CREATE TABLE `bans` ( `id` INT UNSIGNED NOT NULL auto_increment, - `type` TINYINT(1) NOT NULL COMMENT '1 - ip banishment, 2 - namelock, 3 - account banishment, 4 - notation, 5 - deletion', - `value` INT UNSIGNED NOT NULL COMMENT 'ip address (integer), player guid or account number', - `param` INT UNSIGNED NOT NULL DEFAULT 4294967295 COMMENT 'used only for ip banishment mask (integer)', + `type` TINYINT(1) NOT NULL COMMENT '1 - ip, 2 - player, 3 - account, 4 - notation', + `value` INT UNSIGNED NOT NULL COMMENT 'ip - ip address, player - player_id, account - account_id, notation - account_id', + `param` INT UNSIGNED NOT NULL COMMENT 'ip - mask, player - type (1 - report, 2 - lock, 3 - ban), account - player, notation - player', `active` TINYINT(1) NOT NULL DEFAULT TRUE, - `expires` INT NOT NULL, + `expires` INT NOT NULL DEFAULT -1, `added` INT UNSIGNED NOT NULL, `admin_id` INT UNSIGNED NOT NULL DEFAULT 0, `comment` TEXT NOT NULL, - `reason` INT UNSIGNED NOT NULL DEFAULT 0, - `action` INT UNSIGNED NOT NULL DEFAULT 0, - `statement` VARCHAR(255) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `type` (`type`, `value`), KEY `active` (`active`) @@ -356,25 +409,25 @@ CREATE TABLE `bans` CREATE TABLE `global_storage` ( - `key` INT UNSIGNED NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, - `value` VARCHAR(255) NOT NULL DEFAULT '0', + `key` VARCHAR(32) NOT NULL, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, + `value` TEXT NOT NULL, UNIQUE (`key`, `world_id`) ) ENGINE = InnoDB; CREATE TABLE `server_config` ( `config` VARCHAR(35) NOT NULL DEFAULT '', - `value` INT NOT NULL, + `value` VARCHAR(255) NOT NULL DEFAULT '', UNIQUE (`config`) ) ENGINE = InnoDB; -INSERT INTO `server_config` VALUES ('db_version', 23); +INSERT INTO `server_config` VALUES ('db_version', 31); CREATE TABLE `server_motd` ( `id` INT UNSIGNED NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `text` TEXT NOT NULL, UNIQUE (`id`, `world_id`) ) ENGINE = InnoDB; @@ -384,7 +437,7 @@ INSERT INTO `server_motd` VALUES (1, 0, 'Welcome to The Forgotten Server!'); CREATE TABLE `server_record` ( `record` INT NOT NULL, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `timestamp` BIGINT NOT NULL, UNIQUE (`record`, `world_id`, `timestamp`) ) ENGINE = InnoDB; @@ -394,7 +447,7 @@ INSERT INTO `server_record` VALUES (0, 0, 0); CREATE TABLE `server_reports` ( `id` INT NOT NULL AUTO_INCREMENT, - `world_id` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0, + `world_id` TINYINT(4) UNSIGNED NOT NULL DEFAULT 0, `player_id` INT NOT NULL DEFAULT 1, `posx` INT NOT NULL DEFAULT 0, `posy` INT NOT NULL DEFAULT 0, diff --git a/schemas/pgsql.sql b/schemas/pgsql.sql index e52d24f..14b0d85 100644 --- a/schemas/pgsql.sql +++ b/schemas/pgsql.sql @@ -132,7 +132,7 @@ CREATE TABLE "players" FOREIGN KEY ("group_id") REFERENCES "groups"("id") ); -INSERT INTO "players" VALUES (nextval('players_id_seq'::regclass), 'Account Manager', 1, 1, 1, 0, 150, 150, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, 50, 50, 7, '', 400, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 201660000, 0, 10, 10, 10, 10, 0, 0, 0, 0, 0); +INSERT INTO "players" VALUES (nextval('players_id_seq'::regclass), 'Account Manager', 1, 1, 1, 0, 150, 150, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, 853, 921, 7, '', 400, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 201660000, 0, 10, 10, 10, 10, 0, 0, 0, 0, 0); CREATE TABLE "bans" ( diff --git a/schemas/sqlite.sql b/schemas/sqlite.sql index 5903e94..c89771b 100644 --- a/schemas/sqlite.sql +++ b/schemas/sqlite.sql @@ -1,10 +1,10 @@ CREATE TABLE "server_config" ( "config" VARCHAR(35) NOT NULL DEFAULT '', - "value" INTEGER NOT NULL, + "value" VARCHAR(255) NOT NULL DEFAULT '', UNIQUE ("config") ); -INSERT INTO "server_config" VALUES ('db_version', 23); +INSERT INTO "server_config" VALUES ('db_version', 31); CREATE TABLE "server_motd" ( "id" INTEGER NOT NULL, @@ -40,17 +40,18 @@ CREATE TABLE "accounts" ( "id" INTEGER PRIMARY KEY NOT NULL, "name" VARCHAR(255) NOT NULL, "password" VARCHAR(255) NOT NULL, + "salt" VARCHAR(40) NOT NULL DEFAULT '', "premdays" INTEGER NOT NULL DEFAULT 0, "lastday" INTEGER NOT NULL DEFAULT 0, "email" VARCHAR(255) NOT NULL DEFAULT '', - "key" VARCHAR(20) NOT NULL DEFAULT '0', - "blocked" BOOLEAN NOT NULL DEFAULT 0, + "key" VARCHAR(32) NOT NULL DEFAULT '0', + "blocked" BOOLEAN NOT NULL DEFAULT FALSE, "warnings" INTEGER NOT NULL DEFAULT 0, "group_id" INTEGER NOT NULL DEFAULT 1, UNIQUE ("name") ); -INSERT INTO "accounts" VALUES (1, '1', '1', 65535, 0, '', '0', 0, 0, 1); +INSERT INTO "accounts" VALUES (1, '1', '1', '', 65535, 0, '', '0', 0, 0, 1); CREATE TABLE "players" ( "id" INTEGER PRIMARY KEY NOT NULL, @@ -69,6 +70,7 @@ CREATE TABLE "players" ( "looklegs" INTEGER NOT NULL DEFAULT 10, "looktype" INTEGER NOT NULL DEFAULT 136, "lookaddons" INTEGER NOT NULL DEFAULT 0, + "lookmount" INTEGER NOT NULL DEFAULT 0, "maglevel" INTEGER NOT NULL DEFAULT 0, "mana" INTEGER NOT NULL DEFAULT 100, "manamax" INTEGER NOT NULL DEFAULT 100, @@ -83,13 +85,14 @@ CREATE TABLE "players" ( "sex" INTEGER NOT NULL DEFAULT 0, "lastlogin" INTEGER NOT NULL DEFAULT 0, "lastip" INTEGER NOT NULL DEFAULT 0, - "save" BOOLEAN NOT NULL DEFAULT 1, + "save" BOOLEAN NOT NULL DEFAULT TRUE, "skull" INTEGER NOT NULL DEFAULT 0, "skulltime" INTEGER NOT NULL DEFAULT 0, "rank_id" INTEGER NOT NULL, "guildnick" VARCHAR(255) NOT NULL DEFAULT '', "lastlogout" INTEGER NOT NULL DEFAULT 0, "blessings" INTEGER NOT NULL DEFAULT 0, + "pvp_blessing" BOOLEAN NOT NULL DEFAULT FALSE, "balance" INTEGER NOT NULL DEFAULT 0, "stamina" INTEGER NOT NULL DEFAULT 151200000, "direction" INTEGER NOT NULL DEFAULT 2, @@ -99,16 +102,16 @@ CREATE TABLE "players" ( "loss_containers" INTEGER NOT NULL DEFAULT 100, "loss_items" INTEGER NOT NULL DEFAULT 100, "premend" INTEGER NOT NULL DEFAULT 0, - "online" TINYINT NOT NULL DEFAULT 0, + "online" INTEGER NOT NULL DEFAULT 0, "marriage" INTEGER NOT NULL DEFAULT 0, "promotion" INTEGER NOT NULL DEFAULT 0, - "deleted" BOOLEAN NOT NULL DEFAULT 0, + "deleted" INTEGER NOT NULL DEFAULT 0, "description" VARCHAR(255) NOT NULL DEFAULT '', UNIQUE ("name", "deleted"), FOREIGN KEY ("account_id") REFERENCES "accounts" ("id") ); -INSERT INTO "players" VALUES (1, 'Account Manager', 0, 1, 1, 1, 0, 150, 150, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, 50, 50, 7, '', 400, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 201660000, 0, 100, 100, 100, 100, 100, 0, 0, 0, 0, 0, ''); +INSERT INTO "players" VALUES (1, 'Account Manager', 0, 1, 1, 1, 0, 150, 150, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, 0, 853, 921, 7, '', 400, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 0, 201660000, 0, 100, 100, 100, 100, 100, 0, 0, 0, 0, 0, ''); CREATE TABLE "account_viplist" ( "account_id" INTEGER NOT NULL, @@ -120,7 +123,7 @@ CREATE TABLE "account_viplist" ( ); CREATE TABLE "global_storage" ( - "key" INTEGER NOT NULL, + "key" VARCHAR(32) NOT NULL, "world_id" INTEGER NOT NULL DEFAULT 0, "value" VARCHAR(255) NOT NULL DEFAULT '0', UNIQUE ("key", "world_id") @@ -132,11 +135,38 @@ CREATE TABLE "guilds" ( "name" VARCHAR(255) NOT NULL, "ownerid" INTEGER NOT NULL, "creationdata" INTEGER NOT NULL, + "checkdata" INTEGER NOT NULL, "motd" VARCHAR(255) NOT NULL DEFAULT '', + "balance" INTEGER NOT NULL DEFAULT 0, UNIQUE ("name", "world_id"), FOREIGN KEY ("ownerid") REFERENCES "players" ("id") ); +CREATE TABLE "guild_wars" ( + "id" INTEGER PRIMARY KEY, + "guild_id" INTEGER NOT NULL, + "enemy_id" INTEGER NOT NULL, + "begin" INTEGER NOT NULL DEFAULT 0, + "end" INTEGER NOT NULL DEFAULT 0, + "frags" INTEGER NOT NULL DEFAULT 0, + "payment" INTEGER NOT NULL DEFAULT 0, + "guild_kills" INTEGER NOT NULL DEFAULT 0, + "enemy_kills" INTEGER NOT NULL DEFAULT 0, + "status" TINYINT(1) NOT NULL DEFAULT 0, + FOREIGN KEY ("guild_id") REFERENCES "guilds"("id") ON DELETE CASCADE, + FOREIGN KEY ("enemy_id") REFERENCES "guilds"("id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS "guild_kills" ( + "id" INTEGER PRIMARY KEY, + "guild_id" INTEGER NOT NULL, + "war_id" INTEGER NOT NULL, + "death_id" INTEGER NOT NULL. + FOREIGN KEY ("guild_id") REFERENCES "guilds"("id") ON DELETE CASCADE, + FOREIGN KEY ("war_id") REFERENCES "guild_wars"("id") ON DELETE CASCADE, + FOREIGN KEY ("death_id") REFERENCES "player_deaths"("id") ON DELETE CASCADE +); + CREATE TABLE "guild_invites" ( "player_id" INTEGER NOT NULL, "guild_id" INTEGER NOT NULL, @@ -215,6 +245,7 @@ CREATE TABLE "killers" ( "death_id" INTEGER NOT NULL, "final_hit" BOOLEAN NOT NULL DEFAULT FALSE, "unjustified" BOOLEAN NOT NULL DEFAULT FALSE, + "war" INTEGER NOT NULL DEFAULT 0, FOREIGN KEY ("death_id") REFERENCES "player_deaths" ("id") ); @@ -250,6 +281,15 @@ CREATE TABLE "player_namelocks" ( FOREIGN KEY ("player_id") REFERENCES "players" ("id") ); +CREATE TABLE "player_statements" ( + "id" INTEGER PRIMARY KEY, + "player_id" INTEGER NOT NULL, + "channel_id" INTEGER NOT NULL DEFAULT 0, + "text" VARCHAR (255) NOT NULL, + "date" INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY ("player_id") REFERENCES "players"("id") +); + CREATE TABLE "player_skills" ( "player_id" INTEGER NOT NULL, "skillid" INTEGER NOT NULL, @@ -261,7 +301,7 @@ CREATE TABLE "player_skills" ( CREATE TABLE "player_storage" ( "player_id" INTEGER NOT NULL, - "key" INTEGER NOT NULL, + "key" VARCHAR(32) NOT NULL, "value" VARCHAR(255) NOT NULL DEFAULT '0', UNIQUE ("player_id", "key"), FOREIGN KEY ("player_id") REFERENCES "players" ("id") @@ -299,11 +339,11 @@ CREATE TABLE "tile_items" ( ); CREATE TABLE "player_items" ( - "player_id" INT NOT NULL, - "sid" INT NOT NULL, - "pid" INT NOT NULL DEFAULT 0, - "itemtype" INT NOT NULL, - "count" INT NOT NULL DEFAULT 0, + "player_id" INTEGER NOT NULL, + "sid" INTEGER NOT NULL, + "pid" INTEGER NOT NULL DEFAULT 0, + "itemtype" INTEGER NOT NULL, + "count" INTEGER NOT NULL DEFAULT 0, "attributes" BLOB NOT NULL, UNIQUE ("player_id", "sid"), FOREIGN KEY ("player_id") REFERENCES "players" ("id") @@ -320,15 +360,20 @@ CREATE TABLE "bans" ( "id" INTEGER PRIMARY KEY NOT NULL, "type" INTEGER NOT NULL, "value" INTEGER NOT NULL, - "param" INTEGER NOT NULL DEFAULT 4294967295, - "active" BOOLEAN NOT NULL DEFAULT 1, - "expires" INTEGER NOT NULL, + "param" INTEGER NOT NULL, + "active" BOOLEAN NOT NULL DEFAULT TRUE, + "expires" INTEGER NOT NULL DEFAULT -1, "added" INTEGER NOT NULL, "admin_id" INTEGER NOT NULL DEFAULT 0, "comment" TEXT NOT NULL, - "reason" INTEGER NOT NULL DEFAULT 0, - "action" INTEGER NOT NULL DEFAULT 0, - "statement" VARCHAR(255) NOT NULL DEFAULT '' +); + +CREATE TABLE "tile_store" +( + "house_id" INTEGER NOT NULL, + "world_id" INTEGER NOT NULL DEFAULT 0, + "data" LONGBLOB NOT NULL, + FOREIGN KEY ("house_id") REFERENCES "houses" ("id") ); CREATE TRIGGER "oncreate_guilds" @@ -380,6 +425,8 @@ BEGIN DELETE FROM "player_killers" WHERE "player_id" = OLD."id"; DELETE FROM "player_deaths" WHERE "player_id" = OLD."id"; DELETE FROM "guild_invites" WHERE "player_id" = OLD."id"; + DELETE FROM "player_namelocks" WHERE "player_id" = OLD."id"; + DELETE FROM "player_statements" WHERE "player_id" = OLD."id"; DELETE FROM "bans" WHERE "type" IN (2, 5) AND "value" = OLD."id"; UPDATE "houses" SET "owner" = 0 WHERE "owner" = OLD."id"; END; diff --git a/scriptmanager.cpp b/scriptmanager.cpp index f135eae..78ebeea 100644 --- a/scriptmanager.cpp +++ b/scriptmanager.cpp @@ -30,11 +30,13 @@ #include "weapons.h" #include "monsters.h" +#include "npc.h" #include "spawn.h" #include "raids.h" #include "group.h" #include "vocation.h" #include "outfit.h" +#include "mounts.h" #include "quests.h" #include "items.h" #include "chat.h" @@ -53,67 +55,90 @@ GlobalEvents* g_globalEvents = NULL; extern Chat g_chat; extern ConfigManager g_config; extern Monsters g_monsters; +extern Npcs g_npcs; -bool ScriptingManager::load() +ScriptManager::ScriptManager(): +modsLoaded(false) { g_weapons = new Weapons(); + g_spells = new Spells(); + g_actions = new Actions(); + g_talkActions = new TalkActions(); + g_moveEvents = new MoveEvents(); + g_creatureEvents = new CreatureEvents(); + g_globalEvents = new GlobalEvents(); +} + +bool ScriptManager::loadSystem() +{ + std::clog << "> Loading weapons... "; if(!g_weapons->loadFromXml()) { - std::cout << "> ERROR: Unable to load Weapons!" << std::endl; + std::clog << "failed!" << std::endl; return false; } - g_weapons->loadDefaults(); - g_spells = new Spells(); + std::clog << "done." << std::endl << "> Preparing weapons... "; + if(!g_weapons->loadDefaults()) + { + std::clog << "failed!" << std::endl; + return false; + } + + std::clog << "done." << std::endl << "> Loading spells... "; if(!g_spells->loadFromXml()) { - std::cout << "> ERROR: Unable to load Spells!" << std::endl; + std::clog << "failed!" << std::endl; return false; } - g_actions = new Actions(); + std::clog << "done." << std::endl << "> Loading actions... "; if(!g_actions->loadFromXml()) { - std::cout << "> ERROR: Unable to load Actions!" << std::endl; + std::clog << "failed!" << std::endl; return false; } - g_talkActions = new TalkActions(); + std::clog << "done." << std::endl << "> Loading talkactions... "; if(!g_talkActions->loadFromXml()) { - std::cout << "> ERROR: Unable to load TalkActions!" << std::endl; + std::clog << "failed!" << std::endl; return false; } - g_moveEvents = new MoveEvents(); + std::clog << "done." << std::endl << "> Loading movements... "; if(!g_moveEvents->loadFromXml()) { - std::cout << "> ERROR: Unable to load MoveEvents!" << std::endl; + std::clog << "failed!" << std::endl; return false; } - g_creatureEvents = new CreatureEvents(); + std::clog << "done." << std::endl << "> Loading creaturescripts... "; if(!g_creatureEvents->loadFromXml()) { - std::cout << "> ERROR: Unable to load CreatureEvents!" << std::endl; + std::clog << "failed!" << std::endl; return false; } - g_globalEvents = new GlobalEvents(); + std::clog << "done." << std::endl << "> Loading globalscripts... "; if(!g_globalEvents->loadFromXml()) { - std::cout << "> ERROR: Unable to load GlobalEvents!" << std::endl; + std::clog << "failed!" << std::endl; return false; } + std::clog << "done." << std::endl; return true; } -bool ScriptingManager::loadMods() +bool ScriptManager::loadMods() { - boost::filesystem::path modsPath(getFilePath(FILE_TYPE_MOD, "")); + boost::filesystem::path modsPath(getFilePath(FILE_TYPE_MOD)); if(!boost::filesystem::exists(modsPath)) - return true; //silently ignore + { + std::clog << "[Error - ScriptManager::loadMods] Could not locate mods directory" << std::endl; + return false; + } int32_t i = 0, j = 0; bool enabled = false; @@ -123,56 +148,54 @@ bool ScriptingManager::loadMods() if(boost::filesystem::is_directory(it->status()) && (s.size() > 4 ? s.substr(s.size() - 4) : "") != ".xml") continue; - std::cout << "> Loading " << s << "..."; + std::clog << "> Loading " << s << "..."; if(loadFromXml(s, enabled)) { - std::cout << " done"; + std::clog << " done"; if(!enabled) { ++j; - std::cout << ", but disabled"; + std::clog << ", but disabled"; } - std::cout << "."; + std::clog << "."; } else - std::cout << " failed!"; + std::clog << " failed!"; - std::cout << std::endl; + std::clog << std::endl; ++i; } - std::cout << "> " << i << " mods were loaded"; + std::clog << "> " << i << " mods were loaded"; if(j) - std::cout << " (" << j << " disabled)"; + std::clog << " (" << j << " disabled)"; - std::cout << "." << std::endl; + std::clog << "." << std::endl; modsLoaded = true; return true; } -void ScriptingManager::clearMods() +void ScriptManager::clearMods() { modMap.clear(); libMap.clear(); } -bool ScriptingManager::reloadMods() +bool ScriptManager::reloadMods() { clearMods(); return loadMods(); } -bool ScriptingManager::loadFromXml(const std::string& file, bool& enabled) +bool ScriptManager::loadFromXml(const std::string& file, bool& enabled) { enabled = false; - std::string modPath = getFilePath(FILE_TYPE_MOD, file); - - xmlDocPtr doc = xmlParseFile(modPath.c_str()); + xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_MOD, file).c_str()); if(!doc) { - std::cout << "[Error - ScriptingManager::loadFromXml] Cannot load mod " << modPath << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Error - ScriptManager::loadFromXml] Cannot load mod " << file << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } @@ -182,67 +205,106 @@ bool ScriptingManager::loadFromXml(const std::string& file, bool& enabled) xmlNodePtr p, root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"mod")) { - std::cout << "[Error - ScriptingManager::loadFromXml] Malformed mod " << modPath << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Error - ScriptManager::loadFromXml] Malformed mod " << file << std::endl; + std::clog << getLastXMLError() << std::endl; xmlFreeDoc(doc); return false; } if(!readXMLString(root, "name", strValue)) - { - std::cout << "[Warning - ScriptingManager::loadFromXml] Empty name in mod " << modPath << std::endl; - xmlFreeDoc(doc); - return false; - } + strValue = file; ModBlock mod; - mod.enabled = false; - if(readXMLString(root, "enabled", strValue) && booleanString(strValue)) - mod.enabled = true; + mod.enabled = true; + mod.name = strValue; + if(readXMLString(root, "enabled", strValue) && !booleanString(strValue)) + mod.enabled = false; mod.file = file; - mod.name = strValue; if(readXMLString(root, "author", strValue)) mod.author = strValue; + if(readXMLString(root, "version", strValue)) mod.version = strValue; + if(readXMLString(root, "contact", strValue)) mod.contact = strValue; + bool supported = true; + for(p = root->children; p; p = p->next) + { + if(xmlStrcmp(p->name, (const xmlChar*)"server")) + continue; + + supported = false; + for(xmlNodePtr versionNode = p->children; versionNode; versionNode = versionNode->next) + { + std::string id = SOFTWARE_VERSION; + if(readXMLString(versionNode, "id", strValue)) + id = asLowerCaseString(strValue); + + IntegerVec protocol; + protocol.push_back(CLIENT_VERSION_MIN); + if(readXMLString(versionNode, "protocol", strValue)) + protocol = vectorAtoi(explodeString(strValue, "-")); + + int16_t patch = VERSION_PATCH, database = VERSION_DATABASE; + if(readXMLInteger(versionNode, "patch", intValue)) + patch = intValue; + + if(readXMLInteger(versionNode, "database", intValue)) + database = intValue; + + if(id == asLowerCaseString(SOFTWARE_VERSION) && patch >= VERSION_PATCH && database >= VERSION_DATABASE + && protocol[0] >= CLIENT_VERSION_MIN && (protocol.size() < 2 || protocol[1] <= CLIENT_VERSION_MAX)) + { + supported = true; + break; + } + } + } + + if(!supported) + { + std::clog << "[Warning - ScriptManager::loadFromXml] Your server is not supported by mod " << file << std::endl; + xmlFreeDoc(doc); + return false; + } + if(mod.enabled) { std::string scriptsPath = getFilePath(FILE_TYPE_MOD, "scripts/"); - p = root->children; - while(p) + for(p = root->children; p; p = p->next) { if(!xmlStrcmp(p->name, (const xmlChar*)"quest")) Quests::getInstance()->parseQuestNode(p, modsLoaded); + else if(!xmlStrcmp(p->name, (const xmlChar*)"mount")) + Mounts::getInstance()->parseMountNode(p); else if(!xmlStrcmp(p->name, (const xmlChar*)"outfit")) Outfits::getInstance()->parseOutfitNode(p); else if(!xmlStrcmp(p->name, (const xmlChar*)"vocation")) - Vocations::getInstance()->parseVocationNode(p); //duplicates checking is dangerous, shouldn't be performed + Vocations::getInstance()->parseVocationNode(p); //duplicates checking is dangerous, shouldn't be performed until we find some good solution else if(!xmlStrcmp(p->name, (const xmlChar*)"group")) - Groups::getInstance()->parseGroupNode(p); //duplicates checking is dangerous, shouldn't be performed + Groups::getInstance()->parseGroupNode(p); //duplicates checking is dangerous, shouldn't be performed until we find some good solution else if(!xmlStrcmp(p->name, (const xmlChar*)"raid")) - Raids::getInstance()->parseRaidNode(p, modsLoaded, FILE_TYPE_MOD); + Raids::getInstance()->parseRaidNode(p, modsLoaded, FILE_TYPE_MOD); //TODO: support mods path else if(!xmlStrcmp(p->name, (const xmlChar*)"spawn")) Spawns::getInstance()->parseSpawnNode(p, modsLoaded); else if(!xmlStrcmp(p->name, (const xmlChar*)"channel")) - g_chat.parseChannelNode(p); //TODO: duplicates (channel destructor needs sending self close to users) + g_chat.parseChannelNode(p); //TODO: duplicates- channel destructor needs to send closeChannel to users! + else if(!xmlStrcmp(p->name, (const xmlChar*)"npc")) + g_npcs.parseNpcNode(p, FILE_TYPE_MOD); else if(!xmlStrcmp(p->name, (const xmlChar*)"monster")) { - std::string file, name; - if(readXMLString(p, "file", file) && readXMLString(p, "name", name)) - { - file = getFilePath(FILE_TYPE_MOD, "monster/" + file); - g_monsters.loadMonster(file, name, true); - } + std::string path, name; + if((readXMLString(p, "file", path) || readXMLString(p, "path", path)) && readXMLString(p, "name", name)) + g_monsters.loadMonster(getFilePath(FILE_TYPE_MOD, "monster/" + path), name, true); } else if(!xmlStrcmp(p->name, (const xmlChar*)"item")) { if(readXMLInteger(p, "id", intValue)) - Item::items.parseItemNode(p, intValue); //duplicates checking isn't necessary here + Item::items.parseItemNode(p, intValue); } if(!xmlStrcmp(p->name, (const xmlChar*)"description") || !xmlStrcmp(p->name, (const xmlChar*)"info")) { @@ -256,12 +318,14 @@ bool ScriptingManager::loadFromXml(const std::string& file, bool& enabled) { if(!readXMLString(p, "name", strValue)) { - std::cout << "[Warning - ScriptingManager::loadFromXml] Lib without name in mod " << strValue << std::endl; - p = p->next; - continue; + if(!xmlStrcmp(p->name, (const xmlChar*)"lib")) + strValue = mod.name + "-lib"; + else if(!xmlStrcmp(p->name, (const xmlChar*)"config")) + strValue = mod.name + "-config"; } + else + toLowerCaseString(strValue); - toLowerCaseString(strValue); std::string strLib; if(parseXMLContentString(p->children, strLib)) { @@ -275,7 +339,7 @@ bool ScriptingManager::loadFromXml(const std::string& file, bool& enabled) libMap[strValue] = lb; } else - std::cout << "[Warning - ScriptingManager::loadFromXml] Duplicated lib in mod " + std::clog << "[Warning - ScriptManager::loadFromXml] Duplicated lib in mod " << strValue << ", previously declared in " << it->second.first << std::endl; } } @@ -296,13 +360,12 @@ bool ScriptingManager::loadFromXml(const std::string& file, bool& enabled) } } } - - p = p->next; } } enabled = mod.enabled; modMap[mod.name] = mod; + xmlFreeDoc(doc); return true; } diff --git a/scriptmanager.h b/scriptmanager.h index 015271c..0fed5f0 100644 --- a/scriptmanager.h +++ b/scriptmanager.h @@ -32,32 +32,34 @@ struct LibBlock }; typedef std::map LibMap; -class ScriptingManager +class ScriptManager { public: - virtual ~ScriptingManager() {clearMods();} - static ScriptingManager* getInstance() + static ScriptManager* getInstance() { - static ScriptingManager instance; + static ScriptManager instance; return &instance; } - bool load(); + ScriptManager(); + virtual ~ScriptManager() {clearMods();} + bool loadSystem(); bool loadMods(); + void clearMods(); bool reloadMods(); - bool loadFromXml(const std::string& file, bool& enabled); - inline LibMap::iterator getFirstLib() {return libMap.begin();} inline LibMap::iterator getLastLib() {return libMap.end();} inline ModMap::iterator getFirstMod() {return modMap.begin();} inline ModMap::iterator getLastMod() {return modMap.end();} + protected: + bool loadFromXml(const std::string& file, bool& enabled); + private: - ScriptingManager(): modsLoaded(false) {} bool modsLoaded; LibMap libMap; diff --git a/server.cpp b/server.cpp index cdbddff..855348b 100644 --- a/server.cpp +++ b/server.cpp @@ -20,15 +20,38 @@ #include "connection.h" #include "outputmessage.h" #include "textlogger.h" +#include "scheduler.h" -#include "game.h" #include "configmanager.h" +#include "tools.h" -extern Game g_game; extern ConfigManager g_config; bool ServicePort::m_logError = true; +void ServicePort::services(boost::weak_ptr weakService, IPAddressList ips, uint16_t port) +{ + if(weakService.expired()) + return; + + if(ServicePort_ptr service = weakService.lock()) + service->open(ips, port); +} + +void ServicePort::service(boost::weak_ptr weakService, IPAddress ip, uint16_t port) +{ + if(weakService.expired()) + return; + + ServicePort_ptr service = weakService.lock(); + if(!service) + return; + + IPAddressList ips; + ips.push_back(ip); + service->open(ips, port); +} + bool ServicePort::add(Service_ptr newService) { for(ServiceVec::const_iterator it = m_services.begin(); it != m_services.end(); ++it) @@ -41,79 +64,76 @@ bool ServicePort::add(Service_ptr newService) return true; } -void ServicePort::onOpen(boost::weak_ptr weakService, uint16_t port) +void ServicePort::open(IPAddressList ips, uint16_t port) { - if(weakService.expired()) - return; + m_pendingStart = false; + m_serverPort = port; - if(ServicePort_ptr service = weakService.lock()) + bool error = false; + IPAddressList pendingIps; + for(IPAddressList::iterator it = ips.begin(); it != ips.end(); ++it) { - #ifdef __DEBUG_NET_DETAIL__ - std::cout << "ServicePort::onOpen" << std::endl; - #endif - service->open(port); - } -} + try + { + Acceptor_ptr tmp(new boost::asio::ip::tcp::acceptor(m_io_service, + boost::asio::ip::tcp::endpoint(*it, m_serverPort))); + tmp->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + tmp->set_option(boost::asio::ip::tcp::no_delay(true)); -void ServicePort::open(uint16_t port) -{ - m_serverPort = port; - m_pendingStart = false; - try - { - if(g_config.getBool(ConfigManager::BIND_IP_ONLY)) - m_acceptor = new boost::asio::ip::tcp::acceptor(m_io_service, boost::asio::ip::tcp::endpoint( - boost::asio::ip::address(boost::asio::ip::address_v4::from_string( - g_config.getString(ConfigManager::IP))), m_serverPort)); - else - m_acceptor = new boost::asio::ip::tcp::acceptor(m_io_service, boost::asio::ip::tcp::endpoint( - boost::asio::ip::address(boost::asio::ip::address_v4(INADDR_ANY)), m_serverPort)); - - accept(); - } - catch(boost::system::system_error& e) - { - if(m_logError) + accept(tmp); + m_acceptors[tmp] = *it; + } + catch(std::exception& e) { - LOG_MESSAGE(LOGTYPE_ERROR, e.what(), "NETWORK") - m_logError = false; + if(m_logError) + { + LOG_MESSAGE(LOGTYPE_ERROR, e.what(), "NETWORK") + pendingIps.push_back(*it); + if(!error) + error = true; + } } + } + if(error) + { + m_logError = false; m_pendingStart = true; Scheduler::getInstance().addEvent(createSchedulerTask(5000, boost::bind( - &ServicePort::onOpen, boost::weak_ptr(shared_from_this()), m_serverPort))); + &ServicePort::services, boost::weak_ptr(shared_from_this()), pendingIps, m_serverPort))); } } void ServicePort::close() { - if(!m_acceptor) + if(!m_acceptors.size()) return; - if(m_acceptor->is_open()) + for(AcceptorVec::iterator it = m_acceptors.begin(); it != m_acceptors.end(); ++it) { + if(it->first->is_open()) + continue; + boost::system::error_code error; - m_acceptor->close(error); + it->first->close(error); if(error) + { PRINT_ASIO_ERROR("Closing listen socket"); + } } - delete m_acceptor; - m_acceptor = NULL; + m_acceptors.clear(); } -void ServicePort::accept() +void ServicePort::accept(Acceptor_ptr acceptor) { - if(!m_acceptor) - return; - try { boost::asio::ip::tcp::socket* socket = new boost::asio::ip::tcp::socket(m_io_service); - m_acceptor->async_accept(*socket, boost::bind( - &ServicePort::handle, this, socket, boost::asio::placeholders::error)); + acceptor->async_accept(*socket, boost::bind( + &ServicePort::handle, this, acceptor, socket, boost::asio::placeholders::error)); } - catch(boost::system::system_error& e) + catch(std::exception& e) { if(m_logError) { @@ -123,14 +143,14 @@ void ServicePort::accept() } } -void ServicePort::handle(boost::asio::ip::tcp::socket* socket, const boost::system::error_code& error) +void ServicePort::handle(Acceptor_ptr acceptor, boost::asio::ip::tcp::socket* socket, const boost::system::error_code& error) { if(!error) { if(m_services.empty()) { #ifdef __DEBUG_NET__ - std::cout << "[Error - ServerPort::handle] No services running!" << std::endl; + std::clog << "[Error - ServerPort::handle] No services running!" << std::endl; #endif return; } @@ -162,9 +182,9 @@ void ServicePort::handle(boost::asio::ip::tcp::socket* socket, const boost::syst } #ifdef __DEBUG_NET_DETAIL__ - std::cout << "handle - OK" << std::endl; + std::clog << "handle - OK" << std::endl; #endif - accept(); + accept(acceptor); } else if(error != boost::asio::error::operation_aborted) { @@ -174,19 +194,20 @@ void ServicePort::handle(boost::asio::ip::tcp::socket* socket, const boost::syst { m_pendingStart = true; Scheduler::getInstance().addEvent(createSchedulerTask(5000, boost::bind( - &ServicePort::onOpen, boost::weak_ptr(shared_from_this()), m_serverPort))); + &ServicePort::service, boost::weak_ptr(shared_from_this()), + m_acceptors[acceptor], m_serverPort))); } } #ifdef __DEBUG_NET__ else - std::cout << "[Error - ServerPort::handle] Operation aborted." << std::endl; + std::clog << "[Error - ServerPort::handle] Operation aborted." << std::endl; #endif } std::string ServicePort::getProtocolNames() const { if(m_services.empty()) - return ""; + return std::string(); std::string str = m_services.front()->getProtocolName(); for(int32_t i = 1, j = m_services.size(); i < j; ++i) @@ -200,7 +221,7 @@ std::string ServicePort::getProtocolNames() const Protocol* ServicePort::makeProtocol(bool checksum, NetworkMessage& msg) const { - uint8_t protocolId = msg.GetByte(); + uint8_t protocolId = msg.get(); for(ServiceVec::const_iterator it = m_services.begin(); it != m_services.end(); ++it) { if((*it)->getProtocolId() == protocolId && ((checksum && (*it)->hasChecksum()) || !(*it)->hasChecksum())) @@ -215,11 +236,19 @@ void ServiceManager::run() assert(!running); try { - m_io_service.run(); - if(!running) - running = true; + std::vector > threads; + for(uint32_t i = 0; i < g_config.getNumber(ConfigManager::SERVICE_THREADS); ++i) + { + boost::shared_ptr thread(new boost::thread( + boost::bind(&boost::asio::io_service::run, &m_io_service))); + threads.push_back(thread); + } + + running = true; + for(std::vector >::const_iterator it = threads.begin(); it != threads.end(); ++it) + (*it)->join(); } - catch(boost::system::system_error& e) + catch(std::exception& e) { LOG_MESSAGE(LOGTYPE_ERROR, e.what(), "NETWORK") } @@ -237,7 +266,7 @@ void ServiceManager::stop() { m_io_service.post(boost::bind(&ServicePort::close, it->second)); } - catch(boost::system::system_error& e) + catch(std::exception& e) { LOG_MESSAGE(LOGTYPE_ERROR, e.what(), "NETWORK") } diff --git a/server.h b/server.h index d6486c6..36722a5 100644 --- a/server.h +++ b/server.h @@ -18,18 +18,19 @@ #ifndef __SERVER__ #define __SERVER__ #include "otsystem.h" + +#include "connection.h" #include class ServiceBase; -typedef boost::shared_ptr Service_ptr; - class ServicePort; -typedef boost::shared_ptr ServicePort_ptr; - class Connection; -typedef boost::shared_ptr Connection_ptr; - class Protocol; +class NetworkMessage; + +typedef boost::asio::ip::address IPAddress; +typedef std::vector IPAddressList; + class ServiceBase : boost::noncopyable { public: @@ -54,21 +55,22 @@ class Service : public ServiceBase const char* getProtocolName() const {return ProtocolType::protocolName();} }; -class NetworkMessage; +typedef boost::shared_ptr Acceptor_ptr; class ServicePort : boost::noncopyable, public boost::enable_shared_from_this { public: ServicePort(boost::asio::io_service& io_service): m_io_service(io_service), - m_acceptor(NULL), m_serverPort(0), m_pendingStart(false) {} + m_serverPort(0), m_pendingStart(false) {} virtual ~ServicePort() {close();} - bool add(Service_ptr); - static void onOpen(boost::weak_ptr weakService, uint16_t port); + static void services(boost::weak_ptr weakService, IPAddressList ips, uint16_t port); + static void service(boost::weak_ptr weakService, IPAddress ip, uint16_t port); - void open(uint16_t port); + bool add(Service_ptr); + void open(IPAddressList ips, uint16_t port); void close(); - void handle(boost::asio::ip::tcp::socket* socket, const boost::system::error_code& error); + void handle(Acceptor_ptr acceptor, boost::asio::ip::tcp::socket* socket, const boost::system::error_code& error); bool isSingleSocket() const {return m_services.size() && m_services.front()->isSingleSocket();} std::string getProtocolNames() const; @@ -76,21 +78,21 @@ class ServicePort : boost::noncopyable, public boost::enable_shared_from_this ServiceVec; ServiceVec m_services; - boost::asio::io_service& m_io_service; - boost::asio::ip::tcp::acceptor* m_acceptor; + typedef std::map AcceptorVec; + AcceptorVec m_acceptors; + boost::asio::io_service& m_io_service; uint16_t m_serverPort; bool m_pendingStart; static bool m_logError; }; -typedef boost::shared_ptr ServicePort_ptr; class ServiceManager : boost::noncopyable { ServiceManager(const ServiceManager&); @@ -99,7 +101,7 @@ class ServiceManager : boost::noncopyable virtual ~ServiceManager() {stop();} template - bool add(uint16_t port); + bool add(uint16_t port, IPAddressList ips); void run(); void stop(); @@ -119,11 +121,11 @@ class ServiceManager : boost::noncopyable }; template -bool ServiceManager::add(uint16_t port) +bool ServiceManager::add(uint16_t port, IPAddressList ips) { if(!port) { - std::cout << "> ERROR: No port provided for service " << ProtocolType::protocolName() << ". Service disabled." << std::endl; + std::clog << "> ERROR: No port provided for service " << ProtocolType::protocolName() << ", service disabled." << std::endl; return false; } @@ -132,7 +134,7 @@ bool ServiceManager::add(uint16_t port) if(it == m_acceptors.end()) { servicePort.reset(new ServicePort(m_io_service)); - servicePort->open(port); + servicePort->open(ips, port); m_acceptors[port] = servicePort; } else @@ -140,8 +142,8 @@ bool ServiceManager::add(uint16_t port) servicePort = it->second; if(servicePort->isSingleSocket() || ProtocolType::isSingleSocket) { - std::cout << "> ERROR: " << ProtocolType::protocolName() << " and " << servicePort->getProtocolNames(); - std::cout << " cannot use the same port (" << port << ")." << std::endl; + std::clog << "> ERROR: " << ProtocolType::protocolName() << " and " << servicePort->getProtocolNames() + << " cannot use the same port (" << port << ")." << std::endl; return false; } } diff --git a/sha1.cpp b/sha1.cpp deleted file mode 100644 index 427efd9..0000000 --- a/sha1.cpp +++ /dev/null @@ -1,587 +0,0 @@ -/* - * sha1.cpp - * - * Copyright (C) 1998 - * Paul E. Jones - * All Rights Reserved. - * - ***************************************************************************** - * $Id: sha1.cpp,v 1.9 2004/03/27 18:02:20 paulej Exp $ - ***************************************************************************** - * - * Description: - * This class implements the Secure Hashing Standard as defined - * in FIPS PUB 180-1 published April 17, 1995. - * - * The Secure Hashing Standard, which uses the Secure Hashing - * Algorithm (SHA), produces a 160-bit message digest for a - * given data stream. In theory, it is highly improbable that - * two messages will produce the same message digest. Therefore, - * this algorithm can serve as a means of providing a "fingerprint" - * for a message. - * - * Portability Issues: - * SHA-1 is defined in terms of 32-bit "words". This code was - * written with the expectation that the processor has at least - * a 32-bit machine word size. If the machine word size is larger, - * the code should still function properly. One caveat to that - * is that the input functions taking characters and character arrays - * assume that only 8 bits of information are stored in each character. - * - * Caveats: - * SHA-1 is designed to work with messages less than 2^64 bits long. - * Although SHA-1 allows a message digest to be generated for - * messages of any number of bits less than 2^64, this implementation - * only works with messages with a length that is a multiple of 8 - * bits. - * - */ - -#include "otpch.h" -#include "sha1.h" - -/* - * SHA1 - * - * Description: - * This is the constructor for the sha1 class. - * - * Parameters: - * None. - * - * Returns: - * Nothing. - * - * Comments: - * - */ -SHA1::SHA1() -{ - Reset(); -} - -/* - * ~SHA1 - * - * Description: - * This is the destructor for the sha1 class - * - * Parameters: - * None. - * - * Returns: - * Nothing. - * - * Comments: - * - */ -SHA1::~SHA1() -{ - // The destructor does nothing -} - -/* - * Reset - * - * Description: - * This function will initialize the sha1 class member variables - * in preparation for computing a new message digest. - * - * Parameters: - * None. - * - * Returns: - * Nothing. - * - * Comments: - * - */ -void SHA1::Reset() -{ - Length_Low = 0; - Length_High = 0; - Message_Block_Index = 0; - - H[0] = 0x67452301; - H[1] = 0xEFCDAB89; - H[2] = 0x98BADCFE; - H[3] = 0x10325476; - H[4] = 0xC3D2E1F0; - - Computed = false; - Corrupted = false; -} - -/* - * Result - * - * Description: - * This function will return the 160-bit message digest into the - * array provided. - * - * Parameters: - * message_digest_array: [out] - * This is an array of five unsigned integers which will be filled - * with the message digest that has been computed. - * - * Returns: - * True if successful, false if it failed. - * - * Comments: - * - */ -bool SHA1::Result(unsigned *message_digest_array) -{ - int i; // Counter - - if (Corrupted) - { - return false; - } - - if (!Computed) - { - PadMessage(); - Computed = true; - } - - for(i = 0; i < 5; i++) - { - message_digest_array[i] = H[i]; - } - - return true; -} - -/* - * Input - * - * Description: - * This function accepts an array of octets as the next portion of - * the message. - * - * Parameters: - * message_array: [in] - * An array of characters representing the next portion of the - * message. - * - * Returns: - * Nothing. - * - * Comments: - * - */ -void SHA1::Input( const unsigned char *message_array, - unsigned length) -{ - if (!length) - { - return; - } - - if (Computed || Corrupted) - { - Corrupted = true; - return; - } - - while(length-- && !Corrupted) - { - Message_Block[Message_Block_Index++] = (*message_array & 0xFF); - - Length_Low += 8; - Length_Low &= 0xFFFFFFFF; // Force it to 32 bits - if (Length_Low == 0) - { - Length_High++; - Length_High &= 0xFFFFFFFF; // Force it to 32 bits - if (Length_High == 0) - { - Corrupted = true; // Message is too long - } - } - - if (Message_Block_Index == 64) - { - ProcessMessageBlock(); - } - - message_array++; - } -} - -/* - * Input - * - * Description: - * This function accepts an array of octets as the next portion of - * the message. - * - * Parameters: - * message_array: [in] - * An array of characters representing the next portion of the - * message. - * length: [in] - * The length of the message_array - * - * Returns: - * Nothing. - * - * Comments: - * - */ -void SHA1::Input( const char *message_array, - unsigned length) -{ - Input((unsigned char *) message_array, length); -} - -/* - * Input - * - * Description: - * This function accepts a single octets as the next message element. - * - * Parameters: - * message_element: [in] - * The next octet in the message. - * - * Returns: - * Nothing. - * - * Comments: - * - */ -void SHA1::Input(unsigned char message_element) -{ - Input(&message_element, 1); -} - -/* - * Input - * - * Description: - * This function accepts a single octet as the next message element. - * - * Parameters: - * message_element: [in] - * The next octet in the message. - * - * Returns: - * Nothing. - * - * Comments: - * - */ -void SHA1::Input(char message_element) -{ - Input((unsigned char *) &message_element, 1); -} - -/* - * operator<< - * - * Description: - * This operator makes it convenient to provide character strings to - * the SHA1 object for processing. - * - * Parameters: - * message_array: [in] - * The character array to take as input. - * - * Returns: - * A reference to the SHA1 object. - * - * Comments: - * Each character is assumed to hold 8 bits of information. - * - */ -SHA1& SHA1::operator<<(const char *message_array) -{ - const char *p = message_array; - - while(*p) - { - Input(*p); - p++; - } - - return *this; -} - -/* - * operator<< - * - * Description: - * This operator makes it convenient to provide character strings to - * the SHA1 object for processing. - * - * Parameters: - * message_array: [in] - * The character array to take as input. - * - * Returns: - * A reference to the SHA1 object. - * - * Comments: - * Each character is assumed to hold 8 bits of information. - * - */ -SHA1& SHA1::operator<<(const unsigned char *message_array) -{ - const unsigned char *p = message_array; - - while(*p) - { - Input(*p); - p++; - } - - return *this; -} - -/* - * operator<< - * - * Description: - * This function provides the next octet in the message. - * - * Parameters: - * message_element: [in] - * The next octet in the message - * - * Returns: - * A reference to the SHA1 object. - * - * Comments: - * The character is assumed to hold 8 bits of information. - * - */ -SHA1& SHA1::operator<<(const char message_element) -{ - Input((unsigned char *) &message_element, 1); - - return *this; -} - -/* - * operator<< - * - * Description: - * This function provides the next octet in the message. - * - * Parameters: - * message_element: [in] - * The next octet in the message - * - * Returns: - * A reference to the SHA1 object. - * - * Comments: - * The character is assumed to hold 8 bits of information. - * - */ -SHA1& SHA1::operator<<(const unsigned char message_element) -{ - Input(&message_element, 1); - - return *this; -} - -/* - * ProcessMessageBlock - * - * Description: - * This function will process the next 512 bits of the message - * stored in the Message_Block array. - * - * Parameters: - * None. - * - * Returns: - * Nothing. - * - * Comments: - * Many of the variable names in this function, especially the single - * character names, were used because those were the names used - * in the publication. - * - */ -void SHA1::ProcessMessageBlock() -{ - const unsigned K[] = { // Constants defined for SHA-1 - 0x5A827999, - 0x6ED9EBA1, - 0x8F1BBCDC, - 0xCA62C1D6 - }; - int t; // Loop counter - unsigned temp; // Temporary word value - unsigned W[80]; // Word sequence - unsigned A, B, C, D, E; // Word buffers - - /* - * Initialize the first 16 words in the array W - */ - for(t = 0; t < 16; t++) - { - W[t] = ((unsigned) Message_Block[t * 4]) << 24; - W[t] |= ((unsigned) Message_Block[t * 4 + 1]) << 16; - W[t] |= ((unsigned) Message_Block[t * 4 + 2]) << 8; - W[t] |= ((unsigned) Message_Block[t * 4 + 3]); - } - - for(t = 16; t < 80; t++) - W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); - - A = H[0]; - B = H[1]; - C = H[2]; - D = H[3]; - E = H[4]; - - for(t = 0; t < 20; t++) - { - temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = CircularShift(30,B); - B = A; - A = temp; - } - - for(t = 20; t < 40; t++) - { - temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = CircularShift(30,B); - B = A; - A = temp; - } - - for(t = 40; t < 60; t++) - { - temp = CircularShift(5,A) + - ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = CircularShift(30,B); - B = A; - A = temp; - } - - for(t = 60; t < 80; t++) - { - temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; - temp &= 0xFFFFFFFF; - E = D; - D = C; - C = CircularShift(30,B); - B = A; - A = temp; - } - - H[0] = (H[0] + A) & 0xFFFFFFFF; - H[1] = (H[1] + B) & 0xFFFFFFFF; - H[2] = (H[2] + C) & 0xFFFFFFFF; - H[3] = (H[3] + D) & 0xFFFFFFFF; - H[4] = (H[4] + E) & 0xFFFFFFFF; - - Message_Block_Index = 0; -} - -/* - * PadMessage - * - * Description: - * According to the standard, the message must be padded to an even - * 512 bits. The first padding bit must be a '1'. The last 64 bits - * represent the length of the original message. All bits in between - * should be 0. This function will pad the message according to those - * rules by filling the message_block array accordingly. It will also - * call ProcessMessageBlock() appropriately. When it returns, it - * can be assumed that the message digest has been computed. - * - * Parameters: - * None. - * - * Returns: - * Nothing. - * - * Comments: - * - */ -void SHA1::PadMessage() -{ - /* - * Check to see if the current message block is too small to hold - * the initial padding bits and length. If so, we will pad the - * block, process it, and then continue padding into a second block. - */ - if (Message_Block_Index > 55) - { - Message_Block[Message_Block_Index++] = 0x80; - while(Message_Block_Index < 64) - { - Message_Block[Message_Block_Index++] = 0; - } - - ProcessMessageBlock(); - - while(Message_Block_Index < 56) - { - Message_Block[Message_Block_Index++] = 0; - } - } - else - { - Message_Block[Message_Block_Index++] = 0x80; - while(Message_Block_Index < 56) - { - Message_Block[Message_Block_Index++] = 0; - } - - } - - /* - * Store the message length as the last 8 octets - */ - Message_Block[56] = (Length_High >> 24) & 0xFF; - Message_Block[57] = (Length_High >> 16) & 0xFF; - Message_Block[58] = (Length_High >> 8) & 0xFF; - Message_Block[59] = (Length_High) & 0xFF; - Message_Block[60] = (Length_Low >> 24) & 0xFF; - Message_Block[61] = (Length_Low >> 16) & 0xFF; - Message_Block[62] = (Length_Low >> 8) & 0xFF; - Message_Block[63] = (Length_Low) & 0xFF; - - ProcessMessageBlock(); -} - - -/* - * CircularShift - * - * Description: - * This member function will perform a circular shifting operation. - * - * Parameters: - * bits: [in] - * The number of bits to shift (1-31) - * word: [in] - * The value to shift (assumes a 32-bit integer) - * - * Returns: - * The shifted value. - * - * Comments: - * - */ -unsigned SHA1::CircularShift(int bits, unsigned word) -{ - return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits)); -} diff --git a/sha1.h b/sha1.h deleted file mode 100644 index 92e6bc6..0000000 --- a/sha1.h +++ /dev/null @@ -1,99 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// OpenTibia - an opensource roleplaying game -//////////////////////////////////////////////////////////////////////// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -//////////////////////////////////////////////////////////////////////// - -/* - * sha1.h - * - * Copyright (C) 1998 - * Paul E. Jones - * All Rights Reserved. - * - ***************************************************************************** - * $Id: sha1.h,v 1.6 2004/03/27 18:02:26 paulej Exp $ - ***************************************************************************** - * - * Description: - * This class implements the Secure Hashing Standard as defined - * in FIPS PUB 180-1 published April 17, 1995. - * - * Many of the variable names in this class, especially the single - * character names, were used because those were the names used - * in the publication. - * - * Please read the file sha1.cpp for more information. - * - */ - -#ifndef __SHA1__ -#define __SHA1__ -class SHA1 -{ - public: - SHA1(); - virtual ~SHA1(); - - /* - * Re-initialize the class - */ - void Reset(); - - /* - * Returns the message digest - */ - bool Result(unsigned *message_digest_array); - - /* - * Provide input to SHA1 - */ - void Input(const unsigned char *message_array, unsigned length); - void Input(const char *message_array, unsigned length); - void Input(unsigned char message_element); - void Input(char message_element); - SHA1& operator<<(const char *message_array); - SHA1& operator<<(const unsigned char *message_array); - SHA1& operator<<(const char message_element); - SHA1& operator<<(const unsigned char message_element); - - private: - /* - * Process the next 512 bits of the message - */ - void ProcessMessageBlock(); - - /* - * Pads the current message block to 512 bits - */ - void PadMessage(); - - /* - * Performs a circular left shift operation - */ - inline unsigned CircularShift(int bits, unsigned word); - - unsigned H[5]; // Message digest buffers - - unsigned Length_Low; // Message length in bits - unsigned Length_High; // Message length in bits - - unsigned char Message_Block[64]; // 512-bit message blocks - int Message_Block_Index; // Index into message block array - - bool Computed; // Is the digest computed? - bool Corrupted; // Is the message digest corruped? - -}; -#endif diff --git a/spawn.cpp b/spawn.cpp index 3be2151..7a47d32 100644 --- a/spawn.cpp +++ b/spawn.cpp @@ -36,7 +36,6 @@ extern Game g_game; Spawns::Spawns() { - filename = ""; loaded = started = false; } @@ -55,25 +54,21 @@ bool Spawns::loadFromXml(const std::string& _filename) xmlDocPtr doc = xmlParseFile(filename.c_str()); if(!doc) { - std::cout << "[Warning - Spawns::loadFromXml] Cannot open spawns file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Spawns::loadFromXml] Cannot open spawns file." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr spawnNode, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"spawns")) { - std::cout << "[Error - Spawns::loadFromXml] Malformed spawns file." << std::endl; + std::clog << "[Error - Spawns::loadFromXml] Malformed spawns file." << std::endl; xmlFreeDoc(doc); return false; } - spawnNode = root->children; - while(spawnNode) - { - parseSpawnNode(spawnNode, false); - spawnNode = spawnNode->next; - } + for(xmlNodePtr p = root->children; p; p = p->next) + parseSpawnNode(p, false); xmlFreeDoc(doc); loaded = true; @@ -106,7 +101,7 @@ bool Spawns::parseSpawnNode(xmlNodePtr p, bool checkDuplicate) } else { - IntegerVec posVec = vectorAtoi(explodeString(",", strValue)); + IntegerVec posVec = vectorAtoi(explodeString(strValue, ",")); if(posVec.size() < 3) return false; @@ -128,28 +123,21 @@ bool Spawns::parseSpawnNode(xmlNodePtr p, bool checkDuplicate) } spawnList.push_back(spawn); - xmlNodePtr tmpNode = p->children; - while(tmpNode) + for(xmlNodePtr tmpNode = p->children; tmpNode; tmpNode = tmpNode->next) { if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"monster")) { - std::string name; if(!readXMLString(tmpNode, "name", strValue)) - { - tmpNode = tmpNode->next; continue; - } - name = strValue; + std::string name = strValue; int32_t interval = MINSPAWN_INTERVAL / 1000; if(readXMLInteger(tmpNode, "spawntime", intValue) || readXMLInteger(tmpNode, "interval", intValue)) { if(intValue <= interval) { - std::cout << "[Warning - Spawns::loadFromXml] " << name << " " << centerPos << " spawntime cannot"; - std::cout << " be less than " << interval << " seconds." << std::endl; - - tmpNode = tmpNode->next; + std::clog << "[Warning - Spawns::loadFromXml] " << name << " " << centerPos << " spawntime cannot" + << " be less or equal than " << interval << " seconds." << std::endl; continue; } @@ -175,14 +163,10 @@ bool Spawns::parseSpawnNode(xmlNodePtr p, bool checkDuplicate) } else if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"npc")) { - std::string name; if(!readXMLString(tmpNode, "name", strValue)) - { - tmpNode = tmpNode->next; continue; - } - name = strValue; + std::string name = strValue; Position placePos = centerPos; if(readXMLInteger(tmpNode, "x", intValue)) placePos.x += intValue; @@ -199,17 +183,12 @@ bool Spawns::parseSpawnNode(xmlNodePtr p, bool checkDuplicate) Npc* npc = Npc::createNpc(name); if(!npc) - { - tmpNode = tmpNode->next; continue; - } npc->setMasterPosition(placePos, radius); npc->setDirection(direction); npcList.push_back(npc); } - - tmpNode = tmpNode->next; } return true; @@ -238,7 +217,7 @@ void Spawns::clear() spawnList.clear(); loaded = false; - filename = ""; + filename = std::string(); } bool Spawns::isInZone(const Position& centerPos, int32_t radius, const Position& pos) @@ -252,7 +231,7 @@ bool Spawns::isInZone(const Position& centerPos, int32_t radius, const Position& void Spawn::startEvent() { - if(checkSpawnEvent == 0) + if(!checkSpawnEvent) checkSpawnEvent = Scheduler::getInstance().addEvent(createSchedulerTask(getInterval(), boost::bind(&Spawn::checkSpawn, this))); } @@ -312,21 +291,17 @@ bool Spawn::spawnMonster(uint32_t spawnId, MonsterType* mType, const Position& p return false; } } - else + else if(!g_game.placeCreature(monster, pos, false, true)) { - if(!g_game.placeCreature(monster, pos, false, true)) - { - delete monster; - return false; - } + delete monster; + return false; } monster->setSpawn(this); - monster->addRef(); - monster->setMasterPosition(pos, radius); monster->setDirection(dir); + monster->addRef(); spawnedMap.insert(SpawnedPair(spawnId, monster)); spawnMap[spawnId].lastSpawn = OTSYS_TIME(); return true; @@ -334,9 +309,10 @@ bool Spawn::spawnMonster(uint32_t spawnId, MonsterType* mType, const Position& p void Spawn::startup() { + spawnBlock_t sb; for(SpawnMap::iterator it = spawnMap.begin(); it != spawnMap.end(); ++it) { - spawnBlock_t& sb = it->second; + sb = it->second; spawnMonster(it->first, sb.mType, sb.pos, sb.direction, true); } } @@ -344,29 +320,19 @@ void Spawn::startup() void Spawn::checkSpawn() { #ifdef __DEBUG_SPAWN__ - std::cout << "[Notice] Spawn::checkSpawn " << this << std::endl; + std::clog << "[Notice] Spawn::checkSpawn " << this << std::endl; #endif checkSpawnEvent = 0; - - Monster* monster; - uint32_t spawnId; - - for(SpawnedMap::iterator it = spawnedMap.begin(); it != spawnedMap.end();) + for(SpawnedMap::iterator it = spawnedMap.begin(); it != spawnedMap.end(); ) { - spawnId = it->first; - monster = it->second; - - if(monster->isRemoved()) + if(it->second->isRemoved()) { - if(spawnId != 0) - spawnMap[spawnId].lastSpawn = OTSYS_TIME(); + if(it->first) + spawnMap[it->first].lastSpawn = OTSYS_TIME(); + + if(it->second) + it->second->unRef(); - monster->unRef(); - spawnedMap.erase(it++); - } - else if(!isInSpawnZone(monster->getPosition()) && spawnId != 0) - { - spawnedMap.insert(SpawnedPair(0, monster)); spawnedMap.erase(it++); } else @@ -376,32 +342,31 @@ void Spawn::checkSpawn() uint32_t spawnCount = 0; for(SpawnMap::iterator it = spawnMap.begin(); it != spawnMap.end(); ++it) { - spawnId = it->first; spawnBlock_t& sb = it->second; + if(spawnedMap.count(it->first)) + continue; - if(spawnedMap.count(spawnId) == 0) - { - if(OTSYS_TIME() >= sb.lastSpawn + sb.interval) - { - if(findPlayer(sb.pos)) - { - sb.lastSpawn = OTSYS_TIME(); - continue; - } + if(OTSYS_TIME() < sb.lastSpawn + sb.interval) + continue; - spawnMonster(spawnId, sb.mType, sb.pos, sb.direction); - ++spawnCount; - if(spawnCount >= (uint32_t)g_config.getNumber(ConfigManager::RATE_SPAWN)) - break; - } + if(findPlayer(sb.pos)) + { + sb.lastSpawn = OTSYS_TIME(); + continue; } + + spawnMonster(it->first, sb.mType, sb.pos, sb.direction); + uint32_t minSpawnCount = g_config.getNumber(ConfigManager::RATE_SPAWN_MIN), + maxSpawnCount = g_config.getNumber(ConfigManager::RATE_SPAWN_MAX); + if(++spawnCount >= (uint32_t)random_range(minSpawnCount, maxSpawnCount)) + break; } if(spawnedMap.size() < spawnMap.size()) checkSpawnEvent = Scheduler::getInstance().addEvent(createSchedulerTask(getInterval(), boost::bind(&Spawn::checkSpawn, this))); #ifdef __DEBUG_SPAWN__ else - std::cout << "[Notice] Spawn::checkSpawn stopped " << this << std::endl; + std::clog << "[Notice] Spawn::checkSpawn stopped " << this << std::endl; #endif } @@ -409,14 +374,14 @@ bool Spawn::addMonster(const std::string& _name, const Position& _pos, Direction { if(!g_game.getTile(_pos)) { - std::cout << "[Spawn::addMonster] NULL tile at spawn position (" << _pos << ")" << std::endl; + std::clog << "[Spawn::addMonster] NULL tile at spawn position (" << _pos << ")" << std::endl; return false; } MonsterType* mType = g_monsters.getMonsterType(_name); if(!mType) { - std::cout << "[Spawn::addMonster] Cannot find \"" << _name << "\"" << std::endl; + std::clog << "[Spawn::addMonster] Cannot find \"" << _name << "\"" << std::endl; return false; } @@ -439,20 +404,20 @@ void Spawn::removeMonster(Monster* monster) { for(SpawnedMap::iterator it = spawnedMap.begin(); it != spawnedMap.end(); ++it) { - if(it->second == monster) - { - monster->unRef(); - spawnedMap.erase(it); - break; - } + if(it->second != monster) + continue; + + monster->unRef(); + spawnedMap.erase(it); + break; } } void Spawn::stopEvent() { - if(checkSpawnEvent != 0) - { - Scheduler::getInstance().stopEvent(checkSpawnEvent); - checkSpawnEvent = 0; - } + if(!checkSpawnEvent) + return; + + Scheduler::getInstance().stopEvent(checkSpawnEvent); + checkSpawnEvent = 0; } diff --git a/spawn.h b/spawn.h index 1cb6de4..5df37eb 100644 --- a/spawn.h +++ b/spawn.h @@ -46,8 +46,8 @@ class Spawns void startup(); void clear(); - bool isLoaded() {return loaded;} - bool isStarted() {return started;} + bool isLoaded() const {return loaded;} + bool isStarted() const {return started;} private: Spawns(); diff --git a/spells.cpp b/spells.cpp index 3ac76db..96f1267 100644 --- a/spells.cpp +++ b/spells.cpp @@ -39,6 +39,7 @@ Spells::Spells(): m_interface("Spell Interface") { m_interface.initState(); + spellId = 0; } ReturnValue Spells::onPlayerSay(Player* player, const std::string& words) @@ -69,16 +70,26 @@ ReturnValue Spells::onPlayerSay(Player* player, const std::string& words) trimString(reParam); } - if(!instantSpell->playerCastInstant(player, reParam)) + Position pos = player->getPosition(); + if(!instantSpell->castInstant(player, reParam)) return RET_NEEDEXCHANGE; - SpeakClasses type = SPEAK_SAY; + MessageClasses type = MSG_SPEAK_SPELL; if(g_config.getBool(ConfigManager::EMOTE_SPELLS)) - type = SPEAK_MONSTER_SAY; + type = MSG_SPEAK_MONSTER_SAY; if(!g_config.getBool(ConfigManager::SPELL_NAME_INSTEAD_WORDS)) + { + if(g_config.getBool(ConfigManager::UNIFIED_SPELLS)) + { + reWords = instantSpell->getWords(); + if(instantSpell->getHasParam()) + reWords += " \"" + reParam + "\""; + } + return g_game.internalCreatureSay(player, type, reWords, player->isGhost()) ? RET_NOERROR : RET_NOTPOSSIBLE; + } std::string ret = instantSpell->getName(); if(param.length()) @@ -94,8 +105,8 @@ ReturnValue Spells::onPlayerSay(Player* player, const std::string& words) ret += ": " + param.substr(tmp, rtmp); } - return g_game.internalCreatureSay(player, type, ret, player->isGhost()) ? - RET_NOERROR : RET_NOTPOSSIBLE; + return g_game.internalCreatureSay(player, type, ret, player->isGhost(), + NULL, &pos) ? RET_NOERROR : RET_NOTPOSSIBLE; } void Spells::clear() @@ -108,6 +119,7 @@ void Spells::clear() delete it->second; instants.clear(); + spellId = 0; m_interface.reInitState(); } @@ -126,11 +138,12 @@ Event* Spells::getEvent(const std::string& nodeName) return NULL; } -bool Spells::registerEvent(Event* event, xmlNodePtr p, bool override) +bool Spells::registerEvent(Event* event, xmlNodePtr, bool override) { if(InstantSpell* instant = dynamic_cast(event)) { InstantsMap::iterator it = instants.find(instant->getWords()); + instant->setId(spellId++); if(it == instants.end()) { instants[instant->getWords()] = instant; @@ -144,13 +157,14 @@ bool Spells::registerEvent(Event* event, xmlNodePtr p, bool override) return true; } - std::cout << "[Warning - Spells::registerEvent] Duplicate registered instant spell with words: " << instant->getWords() << std::endl; + std::clog << "[Warning - Spells::registerEvent] Duplicate registered instant spell with words: " << instant->getWords() << std::endl; return false; } if(RuneSpell* rune = dynamic_cast(event)) { RunesMap::iterator it = runes.find(rune->getRuneItemId()); + rune->setId(spellId++); if(it == runes.end()) { runes[rune->getRuneItemId()] = rune; @@ -164,7 +178,7 @@ bool Spells::registerEvent(Event* event, xmlNodePtr p, bool override) return true; } - std::cout << "[Warning - Spells::registerEvent] Duplicate registered rune with id: " << rune->getRuneItemId() << std::endl; + std::clog << "[Warning - Spells::registerEvent] Duplicate registered rune with id: " << rune->getRuneItemId() << std::endl; return false; } @@ -193,20 +207,21 @@ RuneSpell* Spells::getRuneSpellByName(const std::string& name) { for(RunesMap::iterator it = runes.begin(); it != runes.end(); ++it) { - if(strcasecmp(it->second->getName().c_str(), name.c_str()) == 0) + if(boost::algorithm::iequals(it->second->getName(), name)) return it->second; } return NULL; } -InstantSpell* Spells::getInstantSpell(const std::string words) +InstantSpell* Spells::getInstantSpell(const std::string& words) { InstantSpell* result = NULL; for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it) { InstantSpell* instantSpell = it->second; - if(strncasecmp(instantSpell->getWords().c_str(), words.c_str(), instantSpell->getWords().length()) == 0) + if(!asLowerCaseString(words).compare(0, instantSpell->getWords().length(), + asLowerCaseString(instantSpell->getWords()))) { if(!result || instantSpell->getWords().length() > result->getWords().length()) result = instantSpell; @@ -241,13 +256,13 @@ InstantSpell* Spells::getInstantSpellByIndex(const Player* player, uint32_t inde for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it) { InstantSpell* instantSpell = it->second; - if(instantSpell->canCast(player)) - { - if(count == index) - return instantSpell; + if(!instantSpell->canCast(player)) + continue; - ++count; - } + if(count == index) + return instantSpell; + + ++count; } return NULL; @@ -255,9 +270,10 @@ InstantSpell* Spells::getInstantSpellByIndex(const Player* player, uint32_t inde InstantSpell* Spells::getInstantSpellByName(const std::string& name) { + std::string tmpName = asLowerCaseString(name); for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it) { - if(strcasecmp(it->second->getName().c_str(), name.c_str()) == 0) + if(tmpName == asLowerCaseString(it->second->getName())) return it->second; } @@ -311,8 +327,7 @@ CombatSpell::CombatSpell(Combat* _combat, bool _needTarget, bool _needDirection) CombatSpell::~CombatSpell() { - if(combat) - delete combat; + delete combat; } bool CombatSpell::loadScriptCombat() @@ -367,7 +382,6 @@ bool CombatSpell::castSpell(Creature* creature, Creature* target) if(combat->hasArea()) { var.type = VARIANT_POSITION; - if(needTarget) var.pos = target->getPosition(); else if(needDirection) @@ -411,7 +425,7 @@ bool CombatSpell::executeCastSpell(Creature* creature, const LuaVariant& var) scriptstream << "local cid = " << env->addThing(creature) << std::endl; env->streamVariant(scriptstream, "var", var); - scriptstream << m_scriptData; + scriptstream << *m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -427,7 +441,7 @@ bool CombatSpell::executeCastSpell(Creature* creature, const LuaVariant& var) #ifdef __DEBUG_LUASCRIPTS__ char desc[60]; sprintf(desc, "onCastSpell - %s", creature->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -445,13 +459,14 @@ bool CombatSpell::executeCastSpell(Creature* creature, const LuaVariant& var) } else { - std::cout << "[Error - CombatSpell::executeCastSpell] Call stack overflow." << std::endl; + std::clog << "[Error - CombatSpell::executeCastSpell] Call stack overflow." << std::endl; return false; } } Spell::Spell() { + spellId = 0; level = 0; magLevel = 0; mana = 0; @@ -468,6 +483,10 @@ Spell::Spell() premium = false; isAggressive = true; learnable = false; + icon = SPELL_NONE; + + for(int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) + skills[i] = 10; } bool Spell::configureSpell(xmlNodePtr p) @@ -487,16 +506,16 @@ bool Spell::configureSpell(xmlNodePtr p) for(uint32_t i = 0; i < sizeof(reservedList) / sizeof(const char*); ++i) { - if(!strcasecmp(reservedList[i], name.c_str())) + if(boost::algorithm::iequals(reservedList[i], name.c_str())) { - std::cout << "Error: [Spell::configureSpell] Spell is using a reserved name: " << reservedList[i] << std::endl; + std::clog << "[Error - Spell::configureSpell] Spell is using a reserved name: " << reservedList[i] << std::endl; return false; } } } else { - std::cout << "Error: [Spell::configureSpell] Spell without name." << std::endl; + std::clog << "[Error - Spell::configureSpell] Spell without name." << std::endl; return false; } @@ -506,6 +525,23 @@ bool Spell::configureSpell(xmlNodePtr p) if(readXMLInteger(p, "maglv", intValue) || readXMLInteger(p, "magiclevel", intValue)) magLevel = intValue; + if(readXMLString(p, "skill", strValue) || readXMLString(p, "skills", strValue)) + { + std::vector strVector = explodeString(strValue, ";"), tmpVector; + for(std::vector::iterator it = strVector.begin(); it != strVector.end(); ++it) + { + tmpVector = explodeString(*it, ","); + if(tmpVector.size() > 1) + { + intValue = atoi(tmpVector[0].c_str()); + if(!intValue) + skills[getSkillId(tmpVector[0])] = atoi(tmpVector[1].c_str()); + else + skills[intValue] = atoi(tmpVector[1].c_str()); + } + } + } + if(readXMLInteger(p, "mana", intValue)) mana = intValue; @@ -539,9 +575,6 @@ bool Spell::configureSpell(xmlNodePtr p) if(readXMLInteger(p, "range", intValue)) range = intValue; - if(readXMLString(p, "blocking", strValue)) - blockingCreature = blockingSolid = booleanString(strValue); - if(readXMLString(p, "blocktype", strValue)) { std::string tmpStrValue = asLowerCaseString(strValue); @@ -552,26 +585,64 @@ bool Spell::configureSpell(xmlNodePtr p) else if(tmpStrValue == "creature") blockingCreature = true; else - std::cout << "[Warning - Spell::configureSpell] Blocktype \"" <children; - while(vocationNode) + if(readXMLInteger(p, "icon", intValue)) // TODO: needs a string values parser, like skulls etc. + icon = (Spells_t)intValue; + + if(readXMLString(p, "groups", strValue)) { - if(!parseVocationNode(vocationNode, vocSpellMap, vocStringVec, error)) - std::cout << "[Warning - Spell::configureSpell] " << error << std::endl; + std::vector strVector = explodeString(strValue, ";"), tmpVector; + for(std::vector::iterator it = strVector.begin(); it != strVector.end(); ++it) + { + tmpVector = explodeString((*it), ","); + uint32_t id = atoi(tmpVector[0].c_str()), exhaust = isAggressive ? 2000 : 1000; + if(tmpVector.size() > 1) + exhaust = atoi(tmpVector[1].c_str()); + + if(!id) + { + strValue = asLowerCaseString(tmpVector[0]); + if(strValue == "attack" || strValue == "attacking") + id = SPELLGROUP_ATTACK; + else if(strValue == "heal" || strValue == "healing") + id = SPELLGROUP_HEALING; + else if(strValue == "support" || strValue == "supporting") + id = SPELLGROUP_SUPPORT; + else if(strValue == "special" || strValue == "ultimate") + id = SPELLGROUP_SPECIAL; + } + + if(id && exhaust) + groupExhaustions[(SpellGroup_t)id] = exhaust; + } + } - vocationNode = vocationNode->next; + if(groupExhaustions.empty()) + { + if(isAggressive) + groupExhaustions[SPELLGROUP_ATTACK] = 2000; + else + groupExhaustions[SPELLGROUP_HEALING] = 1000; + } + + std::string error; + for(xmlNodePtr vocationNode = p->children; vocationNode; vocationNode = vocationNode->next) + { + if(!parseVocationNode(vocationNode, vocSpellMap, vocStringVec, error)) + std::clog << "[Warning - Spell::configureSpell] Spell: " << name << ", " << error << std::endl; } return true; } -bool Spell::playerSpellCheck(Player* player) const +bool Spell::checkSpell(Player* player) const { if(player->hasFlag(PlayerFlag_CannotUseSpells)) return false; @@ -582,7 +653,6 @@ bool Spell::playerSpellCheck(Player* player) const if(!isEnabled()) return false; - bool exhausted = false; if(isAggressive) { if(!player->hasFlag(PlayerFlag_IgnoreProtectionZone) && player->getZone() == ZONE_PROTECTION) @@ -590,20 +660,38 @@ bool Spell::playerSpellCheck(Player* player) const player->sendCancelMessage(RET_ACTIONNOTPERMITTEDINPROTECTIONZONE); return false; } - - if(player->hasCondition(CONDITION_EXHAUST, EXHAUST_COMBAT)) - exhausted = true; } - else if(player->hasCondition(CONDITION_EXHAUST, EXHAUST_HEALING)) - exhausted = true; - if(exhausted && !player->hasFlag(PlayerFlag_HasNoExhaustion)) + if(!player->hasFlag(PlayerFlag_HasNoExhaustion)) { - player->sendCancelMessage(RET_YOUAREEXHAUSTED); - if(isInstant()) - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + bool exhausted = false; + if(g_config.getBool(ConfigManager::ENABLE_COOLDOWNS)) + { + if(!player->hasCondition(CONDITION_SPELLCOOLDOWN, spellId)) + { + for(SpellGroup::const_iterator it = groupExhaustions.begin(); it != groupExhaustions.end(); ++it) + { + if(!player->hasCondition(CONDITION_EXHAUST, (Exhaust_t)((int32_t)it->first + 1))) + continue; - return false; + exhausted = true; + break; + } + } + else + exhausted = true; + } + else if(player->hasCondition(CONDITION_EXHAUST, (isAggressive ? EXHAUST_SPELLGROUP_ATTACK : EXHAUST_SPELLGROUP_HEALING))) + exhausted = true; + + if(exhausted) + { + player->sendCancelMessage(RET_YOUAREEXHAUSTED); + if(isInstant()) + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + + return false; + } } if(isPremium() && !player->isPremium()) @@ -627,6 +715,16 @@ bool Spell::playerSpellCheck(Player* player) const return false; } + for(int16_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) + { + if((int32_t)player->getSkill((skills_t)i, SKILL_LEVEL) < skills[i]) + { + player->sendCancelMessage(RET_NOTENOUGHSKILL); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; + } + } + if(player->getMana() < getManaCost(player) && !player->hasFlag(PlayerFlag_HasInfiniteMana)) { player->sendCancelMessage(RET_NOTENOUGHMANA); @@ -680,9 +778,9 @@ bool Spell::playerSpellCheck(Player* player) const return true; } -bool Spell::playerInstantSpellCheck(Player* player, Creature* creature) +bool Spell::checkInstantSpell(Player* player, Creature* creature) { - if(!playerSpellCheck(player)) + if(!checkSpell(player)) return false; const Position& toPos = creature->getPosition(); @@ -709,7 +807,7 @@ bool Spell::playerInstantSpellCheck(Player* player, Creature* creature) } ReturnValue ret; - if((ret = Combat::canDoCombat(player, tile, isAggressive)) != RET_NOERROR) + if((ret = Combat::canDoCombat(player, tile, isAggressive, false)) != RET_NOERROR) { player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); @@ -749,7 +847,7 @@ bool Spell::playerInstantSpellCheck(Player* player, Creature* creature) Player* targetPlayer = creature->getPlayer(); if(!isAggressive || !targetPlayer || Combat::isInPvpZone(player, targetPlayer) - || player->getSkullClient(targetPlayer) != SKULL_NONE) + || player->getSkullType(targetPlayer) != SKULL_NONE) return true; if(player->getSecureMode() == SECUREMODE_ON) @@ -769,9 +867,9 @@ bool Spell::playerInstantSpellCheck(Player* player, Creature* creature) return true; } -bool Spell::playerInstantSpellCheck(Player* player, const Position& toPos) +bool Spell::checkInstantSpell(Player* player, const Position& toPos) { - if(!playerSpellCheck(player)) + if(!checkSpell(player)) return false; if(toPos.x == 0xFFFF) @@ -800,7 +898,7 @@ bool Spell::playerInstantSpellCheck(Player* player, const Position& toPos) } ReturnValue ret; - if((ret = Combat::canDoCombat(player, tile, isAggressive)) != RET_NOERROR) + if((ret = Combat::canDoCombat(player, tile, isAggressive, false)) != RET_NOERROR) { player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); @@ -821,7 +919,7 @@ bool Spell::playerInstantSpellCheck(Player* player, const Position& toPos) return false; } - if(player->getSkull() == SKULL_BLACK && isAggressive && range == -1) //-1 is (usually?) an area spell + if(player->getSkull() == SKULL_BLACK && isAggressive && range == -1) // CHECKME: -1 is (usually?) an area spell { player->sendCancelMessage(RET_YOUMAYNOTCASTAREAONBLACKSKULL); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); @@ -831,9 +929,9 @@ bool Spell::playerInstantSpellCheck(Player* player, const Position& toPos) return true; } -bool Spell::playerRuneSpellCheck(Player* player, const Position& toPos) +bool Spell::checkRuneSpell(Player* player, const Position& toPos) { - if(!playerSpellCheck(player)) + if(!checkSpell(player)) return false; if(toPos.x == 0xFFFF) @@ -870,7 +968,7 @@ bool Spell::playerRuneSpellCheck(Player* player, const Position& toPos) } ReturnValue ret; - if((ret = Combat::canDoCombat(player, tile, isAggressive)) != RET_NOERROR) + if((ret = Combat::canDoCombat(player, tile, isAggressive, false)) != RET_NOERROR) { player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); @@ -911,7 +1009,7 @@ bool Spell::playerRuneSpellCheck(Player* player, const Position& toPos) Player* targetPlayer = targetCreature->getPlayer(); if(!isAggressive || !targetPlayer || Combat::isInPvpZone(player, targetPlayer) - || player->getSkullClient(targetPlayer) != SKULL_NONE) + || player->getSkullType(targetPlayer) != SKULL_NONE) return true; if(player->getSecureMode() == SECUREMODE_ON) @@ -921,37 +1019,52 @@ bool Spell::playerRuneSpellCheck(Player* player, const Position& toPos) return false; } - if(player->getSkull() == SKULL_BLACK) - { - player->sendCancelMessage(RET_YOUMAYNOTATTACKTHISPLAYER); - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); - return false; - } + if(player->getSkull() != SKULL_BLACK) + return true; - return true; + player->sendCancelMessage(RET_YOUMAYNOTATTACKTHISPLAYER); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; } -void Spell::postCastSpell(Player* player, bool finishedCast /*= true*/, bool payCost /*= true*/) const +void Spell::postSpell(Player* player) const { - if(finishedCast) + if(!player->hasFlag(PlayerFlag_HasNoExhaustion)) { - if(!player->hasFlag(PlayerFlag_HasNoExhaustion) && exhaustion > 0) - player->addExhaust(exhaustion, isAggressive ? EXHAUST_COMBAT : EXHAUST_HEALING); + if(g_config.getBool(ConfigManager::ENABLE_COOLDOWNS)) + { + for(SpellGroup::const_iterator it = groupExhaustions.begin(); it != groupExhaustions.end(); ++it) + { + player->addExhaust(it->second, (Exhaust_t)(it->first + 1)); + player->sendSpellGroupCooldown(it->first, it->second); + } - if(isAggressive && !player->hasFlag(PlayerFlag_NotGainInFight)) - player->addInFightTicks(); + if(exhaustion > 0) + { + player->addCooldown(exhaustion, spellId); + if(icon != SPELL_NONE) + player->sendSpellCooldown(icon, exhaustion); + } + } + else if(exhaustion > 0) + { + player->addExhaust(exhaustion, (isAggressive ? EXHAUST_SPELLGROUP_ATTACK : EXHAUST_SPELLGROUP_HEALING)); + player->sendSpellGroupCooldown(isAggressive ? SPELLGROUP_ATTACK : SPELLGROUP_HEALING, exhaustion); + } } - if(payCost) - postCastSpell(player, (uint32_t)getManaCost(player), (uint32_t)getSoulCost()); + if(isAggressive && !player->hasFlag(PlayerFlag_NotGainInFight)) + player->addInFightTicks(false); + + postSpell(player, (uint32_t)getManaCost(player), (uint32_t)getSoulCost()); } -void Spell::postCastSpell(Player* player, uint32_t manaCost, uint32_t soulCost) const +void Spell::postSpell(Player* player, uint32_t manaCost, uint32_t soulCost) const { if(manaCost > 0) { player->changeMana(-(int32_t)manaCost); - if(!player->hasFlag(PlayerFlag_NotGainMana) && (player->getZone() != ZONE_PVP + if(!player->hasFlag(PlayerFlag_NotGainMana) && (player->getZone() != ZONE_HARDCORE || g_config.getBool(ConfigManager::PVPZONE_ADDMANASPENT))) player->addManaSpent(manaCost); } @@ -963,12 +1076,12 @@ void Spell::postCastSpell(Player* player, uint32_t manaCost, uint32_t soulCost) int32_t Spell::getManaCost(const Player* player) const { if(player && manaPercent) - return (int32_t)std::floor(double(player->getMaxMana() * manaPercent) / 100); + return (int32_t)std::floor((double)(player->getMaxMana() * manaPercent) / 100.); return mana; } -ReturnValue Spell::CreateIllusion(Creature* creature, const Outfit_t outfit, int32_t time) +ReturnValue Spell::CreateIllusion(Creature* creature, const Outfit_t& outfit, int32_t time) { ConditionOutfit* outfitCondition = new ConditionOutfit(CONDITIONID_COMBAT, CONDITION_OUTFIT, time, false, 0); if(!outfitCondition) @@ -1007,7 +1120,7 @@ ReturnValue Spell::CreateIllusion(Creature* creature, uint32_t itemId, int32_t t return CreateIllusion(creature, outfit, time); } -InstantSpell::InstantSpell(LuaScriptInterface* _interface) : TalkAction(_interface) +InstantSpell::InstantSpell(LuaInterface* _interface) : TalkAction(_interface) { needDirection = false; hasParam = false; @@ -1067,7 +1180,7 @@ bool InstantSpell::loadFunction(const std::string& functionName) } else { - std::cout << "[Warning - InstantSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; + std::clog << "[Warning - InstantSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; return false; } @@ -1075,61 +1188,52 @@ bool InstantSpell::loadFunction(const std::string& functionName) return true; } -bool InstantSpell::playerCastInstant(Player* player, const std::string& param) +bool InstantSpell::castInstant(Player* player, const std::string& param) { LuaVariant var; if(selfTarget) { var.type = VARIANT_NUMBER; var.number = player->getID(); - if(!playerInstantSpellCheck(player, player)) + if(!checkInstantSpell(player, player)) return false; } else if(needTarget || casterTargetOrDirection) { Creature* target = NULL; - bool useDirection = false; if(hasParam) { Player* targetPlayer = NULL; ReturnValue ret = g_game.getPlayerByNameWildcard(param, targetPlayer); target = targetPlayer; - if(limitRange && target && !Position::areInRange(Position(limitRange, limitRange, 0), target->getPosition(), player->getPosition())) - useDirection = true; + if(limitRange && target && !Position::areInRange(Position(limitRange, + limitRange, 0), target->getPosition(), player->getPosition())) + target = NULL; - if((!target || target->getHealth() <= 0) && !useDirection) + if((!target || target->getHealth() <= 0) && !casterTargetOrDirection) { - if(!casterTargetOrDirection) - { - player->sendCancelMessage(ret); - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); - return false; - } - - useDirection = true; + player->sendCancelMessage(ret); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; } } else { target = player->getAttackedCreature(); - if(limitRange && target && !Position::areInRange(Position(limitRange, limitRange, 0), target->getPosition(), player->getPosition())) - useDirection = true; + if(limitRange && target && !Position::areInRange(Position(limitRange, + limitRange, 0), target->getPosition(), player->getPosition())) + target = NULL; - if((!target || target->getHealth() <= 0) && !useDirection) + if((!target || target->getHealth() <= 0) && !casterTargetOrDirection) { - if(!casterTargetOrDirection) - { - player->sendCancelMessage(RET_YOUCANONLYUSEITONCREATURES); - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); - return false; - } - - useDirection = true; + player->sendCancelMessage(RET_YOUCANONLYUSEITONCREATURES); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; } } - if(!useDirection) + if(target) { bool canSee = player->canSeeCreature(target); if(!canSee || !canThrowSpell(player, target)) @@ -1141,14 +1245,14 @@ bool InstantSpell::playerCastInstant(Player* player, const std::string& param) var.type = VARIANT_NUMBER; var.number = target->getID(); - if(!playerInstantSpellCheck(player, target)) + if(!checkInstantSpell(player, target)) return false; } else { var.type = VARIANT_POSITION; var.pos = Spells::getCasterPosition(player, player->getDirection()); - if(!playerInstantSpellCheck(player, var.pos)) + if(!checkInstantSpell(player, var.pos)) return false; } } @@ -1156,7 +1260,7 @@ bool InstantSpell::playerCastInstant(Player* player, const std::string& param) { var.type = VARIANT_STRING; var.text = param; - if(!playerSpellCheck(player)) + if(!checkSpell(player)) return false; } else @@ -1167,14 +1271,14 @@ bool InstantSpell::playerCastInstant(Player* player, const std::string& param) else var.pos = player->getPosition(); - if(!playerInstantSpellCheck(player, var.pos)) + if(!checkInstantSpell(player, var.pos)) return false; } if(!internalCastSpell(player, var)) return false; - Spell::postCastSpell(player); + Spell::postSpell(player); return true; } @@ -1241,10 +1345,7 @@ bool InstantSpell::internalCastSpell(Creature* creature, const LuaVariant& var) if(isScripted()) return executeCastSpell(creature, var); - if(function) - return function(this, creature, var.text); - - return false; + return function ? function(this, creature, var.text) : false; } bool InstantSpell::executeCastSpell(Creature* creature, const LuaVariant& var) @@ -1261,7 +1362,7 @@ bool InstantSpell::executeCastSpell(Creature* creature, const LuaVariant& var) scriptstream << "local cid = " << env->addThing(creature) << std::endl; env->streamVariant(scriptstream, "var", var); - scriptstream << m_scriptData; + scriptstream << *m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1277,7 +1378,7 @@ bool InstantSpell::executeCastSpell(Creature* creature, const LuaVariant& var) #ifdef __DEBUG_LUASCRIPTS__ char desc[60]; sprintf(desc, "onCastSpell - %s", creature->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -1295,12 +1396,12 @@ bool InstantSpell::executeCastSpell(Creature* creature, const LuaVariant& var) } else { - std::cout << "[Error - InstantSpell::executeCastSpell] Call stack overflow." << std::endl; + std::clog << "[Error - InstantSpell::executeCastSpell] Call stack overflow." << std::endl; return false; } } -bool InstantSpell::SearchPlayer(const InstantSpell* spell, Creature* creature, const std::string& param) +bool InstantSpell::SearchPlayer(const InstantSpell*, Creature* creature, const std::string& param) { Player* player = creature->getPlayer(); if(!player || player->isRemoved()) @@ -1325,6 +1426,7 @@ bool InstantSpell::SearchPlayer(const InstantSpell* spell, Creature* creature, c std::stringstream ss; ss << targetPlayer->getName() << " " << g_game.getSearchString(player->getPosition(), targetPlayer->getPosition(), true, true) << "."; player->sendTextMessage(MSG_INFO_DESCR, ss.str().c_str()); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_BLUE); return true; } @@ -1378,67 +1480,58 @@ bool InstantSpell::SummonMonster(const InstantSpell* spell, Creature* creature, ReturnValue ret = g_game.placeSummon(creature, param); if(ret == RET_NOERROR) { - spell->postCastSpell(player, (uint32_t)manaCost, (uint32_t)spell->getSoulCost()); + spell->postSpell(player, (uint32_t)manaCost, (uint32_t)spell->getSoulCost()); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_BLUE); - } - else - { - player->sendCancelMessage(ret); - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return true; } - return (ret == RET_NOERROR); + player->sendCancelMessage(ret); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; } -bool InstantSpell::Levitate(const InstantSpell* spell, Creature* creature, const std::string& param) +bool InstantSpell::Levitate(const InstantSpell*, Creature* creature, const std::string& param) { Player* player = creature->getPlayer(); if(!player) return false; - const Position& currentPos = creature->getPosition(); - Position destPos = Spells::getCasterPosition(creature, creature->getDirection()); - + uint16_t floor = 7; ReturnValue ret = RET_NOERROR; - std::string tmpParam = asLowerCaseString(param); + const Position& position = creature->getPosition(); + Position destination = Spells::getCasterPosition(creature, creature->getDirection()); - uint16_t blockedFloor = 7; - bool up = false; + std::string tmpParam = asLowerCaseString(param); if(tmpParam == "up") - { - up = true; - blockedFloor = 8; - } + floor = 8; else if(tmpParam != "down") ret = RET_NOTPOSSIBLE; if(ret == RET_NOERROR) { ret = RET_NOTPOSSIBLE; - if(currentPos.z != blockedFloor) + if(position.z != floor) { Tile* tmpTile = NULL; - if(up) + if(floor != 7) { - tmpTile = g_game.getTile(currentPos.x, currentPos.y, currentPos.z - 1); - destPos.z--; + tmpTile = g_game.getTile(position.x, position.y, position.z - 1); + destination.z--; } else { - tmpTile = g_game.getTile(destPos); - destPos.z++; + tmpTile = g_game.getTile(destination); + destination.z++; } - if(!tmpTile || (tmpTile->ground == NULL && !tmpTile->hasProperty(IMMOVABLEBLOCKSOLID))) + if(!tmpTile || (!tmpTile->ground && !tmpTile->hasProperty(IMMOVABLEBLOCKSOLID))) { Tile* tile = player->getTile(); - tmpTile = g_game.getTile(destPos.x, destPos.y, destPos.z); - if(tile && tmpTile && tmpTile->ground - && !tmpTile->hasProperty(IMMOVABLEBLOCKSOLID) && !tmpTile->floorChange() - && tile->hasFlag(TILESTATE_HOUSE) == tmpTile->hasFlag(TILESTATE_HOUSE) - && tile->hasFlag(TILESTATE_PROTECTIONZONE) == tmpTile->hasFlag(TILESTATE_PROTECTIONZONE) - ) - ret = g_game.internalMoveCreature(NULL, player, tile, tmpTile, FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE); + tmpTile = g_game.getTile(destination); + if(tile && tmpTile && tmpTile->ground && !tmpTile->hasProperty(IMMOVABLEBLOCKSOLID) && + !tmpTile->floorChange() && tile->hasFlag(TILESTATE_HOUSE) == tmpTile->hasFlag(TILESTATE_HOUSE) + && tile->hasFlag(TILESTATE_PROTECTIONZONE) == tmpTile->hasFlag(TILESTATE_PROTECTIONZONE)) + ret = g_game.internalMoveCreature(NULL, player, tile, tmpTile, FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE); } } } @@ -1454,22 +1547,21 @@ bool InstantSpell::Levitate(const InstantSpell* spell, Creature* creature, const return false; } -bool InstantSpell::Illusion(const InstantSpell* spell, Creature* creature, const std::string& param) +bool InstantSpell::Illusion(const InstantSpell*, Creature* creature, const std::string& param) { Player* player = creature->getPlayer(); if(!player) return false; ReturnValue ret = CreateIllusion(creature, param, 60000); - if(ret == RET_NOERROR) - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); - else { - player->sendCancelMessage(ret); - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); + return true; } + player->sendCancelMessage(ret); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return (ret == RET_NOERROR); } @@ -1485,8 +1577,7 @@ bool InstantSpell::canCast(const Player* player) const return player->hasLearnedInstantSpell(getName()); } - -ConjureSpell::ConjureSpell(LuaScriptInterface* _interface): +ConjureSpell::ConjureSpell(LuaInterface* _interface): InstantSpell(_interface) { isAggressive = false; @@ -1525,11 +1616,9 @@ bool ConjureSpell::loadFunction(const std::string& functionName) std::string tmpFunctionName = asLowerCaseString(functionName); if(tmpFunctionName == "conjureitem" || tmpFunctionName == "conjurerune") function = ConjureItem; - else if(tmpFunctionName == "conjurefood") - function = ConjureFood; else { - std::cout << "[Warning - ConjureSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; + std::clog << "[Warning - ConjureSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; return false; } @@ -1538,7 +1627,7 @@ bool ConjureSpell::loadFunction(const std::string& functionName) } ReturnValue ConjureSpell::internalConjureItem(Player* player, uint32_t conjureId, uint32_t conjureCount, - bool transform/* = false*/, uint32_t reagentId/* = 0*/, slots_t slot/* = SLOT_WHEREVER*/, bool test/* = false*/) + bool transform/* = false*/, uint32_t reagentId/* = 0*/) { if(!transform) { @@ -1550,147 +1639,113 @@ ReturnValue ConjureSpell::internalConjureItem(Player* player, uint32_t conjureId if(ret != RET_NOERROR) delete newItem; + g_game.startDecay(newItem); return ret; } if(!reagentId) return RET_NOTPOSSIBLE; - Item* item = player->getInventoryItem(slot); - if(item && item->getID() == reagentId) + std::list containers; + Item *item = NULL, *fromItem = NULL; + for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i) { - if(item->isStackable() && item->getItemCount() != 1) - return RET_YOUNEEDTOSPLITYOURSPEARS; + if(!(item = player->getInventoryItem((slots_t)i))) + continue; - if(test) - return RET_NOERROR; + if(!fromItem && item->getID() == reagentId) + fromItem = item; + else if(Container* container = item->getContainer()) + containers.push_back(container); + } - Item* newItem = g_game.transformItem(item, conjureId, conjureCount); - if(!newItem) - return RET_NOTPOSSIBLE; + if(!fromItem) + { + for(std::list::iterator cit = containers.begin(); cit != containers.end(); ++cit) + { + for(ItemList::const_reverse_iterator it = (*cit)->getReversedItems(); it != (*cit)->getReversedEnd(); ++it) + { + if((*it)->getID() == reagentId) + { + fromItem = (*it); + break; + } - g_game.startDecay(newItem); - return RET_NOERROR; + if(Container* tmp = (*it)->getContainer()) + containers.push_back(tmp); + } + } + } + + if(!fromItem) + return RET_YOUNEEDAMAGICITEMTOCASTSPELL; + + if((fromItem->isStackable() || fromItem->hasCharges()) && fromItem->getSubType() > 1) + { + item = Item::CreateItem(conjureId, conjureCount); + ReturnValue ret = g_game.internalPlayerAddItem(NULL, player, item, false); + if(ret != RET_NOERROR) + return ret; + + g_game.transformItem(fromItem, reagentId, (int32_t)(fromItem->getItemCount() - 1)); } + else + g_game.transformItem(fromItem, conjureId, conjureCount); - return RET_YOUNEEDAMAGICITEMTOCASTSPELL; + g_game.startDecay(item); + return RET_NOERROR; } -bool ConjureSpell::ConjureItem(const ConjureSpell* spell, Creature* creature, const std::string& param) +bool ConjureSpell::ConjureItem(const ConjureSpell* spell, Creature* creature, const std::string&) { Player* player = creature->getPlayer(); if(!player) return false; - if(!player->hasFlag(PlayerFlag_IgnoreSpellCheck) && player->getZone() == ZONE_PVP) + if(!player->hasFlag(PlayerFlag_IgnoreSpellCheck) && player->getZone() == ZONE_HARDCORE) { player->sendCancelMessage(RET_CANNOTCONJUREITEMHERE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } - ReturnValue result = RET_NOERROR; + ReturnValue result = RET_NOTPOSSIBLE; if(spell->getReagentId() != 0) { - ReturnValue resLeft = internalConjureItem(player, spell->getConjureId(), spell->getConjureCount(), - true, spell->getReagentId(), SLOT_LEFT, true); - if(resLeft == RET_NOERROR) - { - resLeft = internalConjureItem(player, spell->getConjureId(), spell->getConjureCount(), - true, spell->getReagentId(), SLOT_LEFT); - if(resLeft == RET_NOERROR) - spell->postCastSpell(player, false); - } - - ReturnValue resRight = internalConjureItem(player, spell->getConjureId(), spell->getConjureCount(), - true, spell->getReagentId(), SLOT_RIGHT, true); - if(resRight == RET_NOERROR) - { - if(resLeft == RET_NOERROR && !spell->playerSpellCheck(player)) - return false; - - resRight = internalConjureItem(player, spell->getConjureId(), spell->getConjureCount(), - true, spell->getReagentId(), SLOT_RIGHT); - if(resRight == RET_NOERROR) - spell->postCastSpell(player, false); - } - - if(resLeft == RET_NOERROR || resRight == RET_NOERROR) + if((result = internalConjureItem(player, spell->getConjureId(), spell->getConjureCount(), true, spell->getReagentId())) == RET_NOERROR) { - spell->postCastSpell(player, true, false); + spell->postSpell(player); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); return true; } - - result = resLeft; - if((result == RET_NOERROR && resRight != RET_NOERROR) || - (result == RET_YOUNEEDAMAGICITEMTOCASTSPELL && resRight == RET_YOUNEEDTOSPLITYOURSPEARS)) - result = resRight; } - else if(internalConjureItem(player, spell->getConjureId(), spell->getConjureCount()) == RET_NOERROR) + else if((result = internalConjureItem(player, spell->getConjureId(), spell->getConjureCount())) == RET_NOERROR) { - spell->postCastSpell(player); + spell->postSpell(player); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); return true; } - if(result != RET_NOERROR) - player->sendCancelMessage(result); - + player->sendCancelMessage(result); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } -bool ConjureSpell::ConjureFood(const ConjureSpell* spell, Creature* creature, const std::string& param) +bool ConjureSpell::castInstant(Player* player, const std::string& param) { - Player* player = creature->getPlayer(); - if(!player) + if(!checkSpell(player)) return false; - static uint32_t foodType[] = - { - ITEM_MEAT, - ITEM_HAM, - ITEM_GRAPE, - ITEM_APPLE, - ITEM_BREAD, - ITEM_CHEESE, - ITEM_ROLL - }; - - if(internalConjureItem(player, foodType[random_range(0, (sizeof(foodType) / sizeof(uint32_t)) - 1)], 1) == RET_NOERROR) - { - if(random_range(0, 100) > 50) - internalConjureItem(player, foodType[random_range(0, (sizeof(foodType) / sizeof(uint32_t)) - 1)], 1); - - spell->postCastSpell(player); - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_GREEN); - return true; - } - - return false; -} - -bool ConjureSpell::playerCastInstant(Player* player, const std::string& param) -{ - if(!playerSpellCheck(player)) - return false; + if(!isScripted()) + return function ? function(this, player, param) : false; - if(isScripted()) - { - LuaVariant var; - var.type = VARIANT_STRING; - var.text = param; - return executeCastSpell(player, var); - } - - if(function) - return function(this, player, param); - - return false; + LuaVariant var; + var.type = VARIANT_STRING; + var.text = param; + return executeCastSpell(player, var); } -RuneSpell::RuneSpell(LuaScriptInterface* _interface): +RuneSpell::RuneSpell(LuaInterface* _interface): Action(_interface) { runeId = 0; @@ -1711,7 +1766,7 @@ bool RuneSpell::configureEvent(xmlNodePtr p) runeId = intValue; else { - std::cout << "Error: [RuneSpell::configureSpell] Rune spell without id." << std::endl; + std::clog << "Error: [RuneSpell::configureSpell] Rune spell without id." << std::endl; return false; } @@ -1737,9 +1792,11 @@ bool RuneSpell::loadFunction(const std::string& functionName) function = Illusion; else if(tmpFunctionName == "convince") function = Convince; + else if(tmpFunctionName == "soulfire") + function = Soulfire; else { - std::cout << "[Warning - RuneSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; + std::clog << "[Warning - RuneSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; return false; } @@ -1747,7 +1804,7 @@ bool RuneSpell::loadFunction(const std::string& functionName) return true; } -bool RuneSpell::Illusion(const RuneSpell* spell, Creature* creature, Item* item, const Position& posFrom, const Position& posTo) +bool RuneSpell::Illusion(const RuneSpell*, Creature* creature, Item*, const Position&, const Position& posTo) { Player* player = creature->getPlayer(); if(!player) @@ -1762,7 +1819,7 @@ bool RuneSpell::Illusion(const RuneSpell* spell, Creature* creature, Item* item, } Item* illusionItem = thing->getItem(); - if(!illusionItem || illusionItem->isNotMoveable()) + if(!illusionItem || !illusionItem->isMovable()) { player->sendCancelMessage(RET_NOTPOSSIBLE); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); @@ -1770,19 +1827,18 @@ bool RuneSpell::Illusion(const RuneSpell* spell, Creature* creature, Item* item, } ReturnValue ret = CreateIllusion(creature, illusionItem->getID(), 60000); - if(ret == RET_NOERROR) - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); - else { - player->sendCancelMessage(ret); - g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); + return true; } - return (ret == RET_NOERROR); + player->sendCancelMessage(ret); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; } -bool RuneSpell::Convince(const RuneSpell* spell, Creature* creature, Item* item, const Position& posFrom, const Position& posTo) +bool RuneSpell::Convince(const RuneSpell* spell, Creature* creature, Item*, const Position&, const Position& posTo) { Player* player = creature->getPlayer(); if(!player) @@ -1821,6 +1877,13 @@ bool RuneSpell::Convince(const RuneSpell* spell, Creature* creature, Item* item, return false; } + if(!player->hasFlag(PlayerFlag_CanConvinceAll) && convinceCreature->getPlayerMaster()) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; + } + int32_t manaCost = 0; if(Monster* monster = convinceCreature->getMonster()) manaCost = (int32_t)(monster->getManaCost() * g_config.getDouble(ConfigManager::RATE_MONSTER_MANA)); @@ -1839,11 +1902,50 @@ bool RuneSpell::Convince(const RuneSpell* spell, Creature* creature, Item* item, return false; } - spell->postCastSpell(player, (uint32_t)manaCost, (uint32_t)spell->getSoulCost()); + spell->postSpell(player, (uint32_t)manaCost, (uint32_t)spell->getSoulCost()); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_WRAPS_RED); return true; } +bool RuneSpell::Soulfire(const RuneSpell* spell, Creature* creature, Item*, const Position&, const Position& posTo) +{ + Player* player = creature->getPlayer(); + if(!player) + return false; + + Thing* thing = g_game.internalGetThing(player, posTo, 0, 0, STACKPOS_LOOK); + if(!thing) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; + } + + Creature* target = thing->getCreature(); + if(!target) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; + } + + ConditionDamage* soulfireCondition = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_FIRE, false, 0); + soulfireCondition->setParam(CONDITIONPARAM_SUBID, 1); + soulfireCondition->setParam(CONDITIONPARAM_OWNER, player->getID()); + + soulfireCondition->addDamage(std::ceil((player->getLevel() + player->getMagicLevel()) / 3.), 9000, -10); + if(!target->addCondition(soulfireCondition)) + { + player->sendCancelMessage(RET_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; + } + + g_game.addDistanceEffect(player->getPosition(), posTo, SHOOT_EFFECT_FIRE); + spell->postSpell(player, true, false); + return true; +} + ReturnValue RuneSpell::canExecuteAction(const Player* player, const Position& toPos) { if(player->hasFlag(PlayerFlag_CannotUseSpells)) @@ -1866,9 +1968,9 @@ ReturnValue RuneSpell::canExecuteAction(const Player* player, const Position& to } bool RuneSpell::executeUse(Player* player, Item* item, const PositionEx& posFrom, - const PositionEx& posTo, bool extendedUse, uint32_t creatureId) + const PositionEx& posTo, bool, uint32_t creatureId) { - if(!playerRuneSpellCheck(player, posTo)) + if(!checkRuneSpell(player, posTo)) return false; bool result = false; @@ -1893,9 +1995,9 @@ bool RuneSpell::executeUse(Player* player, Item* item, const PositionEx& posFrom if(result) { - Spell::postCastSpell(player); + Spell::postSpell(player); if(hasCharges && item && g_config.getBool(ConfigManager::REMOVE_RUNE_CHARGES)) - g_game.transformItem(item, item->getID(), std::max((int32_t)0, ((int32_t)item->getCharges()) - 1)); + g_game.transformItem(item, item->getID(), std::max((int32_t)0, ((int32_t)item->getItemCount()) - 1)); } return result; @@ -1925,10 +2027,7 @@ bool RuneSpell::castSpell(Creature* creature, Creature* target) bool RuneSpell::internalCastSpell(Creature* creature, const LuaVariant& var) { - if(isScripted()) - return executeCastSpell(creature, var); - - return false; + return isScripted() ? executeCastSpell(creature, var) : false; } bool RuneSpell::executeCastSpell(Creature* creature, const LuaVariant& var) @@ -1945,7 +2044,7 @@ bool RuneSpell::executeCastSpell(Creature* creature, const LuaVariant& var) scriptstream << "local cid = " << env->addThing(creature) << std::endl; env->streamVariant(scriptstream, "var", var); - scriptstream << m_scriptData; + scriptstream << *m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -1961,7 +2060,7 @@ bool RuneSpell::executeCastSpell(Creature* creature, const LuaVariant& var) #ifdef __DEBUG_LUASCRIPTS__ char desc[60]; sprintf(desc, "onCastSpell - %s", creature->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -1979,7 +2078,7 @@ bool RuneSpell::executeCastSpell(Creature* creature, const LuaVariant& var) } else { - std::cout << "[Error - RuneSpell::executeCastSpell] Call stack overflow." << std::endl; + std::clog << "[Error - RuneSpell::executeCastSpell] Call stack overflow." << std::endl; return false; } } diff --git a/spells.h b/spells.h index 039f511..ae6fa4e 100644 --- a/spells.h +++ b/spells.h @@ -34,6 +34,7 @@ class Spell; typedef std::map RunesMap; typedef std::map InstantsMap; +typedef std::map SpellGroup; class Spells : public BaseEvents { @@ -46,7 +47,7 @@ class Spells : public BaseEvents RuneSpell* getRuneSpell(uint32_t id); RuneSpell* getRuneSpellByName(const std::string& name); - InstantSpell* getInstantSpell(const std::string words); + InstantSpell* getInstantSpell(const std::string& words); InstantSpell* getInstantSpellByName(const std::string& name); InstantSpell* getInstantSpellByIndex(const Player* player, uint32_t index); @@ -61,12 +62,13 @@ class Spells : public BaseEvents virtual Event* getEvent(const std::string& nodeName); virtual bool registerEvent(Event* event, xmlNodePtr p, bool override); - virtual LuaScriptInterface& getInterface() {return m_interface;} - LuaScriptInterface m_interface; + virtual LuaInterface& getInterface() {return m_interface;} + LuaInterface m_interface; RunesMap runes; InstantsMap instants; + uint32_t spellId; friend class CombatSpell; }; @@ -92,7 +94,7 @@ class CombatSpell : public Event, public BaseSpell virtual bool castSpell(Creature* creature); virtual bool castSpell(Creature* creature, Creature* target); - virtual bool configureEvent(xmlNodePtr p) {return true;} + virtual bool configureEvent(xmlNodePtr) {return true;} //scripting bool executeCastSpell(Creature* creature, const LuaVariant& var); @@ -118,8 +120,8 @@ class Spell : public BaseSpell bool configureSpell(xmlNodePtr xmlspell); const std::string& getName() const {return name;} - void postCastSpell(Player* player, bool isFinished = true, bool payCost = true) const; - void postCastSpell(Player* player, uint32_t manaCost, uint32_t soulCost) const; + void postSpell(Player* player) const; + void postSpell(Player* player, uint32_t manaCost, uint32_t soulCost) const; int32_t getManaCost(const Player* player) const; int32_t getSoulCost() const {return soul;} @@ -128,24 +130,35 @@ class Spell : public BaseSpell int32_t getMana() const {return mana;} int32_t getManaPercent() const {return manaPercent;} uint32_t getExhaustion() const {return exhaustion;} - const bool isEnabled() const {return enabled;} - const bool isPremium() const {return premium;} + Spells_t getIcon() const {return icon;} + SpellGroup getGroupExhaustions() const {return groupExhaustions;} + uint16_t getId() const {return spellId;} + void setId(uint16_t id) {spellId = id;} + + bool isEnabled() const {return enabled;} + bool isPremium() const {return premium;} virtual bool isInstant() const = 0; bool isLearnable() const {return learnable;} - static ReturnValue CreateIllusion(Creature* creature, const Outfit_t outfit, int32_t time); + static ReturnValue CreateIllusion(Creature* creature, const Outfit_t& outfit, int32_t time); static ReturnValue CreateIllusion(Creature* creature, const std::string& name, int32_t time); static ReturnValue CreateIllusion(Creature* creature, uint32_t itemId, int32_t time); protected: - bool playerSpellCheck(Player* player) const; - bool playerInstantSpellCheck(Player* player, Creature* creature); - bool playerInstantSpellCheck(Player* player, const Position& toPos); - bool playerRuneSpellCheck(Player* player, const Position& toPos); + bool checkSpell(Player* player) const; + bool checkInstantSpell(Player* player, Creature* creature); + bool checkInstantSpell(Player* player, const Position& toPos); + bool checkRuneSpell(Player* player, const Position& toPos); + + private: + uint16_t spellId; + protected: int32_t level; int32_t magLevel; + int32_t skills[SKILL_LAST + 1]; + bool premium; bool learnable; bool enabled; @@ -163,24 +176,24 @@ class Spell : public BaseSpell bool selfTarget; bool isAggressive; - VocationMap vocSpellMap; - typedef std::vector VocStringVec; - VocStringVec vocStringVec; - - private: + Spells_t icon; + SpellGroup groupExhaustions; std::string name; + + VocationMap vocSpellMap; + StringVec vocStringVec; }; class InstantSpell : public TalkAction, public Spell { public: - InstantSpell(LuaScriptInterface* _interface); + InstantSpell(LuaInterface* _interface); virtual ~InstantSpell() {} virtual bool configureEvent(xmlNodePtr p); virtual bool loadFunction(const std::string& functionName); - virtual bool playerCastInstant(Player* player, const std::string& param); + virtual bool castInstant(Player* player, const std::string& param); virtual bool castSpell(Creature* creature); virtual bool castSpell(Creature* creature, Creature* target); @@ -216,16 +229,16 @@ class InstantSpell : public TalkAction, public Spell class ConjureSpell : public InstantSpell { public: - ConjureSpell(LuaScriptInterface* _interface); + ConjureSpell(LuaInterface* _interface); virtual ~ConjureSpell() {} virtual bool configureEvent(xmlNodePtr p); virtual bool loadFunction(const std::string& functionName); - virtual bool playerCastInstant(Player* player, const std::string& param); + virtual bool castInstant(Player* player, const std::string& param); - virtual bool castSpell(Creature* creature) {return false;} - virtual bool castSpell(Creature* creature, Creature* target) {return false;} + virtual bool castSpell(Creature*) {return false;} + virtual bool castSpell(Creature*, Creature*) {return false;} uint32_t getConjureId() const {return conjureId;} uint32_t getConjureCount() const {return conjureCount;} @@ -236,10 +249,9 @@ class ConjureSpell : public InstantSpell virtual std::string getScriptEventParams() const {return "cid, var";} static ReturnValue internalConjureItem(Player* player, uint32_t conjureId, uint32_t conjureCount, - bool transform = false, uint32_t reagentId = 0, slots_t slot = SLOT_WHEREEVER, bool test = false); + bool transform = false, uint32_t reagentId = 0); static ConjureSpellFunction ConjureItem; - static ConjureSpellFunction ConjureFood; bool internalCastSpell(Creature* creature, const LuaVariant& var); Position getCasterPosition(Creature* creature); @@ -254,7 +266,7 @@ class ConjureSpell : public InstantSpell class RuneSpell : public Action, public Spell { public: - RuneSpell(LuaScriptInterface* _interface); + RuneSpell(LuaInterface* _interface); virtual ~RuneSpell() {} virtual bool configureEvent(xmlNodePtr p); @@ -271,7 +283,6 @@ class RuneSpell : public Action, public Spell //scripting bool executeCastSpell(Creature* creature, const LuaVariant& var); - virtual bool isInstant() const {return false;} uint32_t getRuneItemId(){return runeId;} @@ -281,6 +292,7 @@ class RuneSpell : public Action, public Spell static RuneSpellFunction Illusion; static RuneSpellFunction Convince; + static RuneSpellFunction Soulfire; bool internalCastSpell(Creature* creature, const LuaVariant& var); diff --git a/status.cpp b/status.cpp index 504738f..7cb9228 100644 --- a/status.cpp +++ b/status.cpp @@ -15,12 +15,11 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" -#include "resources.h" - #include #include #include "status.h" +#include "const.h" #include "tools.h" #include "connection.h" @@ -30,11 +29,6 @@ #include "configmanager.h" #include "game.h" -#ifndef WINDOWS - #define SOCKET_ERROR -1 - #define INVALID_SOCKET -1 -#endif - extern ConfigManager g_config; extern Game g_game; @@ -45,28 +39,35 @@ IpConnectMap ProtocolStatus::ipConnectMap; void ProtocolStatus::onRecvFirstMessage(NetworkMessage& msg) { - for(StringVec::const_iterator it = g_game.blacklist.begin(); it != g_game.blacklist.end(); ++it) + uint32_t ip = getIP(); + if(ip != LOCALHOST) { - if((*it) == convertIPAddress(getIP())) + std::string _ip = convertIPAddress(ip); + if(!g_game.isInWhitelist(_ip)) { - getConnection()->close(); - return; + if(g_game.isInBlacklist(_ip)) + { + getConnection()->close(); + return; + } + + IpConnectMap::const_iterator it = ipConnectMap.find(ip); + if(it != ipConnectMap.end() && OTSYS_TIME() < it->second + g_config.getNumber(ConfigManager::STATUSQUERY_TIMEOUT)) + { + getConnection()->close(); + return; + } } - } - IpConnectMap::const_iterator it = ipConnectMap.find(getIP()); - if(it != ipConnectMap.end() && OTSYS_TIME() < it->second + g_config.getNumber(ConfigManager::STATUSQUERY_TIMEOUT)) - { - getConnection()->close(); - return; + ipConnectMap[ip] = OTSYS_TIME(); } - ipConnectMap[getIP()] = OTSYS_TIME(); - switch(msg.GetByte()) + uint8_t type = msg.get(); + switch(type) { case 0xFF: { - if(msg.GetString(4) == "info") + if(msg.getString(false, 4) == "info") { if(OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false)) { @@ -74,11 +75,10 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage& msg) if(Status* status = Status::getInstance()) { bool sendPlayers = false; - if(msg.getMessageLength() > msg.getReadPos()) - sendPlayers = msg.GetByte() == 0x01; + if(msg.size() > msg.position()) + sendPlayers = msg.get() == 0x01; - std::string str = status->getStatusString(sendPlayers); - output->AddBytes(str.c_str(), str.size()); + output->putString(status->getStatusString(sendPlayers), false); } setRawMessages(true); // we dont want the size header, nor encryption @@ -91,7 +91,7 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage& msg) case 0x01: { - uint32_t requestedInfo = msg.GetU16(); //Only a Byte is necessary, though we could add new infos here + uint32_t requestedInfo = msg.get(); // only a byte is necessary, though we could add new infos here if(OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false)) { TRACK_MESSAGE(output); @@ -111,38 +111,35 @@ void ProtocolStatus::onRecvFirstMessage(NetworkMessage& msg) getConnection()->close(); } +#ifdef __DEBUG_NET_DETAIL__ void ProtocolStatus::deleteProtocolTask() { -#ifdef __DEBUG_NET_DETAIL__ - std::cout << "Deleting ProtocolStatus" << std::endl; -#endif + std::clog << "Deleting ProtocolStatus" << std::endl; Protocol::deleteProtocolTask(); } +#endif std::string Status::getStatusString(bool sendPlayers) const { - char buffer[90]; - xmlDocPtr doc; - xmlNodePtr p, root; - - doc = xmlNewDoc((const xmlChar*)"1.0"); + xmlDocPtr doc = xmlNewDoc((const xmlChar*)"1.0"); doc->children = xmlNewDocNode(doc, NULL, (const xmlChar*)"tsqp", NULL); - root = doc->children; + xmlNodePtr root = doc->children; + char buffer[90]; xmlSetProp(root, (const xmlChar*)"version", (const xmlChar*)"1.0"); - p = xmlNewNode(NULL,(const xmlChar*)"serverinfo"); + xmlNodePtr p = xmlNewNode(NULL,(const xmlChar*)"serverinfo"); sprintf(buffer, "%u", (uint32_t)getUptime()); xmlSetProp(p, (const xmlChar*)"uptime", (const xmlChar*)buffer); xmlSetProp(p, (const xmlChar*)"ip", (const xmlChar*)g_config.getString(ConfigManager::IP).c_str()); xmlSetProp(p, (const xmlChar*)"servername", (const xmlChar*)g_config.getString(ConfigManager::SERVER_NAME).c_str()); - sprintf(buffer, "%d", g_config.getNumber(ConfigManager::LOGIN_PORT)); + sprintf(buffer, "%d", (int32_t)g_config.getNumber(ConfigManager::LOGIN_PORT)); xmlSetProp(p, (const xmlChar*)"port", (const xmlChar*)buffer); xmlSetProp(p, (const xmlChar*)"location", (const xmlChar*)g_config.getString(ConfigManager::LOCATION).c_str()); xmlSetProp(p, (const xmlChar*)"url", (const xmlChar*)g_config.getString(ConfigManager::URL).c_str()); - xmlSetProp(p, (const xmlChar*)"server", (const xmlChar*)STATUS_SERVER_NAME); - xmlSetProp(p, (const xmlChar*)"version", (const xmlChar*)STATUS_SERVER_VERSION); - xmlSetProp(p, (const xmlChar*)"client", (const xmlChar*)STATUS_SERVER_PROTOCOL); + xmlSetProp(p, (const xmlChar*)"server", (const xmlChar*)SOFTWARE_NAME); + xmlSetProp(p, (const xmlChar*)"version", (const xmlChar*)SOFTWARE_VERSION); + xmlSetProp(p, (const xmlChar*)"client", (const xmlChar*)SOFTWARE_PROTOCOL); xmlAddChild(root, p); p = xmlNewNode(NULL,(const xmlChar*)"owner"); @@ -153,7 +150,7 @@ std::string Status::getStatusString(bool sendPlayers) const p = xmlNewNode(NULL,(const xmlChar*)"players"); sprintf(buffer, "%d", g_game.getPlayersOnline()); xmlSetProp(p, (const xmlChar*)"online", (const xmlChar*)buffer); - sprintf(buffer, "%d", g_config.getNumber(ConfigManager::MAX_PLAYERS)); + sprintf(buffer, "%d", (int32_t)g_config.getNumber(ConfigManager::MAX_PLAYERS)); xmlSetProp(p, (const xmlChar*)"max", (const xmlChar*)buffer); sprintf(buffer, "%d", g_game.getPlayersRecord()); xmlSetProp(p, (const xmlChar*)"peak", (const xmlChar*)buffer); @@ -187,7 +184,7 @@ std::string Status::getStatusString(bool sendPlayers) const xmlAddChild(root, p); p = xmlNewNode(NULL,(const xmlChar*)"map"); - xmlSetProp(p, (const xmlChar*)"name", (const xmlChar*)m_mapName.c_str()); + xmlSetProp(p, (const xmlChar*)"name", (const xmlChar*)g_config.getString(ConfigManager::MAP_NAME).c_str()); xmlSetProp(p, (const xmlChar*)"author", (const xmlChar*)g_config.getString(ConfigManager::MAP_AUTHOR).c_str()); uint32_t mapWidth, mapHeight; @@ -218,57 +215,57 @@ void Status::getInfo(uint32_t requestedInfo, OutputMessage_ptr output, NetworkMe { if(requestedInfo & REQUEST_BASIC_SERVER_INFO) { - output->AddByte(0x10); - output->AddString(g_config.getString(ConfigManager::SERVER_NAME).c_str()); - output->AddString(g_config.getString(ConfigManager::IP).c_str()); + output->put(0x10); + output->putString(g_config.getString(ConfigManager::SERVER_NAME).c_str()); + output->putString(g_config.getString(ConfigManager::IP).c_str()); char buffer[10]; - sprintf(buffer, "%d", g_config.getNumber(ConfigManager::LOGIN_PORT)); - output->AddString(buffer); + sprintf(buffer, "%d", (int32_t)g_config.getNumber(ConfigManager::LOGIN_PORT)); + output->putString(buffer); } if(requestedInfo & REQUEST_SERVER_OWNER_INFO) { - output->AddByte(0x11); - output->AddString(g_config.getString(ConfigManager::OWNER_NAME).c_str()); - output->AddString(g_config.getString(ConfigManager::OWNER_EMAIL).c_str()); + output->put(0x11); + output->putString(g_config.getString(ConfigManager::OWNER_NAME).c_str()); + output->putString(g_config.getString(ConfigManager::OWNER_EMAIL).c_str()); } if(requestedInfo & REQUEST_MISC_SERVER_INFO) { - output->AddByte(0x12); - output->AddString(g_config.getString(ConfigManager::MOTD).c_str()); - output->AddString(g_config.getString(ConfigManager::LOCATION).c_str()); - output->AddString(g_config.getString(ConfigManager::URL).c_str()); + output->put(0x12); + output->putString(g_config.getString(ConfigManager::MOTD).c_str()); + output->putString(g_config.getString(ConfigManager::LOCATION).c_str()); + output->putString(g_config.getString(ConfigManager::URL).c_str()); uint64_t uptime = getUptime(); - output->AddU32((uint32_t)(uptime >> 32)); - output->AddU32((uint32_t)(uptime)); + output->put((uint32_t)(uptime >> 32)); + output->put((uint32_t)(uptime)); } if(requestedInfo & REQUEST_PLAYERS_INFO) { - output->AddByte(0x20); - output->AddU32(g_game.getPlayersOnline()); - output->AddU32(g_config.getNumber(ConfigManager::MAX_PLAYERS)); - output->AddU32(g_game.getPlayersRecord()); + output->put(0x20); + output->put(g_game.getPlayersOnline()); + output->put((uint32_t)g_config.getNumber(ConfigManager::MAX_PLAYERS)); + output->put(g_game.getPlayersRecord()); } if(requestedInfo & REQUEST_SERVER_MAP_INFO) { - output->AddByte(0x30); - output->AddString(m_mapName.c_str()); - output->AddString(g_config.getString(ConfigManager::MAP_AUTHOR).c_str()); + output->put(0x30); + output->putString(g_config.getString(ConfigManager::MAP_NAME).c_str()); + output->putString(g_config.getString(ConfigManager::MAP_AUTHOR).c_str()); uint32_t mapWidth, mapHeight; g_game.getMapDimensions(mapWidth, mapHeight); - output->AddU16(mapWidth); - output->AddU16(mapHeight); + output->put(mapWidth); + output->put(mapHeight); } if(requestedInfo & REQUEST_EXT_PLAYERS_INFO) { - output->AddByte(0x21); + output->put(0x21); std::list > players; for(AutoList::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) { @@ -276,31 +273,31 @@ void Status::getInfo(uint32_t requestedInfo, OutputMessage_ptr output, NetworkMe players.push_back(std::make_pair(it->second->getName(), it->second->getLevel())); } - output->AddU32(players.size()); + output->put(players.size()); for(std::list >::iterator it = players.begin(); it != players.end(); ++it) { - output->AddString(it->first); - output->AddU32(it->second); + output->putString(it->first); + output->put(it->second); } } if(requestedInfo & REQUEST_PLAYER_STATUS_INFO) { - output->AddByte(0x22); - const std::string name = msg.GetString(); + output->put(0x22); + const std::string name = msg.getString(); Player* p = NULL; if(g_game.getPlayerByNameWildcard(name, p) == RET_NOERROR && !p->isGhost()) - output->AddByte(0x01); + output->put(0x01); else - output->AddByte(0x00); + output->put(0x00); } if(requestedInfo & REQUEST_SERVER_SOFTWARE_INFO) { - output->AddByte(0x23); - output->AddString(STATUS_SERVER_NAME); - output->AddString(STATUS_SERVER_VERSION); - output->AddString(STATUS_SERVER_PROTOCOL); + output->put(0x23); + output->putString(SOFTWARE_NAME); + output->putString(SOFTWARE_VERSION); + output->putString(SOFTWARE_PROTOCOL); } } diff --git a/status.h b/status.h index 5b8d9d9..9fa5c7c 100644 --- a/status.h +++ b/status.h @@ -58,11 +58,14 @@ class ProtocolStatus : public Protocol enum {protocolId = 0xFF}; enum {isSingleSocket = false}; enum {hasChecksum = false}; + static const char* protocolName() {return "status protocol";} protected: static IpConnectMap ipConnectMap; + #ifdef __DEBUG_NET_DETAIL__ virtual void deleteProtocolTask(); + #endif }; class Status @@ -78,9 +81,6 @@ class Status std::string getStatusString(bool sendPlayers) const; void getInfo(uint32_t requestedInfo, OutputMessage_ptr output, NetworkMessage& msg) const; - const std::string& getMapName() const {return m_mapName;} - void setMapName(std::string mapName) {m_mapName = mapName;} - uint32_t getUptime() const {return (OTSYS_TIME() - m_start) / 1000;} int64_t getStart() const {return m_start;} @@ -92,6 +92,5 @@ class Status private: int64_t m_start; - std::string m_mapName; }; #endif diff --git a/talkaction.cpp b/talkaction.cpp index 619afba..aceaace 100644 --- a/talkaction.cpp +++ b/talkaction.cpp @@ -17,9 +17,6 @@ #include "otpch.h" #include "talkaction.h" -#include -#include - #include "iologindata.h" #include "ioban.h" @@ -33,10 +30,12 @@ #include "status.h" #include "textlogger.h" +#include #ifdef __ENABLE_SERVER_DIAGNOSTIC__ #include "outputmessage.h" #include "connection.h" #include "admin.h" +#include "manager.h" #include "protocollogin.h" #include "protocolold.h" #endif @@ -51,10 +50,11 @@ extern Game g_game; extern Chat g_chat; extern TalkActions* g_talkActions; -TalkActions::TalkActions() : +TalkActions::TalkActions(): m_interface("TalkAction Interface") { m_interface.initState(); + defaultTalkAction = NULL; } TalkActions::~TalkActions() @@ -69,6 +69,9 @@ void TalkActions::clear() talksMap.clear(); m_interface.reInitState(); + + delete defaultTalkAction; + defaultTalkAction = NULL; } Event* TalkActions::getEvent(const std::string& nodeName) @@ -85,11 +88,26 @@ bool TalkActions::registerEvent(Event* event, xmlNodePtr p, bool override) if(!talkAction) return false; - std::string sep; - if(!readXMLString(p, "separator", sep) || sep.empty()) - sep = ";"; + std::string strValue; + if(readXMLString(p, "default", strValue) && booleanString(strValue)) + { + if(!defaultTalkAction) + defaultTalkAction = talkAction; + else if(override) + { + delete defaultTalkAction; + defaultTalkAction = talkAction; + } + else + std::clog << "[Warning - TalkAction::registerEvent] You cannot define more than one default talkAction." << std::endl; + + return true; + } + + if(!readXMLString(p, "separator", strValue) || strValue.empty()) + strValue = ";"; - StringVec strVector = explodeString(talkAction->getWords(), sep); + StringVec strVector = explodeString(talkAction->getWords(), strValue); for(StringVec::iterator it = strVector.begin(); it != strVector.end(); ++it) { trimString(*it); @@ -98,11 +116,11 @@ bool TalkActions::registerEvent(Event* event, xmlNodePtr p, bool override) { if(!override) { - std::cout << "[Warning - TalkAction::configureEvent] Duplicate registered talkaction with words: " << (*it) << std::endl; + std::clog << "[Warning - TalkAction::registerEvent] Duplicate registered talkaction with words: " << (*it) << std::endl; continue; } - else - delete talksMap[(*it)]; + + delete talksMap[(*it)]; } talksMap[(*it)] = new TalkAction(talkAction); @@ -114,76 +132,92 @@ bool TalkActions::registerEvent(Event* event, xmlNodePtr p, bool override) bool TalkActions::onPlayerSay(Creature* creature, uint16_t channelId, const std::string& words, bool ignoreAccess) { - std::string cmdstring[TALKFILTER_LAST] = words, paramstring[TALKFILTER_LAST] = ""; - size_t loc = words.find('"', 0); - if(loc != std::string::npos && loc >= 0) + std::string cmd[TALKFILTER_LAST], param[TALKFILTER_LAST]; + for(int32_t i = 0; i < TALKFILTER_LAST; ++i) + cmd[i] = words; + + std::string::size_type loc = words.find('"', 0); + if(loc != std::string::npos) { - cmdstring[TALKFILTER_QUOTATION] = std::string(words, 0, loc); - paramstring[TALKFILTER_QUOTATION] = std::string(words, (loc + 1), (words.size() - (loc - 1))); - trimString(cmdstring[TALKFILTER_QUOTATION]); + cmd[TALKFILTER_QUOTATION] = std::string(words, 0, loc); + param[TALKFILTER_QUOTATION] = std::string(words, (loc + 1), (words.size() - (loc - 1))); + trimString(cmd[TALKFILTER_QUOTATION]); } loc = words.find(" ", 0); - if(loc != std::string::npos && loc >= 0) + if(loc != std::string::npos) { - cmdstring[TALKFILTER_WORD] = std::string(words, 0, loc); - paramstring[TALKFILTER_WORD] = std::string(words, (loc + 1), (words.size() - (loc - 1))); + cmd[TALKFILTER_WORD] = std::string(words, 0, loc); + param[TALKFILTER_WORD] = std::string(words, (loc + 1), (words.size() - (loc - 1))); - size_t sloc = words.find(" ", ++loc); - if(sloc != std::string::npos && sloc >= 0) + std::string::size_type spaceLoc = words.find(" ", ++loc); + if(spaceLoc != std::string::npos) { - cmdstring[TALKFILTER_WORD_SPACED] = std::string(words, 0, sloc); - paramstring[TALKFILTER_WORD_SPACED] = std::string(words, (sloc + 1), (words.size() - (sloc - 1))); + cmd[TALKFILTER_WORD_SPACED] = std::string(words, 0, spaceLoc); + param[TALKFILTER_WORD_SPACED] = std::string(words, (spaceLoc + 1), (words.size() - (spaceLoc - 1))); } } TalkAction* talkAction = NULL; for(TalkActionsMap::iterator it = talksMap.begin(); it != talksMap.end(); ++it) { - if(it->first == cmdstring[it->second->getFilter()] || (!it->second->isSensitive() && - !strcasecmp(it->first.c_str(), cmdstring[it->second->getFilter()].c_str()))) + if(it->first == cmd[it->second->getFilter()] || (!it->second->isSensitive() + && boost::algorithm::iequals(it->first, cmd[it->second->getFilter()]))) { talkAction = it->second; break; } } + if(!talkAction && defaultTalkAction) + talkAction = defaultTalkAction; + if(!talkAction || (talkAction->getChannel() != -1 && talkAction->getChannel() != channelId)) return false; Player* player = creature->getPlayer(); - StringVec exceptions = talkAction->getExceptions(); - if(player && ((!ignoreAccess && std::find(exceptions.begin(), exceptions.end(), asLowerCaseString( - player->getName())) == exceptions.end() && talkAction->getAccess() > player->getAccess()) - || player->isAccountManager())) + if(player) { - if(player->hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges)) + if(!player->canDoExAction()) + return false; + + StringVec exceptions = talkAction->getExceptions(); + if((!ignoreAccess && std::find(exceptions.begin(), exceptions.end(), asLowerCaseString( + player->getName())) == exceptions.end() && (talkAction->getAccess() > player->getAccess() + || (talkAction->hasGroups() && !talkAction->hasGroup(player->getGroupId())))) + || player->isAccountManager()) { - player->sendTextMessage(MSG_STATUS_SMALL, "You cannot execute this talkaction."); - return true; + if(player->hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges)) + { + player->sendTextMessage(MSG_STATUS_SMALL, "You cannot execute this talkaction."); + return true; + } + + return false; } - return false; + if(!player->hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges)) + player->setNextExAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::CUSTOM_ACTIONS_DELAY_INTERVAL) - 10); } if(talkAction->isLogged()) { if(player) - player->sendTextMessage(MSG_STATUS_CONSOLE_RED, words.c_str()); + player->sendTextMessage(MSG_EVENT_ORANGE, words.c_str()); Logger::getInstance()->eFile("talkactions/" + creature->getName() + ".log", words, true); } if(talkAction->isScripted()) - return talkAction->executeSay(creature, cmdstring[talkAction->getFilter()], paramstring[talkAction->getFilter()], channelId); + return (talkAction->executeSay(creature, cmd[talkAction->getFilter()], param[talkAction->getFilter()], channelId) != 0); if(TalkFunction* function = talkAction->getFunction()) - return function(creature, cmdstring[talkAction->getFilter()], paramstring[talkAction->getFilter()]); + return function(creature, cmd[talkAction->getFilter()], param[talkAction->getFilter()]); return false; } -TalkAction::TalkAction(LuaScriptInterface* _interface): +TalkAction::TalkAction(LuaInterface* _interface): Event(_interface) { m_function = NULL; @@ -206,6 +240,7 @@ Event(copy) m_hidden = copy->m_hidden; m_sensitive = copy->m_sensitive; m_exceptions = copy->m_exceptions; + m_groups = copy->m_groups; } bool TalkAction::configureEvent(xmlNodePtr p) @@ -213,9 +248,9 @@ bool TalkAction::configureEvent(xmlNodePtr p) std::string strValue; if(readXMLString(p, "words", strValue)) m_words = strValue; - else + else if(!readXMLString(p, "default", strValue) || !booleanString(strValue)) { - std::cout << "[Error - TalkAction::configureEvent] No words for TalkAction." << std::endl; + std::clog << "[Error - TalkAction::configureEvent] No words for TalkAction." << std::endl; return false; } @@ -229,20 +264,27 @@ bool TalkAction::configureEvent(xmlNodePtr p) else if(tmpStrValue == "word-spaced") m_filter = TALKFILTER_WORD_SPACED; else - std::cout << "[Warning - TalkAction::configureEvent] Unknown filter for TalkAction: " << strValue << ", using default." << std::endl; + std::clog << "[Warning - TalkAction::configureEvent] Unknown filter for TalkAction: " << strValue << ", using default." << std::endl; } int32_t intValue; if(readXMLInteger(p, "access", intValue)) m_access = intValue; + if(readXMLString(p, "group", strValue) || readXMLString(p, "groups", strValue)) + { + m_groups.clear(); + if(!parseIntegerVec(strValue, m_groups)) + std::clog << "[Warning - TalkAction::configureEvent] Invalid group(s) for TalkAction: " << strValue << std::endl; + } + if(readXMLInteger(p, "channel", intValue)) m_channel = intValue; - if(readXMLString(p, "log", strValue) || readXMLString(p, "logged", strValue)) + if(readXMLString(p, "logged", strValue) || readXMLString(p, "log", strValue)) m_logged = booleanString(strValue); - if(readXMLString(p, "hide", strValue) || readXMLString(p, "hidden", strValue)) + if(readXMLString(p, "hidden", strValue) || readXMLString(p, "hide", strValue)) m_hidden = booleanString(strValue); if(readXMLString(p, "case-sensitive", strValue) || readXMLString(p, "casesensitive", strValue) || readXMLString(p, "sensitive", strValue)) @@ -256,36 +298,36 @@ bool TalkAction::configureEvent(xmlNodePtr p) bool TalkAction::loadFunction(const std::string& functionName) { - std::string tmpFunctionName = asLowerCaseString(functionName); - if(tmpFunctionName == "housebuy") + m_functionName = asLowerCaseString(functionName); + if(m_functionName == "housebuy") m_function = houseBuy; - else if(tmpFunctionName == "housesell") + else if(m_functionName == "housesell") m_function = houseSell; - else if(tmpFunctionName == "housekick") + else if(m_functionName == "housekick") m_function = houseKick; - else if(tmpFunctionName == "housedoorlist") + else if(m_functionName == "housedoorlist") m_function = houseDoorList; - else if(tmpFunctionName == "houseguestlist") + else if(m_functionName == "houseguestlist") m_function = houseGuestList; - else if(tmpFunctionName == "housesubownerlist") + else if(m_functionName == "housesubownerlist") m_function = houseSubOwnerList; - else if(tmpFunctionName == "guildjoin") + else if(m_functionName == "guildjoin") m_function = guildJoin; - else if(tmpFunctionName == "guildcreate") + else if(m_functionName == "guildcreate") m_function = guildCreate; - else if(tmpFunctionName == "thingproporties") + else if(m_functionName == "thingproporties") m_function = thingProporties; - else if(tmpFunctionName == "banishmentinfo") + else if(m_functionName == "banishmentinfo") m_function = banishmentInfo; - else if(tmpFunctionName == "diagnostics") + else if(m_functionName == "diagnostics") m_function = diagnostics; - else if(tmpFunctionName == "addskill") - m_function = addSkill; - else if(tmpFunctionName == "ghost") + else if(m_functionName == "ghost") m_function = ghost; + else if(m_functionName == "software") + m_function = software; else { - std::cout << "[Warning - TalkAction::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; + std::clog << "[Warning - TalkAction::loadFunction] Function \"" << m_functionName << "\" does not exist." << std::endl; return false; } @@ -310,7 +352,9 @@ int32_t TalkAction::executeSay(Creature* creature, const std::string& words, std scriptstream << "local param = \"" << param << "\"" << std::endl; scriptstream << "local channel = " << channel << std::endl; - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -326,7 +370,7 @@ int32_t TalkAction::executeSay(Creature* creature, const std::string& words, std #ifdef __DEBUG_LUASCRIPTS__ char desc[125]; sprintf(desc, "%s - %s- %s", creature->getName().c_str(), words.c_str(), param.c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -347,12 +391,12 @@ int32_t TalkAction::executeSay(Creature* creature, const std::string& words, std } else { - std::cout << "[Error - TalkAction::executeSay] Call stack overflow." << std::endl; + std::clog << "[Error - TalkAction::executeSay] Call stack overflow." << std::endl; return 0; } } -bool TalkAction::houseBuy(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::houseBuy(Creature* creature, const std::string&, const std::string&) { Player* player = creature->getPlayer(); if(!player || !g_config.getBool(ConfigManager::HOUSE_BUY_AND_SELL)) @@ -367,25 +411,24 @@ bool TalkAction::houseBuy(Creature* creature, const std::string& cmd, const std: return false; } - HouseTile* houseTile = tile->getHouseTile(); - if(!houseTile) + House* house = tile->getHouse(); + if(!house) { player->sendCancel("You have to be looking at door of flat you would like to purchase."); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } - House* house = houseTile->getHouse(); - if(!house) + if(!house->getDoorByPosition(pos)) { player->sendCancel("You have to be looking at door of flat you would like to purchase."); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } - if(!house->getDoorByPosition(pos)) + if(house->isBidded()) { - player->sendCancel("You have to be looking at door of flat you would like to purchase."); + player->sendCancel("You cannot buy house which is currently bidded on an auction."); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); return false; } @@ -451,7 +494,7 @@ bool TalkAction::houseBuy(Creature* creature, const std::string& cmd, const std: return false; } - if(g_game.getMoney(player) < house->getPrice() || !g_game.removeMoney(player, house->getPrice())) + if((uint32_t)g_game.getMoney(player) < house->getPrice() || !g_game.removeMoney(player, house->getPrice())) { player->sendCancel("You do not have enough money."); g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); @@ -459,6 +502,31 @@ bool TalkAction::houseBuy(Creature* creature, const std::string& cmd, const std: } house->setOwnerEx(player->getGUID(), true); + if(g_config.getBool(ConfigManager::HOUSE_SKIP_INIT_RENT)) + { + uint32_t paidUntil = time(NULL); + switch(Houses::getInstance()->getRentPeriod()) + { + case RENTPERIOD_DAILY: + paidUntil += 86400; + break; + case RENTPERIOD_WEEKLY: + paidUntil += 7 * 86400; + break; + case RENTPERIOD_MONTHLY: + paidUntil += 30 * 86400; + break; + case RENTPERIOD_YEARLY: + paidUntil += 365 * 86400; + break; + default: + break; + } + + house->setPaidUntil(paidUntil); + house->setLastWarning(0); + } + std::string ret = "You have successfully bought this "; if(house->isGuild()) ret += "hall"; @@ -479,7 +547,7 @@ bool TalkAction::houseBuy(Creature* creature, const std::string& cmd, const std: return false; } -bool TalkAction::houseSell(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::houseSell(Creature* creature, const std::string&, const std::string& param) { Player* player = creature->getPlayer(); if(!player || !g_config.getBool(ConfigManager::HOUSE_BUY_AND_SELL)) @@ -500,6 +568,14 @@ bool TalkAction::houseSell(Creature* creature, const std::string& cmd, const std return false; } + Tile* tile = g_game.getTile(player->getPosition()); + if(!tile || !tile->getHouseTile() || tile->getHouseTile()->getHouse() != house) + { + player->sendCancel("You have to be inside a house that you would like to sell."); + g_game.addMagicEffect(player->getPosition(), MAGIC_EFFECT_POFF); + return false; + } + Player* tradePartner = NULL; ReturnValue ret = g_game.getPlayerByNameWildcard(param, tradePartner); if(ret != RET_NOERROR) @@ -596,7 +672,7 @@ bool TalkAction::houseSell(Creature* creature, const std::string& cmd, const std return false; } -bool TalkAction::houseKick(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::houseKick(Creature* creature, const std::string&, const std::string& param) { Player* player = creature->getPlayer(); if(!player) @@ -618,7 +694,7 @@ bool TalkAction::houseKick(Creature* creature, const std::string& cmd, const std return false; } -bool TalkAction::houseDoorList(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::houseDoorList(Creature* creature, const std::string&, const std::string&) { Player* player = creature->getPlayer(); if(!player) @@ -648,7 +724,7 @@ bool TalkAction::houseDoorList(Creature* creature, const std::string& cmd, const return false; } -bool TalkAction::houseGuestList(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::houseGuestList(Creature* creature, const std::string&, const std::string&) { Player* player = creature->getPlayer(); if(!player) @@ -670,7 +746,7 @@ bool TalkAction::houseGuestList(Creature* creature, const std::string& cmd, cons return false; } -bool TalkAction::houseSubOwnerList(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::houseSubOwnerList(Creature* creature, const std::string&, const std::string&) { Player* player = creature->getPlayer(); if(!player) @@ -692,7 +768,7 @@ bool TalkAction::houseSubOwnerList(Creature* creature, const std::string& cmd, c return false; } -bool TalkAction::guildJoin(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::guildJoin(Creature* creature, const std::string&, const std::string& param) { Player* player = creature->getPlayer(); if(!player || !g_config.getBool(ConfigManager::INGAME_GUILD_MANAGEMENT)) @@ -708,12 +784,12 @@ bool TalkAction::guildJoin(Creature* creature, const std::string& cmd, const std if(player->isGuildInvited(guildId)) { IOGuild::getInstance()->joinGuild(player, guildId); - player->sendTextMessage(MSG_INFO_DESCR, "You have joined the guild."); + player->sendTextMessage(MSG_EVENT_GUILD, "You have joined the guild."); char buffer[80]; sprintf(buffer, "%s has joined the guild.", player->getName().c_str()); if(ChatChannel* guildChannel = g_chat.getChannel(player, 0x00)) - guildChannel->talk(player, SPEAK_CHANNEL_RA, buffer); + guildChannel->talk("", MSG_CHANNEL_HIGHLIGHT, buffer); } else player->sendCancel("You are not invited to that guild."); @@ -727,7 +803,7 @@ bool TalkAction::guildJoin(Creature* creature, const std::string& cmd, const std return true; } -bool TalkAction::guildCreate(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::guildCreate(Creature* creature, const std::string&, const std::string& param) { Player* player = creature->getPlayer(); if(!player || !g_config.getBool(ConfigManager::INGAME_GUILD_MANAGEMENT)) @@ -747,8 +823,8 @@ bool TalkAction::guildCreate(Creature* creature, const std::string& cmd, const s return true; } - const uint32_t minLength = g_config.getNumber(ConfigManager::MIN_GUILDNAME); - const uint32_t maxLength = g_config.getNumber(ConfigManager::MAX_GUILDNAME); + uint32_t minLength = g_config.getNumber(ConfigManager::MIN_GUILDNAME), + maxLength = g_config.getNumber(ConfigManager::MAX_GUILDNAME); if(param_.length() < minLength) { player->sendCancel("That guild name is too short, please select a longer name."); @@ -771,31 +847,31 @@ bool TalkAction::guildCreate(Creature* creature, const std::string& cmd, const s const uint32_t levelToFormGuild = g_config.getNumber(ConfigManager::LEVEL_TO_FORM_GUILD); if(player->getLevel() < levelToFormGuild) { - char buffer[70 + levelToFormGuild]; - sprintf(buffer, "You have to be at least Level %d to form a guild.", levelToFormGuild); - player->sendCancel(buffer); + std::stringstream stream; + stream << "You have to be at least Level " << levelToFormGuild << " to form a guild."; + player->sendCancel(stream.str().c_str()); return true; } const int32_t premiumDays = g_config.getNumber(ConfigManager::GUILD_PREMIUM_DAYS); - if(player->getPremiumDays() < premiumDays) + if(player->getPremiumDays() < premiumDays && !g_config.getBool(ConfigManager::FREE_PREMIUM)) { - char buffer[70 + premiumDays]; - sprintf(buffer, "You need to have at least %d premium days to form a guild.", premiumDays); - player->sendCancel(buffer); + std::stringstream stream; + stream << "You need to have at least " << premiumDays << " premium days to form a guild."; + player->sendCancel(stream.str().c_str()); return true; } player->setGuildName(param_); IOGuild::getInstance()->createGuild(player); - char buffer[50 + maxLength]; - sprintf(buffer, "You have formed guild \"%s\"!", param_.c_str()); - player->sendTextMessage(MSG_INFO_DESCR, buffer); + std::stringstream stream; + stream << "You have formed guild \"" << param.c_str() << "\"!"; + player->sendTextMessage(MSG_EVENT_GUILD, stream.str().c_str()); return true; } -bool TalkAction::thingProporties(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::thingProporties(Creature* creature, const std::string&, const std::string& param) { Player* player = creature->getPlayer(); if(!player) @@ -828,15 +904,20 @@ bool TalkAction::thingProporties(Creature* creature, const std::string& cmd, con toLowerCaseString(action); if(Item* item = thing->getItem()) { - if(action == "set") + if(action == "set" || action == "add" || action == "new") { - std::string key = parseParams(it, tokens.end()), value = parseParams(it, tokens.end()); - if(atoi(value.c_str()) || value == "0") + std::string type = parseParams(it, tokens.end()), key = parseParams(it, + tokens.end()), value = parseParams(it, tokens.end()); + if(type == "integer" || type == "number" || type == "int" || type == "num") item->setAttribute(key, atoi(value.c_str())); + else if(type == "float" || type == "double") + item->setAttribute(key, (float)atof(value.c_str())); + else if(type == "bool" || type == "boolean") + item->setAttribute(key, booleanString(value)); else item->setAttribute(key, value); } - else if(action == "erase" || action == "remove") + else if(action == "erase" || action == "remove" || action == "delete") item->eraseAttribute(parseParams(it, tokens.end())); else if(action == "action" || action == "actionid" || action == "aid") { @@ -852,13 +933,12 @@ bool TalkAction::thingProporties(Creature* creature, const std::string& cmd, con if(tmp >= 1000 || tmp <= 0xFFFF) item->setUniqueId(tmp); } - else if(action == "destination" || action == "position" - || action == "pos" || action == "dest") //TODO: doesn't work + else if(action == "destination" || action == "position" || action == "pos" + || action == "dest" || action == "location" || action == "loc") //TODO: doesn't work { if(Teleport* teleport = item->getTeleport()) - teleport->setDestination(Position(atoi(parseParams(it, - tokens.end()).c_str()), atoi(parseParams(it, tokens.end()).c_str()), - atoi(parseParams(it, tokens.end()).c_str()))); + teleport->setDestination(Position(atoi(parseParams(it, tokens.end()).c_str()), atoi( + parseParams(it, tokens.end()).c_str()), atoi(parseParams(it, tokens.end()).c_str()))); } else { @@ -884,15 +964,30 @@ bool TalkAction::thingProporties(Creature* creature, const std::string& cmd, con _creature->setDropLoot((lootDrop_t)atoi(parseParams(it, tokens.end()).c_str())); else if(action == "lossskill") _creature->setLossSkill(booleanString(parseParams(it, tokens.end()))); + else if(action == "storage") + _creature->setStorage(parseParams(it, tokens.end()), parseParams(it, tokens.end())); else if(action == "cannotmove") + { _creature->setNoMove(booleanString(parseParams(it, tokens.end()))); + _creature->onWalkAborted(); + } else if(action == "skull") { - _creature->setSkull(getSkull(parseParams(it, tokens.end()))); + _creature->setSkull(getSkulls(parseParams(it, tokens.end()))); g_game.updateCreatureSkull(_creature); } + else if(action == "shield") + { + _creature->setShield(getShields(parseParams(it, tokens.end()))); + g_game.updateCreatureShield(_creature); + } + else if(action == "emblem") + { + _creature->setEmblem(getEmblems(parseParams(it, tokens.end()))); + g_game.updateCreatureEmblem(_creature); + } else if(action == "speaktype") - _creature->setSpeakType((SpeakClasses)atoi(parseParams(it, tokens.end()).c_str())); + _creature->setSpeakType((MessageClasses)atoi(parseParams(it, tokens.end()).c_str())); else if(Player* _player = _creature->getPlayer()) { if(action == "fyi") @@ -906,13 +1001,20 @@ bool TalkAction::thingProporties(Creature* creature, const std::string& cmd, con else if(action == "guildnick") _player->setGuildNick(parseParams(it, tokens.end()).c_str()); else if(action == "group") - _player->setGroupId(atoi(parseParams(it, tokens.end()).c_str())); + { + uint16_t tmp = atoi(parseParams(it, tokens.end()).c_str()); + if(tmp >= _player->getGroupId()) + { + invalid = "security failure - you can set only lower group than your own!"; + break; + } + else + _player->setGroupId(tmp); + } else if(action == "vocation") _player->setVocation(atoi(parseParams(it, tokens.end()).c_str())); else if(action == "sex" || action == "gender") _player->setSex(atoi(parseParams(it, tokens.end()).c_str())); - else if(action == "stamina") - _player->setStaminaMinutes(atoi(parseParams(it, tokens.end()).c_str())); else if(action == "town" || action == "temple") { if(Town* town = Towns::getInstance()->getTown(parseParams(it, tokens.end()))) @@ -921,15 +1023,17 @@ bool TalkAction::thingProporties(Creature* creature, const std::string& cmd, con _player->setTown(town->getID()); } } - else if(action == "balance") - _player->balance = atoi(parseParams(it, tokens.end()).c_str()); else if(action == "marriage" || action == "partner") _player->marriage = atoi(parseParams(it, tokens.end()).c_str()); + else if(action == "balance") + _player->balance = atoi(parseParams(it, tokens.end()).c_str()); else if(action == "rates") _player->rates[atoi(parseParams(it, tokens.end()).c_str())] = atof( parseParams(it, tokens.end()).c_str()); else if(action == "idle") _player->setIdleTime(atoi(parseParams(it, tokens.end()).c_str())); + else if(action == "stamina") + _player->setStaminaMinutes(atoi(parseParams(it, tokens.end()).c_str())); else if(action == "capacity" || action == "cap") _player->setCapacity(atoi(parseParams(it, tokens.end()).c_str())); else if(action == "execute") @@ -945,6 +1049,12 @@ bool TalkAction::thingProporties(Creature* creature, const std::string& cmd, con break; } } + /*else if(Npc* _npc = _creature->getNpc()) + { + } + else if(Monster* _monster = _creature->getMonster()) + { + }*/ else { std::stringstream s; @@ -955,29 +1065,32 @@ bool TalkAction::thingProporties(Creature* creature, const std::string& cmd, con } } - const SpectatorVec& list = g_game.getSpectators(pos); - SpectatorVec::const_iterator it; + if(invalid.empty()) + { + const SpectatorVec& list = g_game.getSpectators(pos); + SpectatorVec::const_iterator it; - Player* tmpPlayer = NULL; - for(it = list.begin(); it != list.end(); ++it) + Player* tmpPlayer = NULL; + for(it = list.begin(); it != list.end(); ++it) + { + if((tmpPlayer = (*it)->getPlayer())) + tmpPlayer->sendUpdateTile(tile, pos); + } + + for(it = list.begin(); it != list.end(); ++it) + (*it)->onUpdateTile(tile, pos); + } + else { - if((tmpPlayer = (*it)->getPlayer())) - tmpPlayer->sendUpdateTile(tile, pos); + std::string tmp = "Following action was invalid: " + invalid; + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, tmp.c_str()); } - for(it = list.begin(); it != list.end(); ++it) - (*it)->onUpdateTile(tile, pos); - g_game.addMagicEffect(pos, MAGIC_EFFECT_WRAPS_GREEN); - if(invalid.empty()) - return true; - - std::string tmp = "Following action was invalid: " + invalid; - player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, tmp.c_str()); return true; } -bool TalkAction::banishmentInfo(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::banishmentInfo(Creature* creature, const std::string&, const std::string& param) { Player* player = creature->getPlayer(); if(!player) @@ -1052,120 +1165,63 @@ bool TalkAction::banishmentInfo(Creature* creature, const std::string& cmd, cons if(deletion) end = what + (std::string)" won't be undeleted"; - char buffer[500 + ban.comment.length()]; - sprintf(buffer, "%s has been %s at:\n%s by: %s,\nfor the following reason:\n%s.\nThe action taken was:\n%s.\nThe comment given was:\n%s.\n%s%s.", - what.c_str(), (deletion ? "deleted" : "banished"), formatDateShort(ban.added).c_str(), admin.c_str(), getReason(ban.reason).c_str(), - getAction(ban.action, false).c_str(), ban.comment.c_str(), end.c_str(), (deletion ? "." : formatDateShort(ban.expires, true).c_str())); + std::stringstream ss; + ss << what.c_str() << " has been " << (deletion ? "deleted" : "banished") << " at:\n" << formatDateEx(ban.added, "%d %b %Y").c_str() << " by: " << + admin.c_str() << ".\nThe comment given was:\n" << ban.comment.c_str() << ".\n" << end.c_str() << (deletion ? "." : formatDateEx(ban.expires).c_str()) << "."; - player->sendFYIBox(buffer); + player->sendFYIBox(ss.str().c_str()); return true; } -bool TalkAction::diagnostics(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::diagnostics(Creature* creature, const std::string&, const std::string&) { Player* player = creature->getPlayer(); if(!player) return false; - #ifdef __ENABLE_SERVER_DIAGNOSTIC__ - std::stringstream text; - text << "Server diagonostic:\n"; - player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, text.str().c_str()); - - text.str(""); - text << "World:" << "\n"; - text << "--------------------\n"; - text << "Player: " << g_game.getPlayersOnline() << " (" << Player::playerCount << ")" << std::endl; - text << "Npc: " << g_game.getNpcsOnline() << " (" << Npc::npcCount << ")" << std::endl; - text << "Monster: " << g_game.getMonstersOnline() << " (" << Monster::monsterCount << ")" << std::endl << std::endl; - player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, text.str().c_str()); - - text.str(""); - text << "Protocols:" << "\n"; - text << "--------------------\n"; - text << "ProtocolGame: " << ProtocolGame::protocolGameCount << std::endl; - text << "ProtocolLogin: " << ProtocolLogin::protocolLoginCount << std::endl; -#ifdef __REMOTE_CONTROL__ - text << "ProtocolAdmin: " << ProtocolAdmin::protocolAdminCount << std::endl; -#endif - text << "ProtocolStatus: " << ProtocolStatus::protocolStatusCount << std::endl; - text << "ProtocolOld: " << ProtocolOld::protocolOldCount << std::endl << std::endl; - player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, text.str().c_str()); - - text.str(""); - text << "Connections:\n"; - text << "--------------------\n"; - text << "Active connections: " << Connection::connectionCount << "\n"; - text << "Total message pool: " << OutputMessagePool::getInstance()->getTotalMessageCount() << std::endl; - text << "Auto message pool: " << OutputMessagePool::getInstance()->getAutoMessageCount() << std::endl; - text << "Queued message pool: " << OutputMessagePool::getInstance()->getQueuedMessageCount() << std::endl; - text << "Free message pool: " << OutputMessagePool::getInstance()->getAvailableMessageCount() << std::endl << std::endl; - player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, text.str().c_str()); - - text.str(""); - text << "Libraries:\n"; - text << "--------------------\n"; - text << "Platform: " << BOOST_PLATFORM << std::endl; - text << "Compiler: " << BOOST_COMPILER << std::endl; - text << "Boost: " << BOOST_VERSION << std::endl; - text << "ASIO: " << BOOST_ASIO_VERSION << std::endl; - text << "XML: " << XML_DEFAULT_VERSION << std::endl; - text << "Lua: " << LUA_VERSION << std::endl; - player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, text.str().c_str()); + std::stringstream s; + s << "Server diagonostic:" << std::endl; + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, s.str()); + + s.str(""); + s << "World:" << std::endl + << "--------------------" << std::endl + << "Player: " << g_game.getPlayersOnline() << " (" << Player::playerCount << ")" << std::endl + << "Npc: " << g_game.getNpcsOnline() << " (" << Npc::npcCount << ")" << std::endl + << "Monster: " << g_game.getMonstersOnline() << " (" << Monster::monsterCount << ")" << std::endl << std::endl; + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, s.str()); + + s.str(""); + s << "Protocols:" << std::endl + << "--------------------" << std::endl + << "ProtocolGame: " << ProtocolGame::protocolGameCount << std::endl + << "ProtocolLogin: " << ProtocolLogin::protocolLoginCount << std::endl +#ifdef __OTADMIN__ + << "ProtocolAdmin: " << ProtocolAdmin::protocolAdminCount << std::endl +#endif + << "ProtocolManager: " << ProtocolManager::protocolManagerCount << std::endl + << "ProtocolStatus: " << ProtocolStatus::protocolStatusCount << std::endl + << "ProtocolOld: " << ProtocolOld::protocolOldCount << std::endl << std::endl; + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, s.str()); + + s.str(""); + s << "Connections:" << std::endl + << "--------------------" << std::endl + << "Active connections: " << Connection::connectionCount << std::endl + << "Total message pool: " << OutputMessagePool::getInstance()->getTotalMessageCount() << std::endl + << "Auto message pool: " << OutputMessagePool::getInstance()->getAutoMessageCount() << std::endl + << "Queued message pool: " << OutputMessagePool::getInstance()->getQueuedMessageCount() << std::endl + << "Free message pool: " << OutputMessagePool::getInstance()->getAvailableMessageCount() << std::endl; + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, s.str()); + +#else + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Command not available, please rebuild your software with -D__ENABLE_SERVER_DIAG__"); #endif return true; } -bool TalkAction::addSkill(Creature* creature, const std::string& cmd, const std::string& param) -{ - Player* player = creature->getPlayer(); - if(!player) - return false; - - StringVec params = explodeString(param, ","); - if(params.size() < 2) - { - player->sendTextMessage(MSG_STATUS_SMALL, "Command requires at least 2 parameters."); - return true; - } - - uint32_t amount = 1; - if(params.size() > 2) - { - std::string tmp = params[2]; - trimString(tmp); - amount = (uint32_t)std::max(1, atoi(tmp.c_str())); - } - - std::string name = params[0], skill = params[1]; - trimString(name); - trimString(skill); - - Player* target = NULL; - ReturnValue ret = g_game.getPlayerByNameWildcard(name, target); - if(ret != RET_NOERROR) - { - player->sendCancelMessage(ret); - return true; - } - - if(skill[0] == 'l' || skill[0] == 'e') - target->addExperience(uint64_t(Player::getExpForLevel(target->getLevel() + amount) - target->getExperience())); - else if(skill[0] == 'm') - target->addManaSpent((uint64_t)(target->getVocation()->getReqMana(target->getMagicLevel() + - amount) - target->getSpentMana()), false); - else - { - skills_t skillId = getSkillId(skill); - target->addSkillAdvance(skillId, (uint32_t)(target->getVocation()->getReqSkillTries(skillId, target->getSkill(skillId, - SKILL_LEVEL) + amount) - target->getSkill(skillId, SKILL_TRIES)), false); - } - - return true; -} - -bool TalkAction::ghost(Creature* creature, const std::string& cmd, const std::string& param) +bool TalkAction::ghost(Creature* creature, const std::string&, const std::string&) { Player* player = creature->getPlayer(); if(!player) @@ -1173,7 +1229,7 @@ bool TalkAction::ghost(Creature* creature, const std::string& cmd, const std::st if(player->hasFlag(PlayerFlag_CannotBeSeen)) { - player->sendTextMessage(MSG_INFO_DESCR, "Command disabled for players with special, invisibility flag."); + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, "Command disabled for players with special, invisibility flag."); return true; } @@ -1188,8 +1244,8 @@ bool TalkAction::ghost(Creature* creature, const std::string& cmd, const std::st IOLoginData::getInstance()->updateOnlineStatus(player->getGUID(), true); for(AutoList::iterator pit = Player::autoList.begin(); pit != Player::autoList.end(); ++pit) { - if(!pit->second->canSeeCreature(player)) - pit->second->notifyLogIn(player); + if((tmpPlayer = pit->second) && !tmpPlayer->canSeeCreature(player)) + tmpPlayer->notifyLogIn(player); } for(it = list.begin(); it != list.end(); ++it) @@ -1213,8 +1269,8 @@ bool TalkAction::ghost(Creature* creature, const std::string& cmd, const std::st for(AutoList::iterator pit = Player::autoList.begin(); pit != Player::autoList.end(); ++pit) { - if(!pit->second->canSeeCreature(player)) - pit->second->notifyLogOut(player); + if((tmpPlayer = pit->second) && !tmpPlayer->canSeeCreature(player)) + tmpPlayer->notifyLogOut(player); } IOLoginData::getInstance()->updateOnlineStatus(player->getGUID(), false); @@ -1230,3 +1286,31 @@ bool TalkAction::ghost(Creature* creature, const std::string& cmd, const std::st return true; } + +bool TalkAction::software(Creature* creature, const std::string&, const std::string&) +{ + Player* player = creature->getPlayer(); + if(!player) + return false; + + std::stringstream s; + s << SOFTWARE_NAME << ", version " << SOFTWARE_VERSION << " (" << SOFTWARE_CODENAME << ")" << std::endl; + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, s.str()); + + s.str(""); + s << "Compiled at: " << __DATE__ << ", " << __TIME__ << "." << std::endl; + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, s.str()); + + s.str(""); + s << "Libraries:" << std::endl + << "--------------------" << std::endl + << "Platform: " << BOOST_PLATFORM << std::endl + << "Compiler: " << BOOST_COMPILER << std::endl + << "Boost: " << BOOST_VERSION << std::endl + << "ASIO: " << BOOST_ASIO_VERSION << std::endl + << "XML: " << XML_DEFAULT_VERSION << std::endl + << "Lua: " << LUA_VERSION << std::endl; + + player->sendTextMessage(MSG_STATUS_CONSOLE_BLUE, s.str()); + return true; +} diff --git a/talkaction.h b/talkaction.h index f20c80a..93b7f5c 100644 --- a/talkaction.h +++ b/talkaction.h @@ -49,6 +49,7 @@ class TalkActions : public BaseEvents inline TalkActionsMap::const_iterator getLastTalk() const {return talksMap.end();} protected: + TalkAction* defaultTalkAction; TalkActionsMap talksMap; virtual std::string getScriptBaseName() const {return "talkactions";} @@ -57,8 +58,8 @@ class TalkActions : public BaseEvents virtual Event* getEvent(const std::string& nodeName); virtual bool registerEvent(Event* event, xmlNodePtr p, bool override); - virtual LuaScriptInterface& getInterface() {return m_interface;} - LuaScriptInterface m_interface; + virtual LuaInterface& getInterface() {return m_interface;} + LuaInterface m_interface; }; typedef bool (TalkFunction)(Creature* creature, const std::string& words, const std::string& param); @@ -66,7 +67,7 @@ class TalkAction : public Event { public: TalkAction(const TalkAction* copy); - TalkAction(LuaScriptInterface* _interface); + TalkAction(LuaInterface* _interface); virtual ~TalkAction() {} virtual bool configureEvent(xmlNodePtr p); @@ -74,6 +75,7 @@ class TalkAction : public Event int32_t executeSay(Creature* creature, const std::string& words, std::string param, uint16_t channel); + std::string getFunctionName() const {return m_functionName;} std::string getWords() const {return m_words;} void setWords(const std::string& words) {m_words = words;} @@ -88,6 +90,12 @@ class TalkAction : public Event bool isHidden() const {return m_hidden;} bool isSensitive() const {return m_sensitive;} + bool hasGroups() const {return !m_groups.empty();} + bool hasGroup(int32_t value) const {return std::find(m_groups.begin(), m_groups.end(), value) != m_groups.end();} + + IntegerVec::const_iterator getGroupsBegin() const {return m_groups.begin();} + IntegerVec::const_iterator getGroupsEnd() const {return m_groups.end();} + protected: virtual std::string getScriptEventName() const {return "onSay";} virtual std::string getScriptEventParams() const {return "cid, words, param, channel";} @@ -103,15 +111,16 @@ class TalkAction : public Event static TalkFunction thingProporties; static TalkFunction banishmentInfo; static TalkFunction diagnostics; - static TalkFunction addSkill; static TalkFunction ghost; + static TalkFunction software; - std::string m_words; + std::string m_words, m_functionName; TalkFunction* m_function; TalkActionFilter m_filter; uint32_t m_access; int32_t m_channel; bool m_logged, m_hidden, m_sensitive; StringVec m_exceptions; + IntegerVec m_groups; }; #endif diff --git a/teleport.cpp b/teleport.cpp index 1b157f3..44ef4a7 100644 --- a/teleport.cpp +++ b/teleport.cpp @@ -27,7 +27,7 @@ Attr_ReadValue Teleport::readAttr(AttrTypes_t attr, PropStream& propStream) return Item::readAttr(attr, propStream); TeleportDest* dest; - if(!propStream.GET_STRUCT(dest)) + if(!propStream.getStruct(dest)) return ATTR_READ_ERROR; setDestination(Position(dest->_x, dest->_y, dest->_z)); @@ -37,43 +37,36 @@ Attr_ReadValue Teleport::readAttr(AttrTypes_t attr, PropStream& propStream) bool Teleport::serializeAttr(PropWriteStream& propWriteStream) const { bool ret = Item::serializeAttr(propWriteStream); - propWriteStream.ADD_UCHAR(ATTR_TELE_DEST); + propWriteStream.addByte(ATTR_TELE_DEST); TeleportDest dest; dest._x = destination.x; dest._y = destination.y; dest._z = destination.z; - propWriteStream.ADD_VALUE(dest); + propWriteStream.addType(dest); return ret; } -void Teleport::__addThing(Creature* actor, int32_t index, Thing* thing) +void Teleport::__addThing(Creature* actor, int32_t, Thing* thing) { + if(!thing || thing->isRemoved()) + return; + Tile* destTile = g_game.getTile(destination); if(!destTile) return; if(Creature* creature = thing->getCreature()) { + g_game.addMagicEffect(creature->getPosition(), MAGIC_EFFECT_TELEPORT, creature->isGhost()); creature->getTile()->moveCreature(actor, creature, destTile); g_game.addMagicEffect(destTile->getPosition(), MAGIC_EFFECT_TELEPORT, creature->isGhost()); } else if(Item* item = thing->getItem()) - g_game.internalMoveItem(actor, getTile(), destTile, INDEX_WHEREEVER, item, item->getItemCount(), NULL); -} - -void Teleport::postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link /*= LINK_OWNER*/) -{ - if(getParent()) - getParent()->postAddNotification(actor, thing, oldParent, index, LINK_PARENT); -} - -void Teleport::postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link /*= LINK_OWNER*/) -{ - if(getParent()) - getParent()->postRemoveNotification(actor, thing, newParent, - index, isCompleteRemoval, LINK_PARENT); -} + { + g_game.addMagicEffect(item->getPosition(), MAGIC_EFFECT_TELEPORT); + g_game.internalMoveItem(actor, item->getTile(), destTile, INDEX_WHEREEVER, item, item->getItemCount(), NULL); + g_game.addMagicEffect(destTile->getPosition(), MAGIC_EFFECT_TELEPORT); + } +} \ No newline at end of file diff --git a/teleport.h b/teleport.h index eaa0cfc..e6bd603 100644 --- a/teleport.h +++ b/teleport.h @@ -48,27 +48,35 @@ class Teleport : public Item, public Cylinder virtual Creature* getCreature() {return NULL;} virtual const Creature* getCreature() const {return NULL;} - virtual ReturnValue __queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const {return RET_NOTPOSSIBLE;} - virtual ReturnValue __queryMaxCount(int32_t index, const Thing* thing, uint32_t count, - uint32_t& maxQueryCount, uint32_t flags) const {return RET_NOTPOSSIBLE;} - virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, - uint32_t flags) const {return RET_NOERROR;} - virtual Cylinder* __queryDestination(int32_t& index, const Thing* thing, Item** destItem, - uint32_t& flags) {return this;} + virtual ReturnValue __queryAdd(int32_t, const Thing*, uint32_t, + uint32_t, Creature* = NULL) const {return RET_NOTPOSSIBLE;} + virtual ReturnValue __queryMaxCount(int32_t, const Thing*, uint32_t, + uint32_t&, uint32_t) const {return RET_NOTPOSSIBLE;} + virtual ReturnValue __queryRemove(const Thing*, uint32_t, + uint32_t, Creature* = NULL) const {return RET_NOERROR;} + virtual Cylinder* __queryDestination(int32_t&, const Thing*, Item**, + uint32_t&) {return this;} virtual void __addThing(Creature* actor, Thing* thing) {return __addThing(actor, 0, thing);} virtual void __addThing(Creature* actor, int32_t index, Thing* thing); - virtual void __updateThing(Thing* thing, uint16_t itemId, uint32_t count) {} - virtual void __replaceThing(uint32_t index, Thing* thing) {} + virtual void __updateThing(Thing*, uint16_t, uint32_t) {} + virtual void __replaceThing(uint32_t, Thing*) {} - virtual void __removeThing(Thing* thing, uint32_t count) {} + virtual void __removeThing(Thing*, uint32_t) {} virtual void postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link = LINK_OWNER); + int32_t index, CylinderLink_t) + { + if(getParent()) + getParent()->postAddNotification(actor, thing, oldParent, index, LINK_PARENT); + } virtual void postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link = LINK_OWNER); + int32_t index, bool isCompleteRemoval, CylinderLink_t) + { + if(getParent()) + getParent()->postRemoveNotification(actor, thing, newParent, index, isCompleteRemoval, LINK_PARENT); + } private: Position destination; diff --git a/textlogger.cpp b/textlogger.cpp index 927d1e4..3730610 100644 --- a/textlogger.cpp +++ b/textlogger.cpp @@ -17,20 +17,36 @@ #include "otpch.h" #include "textlogger.h" +#include "manager.h" +#include "dispatcher.h" + +#include "configmanager.h" +#include "game.h" #include "tools.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) -#include "gui.h" -#endif + +extern ConfigManager g_config; +extern Game g_game; void Logger::open() { + std::string path = g_config.getString(ConfigManager::OUTPUT_LOG); + if(path.length() < 3) + path = ""; + else if(path[0] != '/' && path[1] != ':') + path = getFilePath(FILE_TYPE_LOG, path); + m_files[LOGFILE_ADMIN] = fopen(getFilePath(FILE_TYPE_LOG, "admin.log").c_str(), "a"); - m_files[LOGFILE_CLIENT_ASSERTION] = fopen(getFilePath(FILE_TYPE_LOG, "client_assertions.log").c_str(), "a"); + if(!path.empty()) + m_files[LOGFILE_OUTPUT] = fopen(path.c_str(), (g_config.getBool(ConfigManager::TRUNCATE_LOG) ? "w" : "a")); + + m_files[LOGFILE_ASSERTIONS] = fopen(getFilePath(FILE_TYPE_LOG, "client_assertions.log").c_str(), "a"); + m_loaded = true; } void Logger::close() { - for(uint8_t i = 0; i <= LOGFILE_LAST; i++) + m_loaded = false; + for(uint8_t i = 0; i <= LOGFILE_LAST; ++i) { if(m_files[i]) fclose(m_files[i]); @@ -39,7 +55,7 @@ void Logger::close() void Logger::iFile(LogFile_t file, std::string output, bool newLine) { - if(!m_files[file]) + if(!m_loaded || !m_files[file]) return; internal(m_files[file], output, newLine); @@ -69,6 +85,9 @@ void Logger::internal(FILE* file, std::string output, bool newLine) void Logger::log(const char* func, LogType_t type, std::string message, std::string channel/* = ""*/, bool newLine/* = true*/) { + if(!m_loaded) + return; + std::stringstream ss; ss << "[" << formatDate() << "]" << " ("; switch(type) @@ -94,62 +113,47 @@ void Logger::log(const char* func, LogType_t type, std::string message, std::str } ss << " - " << func << ") "; - if(!channel.empty()) ss << channel << ": "; ss << message; iFile(LOGFILE_ADMIN, ss.str(), newLine); } -#if defined(WINDOWS) && !defined(__CONSOLE__) -GUILogger::GUILogger() +OutputHandler::OutputHandler() { - out = std::cout.rdbuf(); - err = std::cerr.rdbuf(); - log = std::clog.rdbuf(); - m_displayDate = true; + log = std::clog.rdbuf(this); + err = std::cerr.rdbuf(this); } -GUILogger::~GUILogger() +OutputHandler::~OutputHandler() { - std::cout.rdbuf(out); - std::cerr.rdbuf(err); std::clog.rdbuf(log); + std::cerr.rdbuf(err); } -int32_t GUILogger::overflow(int32_t c) +std::streambuf::int_type OutputHandler::overflow(std::streambuf::int_type c/* = traits_type::eof()*/) { - if(c == '\n') - { - GUI::getInstance()->m_logText += "\r\n"; - SendMessage(GetDlgItem(GUI::getInstance()->m_mainWindow, ID_LOG), WM_SETTEXT, 0, (LPARAM)GUI::getInstance()->m_logText.c_str()); - SendMessage(GUI::getInstance()->m_logWindow, EM_LINESCROLL, 0, ++GUI::getInstance()->m_lineCount); - - char buffer[85]; - sprintf(buffer, "logs/server/%s.log", formatDateShort().c_str()); - if(FILE* file = fopen(buffer, "a")) - { - fprintf(file, "[%s] %s\n", formatDate().c_str(), m_cache.c_str()); - fclose(file); - m_cache = ""; - } - - m_displayDate = true; - } - else + m_cache += c; + if(c != '\n' && c != '\r') + return c; + + if(m_cache.size() > 1) + std::cout << "[" << formatTime(0, true) << "] "; + + std::cout.write(m_cache.c_str(), m_cache.size()); + if(Logger::getInstance()->isLoaded()) { - if(m_displayDate) - { - GUI::getInstance()->m_logText += std::string("[" + formatDate() +"] ").c_str(); - m_displayDate = false; - } - - GUI::getInstance()->m_logText += (char)c; - m_cache += c; + std::stringstream s; + if(m_cache.size() > 1) + s << "[" << formatDate() << "] "; + + s.write(m_cache.c_str(), m_cache.size()); + Logger::getInstance()->iFile(LOGFILE_OUTPUT, s.str(), false); + if(g_game.isRunning()) + Dispatcher::getInstance().addTask(createTask(boost::bind(&Manager::output, Manager::getInstance(), m_cache))); } + m_cache.clear(); return c; } -#endif - diff --git a/textlogger.h b/textlogger.h index 4b323a8..ccc8112 100644 --- a/textlogger.h +++ b/textlogger.h @@ -19,17 +19,13 @@ #define __TEXTLOGGER__ #include "otsystem.h" -#if defined(WINDOWS) && !defined(__CONSOLE__) -#include -#include -#endif - enum LogFile_t { LOGFILE_FIRST = 0, LOGFILE_ADMIN = LOGFILE_FIRST, - LOGFILE_CLIENT_ASSERTION = 1, - LOGFILE_LAST = LOGFILE_CLIENT_ASSERTION + LOGFILE_OUTPUT = 1, + LOGFILE_ASSERTIONS = 2, + LOGFILE_LAST = LOGFILE_ASSERTIONS }; enum LogType_t @@ -53,37 +49,40 @@ class Logger void open(); void close(); + bool isLoaded() const {return m_loaded;} + void iFile(LogFile_t file, std::string output, bool newLine); void eFile(std::string file, std::string output, bool newLine); void log(const char* func, LogType_t type, std::string message, std::string channel = "", bool newLine = true); private: - Logger() {} + Logger() {m_loaded = false;} void internal(FILE* file, std::string output, bool newLine); FILE* m_files[LOGFILE_LAST + 1]; + bool m_loaded; }; #define LOG_MESSAGE(type, message, channel) \ - Logger::getInstance()->log(__OTSERV_FUNCTION__, type, message, channel); + Logger::getInstance()->log(__PRETTY_FUNCTION__, type, message, channel); -#if defined(WINDOWS) && !defined(__CONSOLE__) -class GUILogger : public std::streambuf +class OutputHandler : public std::streambuf { public: - GUILogger(); - virtual ~GUILogger(); - - std::streambuf* out; - std::streambuf* err; - std::streambuf* log; + virtual ~OutputHandler(); + static OutputHandler* getInstance() + { + static OutputHandler instance; + return &instance; + } protected: - int32_t overflow(int32_t c); + OutputHandler(); + std::streambuf::int_type overflow(std::streambuf::int_type c = traits_type::eof()); - bool m_displayDate; + std::streambuf* log; + std::streambuf* err; std::string m_cache; }; #endif -#endif diff --git a/forgottenserver.s3db b/theforgottenserver.s3db similarity index 68% rename from forgottenserver.s3db rename to theforgottenserver.s3db index cc10d40d45a1d701b963e39219a0abb95e83127b..66a044631e7e04f92f9d994f17e1b9b85d7394b0 100644 GIT binary patch literal 87040 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 diff --git a/thing.cpp b/thing.cpp index 6a2c056..c84c535 100644 --- a/thing.cpp +++ b/thing.cpp @@ -70,8 +70,7 @@ Tile* Thing::getTile() #ifdef __DEBUG_MOVESYS__ if(!cylinder) { - std::cout << "[Failure - Thing::getTile] NULL tile" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Thing::getTile] NULL tile" << std::endl; return &(Tile::nullTile); } #endif @@ -88,8 +87,7 @@ const Tile* Thing::getTile() const #ifdef __DEBUG_MOVESYS__ if(!cylinder) { - std::cout << "[Failure - Thing::getTile] NULL tile" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Thing::getTile] NULL tile" << std::endl; return &(Tile::nullTile); } #endif @@ -106,8 +104,7 @@ Position Thing::getPosition() const return tile->getPosition(); #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Thing::getTile] NULL tile" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Thing::getTile] NULL tile" << std::endl; #endif return Tile::nullTile.getPosition(); } diff --git a/thing.h b/thing.h index faeb5e3..2158e74 100644 --- a/thing.h +++ b/thing.h @@ -21,71 +21,70 @@ enum ReturnValue { - RET_DONTSHOWMESSAGE = 0, - RET_NOERROR = 1, - RET_NOTPOSSIBLE = 2, - RET_NOTENOUGHROOM = 3, - RET_PLAYERISPZLOCKED = 4, - RET_PLAYERISNOTINVITED = 5, - RET_CANNOTTHROW = 6, - RET_THEREISNOWAY = 7, - RET_DESTINATIONOUTOFREACH = 8, - RET_CREATUREBLOCK = 9, - RET_NOTMOVEABLE = 10, - RET_DROPTWOHANDEDITEM = 11, - RET_BOTHHANDSNEEDTOBEFREE = 12, - RET_CANONLYUSEONEWEAPON = 13, - RET_NEEDEXCHANGE = 14, - RET_CANNOTBEDRESSED = 15, - RET_PUTTHISOBJECTINYOURHAND = 16, - RET_PUTTHISOBJECTINBOTHHANDS = 17, - RET_TOOFARAWAY = 18, - RET_FIRSTGODOWNSTAIRS = 19, - RET_FIRSTGOUPSTAIRS = 20, - RET_CONTAINERNOTENOUGHROOM = 21, - RET_NOTENOUGHCAPACITY = 22, - RET_CANNOTPICKUP = 23, - RET_THISISIMPOSSIBLE = 24, - RET_DEPOTISFULL = 25, - RET_CREATUREDOESNOTEXIST = 26, - RET_CANNOTUSETHISOBJECT = 27, - RET_PLAYERWITHTHISNAMEISNOTONLINE = 28, - RET_NOTREQUIREDLEVELTOUSERUNE = 29, - RET_YOUAREALREADYTRADING = 30, - RET_THISPLAYERISALREADYTRADING = 31, - RET_YOUMAYNOTLOGOUTDURINGAFIGHT = 32, - RET_DIRECTPLAYERSHOOT = 33, - RET_NOTENOUGHLEVEL = 34, - RET_NOTENOUGHMAGICLEVEL = 35, - RET_NOTENOUGHMANA = 36, - RET_NOTENOUGHSOUL = 37, - RET_YOUAREEXHAUSTED = 38, - RET_PLAYERISNOTREACHABLE = 39, - RET_CANONLYUSETHISRUNEONCREATURES = 40, - RET_ACTIONNOTPERMITTEDINPROTECTIONZONE = 41, - RET_YOUMAYNOTATTACKTHISPLAYER = 42, - RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE = 43, - RET_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE = 44, - RET_YOUMAYNOTATTACKTHISCREATURE = 45, - RET_YOUCANONLYUSEITONCREATURES = 46, - RET_CREATUREISNOTREACHABLE = 47, - RET_TURNSECUREMODETOATTACKUNMARKEDPLAYERS = 48, - RET_YOUNEEDPREMIUMACCOUNT = 49, - RET_YOUNEEDTOLEARNTHISSPELL = 50, - RET_YOURVOCATIONCANNOTUSETHISSPELL = 51, - RET_YOUNEEDAWEAPONTOUSETHISSPELL = 52, - RET_PLAYERISPZLOCKEDLEAVEPVPZONE = 53, - RET_PLAYERISPZLOCKEDENTERPVPZONE = 54, - RET_ACTIONNOTPERMITTEDINANOPVPZONE = 55, - RET_YOUCANNOTLOGOUTHERE = 56, - RET_YOUNEEDAMAGICITEMTOCASTSPELL = 57, - RET_CANNOTCONJUREITEMHERE = 58, - RET_YOUNEEDTOSPLITYOURSPEARS = 59, - RET_NAMEISTOOAMBIGUOUS = 60, - RET_CANONLYUSEONESHIELD = 61, - RET_YOUARENOTTHEOWNER = 62, - RET_YOUMAYNOTCASTAREAONBLACKSKULL = 63, - RET_TILEISFULL = 64 + RET_NOERROR = 0, + RET_NOTPOSSIBLE = 1, + RET_NOTENOUGHROOM = 2, + RET_PLAYERISPZLOCKED = 3, + RET_PLAYERISNOTINVITED = 4, + RET_CANNOTTHROW = 5, + RET_THEREISNOWAY = 6, + RET_DESTINATIONOUTOFREACH = 7, + RET_CREATUREBLOCK = 8, + RET_NOTMOVABLE = 9, + RET_DROPTWOHANDEDITEM = 10, + RET_BOTHHANDSNEEDTOBEFREE = 11, + RET_CANONLYUSEONEWEAPON = 12, + RET_NEEDEXCHANGE = 13, + RET_CANNOTBEDRESSED = 14, + RET_PUTTHISOBJECTINYOURHAND = 15, + RET_PUTTHISOBJECTINBOTHHANDS = 16, + RET_TOOFARAWAY = 17, + RET_FIRSTGODOWNSTAIRS = 18, + RET_FIRSTGOUPSTAIRS = 19, + RET_CONTAINERNOTENOUGHROOM = 20, + RET_NOTENOUGHCAPACITY = 21, + RET_CANNOTPICKUP = 22, + RET_THISISIMPOSSIBLE = 23, + RET_DEPOTISFULL = 24, + RET_CREATUREDOESNOTEXIST = 25, + RET_CANNOTUSETHISOBJECT = 26, + RET_PLAYERWITHTHISNAMEISNOTONLINE = 27, + RET_NOTREQUIREDLEVELTOUSERUNE = 28, + RET_YOUAREALREADYTRADING = 29, + RET_THISPLAYERISALREADYTRADING = 30, + RET_YOUMAYNOTLOGOUTDURINGAFIGHT = 31, + RET_DIRECTPLAYERSHOOT = 32, + RET_NOTENOUGHLEVEL = 33, + RET_NOTENOUGHMAGICLEVEL = 34, + RET_NOTENOUGHMANA = 35, + RET_NOTENOUGHSOUL = 36, + RET_YOUAREEXHAUSTED = 37, + RET_PLAYERISNOTREACHABLE = 38, + RET_CANONLYUSETHISRUNEONCREATURES = 39, + RET_ACTIONNOTPERMITTEDINPROTECTIONZONE = 40, + RET_YOUMAYNOTATTACKTHISPLAYER = 41, + RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE = 42, + RET_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE = 43, + RET_YOUMAYNOTATTACKTHISCREATURE = 44, + RET_YOUCANONLYUSEITONCREATURES = 45, + RET_CREATUREISNOTREACHABLE = 46, + RET_TURNSECUREMODETOATTACKUNMARKEDPLAYERS = 47, + RET_YOUNEEDPREMIUMACCOUNT = 48, + RET_YOUNEEDTOLEARNTHISSPELL = 49, + RET_YOURVOCATIONCANNOTUSETHISSPELL = 50, + RET_YOUNEEDAWEAPONTOUSETHISSPELL = 51, + RET_PLAYERISPZLOCKEDLEAVEPVPZONE = 52, + RET_PLAYERISPZLOCKEDENTERPVPZONE = 53, + RET_ACTIONNOTPERMITTEDINANOPVPZONE = 54, + RET_YOUCANNOTLOGOUTHERE = 55, + RET_YOUNEEDAMAGICITEMTOCASTSPELL = 56, + RET_CANNOTCONJUREITEMHERE = 57, + RET_TILEISFULL = 58, + RET_NAMEISTOOAMBIGUOUS = 59, + RET_CANONLYUSEONESHIELD = 60, + RET_YOUARENOTTHEOWNER = 61, + RET_YOUMAYNOTCASTAREAONBLACKSKULL = 62, + RET_NOTENOUGHSKILL = 63 }; class Tile; @@ -105,7 +104,7 @@ class Thing void unRef() { --refCount; - if(refCount <= 0) + if(!refCount) delete this; } @@ -135,6 +134,6 @@ class Thing private: Cylinder* parent; - int32_t refCount; + int16_t refCount; }; #endif diff --git a/tile.cpp b/tile.cpp index f91df4b..07a3de7 100644 --- a/tile.cpp +++ b/tile.cpp @@ -76,22 +76,6 @@ bool Tile::hasProperty(Item* exclude, enum ITEMPROPERTY prop) const return false; } -HouseTile* Tile::getHouseTile() -{ - if(isHouseTile()) - return static_cast(this); - - return NULL; -} - -const HouseTile* Tile::getHouseTile() const -{ - if(isHouseTile()) - return static_cast(this); - - return NULL; -} - bool Tile::hasHeight(uint32_t n) const { uint32_t height = 0; @@ -119,13 +103,19 @@ bool Tile::hasHeight(uint32_t n) const return false; } -bool Tile::isSwimmingPool(bool checkPz /*= true*/) const +bool Tile::isFull() const { - if(TrashHolder* trashHolder = getTrashHolder()) - return trashHolder->getEffect() == MAGIC_EFFECT_LOSE_ENERGY && (!checkPz || - getZone() == ZONE_PROTECTION || getZone() == ZONE_NOPVP); + uint32_t limit = 0; + if(hasFlag(TILESTATE_PROTECTIONZONE)) + limit = g_config.getNumber(ConfigManager::PROTECTION_TILE_LIMIT); + else + limit = g_config.getNumber(ConfigManager::TILE_LIMIT); - return false; + if(!limit) + limit = 0xFFFF; + + const TileItemVector* items = getItemList(); + return items && items->size() >= limit; } uint32_t Tile::getCreatureCount() const @@ -154,7 +144,7 @@ uint32_t Tile::getTopItemCount() const uint32_t Tile::getDownItemCount() const { - if(const TileItemVector* items =getItemList()) + if(const TileItemVector* items = getItemList()) return items->getDownItemCount(); return 0; @@ -297,8 +287,8 @@ Item* Tile::getItemByTopOrder(uint32_t topOrder) { if(TileItemVector* items = getItemList()) { - ItemVector::reverse_iterator eit = ItemVector::reverse_iterator(items->getBeginTopItem()); - for(ItemVector::reverse_iterator it = ItemVector::reverse_iterator(items->getEndTopItem()); it != eit; ++it) + for(ItemVector::reverse_iterator it = ItemVector::reverse_iterator(items->getEndTopItem()), + end = ItemVector::reverse_iterator(items->getBeginTopItem()); it != end; ++it) { if(Item::items[(*it)->getID()].alwaysOnTopOrder == (int32_t)topOrder) return (*it); @@ -365,9 +355,7 @@ const Creature* Tile::getTopVisibleCreature(const Creature* creature) const void Tile::onAddTileItem(Item* item) { updateTileFlags(item, false); - const Position& cylinderMapPos = pos; - - const SpectatorVec& list = g_game.getSpectators(cylinderMapPos); + const SpectatorVec& list = g_game.getSpectators(pos); SpectatorVec::const_iterator it; //send to client @@ -375,19 +363,17 @@ void Tile::onAddTileItem(Item* item) for(it = list.begin(); it != list.end(); ++it) { if((tmpPlayer = (*it)->getPlayer())) - tmpPlayer->sendAddTileItem(this, cylinderMapPos, item); + tmpPlayer->sendAddTileItem(this, pos, item); } //event methods for(it = list.begin(); it != list.end(); ++it) - (*it)->onAddTileItem(this, cylinderMapPos, item); + (*it)->onAddTileItem(this, pos, item); } void Tile::onUpdateTileItem(Item* oldItem, const ItemType& oldType, Item* newItem, const ItemType& newType) { - const Position& cylinderMapPos = pos; - - const SpectatorVec& list = g_game.getSpectators(cylinderMapPos); + const SpectatorVec& list = g_game.getSpectators(pos); SpectatorVec::const_iterator it; //send to client @@ -395,19 +381,17 @@ void Tile::onUpdateTileItem(Item* oldItem, const ItemType& oldType, Item* newIte for(it = list.begin(); it != list.end(); ++it) { if((tmpPlayer = (*it)->getPlayer())) - tmpPlayer->sendUpdateTileItem(this, cylinderMapPos, oldItem, newItem); + tmpPlayer->sendUpdateTileItem(this, pos, oldItem, newItem); } //event methods for(it = list.begin(); it != list.end(); ++it) - (*it)->onUpdateTileItem(this, cylinderMapPos, oldItem, oldType, newItem, newType); + (*it)->onUpdateTileItem(this, pos, oldItem, oldType, newItem, newType); } -void Tile::onRemoveTileItem(const SpectatorVec& list, std::vector& oldStackposVector, Item* item) +void Tile::onRemoveTileItem(const SpectatorVec& list, std::vector& oldStackposVector, Item* item) { updateTileFlags(item, true); - const Position& cylinderMapPos = pos; - const ItemType& iType = Item::items[item->getID()]; SpectatorVec::const_iterator it; @@ -417,19 +401,17 @@ void Tile::onRemoveTileItem(const SpectatorVec& list, std::vector& old for(it = list.begin(); it != list.end(); ++it) { if((tmpPlayer = (*it)->getPlayer())) - tmpPlayer->sendRemoveTileItem(this, cylinderMapPos, oldStackposVector[i++], item); + tmpPlayer->sendRemoveTileItem(this, pos, oldStackposVector[i++], item); } //event methods for(it = list.begin(); it != list.end(); ++it) - (*it)->onRemoveTileItem(this, cylinderMapPos, iType, item); + (*it)->onRemoveTileItem(this, pos, iType, item); } void Tile::onUpdateTile() { - const Position& cylinderMapPos = pos; - - const SpectatorVec& list = g_game.getSpectators(cylinderMapPos); + const SpectatorVec& list = g_game.getSpectators(pos); SpectatorVec::const_iterator it; //send to client @@ -437,12 +419,12 @@ void Tile::onUpdateTile() for(it = list.begin(); it != list.end(); ++it) { if((tmpPlayer = (*it)->getPlayer())) - tmpPlayer->sendUpdateTile(this, cylinderMapPos); + tmpPlayer->sendUpdateTile(this, pos); } //event methods for(it = list.begin(); it != list.end(); ++it) - (*it)->onUpdateTile(this, cylinderMapPos); + (*it)->onUpdateTile(this, pos); } void Tile::moveCreature(Creature* actor, Creature* creature, Cylinder* toCylinder, bool forceTeleport/* = false*/) @@ -459,7 +441,7 @@ void Tile::moveCreature(Creature* actor, Creature* creature, Cylinder* toCylinde if(forceTeleport || !newTile->ground || !Position::areInRange<1,1,0>(pos, newPos)) teleport = true; - std::vector oldStackposVector; + std::vector oldStackposVector; Player* tmpPlayer = NULL; for(it = list.begin(); it != list.end(); ++it) { @@ -496,7 +478,7 @@ void Tile::moveCreature(Creature* actor, Creature* creature, Cylinder* toCylinde int32_t i = 0; for(it = list.begin(); it != list.end(); ++it) { - if((tmpPlayer = (*it)->getPlayer()) && tmpPlayer->canSeeCreature(creature)) + if((tmpPlayer = (*it)->getPlayer())) tmpPlayer->sendCreatureMove(creature, newTile, newPos, this, pos, oldStackposVector[i++], teleport); } @@ -508,8 +490,8 @@ void Tile::moveCreature(Creature* actor, Creature* creature, Cylinder* toCylinde newTile->postAddNotification(actor, creature, this, newStackpos); } -ReturnValue Tile::__queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const +ReturnValue Tile::__queryAdd(int32_t, const Thing* thing, uint32_t, + uint32_t flags, Creature*) const { const CreatureVector* creatures = getCreatures(); const TileItemVector* items = getItemList(); @@ -537,7 +519,7 @@ ReturnValue Tile::__queryAdd(int32_t index, const Thing* thing, uint32_t count, if(monster->canPushCreatures() && !monster->isSummon()) { - if(creatures) + if(creatures && !creatures->empty()) { Creature* tmp = NULL; for(uint32_t i = 0; i < creatures->size(); ++i) @@ -546,10 +528,8 @@ ReturnValue Tile::__queryAdd(int32_t index, const Thing* thing, uint32_t count, if(creature->canWalkthrough(tmp)) continue; - if(!tmp->getMonster() || !tmp->isPushable() || - (tmp->getMonster()->isSummon() && - tmp->getMonster()->isPlayerSummon())) - return RET_NOTPOSSIBLE; + if(!tmp->getMonster() || !tmp->isPushable() || tmp->isPlayerSummon()) + return RET_NOTENOUGHROOM; //NOTPOSSIBLE } } } @@ -558,7 +538,7 @@ ReturnValue Tile::__queryAdd(int32_t index, const Thing* thing, uint32_t count, for(CreatureVector::const_iterator cit = creatures->begin(); cit != creatures->end(); ++cit) { if(!creature->canWalkthrough(*cit)) - return RET_NOTENOUGHROOM; + return RET_NOTENOUGHROOM; //NOTPOSSIBLE } } @@ -569,51 +549,55 @@ ReturnValue Tile::__queryAdd(int32_t index, const Thing* thing, uint32_t count, return RET_NOTPOSSIBLE; if((hasFlag(TILESTATE_BLOCKSOLID) || (hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_NOFIELDBLOCKPATH))) - && (!(monster->canPushItems() || hasBitSet(FLAG_IGNOREBLOCKITEM, flags)))) + && !(monster->canPushItems() || hasBitSet(FLAG_IGNOREBLOCKITEM, flags))) return RET_NOTPOSSIBLE; + if(!items) // Do not seek for fields if there are no items + return RET_NOERROR; + MagicField* field = getFieldItem(); - if(field && !field->isBlocking(monster)) - { - CombatType_t combatType = field->getCombatType(); - //There is 3 options for a monster to enter a magic field - //1) Monster is immune - if(!monster->isImmune(combatType)) - { - //1) Monster is "strong" enough to handle the damage - //2) Monster is already afflicated by this type of condition - if(!hasBitSet(FLAG_IGNOREFIELDDAMAGE, flags)) - return RET_NOTPOSSIBLE; + if(!field) + return RET_NOERROR; - if(!monster->canPushItems() && !monster->hasCondition( - Combat::DamageToConditionType(combatType), false)) - return RET_NOTPOSSIBLE; - } - } + if(field->isBlocking(creature)) + return RET_NOTPOSSIBLE; - return RET_NOERROR; + CombatType_t combatType = field->getCombatType(); + //There is 3 options for a monster to enter a magic field + //1) Monster is immune + if(monster->isImmune(combatType)) + return RET_NOERROR; + + //1) Monster is "strong" enough to handle the damage + //2) Monster is already afflicated by this type of condition + if(!hasBitSet(FLAG_IGNOREFIELDDAMAGE, flags)) + return RET_NOTPOSSIBLE; + + return !monster->hasCondition(Combat::DamageToConditionType(combatType), -1, false) && + (!monster->canPushItems() || !monster->hasRecentBattle()) ? RET_NOTPOSSIBLE : RET_NOERROR; } - else if(const Player* player = creature->getPlayer()) + + if(const Player* player = creature->getPlayer()) { if(creatures && !creatures->empty() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags)) { for(CreatureVector::const_iterator cit = creatures->begin(); cit != creatures->end(); ++cit) { if(!creature->canWalkthrough(*cit)) - return RET_NOTENOUGHROOM; //RET_NOTPOSSIBLE + return RET_NOTENOUGHROOM; //NOTPOSSIBLE } } if(!player->getParent() && hasFlag(TILESTATE_NOLOGOUT)) //player is trying to login to a "no logout" tile return RET_NOTPOSSIBLE; - if(player->isPzLocked() && !player->getTile()->hasFlag(TILESTATE_PVPZONE) && hasFlag(TILESTATE_PVPZONE)) //player is trying to enter a pvp zone while being pz-locked + if(player->isPzLocked() && !player->getTile()->hasFlag(TILESTATE_HARDCOREZONE) && hasFlag(TILESTATE_HARDCOREZONE)) //player is trying to enter a pvp zone while being pz-locked return RET_PLAYERISPZLOCKEDENTERPVPZONE; - if(player->isPzLocked() && player->getTile()->hasFlag(TILESTATE_PVPZONE) && !hasFlag(TILESTATE_PVPZONE)) //player is trying to leave a pvp zone while being pz-locked + if(player->isPzLocked() && player->getTile()->hasFlag(TILESTATE_HARDCOREZONE) && !hasFlag(TILESTATE_HARDCOREZONE)) //player is trying to leave a pvp zone while being pz-locked return RET_PLAYERISPZLOCKEDLEAVEPVPZONE; - if(hasFlag(TILESTATE_NOPVPZONE) && player->isPzLocked()) + if(hasFlag(TILESTATE_OPTIONALZONE) && player->isPzLocked()) return RET_PLAYERISPZLOCKED; if(hasFlag(TILESTATE_PROTECTIONZONE) && player->isPzLocked()) @@ -624,7 +608,7 @@ ReturnValue Tile::__queryAdd(int32_t index, const Thing* thing, uint32_t count, for(CreatureVector::const_iterator cit = creatures->begin(); cit != creatures->end(); ++cit) { if(!creature->canWalkthrough(*cit)) - return RET_NOTENOUGHROOM; + return RET_NOTENOUGHROOM; //NOTPOSSIBLE } } @@ -634,55 +618,47 @@ ReturnValue Tile::__queryAdd(int32_t index, const Thing* thing, uint32_t count, if(field && field->isBlocking(creature)) return RET_NOTPOSSIBLE; - if(!hasBitSet(FLAG_IGNOREBLOCKITEM, flags)) - { - //If the FLAG_IGNOREBLOCKITEM bit isn't set we dont have to iterate every single item - if(hasFlag(TILESTATE_BLOCKSOLID)) - return RET_NOTENOUGHROOM; - } - else + if(hasBitSet(FLAG_IGNOREBLOCKITEM, flags)) //if the FLAG_IGNOREBLOCKITEM bit isn't set we dont have to iterate every single item { //FLAG_IGNOREBLOCKITEM is set if(ground) { const ItemType& iType = Item::items[ground->getID()]; - if(ground->isBlocking(creature) && (!iType.moveable || (ground->isLoadedFromMap() && - (ground->getUniqueId() || (ground->getActionId() - && ground->getContainer()))))) + if(ground->isBlocking(creature) && (!iType.movable || (ground->isLoadedFromMap() && + (ground->getUniqueId() || (ground->getActionId() && ground->getContainer()))))) return RET_NOTPOSSIBLE; } if(const TileItemVector* items = getItemList()) { - Item* iItem = NULL; for(ItemVector::const_iterator it = items->begin(); it != items->end(); ++it) { - iItem = (*it); - const ItemType& iType = Item::items[iItem->getID()]; - if(iItem->isBlocking(creature) && (!iType.moveable || (iItem->isLoadedFromMap() && - (iItem->getUniqueId() || (iItem->getActionId() - && iItem->getContainer()))))) + const ItemType& iType = Item::items[(*it)->getID()]; + if((*it)->isBlocking(creature) && (!iType.movable || ((*it)->isLoadedFromMap() && + ((*it)->getUniqueId() || ((*it)->getActionId() && (*it)->getContainer()))))) return RET_NOTPOSSIBLE; } } } + else if(hasFlag(TILESTATE_BLOCKSOLID)) + return RET_NOTPOSSIBLE; } } else if(const Item* item = thing->getItem()) { #ifdef __DEBUG__ if(thing->getParent() == NULL && !hasBitSet(FLAG_NOLIMIT, flags)) - std::cout << "[Notice - Tile::__queryAdd] thing->getParent() == NULL" << std::endl; + std::clog << "[Notice - Tile::__queryAdd] thing->getParent() == NULL" << std::endl; #endif - if(items && items->size() >= 0xFFFF) - return RET_NOTPOSSIBLE; - if(hasBitSet(FLAG_NOLIMIT, flags)) return RET_NOERROR; - bool itemIsHangable = item->isHangable(); - if(!ground && !itemIsHangable) + if(isFull()) + return RET_TILEISFULL; + + bool isHangable = item->isHangable(); + if(!ground && !isHangable) return RET_NOTPOSSIBLE; if(creatures && !creatures->empty() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags)) @@ -690,65 +666,51 @@ ReturnValue Tile::__queryAdd(int32_t index, const Thing* thing, uint32_t count, for(CreatureVector::const_iterator cit = creatures->begin(); cit != creatures->end(); ++cit) { if(!(*cit)->isGhost() && item->isBlocking(*cit)) - return RET_NOTENOUGHROOM; + return RET_NOTENOUGHROOM; //NOTPOSSIBLE } } - if(hasFlag(TILESTATE_PROTECTIONZONE)) - { - const uint32_t itemLimit = g_config.getNumber(ConfigManager::ITEMLIMIT_PROTECTIONZONE); - if(itemLimit && getThingCount() > itemLimit) - return RET_TILEISFULL; - } - bool hasHangable = false, supportHangable = false; if(items) { - Thing* iThing = NULL; - for(uint32_t i = 0; i < getThingCount(); ++i) + for(ItemVector::const_iterator it = items->begin(); it != items->end(); ++it) { - iThing = __getThing(i); - if(const Item* iItem = iThing->getItem()) - { - const ItemType& iType = Item::items[iItem->getID()]; - if(iType.isHangable) - hasHangable = true; + const ItemType& iType = Item::items[(*it)->getID()]; + if(iType.isHangable) + hasHangable = true; - if(iType.isHorizontal || iType.isVertical) - supportHangable = true; + if(iType.isHorizontal || iType.isVertical) + supportHangable = true; - if(itemIsHangable && (iType.isHorizontal || iType.isVertical)) - continue; - else if(iType.blockSolid) - { - if(!item->isPickupable()) - return RET_NOTENOUGHROOM; + if((isHangable && (iType.isHorizontal || iType.isVertical)) || !(*it)->isBlocking(NULL)) + continue; - if(iType.allowPickupable) - continue; + if(!item->isPickupable()) + return RET_NOTPOSSIBLE; - if(!iType.hasHeight || iType.pickupable || iType.isBed()) - return RET_NOTENOUGHROOM; - } - } + if(iType.allowPickupable) + continue; + + if(!iType.hasHeight || iType.pickupable || iType.isBed()) + return RET_NOTPOSSIBLE; } } - if(itemIsHangable && hasHangable && supportHangable) + if(isHangable && hasHangable && supportHangable) return RET_NEEDEXCHANGE; } return RET_NOERROR; } -ReturnValue Tile::__queryMaxCount(int32_t index, const Thing* thing, uint32_t count, uint32_t& maxQueryCount, - uint32_t flags) const +ReturnValue Tile::__queryMaxCount(int32_t, const Thing*, uint32_t count, uint32_t& maxQueryCount, + uint32_t ) const { maxQueryCount = std::max((uint32_t)1, count); return RET_NOERROR; } -ReturnValue Tile::__queryRemove(const Thing* thing, uint32_t count, uint32_t flags) const +ReturnValue Tile::__queryRemove(const Thing* thing, uint32_t count, uint32_t flags, Creature*) const { int32_t index = __getIndexOfThing(thing); if(index == -1) @@ -756,50 +718,50 @@ ReturnValue Tile::__queryRemove(const Thing* thing, uint32_t count, uint32_t fla const Item* item = thing->getItem(); if(!item || !count || (item->isStackable() && count > item->getItemCount()) - || (item->isNotMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags))) + || (!item->isMovable() && !hasBitSet(FLAG_IGNORENOTMOVABLE, flags))) return RET_NOTPOSSIBLE; return RET_NOERROR; } -Cylinder* Tile::__queryDestination(int32_t& index, const Thing* thing, Item** destItem, +Cylinder* Tile::__queryDestination(int32_t&, const Thing*, Item** destItem, uint32_t& flags) { Tile* destTile = NULL; - *destItem = NULL; + Position destPosition = pos; - Position _pos = pos; + *destItem = NULL; if(floorChange(CHANGE_DOWN)) { - _pos.z++; + destPosition.z++; for(int32_t i = CHANGE_FIRST_EX; i < CHANGE_LAST; ++i) { - Position __pos = _pos; + Position tmpPosition = destPosition; Tile* tmpTile = NULL; switch(i) { case CHANGE_NORTH_EX: - __pos.y++; - if((tmpTile = g_game.getTile(__pos))) - __pos.y++; + tmpPosition.y++; + if((tmpTile = g_game.getTile(tmpPosition))) + tmpPosition.y++; break; case CHANGE_SOUTH_EX: - __pos.y--; - if((tmpTile = g_game.getTile(__pos))) - __pos.y--; + tmpPosition.y--; + if((tmpTile = g_game.getTile(tmpPosition))) + tmpPosition.y--; break; case CHANGE_EAST_EX: - __pos.x--; - if((tmpTile = g_game.getTile(__pos))) - __pos.x--; + tmpPosition.x--; + if((tmpTile = g_game.getTile(tmpPosition))) + tmpPosition.x--; break; case CHANGE_WEST_EX: - __pos.x++; - if((tmpTile = g_game.getTile(__pos))) - __pos.x++; + tmpPosition.x++; + if((tmpTile = g_game.getTile(tmpPosition))) + tmpPosition.x++; break; default: @@ -809,58 +771,80 @@ Cylinder* Tile::__queryDestination(int32_t& index, const Thing* thing, Item** de if(!tmpTile || !tmpTile->floorChange((FloorChange_t)i)) continue; - destTile = g_game.getTile(__pos); + destTile = g_game.getTile(tmpPosition); break; } if(!destTile) { - if(Tile* downTile = g_game.getTile(_pos)) + if(Tile* downTile = g_game.getTile(destPosition)) { if(downTile->floorChange(CHANGE_NORTH) || downTile->floorChange(CHANGE_NORTH_EX)) - _pos.y++; + destPosition.y++; if(downTile->floorChange(CHANGE_SOUTH) || downTile->floorChange(CHANGE_SOUTH_EX)) - _pos.y--; + destPosition.y--; if(downTile->floorChange(CHANGE_EAST) || downTile->floorChange(CHANGE_EAST_EX)) - _pos.x--; + destPosition.x--; if(downTile->floorChange(CHANGE_WEST) || downTile->floorChange(CHANGE_WEST_EX)) - _pos.x++; + destPosition.x++; - destTile = g_game.getTile(_pos); + destTile = g_game.getTile(destPosition); } } } else if(floorChange()) { - _pos.z--; - if(floorChange(CHANGE_NORTH)) - _pos.y--; - - if(floorChange(CHANGE_SOUTH)) - _pos.y++; - - if(floorChange(CHANGE_EAST)) - _pos.x++; - - if(floorChange(CHANGE_WEST)) - _pos.x--; - + destPosition.z--; + Position tmpPosition = destPosition; if(floorChange(CHANGE_NORTH_EX)) - _pos.y -= 2; + { + tmpPosition.y--; + if((destTile = g_game.getTile(tmpPosition))) + tmpPosition.y--; + } + else if(floorChange(CHANGE_SOUTH_EX)) + { + tmpPosition.y++; + if((destTile = g_game.getTile(tmpPosition))) + tmpPosition.y++; + } + else if(floorChange(CHANGE_EAST_EX)) + { + tmpPosition.x++; + if((destTile = g_game.getTile(tmpPosition))) + tmpPosition.x++; + } + else if(floorChange(CHANGE_WEST_EX)) + { + tmpPosition.x--; + if((destTile = g_game.getTile(tmpPosition))) + tmpPosition.x--; + } - if(floorChange(CHANGE_SOUTH_EX)) - _pos.y += 2; + if(!destTile) + { + if(floorChange(CHANGE_NORTH)) + destPosition.y--; - if(floorChange(CHANGE_EAST_EX)) - _pos.x += 2; + if(floorChange(CHANGE_SOUTH)) + destPosition.y++; - if(floorChange(CHANGE_WEST_EX)) - _pos.x -= 2; + if(floorChange(CHANGE_EAST)) + destPosition.x++; - destTile = g_game.getTile(_pos); + if(floorChange(CHANGE_WEST)) + destPosition.x--; + + destTile = g_game.getTile(destPosition); + } + else if(destTile->floorChange(CHANGE_DOWN)) + { + if(Tile* tmpTile = g_game.getTile(tmpPosition)) + destTile = tmpTile; + } } if(!destTile) @@ -870,15 +854,14 @@ Cylinder* Tile::__queryDestination(int32_t& index, const Thing* thing, Item** de if(destTile) { - Thing* destThing = destTile->getTopDownItem(); - if(destThing && !destThing->isRemoved()) - *destItem = destThing->getItem(); + if(Item* item = destTile->getTopDownItem()) + *destItem = item; } return destTile; } -void Tile::__addThing(Creature* actor, int32_t index, Thing* thing) +void Tile::__addThing(Creature* actor, int32_t, Thing* thing) { if(Creature* creature = thing->getCreature()) { @@ -896,8 +879,7 @@ void Tile::__addThing(Creature* actor, int32_t index, Thing* thing) if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Tile::__addThing] item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Tile::__addThing] item == NULL" << std::endl; #endif return/* RET_NOTPOSSIBLE*/; } @@ -917,19 +899,35 @@ void Tile::__addThing(Creature* actor, int32_t index, Thing* thing) { if(ground) { - const ItemType& oldType = Item::items[ground->getID()]; int32_t oldGroundIndex = __getIndexOfThing(ground); Item* oldGround = ground; - ground->setParent(NULL); - g_game.freeThing(ground); - ground = item; - updateTileFlags(oldGround, true); updateTileFlags(item, false); + ground = item; + +#ifdef __GROUND_CACHE__ + std::map::iterator it = g_game.grounds.find(oldGround); + bool erase = it == g_game.grounds.end(); + if(!erase) + { + it->second--; + erase = it->second < 1; + if(erase) + g_game.grounds.erase(it); + } + + if(erase) + { +#endif + oldGround->setParent(NULL); + g_game.freeThing(oldGround); +#ifdef __GROUND_CACHE__ + } +#endif - onUpdateTileItem(oldGround, oldType, item, Item::items[item->getID()]); postRemoveNotification(actor, oldGround, NULL, oldGroundIndex, true); + onUpdateTile(); } else { @@ -1003,7 +1001,7 @@ void Tile::__addThing(Creature* actor, int32_t index, Thing* thing) if(!(oldField = (*it)->getMagicField())) continue; - if(oldField->isReplaceable()) + if(oldField->isReplacable()) { int32_t oldFieldIndex = __getIndexOfThing(*it); __removeThing(oldField, 1); @@ -1041,8 +1039,7 @@ void Tile::__updateThing(Thing* thing, uint16_t itemId, uint32_t count) if(index == -1) { #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Tile::__updateThing] index == -1" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Tile::__updateThing] index == -1" << std::endl; #endif return/* RET_NOTPOSSIBLE*/; } @@ -1051,8 +1048,7 @@ void Tile::__updateThing(Thing* thing, uint16_t itemId, uint32_t count) if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Tile::__updateThing] item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Tile::__updateThing] item == NULL" << std::endl; #endif return/* RET_NOTPOSSIBLE*/; } @@ -1074,8 +1070,7 @@ void Tile::__replaceThing(uint32_t index, Thing* thing) if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Tile::__replaceThing] item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Tile::__replaceThing] item == NULL" << std::endl; #endif return/* RET_NOTPOSSIBLE*/; } @@ -1093,8 +1088,11 @@ void Tile::__replaceThing(uint32_t index, Thing* thing) --pos; } - TileItemVector* items = getItemList(); - if(!oldItem && items) + TileItemVector* items = NULL; + if(!oldItem) + items = getItemList(); + + if(items) { int32_t topItemSize = getTopItemCount(); if(pos < topItemSize) @@ -1117,8 +1115,7 @@ void Tile::__replaceThing(uint32_t index, Thing* thing) if(pos < (int32_t)creatures->size()) { #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Tile::__replaceThing] Update object is a creature" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Tile::__replaceThing] Update object is a creature" << std::endl; #endif return/* RET_NOTPOSSIBLE*/; } @@ -1149,13 +1146,32 @@ void Tile::__replaceThing(uint32_t index, Thing* thing) updateTileFlags(item, false); onUpdateTileItem(oldItem, Item::items[oldItem->getID()], item, Item::items[item->getID()]); - oldItem->setParent(NULL); +#ifdef __GROUND_CACHE__ + + std::map::iterator it = g_game.grounds.find(oldItem); + bool erase = it == g_game.grounds.end(); + if(!erase) + { + it->second--; + erase = it->second < 1; + if(erase) + g_game.grounds.erase(it); + } + + if(erase) + { +#endif + oldItem->setParent(NULL); + g_game.freeThing(oldItem); +#ifdef __GROUND_CACHE__ + } +#endif + return/* RET_NOERROR*/; } - #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Tile::__replaceThing] Update object not found" << std::endl; - DEBUG_REPORT + + std::clog << "[Failure - Tile::__replaceThing] Update object not found" << std::endl; #endif } @@ -1170,8 +1186,7 @@ void Tile::__removeThing(Thing* thing, uint32_t count) if(it == creatures->end()) { #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Tile::__removeThing] creature not found" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Tile::__removeThing] creature not found" << std::endl; #endif return/* RET_NOTPOSSIBLE*/; } @@ -1182,10 +1197,7 @@ void Tile::__removeThing(Thing* thing, uint32_t count) } #ifdef __DEBUG_MOVESYS__ else - { - std::cout << "[Failure - Tile::__removeThing] creature not found" << std::endl; - DEBUG_REPORT - } + std::clog << "[Failure - Tile::__removeThing] creature not found" << std::endl; #endif return; @@ -1195,8 +1207,7 @@ void Tile::__removeThing(Thing* thing, uint32_t count) if(!item) { #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Tile::__removeThing] item == NULL" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Tile::__removeThing] item == NULL" << std::endl; #endif return/* RET_NOTPOSSIBLE*/; } @@ -1205,8 +1216,7 @@ void Tile::__removeThing(Thing* thing, uint32_t count) if(index == -1) { #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Tile::__removeThing] index == -1" << std::endl; - DEBUG_REPORT + std::clog << "[Failure - Tile::__removeThing] index == -1" << std::endl; #endif return/* RET_NOTPOSSIBLE*/; } @@ -1214,7 +1224,7 @@ void Tile::__removeThing(Thing* thing, uint32_t count) if(item == ground) { const SpectatorVec& list = g_game.getSpectators(pos); - std::vector oldStackposVector; + std::vector oldStackposVector; Player* tmpPlayer = NULL; for(SpectatorVec::const_iterator it = list.begin(); it != list.end(); ++it) @@ -1223,10 +1233,29 @@ void Tile::__removeThing(Thing* thing, uint32_t count) oldStackposVector.push_back(getClientIndexOfThing(tmpPlayer, ground)); } - ground->setParent(NULL); - ground = NULL; +#ifdef __GROUND_CACHE__ + std::map::iterator it = g_game.grounds.find(ground); + bool erase = it == g_game.grounds.end(); + if(!erase) + { + it->second--; + erase = it->second < 1; + if(erase) + g_game.grounds.erase(it); + } + if(erase) + { +#endif + ground->setParent(NULL); + g_game.freeThing(ground); +#ifdef __GROUND_CACHE__ + } +#endif + + ground = NULL; --thingCount; + onRemoveTileItem(list, oldStackposVector, item); return/* RET_NOERROR*/; } @@ -1237,22 +1266,20 @@ void Tile::__removeThing(Thing* thing, uint32_t count) if(item->isAlwaysOnTop()) { - for(ItemVector::iterator it = items->getBeginTopItem(); it != items->getEndTopItem(); ++it) + ItemVector::iterator it = std::find(items->getBeginTopItem(), items->getEndTopItem(), item); + if(it != items->end()) { - if(*it != item) - continue; - const SpectatorVec& list = g_game.getSpectators(pos); - std::vector oldStackposVector; + std::vector oldStackposVector; Player* tmpPlayer = NULL; for(SpectatorVec::const_iterator iit = list.begin(); iit != list.end(); ++iit) { if((tmpPlayer = (*iit)->getPlayer())) - oldStackposVector.push_back(getClientIndexOfThing(tmpPlayer, *it)); + oldStackposVector.push_back(getClientIndexOfThing(tmpPlayer, item)); } - (*it)->setParent(NULL); + item->setParent(NULL); items->erase(it); --thingCount; @@ -1262,11 +1289,9 @@ void Tile::__removeThing(Thing* thing, uint32_t count) } else { - for(ItemVector::iterator it = items->getBeginDownItem(); it != items->getEndDownItem(); ++it) + ItemVector::iterator it = std::find(items->getBeginDownItem(), items->getEndDownItem(), item); + if(it != items->end()) { - if((*it) != item) - continue; - if(item->isStackable() && count != item->getItemCount()) { uint8_t newCount = (uint8_t)std::max((int32_t)0, (int32_t)(item->getItemCount() - count)); @@ -1281,16 +1306,16 @@ void Tile::__removeThing(Thing* thing, uint32_t count) else { const SpectatorVec& list = g_game.getSpectators(pos); - std::vector oldStackposVector; + std::vector oldStackposVector; Player* tmpPlayer = NULL; for(SpectatorVec::const_iterator iit = list.begin(); iit != list.end(); ++iit) { if((tmpPlayer = (*iit)->getPlayer())) - oldStackposVector.push_back(getClientIndexOfThing(tmpPlayer, *it)); + oldStackposVector.push_back(getClientIndexOfThing(tmpPlayer, item)); } - (*it)->setParent(NULL); + item->setParent(NULL); items->erase(it); --items->downItemCount; @@ -1301,10 +1326,9 @@ void Tile::__removeThing(Thing* thing, uint32_t count) return/* RET_NOERROR*/; } } - #ifdef __DEBUG_MOVESYS__ - std::cout << "[Failure - Tile::__removeThing] thing not found" << std::endl; - DEBUG_REPORT + + std::clog << "[Failure - Tile::__removeThing] thing not found" << std::endl; #endif } @@ -1313,12 +1337,6 @@ int32_t Tile::getClientIndexOfThing(const Player* player, const Thing* thing) co if(ground && ground == thing) return 0; - if(const Item* item = thing->getItem()) - { - if(item->isGroundTile()) - return 0; - } - int32_t n = 0; if(!ground) n--; @@ -1375,6 +1393,9 @@ int32_t Tile::__getIndexOfThing(const Thing* thing) const return 0; int32_t n = 0; + if(!ground) + n--; + const TileItemVector* items = getItemList(); if(items) { @@ -1419,30 +1440,17 @@ int32_t Tile::__getIndexOfThing(const Thing* thing) const return -1; } -uint32_t Tile::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/, bool itemCount /*= true*/) const +uint32_t Tile::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) const { + const TileItemVector* items = getItemList(); + if(!items) + return 0; + uint32_t count = 0; - Thing* thing = NULL; - for(uint32_t i = 0; i < getThingCount(); ++i) + for(ItemVector::const_iterator it = items->begin(); it != items->end(); ++it) { - if(!(thing = __getThing(i))) - continue; - - if(const Item* item = thing->getItem()) - { - if(item->getID() != itemId || (subType != -1 && subType != item->getSubType())) - continue; - - if(!itemCount) - { - if(item->isRune()) - count+= item->getCharges(); - else - count+= item->getItemCount(); - } - else - count+= item->getItemCount(); - } + if((*it)->getID() == itemId) + count += Item::countByType(*it, subType); } return count; @@ -1463,11 +1471,7 @@ Thing* Tile::__getThing(uint32_t index) const { uint32_t topItemSize = items->getTopItemCount(); if(index < topItemSize) - { - Item* item = items->at(items->downItemCount + index); - if(item && !item->isRemoved()) - return item; - } + return items->at(items->downItemCount + index); index -= topItemSize; } @@ -1481,21 +1485,15 @@ Thing* Tile::__getThing(uint32_t index) const } if(items && index < items->getDownItemCount()) - { - Item* item = items->at(index); - if(item && !item->isRemoved()) - return item; - } + return items->at(index); return NULL; } void Tile::postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link/* = LINK_OWNER*/) + int32_t index, CylinderLink_t link/* = LINK_OWNER*/) { - const Position& cylinderMapPos = pos; - - const SpectatorVec& list = g_game.getSpectators(cylinderMapPos); + const SpectatorVec& list = g_game.getSpectators(pos); SpectatorVec::const_iterator it; Player* tmpPlayer = NULL; @@ -1546,13 +1544,11 @@ void Tile::postAddNotification(Creature* actor, Thing* thing, const Cylinder* ol } void Tile::postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link/* = LINK_OWNER*/) + int32_t index, bool isCompleteRemoval, CylinderLink_t/* link = LINK_OWNER*/) { - const Position& cylinderMapPos = pos; - - const SpectatorVec& list = g_game.getSpectators(cylinderMapPos); + const SpectatorVec& list = g_game.getSpectators(pos); SpectatorVec::const_iterator it; - if(/*isCompleteRemoval && */getThingCount() > 8) + if(/*isCompleteRemoval && */thingCount > 8) onUpdateTile(); Player* tmpPlayer = NULL; @@ -1578,7 +1574,7 @@ void Tile::postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* } } -void Tile::__internalAddThing(uint32_t index, Thing* thing) +void Tile::__internalAddThing(uint32_t, Thing* thing) { thing->setParent(this); if(Creature* creature = thing->getCreature()) @@ -1638,9 +1634,9 @@ void Tile::__internalAddThing(uint32_t index, Thing* thing) updateTileFlags(item, false); } -void Tile::updateTileFlags(Item* item, bool removed) +void Tile::updateTileFlags(Item* item, bool remove) { - if(!removed) + if(!remove) { if(!hasFlag(TILESTATE_FLOORCHANGE)) { diff --git a/tile.h b/tile.h index 3593fea..0869b7e 100644 --- a/tile.h +++ b/tile.h @@ -30,24 +30,22 @@ class BedItem; class Player; class Creature; +class House; class HouseTile; class QTreeLeafNode; -typedef std::list PlayerList; typedef std::list SpectatorVec; -typedef std::map > SpectatorCache; - -typedef std::vector ItemVector; typedef std::vector CreatureVector; +typedef std::map > SpectatorCache; enum tileflags_t { TILESTATE_NONE = 0, TILESTATE_PROTECTIONZONE = 1 << 0, TILESTATE_TRASHED = 1 << 1, - TILESTATE_NOPVPZONE = 1 << 2, + TILESTATE_OPTIONALZONE = 1 << 2, TILESTATE_NOLOGOUT = 1 << 3, - TILESTATE_PVPZONE = 1 << 4, + TILESTATE_HARDCOREZONE = 1 << 4, TILESTATE_REFRESH = 1 << 5, //internal usage @@ -80,10 +78,10 @@ enum tileflags_t enum ZoneType_t { ZONE_PROTECTION, - ZONE_NOPVP, - ZONE_PVP, + ZONE_OPTIONAL, + ZONE_HARDCORE, ZONE_NOLOGOUT, - ZONE_NORMAL + ZONE_OPEN }; class TileItemVector @@ -107,7 +105,8 @@ class TileItemVector bool empty() {return items.empty();} bool empty() const {return items.empty();} - void push_back(Item* item) {return items.push_back(item);} + void push_back(Item* item) {items.push_back(item);} + void push_front(Item* item) {items.insert(items.begin(), item);} ItemVector::iterator insert(ItemVector::iterator _where, Item* item) {return items.insert(_where, item);} ItemVector::iterator erase(ItemVector::iterator _pos) {return items.erase(_pos);} @@ -126,15 +125,19 @@ class TileItemVector ItemVector::iterator getEndTopItem() {return items.end();} ItemVector::const_iterator getEndTopItem() const {return items.end();} - uint32_t getTopItemCount() const {return std::distance(getBeginTopItem(), getEndTopItem());} - uint32_t getDownItemCount() const {return std::distance(getBeginDownItem(), getEndDownItem());} + uint32_t getTopItemCount() const {return items.size() - downItemCount;} + uint32_t getDownItemCount() const {return downItemCount;} Item* getTopTopItem(); Item* getTopDownItem(); + void addDownItem() {++downItemCount;} + void removeDownItem() {--downItemCount;} + private: + friend class Tile; + ItemVector items; uint16_t downItemCount; - friend class Tile; }; class Tile : public Cylinder @@ -152,9 +155,10 @@ class Tile : public Cylinder const CreatureVector* getCreatures() const; CreatureVector* makeCreatures(); - HouseTile* getHouseTile(); - const HouseTile* getHouseTile() const; - bool isHouseTile() const {return hasFlag(TILESTATE_HOUSE);} + virtual HouseTile* getHouseTile() {return NULL;} + virtual const HouseTile* getHouseTile() const {return NULL;} + virtual House* getHouse() {return NULL;} + virtual const House* getHouse() const {return NULL;} MagicField* getFieldItem() const; Teleport* getTeleportItem() const; @@ -162,18 +166,21 @@ class Tile : public Cylinder Mailbox* getMailbox() const; BedItem* getBedItem() const; - Creature* getTopCreature(); Item* getTopTopItem(); Item* getTopDownItem(); - bool isMoveableBlocking() const; + Item* getItemByTopOrder(uint32_t topOrder); + + Creature* getTopCreature(); Thing* getTopVisibleThing(const Creature* creature); Creature* getTopVisibleCreature(const Creature* creature); const Creature* getTopVisibleCreature(const Creature* creature) const; - Item* getItemByTopOrder(uint32_t topOrder); uint32_t getThingCount() const {return thingCount;} + void updateThingCount(int32_t amount) {thingCount += amount;} + uint32_t getCreatureCount() const; uint32_t getItemCount() const; + uint32_t getTopItemCount() const; uint32_t getDownItemCount() const; @@ -221,17 +228,17 @@ class Tile : public Cylinder if(hasFlag(TILESTATE_PROTECTIONZONE)) return ZONE_PROTECTION; - if(hasFlag(TILESTATE_NOPVPZONE)) - return ZONE_NOPVP; + if(hasFlag(TILESTATE_OPTIONALZONE)) + return ZONE_OPTIONAL; - if(hasFlag(TILESTATE_PVPZONE)) - return ZONE_PVP; + if(hasFlag(TILESTATE_HARDCOREZONE)) + return ZONE_HARDCORE; - return ZONE_NORMAL; + return ZONE_OPEN; } - bool isSwimmingPool(bool checkPz = true) const; bool hasHeight(uint32_t n) const; + bool isFull() const; void moveCreature(Creature* actor, Creature* creature, Cylinder* toCylinder, bool forceTeleport = false); int32_t getClientIndexOfThing(const Player* player, const Thing* thing) const; @@ -249,10 +256,10 @@ class Tile : public Cylinder virtual const Creature* getCreature() const {return NULL;} virtual ReturnValue __queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const; + uint32_t flags, Creature* actor = NULL) const; virtual ReturnValue __queryMaxCount(int32_t index, const Thing* thing, uint32_t count, uint32_t& maxQueryCount, uint32_t flags) const; - virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, uint32_t flags) const; + virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, uint32_t flags, Creature* actor = NULL) const; virtual Cylinder* __queryDestination(int32_t& index, const Thing* thing, Item** destItem, uint32_t& flags); @@ -267,27 +274,28 @@ class Tile : public Cylinder virtual int32_t __getIndexOfThing(const Thing* thing) const; virtual int32_t __getFirstIndex() const {return 0;} virtual int32_t __getLastIndex() const {return thingCount;} - virtual uint32_t __getItemTypeCount(uint16_t itemId, int32_t subType = -1, bool itemCount = true) const; + virtual Thing* __getThing(uint32_t index) const; + virtual uint32_t __getItemTypeCount(uint16_t itemId, int32_t subType = -1) const; virtual void postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link = LINK_OWNER); + int32_t index, CylinderLink_t link = LINK_OWNER); virtual void postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link = LINK_OWNER); + int32_t index, bool isCompleteRemoval, CylinderLink_t link = LINK_OWNER); virtual void __internalAddThing(Thing* thing) {__internalAddThing(0, thing);} virtual void __internalAddThing(uint32_t index, Thing* thing); + void onUpdateTile(); + void updateTileFlags(Item* item, bool remove); + private: void onAddTileItem(Item* item); void onUpdateTileItem(Item* oldItem, const ItemType& oldType, Item* newItem, const ItemType& newType); - void onRemoveTileItem(const SpectatorVec& list, std::vector& oldStackPosVector, Item* item); - void onUpdateTile(); - - void updateTileFlags(Item* item, bool removed); + void onRemoveTileItem(const SpectatorVec& list, std::vector& oldStackPosVector, Item* item); protected: - bool isDynamic() const {return (m_flags & TILESTATE_DYNAMIC_TILE);} + bool isDynamic() const {return (m_flags & TILESTATE_DYNAMIC_TILE) != 0;} public: QTreeLeafNode* qt_node; diff --git a/tools.cpp b/tools.cpp index 11fdc2c..a925226 100644 --- a/tools.cpp +++ b/tools.cpp @@ -15,55 +15,93 @@ // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" +#include "tools.h" + #include #include -#include "tools.h" -#include "md5.h" -#include "sha1.h" +#include +#include #include "vocation.h" #include "configmanager.h" + extern ConfigManager g_config; +std::string transformToMD5(std::string plainText, bool upperCase) +{ + MD5_CTX c; + MD5_Init(&c); + MD5_Update(&c, plainText.c_str(), plainText.length()); + + uint8_t md[MD5_DIGEST_LENGTH]; + MD5_Final(md, &c); + + char output[(MD5_DIGEST_LENGTH << 1) + 1]; + for(int32_t i = 0; i < (int32_t)sizeof(md); ++i) + sprintf(output + (i << 1), "%.2X", md[i]); + + if(upperCase) + return std::string(output); + + return asLowerCaseString(std::string(output)); +} + std::string transformToSHA1(std::string plainText, bool upperCase) { - SHA1 sha1; - unsigned sha1Hash[5]; - std::stringstream hexStream; + SHA_CTX c; + SHA1_Init(&c); + SHA1_Update(&c, plainText.c_str(), plainText.length()); - sha1.Input((const uint8_t*)plainText.c_str(), plainText.length()); - sha1.Result(sha1Hash); + uint8_t md[SHA_DIGEST_LENGTH]; + SHA1_Final(md, &c); - hexStream.flags(std::ios::hex | std::ios::uppercase); - for(uint32_t i = 0; i < 5; ++i) - hexStream << std::setw(8) << std::setfill('0') << (uint32_t)sha1Hash[i]; + char output[(SHA_DIGEST_LENGTH << 1) + 1]; + for(int32_t i = 0; i < (int32_t)sizeof(md); ++i) + sprintf(output + (i << 1), "%.2X", md[i]); - std::string hexStr = hexStream.str(); - if(!upperCase) - toLowerCaseString(hexStr); + if(upperCase) + return std::string(output); - return hexStr; + return asLowerCaseString(std::string(output)); } -std::string transformToMD5(std::string plainText, bool upperCase) +std::string transformToSHA256(std::string plainText, bool upperCase) { - MD5_CTX m_md5; - std::stringstream hexStream; + SHA256_CTX c; + SHA256_Init(&c); + SHA256_Update(&c, plainText.c_str(), plainText.length()); - MD5Init(&m_md5, 0); - MD5Update(&m_md5, (const uint8_t*)plainText.c_str(), plainText.length()); - MD5Final(&m_md5); + uint8_t md[SHA256_DIGEST_LENGTH]; + SHA256_Final(md, &c); - hexStream.flags(std::ios::hex | std::ios::uppercase); - for(uint32_t i = 0; i < 16; ++i) - hexStream << std::setw(2) << std::setfill('0') << (uint32_t)m_md5.digest[i]; + char output[(SHA256_DIGEST_LENGTH << 1) + 1]; + for(int32_t i = 0; i < (int32_t)sizeof(md); ++i) + sprintf(output + (i << 1), "%.2X", md[i]); - std::string hexStr = hexStream.str(); - if(!upperCase) - toLowerCaseString(hexStr); + if(upperCase) + return std::string(output); - return hexStr; + return asLowerCaseString(std::string(output)); +} + +std::string transformToSHA512(std::string plainText, bool upperCase) +{ + SHA512_CTX c; + SHA512_Init(&c); + SHA512_Update(&c, plainText.c_str(), plainText.length()); + + uint8_t md[SHA512_DIGEST_LENGTH]; + SHA512_Final(md, &c); + + char output[(SHA512_DIGEST_LENGTH << 1) + 1]; + for(int32_t i = 0; i < (int32_t)sizeof(md); ++i) + sprintf(output + (i << 1), "%.2X", md[i]); + + if(upperCase) + return std::string(output); + + return asLowerCaseString(std::string(output)); } void _encrypt(std::string& str, bool upperCase) @@ -76,6 +114,12 @@ void _encrypt(std::string& str, bool upperCase) case ENCRYPTION_SHA1: str = transformToSHA1(str, upperCase); break; + case ENCRYPTION_SHA256: + str = transformToSHA256(str, upperCase); + break; + case ENCRYPTION_SHA512: + str = transformToSHA512(str, upperCase); + break; default: { if(upperCase) @@ -93,19 +137,24 @@ bool encryptTest(std::string plain, std::string& hash) return plain == hash; } -void replaceString(std::string& text, const std::string key, const std::string value) +bool replaceString(std::string& text, const std::string& key, const std::string& value) { - if(value.find(key) != std::string::npos) //don't allow infinite loops - return; + if(text.find(key) == std::string::npos) + return false; + + std::string::size_type start = 0, pos = 0; + while((start = text.find(key, pos)) != std::string::npos) + { + text.replace(start, key.size(), value); + pos = start + value.size(); + } - for(std::string::size_type keyStart = text.find(key); keyStart - != std::string::npos; keyStart = text.find(key)) - text.replace(keyStart, key.size(), value); + return true; } void trim_right(std::string& source, const std::string& t) { - source.erase(source.find_last_not_of(t)+1); + source.erase(source.find_last_not_of(t) + 1); } void trim_left(std::string& source, const std::string& t) @@ -143,18 +192,37 @@ bool booleanString(std::string source) return (source == "yes" || source == "true" || atoi(source.c_str()) > 0); } -bool readXMLInteger(xmlNodePtr node, const char* tag, int& value) +std::string ucfirst(std::string source) { - char* nodeValue = (char*)xmlGetProp(node, (xmlChar*)tag); - if(!nodeValue) - return false; + for(uint16_t i = 0; i < (uint16_t)source.length(); ++i) + { + if(source[i] != ' ') + { + source[i] = upchar(source[i]); + break; + } + } - value = atoi(nodeValue); - xmlFree(nodeValue); - return true; + return source; +} + +std::string ucwords(std::string source) +{ + bool tmp = true; + for(uint16_t i = 0; i < (uint16_t)source.length(); ++i) + { + if(source[i] == ' ') + tmp = true; + else if(tmp) + { + source[i] = upchar(source[i]); + tmp = false; + } + } + + return source; } -#if defined WINDOWS && !defined __GNUC__ bool readXMLInteger(xmlNodePtr node, const char* tag, int32_t& value) { char* nodeValue = (char*)xmlGetProp(node, (xmlChar*)tag); @@ -165,7 +233,6 @@ bool readXMLInteger(xmlNodePtr node, const char* tag, int32_t& value) xmlFree(nodeValue); return true; } -#endif bool readXMLInteger64(xmlNodePtr node, const char* tag, int64_t& value) { @@ -260,41 +327,78 @@ std::string getLastXMLError() return ss.str(); } -bool utf8ToLatin1(char* intext, std::string& outtext) +bool utf8ToLatin1(char* inText, std::string& outText) +{ + outText = ""; + if(!inText) + return false; + + int32_t inLen = strlen(inText); + if(!inLen) + return false; + + int32_t outLen = inLen << 1; + uint8_t* outBuf = new uint8_t[outLen]; + + int32_t res = UTF8Toisolat1(outBuf, &outLen, (uint8_t*)inText, &inLen); + if(res < 0) + { + delete[] outBuf; + return false; + } + + outBuf[outLen] = '\0'; + outText = (char*)outBuf; + + delete[] outBuf; + return true; +} + +bool latin1ToUtf8(char* inText, std::string& outText) { - outtext = ""; - if(!intext) + outText = ""; + if(!inText) return false; - int32_t inlen = strlen(intext); - if(!inlen) + int32_t inLen = strlen(inText); + if(!inLen) return false; - int32_t outlen = inlen * 2; - uint8_t* outbuf = new uint8_t[outlen]; + int32_t outLen = inLen << 1; + uint8_t* outBuf = new uint8_t[outLen]; - int32_t res = UTF8Toisolat1(outbuf, &outlen, (uint8_t*)intext, &inlen); + int32_t res = isolat1ToUTF8(outBuf, &outLen, (uint8_t*)inText, &inLen); if(res < 0) { - delete[] outbuf; + delete[] outBuf; return false; } - outbuf[outlen] = '\0'; - outtext = (char*)outbuf; + outBuf[outLen] = '\0'; + outText = (char*)outBuf; - delete[] outbuf; + delete[] outBuf; return true; } -StringVec explodeString(const std::string& string, const std::string& separator) +StringVec explodeString(const std::string& string, const std::string& separator, bool trim/* = true*/, uint16_t limit/* = 0*/) { StringVec returnVector; size_t start = 0, end = 0; + + uint16_t i = 1; while((end = string.find(separator, start)) != std::string::npos) { - returnVector.push_back(string.substr(start, end - start)); + std::string t = string.substr(start, end - start); + if(trim) + trimString(t); + + returnVector.push_back(t); start = end + separator.size(); + + ++i; + if(limit > 0 && i > limit) + break; } returnVector.push_back(string.substr(start)); @@ -305,7 +409,11 @@ IntegerVec vectorAtoi(StringVec stringVector) { IntegerVec returnVector; for(StringVec::iterator it = stringVector.begin(); it != stringVector.end(); ++it) - returnVector.push_back(atoi((*it).c_str())); + { + int32_t number = atoi((*it).c_str()); + if(number || (*it) == "0") + returnVector.push_back(number); + } return returnVector; } @@ -326,7 +434,7 @@ int32_t round(float v) uint32_t rand24b() { - return ((rand() << 12) ^ (rand())) & (0xFFFFFF); + return ((rand() << 12) ^ rand()) & 0xFFFFFF; } float box_muller(float m, float s) @@ -458,7 +566,6 @@ bool isValidName(std::string text, bool forceUppercaseOnFirstLetter/* = true*/) if(text[size] != 32) { lenBeforeSpace++; - if(text[size] != 39) lenBeforeQuote++; else @@ -524,7 +631,7 @@ bool checkText(std::string text, std::string str) return asLowerCaseString(text) == str; } -std::string generateRecoveryKey(int32_t fieldCount, int32_t fieldLenght) +std::string generateRecoveryKey(int32_t fieldCount, int32_t fieldLenght, bool mixCase/* = false*/) { std::stringstream key; int32_t i = 0, j = 0, lastNumber = 99, number = 0; @@ -536,7 +643,7 @@ std::string generateRecoveryKey(int32_t fieldCount, int32_t fieldLenght) do { madeNumber = madeCharacter = false; - if((bool)random_range(0, 1)) + if((mixCase && !random_range(0, 2)) || (!mixCase && !random_range(0, 1))) { number = random_range(2, 9); if(number != lastNumber) @@ -548,7 +655,11 @@ std::string generateRecoveryKey(int32_t fieldCount, int32_t fieldLenght) } else { - character = (char)random_range(65, 90); + if(mixCase && !random_range(0, 1)) + character = (char)random_range(97, 122); + else + character = (char)random_range(65, 90); + if(character != lastCharacter) { key << character; @@ -599,50 +710,69 @@ std::string parseParams(tokenizer::iterator &it, tokenizer::iterator end) std::string formatDate(time_t _time/* = 0*/) { - char buffer[21]; if(!_time) _time = time(NULL); const tm* tms = localtime(&_time); + std::stringstream s; if(tms) - sprintf(buffer, "%02d/%02d/%04d %02d:%02d:%02d", tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour, tms->tm_min, tms->tm_sec); + s << tms->tm_mday << "/" << (tms->tm_mon + 1) << "/" << (tms->tm_year + 1900) << " " << tms->tm_hour << ":" << tms->tm_min << ":" << tms->tm_sec; else - sprintf(buffer, "UNIX Time: %d", (int32_t)_time); + s << "UNIX Time: " << (int32_t)_time; - return buffer; + return s.str(); } -std::string formatDateShort(time_t _time, bool detailed/* = false*/) +std::string formatDateEx(time_t _time/* = 0*/, std::string format/* = "%d %b %Y, %H:%M:%S"*/) { - char buffer[21]; if(!_time) _time = time(NULL); const tm* tms = localtime(&_time); + char buffer[100]; if(tms) - { - std::string format = "%d %b %Y"; - if(detailed) - format += " %H:%M:%S"; - strftime(buffer, 25, format.c_str(), tms); - } else sprintf(buffer, "UNIX Time: %d", (int32_t)_time); return buffer; } -std::string formatTime(int32_t hours, int32_t minutes) +std::string formatTime(time_t _time/* = 0*/, bool ms/* = false*/) { - std::stringstream time; - if(hours) - time << hours << " " << (hours > 1 ? "hours" : "hour") << (minutes ? " and " : ""); + if(!_time) + _time = time(NULL); + else if(ms) + ms = false; - if(minutes) - time << minutes << " " << (minutes > 1 ? "minutes" : "minute"); + const tm* tms = localtime(&_time); + std::stringstream s; + if(tms) + { + s << tms->tm_hour << ":" << tms->tm_min << ":"; + if(tms->tm_sec < 10) + s << "0"; - return time.str(); + s << tms->tm_sec; + if(ms) + { + timeb t; + ftime(&t); + + s << "."; // make it format zzz + if(t.millitm < 10) + s << "0"; + + if(t.millitm < 100) + s << "0"; + + s << t.millitm; + } + } + else + s << "UNIX Time: " << (int32_t)_time; + + return s.str(); } std::string convertIPAddress(uint32_t ip) @@ -652,67 +782,105 @@ std::string convertIPAddress(uint32_t ip) return buffer; } -Skulls_t getSkull(std::string strValue) +Skulls_t getSkulls(std::string strValue) { std::string tmpStrValue = asLowerCaseString(strValue); + if(tmpStrValue == "orange" || tmpStrValue == "6") + return SKULL_ORANGE; + if(tmpStrValue == "black" || tmpStrValue == "5") return SKULL_BLACK; - else if(tmpStrValue == "red" || tmpStrValue == "4") + + if(tmpStrValue == "red" || tmpStrValue == "4") return SKULL_RED; - else if(tmpStrValue == "white" || tmpStrValue == "3") + + if(tmpStrValue == "white" || tmpStrValue == "3") return SKULL_WHITE; - else if(tmpStrValue == "green" || tmpStrValue == "2") + + if(tmpStrValue == "green" || tmpStrValue == "2") return SKULL_GREEN; - else if(tmpStrValue == "yellow" || tmpStrValue == "1") + + if(tmpStrValue == "yellow" || tmpStrValue == "1") return SKULL_YELLOW; return SKULL_NONE; } -PartyShields_t getPartyShield(std::string strValue) +PartyShields_t getShields(std::string strValue) { std::string tmpStrValue = asLowerCaseString(strValue); if(tmpStrValue == "whitenoshareoff" || tmpStrValue == "10") return SHIELD_YELLOW_NOSHAREDEXP; - else if(tmpStrValue == "blueshareoff" || tmpStrValue == "9") + + if(tmpStrValue == "blueshareoff" || tmpStrValue == "9") return SHIELD_BLUE_NOSHAREDEXP; - else if(tmpStrValue == "yellowshareblink" || tmpStrValue == "8") + + if(tmpStrValue == "yellowshareblink" || tmpStrValue == "8") return SHIELD_YELLOW_NOSHAREDEXP_BLINK; - else if(tmpStrValue == "blueshareblink" || tmpStrValue == "7") + + if(tmpStrValue == "blueshareblink" || tmpStrValue == "7") return SHIELD_BLUE_NOSHAREDEXP_BLINK; - else if(tmpStrValue == "yellowshareon" || tmpStrValue == "6") + + if(tmpStrValue == "yellowshareon" || tmpStrValue == "6") return SHIELD_YELLOW_SHAREDEXP; - else if(tmpStrValue == "blueshareon" || tmpStrValue == "5") + + if(tmpStrValue == "blueshareon" || tmpStrValue == "5") return SHIELD_BLUE_SHAREDEXP; - else if(tmpStrValue == "yellow" || tmpStrValue == "4") + + if(tmpStrValue == "yellow" || tmpStrValue == "4") return SHIELD_YELLOW; - else if(tmpStrValue == "blue" || tmpStrValue == "3") + + if(tmpStrValue == "blue" || tmpStrValue == "3") return SHIELD_BLUE; - else if(tmpStrValue == "whiteyellow" || tmpStrValue == "2") + + if(tmpStrValue == "whiteyellow" || tmpStrValue == "2") return SHIELD_WHITEYELLOW; - else if(tmpStrValue == "whiteblue" || tmpStrValue == "1") + + if(tmpStrValue == "whiteblue" || tmpStrValue == "1") return SHIELD_WHITEBLUE; return SHIELD_NONE; } +GuildEmblems_t getEmblems(std::string strValue) +{ + std::string tmpStrValue = asLowerCaseString(strValue); + if(tmpStrValue == "blue" || tmpStrValue == "3") + return EMBLEM_BLUE; + + if(tmpStrValue == "red" || tmpStrValue == "2") + return EMBLEM_RED; + + if(tmpStrValue == "green" || tmpStrValue == "1") + return EMBLEM_GREEN; + + return EMBLEM_NONE; +} + Direction getDirection(std::string string) { if(string == "north" || string == "n" || string == "0") return NORTH; - else if(string == "east" || string == "e" || string == "1") + + if(string == "east" || string == "e" || string == "1") return EAST; - else if(string == "south" || string == "s" || string == "2") + + if(string == "south" || string == "s" || string == "2") return SOUTH; - else if(string == "west" || string == "w" || string == "3") + + if(string == "west" || string == "w" || string == "3") return WEST; - else if(string == "southwest" || string == "south west" || string == "south-west" || string == "sw" || string == "4") + + if(string == "southwest" || string == "south west" || string == "south-west" || string == "sw" || string == "4") return SOUTHWEST; - else if(string == "southeast" || string == "south east" || string == "south-east" || string == "se" || string == "5") + + if(string == "southeast" || string == "south east" || string == "south-east" || string == "se" || string == "5") return SOUTHEAST; - else if(string == "northwest" || string == "north west" || string == "north-west" || string == "nw" || string == "6") + + if(string == "northwest" || string == "north west" || string == "north-west" || string == "nw" || string == "6") return NORTHWEST; - else if(string == "northeast" || string == "north east" || string == "north-east" || string == "ne" || string == "7") + + if(string == "northeast" || string == "north east" || string == "north-east" || string == "ne" || string == "7") return NORTHEAST; return SOUTH; @@ -858,100 +1026,113 @@ struct SkillIdNames skills_t skillId; }; +struct WeaponTypeNames +{ + const char* name; + WeaponType_t weaponType; +}; + MagicEffectNames magicEffectNames[] = { - {"redspark", MAGIC_EFFECT_DRAW_BLOOD}, - {"bluebubble", MAGIC_EFFECT_LOSE_ENERGY}, - {"poff", MAGIC_EFFECT_POFF}, - {"yellowspark", MAGIC_EFFECT_BLOCKHIT}, - {"explosionarea", MAGIC_EFFECT_EXPLOSION_AREA}, - {"explosion", MAGIC_EFFECT_EXPLOSION_DAMAGE}, - {"firearea", MAGIC_EFFECT_FIRE_AREA}, - {"yellowbubble", MAGIC_EFFECT_YELLOW_RINGS}, - {"greenbubble", MAGIC_EFFECT_POISON_RINGS}, - {"blackspark", MAGIC_EFFECT_HIT_AREA}, - {"teleport", MAGIC_EFFECT_TELEPORT}, - {"energy", MAGIC_EFFECT_ENERGY_DAMAGE}, - {"blueshimmer", MAGIC_EFFECT_WRAPS_BLUE}, - {"redshimmer", MAGIC_EFFECT_WRAPS_RED}, - {"greenshimmer", MAGIC_EFFECT_WRAPS_GREEN}, - {"fire", MAGIC_EFFECT_HITBY_FIRE}, - {"greenspark", MAGIC_EFFECT_POISON}, - {"mortarea", MAGIC_EFFECT_MORT_AREA}, - {"greennote", MAGIC_EFFECT_SOUND_GREEN}, - {"rednote", MAGIC_EFFECT_SOUND_RED}, - {"poison", MAGIC_EFFECT_POISON_AREA}, - {"yellownote", MAGIC_EFFECT_SOUND_YELLOW}, - {"purplenote", MAGIC_EFFECT_SOUND_PURPLE}, - {"bluenote", MAGIC_EFFECT_SOUND_BLUE}, - {"whitenote", MAGIC_EFFECT_SOUND_WHITE}, - {"bubbles", MAGIC_EFFECT_BUBBLES}, - {"dice", MAGIC_EFFECT_CRAPS}, - {"giftwraps", MAGIC_EFFECT_GIFT_WRAPS}, - {"yellowfirework", MAGIC_EFFECT_FIREWORK_YELLOW}, - {"redfirework", MAGIC_EFFECT_FIREWORK_RED}, - {"bluefirework", MAGIC_EFFECT_FIREWORK_BLUE}, - {"stun", MAGIC_EFFECT_STUN}, - {"sleep", MAGIC_EFFECT_SLEEP}, - {"watercreature", MAGIC_EFFECT_WATERCREATURE}, - {"groundshaker", MAGIC_EFFECT_GROUNDSHAKER}, - {"hearts", MAGIC_EFFECT_HEARTS}, - {"fireattack", MAGIC_EFFECT_FIREATTACK}, - {"energyarea", MAGIC_EFFECT_ENERGY_AREA}, - {"smallclouds", MAGIC_EFFECT_SMALLCLOUDS}, - {"holydamage", MAGIC_EFFECT_HOLYDAMAGE}, - {"bigclouds", MAGIC_EFFECT_BIGCLOUDS}, - {"icearea", MAGIC_EFFECT_ICEAREA}, - {"icetornado", MAGIC_EFFECT_ICETORNADO}, - {"iceattack", MAGIC_EFFECT_ICEATTACK}, - {"stones", MAGIC_EFFECT_STONES}, - {"smallplants", MAGIC_EFFECT_SMALLPLANTS}, - {"carniphila", MAGIC_EFFECT_CARNIPHILA}, - {"purpleenergy", MAGIC_EFFECT_PURPLEENERGY}, - {"yellowenergy", MAGIC_EFFECT_YELLOWENERGY}, - {"holyarea", MAGIC_EFFECT_HOLYAREA}, - {"bigplants", MAGIC_EFFECT_BIGPLANTS}, - {"cake", MAGIC_EFFECT_CAKE}, - {"giantice", MAGIC_EFFECT_GIANTICE}, - {"watersplash", MAGIC_EFFECT_WATERSPLASH}, - {"plantattack", MAGIC_EFFECT_PLANTATTACK}, - {"tutorialarrow", MAGIC_EFFECT_TUTORIALARROW}, - {"tutorialsquare", MAGIC_EFFECT_TUTORIALSQUARE}, + {"redspark", MAGIC_EFFECT_DRAW_BLOOD}, + {"bluebubble", MAGIC_EFFECT_LOSE_ENERGY}, + {"poff", MAGIC_EFFECT_POFF}, + {"yellowspark", MAGIC_EFFECT_BLOCKHIT}, + {"explosionarea", MAGIC_EFFECT_EXPLOSION_AREA}, + {"explosion", MAGIC_EFFECT_EXPLOSION_DAMAGE}, + {"firearea", MAGIC_EFFECT_FIRE_AREA}, + {"yellowbubble", MAGIC_EFFECT_YELLOW_RINGS}, + {"greenbubble", MAGIC_EFFECT_POISON_RINGS}, + {"blackspark", MAGIC_EFFECT_HIT_AREA}, + {"teleport", MAGIC_EFFECT_TELEPORT}, + {"energy", MAGIC_EFFECT_ENERGY_DAMAGE}, + {"blueshimmer", MAGIC_EFFECT_WRAPS_BLUE}, + {"redshimmer", MAGIC_EFFECT_WRAPS_RED}, + {"greenshimmer", MAGIC_EFFECT_WRAPS_GREEN}, + {"fire", MAGIC_EFFECT_HITBY_FIRE}, + {"greenspark", MAGIC_EFFECT_POISON}, + {"mortarea", MAGIC_EFFECT_MORT_AREA}, + {"greennote", MAGIC_EFFECT_SOUND_GREEN}, + {"rednote", MAGIC_EFFECT_SOUND_RED}, + {"poison", MAGIC_EFFECT_POISON_AREA}, + {"yellownote", MAGIC_EFFECT_SOUND_YELLOW}, + {"purplenote", MAGIC_EFFECT_SOUND_PURPLE}, + {"bluenote", MAGIC_EFFECT_SOUND_BLUE}, + {"whitenote", MAGIC_EFFECT_SOUND_WHITE}, + {"bubbles", MAGIC_EFFECT_BUBBLES}, + {"dice", MAGIC_EFFECT_CRAPS}, + {"giftwraps", MAGIC_EFFECT_GIFT_WRAPS}, + {"yellowfirework", MAGIC_EFFECT_FIREWORK_YELLOW}, + {"redfirework", MAGIC_EFFECT_FIREWORK_RED}, + {"bluefirework", MAGIC_EFFECT_FIREWORK_BLUE}, + {"stun", MAGIC_EFFECT_STUN}, + {"sleep", MAGIC_EFFECT_SLEEP}, + {"watercreature", MAGIC_EFFECT_WATERCREATURE}, + {"groundshaker", MAGIC_EFFECT_GROUNDSHAKER}, + {"hearts", MAGIC_EFFECT_HEARTS}, + {"fireattack", MAGIC_EFFECT_FIREATTACK}, + {"energyarea", MAGIC_EFFECT_ENERGY_AREA}, + {"smallclouds", MAGIC_EFFECT_SMALLCLOUDS}, + {"holydamage", MAGIC_EFFECT_HOLYDAMAGE}, + {"bigclouds", MAGIC_EFFECT_BIGCLOUDS}, + {"icearea", MAGIC_EFFECT_ICEAREA}, + {"icetornado", MAGIC_EFFECT_ICETORNADO}, + {"iceattack", MAGIC_EFFECT_ICEATTACK}, + {"stones", MAGIC_EFFECT_STONES}, + {"smallplants", MAGIC_EFFECT_SMALLPLANTS}, + {"carniphila", MAGIC_EFFECT_CARNIPHILA}, + {"purpleenergy", MAGIC_EFFECT_PURPLEENERGY}, + {"yellowenergy", MAGIC_EFFECT_YELLOWENERGY}, + {"holyarea", MAGIC_EFFECT_HOLYAREA}, + {"bigplants", MAGIC_EFFECT_BIGPLANTS}, + {"cake", MAGIC_EFFECT_CAKE}, + {"giantice", MAGIC_EFFECT_GIANTICE}, + {"watersplash", MAGIC_EFFECT_WATERSPLASH}, + {"plantattack", MAGIC_EFFECT_PLANTATTACK}, + {"tutorialarrow", MAGIC_EFFECT_TUTORIALARROW}, + {"tutorialsquare", MAGIC_EFFECT_TUTORIALSQUARE}, {"mirrorhorizontal", MAGIC_EFFECT_MIRRORHORIZONTAL}, - {"mirrorvertical", MAGIC_EFFECT_MIRRORVERTICAL}, - {"skullhorizontal", MAGIC_EFFECT_SKULLHORIZONTAL}, - {"skullvertical", MAGIC_EFFECT_SKULLVERTICAL}, - {"assassin", MAGIC_EFFECT_ASSASSIN}, - {"stepshorizontal", MAGIC_EFFECT_STEPSHORIZONTAL}, - {"bloodysteps", MAGIC_EFFECT_BLOODYSTEPS}, - {"stepsvertical", MAGIC_EFFECT_STEPSVERTICAL}, - {"yalaharighost", MAGIC_EFFECT_YALAHARIGHOST}, - {"bats", MAGIC_EFFECT_BATS}, - {"smoke", MAGIC_EFFECT_SMOKE}, - {"insects", MAGIC_EFFECT_INSECTS} + {"mirrorvertical", MAGIC_EFFECT_MIRRORVERTICAL}, + {"skullhorizontal", MAGIC_EFFECT_SKULLHORIZONTAL}, + {"skullvertical", MAGIC_EFFECT_SKULLVERTICAL}, + {"assassin", MAGIC_EFFECT_ASSASSIN}, + {"stepshorizontal", MAGIC_EFFECT_STEPSHORIZONTAL}, + {"bloodysteps", MAGIC_EFFECT_BLOODYSTEPS}, + {"stepsvertical", MAGIC_EFFECT_STEPSVERTICAL}, + {"yalaharighost", MAGIC_EFFECT_YALAHARIGHOST}, + {"bats", MAGIC_EFFECT_BATS}, + {"smoke", MAGIC_EFFECT_SMOKE}, + {"insects", MAGIC_EFFECT_INSECTS}, + {"dragonhead", MAGIC_EFFECT_DRAGONHEAD}, + {"orcshaman", MAGIC_EFFECT_ORCSHAMAN}, + {"orcshamanfire", MAGIC_EFFECT_ORCSHAMAN_FIRE}, + {"thunder", MAGIC_EFFECT_THUNDER}, + {"ferumbras", MAGIC_EFFECT_FERUMBRAS}, + {"confettihorizontal", MAGIC_EFFECT_CONFETTIHORIZONTAL}, + {"confettivertical", MAGIC_EFFECT_CONFETTIVERTICAL} }; ShootTypeNames shootTypeNames[] = { - {"spear", SHOOT_EFFECT_SPEAR}, - {"bolt", SHOOT_EFFECT_BOLT}, - {"arrow", SHOOT_EFFECT_ARROW}, - {"fire", SHOOT_EFFECT_FIRE}, - {"energy", SHOOT_EFFECT_ENERGY}, + {"spear", SHOOT_EFFECT_SPEAR}, + {"bolt", SHOOT_EFFECT_BOLT}, + {"arrow", SHOOT_EFFECT_ARROW}, + {"fire", SHOOT_EFFECT_FIRE}, + {"energy", SHOOT_EFFECT_ENERGY}, {"poisonarrow", SHOOT_EFFECT_POISONARROW}, {"burstarrow", SHOOT_EFFECT_BURSTARROW}, {"throwingstar", SHOOT_EFFECT_THROWINGSTAR}, {"throwingknife", SHOOT_EFFECT_THROWINGKNIFE}, {"smallstone", SHOOT_EFFECT_SMALLSTONE}, - {"death", SHOOT_EFFECT_DEATH}, + {"death", SHOOT_EFFECT_DEATH}, {"largerock", SHOOT_EFFECT_LARGEROCK}, {"snowball", SHOOT_EFFECT_SNOWBALL}, {"powerbolt", SHOOT_EFFECT_POWERBOLT}, - {"poison", SHOOT_EFFECT_POISONFIELD}, + {"poison", SHOOT_EFFECT_POISONFIELD}, {"infernalbolt", SHOOT_EFFECT_INFERNALBOLT}, {"huntingspear", SHOOT_EFFECT_HUNTINGSPEAR}, {"enchantedspear", SHOOT_EFFECT_ENCHANTEDSPEAR}, - {"redstar", SHOOT_EFFECT_REDSTAR}, + {"redstar", SHOOT_EFFECT_REDSTAR}, {"greenstar", SHOOT_EFFECT_GREENSTAR}, {"royalspear", SHOOT_EFFECT_ROYALSPEAR}, {"sniperarrow", SHOOT_EFFECT_SNIPERARROW}, @@ -961,9 +1142,9 @@ ShootTypeNames shootTypeNames[] = {"whirlwindaxe", SHOOT_EFFECT_WHIRLWINDAXE}, {"whirlwindclub", SHOOT_EFFECT_WHIRLWINDCLUB}, {"etherealspear", SHOOT_EFFECT_ETHEREALSPEAR}, - {"ice", SHOOT_EFFECT_ICE}, - {"earth", SHOOT_EFFECT_EARTH}, - {"holy", SHOOT_EFFECT_HOLY}, + {"ice", SHOOT_EFFECT_ICE}, + {"earth", SHOOT_EFFECT_EARTH}, + {"holy", SHOOT_EFFECT_HOLY}, {"suddendeath", SHOOT_EFFECT_SUDDENDEATH}, {"flasharrow", SHOOT_EFFECT_FLASHARROW}, {"flammingarrow", SHOOT_EFFECT_FLAMMINGARROW}, @@ -975,20 +1156,23 @@ ShootTypeNames shootTypeNames[] = {"smallearth", SHOOT_EFFECT_SMALLEARTH}, {"eartharrow", SHOOT_EFFECT_EARTHARROW}, {"explosion", SHOOT_EFFECT_EXPLOSION}, - {"cake", SHOOT_EFFECT_CAKE} + {"cake", SHOOT_EFFECT_CAKE}, + {"tarsalarrow", SHOOT_EFFECT_TARSALARROW}, + {"vortexbolt", SHOOT_EFFECT_VORTEXBOLT}, + {"football", SHOOT_EFFECT_FOOTBALL} }; CombatTypeNames combatTypeNames[] = { - {"physical", COMBAT_PHYSICALDAMAGE}, + {"physical", COMBAT_PHYSICALDAMAGE}, {"energy", COMBAT_ENERGYDAMAGE}, {"earth", COMBAT_EARTHDAMAGE}, {"fire", COMBAT_FIREDAMAGE}, - {"undefined", COMBAT_UNDEFINEDDAMAGE}, - {"lifedrain", COMBAT_LIFEDRAIN}, - {"life drain", COMBAT_LIFEDRAIN}, - {"manadrain", COMBAT_MANADRAIN}, - {"mana drain", COMBAT_MANADRAIN}, + {"undefined", COMBAT_UNDEFINEDDAMAGE}, + {"lifedrain", COMBAT_LIFEDRAIN}, + {"life drain", COMBAT_LIFEDRAIN}, + {"manadrain", COMBAT_MANADRAIN}, + {"mana drain", COMBAT_MANADRAIN}, {"healing", COMBAT_HEALING}, {"drown", COMBAT_DROWNDAMAGE}, {"ice", COMBAT_ICEDAMAGE}, @@ -998,11 +1182,11 @@ CombatTypeNames combatTypeNames[] = AmmoTypeNames ammoTypeNames[] = { - {"spear", AMMO_SPEAR}, - {"arrow", AMMO_ARROW}, + {"spear", AMMO_SPEAR}, + {"arrow", AMMO_ARROW}, {"poisonarrow", AMMO_ARROW}, {"burstarrow", AMMO_ARROW}, - {"bolt", AMMO_BOLT}, + {"bolt", AMMO_BOLT}, {"powerbolt", AMMO_BOLT}, {"smallstone", AMMO_STONE}, {"largerock", AMMO_STONE}, @@ -1026,7 +1210,7 @@ AmmoTypeNames ammoTypeNames[] = AmmoActionNames ammoActionNames[] = { - {"move", AMMOACTION_MOVE}, + {"move", AMMOACTION_MOVE}, {"moveback", AMMOACTION_MOVEBACK}, {"move back", AMMOACTION_MOVEBACK}, {"removecharge", AMMOACTION_REMOVECHARGE}, @@ -1042,21 +1226,23 @@ FluidTypeNames fluidTypeNames[] = {"blood", FLUID_BLOOD}, {"beer", FLUID_BEER}, {"slime", FLUID_SLIME}, - {"lemonade", FLUID_LEMONADE}, + {"lemonade", FLUID_LEMONADE}, {"milk", FLUID_MILK}, {"mana", FLUID_MANA}, {"life", FLUID_LIFE}, {"oil", FLUID_OIL}, {"urine", FLUID_URINE}, - {"coconutmilk", FLUID_COCONUTMILK}, - {"coconut milk", FLUID_COCONUTMILK}, + {"coconutmilk", FLUID_COCONUTMILK}, + {"coconut milk",FLUID_COCONUTMILK}, {"wine", FLUID_WINE}, {"mud", FLUID_MUD}, - {"fruitjuice", FLUID_FRUITJUICE}, - {"fruit juice", FLUID_FRUITJUICE}, + {"fruitjuice", FLUID_FRUITJUICE}, + {"fruit juice", FLUID_FRUITJUICE}, {"lava", FLUID_LAVA}, {"rum", FLUID_RUM}, - {"swamp", FLUID_SWAMP} + {"swamp", FLUID_SWAMP}, + {"tea", FLUID_TEA}, + {"mead", FLUID_MEAD} }; SkillIdNames skillIdNames[] = @@ -1065,22 +1251,35 @@ SkillIdNames skillIdNames[] = {"club", SKILL_CLUB}, {"sword", SKILL_SWORD}, {"axe", SKILL_AXE}, - {"distance", SKILL_DIST}, + {"distance", SKILL_DIST}, {"dist", SKILL_DIST}, - {"shielding", SKILL_SHIELD}, + {"shielding", SKILL_SHIELD}, {"shield", SKILL_SHIELD}, {"fishing", SKILL_FISH}, {"fish", SKILL_FISH}, {"level", SKILL__LEVEL}, - {"magiclevel", SKILL__MAGLEVEL}, - {"magic level", SKILL__MAGLEVEL} + {"magiclevel", SKILL__MAGLEVEL}, + {"magic level", SKILL__MAGLEVEL}, + {"experience", SKILL__EXPERIENCE} +}; + +WeaponTypeNames weaponTypeNames[] = { + {"", WEAPON_NONE}, + {"sword", WEAPON_SWORD}, + {"club", WEAPON_CLUB}, + {"axe", WEAPON_AXE}, + {"distance", WEAPON_DIST}, + {"shield", WEAPON_SHIELD}, + {"fist", WEAPON_FIST}, + {"wand", WEAPON_WAND}, + {"ammunition", WEAPON_AMMO} }; MagicEffect_t getMagicEffect(const std::string& strValue) { for(uint32_t i = 0; i < sizeof(magicEffectNames) / sizeof(MagicEffectNames); ++i) { - if(!strcasecmp(strValue.c_str(), magicEffectNames[i].name)) + if(boost::algorithm::iequals(strValue.c_str(), magicEffectNames[i].name)) return magicEffectNames[i].magicEffect; } @@ -1091,7 +1290,7 @@ ShootEffect_t getShootType(const std::string& strValue) { for(uint32_t i = 0; i < sizeof(shootTypeNames) / sizeof(ShootTypeNames); ++i) { - if(!strcasecmp(strValue.c_str(), shootTypeNames[i].name)) + if(boost::algorithm::iequals(strValue.c_str(), shootTypeNames[i].name)) return shootTypeNames[i].shootType; } @@ -1102,7 +1301,7 @@ CombatType_t getCombatType(const std::string& strValue) { for(uint32_t i = 0; i < sizeof(combatTypeNames) / sizeof(CombatTypeNames); ++i) { - if(!strcasecmp(strValue.c_str(), combatTypeNames[i].name)) + if(boost::algorithm::iequals(strValue.c_str(), combatTypeNames[i].name)) return combatTypeNames[i].combatType; } @@ -1113,7 +1312,7 @@ Ammo_t getAmmoType(const std::string& strValue) { for(uint32_t i = 0; i < sizeof(ammoTypeNames) / sizeof(AmmoTypeNames); ++i) { - if(!strcasecmp(strValue.c_str(), ammoTypeNames[i].name)) + if(boost::algorithm::iequals(strValue.c_str(), ammoTypeNames[i].name)) return ammoTypeNames[i].ammoType; } @@ -1124,7 +1323,7 @@ AmmoAction_t getAmmoAction(const std::string& strValue) { for(uint32_t i = 0; i < sizeof(ammoActionNames) / sizeof(AmmoActionNames); ++i) { - if(!strcasecmp(strValue.c_str(), ammoActionNames[i].name)) + if(boost::algorithm::iequals(strValue.c_str(), ammoActionNames[i].name)) return ammoActionNames[i].ammoAction; } @@ -1135,7 +1334,7 @@ FluidTypes_t getFluidType(const std::string& strValue) { for(uint32_t i = 0; i < sizeof(fluidTypeNames) / sizeof(FluidTypeNames); ++i) { - if(!strcasecmp(strValue.c_str(), fluidTypeNames[i].name)) + if(boost::algorithm::iequals(strValue.c_str(), fluidTypeNames[i].name)) return fluidTypeNames[i].fluidType; } @@ -1146,13 +1345,102 @@ skills_t getSkillId(const std::string& strValue) { for(uint32_t i = 0; i < sizeof(skillIdNames) / sizeof(SkillIdNames); ++i) { - if(!strcasecmp(strValue.c_str(), skillIdNames[i].name)) + if(boost::algorithm::iequals(strValue.c_str(), skillIdNames[i].name)) return skillIdNames[i].skillId; } return SKILL_FIST; } +WeaponType_t getWeaponType(const std::string& strValue) +{ + for(uint32_t i = 0; i < sizeof(weaponTypeNames) / sizeof(WeaponTypeNames); ++i) + { + if(boost::algorithm::iequals(strValue.c_str(), weaponTypeNames[i].name)) + return weaponTypeNames[i].weaponType; + } + + return WEAPON_NONE; +} + +void getCombatDetails(CombatType_t combatType, MagicEffect_t& magicEffect, Color_t& textColor) +{ + switch(combatType) + { + case COMBAT_PHYSICALDAMAGE: + { + textColor = COLOR_RED; + magicEffect = MAGIC_EFFECT_DRAW_BLOOD; + break; + } + + case COMBAT_ENERGYDAMAGE: + { + textColor = COLOR_PURPLE; + magicEffect = MAGIC_EFFECT_ENERGY_DAMAGE; + break; + } + + case COMBAT_EARTHDAMAGE: + { + textColor = COLOR_LIGHTGREEN; + magicEffect = MAGIC_EFFECT_POISON_RINGS; + break; + } + + case COMBAT_DROWNDAMAGE: + { + textColor = COLOR_LIGHTBLUE; + magicEffect = MAGIC_EFFECT_LOSE_ENERGY; + break; + } + + case COMBAT_FIREDAMAGE: + { + textColor = COLOR_ORANGE; + magicEffect = MAGIC_EFFECT_HITBY_FIRE; + break; + } + + case COMBAT_ICEDAMAGE: + { + textColor = COLOR_TEAL; + magicEffect = MAGIC_EFFECT_ICEATTACK; + break; + } + + case COMBAT_HOLYDAMAGE: + { + textColor = COLOR_YELLOW; + magicEffect = MAGIC_EFFECT_HOLYDAMAGE; + break; + } + + case COMBAT_DEATHDAMAGE: + { + textColor = COLOR_DARKRED; + magicEffect = MAGIC_EFFECT_SMALLCLOUDS; + break; + } + + case COMBAT_LIFEDRAIN: + { + textColor = COLOR_RED; + magicEffect = MAGIC_EFFECT_WRAPS_RED; + break; + } + + case COMBAT_MANADRAIN: + { + textColor = COLOR_BLUE; + magicEffect = MAGIC_EFFECT_LOSE_ENERGY; + } + + default: + break; + } +} + std::string getCombatName(CombatType_t combatType) { switch(combatType) @@ -1247,106 +1535,31 @@ std::string getSkillName(uint16_t skillId, bool suffix/* = true*/) return "unknown"; } -std::string getReason(int32_t reasonId) +std::string getWeaponName(WeaponType_t weaponType) { - switch(reasonId) + switch(weaponType) { - case 0: - return "Offensive Name"; - case 1: - return "Invalid Name Format"; - case 2: - return "Unsuitable Name"; - case 3: - return "Name Inciting Rule Violation"; - case 4: - return "Offensive Statement"; - case 5: - return "Spamming"; - case 6: - return "Illegal Advertising"; - case 7: - return "Off-Topic Public Statement"; - case 8: - return "Non-English Public Statement"; - case 9: - return "Inciting Rule Violation"; - case 10: - return "Bug Abuse"; - case 11: - return "Game Weakness Abuse"; - case 12: - return "Using Unofficial Software to Play"; - case 13: - return "Hacking"; - case 14: - return "Multi-Clienting"; - case 15: - return "Account Trading or Sharing"; - case 16: - return "Threatening Gamemaster"; - case 17: - return "Pretending to Have Influence on Rule Enforcement"; - case 18: - return "False Report to Gamemaster"; - case 19: - return "Destructive Behaviour"; - case 20: - return "Excessive Unjustified Player Killing"; + case WEAPON_SWORD: + return "sword"; + case WEAPON_CLUB: + return "club"; + case WEAPON_AXE: + return "axe"; + case WEAPON_DIST: + return "distance"; + case WEAPON_SHIELD: + return "shield"; + case WEAPON_WAND: + return "wand"; + case WEAPON_FIST: + return "fist"; + case WEAPON_AMMO: + return "ammunition"; default: break; } - return "Unknown Reason"; -} - -std::string getAction(ViolationAction_t actionId, bool ipBanishment) -{ - std::string action = "Unknown"; - switch(actionId) - { - case ACTION_NOTATION: - action = "Notation"; - break; - case ACTION_NAMEREPORT: - action = "Name Report"; - break; - case ACTION_BANISHMENT: - action = "Banishment"; - break; - case ACTION_BANREPORT: - action = "Name Report + Banishment"; - break; - case ACTION_BANFINAL: - action = "Banishment + Final Warning"; - break; - case ACTION_BANREPORTFINAL: - action = "Name Report + Banishment + Final Warning"; - break; - case ACTION_STATEMENT: - action = "Statement Report"; - break; - //internal use - case ACTION_DELETION: - action = "Deletion"; - break; - case ACTION_NAMELOCK: - action = "Name Lock"; - break; - case ACTION_BANLOCK: - action = "Name Lock + Banishment"; - break; - case ACTION_BANLOCKFINAL: - action = "Name Lock + Banishment + Final Warning"; - break; - default: - break; - } - - if(ipBanishment) - action += " + IP Banishment"; - - return action; + return ""; } std::string parseVocationString(StringVec vocStringVec) @@ -1466,9 +1679,9 @@ bool fileExists(const char* filename) return true; } -uint32_t adlerChecksum(uint8_t *data, size_t length) +uint32_t adlerChecksum(uint8_t* data, size_t length) { - if(length > NETWORKMESSAGE_MAXSIZE || length < 0) + if(length > NETWORK_MAX_SIZE || !length) return 0; const uint16_t adler = 65521; @@ -1483,7 +1696,6 @@ uint32_t adlerChecksum(uint8_t *data, size_t length) b += a; } while(--tmp); - a %= adler; b %= adler; } @@ -1491,53 +1703,53 @@ uint32_t adlerChecksum(uint8_t *data, size_t length) return (b << 16) | a; } -std::string getFilePath(FileType_t filetype, std::string filename) +std::string getFilePath(FileType_t type, std::string name/* = ""*/) { #ifdef __FILESYSTEM_HIERARCHY_STANDARD__ - std::string path = "/usr/share/tfs/"; - #endif + std::string path = "/var/lib/tfs/"; + #else std::string path = g_config.getString(ConfigManager::DATA_DIRECTORY); - switch(filetype) + #endif + switch(type) { case FILE_TYPE_OTHER: - path += filename; + path += name; break; case FILE_TYPE_XML: - path += "XML/" + filename; + path += "XML/" + name; break; case FILE_TYPE_LOG: #ifndef __FILESYSTEM_HIERARCHY_STANDARD__ - path += "logs/" + filename; + path = g_config.getString(ConfigManager::LOGS_DIRECTORY) + name; #else - path = "/var/log/tfs/" + filename; + path = "/var/log/tfs/" + name; #endif break; case FILE_TYPE_MOD: { #ifndef __FILESYSTEM_HIERARCHY_STANDARD__ - path = "mods/" + filename; + path = "mods/" + name; #else - path = "/etc/tfs/mods/" + filename; + path = "/usr/share/tfs/" + name; #endif break; } case FILE_TYPE_CONFIG: { - #if defined(__FILESYSTEM_HIERARCHY_STANDARD__) && defined(__HOMEDIR_CONF__) - if(fileExists("~/.tfs/" + filename)) - path = "~/.tfs/" + filename; + #if defined(__HOMEDIR_CONF__) + if(fileExists("~/.tfs/" + name)) + path = "~/.tfs/" + name; else - path = "/etc/tfs/" + filename; - - #elif defined(__FILESYSTEM_HIERARCHY_STANDARD__) - path = "/etc/tfs/" + filename; + #endif + #if defined(__FILESYSTEM_HIERARCHY_STANDARD__) + path = "/etc/tfs/" + name; #else - path = filename; + path = name; #endif break; } default: - std::cout << "ERROR: Wrong file type!" << std::endl; + std::clog << "> ERROR: Wrong file type!" << std::endl; break; } return path; diff --git a/tools.h b/tools.h index a171441..09a88d7 100644 --- a/tools.h +++ b/tools.h @@ -29,8 +29,16 @@ typedef std::vector StringVec; typedef std::vector IntegerVec; -typedef boost::tokenizer > tokenizer; +typedef boost::tokenizer > tokenizer; // TODO: replace by StringVec... typedef std::map VocationMap; +enum FileType_t +{ + FILE_TYPE_XML, + FILE_TYPE_LOG, + FILE_TYPE_OTHER, + FILE_TYPE_CONFIG, + FILE_TYPE_MOD +}; enum DistributionType_t { @@ -39,81 +47,112 @@ enum DistributionType_t DISTRO_NORMAL }; -enum FileType_t +template +inline void asString(const T& object, std::string& s) { - FILE_TYPE_XML, - FILE_TYPE_LOG, - FILE_TYPE_OTHER, - FILE_TYPE_CONFIG, - FILE_TYPE_MOD -}; + std::ostringstream ss; + ss << object; + s = ss.str(); +} -std::string transformToMD5(std::string plainText, bool upperCase); -std::string transformToSHA1(std::string plainText, bool upperCase); +template +inline std::string asString(const T& object) +{ + std::ostringstream ss; + ss << object; + return ss.str(); +} -void _encrypt(std::string& str, bool upperCase); -bool encryptTest(std::string plain, std::string& hash); +template +inline T fromString(const std::string& s) +{ + std::istringstream ss (s); + T t; + ss >> t; + return t; +} -void replaceString(std::string& text, const std::string key, const std::string value); void trim_right(std::string& source, const std::string& t); void trim_left(std::string& source, const std::string& t); +std::string trimString(std::string& str); + void toLowerCaseString(std::string& source); void toUpperCaseString(std::string& source); + std::string asLowerCaseString(const std::string& source); std::string asUpperCaseString(const std::string& source); -bool booleanString(std::string source); -bool readXMLInteger(xmlNodePtr node, const char* tag, int& value); -#if defined WINDOWS && !defined __GNUC__ -bool readXMLInteger(xmlNodePtr node, const char* tag, int32_t& value); -#endif -bool readXMLInteger64(xmlNodePtr node, const char* tag, int64_t& value); -bool readXMLFloat(xmlNodePtr node, const char* tag, float& value); -bool readXMLString(xmlNodePtr node, const char* tag, std::string& value); -bool readXMLContentString(xmlNodePtr node, std::string& value); -bool parseXMLContentString(xmlNodePtr node, std::string& value); -std::string getLastXMLError(); -bool utf8ToLatin1(char* intext, std::string& outtext); +bool replaceString(std::string& text, const std::string& key, const std::string& value); +bool booleanString(std::string source); -StringVec explodeString(const std::string& string, const std::string& separator); -IntegerVec vectorAtoi(StringVec stringVector); -bool hasBitSet(uint32_t flag, uint32_t flags); +char upchar(char character); +std::string ucfirst(std::string source); +std::string ucwords(std::string source); bool isNumber(char character); +bool isNumbers(std::string text); + bool isLowercaseLetter(char character); bool isUppercaseLetter(char character); + bool isPasswordCharacter(char character); bool isValidAccountName(std::string text); bool isValidPassword(std::string text); bool isValidName(std::string text, bool forceUppercaseOnFirstLetter = true); -bool isNumbers(std::string text); -char upchar(char character); +std::string transformToMD5(std::string plainText, bool upperCase); +std::string transformToSHA1(std::string plainText, bool upperCase); +std::string transformToSHA256(std::string plainText, bool upperCase); +std::string transformToSHA512(std::string plainText, bool upperCase); + +void _encrypt(std::string& str, bool upperCase); +bool encryptTest(std::string plain, std::string& hash); + +StringVec explodeString(const std::string& string, const std::string& separator, bool trim = true, uint16_t limit = 0); +IntegerVec vectorAtoi(StringVec stringVector); +std::string parseParams(tokenizer::iterator &it, tokenizer::iterator end); // TODO: replace by StringVec... + bool checkText(std::string text, std::string str); -std::string trimString(std::string& str); -std::string parseParams(tokenizer::iterator &it, tokenizer::iterator end); +std::string convertIPAddress(uint32_t ip); +std::string generateRecoveryKey(int32_t fieldCount, int32_t fieldLength, bool mixCase = false); -std::string generateRecoveryKey(int32_t fieldCount, int32_t fieldLength); -int32_t random_range(int32_t lowest_number, int32_t highest_number, DistributionType_t type = DISTRO_UNIFORM); +std::string formatDate(time_t _time = 0); +std::string formatDateEx(time_t _time = 0, std::string format = "%d %b %Y, %H:%M:%S"); +std::string formatTime(time_t _time = 0, bool miliseconds = false); -int32_t round(float v); uint32_t rand24b(); float box_muller(float m, float s); +int32_t random_range(int32_t lowestNumber, int32_t highestNumber, DistributionType_t type = DISTRO_UNIFORM); + +int32_t round(float v); +bool hasBitSet(uint32_t flag, uint32_t flags); +uint32_t adlerChecksum(uint8_t* data, size_t length); -Skulls_t getSkull(std::string strValue); -PartyShields_t getPartyShield(std::string strValue); +bool utf8ToLatin1(char* inText, std::string& outText); +bool latin1ToUtf8(char* inText, std::string& outText); + +bool readXMLInteger(xmlNodePtr node, const char* tag, int32_t& value); +bool readXMLInteger64(xmlNodePtr node, const char* tag, int64_t& value); +bool readXMLFloat(xmlNodePtr node, const char* tag, float& value); +bool readXMLString(xmlNodePtr node, const char* tag, std::string& value); +bool readXMLContentString(xmlNodePtr node, std::string& value); +bool parseXMLContentString(xmlNodePtr node, std::string& value); +std::string getLastXMLError(); + +std::string parseVocationString(StringVec vocStringVec); +bool parseVocationNode(xmlNodePtr vocationNode, VocationMap& vocationMap, StringVec& vocStringMap, std::string& errorStr); +bool parseIntegerVec(std::string str, IntegerVec& intVector); + +Skulls_t getSkulls(std::string strValue); +PartyShields_t getShields(std::string strValue); +GuildEmblems_t getEmblems(std::string strValue); Direction getDirection(std::string string); Direction getDirectionTo(Position pos1, Position pos2, bool extended = true); Direction getReverseDirection(Direction dir); Position getNextPosition(Direction direction, Position pos); -std::string formatDate(time_t _time = 0); -std::string formatDateShort(time_t _time = 0, bool detailed = false); -std::string formatTime(int32_t hours, int32_t minutes); -std::string convertIPAddress(uint32_t ip); - MagicEffect_t getMagicEffect(const std::string& strValue); ShootEffect_t getShootType(const std::string& strValue); Ammo_t getAmmoType(const std::string& strValue); @@ -121,19 +160,13 @@ AmmoAction_t getAmmoAction(const std::string& strValue); CombatType_t getCombatType(const std::string& strValue); FluidTypes_t getFluidType(const std::string& strValue); skills_t getSkillId(const std::string& strValue); +WeaponType_t getWeaponType(const std::string& strValue); +void getCombatDetails(CombatType_t combatType, MagicEffect_t& magicEffect, Color_t& textColor); std::string getCombatName(CombatType_t combatType); std::string getSkillName(uint16_t skillId, bool suffix = true); - -std::string getReason(int32_t reasonId); -std::string getAction(ViolationAction_t actionId, bool ipBanishment); - -std::string parseVocationString(StringVec vocStringVec); -bool parseVocationNode(xmlNodePtr vocationNode, VocationMap& vocationMap, StringVec& vocStringMap, std::string& errorStr); -bool parseIntegerVec(std::string str, IntegerVec& intVector); +std::string getWeaponName(WeaponType_t weaponType); bool fileExists(const char* filename); -uint32_t adlerChecksum(uint8_t *data, size_t length); - -std::string getFilePath(FileType_t filetype, std::string filename); +std::string getFilePath(FileType_t type, std::string name = ""); #endif diff --git a/town.h b/town.h index d508a82..0f63e10 100644 --- a/town.h +++ b/town.h @@ -64,7 +64,7 @@ class Towns { for(TownMap::iterator it = townMap.begin(); it != townMap.end(); ++it) { - if(!strcasecmp(it->second->getName().c_str(), townName.c_str())) + if(boost::algorithm::iequals(it->second->getName(), townName)) return it->second; } @@ -80,6 +80,9 @@ class Towns return NULL; } + TownMap::const_iterator getFirstTown() const {return townMap.begin();} + TownMap::const_iterator getLastTown() const {return townMap.end();} + private: TownMap townMap; }; diff --git a/trashholder.cpp b/trashholder.cpp index 358e55e..1271f4b 100644 --- a/trashholder.cpp +++ b/trashholder.cpp @@ -19,17 +19,18 @@ #include "game.h" #include "spells.h" +#include "const.h" extern Game g_game; -void TrashHolder::__addThing(Creature* actor, int32_t index, Thing* thing) +void TrashHolder::__addThing(Creature* actor, int32_t, Thing* thing) { if(Item* item = thing->getItem()) { - if(item == this || !item->isMoveable()) + if(item == this || !item->isMovable()) return; - if(getTile()->isSwimmingPool()) + if(g_game.isSwimmingPool(this, getTile(), true)) { if(item->getID() == ITEM_WATERBALL_SPLASH) return; @@ -45,30 +46,14 @@ void TrashHolder::__addThing(Creature* actor, int32_t index, Thing* thing) if(effect != MAGIC_EFFECT_NONE) g_game.addMagicEffect(getPosition(), effect); } - else if(getTile()->isSwimmingPool(false) && thing->getCreature()) + else if(g_game.isSwimmingPool(this, getTile(), false) && thing->getCreature()) { Player* player = thing->getCreature()->getPlayer(); if(player && player->getPosition() == player->getLastPosition()) { //player has just logged in a swimming pool - static Outfit_t outfit; - outfit.lookType = 267; + static Outfit_t outfit(SWIMMING_OUTFIT); Spell::CreateIllusion(player, outfit, -1); } } -} - -void TrashHolder::postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link /*= LINK_OWNER*/) -{ - if(getParent()) - getParent()->postAddNotification(actor, thing, oldParent, index, LINK_PARENT); -} - -void TrashHolder::postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link /*= LINK_OWNER*/) -{ - if(getParent()) - getParent()->postRemoveNotification(actor, thing, newParent, - index, isCompleteRemoval, LINK_PARENT); -} +} \ No newline at end of file diff --git a/trashholder.h b/trashholder.h index 8f50e3b..845d399 100644 --- a/trashholder.h +++ b/trashholder.h @@ -42,27 +42,35 @@ class TrashHolder : public Item, public Cylinder virtual Creature* getCreature() {return NULL;} virtual const Creature* getCreature() const {return NULL;} - virtual ReturnValue __queryAdd(int32_t index, const Thing* thing, uint32_t count, - uint32_t flags) const {return RET_NOERROR;} - virtual ReturnValue __queryMaxCount(int32_t index, const Thing* thing, uint32_t count, - uint32_t& maxQueryCount, uint32_t flags) const {return RET_NOERROR;} - virtual ReturnValue __queryRemove(const Thing* thing, uint32_t count, - uint32_t flags) const {return RET_NOTPOSSIBLE;} - virtual Cylinder* __queryDestination(int32_t& index, const Thing* thing, Item** destItem, - uint32_t& flags) {return this;} + virtual ReturnValue __queryAdd(int32_t, const Thing*, uint32_t, + uint32_t, Creature* = NULL) const {return RET_NOERROR;} + virtual ReturnValue __queryMaxCount(int32_t, const Thing*, uint32_t, + uint32_t&, uint32_t) const {return RET_NOERROR;} + virtual ReturnValue __queryRemove(const Thing*, uint32_t, + uint32_t, Creature* = NULL) const {return RET_NOTPOSSIBLE;} + virtual Cylinder* __queryDestination(int32_t&, const Thing*, Item**, + uint32_t&) {return this;} virtual void __addThing(Creature* actor, Thing* thing) {return __addThing(actor, 0, thing);} virtual void __addThing(Creature* actor, int32_t index, Thing* thing); - virtual void __updateThing(Thing* thing, uint16_t itemId, uint32_t count) {} - virtual void __replaceThing(uint32_t index, Thing* thing) {} + virtual void __updateThing(Thing*, uint16_t, uint32_t) {} + virtual void __replaceThing(uint32_t, Thing*) {} - virtual void __removeThing(Thing* thing, uint32_t count) {} + virtual void __removeThing(Thing*, uint32_t) {} virtual void postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent, - int32_t index, cylinderlink_t link = LINK_OWNER); + int32_t index, CylinderLink_t) + { + if(getParent()) + getParent()->postAddNotification(actor, thing, oldParent, index, LINK_PARENT); + } virtual void postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent, - int32_t index, bool isCompleteRemoval, cylinderlink_t link = LINK_OWNER); + int32_t index, bool isCompleteRemoval, CylinderLink_t) + { + if(getParent()) + getParent()->postRemoveNotification(actor, thing, newParent, index, isCompleteRemoval, LINK_PARENT); + } MagicEffect_t getEffect() const {return effect;} diff --git a/vc10/TheForgottenServer.ico b/vc10/TheForgottenServer.ico new file mode 100644 index 0000000000000000000000000000000000000000..0e06e96ea326737272bdec006c27310b79a7a436 GIT binary patch literal 32038 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 h`yQ)xI7=Z%-*Ub + + + + + + + $(TFS_LIBS) + + + + \ No newline at end of file diff --git a/vc10/arch64.props b/vc10/arch64.props new file mode 100644 index 0000000..6c086a5 --- /dev/null +++ b/vc10/arch64.props @@ -0,0 +1,12 @@ + + + + + + + + $(TFS_LIBS64) + + + + \ No newline at end of file diff --git a/vc10/debug.props b/vc10/debug.props new file mode 100644 index 0000000..7770acf --- /dev/null +++ b/vc10/debug.props @@ -0,0 +1,17 @@ + + + + + + true + + + + MultiThreadedDLL + + + Default + + + + \ No newline at end of file diff --git a/vc10/release.props b/vc10/release.props new file mode 100644 index 0000000..384ae26 --- /dev/null +++ b/vc10/release.props @@ -0,0 +1,18 @@ + + + + + + false + + + + Full + true + + + UseLinkTimeCodeGeneration + + + + \ No newline at end of file diff --git a/vc10/settings.props b/vc10/settings.props new file mode 100644 index 0000000..8d8cbfd --- /dev/null +++ b/vc10/settings.props @@ -0,0 +1,87 @@ + + + + + $(TFSSDKDir)\LuaJIT-2.0.0-beta9 + $(TFSSDKDir)\libxml2-2.7.8 + $(TFSSDKDir)\libiconv-1.14 + $(TFSSDKDir)\mpir-2.5.0 + $(TFSSDKDir)\sqlite3-3.7.7.1 + $(TFSSDKDir)\mysql-connector-c-6.0.2 + $(TFSSDKDir)\openssl-0.9.8k + $(TFSSDKDir)\postgresql-9.1.2-1 + __LUAJIT__;__USE_SQLITE__;__USE_MYSQL__;__ENABLE_SERVER_DIAGNOSTIC__;__EXCEPTION_TRACER__;_CRT_SECURE_NO_WARNINGS; + $(BoostDir);$(LUA_DIR)\include;$(LIBXML2_DIR)\include;$(LIBICONV_DIR)\include;$(GMP_DIR)\include;$(SQLITE_DIR)\include;$(MYSQLC_DIR)\include;$(OPENSSL_DIR)\include;$(POSTGRES_DIR)\include + $(BoostDir)\stage\lib;$(LUA_DIR)\lib86;$(LIBXML2_DIR)\lib;$(LIBICONV_DIR)\lib;$(GMP_DIR)\lib;$(SQLITE_DIR)\lib;$(MYSQLC_DIR)\lib;$(OPENSSL_DIR)\lib;$(POSTGRES_DIR)\lib + $(BoostDir)\stage64\lib;$(LUA_DIR)\lib64;$(LIBXML2_DIR)\lib64;$(LIBICONV_DIR)\lib64;$(GMP_DIR)\lib64;$(SQLITE_DIR)\lib64;$(MYSQLC_DIR)\lib64;$(OPENSSL_DIR)\lib64;$(POSTGRES_DIR)\lib64 + lua51.lib;libxml2.lib;mpir.lib;sqlite.lib;iconv.lib;libmysql.lib;libeay32.lib;dbghelp.lib + + + + + $(TFS_INCLUDES) + Level3 + true + Use + otpch.h + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(TFS_LIBDEPS) + Default + + + + + $(LUA_DIR) + true + + + $(LIBXML2_DIR) + true + + + $(LIBICONV_DIR) + true + + + $(GMP_DIR) + true + + + $(SQLITE_DIR) + true + + + $(MYSQLC_DIR) + true + + + $(OPENSSL_DIR) + true + + + $(POSTGRES_DIR) + true + + + $(PREPROCESSOR_DEFS) + true + + + $(TFS_INCLUDES) + true + + + $(TFS_LIBS) + true + + + $(TFS_LIBS64) + true + + + $(TFS_LIBDEPS) + true + + + \ No newline at end of file diff --git a/vc10/tfs.sln b/vc10/tfs.sln new file mode 100644 index 0000000..a4ecb33 --- /dev/null +++ b/vc10/tfs.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tfs", "tfs.vcxproj", "{25402E3A-A272-4B11-DA54-87375DF58282}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {25402E3A-A272-4B11-DA54-87375DF58282}.Debug|Win32.ActiveCfg = Debug|Win32 + {25402E3A-A272-4B11-DA54-87375DF58282}.Debug|Win32.Build.0 = Debug|Win32 + {25402E3A-A272-4B11-DA54-87375DF58282}.Debug|x64.ActiveCfg = Debug|x64 + {25402E3A-A272-4B11-DA54-87375DF58282}.Debug|x64.Build.0 = Debug|x64 + {25402E3A-A272-4B11-DA54-87375DF58282}.Release|Win32.ActiveCfg = Release|Win32 + {25402E3A-A272-4B11-DA54-87375DF58282}.Release|Win32.Build.0 = Release|Win32 + {25402E3A-A272-4B11-DA54-87375DF58282}.Release|x64.ActiveCfg = Release|x64 + {25402E3A-A272-4B11-DA54-87375DF58282}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vc10/tfs.vcxproj b/vc10/tfs.vcxproj new file mode 100644 index 0000000..f53e958 --- /dev/null +++ b/vc10/tfs.vcxproj @@ -0,0 +1,316 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Win32Proj + + + + Application + true + + + Application + true + + + Application + false + + + Application + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + true + + + true + + + + WIN32;_DEBUG;_CONSOLE;$(PREPROCESSOR_DEFS);%(PreprocessorDefinitions) + MultiThreadedDebugDLL + ProgramDatabase + Disabled + false + + + MachineX86 + true + Console + + + + + WIN32;_DEBUG;_CONSOLE;$(PREPROCESSOR_DEFS);%(PreprocessorDefinitions) + MultiThreadedDebugDLL + ProgramDatabase + Disabled + false + + + true + Console + + + + + WIN32;NDEBUG;_CONSOLE;$(PREPROCESSOR_DEFS);%(PreprocessorDefinitions) + MultiThreadedDLL + ProgramDatabase + + + MachineX86 + true + Console + true + true + + + + + WIN32;NDEBUG;_CONSOLE;$(PREPROCESSOR_DEFS);%(PreprocessorDefinitions) + MultiThreadedDLL + ProgramDatabase + + + true + Console + true + true + + + + + + \ No newline at end of file diff --git a/vc10/tfs.vcxproj.filters b/vc10/tfs.vcxproj.filters new file mode 100644 index 0000000..908942b --- /dev/null +++ b/vc10/tfs.vcxproj.filters @@ -0,0 +1,524 @@ + + + + + {3F06559B-9B91-421C-8886-9A1EACE3EAC3} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {DD8F8DC2-FDFD-41D6-915F-00B20A125F1D} + h;hpp;hxx;hm;inl;inc;xsd + + + {B2CB7932-D446-40FF-80AA-6F901A012AFF} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/vocation.cpp b/vocation.cpp index 267929d..99f4842 100644 --- a/vocation.cpp +++ b/vocation.cpp @@ -49,7 +49,7 @@ bool Vocations::parseVocationNode(xmlNodePtr p) if(!readXMLInteger(p, "id", intValue)) { - std::cout << "[Error - Vocations::parseVocationNode] Missing vocation id." << std::endl; + std::clog << "[Error - Vocations::parseVocationNode] Missing vocation id." << std::endl; return false; } @@ -57,6 +57,9 @@ bool Vocations::parseVocationNode(xmlNodePtr p) if(readXMLString(p, "name", strValue)) voc->setName(strValue); + if(readXMLInteger(p, "clientId", intValue)) + voc->setClientId(intValue); + if(readXMLString(p, "description", strValue)) voc->setDescription(strValue); @@ -111,6 +114,12 @@ bool Vocations::parseVocationNode(xmlNodePtr p) if(readXMLInteger(p, "lessloss", intValue)) voc->setLessLoss(intValue); + if(readXMLString(p, "droploot", strValue) || readXMLString(p, "lootdrop", strValue)) + voc->setDropLoot(booleanString(strValue)); + + if(readXMLString(p, "skillloss", strValue) || readXMLString(p, "lossskill", strValue)) + voc->setLossSkill(booleanString(strValue)); + for(xmlNodePtr configNode = p->children; configNode; configNode = configNode->next) { if(!xmlStrcmp(configNode->name, (const xmlChar*)"skill")) @@ -163,9 +172,9 @@ bool Vocations::parseVocationNode(xmlNodePtr p) if(readXMLInteger(configNode, "id", intValue)) { skills_t skill = (skills_t)intValue; - if(intValue < SKILL_FIRST || intValue >= SKILL__LAST) + if(skill < SKILL_FIRST || skill >= SKILL__LAST) { - std::cout << "[Error - Vocations::parseVocationNode] No valid skill id (" << intValue << ")." << std::endl; + std::clog << "[Error - Vocations::parseVocationNode] No valid skill id (" << intValue << ")." << std::endl; continue; } @@ -206,7 +215,7 @@ bool Vocations::parseVocationNode(xmlNodePtr p) { if(readXMLInteger(configNode, "percentAll", intValue)) { - for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) voc->increaseAbsorb((CombatType_t)i, intValue); } @@ -268,7 +277,7 @@ bool Vocations::parseVocationNode(xmlNodePtr p) { if(readXMLInteger(configNode, "percentAll", intValue)) { - for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) voc->increaseReflect(REFLECT_PERCENT, (CombatType_t)i, intValue); } @@ -328,7 +337,7 @@ bool Vocations::parseVocationNode(xmlNodePtr p) if(readXMLInteger(configNode, "chanceAll", intValue)) { - for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++) + for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1) voc->increaseReflect(REFLECT_CHANCE, (CombatType_t)i, intValue); } @@ -397,20 +406,20 @@ bool Vocations::loadFromXml() xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML,"vocations.xml").c_str()); if(!doc) { - std::cout << "[Warning - Vocations::loadFromXml] Cannot load vocations file." << std::endl; - std::cout << getLastXMLError() << std::endl; + std::clog << "[Warning - Vocations::loadFromXml] Cannot load vocations file." << std::endl; + std::clog << getLastXMLError() << std::endl; return false; } - xmlNodePtr p, root = xmlDocGetRootElement(doc); + xmlNodePtr root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"vocations")) { - std::cout << "[Error - Vocations::loadFromXml] Malformed vocations file." << std::endl; + std::clog << "[Error - Vocations::loadFromXml] Malformed vocations file." << std::endl; xmlFreeDoc(doc); return false; } - for(p = root->children; p; p = p->next) + for(xmlNodePtr p = root->children; p; p = p->next) parseVocationNode(p); xmlFreeDoc(doc); @@ -423,7 +432,7 @@ Vocation* Vocations::getVocation(uint32_t vocId) if(it != vocationsMap.end()) return it->second; - std::cout << "[Warning - Vocations::getVocation] Vocation " << vocId << " not found." << std::endl; + std::clog << "[Warning - Vocations::getVocation] Vocation " << vocId << " not found." << std::endl; return &Vocations::defVoc; } @@ -431,7 +440,7 @@ int32_t Vocations::getVocationId(const std::string& name) { for(VocationsMap::iterator it = vocationsMap.begin(); it != vocationsMap.end(); ++it) { - if(!strcasecmp(it->second->getName().c_str(), name.c_str())) + if(boost::algorithm::iequals(it->second->getName(), name)) return it->first; } @@ -463,8 +472,9 @@ void Vocation::reset() memset(reflect[REFLECT_CHANCE], 0, sizeof(reflect[REFLECT_CHANCE])); needPremium = false; - attackable = true; + attackable = dropLoot = skillLoss = true; lessLoss = fromVocation = 0; + clientId = 0; gain[GAIN_SOUL] = 100; gainTicks[GAIN_SOUL] = 120; baseSpeed = 220; @@ -478,29 +488,29 @@ void Vocation::reset() skillBase[SKILL_SHIELD] = 100; skillBase[SKILL_DIST] = 30; skillBase[SKILL_FISH] = 20; - for(int32_t i = SKILL_FIST; i < SKILL_DIST; i++) + for(int32_t i = SKILL_FIST; i < SKILL_DIST; ++i) skillBase[i] = 50; skillMultipliers[SKILL_FIST] = 1.5f; skillMultipliers[SKILL_FISH] = 1.1f; skillMultipliers[SKILL__LEVEL] = 1.0f; - for(int32_t i = SKILL_CLUB; i < SKILL_FISH; i++) + for(int32_t i = SKILL_CLUB; i < SKILL_FISH; ++i) skillMultipliers[i] = 2.0f; formulaMultipliers[MULTIPLIER_MANA] = 4.0f; - for(int32_t i = MULTIPLIER_FIRST; i < MULTIPLIER_LAST; i++) + for(int32_t i = MULTIPLIER_FIRST; i < MULTIPLIER_LAST; ++i) formulaMultipliers[i] = 1.0f; } int16_t Vocation::getReflect(CombatType_t combat) const { - if(reflect[REFLECT_CHANCE][combat] < random_range(0, 100)) + if(reflect[REFLECT_CHANCE][combat] >= random_range(1, 100)) return reflect[REFLECT_PERCENT][combat]; return 0; } -uint32_t Vocation::getReqSkillTries(int32_t skill, int32_t level) +uint64_t Vocation::getReqSkillTries(int32_t skill, int32_t level) { if(skill < SKILL_FIRST || skill > SKILL_LAST) return 0; @@ -510,7 +520,7 @@ uint32_t Vocation::getReqSkillTries(int32_t skill, int32_t level) if(it != cacheSkill[skill].end()) return it->second; - skillMap[level] = (uint32_t)(skillBase[skill] * std::pow(skillMultipliers[skill], (level - 11))); + skillMap[level] = (uint64_t)(skillBase[skill] * std::pow(skillMultipliers[skill], (level - 11))); return skillMap[level]; } diff --git a/vocation.h b/vocation.h index 248bd51..4185880 100644 --- a/vocation.h +++ b/vocation.h @@ -20,6 +20,7 @@ #include "otsystem.h" #include "enums.h" +#include "const.h" enum multiplier_t { @@ -58,13 +59,16 @@ class Vocation uint32_t getId() const {return id;} void setId(int32_t v) {id = v;} + uint16_t getClientId() const {return clientId;} + void setClientId(uint16_t v) {clientId = v;} + uint32_t getFromVocation() const {return fromVocation;} void setFromVocation(int32_t v) {fromVocation = v;} - const std::string& getName() const {return name;} + std::string getName() const {return name;} void setName(const std::string& v) {name = v;} - const std::string& getDescription() const {return description;} + std::string getDescription() const {return description;} void setDescription(const std::string& v) {description = v;} bool isAttackable() const {return attackable;} @@ -73,6 +77,12 @@ class Vocation bool isPremiumNeeded() const {return needPremium;} void setNeedPremium(bool v) {needPremium = v;} + bool getDropLoot() const {return dropLoot;} + void setDropLoot(bool v) {dropLoot = v;} + + bool getLossSkill() const {return skillLoss;} + void setLossSkill(bool v) {skillLoss = v;} + uint32_t getAttackSpeed() const {return attackSpeed;} void setAttackSpeed(uint32_t v) {attackSpeed = v;} @@ -107,15 +117,16 @@ class Vocation void setSkillMultiplier(skills_t s, float v) {skillMultipliers[s] = v;} void setSkillBase(skills_t s, uint32_t v) {skillBase[s] = v;} - uint32_t getReqSkillTries(int32_t skill, int32_t level); + uint64_t getReqSkillTries(int32_t skill, int32_t level); uint64_t getReqMana(uint32_t magLevel); private: - typedef std::map cacheMap; + typedef std::map cacheMap; cacheMap cacheSkill[SKILL_LAST + 1]; cacheMap cacheMana; - bool attackable, needPremium; + bool attackable, needPremium, dropLoot, skillLoss; + uint16_t clientId; int32_t lessLoss, capGain; uint32_t id, fromVocation, baseSpeed, attackSpeed; std::string name, description; diff --git a/waitlist.cpp b/waitlist.cpp index f605925..56c0cfc 100644 --- a/waitlist.cpp +++ b/waitlist.cpp @@ -28,10 +28,9 @@ extern Game g_game; WaitList::iterator WaitingList::find(const Player* player, uint32_t& slot) { slot = 1; - std::string name = asLowerCaseString(player->getName()); for(WaitList::iterator it = waitList.begin(); it != waitList.end(); ++it) { - if((*it)->ip == player->getIP() && asLowerCaseString((*it)->name) == name) + if((*it)->ip == player->getIP() && boost::algorithm::iequals((*it)->name, player->getName())) return it; ++slot; diff --git a/waypoints.h b/waypoints.h index 61c2650..01bda9f 100644 --- a/waypoints.h +++ b/waypoints.h @@ -36,7 +36,7 @@ class Waypoints { public: // Does not require either constructor nor destructor - void addWaypoint(WaypointPtr waypoint); + inline void addWaypoint(WaypointPtr waypoint); WaypointPtr getWaypointByName(const std::string& name) const; const WaypointMap& getWaypointsMap() const {return waypoints;} diff --git a/weapons.cpp b/weapons.cpp index 44d414f..1a27e49 100644 --- a/weapons.cpp +++ b/weapons.cpp @@ -81,12 +81,12 @@ bool Weapons::loadDefaults() break; } - case WEAPON_AMMO: case WEAPON_DIST: - { - if(it->weaponType == WEAPON_DIST && it->ammoType != AMMO_NONE) - continue; + if(it->ammoType != AMMO_NONE) + break; + case WEAPON_AMMO: + { if(WeaponDistance* weapon = new WeaponDistance(&m_interface)) { weapon->configureWeapon(*it); @@ -111,7 +111,7 @@ Event* Weapons::getEvent(const std::string& nodeName) if(tmpNodeName == "melee") return new WeaponMelee(&m_interface); - if(tmpNodeName == "distance" || tmpNodeName == "ammunition") + if(tmpNodeName == "distance" || tmpNodeName == "ammunition" || tmpNodeName == "ammo") return new WeaponDistance(&m_interface); if(tmpNodeName == "wand" || tmpNodeName == "rod") @@ -120,7 +120,7 @@ Event* Weapons::getEvent(const std::string& nodeName) return NULL; } -bool Weapons::registerEvent(Event* event, xmlNodePtr p, bool override) +bool Weapons::registerEvent(Event* event, xmlNodePtr, bool override) { Weapon* weapon = dynamic_cast(event); if(!weapon) @@ -140,7 +140,7 @@ bool Weapons::registerEvent(Event* event, xmlNodePtr p, bool override) return true; } - std::cout << "[Warning - Weapons::registerEvent] Duplicate registered item with id: " << weapon->getID() << std::endl; + std::clog << "[Warning - Weapons::registerEvent] Duplicate registered item with id: " << weapon->getID() << std::endl; return false; } @@ -154,7 +154,7 @@ int32_t Weapons::getMaxWeaponDamage(int32_t level, int32_t attackSkill, int32_t return (int32_t)std::ceil((2 * (attackValue * (attackSkill + 5.8) / 25 + (level - 1) / 10.)) / attackFactor); } -Weapon::Weapon(LuaScriptInterface* _interface): +Weapon::Weapon(LuaInterface* _interface): Event(_interface) { id = 0; @@ -174,18 +174,13 @@ Weapon::Weapon(LuaScriptInterface* _interface): params.combatType = COMBAT_PHYSICALDAMAGE; } -Weapon::~Weapon() -{ - // -} - bool Weapon::configureEvent(xmlNodePtr p) { int32_t intValue, wieldInfo = 0; std::string strValue; if(!readXMLInteger(p, "id", intValue)) { - std::cout << "Error: [Weapon::configureEvent] Weapon without id." << std::endl; + std::clog << "Error: [Weapon::configureEvent] Weapon without id." << std::endl; return false; } @@ -242,7 +237,7 @@ bool Weapon::configureEvent(xmlNodePtr p) while(vocationNode) { if(!parseVocationNode(vocationNode, vocWeaponMap, vocStringVec, error)) - std::cout << "[Warning - Weapon::configureEvent] " << error << std::endl; + std::clog << "[Warning - Weapon::configureEvent] " << error << std::endl; vocationNode = vocationNode->next; } @@ -253,9 +248,10 @@ bool Weapon::configureEvent(xmlNodePtr p) if(wieldInfo) { ItemType& it = Item::items.getItemType(id); - it.wieldInfo = wieldInfo; - it.minReqLevel = getReqLevel(); it.minReqMagicLevel = getReqMagLv(); + it.minReqLevel = getReqLevel(); + + it.wieldInfo = wieldInfo; it.vocationString = parseVocationString(vocStringVec); } @@ -287,12 +283,13 @@ int32_t Weapon::playerWeaponCheck(Player* player, Creature* target) const if(playerPos.z != targetPos.z) return 0; - const ItemType& it = Item::items[getID()]; - int32_t range; + const ItemType& it = Item::items[id]; + int32_t range = it.shootRange; if(it.weaponType == WEAPON_AMMO) - range = player->getShootRange(); - else - range = it.shootRange; + { + if(Item* item = player->getWeapon(true)) + range = item->getShootRange(); + } if(std::max(std::abs(playerPos.x - targetPos.x), std::abs(playerPos.y - targetPos.y)) > range) return 0; @@ -315,23 +312,41 @@ int32_t Weapon::playerWeaponCheck(Player* player, Creature* target) const if(!vocWeaponMap.empty() && vocWeaponMap.find(player->getVocationId()) == vocWeaponMap.end()) return 0; - int32_t damageModifier = 100; + int32_t modifier = 100; if(player->getLevel() < getReqLevel()) - damageModifier = (isWieldedUnproperly() ? damageModifier / 2 : 0); + { + if(!isWieldedUnproperly()) + return 0; + + double penalty = (getReqLevel() - player->getLevel()) * 0.02; + if(penalty > 0.5) + penalty = 0.5; + + modifier -= (int32_t)(modifier * penalty); + } if(player->getMagicLevel() < getReqMagLv()) - damageModifier = (isWieldedUnproperly() ? damageModifier / 2 : 0); + { + if(!isWieldedUnproperly()) + return 0; - return damageModifier; + double penalty = (getReqMagLv() - player->getMagicLevel()) * 0.02; + if(penalty > 0.5) + penalty = 0.5; + + modifier -= (int32_t)(modifier * penalty); + } + + return modifier; } bool Weapon::useWeapon(Player* player, Item* item, Creature* target) const { - int32_t damageModifier = playerWeaponCheck(player, target); - if(!damageModifier) + int32_t modifier = playerWeaponCheck(player, target); + if(!modifier) return false; - return internalUseWeapon(player, item, target, damageModifier); + return internalUseWeapon(player, item, target, modifier); } bool Weapon::useFist(Player* player, Creature* target) @@ -342,11 +357,10 @@ bool Weapon::useFist(Player* player, Creature* target) return false; float attackFactor = player->getAttackFactor(); - int32_t attackSkill = player->getSkill(SKILL_FIST, SKILL_LEVEL); - int32_t attackValue = 7; + int32_t attackSkill = player->getSkill(SKILL_FIST, SKILL_LEVEL), attackValue = g_config.getNumber(ConfigManager::FIST_BASE_ATTACK); double maxDamage = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor); - if(random_range(1, 100) <= g_config.getNumber(ConfigManager::CRITICAL_HIT_CHANCE)) + if(g_config.getNumber(ConfigManager::CRITICAL_HIT_CHANCE) >= random_range(1, 100)) { maxDamage = std::pow(maxDamage, g_config.getDouble(ConfigManager::CRITICAL_HIT_MUL)); player->sendCritical(); @@ -371,7 +385,7 @@ bool Weapon::useFist(Player* player, Creature* target) return true; } -bool Weapon::internalUseWeapon(Player* player, Item* item, Creature* target, int32_t damageModifier) const +bool Weapon::internalUseWeapon(Player* player, Item* item, Creature* target, int32_t modifier) const { if(isScripted()) { @@ -382,8 +396,12 @@ bool Weapon::internalUseWeapon(Player* player, Item* item, Creature* target, int } else { - int32_t damage = (getWeaponDamage(player, target, item) * damageModifier) / 100; - Combat::doCombatHealth(player, target, damage, damage, params); + CombatParams _params = params; + _params.element.type = item->getElementType(); + _params.element.damage = getWeaponElementDamage(player, item); + + int32_t damage = (getWeaponDamage(player, target, item) * modifier) / 100; + Combat::doCombatHealth(player, target, damage, damage, _params); } onUsedAmmo(player, item, target->getTile()); @@ -411,25 +429,25 @@ bool Weapon::internalUseWeapon(Player* player, Item* item, Tile* tile) const return true; } -void Weapon::onUsedWeapon(Player* player, Item* item, Tile* destTile) const +void Weapon::onUsedWeapon(Player* player, Item* item, Tile*) const { if(!player->hasFlag(PlayerFlag_NotGainSkill)) { skills_t skillType; - uint32_t skillPoint = 0; + uint64_t skillPoint = 0; if(getSkillType(player, item, skillType, skillPoint)) player->addSkillAdvance(skillType, skillPoint); } if(!player->hasFlag(PlayerFlag_HasNoExhaustion) && exhaustion > 0) - player->addExhaust(exhaustion, EXHAUST_COMBAT); + player->addExhaust(exhaustion, EXHAUST_MELEE); int32_t manaCost = getManaCost(player); if(manaCost > 0) { player->changeMana(-manaCost); - if(!player->hasFlag(PlayerFlag_NotGainMana) && (player->getZone() != ZONE_PVP - || !g_config.getBool(ConfigManager::PVPZONE_ADDMANASPENT))) + if(!player->hasFlag(PlayerFlag_NotGainMana) && (player->getZone() != ZONE_HARDCORE + || g_config.getBool(ConfigManager::PVPZONE_ADDMANASPENT))) player->addManaSpent(manaCost); } @@ -469,17 +487,10 @@ void Weapon::onUsedAmmo(Player* player, Item* item, Tile* destTile) const int32_t Weapon::getManaCost(const Player* player) const { - if(mana != 0) + if(mana) return mana; - if(manaPercent != 0) - { - int32_t maxMana = player->getMaxMana(); - int32_t manaCost = (maxMana * manaPercent) / 100; - return manaCost; - } - - return 0; + return manaPercent ? (player->getMaxMana() * manaPercent) / 100 : 0; } bool Weapon::executeUseWeapon(Player* player, const LuaVariant& var) const @@ -496,7 +507,9 @@ bool Weapon::executeUseWeapon(Player* player, const LuaVariant& var) const scriptstream << "local cid = " << env->addThing(player) << std::endl; env->streamVariant(scriptstream, "var", var); - scriptstream << m_scriptData; + if(m_scriptData) + scriptstream << *m_scriptData; + bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { @@ -512,7 +525,7 @@ bool Weapon::executeUseWeapon(Player* player, const LuaVariant& var) const #ifdef __DEBUG_LUASCRIPTS__ char desc[60]; sprintf(desc, "onUseWeapon - %s", player->getName().c_str()); - env->setEventDesc(desc); + env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); @@ -531,73 +544,38 @@ bool Weapon::executeUseWeapon(Player* player, const LuaVariant& var) const } else { - std::cout << "[Error - Weapon::executeUseWeapon] Call stack overflow" << std::endl; + std::clog << "[Error - Weapon::executeUseWeapon] Call stack overflow" << std::endl; return false; } } -WeaponMelee::WeaponMelee(LuaScriptInterface* _interface): - Weapon(_interface) -{ - elementType = COMBAT_NONE; - elementDamage = 0; -} - -bool WeaponMelee::configureEvent(xmlNodePtr p) -{ - return Weapon::configureEvent(p); -} - -bool WeaponMelee::configureWeapon(const ItemType& it) -{ - elementType = it.abilities.elementType; - elementDamage = it.abilities.elementDamage; - return Weapon::configureWeapon(it); -} +WeaponMelee::WeaponMelee(LuaInterface* _interface): + Weapon(_interface) {} bool WeaponMelee::useWeapon(Player* player, Item* item, Creature* target) const { - if(!Weapon::useWeapon(player, item, target)) + int32_t modifier = playerWeaponCheck(player, target); + if(!modifier) return false; - if(elementDamage && elementType != COMBAT_NONE) - { - CombatParams element; - element.combatType = elementType; - - int32_t damage = getElementDamage(player, item); - Combat::doCombatHealth(player, target, damage, damage, element); - } - - return true; -} - -void WeaponMelee::onUsedWeapon(Player* player, Item* item, Tile* destTile) const -{ - Weapon::onUsedWeapon(player, item, destTile); -} - -void WeaponMelee::onUsedAmmo(Player* player, Item* item, Tile* destTile) const -{ - Weapon::onUsedAmmo(player, item, destTile); + return internalUseWeapon(player, item, target, modifier); } bool WeaponMelee::getSkillType(const Player* player, const Item* item, - skills_t& skill, uint32_t& skillpoint) const + skills_t& skill, uint64_t& skillPoint) const { - skillpoint = 0; + skillPoint = 0; if(player->getAddAttackSkill()) { switch(player->getLastAttackBlockType()) { case BLOCK_ARMOR: case BLOCK_NONE: - skillpoint = 1; + skillPoint = 1; break; case BLOCK_DEFENSE: default: - skillpoint = 0; break; } } @@ -635,14 +613,14 @@ bool WeaponMelee::getSkillType(const Player* player, const Item* item, return false; } -int32_t WeaponMelee::getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage /*= false*/) const +int32_t WeaponMelee::getWeaponDamage(const Player* player, const Creature*, const Item* item, bool maxDamage /*= false*/) const { - int32_t attackSkill = player->getWeaponSkill(item); - int32_t attackValue = std::max((int32_t)0, (int32_t(item->getAttack() + item->getExtraAttack()) - elementDamage)); + int32_t attackSkill = player->getWeaponSkill(item), attackValue = std::max((int32_t)0, + (int32_t(item->getAttack() + item->getExtraAttack()) - item->getElementDamage())); float attackFactor = player->getAttackFactor(); double maxValue = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor); - if(random_range(1, 100) <= g_config.getNumber(ConfigManager::CRITICAL_HIT_CHANCE)) + if(g_config.getNumber(ConfigManager::CRITICAL_HIT_CHANCE) >= random_range(1, 100)) { maxValue = std::pow(maxValue, g_config.getDouble(ConfigManager::CRITICAL_HIT_MUL)); player->sendCritical(); @@ -659,38 +637,32 @@ int32_t WeaponMelee::getWeaponDamage(const Player* player, const Creature* targe return -random_range(0, ret, DISTRO_NORMAL); } -int32_t WeaponMelee::getElementDamage(const Player* player, const Item* item) const +int32_t WeaponMelee::getWeaponElementDamage(const Player* player, const Item* item, bool maxDamage/* = false*/) const { - int32_t attackSkill = player->getWeaponSkill(item); + int32_t attackSkill = player->getWeaponSkill(item), attackValue = item->getElementDamage(); float attackFactor = player->getAttackFactor(); - double maxValue = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, elementDamage, attackFactor); - if(random_range(1, 100) <= g_config.getNumber(ConfigManager::CRITICAL_HIT_CHANCE)) - { - maxValue = std::pow(maxValue, g_config.getDouble(ConfigManager::CRITICAL_HIT_MUL)); - player->sendCritical(); - } + double maxValue = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor); Vocation* vocation = player->getVocation(); if(vocation && vocation->getMultiplier(MULTIPLIER_MELEE) != 1.0) maxValue *= vocation->getMultiplier(MULTIPLIER_MELEE); - return -random_range(0, (int32_t)std::floor(maxValue), DISTRO_NORMAL); + int32_t ret = (int32_t)std::floor(maxValue); + if(maxDamage) + return -ret; + + return -random_range(0, ret, DISTRO_NORMAL); } -WeaponDistance::WeaponDistance(LuaScriptInterface* _interface): +WeaponDistance::WeaponDistance(LuaInterface* _interface): Weapon(_interface) { hitChance = -1; - maxHitChance = breakChance = ammoAttackValue = 0; + maxHitChance = breakChance = attack = 0; swing = params.blockedByShield = false; } -bool WeaponDistance::configureEvent(xmlNodePtr p) -{ - return Weapon::configureEvent(p); -} - bool WeaponDistance::configureWeapon(const ItemType& it) { if(it.ammoType != AMMO_NONE) //hit chance on two-handed weapons is limited to 90% @@ -711,7 +683,7 @@ bool WeaponDistance::configureWeapon(const ItemType& it) ammoAction = it.ammoAction; params.effects.distance = it.shootType; - ammoAttackValue = it.attack; + attack = it.attack; return Weapon::configureWeapon(it); } @@ -720,11 +692,10 @@ int32_t WeaponDistance::playerWeaponCheck(Player* player, Creature* target) cons const ItemType& it = Item::items[id]; if(it.weaponType == WEAPON_AMMO) { - if(Item* bow = player->getWeapon(true)) + if(Item* item = player->getWeapon(true)) { - const Weapon* boWeapon = g_weapons->getWeapon(bow); - if(boWeapon) - return boWeapon->playerWeaponCheck(player, target); + if(const Weapon* weapon = g_weapons->getWeapon(item)) + return weapon->playerWeaponCheck(player, target); } } @@ -733,19 +704,19 @@ int32_t WeaponDistance::playerWeaponCheck(Player* player, Creature* target) cons bool WeaponDistance::useWeapon(Player* player, Item* item, Creature* target) const { - int32_t damageModifier = playerWeaponCheck(player, target); - if(damageModifier == 0) + int32_t modifier = playerWeaponCheck(player, target); + if(!modifier) return false; - int32_t chance; - if(hitChance == -1) + int32_t chance = hitChance; + if(chance == -1) { //hit chance is based on distance to target and distance skill - uint32_t skill = player->getSkill(SKILL_DIST, SKILL_LEVEL); const Position& playerPos = player->getPosition(); const Position& targetPos = target->getPosition(); - uint32_t distance = std::max(std::abs(playerPos.x - targetPos.x), std::abs(playerPos.y - targetPos.y)); + uint32_t distance = std::max(std::abs(playerPos.x - targetPos.x), std::abs( + playerPos.y - targetPos.y)), skill = player->getSkill(SKILL_DIST, SKILL_LEVEL); if(maxHitChance == 75) { //chance for one-handed weapons @@ -841,17 +812,15 @@ bool WeaponDistance::useWeapon(Player* player, Item* item, Creature* target) con else chance = maxHitChance; } - else - chance = hitChance; if(item->getWeaponType() == WEAPON_AMMO) { Item* bow = player->getWeapon(true); - if(bow && bow->getHitChance() > 0) + if(bow && bow->getHitChance() != -1) chance += bow->getHitChance(); } - if(chance < random_range(1, 100)) + if(random_range(1, 100) > chance) { //we failed attack, miss! Tile* destTile = target->getTile(); @@ -867,49 +836,47 @@ bool WeaponDistance::useWeapon(Player* player, Item* item, Creature* target) con destList.push_back(std::make_pair(1, -1)); destList.push_back(std::make_pair(1, 0)); destList.push_back(std::make_pair(1, 1)); - std::random_shuffle(destList.begin(), destList.end()); + std::random_shuffle(destList.begin(), destList.end()); Position destPos = target->getPosition(); + Tile* tmpTile = NULL; for(std::vector >::iterator it = destList.begin(); it != destList.end(); ++it) { - if((tmpTile = g_game.getTile(destPos.x + it->first, destPos.y + it->second, destPos.z)) - && !tmpTile->hasProperty(IMMOVABLEBLOCKSOLID) && tmpTile->ground) - { - destTile = tmpTile; - break; - } + if(!(tmpTile = g_game.getTile(destPos.x + it->first, destPos.y + it->second, destPos.z)) + || tmpTile->hasProperty(IMMOVABLEBLOCKSOLID) || !tmpTile->ground) + continue; + + destTile = tmpTile; + break; } } Weapon::internalUseWeapon(player, item, destTile); } else - Weapon::internalUseWeapon(player, item, target, damageModifier); + Weapon::internalUseWeapon(player, item, target, modifier); return true; } -void WeaponDistance::onUsedWeapon(Player* player, Item* item, Tile* destTile) const -{ - Weapon::onUsedWeapon(player, item, destTile); -} - void WeaponDistance::onUsedAmmo(Player* player, Item* item, Tile* destTile) const { - if(ammoAction == AMMOACTION_MOVEBACK && breakChance > 0 && random_range(1, 100) < breakChance) - g_game.transformItem(item, item->getID(), std::max(0, item->getItemCount() - 1)); + if(!g_config.getBool(ConfigManager::REMOVE_WEAPON_AMMO)) + return; + + if(ammoAction == AMMOACTION_MOVEBACK && breakChance > 0 && random_range(1, 100) <= breakChance) + g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getItemCount() - 1)); else Weapon::onUsedAmmo(player, item, destTile); } int32_t WeaponDistance::getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage /*= false*/) const { - int32_t attackValue = ammoAttackValue; + int32_t attackValue = attack; if(item->getWeaponType() == WEAPON_AMMO) { - Item* bow = const_cast(player)->getWeapon(true); - if(bow) + if(Item* bow = const_cast(player)->getWeapon(true)) attackValue += bow->getAttack() + bow->getExtraAttack(); } @@ -917,9 +884,9 @@ int32_t WeaponDistance::getWeaponDamage(const Player* player, const Creature* ta float attackFactor = player->getAttackFactor(); double maxValue = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor); - if(random_range(1, 100) <= g_config.getNumber(ConfigManager::CRITICAL_HIT_CHANCE)) + if(g_config.getNumber(ConfigManager::CRITICAL_HIT_CHANCE) >= random_range(1, 100)) { - maxValue = std::pow(maxValue, g_config.getDouble(ConfigManager::CRITICAL_HIT_MUL)); + maxValue *= g_config.getDouble(ConfigManager::CRITICAL_HIT_MUL); player->sendCritical(); } @@ -943,34 +910,33 @@ int32_t WeaponDistance::getWeaponDamage(const Player* player, const Creature* ta return -random_range(minValue, ret, DISTRO_NORMAL); } -bool WeaponDistance::getSkillType(const Player* player, const Item* item, - skills_t& skill, uint32_t& skillpoint) const +bool WeaponDistance::getSkillType(const Player* player, const Item*, + skills_t& skill, uint64_t& skillPoint) const { skill = SKILL_DIST; - skillpoint = 0; - + skillPoint = 0; if(player->getAddAttackSkill()) { switch(player->getLastAttackBlockType()) { case BLOCK_NONE: - skillpoint = 2; + skillPoint = 2; break; case BLOCK_ARMOR: - skillpoint = 1; + skillPoint = 1; break; case BLOCK_DEFENSE: default: - skillpoint = 0; break; } } + return true; } -WeaponWand::WeaponWand(LuaScriptInterface* _interface): +WeaponWand::WeaponWand(LuaInterface* _interface): Weapon(_interface) { minChange = 0; @@ -1000,7 +966,7 @@ bool WeaponWand::configureWeapon(const ItemType& it) return Weapon::configureWeapon(it); } -int32_t WeaponWand::getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage /* = false*/) const +int32_t WeaponWand::getWeaponDamage(const Player* player, const Creature*, const Item*, bool maxDamage /* = false*/) const { float multiplier = 1.0f; if(Vocation* vocation = player->getVocation()) diff --git a/weapons.h b/weapons.h index 373771f..732ecaa 100644 --- a/weapons.h +++ b/weapons.h @@ -52,8 +52,8 @@ class Weapons : public BaseEvents virtual Event* getEvent(const std::string& nodeName); virtual bool registerEvent(Event* event, xmlNodePtr p, bool override); - virtual LuaScriptInterface& getInterface() {return m_interface;} - LuaScriptInterface m_interface; + virtual LuaInterface& getInterface() {return m_interface;} + LuaInterface m_interface; typedef std::map WeaponMap; WeaponMap weapons; @@ -62,29 +62,30 @@ class Weapons : public BaseEvents class Weapon : public Event { public: - Weapon(LuaScriptInterface* _interface); - virtual ~Weapon(); + Weapon(LuaInterface* _interface); + virtual ~Weapon() {} + + static bool useFist(Player* player, Creature* target); - virtual bool configureEvent(xmlNodePtr p); virtual bool loadFunction(const std::string& functionName); + virtual bool configureEvent(xmlNodePtr p); virtual bool configureWeapon(const ItemType& it); virtual int32_t playerWeaponCheck(Player* player, Creature* target) const; - static bool useFist(Player* player, Creature* target); - virtual bool useWeapon(Player* player, Item* item, Creature* target) const; + uint16_t getID() const {return id;} + virtual bool interruptSwing() const {return !swing;} CombatParams getCombatParam() const {return params;} - uint16_t getID() const {return id;} + virtual bool useWeapon(Player* player, Item* item, Creature* target) const; virtual int32_t getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage = false) const = 0; - virtual int32_t getElementDamage(const Player* player, const Creature* target) const {return 0;} - virtual bool interruptSwing() const {return !swing;} + virtual int32_t getWeaponElementDamage(const Player*, const Item*, bool = false) const {return 0;} - const uint32_t getReqLevel() const {return level;} - const uint32_t getReqMagLv() const {return magLevel;} - const bool hasExhaustion() const {return exhaustion;} - const bool isPremium() const {return premium;} - const bool isWieldedUnproperly() const {return wieldUnproperly;} + uint32_t getReqLevel() const {return level;} + uint32_t getReqMagLv() const {return magLevel;} + bool hasExhaustion() const {return exhaustion != 0;} + bool isPremium() const {return premium;} + bool isWieldedUnproperly() const {return wieldUnproperly;} protected: virtual std::string getScriptEventName() const {return "onUseWeapon";} @@ -97,22 +98,16 @@ class Weapon : public Event virtual void onUsedWeapon(Player* player, Item* item, Tile* destTile) const; virtual void onUsedAmmo(Player* player, Item* item, Tile* destTile) const; - virtual bool getSkillType(const Player* player, const Item* item, skills_t& skill, uint32_t& skillpoint) const {return false;} + virtual bool getSkillType(const Player*, const Item*, skills_t&, uint64_t&) const {return false;} int32_t getManaCost(const Player* player) const; uint16_t id; - bool enabled; - bool premium; uint32_t exhaustion; - bool wieldUnproperly; - int32_t level; - int32_t magLevel; - int32_t mana; - int32_t manaPercent; - int32_t soul; + bool enabled, premium, wieldUnproperly, swing; + int32_t level, magLevel, mana, manaPercent, soul; + AmmoAction_t ammoAction; - bool swing; CombatParams params; private: @@ -122,50 +117,41 @@ class Weapon : public Event class WeaponMelee : public Weapon { public: - WeaponMelee(LuaScriptInterface* _interface); + WeaponMelee(LuaInterface* _interface); virtual ~WeaponMelee() {} - virtual bool configureEvent(xmlNodePtr p); - virtual bool configureWeapon(const ItemType& it); - virtual bool useWeapon(Player* player, Item* item, Creature* target) const; virtual int32_t getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage = false) const; - virtual int32_t getElementDamage(const Player* player, const Item* item) const; + virtual int32_t getWeaponElementDamage(const Player* player, const Item* item, bool maxDamage = false) const; protected: - virtual void onUsedWeapon(Player* player, Item* item, Tile* destTile) const; - virtual void onUsedAmmo(Player* player, Item* item, Tile* destTile) const; - virtual bool getSkillType(const Player* player, const Item* item, skills_t& skill, uint32_t& skillpoint) const; - - CombatType_t elementType; - int16_t elementDamage; + virtual bool getSkillType(const Player* player, const Item* item, skills_t& skill, uint64_t& skillPoint) const; }; class WeaponDistance : public Weapon { public: - WeaponDistance(LuaScriptInterface* _interface); + WeaponDistance(LuaInterface* _interface); virtual ~WeaponDistance() {} - virtual bool configureEvent(xmlNodePtr p); virtual bool configureWeapon(const ItemType& it); virtual int32_t playerWeaponCheck(Player* player, Creature* target) const; + virtual bool useWeapon(Player* player, Item* item, Creature* target) const; virtual int32_t getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage = false) const; protected: - virtual void onUsedWeapon(Player* player, Item* item, Tile* destTile) const; virtual void onUsedAmmo(Player* player, Item* item, Tile* destTile) const; - virtual bool getSkillType(const Player* player, const Item* item, skills_t& skill, uint32_t& skillpoint) const; + virtual bool getSkillType(const Player* player, const Item* item, skills_t& skill, uint64_t& skillPoint) const; - int32_t hitChance, maxHitChance, breakChance, ammoAttackValue; + int32_t hitChance, maxHitChance, breakChance, attack; }; class WeaponWand : public Weapon { public: - WeaponWand(LuaScriptInterface* _interface); + WeaponWand(LuaInterface* _interface); virtual ~WeaponWand() {} virtual bool configureEvent(xmlNodePtr p); @@ -174,7 +160,7 @@ class WeaponWand : public Weapon virtual int32_t getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage = false) const; protected: - virtual bool getSkillType(const Player* player, const Item* item, skills_t& skill, uint32_t& skillpoint) const {return false;} + virtual bool getSkillType(const Player*, const Item*, skills_t&, uint64_t&) const {return false;} int32_t minChange, maxChange; };

+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