Compare commits

..
Author SHA1 Message Date
MagicBotandClaude Opus 5.5 26aa8de86c Refuse a talent unless it outranks the held one with the same sub type
Test on dev (character Moo): with Enhanced Strength held, the client shaded
an Increased Strength runestone red and showed its refusal popup, but the
server applied it as a downgrade. The client's 0x4768D0 (ArcRuneTemplate
vtable slot 0x1E8) blocks a talent when a held talent with the same
rune_sub_type has an equal or higher rank, rank 0 included. AddRune's
rank-0 replace branch is never reached past that check.

Value changes vs the previous commit:
- Rank-0 stat runes are refused while any rank of their chain is held
  (same as the old DB type 4 block)
- Rank-0 talents in the same group (e.g. Bloodline sub-race runestones)
  are refused instead of replacing each other

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:29:02 -04:00
MagicBotandClaude Opus 5.5 0536aea27a Build rune requirements from the runes and check them like the client
RuneBase(RuneTemplate) fills the race, class and discipline maps from
item_race_req / item_class_req / item_disc_req (names to rune ids through
CObjectIndex; class runes with rune_is_standard_character_creation go to
baseClass, the rest to promotionClass) with a restrict flag for each, and
the level from item_level_req. static_rune_runereq is no longer used for
cached rune bases; GET_RUNE_REQS stays until 2.11.

ApplyRuneMsg follows ArcCharacter_MeetsItemReqs (0x475F40) and
ArcItemRestrict_Check (0x4BAC60): restrict true = blacklist, false =
whitelist. The class check passes when the base class or the promotion is
listed. The discipline check is against the discs already held, using the
new rune's list only.

Parity (offline, scripts/p25a.py with feasible race/promotion pairs under
the client rule; scripts/p25b.py over all 315 rune bases). Value changes,
all decided JSON (D6, 2026-10-21):
- 3001 Animator: + Irekei (4 pairs); Summoner blocks it
- 3008 Commander: Saboteur blocks it
- 3010 Duelist: level 20, 5 races, 7 promotions (had no DB rows; -111 pairs)
- 3018 Enchanter, 3030 Conjurer: + Vampire (4 pairs each)
- 3020 Summoner: - Thief/Warrior/Barbarian (28 pairs), + Centaur Druid/Priest
  (4); Animator blocks it
- 3031 Darksworn: + Irekei Priest (2); 3032 Valkyr: + Human/Aelfborn Warrior (2)
- 3037 Artillerist: + Sentinel (11); 3044 Strigoi: Belgosch blocks it
- 252127 Master of Throwing: - Scout (16 pairs)
- 70 more runes with no DB rows gain their JSON lists (class runes 2500-2526
  except the Pet rune, creation talents and Bloodline talents); only runestones are checked
- Stat chains: the DB "higher rank held" blocks leave the discipline map.
  Ranks 1-8 are still refused by the sub type rule; a rank-0 rune now
  replaces a higher rank as the client does (decided: follow the client)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:02:10 -04:00
MagicBotandClaude Opus 5.5 c3f1861cf7 Replace talents by sub type and rank, cap talents at 8
Follows the client's ArcCharacter_AddRune (0x4695D0, specs/RuneRequirements.md):
a talent replaces a held talent with the same non-zero rune_sub_type when its
rank is 0 or higher than the held rank; otherwise the apply fails. With no
talent to replace, a new talent needs fewer than 8 held talents.

RuneBase gets runeType, a sub type token (SBStringHash(rune_sub_type), or the
stored hash) and rank from the rune. It no longer copies the static_rune_runereq
type 5 overwrite list, and ApplyRuneMsg no longer reads it.

Value changes:
- Stat chains (*MaxIncrease) replace the same ranks as before; the DB typos go
  away (250026 listed 250019 twice and missed 250018)
- Talents in the same group now replace each other when applied from a
  runestone (e.g. Bloodline sub-race talents); before, both were added
- Limit: 8 talents instead of more than 12 runes of any kind
- Rank-0 downgrade (decided: follow the client) takes effect in the next
  commit, when the DB stat-chain blocks leave the discipline map

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:00:46 -04:00
MagicBotandClaude Opus 5.5 8f87778c43 Show "You cannot apply that rune" when a rune is refused
applyRune returns false on every failed requirement without telling the
player, so using a rune the character doesn't qualify for did nothing. The
caller now sends popup 161 on refusal, matching popup 160 on success. The id
comes from the server's ErrorPopupMsg list; the popup strings aren't in the
client exe, so it isn't confirmed in IDA.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:53:52 -04:00
MagicBotandClaude Opus 5.5 0ff7e7736c Load RuneBase and its attributes from the runes
LoadAllRuneBases builds one RuneBase per static_rune_runebase row from the rune
with the same id (all 315 have one) and fills runeBaseAttributeMap from the
rune; the row now only supplies type, subtype, the runereq maps and effects. A
row without a rune would stay the DB object, without attributes, with a
warning. static_rune_runebaseattribute is no longer read at boot (the
LoadAllAttributes calls are gone from both servers). The SQL loaders stay until
task 2.11.

Value changes (decisions: JSON, D2):
- name: Confesser -> Confessor, Theif -> Thief, LagMaster -> Lagmaster,
  WP Dev -> Wolfpack Developer, Exception X -> Exceptional X (Strength,
  Dexterity, Constitution, Intelligence, Spirit). No code matches these names.
- description: '' / CSR text -> rune_dsc (unread)
- attributes added where the DB had none:
  - admin 2905 Lagmaster spi +200, spi max +200; 2907 Watcher con +200;
    2909 Feature Character every max +200; 2911 QA Test Rune con +200
    (applies to characters granted them; none hold them on dev)
  - creation cost 5/12/10/10/10 on 250116, 250117, 250123-250125 (duplicate
    talents not offered at creation)
  - base classes 2500-2503, Pet 2522, guards 252621-252623: start/max
    attributes (never player CharacterRunes; NPC rune attributes are unread)

All 365 DB attribute rows match the JSON; final parity before the switch
(scripts/p24.py): 0 other differences. The static_rune_runebase comparator is
removed; it would only compare the JSON with itself now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:30:43 -04:00
MagicBotandClaude Opus 5.5 83f57b4def Build RuneBase and its attributes from the rune and compare them
Adds RuneBase(RuneTemplate, RuneBase): name from item_base_name, description
from rune_dsc. type and subtype stay the static_rune_runebase values: they are
server enums that don't map cleanly from rune_type/rune_sub_type (CLASS runes
carry 0/2/3/5/256/289; the creation exclusive-group subtypes 1-10 leave out
Blood Gift, the MaxIncrease chains and the hashed sub types). The requirement
maps and effects are copied from the DB row until 2.5b/2.10.
RuneBaseAttribute.getAttributesForRune builds attribute 0 from
rune_creation_cost, 1-5 from rune_attr_adj, 6-10 from rune_max_attr_adj and
11-15 from item_attr_req. A static_rune_runebase comparator checks name and
attributes against the JSON.

Offline parity (scripts/p24.py, 315 runebase rows + 365 attribute rows vs the
RUNE JSON): every row has a rune; all 365 DB attribute rows match. Differences:
- name: 9 DB typos (Confesser, Theif, LagMaster, WP Dev, Exception Strength/
  Dexterity/Constitution/Intelligence/Spirit)
- attributes: 17 runes with no DB rows gain JSON values: base classes
  2500-2503, Pet 2522, admin 2905/2907/2909/2911, guards 252621-252623, and
  creation cost on 250116, 250117, 250123-250125
- description: '' (or CSR text on 2901-2911) -> rune_dsc; nothing reads it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:25:20 -04:00
MagicBotandClaude Opus 5.5 98f09d0713 Load Race from the race runes instead of static_rune_race
loadAllRaces builds one Race per static_rune_race row from the RACE rune with
the same id (all 26, including 2018-2021, decision: JSON); the row now only
supplies minHeight and strHeightMod, and a row without a race rune would load
from the DB with a warning. Colors stay in the DB. racebaseclass,
racehairstyle and racebeardstyle are no longer read at boot. The SQL loader
stays until task 2.11.

Value changes (decisions: JSON, D2):
- name drops the ", Male"/", Female" suffix ("Aelfborn, Male" -> "Aelfborn").
  WhoResponseMsg's split(",") and runAfterLoad's suffix strip still work;
  runAfterLoad now resolves 2019 to HalfGiant (it threw on the DB name).
- description '' -> rune_dsc blurb (unread)
- Minotaur hair 6958 -> 6598; Vampire M beard 5020170 -> 5020300
- beard style 0 dropped on Shade M/F, Minotaur, Vampire F (beard styles are
  never validated); hair 0 kept on Aracoix M/F and Shade M/F by the
  empty-hair rule
- 2018 Archon: startingPoints 0 -> 30, token 0 -> -317458791, health 0 -> 20,
  stamina 0 -> 90
- 2019 Half-Giant-Old Man: strMax 150 -> 140, dex 40/65 -> 30/75, con 60/140
  -> 50/130, spi 30/60 -> 35/80, startingPoints 20 -> 15, health/mana/stamina
  0 -> 10/5/70, baseClasses {} -> {Fighter 2500}
- 2020/2021 CSR: all stats 60/120 -> 40/100, startingPoints 0 -> 30, token
  0 -> 52803, health/mana/stamina 0 -> 40/40/10000

All other fields unchanged; final parity before the switch (scripts/p23.py):
22 creation races, 0 differences in attributes, bonuses, startingPoints,
token and base classes. The static_rune_race comparator is removed; it would
only compare the JSON with itself now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 11:02:06 -04:00
MagicBotandClaude Opus 5.5 a30a065841 Build Race from its race rune and compare every field
Adds Race(RuneTemplate, Race): start/max attributes from rune_attr_adj/
rune_max_attr_adj (min is always 0), health/mana/stamina bonus from
rune_health/mana/stamina, startingPoints = 30 - rune_creation_cost, token =
SBStringHash(rune_sub_type), name from obj_name, description from rune_dsc,
hair/beard styles from rune_hair/rune_beard (style 0 when rune_hair is empty),
and baseClasses = the creation class runes whose item_race_req names the race
sub type. minHeight/strHeightMod come from the static_rune_race row; colors,
skills, powers and effects still load from the DB by rune id. A
static_rune_race comparator checks every field against a JSON-built Race.

Offline parity (scripts/p23.py, 26 rows + racebaseclass/hairstyle/beardstyle
vs the RACE runes): the 22 creation races match in every attribute, bonus,
startingPoints, token and base class. Differences:
- name: "Aelfborn, Male" -> "Aelfborn" on every row
- hair 0 dropped by the JSON on Aracoix M/F and Shade M/F (kept by the
  empty-hair rule); beard 0 dropped on Shade M/F, Minotaur, Vampire F
- Minotaur hair 6958 -> 6598, Vampire M beard 5020170 -> 5020300
- 2018 Archon: startingPoints 0 -> 30, token 0 -> SBStringHash("Celestial"),
  health 0 -> 20, stamina 0 -> 90
- 2019 Half-Giant-Old Man: str/dex/con/spi start and max, startingPoints
  20 -> 15, health/mana/stamina 0 -> 10/5/70, baseClasses {} -> {Fighter}
- 2020/2021 CSR: stats 60/120 -> 40/100, startingPoints 0 -> 30, token 0 ->
  SBStringHash("CSR"), health/mana/stamina 0 -> 40/40/10000

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 11:01:43 -04:00
MagicBotandClaude Opus 5.5 957293165b Load PromotionClass from the promotion runes instead of static_rune_promotion
LoadAllPromotions builds one PromotionClass per promotion CLASS rune (22,
2504-2526 without 2522) and caches it, then runs the DB loader, which skips
cached ids, so only the CSR/admin rows 2901-2911 still come from
static_rune_promotion (decision: DB fallback). The world server calls it in
place of GET_ALL_PROMOTIONS; the login server now preloads promotions too, so
character load there gets the same cached objects. The SQL loader stays until
task 2.11.

Value changes: Templar 2516 manaMod 14 -> 15 (decision: JSON, D2).
description NULL -> rune_dsc blurb (unread). All other fields unchanged;
final parity before the switch (scripts/p22.py): 22 promotion rows, 0
differences in name, token, health, stamina and the 163 allowed rune tokens.
The static_rune_promotion comparator is removed; it would only compare the
JSON with itself now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:33:45 -04:00
MagicBotandClaude Opus 5.5 e91c803a32 Build PromotionClass from its class rune and compare every field
Adds PromotionClass(RuneTemplate): health/mana/stamina from rune_health/mana/
stamina, token = SBStringHash(rune_sub_type), description from rune_dsc, and
allowedRunes = the sub type tokens of the race and base class runes named in
item_race_req/item_class_req (all whitelists). Skills, powers and effects still
load from the DB by rune id (tasks 2.8, 2.9, 2.10). isPromotionRune selects the
non-creation CLASS runes that require a base class (skips 2500-2503 and Pet).
A static_rune_promotion comparator checks every field against a JSON-built
PromotionClass; rows 2901+ (TALENT runes in the JSON) report as missing.

Offline parity (scripts/p22.py, 33 rows + 163 promotionrunereq rows vs 22
promotion runes): 0 differences in name, token, health, stamina and allowed
rune tokens. Templar 2516 manaMod is 14 in the DB and 15 in the JSON.
2901-2911 have no promotion rune. description is NULL in the DB and the
rune_dsc blurb in the JSON; nothing reads PromotionClass.getDescription().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:33:08 -04:00
MagicBotandClaude Opus 5.5 992177eb0c Return the cached PromotionClass from GET_PROMOTION_CLASS
Character load and the promotion trainer re-read static_rune_promotion on every
call and replaced the cached object. They now get the object loaded at boot,
as GET_BASE_CLASS already does; the DB query is only a miss fallback.
No value changes: the cached and re-read objects hold the same row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 10:32:24 -04:00
MagicBotandClaude Opus 5.5 c8c322a3f5 Load BaseClass from the class runes instead of static_rune_baseclass
LoadAllBaseClasses builds one BaseClass per CLASS rune with
rune_is_standard_character_creation (2500-2503; promotions and Pet are
skipped) and caches it. Race.baseClasses picks up the cached objects through
GET_BASECLASS_FOR_RACE. The SQL loader stays until task 2.11.

Value changes: description goes from '' to the rune_dsc class blurb (unread).
All other fields unchanged; final parity before the switch (scripts/p21.py):
4 rows, 0 differences in name, attribute mods, token, health/mana/stamina mods.
The static_rune_baseclass comparator is removed; it would only compare the
JSON with itself now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 09:55:09 -04:00
MagicBotandClaude Opus 5.5 5acd3492ed Build BaseClass from its class rune and compare every field
Adds BaseClass(RuneTemplate): attributes from rune_attr_adj, health/mana/stamina
from rune_health/mana/stamina, token = SBStringHash(rune_sub_type), description
from rune_dsc. Skills, powers and effects still load from the DB by rune id
(tasks 2.8, 2.9, 2.10). The static_rune_baseclass comparator now checks every
field against a JSON-built BaseClass.

Offline parity (scripts/p21.py, 4 rows vs the CLASS runes with
rune_is_standard_character_creation = 2500-2503): 0 differences except
description, which is '' in the DB and the class blurb in rune_dsc. Nothing
reads BaseClass.getDescription().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 09:54:46 -04:00
MagicBotandClaude Opus 5.5 0283f85e87 Remove the legacy ItemTemplate and static_item_templates loader
Every reader moved to CObjectTemplate in 1.7, so the DB-loaded
engine.objects.ItemTemplate, LOAD_ITEM_TEMPLATES and linkCacheTemplates
are dead. No value changes: nothing read the legacy maps any more.
The boot log loses the legacy per-type count and the link warnings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:53:14 -04:00
MagicBotandClaude Opus 5.5 4b8430b0ba Use GameObjectType to check the ./fill target
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:44:11 -04:00
MagicBotandClaude Opus 5.5 f07f9945d7 Compare the selected warehouse by UUID in ./fill
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:42:29 -04:00
MagicBotandClaude Opus 5.5 077dcc071d Rename ./fillwarehouse to ./fill and require a selected warehouse
The command now acts on the selected building and refuses anything
whose blueprint is not a warehouse, or a warehouse that is not its
city's active one, instead of using the city the player stands in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:42:09 -04:00
MagicBotandClaude Opus 5.5 c616afa33b Add ./fillwarehouse dev command
Sets every resource in the warehouse of the city the player stands in,
either to an optional amount or, by default, to each resource's
deposit limit. Makes forge and work order testing practical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:40:01 -04:00
MagicBotandClaude Opus 5.5 1ac522a2e0 Fix /makeitem failing to persist items
MakeItemCmd left the container at NONE, which item_CREATE's cont
parameter rejects, so PERSIST returned null and addItemToInventory
threw a NullPointerException. Set the container to INVENTORY as
CreateItemCmd does, and report an error if PERSIST still fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:27:47 -04:00
MagicBotandClaude Opus 5.5 566870de79 Move the remaining legacy item template lookups to CObjectTemplate
Dev commands, BuildingManager, ChatManager, ForgeManager, NPCManager,
NPC, Warehouse, WorkOrder and ManageNPCMsg now call
CObjectTemplate.lookup instead of ItemTemplate.templates.get. Forge
production cost reads getItemResourceCosts(). Only the DB loader and
linkCacheTemplates still use the legacy class (removed in Task 1.8).

Removes the static_item_templates ParityCheck comparator. Final parity
(offline p17.py, 7586 rows): every id has a cache template, 0 item_type
differences, 0 item_resource_costs differences (84 ITEMs have costs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 07:56:08 -04:00
MagicBotandClaude Opus 5.5 6b4e9815e0 Switch Item.template and MobLoot to CObjectTemplate
Item.template is now the cache template from CObjectTemplate.lookup
instead of the legacy DB ItemTemplate, so item.template.cacheTemplate.X
becomes item.template.X. MobLoot, LootManager, ItemManager.isTwoHanded/
isShield and CombatManager.getSwingAnimation take a CObjectTemplate.

No value changes: every getter already read the cache template through
the bridge, and the lookup resolves the same 7586 ids as
static_item_templates with the same item_type (offline parity p17.py,
0 differences).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 07:55:28 -04:00
MagicBotandClaude Opus 5.5 f39e35ac23 Apply the dual-wield damage cut only when both hands hold weapons
The 30% cut never applied: each hand was passed itself as the other
hand, and the check compared the template to ItemType.WEAPON. The
client (0x466EB0 / 0x46D200) multiplies min and max damage by 0.7 when
both held slots contain an item_type WEAPON. Value change: players
wielding two weapons now deal 70% damage per hand; single weapon,
weapon + shield and unarmed are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 07:54:02 -04:00
MagicBotandClaude Opus 5.5 70ea9b3bc9 Move the vendor mod table off the legacy item template
LOAD_TEMPLATE_MODTABLES now returns a templateID -> modTable map that
ForgeManager owns (vendorModTables). ForgeManager.forgeItem, WorkOrder
validation and the ManageNPCMsg forge item list read it through
ForgeManager.getVendorModTable(), and ItemTemplate.modTable is removed.

Values are unchanged: static_vendor_items has 5089 rows, one per
templateID, all present in static_item_templates, modTable 0-30.
Templates not in the table still read 0. The old loader stopped at the
first templateID missing from ItemTemplate.templates; there are none.
No comparator added or removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 07:37:54 -04:00
MagicBotandClaude Opus 5.5 2b4c1a73d1 Read item user power actions from the cache template
The magical name color check and loadTemplateEnchantments read
getItemUserPowerAction(). The list keeps duplicates, so the baked-in
enchantments collapse a repeated power to its last entry, as the legacy
HashMap did; applying it twice would add its name to effectNames twice and
double its share of calcMagicValue.

Offline parity (p15e.py, 7586 rows): 0 differences in the lists (power and
arguments, in order). Duplicates exist only on 81 RUNE templates (mob,
guard and siege runes, plus discipline rune 3006 Blade Weaver with two
identical AMBIDEXTERITY entries); none on ITEM or DEED.

Value changes: none. Enchantments are now applied in file order instead
of HashMap order; effects are keyed by effect id and calcMagicValue is a
sum, so the result is the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:35:38 -04:00
MagicBotandClaude Opus 5.5 a37de9fc31 Read the item power grant from the cache template
The BUCKET/POTION/SCROLL use path takes the first item_power_grant entry
in file order from getItemPowerGrant() instead of the first key of the
legacy HashMap.

Offline parity (p15e.py, 7586 rows): 0 differences in the grant lists.
The legacy HashMap order picks a different power on 664 templates, all
RUNE folder runes with item_type RUNE, which take the RUNE case and never
reach this path. Every ITEM-folder grant (scrolls, potions, bucket, rod,
gift rings) has exactly one entry and no DEED has one, so the power and
value used are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:35:19 -04:00
MagicBotandClaude Opus 5.5 81d4ab7b99 Read item skill used and mastery from the cache template
CombatManager shield checks, weapon skill/mastery lookups in
AbstractCharacter, armor defense and PlayerCharacter.validEquip read
getItemSkillUsed/getItemSkillMasteryUsed. Offline parity (p15d.py, 7586
rows): 0 mastery differences; skill used differs only where the legacy
loader blanked it.

The cache keeps "Cloth" (226 ARMOR templates), which the legacy loader
turned into "". Cloth is a placeholder in Skills.cfg that no character
has, so getArmorDefense now checks for it explicitly and still returns the
flat defense rating. No weapon uses Cloth and no power equip prereq names
it, so the other sites behave the same.

Value changes:
- 195340 Fortified Dragon Plate Helm of the Lion: skill used is the
  unresolved hash "2624663" instead of "". Defense is still the flat
  rating (no such skill), but players wearing it now hit the
  "armor equipped without the necessary skill" error log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:29:01 -04:00
MagicBotandClaude Opus 5.5 7abc63ffc3 Read item skill requirements from the cache template
validForSkills, isShield, the guard attack rating and the NPC vendor skill
filter read getItemSkillReq. Offline parity (p15d.py, 7586 rows): 0
differences. No template lists "Cloth" in item_skill_req and none has more
than one entry, so file order vs HashMap order is moot.

Value changes: none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:28:32 -04:00
MagicBotandClaude Opus 5.5 5b3c511b55 Read item eq slots from the cache template
isTwoHanded, validForSlot, getSwingAnimation and the mob equipment set
read getItemEqSlotsOr/And, which drop the exporter's NONE. Offline parity
(p15c.py, 7586 rows): 0 differences once NONE is ignored.

Value changes:
- validForSlot: items whose only slot is NONE no longer pass for slot NONE.
- getSwingAnimation: the 7 slotless weapons (Rock 19297, Arrow 25650,
  siege ammo 29000/29010/29020/29030/29350) return the default 75 instead
  of indexing their empty left-hand anim list.
- MobBase equipment sets skip slotless items instead of keying them under
  NONE. No EQUIP booty entry references one (the 104 sets that do are ITEM
  booty sets).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:23:41 -04:00
MagicBotandClaude Opus 5.5 73947f3dc8 Drop NONE from rune and deed eq slots
Matches ItemParser, so every CObjectTemplate returns an empty slot set for
the exporter's ["NONE"] mask. All 2695 RUNE and 880 DEED templates have
["NONE"] only; nothing reads these sets yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:22:57 -04:00
MagicBotandClaude Opus 5.5 c7d8483d55 Read item race/class/disc requirements from the cache template
ValidRace/ValidClass/ValidDiscipline use the single req set plus the
restrict flag (true = blacklist, false = whitelist) instead of the legacy
req/res split. Offline parity (p15c.py, 7586 rows): 0 differences in
restrict or set for race, class and disc.

Value changes: none for equip checks. The legacy disc blacklist branch was
inverted (passed only when a listed disc was held); the new check passes
when none is held. No ITEM has a disc blacklist (only 19 RUNEs), and
ValidDiscipline has no callers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:22:36 -04:00
MagicBotandClaude Opus 5.5 0688cf231f Fix isTwoHanded always returning false
EnumSet.contains(EnumSet) is never true; use containsAll so the 350
weapons with item_eq_slots_and {LHELD, RHELD} block the other hand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:22:09 -04:00
MagicBotandClaude Opus 5.5 6b2f000434 Read the weapon block from the cache template item_weapon
Range, speed, damage list and swing anims now come from
engine.parsers.objects.ItemTemplate.getWeapon(templateID). Non-weapons and
missing templates get an empty block, matching the legacy zero defaults.

Offline parity (scripts/p15b.py, 811 WEAPON rows vs COBJECTS/ITEM): 0
differences in speed, range, projectile, idle anim, damage list or either
anim list. No duplicate damage types exist, so sum vs first is moot; first
entry is used as before. 26 weapons (ammo, lutes, no-stats crossbows) have an
empty damage list, same as the DB. No value changes. No comparator removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:17:24 -04:00
MagicBotandClaude Opus 5.5 441a935bd9 Read armor resists from the cache template EnumMap
Resists.getArmorResists and NPCManager.applyEquipmentResists now read
cacheTemplate.getCombatAttackResist() with DamageType keys instead of the
legacy String-keyed map, and default a missing key to 0 instead of an
unboxing NPE.

Offline parity (scripts/p15a.py, all 7586 static_item_templates rows vs
the template CObjectIndex.lookup picks): 0 differences in the full
combat_attack_resist map. Every ITEM/DEED/RUNE template carries all 16
keys, so no value changes. No comparator added or removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:11:21 -04:00
MagicBotandClaude Opus 5.5 b63826a7b7 Import DeedTemplate in PlaceAssetMsgHandler
c7747467 used DeedTemplate._deeds in the deed placement check without
importing it, which broke the build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:52:22 -04:00
MagicBotandClaude Opus 5.5 2774d8a510 Read offering info and bane rank through the bridge
Shrine boons read getItemOfferingInfo() and the banestone and bane
production paths read getItemBaneRank() on template.cacheTemplate.

The legacy parser only kept item_bane_rank when ACTIONRESPONSE is
4250517122; the cache parsers always read it. Parity shows this makes
no difference: every template outside that gate has bane rank 0.

Parity (offline, scripts/p14.py, 7586 templates): 0 differences in
item_offering_info and item_bane_rank. No value changes expected.

JSON-Refactor-Plan Task 1.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:46:14 -04:00
MagicBotandClaude Opus 5.5 9a10574e59 Read defense, bulk and primary attr from the cache ItemTemplate
item_defense_rating, item_bulk_factor and item_primary_attr are
Item-only fields, not on CObjectTemplate, so their consumers read
engine.parsers.objects.ItemTemplate._items by templateID, like the
RuneTemplate and DeedTemplate consumers. The primary attr checks
compare with == because the cache template leaves the attr null
(legacy: None) on non-weapons.

Parity (offline, scripts/p14.py, 7586 templates): 0 differences in
item_defense_rating, item_bulk_factor and item_primary_attr. Every DB
ARMOR and WEAPON template is in the ITEM folder, so both sides gate
these fields on the same templates. No value changes expected.

JSON-Refactor-Plan Task 1.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:45:59 -04:00
MagicBotandClaude Opus 5.5 d197f62c1f Read template health, charges and flags through the bridge
Consumers of the legacy ItemTemplate combat_health_full,
item_initial_charges and item_flags now read getCombatHealthFull(),
getItemInitialCharges() and getItemFlags() on template.cacheTemplate.
No consumer read the template's combat_health_current; the
combat_health_current reads are the Item's own field.

Parity (offline, scripts/p14.py, 7586 templates): 0 differences in
combat_health_current, combat_health_full, item_initial_charges and
item_flags ("None" dropped on both sides). No value changes expected.

JSON-Refactor-Plan Task 1.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:45:17 -04:00
MagicBotandClaude Opus 5.5 dc4a9ee08a Read item weight, value and base name through the bridge
Consumers of the legacy ItemTemplate item_wt, item_value and
item_base_name now read getItemWt(), getItemValue() and
getItemBaseName() on template.cacheTemplate.

Parity (offline, scripts/p14.py, 7586 templates): 0 differences in
item_wt, item_value and item_base_name. No value changes expected.

JSON-Refactor-Plan Task 1.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:44:35 -04:00
MagicBotandClaude Opus 5.5 b8ac8cce3e Read item_type through the cache template bridge
Every consumer of the legacy ItemTemplate item_type now reads
template.cacheTemplate.getItemType(). Left on the legacy field: the
type count in dbItemHandler.LOAD_ITEM_TEMPLATES, which runs before the
bridge is linked. The charter checks already read DeedTemplate.

Parity (offline, static_item_templates vs the template
CObjectIndex.lookup picks, scripts/p14.py, 7586 templates: ITEM 4011,
DEED 880, RUNE 2695): 0 differences in item_type. No value changes
expected.

JSON-Refactor-Plan Task 1.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:43:57 -04:00
MagicBotandClaude Opus 5.5 6825f94f1e Check charters through DeedTemplate
GuildCreationFinalizeHandler and UseCharterMsg now match charters by
DeedTemplate._deeds instead of the shared item_type getter, like the
other deed paths.

Value change: template 545 "Charter" (ITEM folder, CHARTER) no longer
counts as a charter. It is a base template that players can't get: its
only DB reference is static_vendor_items with vendorType 0, it has no
loot rows, and it is not in GuildCharterType.templateLookup, so guild
creation already rejected it.

JSON-Refactor-Plan Task 1.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:47:09 -04:00
MagicBotandClaude Opus 5.5 c774746736 Read deed consumers from DeedTemplate
Deed placement (ObjectActionMsgHandler, PlaceAssetMsgHandler) reads
deed_structure_id from DeedTemplate._deeds. The deed, employment
contract and charter type checks (PlaceAssetMsgHandler,
ActivateNPCMsgHandler, GuildCreationFinalizeHandler, UseCharterMsg,
Item.serialize) read item_type through the cache template bridge.
Charters go through the shared getter rather than _deeds because
template 545 "Charter" is a CHARTER in the ITEM folder.

Parity (offline, static_item_templates vs COBJECTS, scripts/p13.py,
881 templates: all 880 DEED plus ITEM 545): 0 differences in
item_type and deed_structure_id. Every DB DEED, EMPLOYMENTCONTRACT,
PETTOTEM and FURNITUREDEED id is in the DEED folder. No value changes
expected. No pet totem or furniture deed consumer reads template
fields, so neither changes.

JSON-Refactor-Plan Task 1.3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:42:54 -04:00
MagicBotandClaude Opus 5.5 3e087bd1ac Read mob rune damage and natural attacks from RuneTemplate
AbstractCharacter mob damage/ATR and CombatManager.getNaturalAttack now
read RuneTemplate._runes instead of the legacy ItemTemplate map.

Parity (offline, static_item_templates vs COBJECTS/RUNE, 2695 runes):
0 differences in rune_min_damage, rune_max_damage, rune_attack and
rune_naturalattacks. All 2366 runes with natural attacks use the
10-value layout. No value changes expected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:35:02 -04:00
MagicBotandClaude Opus 5.5 765cb9edb6 Bridge legacy ItemTemplate to its cache template
Each engine.objects.ItemTemplate gets a cacheTemplate field, filled by
linkCacheTemplates() right after CObjectIndex.build() on both servers,
so consumers can move to CObjectTemplate one field at a time.

ParityCheck gains a static_item_templates comparator: a null bridge is
reported as missing in json, and obj_name and item_type are compared
through the interface to catch a wrong folder pick.

Import convention until the legacy class is removed (Task 1.8): the
legacy engine.objects.ItemTemplate keeps the plain import; the cache
class is always written engine.parsers.objects.ItemTemplate. Migrated
consumers should depend on CObjectTemplate instead.

JSON-Refactor-Plan Task 1.1. No consumers change; no values change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:21:47 -04:00
MagicBotandClaude Opus 5.5 4765e7993f Add shared obj/combat/item getters to CObjectTemplate
The ITEM, DEED and RUNE cache templates implement the getters for the
65 fields all three define. The Item-only fields (weapon, primary and
secondary attr, bulk, defense rating, sheath, stub flag/id) stay off the
interface. CObjectTemplate.lookup delegates to CObjectIndex.lookup.

JSON-Refactor-Plan Task 1.1 (D1). No consumers change; no values change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:20:33 -04:00
68 changed files with 2090 additions and 1171 deletions

No files matched your search

+9 -35
View File
@@ -16,8 +16,6 @@ import engine.mbEnums.ItemType;
import engine.objects.AbstractCharacter;
import engine.objects.CharacterItemManager;
import engine.objects.Item;
import engine.objects.ItemTemplate;
import org.json.JSONObject;
import org.pmw.tinylog.Logger;
import java.sql.Connection;
@@ -177,47 +175,23 @@ public class dbItemHandler extends dbHandlerBase {
return itemList;
}
public void LOAD_ITEM_TEMPLATES() {
public HashMap<Integer, Integer> LOAD_TEMPLATE_MODTABLES() {
HashMap<ItemType, Integer> templateTCountMap = new HashMap<>();
try (Connection connection = DbManager.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM `static_item_templates`;");
ResultSet rs = preparedStatement.executeQuery()) {
while (rs.next()) {
int templateID = rs.getInt("id");
JSONObject jsonObject = new JSONObject(rs.getString("template"));
ItemTemplate itemTemplate = new ItemTemplate(jsonObject);
itemTemplate.template_id = templateID;
ItemTemplate.templates.put(templateID, itemTemplate);
templateTCountMap.merge(itemTemplate.item_type, 1, Integer::sum);
}
Logger.info(templateTCountMap.toString());
} catch (Exception e) {
Logger.error(e);
}
}
public void LOAD_TEMPLATE_MODTABLES() {
HashMap<Integer, Integer> modTables = new HashMap<>();
try (Connection connection = DbManager.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM `static_vendor_items`;");
ResultSet rs = preparedStatement.executeQuery()) {
while (rs.next()) {
int templateID = rs.getInt("templateID");
int modTable = rs.getInt("modTable");
ItemTemplate template = ItemTemplate.templates.get(templateID);
template.modTable = modTable;
}
while (rs.next())
modTables.put(rs.getInt("templateID"), rs.getInt("modTable"));
} catch (Exception e) {
Logger.error(e);
}
Logger.info("read: " + modTables.size());
return modTables;
}
public Item GET_ITEM(final int itemUUID) {
@@ -325,7 +299,7 @@ public class dbItemHandler extends dbHandlerBase {
for (Item item : inventory) {
if (item.template.item_type.equals(ItemType.GOLD))
if (item.template.getItemType().equals(ItemType.GOLD))
continue;
try (Connection connection = DbManager.getConnection();
@@ -474,7 +448,7 @@ public class dbItemHandler extends dbHandlerBase {
*/
public boolean UPDATE_GOLD(final Item item, int newValue, int oldValue) {
if (!item.template.item_type.equals(ItemType.GOLD))
if (!item.template.getItemType().equals(ItemType.GOLD))
return false;
try (Connection connection = DbManager.getConnection();
@@ -554,7 +528,7 @@ public class dbItemHandler extends dbHandlerBase {
// Write 0 if we will not modify the value from template
if (value == item.template.item_value)
if (value == item.template.getItemValue())
value = 0;
try (Connection connection = DbManager.getConnection();
@@ -50,7 +50,15 @@ public class dbPromotionClassHandler extends dbHandlerBase {
public PromotionClass GET_PROMOTION_CLASS(final int objectUUID) {
PromotionClass promotionClass = null;
PromotionClass promotionClass;
if (objectUUID == 0)
return null;
promotionClass = (PromotionClass) DbManager.getFromCache(mbEnums.GameObjectType.PromotionClass, objectUUID);
if (promotionClass != null)
return promotionClass;
try (Connection connection = DbManager.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM `static_rune_promotion` WHERE `ID` = ?")) {
+3 -3
View File
@@ -15,8 +15,8 @@ import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.objects.AbstractGameObject;
import engine.objects.Item;
import engine.objects.ItemTemplate;
import engine.objects.PlayerCharacter;
import engine.parsers.objects.CObjectTemplate;
import org.pmw.tinylog.Logger;
/**
@@ -38,7 +38,7 @@ public class CreateItemCmd extends AbstractDevCmd {
}
int templateID = Integer.parseInt(words[0]);
ItemTemplate template = ItemTemplate.templates.get(templateID);
CObjectTemplate template = CObjectTemplate.lookup(templateID);
if (template == null) {
ChatManager.chatSystemInfo(playerCharacter, "No such template found.");
@@ -51,7 +51,7 @@ public class CreateItemCmd extends AbstractDevCmd {
for (int i = 0; i < size; i++) {
if (!playerCharacter.charItemManager.hasRoomInventory(template.item_wt)) {
if (!playerCharacter.charItemManager.hasRoomInventory(template.getItemWt())) {
ChatManager.chatSystemInfo(playerCharacter, "You are encumbered!.");
break;
}
+106
View File
@@ -0,0 +1,106 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.devcmd.cmds;
import engine.devcmd.AbstractDevCmd;
import engine.gameManager.ChatManager;
import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.objects.AbstractGameObject;
import engine.objects.Building;
import engine.objects.City;
import engine.objects.PlayerCharacter;
import engine.objects.Warehouse;
import java.util.HashMap;
public class FillCmd extends AbstractDevCmd {
public FillCmd() {
super("fill");
}
@Override
protected void _doCmd(PlayerCharacter pc, String[] words,
AbstractGameObject target) {
Building building;
City city;
Warehouse warehouse;
int amount = Integer.MAX_VALUE;
HashMap<mbEnums.ResourceType, Integer> oldResources;
if (words.length > 1) {
this.sendUsage(pc);
return;
}
if (words.length == 1 && !words[0].isEmpty()) {
try {
amount = Integer.parseInt(words[0]);
} catch (NumberFormatException e) {
throwbackError(pc, "Amount must be a number, " + words[0] + " is invalid");
return;
}
if (amount < 0) {
throwbackError(pc, "Amount must be 0 or more");
return;
}
}
if (target == null || target.getObjectType() != mbEnums.GameObjectType.Building) {
throwbackError(pc, "Select a warehouse");
return;
}
building = (Building) target;
if (building.getBlueprint() == null || building.getBlueprint().getBuildingGroup() != mbEnums.BuildingGroup.WAREHOUSE) {
throwbackError(pc, "Selected building is not a warehouse");
return;
}
city = building.getCity();
if (city == null || city.warehouse == null || city.warehouse.building == null
|| city.warehouse.building.getObjectUUID() != building.getObjectUUID()) {
throwbackError(pc, "Selected warehouse is not the active warehouse of a city");
return;
}
warehouse = city.warehouse;
oldResources = new HashMap<>(warehouse.resources);
for (mbEnums.ResourceType resourceType : mbEnums.ResourceType.values())
warehouse.resources.put(resourceType, Math.min(amount, resourceType.deposit_limit));
if (!DbManager.WarehouseQueries.UPDATE_WAREHOUSE(warehouse)) {
warehouse.resources.putAll(oldResources);
throwbackError(pc, "Failed to update the warehouse in the database");
return;
}
ChatManager.chatSayInfo(pc, "Filled the " + city.getCityName() + " warehouse"
+ (amount == Integer.MAX_VALUE ? " to each deposit limit" : " with " + amount + " of each resource (capped at the deposit limit)"));
}
@Override
protected String _getHelpString() {
return "Sets every resource in the selected warehouse";
}
@Override
protected String _getUsageString() {
return "' ./fill [amount]' with a warehouse selected (no amount fills each resource to its deposit limit)";
}
}
+6 -5
View File
@@ -18,6 +18,7 @@ import engine.mbEnums.BuildingGroup;
import engine.mbEnums.GameObjectType;
import engine.mbEnums.TargetColor;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import engine.util.StringUtils;
import java.text.DecimalFormat;
@@ -507,17 +508,17 @@ public class InfoCmd extends AbstractDevCmd {
case Item: //intentional passthrough
case MobLoot:
Item item = (Item) target;
ItemTemplate template = ItemTemplate.templates.get(item.templateID);
output += StringUtils.addWS("Template: " + template.template_id, 20);
output += "Weight: " + template.item_wt;
CObjectTemplate template = CObjectTemplate.lookup(item.templateID);
output += StringUtils.addWS("Template: " + template.getTemplateId(), 20);
output += "Weight: " + template.getItemWt();
output += newline;
DecimalFormat df = new DecimalFormat("###,###,###,###,##0");
output += StringUtils.addWS("Qty: "
+ df.format(item.getNumOfItems()), 20);
output += "Charges: " + (byte) item.chargesRemaining
+ '/' + item.template.item_initial_charges;
+ '/' + item.template.getItemInitialCharges();
output += newline;
output += "Name: " + template.item_base_name;
output += "Name: " + template.getItemBaseName();
output += newline;
output += item.getContainerInfo();
+15 -7
View File
@@ -11,9 +11,11 @@ package engine.devcmd.cmds;
import engine.devcmd.AbstractDevCmd;
import engine.gameManager.DbManager;
import engine.mbEnums.ItemContainerType;
import engine.mbEnums.ItemType;
import engine.mbEnums.OwnerType;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import engine.powers.EffectsBase;
/**
@@ -59,17 +61,17 @@ public class MakeItemCmd extends AbstractDevCmd {
}
int templateID;
ItemTemplate template;
CObjectTemplate template;
try {
templateID = Integer.parseInt(words[0]);
template = ItemTemplate.templates.get(templateID);
template = CObjectTemplate.lookup(templateID);
if (template == null) {
throwbackError(pc, "No item template with id " + words[0]);
return;
}
if (template.item_type == ItemType.GOLD) {
if (template.getItemType() == ItemType.GOLD) {
this.throwbackInfo(pc, "use /addgold to add gold.");
return;
}
@@ -131,7 +133,7 @@ public class MakeItemCmd extends AbstractDevCmd {
return;
}
}
template = ItemTemplate.templates.get(templateID);
template = CObjectTemplate.lookup(templateID);
if (template == null) {
throwbackError(pc, "Unable to find template " + templateID);
@@ -139,8 +141,8 @@ public class MakeItemCmd extends AbstractDevCmd {
}
if ((numItems > 1)
&& (template.item_type.equals(ItemType.RESOURCE) == false)
&& (template.item_type.equals(ItemType.OFFERING)) == false)
&& (template.getItemType().equals(ItemType.RESOURCE) == false)
&& (template.getItemType().equals(ItemType.OFFERING)) == false)
numItems = 1;
CharacterItemManager cim = pc.charItemManager;
@@ -154,7 +156,7 @@ public class MakeItemCmd extends AbstractDevCmd {
for (int i = 0; i < quantity; i++) {
int weight = template.item_wt;
int weight = template.getItemWt();
if (!cim.hasRoomInventory(weight)) {
throwbackError(pc, "Not enough room in inventory for any more of this item. " + i + " produced.");
@@ -168,6 +170,7 @@ public class MakeItemCmd extends AbstractDevCmd {
Item item = new Item(templateID);
item.ownerID = pc.getObjectUUID();
item.ownerType = OwnerType.PlayerCharacter;
item.containerType = ItemContainerType.INVENTORY;
if (numItems > 1)
item.setNumOfItems(numItems);
@@ -179,6 +182,11 @@ public class MakeItemCmd extends AbstractDevCmd {
return;
}
if (item == null) {
throwbackError(pc, "DB error 2: Unable to create item " + templateID + ".");
return;
}
//create prefix
if (!prefix.isEmpty())
item.addPermanentEnchantmentForDev(prefix, 0);
+3 -2
View File
@@ -11,6 +11,7 @@ package engine.devcmd.cmds;
import engine.devcmd.AbstractDevCmd;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import java.util.ArrayList;
@@ -52,8 +53,8 @@ public class PrintBankCmd extends AbstractDevCmd {
throwbackInfo(pc, "Bank for " + type + ' ' + name + " (" + tar.getObjectUUID() + ')');
for (Item item : list) {
ItemTemplate template = ItemTemplate.templates.get(item.templateID);
throwbackInfo(pc, " " + template.item_base_name + ", count: " + item.getNumOfItems());
CObjectTemplate template = CObjectTemplate.lookup(item.templateID);
throwbackInfo(pc, " " + template.getItemBaseName() + ", count: " + item.getNumOfItems());
}
Item gold = cim.getGoldBank();
+3 -2
View File
@@ -13,6 +13,7 @@ import engine.devcmd.AbstractDevCmd;
import engine.gameManager.ChatManager;
import engine.mbEnums.GameObjectType;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import engine.powers.effectmodifiers.AbstractEffectModifier;
public class PrintBonusesCmd extends AbstractDevCmd {
@@ -33,8 +34,8 @@ public class PrintBonusesCmd extends AbstractDevCmd {
if (target instanceof Item) {
type = "Item";
tar = (Item) target;
ItemTemplate template = ItemTemplate.templates.get(((Item) tar).templateID);
name = template.item_base_name;
CObjectTemplate template = CObjectTemplate.lookup(((Item) tar).templateID);
name = template.getItemBaseName();
} else if (target instanceof AbstractCharacter) {
tar = (AbstractCharacter) target;
name = ((AbstractCharacter) tar).getFirstName();
+3 -3
View File
@@ -66,7 +66,7 @@ public class PrintEquipCmd extends AbstractDevCmd {
throwbackInfo(pc, "Equip for " + type + ' ' + name + " (" + tar.getObjectUUID() + ')');
for (mbEnums.EquipSlotType slot : tarMob.charItemManager.equipped.keySet()) {
Item equip = tarMob.charItemManager.equipped.get(slot);
throwbackInfo(pc, equip.templateID + " : " + equip.template.item_base_name + ", slot: " + slot);
throwbackInfo(pc, equip.templateID + " : " + equip.template.getItemBaseName() + ", slot: " + slot);
}
return;
}
@@ -76,7 +76,7 @@ public class PrintEquipCmd extends AbstractDevCmd {
throwbackInfo(pc, "Equip for " + type + ' ' + name + " (" + tar.getObjectUUID() + ')');
for (mbEnums.EquipSlotType slot : tarMob.charItemManager.equipped.keySet()) {
Item equip = tarMob.charItemManager.equipped.get(slot);
throwbackInfo(pc, equip.templateID + " : " + equip.template.item_base_name + ", slot: " + slot);
throwbackInfo(pc, equip.templateID + " : " + equip.template.getItemBaseName() + ", slot: " + slot);
}
return;
}
@@ -87,7 +87,7 @@ public class PrintEquipCmd extends AbstractDevCmd {
for (mbEnums.EquipSlotType slot : list.keySet()) {
Item item = list.get(slot);
throwbackInfo(pc, " " + item.template.item_base_name + ", slot: " + slot);
throwbackInfo(pc, " " + item.template.getItemBaseName() + ", slot: " + slot);
}
}
@@ -12,6 +12,7 @@ package engine.devcmd.cmds;
import engine.devcmd.AbstractDevCmd;
import engine.mbEnums.ItemType;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import java.text.DecimalFormat;
import java.util.ArrayList;
@@ -75,18 +76,18 @@ public class PrintInventoryCmd extends AbstractDevCmd {
int goldCount = 0;
for (Item item : inventory) {
ItemTemplate template = ItemTemplate.templates.get(item.templateID);
CObjectTemplate template = CObjectTemplate.lookup(item.templateID);
if (item.template.item_type.equals(ItemType.GOLD) == false) {
if (item.template.getItemType().equals(ItemType.GOLD) == false) {
String chargeInfo = "";
int chargeMax = template.item_initial_charges;
int chargeMax = template.getItemInitialCharges();
if (chargeMax > 0) {
int charges = item.chargesRemaining;
chargeInfo = " charges: " + charges + '/' + chargeMax;
}
throwbackInfo(pc, " " + template.item_base_name + ", count: " + item.getNumOfItems() + chargeInfo);
throwbackInfo(pc, " " + template.getItemBaseName() + ", count: " + item.getNumOfItems() + chargeInfo);
} else
goldCount += item.getNumOfItems();
}
+3 -2
View File
@@ -11,6 +11,7 @@ package engine.devcmd.cmds;
import engine.devcmd.AbstractDevCmd;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import java.util.ArrayList;
@@ -50,8 +51,8 @@ public class PrintVaultCmd extends AbstractDevCmd {
ArrayList<Item> list = cim.getVault();
throwbackInfo(pc, "Vault for " + type + ' ' + name + " (" + tar.getObjectUUID() + ')');
for (Item item : list) {
ItemTemplate template = ItemTemplate.templates.get(item.templateID);
throwbackInfo(pc, " " + template.item_base_name + ", count: " + item.getNumOfItems());
CObjectTemplate template = CObjectTemplate.lookup(item.templateID);
throwbackInfo(pc, " " + template.getItemBaseName() + ", count: " + item.getNumOfItems());
}
Item gold = cim.getGoldVault();
if (gold != null)
+4 -3
View File
@@ -5,6 +5,7 @@ import engine.gameManager.LootManager;
import engine.gameManager.ZoneManager;
import engine.loot.BootySetEntry;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import java.util.ArrayList;
import java.util.concurrent.ThreadLocalRandom;
@@ -34,10 +35,10 @@ public class SimulateBootyCmd extends AbstractDevCmd {
if (mob.bootySet != 0) {
for (BootySetEntry entry : LootManager._bootySetMap.get(mob.bootySet)) {
ItemTemplate template = ItemTemplate.templates.get(entry.templateID);
CObjectTemplate template = CObjectTemplate.lookup(entry.templateID);
if (template != null)
output += "[" + entry.bootyType + "] " + template.item_base_name + " [Chance] " + entry.dropChance + newline;
output += "[" + entry.bootyType + "] " + template.getItemBaseName() + " [Chance] " + entry.dropChance + newline;
}
}
@@ -57,7 +58,7 @@ public class SimulateBootyCmd extends AbstractDevCmd {
try {
mob.loadInventory();
for (Item lootItem : mob.charItemManager.getInventory()) {
switch (lootItem.template.item_type) {
switch (lootItem.template.getItemType()) {
case EMPLOYMENTCONTRACT: //CONTRACT
Contracts.add(lootItem);
break;
+6 -5
View File
@@ -22,6 +22,7 @@ import engine.mbEnums.BuildingGroup;
import engine.mbEnums.GameObjectType;
import engine.net.client.msg.ErrorPopupMsg;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
@@ -199,12 +200,12 @@ public enum BuildingManager {
return false;
}
ItemTemplate template = ItemTemplate.templates.get(1705032); // Elan Stone
CObjectTemplate template = CObjectTemplate.lookup(1705032); // Elan Stone
if (!player.charItemManager.hasRoomInventory(template.item_wt))
if (!player.charItemManager.hasRoomInventory(template.getItemWt()))
return false;
if (!ItemManager.MakeItemForPlayer(template.template_id, player, amount))
if (!ItemManager.MakeItemForPlayer(template.getTemplateId(), player, amount))
return false;
shrine.setFavors(0);
@@ -218,12 +219,12 @@ public enum BuildingManager {
for (mbEnums.ResourceType resourceType : EnumSet.allOf(mbEnums.ResourceType.class)) {
ItemTemplate resourceTemplate = ItemTemplate.templates.get(resourceType.templateID);
CObjectTemplate resourceTemplate = CObjectTemplate.lookup(resourceType.templateID);
if (resourceTemplate == null)
continue;
if (!player.charItemManager.hasRoomInventory(resourceTemplate.item_wt)) {
if (!player.charItemManager.hasRoomInventory(resourceTemplate.getItemWt())) {
ChatManager.chatSystemInfo(player, "You can not carry any more of that item.");
return false;
}
+4 -3
View File
@@ -22,6 +22,7 @@ import engine.net.client.ClientConnection;
import engine.net.client.handlers.ClientAdminCommandMsgHandler;
import engine.net.client.msg.chat.*;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import engine.server.MBServerStatics;
import engine.server.world.WorldServer;
import org.pmw.tinylog.Logger;
@@ -535,12 +536,12 @@ public enum ChatManager {
if (item != null) //Steal
if (success) {
String name = "";
if (item.template.item_type.equals(mbEnums.ItemType.GOLD))
if (item.template.getItemType().equals(mbEnums.ItemType.GOLD))
name = amount + " gold ";
else {
String vowels = "aeiou";
ItemTemplate template = ItemTemplate.templates.get(item.templateID);
String iName = template.item_base_name;
CObjectTemplate template = CObjectTemplate.lookup(item.templateID);
String iName = template.getItemBaseName();
if (iName.length() > 0)
if (vowels.indexOf(iName.substring(0, 1).toLowerCase()) >= 0)
name = "an " + iName + ' ';
+33 -30
View File
@@ -18,6 +18,8 @@ import engine.net.client.msg.PowerProjectileMsg;
import engine.net.client.msg.TargetedActionMsg;
import engine.net.client.msg.UpdateStateMsg;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import engine.parsers.objects.RuneTemplate;
import engine.powers.DamageShield;
import engine.powers.effectmodifiers.AbstractEffectModifier;
import engine.powers.effectmodifiers.WeaponProcEffectModifier;
@@ -92,25 +94,25 @@ public enum CombatManager {
return;
}
if (mainWeapon == null && offWeapon != null && !offWeapon.template.item_skill_used.equals("Block")) {
if (mainWeapon == null && offWeapon != null && !offWeapon.template.getItemSkillUsed().equals("Block")) {
//swing left hand only
processAttack(attacker, target, mbEnums.EquipSlotType.LHELD);
return;
}
if (mainWeapon == null && offWeapon != null && offWeapon.template.item_skill_used.equals("Block")) {
if (mainWeapon == null && offWeapon != null && offWeapon.template.getItemSkillUsed().equals("Block")) {
//no weapon equipped with a shield, punch with one hand
processAttack(attacker, target, mbEnums.EquipSlotType.RHELD);
return;
}
if (mainWeapon != null && offWeapon != null && offWeapon.template.item_skill_used.equals("Block")) {
if (mainWeapon != null && offWeapon != null && offWeapon.template.getItemSkillUsed().equals("Block")) {
//one weapon equipped with a shield, swing with one hand
processAttack(attacker, target, mbEnums.EquipSlotType.RHELD);
return;
}
if (mainWeapon != null && offWeapon != null && !offWeapon.template.item_skill_used.equals("Block")) {
if (mainWeapon != null && offWeapon != null && !offWeapon.template.getItemSkillUsed().equals("Block")) {
//two weapons equipped, swing both hands
processAttack(attacker, target, mbEnums.EquipSlotType.RHELD);
if (attacker.getObjectType().equals(mbEnums.GameObjectType.PlayerCharacter))
@@ -146,7 +148,7 @@ public enum CombatManager {
if (bonus != null)
rangeMod += bonus.getFloatPercentAll(mbEnums.ModType.WeaponRange, mbEnums.SourceType.None);
attackRange += weapon.template.item_weapon_max_range * rangeMod;
attackRange += engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_max_range * rangeMod;
} else if (attacker.getObjectType().equals(mbEnums.GameObjectType.Mob)) {
//unarmed mob, add the natural attack range from its rune (0 for most melee mobs)
@@ -211,7 +213,7 @@ public enum CombatManager {
if (weapon != null) {
float wepSpeed = (int) (weapon.template.item_weapon_wepspeed);
float wepSpeed = (int) (engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_wepspeed);
if (weapon.getBonusPercent(mbEnums.ModType.WeaponSpeed, mbEnums.SourceType.None) != 0f) //add weapon speed bonus
wepSpeed *= (1 + weapon.getBonus(mbEnums.ModType.WeaponSpeed, mbEnums.SourceType.None));
@@ -270,13 +272,13 @@ public enum CombatManager {
if (weapon != null && !attacker.getObjectType().equals(mbEnums.GameObjectType.Mob)) {
//check if Out of Stamina
if (attacker.getObjectType().equals(mbEnums.GameObjectType.PlayerCharacter)) {
if (attacker.getStamina() < (weapon.template.item_wt / 3f)) {
if (attacker.getStamina() < (weapon.template.getItemWt() / 3f)) {
//set auto attack job
setAutoAttackJob(attacker, slot, delay);
return;
}
}
float stam = weapon.template.item_wt / 3f;
float stam = weapon.template.getItemWt() / 3f;
stam = (stam < 1) ? 1 : stam;
attacker.modifyStamina(-(stam), attacker, true);
} else
@@ -416,7 +418,7 @@ public enum CombatManager {
if (((Mob) attacker).isSiege())
damageType = mbEnums.DamageType.SIEGE;
} else {
damageType = (mbEnums.DamageType) attacker.charItemManager.getEquipped().get(slot).template.item_weapon_damage.keySet().toArray()[0];
damageType = engine.parsers.objects.ItemTemplate.getWeapon(attacker.charItemManager.getEquipped().get(slot).templateID).weapon_damage.get(0).damage_type;
}
//get resists
@@ -577,13 +579,14 @@ public enum CombatManager {
target.setCombatTarget(attacker);
}
public static int getSwingAnimation(ItemTemplate wb, DeferredPowerJob dpj, mbEnums.EquipSlotType slot) {
public static int getSwingAnimation(CObjectTemplate wb, DeferredPowerJob dpj, mbEnums.EquipSlotType slot) {
//No weapon, return default animation
if (wb == null)
return 75;
int token;
engine.parsers.objects.ItemTemplate.Weapon weapon = engine.parsers.objects.ItemTemplate.getWeapon(wb.getTemplateId());
if (dpj != null) {
@@ -594,7 +597,7 @@ public enum CombatManager {
}
//Item has no equipment slots and should not try to return an animation, return default instead
if (wb.item_eq_slots_or == null || wb.item_eq_slots_or.isEmpty()) {
if (wb.getItemEqSlotsOr().isEmpty()) {
return 75;
}
@@ -603,18 +606,18 @@ public enum CombatManager {
int random;
//Item can only be equipped in one slot, return animation for that slot
if (wb.item_eq_slots_or.size() == 1) {
if (wb.item_eq_slots_or.iterator().next().equals(mbEnums.EquipSlotType.RHELD)) {
anim = wb.weapon_attack_anim_right.get(0)[0];
if (wb.getItemEqSlotsOr().size() == 1) {
if (wb.getItemEqSlotsOr().iterator().next().equals(mbEnums.EquipSlotType.RHELD)) {
anim = weapon.weapon_attack_anim_right.get(0)[0];
if (dpj != null) {
random = ThreadLocalRandom.current().nextInt(wb.weapon_attack_anim_right.size());
anim = wb.weapon_attack_anim_right.get(random)[0];
random = ThreadLocalRandom.current().nextInt(weapon.weapon_attack_anim_right.size());
anim = weapon.weapon_attack_anim_right.get(random)[0];
}
} else {
anim = wb.weapon_attack_anim_left.get(0)[0];
anim = weapon.weapon_attack_anim_left.get(0)[0];
if (dpj != null) {
random = ThreadLocalRandom.current().nextInt(wb.weapon_attack_anim_left.size());
anim = wb.weapon_attack_anim_left.get(random)[0];
random = ThreadLocalRandom.current().nextInt(weapon.weapon_attack_anim_left.size());
anim = weapon.weapon_attack_anim_left.get(random)[0];
}
}
return anim;
@@ -622,16 +625,16 @@ public enum CombatManager {
//Item can be equipped in either hand, and should have animation sets for each hand
if (slot.equals(mbEnums.EquipSlotType.RHELD)) {
anim = wb.weapon_attack_anim_right.get(0)[0];
anim = weapon.weapon_attack_anim_right.get(0)[0];
if (dpj != null) {
random = ThreadLocalRandom.current().nextInt(wb.weapon_attack_anim_right.size());
anim = wb.weapon_attack_anim_right.get(random)[0];
random = ThreadLocalRandom.current().nextInt(weapon.weapon_attack_anim_right.size());
anim = weapon.weapon_attack_anim_right.get(random)[0];
}
} else {
anim = wb.weapon_attack_anim_left.get(0)[0];
anim = weapon.weapon_attack_anim_left.get(0)[0];
if (dpj != null) {
random = ThreadLocalRandom.current().nextInt(wb.weapon_attack_anim_left.size());
anim = wb.weapon_attack_anim_left.get(random)[0];
random = ThreadLocalRandom.current().nextInt(weapon.weapon_attack_anim_left.size());
anim = weapon.weapon_attack_anim_left.get(random)[0];
}
}
return anim;
@@ -713,7 +716,7 @@ public enum CombatManager {
// Mobs use their mobbase rune, players use their race rune.
int runeID;
ItemTemplate rune;
RuneTemplate rune;
int hand = slot.equals(mbEnums.EquipSlotType.LHELD) ? 2 : 1;
switch (attacker.getObjectType()) {
@@ -727,18 +730,18 @@ public enum CombatManager {
return null;
}
rune = ItemTemplate.templates.get(runeID);
rune = RuneTemplate._runes.get(runeID);
if (rune == null || rune.rune_natural_attacks.isEmpty())
if (rune == null || rune.rune_naturalattacks.isEmpty())
return null;
for (float[] attack : rune.rune_natural_attacks)
for (float[] attack : rune.rune_naturalattacks)
if (attack.length > 3 && (int) attack[3] == hand)
return attack;
// No entry for this hand, fall back to the first natural attack
return rune.rune_natural_attacks.get(0);
return rune.rune_naturalattacks.get(0);
}
public static int calculatePetDamage(AbstractCharacter agent) {
@@ -101,6 +101,7 @@ public enum DevCmdManager {
DevCmdManager.registerDevCmd(new SetAdminRuneCmd());
DevCmdManager.registerDevCmd(new SetInvulCmd());
DevCmdManager.registerDevCmd(new MakeItemCmd());
DevCmdManager.registerDevCmd(new FillCmd());
DevCmdManager.registerDevCmd(new SetSubRaceCmd());
// Admin
+16 -9
View File
@@ -15,8 +15,8 @@ import engine.mbEnums;
import engine.net.client.msg.ItemProductionMsg;
import engine.objects.City;
import engine.objects.Item;
import engine.objects.ItemTemplate;
import engine.objects.NPC;
import engine.parsers.objects.CObjectTemplate;
import engine.powers.EffectsBase;
import org.pmw.tinylog.Logger;
@@ -49,6 +49,9 @@ public enum ForgeManager implements Runnable {
public static final ConcurrentHashMap<NPC, ConcurrentHashMap.KeySetView<WorkOrder, Boolean>> vendorWorkOrderLookup = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<Item, WorkOrder> itemWorkOrderLookup = new ConcurrentHashMap<>();
// Server-only data from static_vendor_items; the client templates have no mod table.
public static HashMap<Integer, Integer> vendorModTables = new HashMap<>();
public static void start() {
Thread forgeManager;
@@ -222,12 +225,12 @@ public enum ForgeManager implements Runnable {
// Calculate production cost for a single run of the workOrder
HashMap<mbEnums.ResourceType, Integer> production_cost = new HashMap<>();
ItemTemplate template = ItemTemplate.templates.get(workOrder.templateID);
CObjectTemplate template = CObjectTemplate.lookup(workOrder.templateID);
// Add gold and resource costs from template
production_cost.put(mbEnums.ResourceType.GOLD, template.item_value);
production_cost.putAll(template.item_resource_cost);
production_cost.put(mbEnums.ResourceType.GOLD, template.getItemValue());
production_cost.putAll(template.getItemResourceCosts());
// Calculate cost of prefix and suffix
@@ -248,8 +251,8 @@ public enum ForgeManager implements Runnable {
// Create new virtual item from specified template
ItemTemplate template = ItemTemplate.templates.get(workOrder.templateID);
Item forgedItem = new Item(workOrder.templateID);
int modTable = getVendorModTable(workOrder.templateID);
// forgedItem gets a negative id; a virtual item which is not persisted
@@ -264,13 +267,13 @@ public enum ForgeManager implements Runnable {
// Assign a prefix and suffix to this item if random rolled
if (workOrder.prefixToken == 0 && workOrder.vendor.getItemModTable().contains((template.modTable)))
forgedItem.prefixToken = calcRandomMod(workOrder.vendor, mbEnums.ItemModType.PREFIX, template.modTable);
if (workOrder.prefixToken == 0 && workOrder.vendor.getItemModTable().contains(modTable))
forgedItem.prefixToken = calcRandomMod(workOrder.vendor, mbEnums.ItemModType.PREFIX, modTable);
else
forgedItem.prefixToken = workOrder.prefixToken;
if (workOrder.suffixToken == 0 && workOrder.vendor.getItemModTable().contains((template.modTable)))
forgedItem.suffixToken = calcRandomMod(workOrder.vendor, mbEnums.ItemModType.SUFFIX, template.modTable);
if (workOrder.suffixToken == 0 && workOrder.vendor.getItemModTable().contains(modTable))
forgedItem.suffixToken = calcRandomMod(workOrder.vendor, mbEnums.ItemModType.SUFFIX, modTable);
else
forgedItem.suffixToken = workOrder.suffixToken;
@@ -364,6 +367,10 @@ public enum ForgeManager implements Runnable {
DbManager.WarehouseQueries.WRITE_WORKORDER(workOrder);
}
public static int getVendorModTable(int templateID) {
return vendorModTables.getOrDefault(templateID, 0);
}
public static int calcRandomMod(NPC vendor, mbEnums.ItemModType itemModType, int modTable) {
// Random prefix or suffix token based on item.template.modtable
+1 -1
View File
@@ -307,7 +307,7 @@ public enum GroupManager {
return false;
}
if (!item.template.item_type.equals(mbEnums.ItemType.GOLD)) //only split goldItem
if (!item.template.getItemType().equals(mbEnums.ItemType.GOLD)) //only split goldItem
return false;
Group group = getGroup(pc);
+35 -54
View File
@@ -2,6 +2,7 @@ package engine.gameManager;
import engine.mbEnums;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import engine.powers.EffectsBase;
import org.pmw.tinylog.Logger;
@@ -21,66 +22,46 @@ public enum ItemManager {
public static final AtomicInteger lastNegativeID = new AtomicInteger(0);
// restrict true: the set is a blacklist; false: a whitelist. An empty set allows everyone.
public static Boolean ValidRace(Item item, mbEnums.MonsterType race) {
if (item.template.item_race_req.isEmpty() && item.template.item_race_res.isEmpty())
EnumSet<mbEnums.MonsterType> races = item.template.getItemRaceReq();
boolean restrict = item.template.getItemRaceReqRestrict();
if (races.isEmpty())
return true;
if (item.template.item_race_req.isEmpty() == false)
if (item.template.item_race_req.contains(race))
return true;
if (item.template.item_race_res.isEmpty() == false)
if (item.template.item_race_res.contains(race) == false)
return true;
return false;
return races.contains(race) != restrict;
}
public static Boolean ValidClass(Item item, mbEnums.ClassType base, mbEnums.ClassType profession) {
// Early exit if no entry
EnumSet<mbEnums.ClassType> classes = item.template.getItemClassReq();
boolean restrict = item.template.getItemClassReqRestrict();
boolean listed;
if (item.template.item_class_req.isEmpty() && item.template.item_class_res.isEmpty())
if (classes.isEmpty())
return true;
if (item.template.item_class_req.isEmpty() == false)
if (item.template.item_class_req.contains(base) || item.template.item_class_req.contains(profession))
return true;
listed = classes.contains(base) || classes.contains(profession);
if (item.template.item_class_res.isEmpty() == false)
if (item.template.item_class_res.contains(base) == false && item.template.item_class_res.contains(profession) == false)
return true;
return false;
return listed != restrict;
}
public static Boolean ValidDiscipline(Item item, EnumSet<mbEnums.DisciplineType> discs) {
// Early exit if no entry
EnumSet<mbEnums.DisciplineType> disciplines = item.template.getItemDiscReq();
boolean restrict = item.template.getItemDiscReqRestrict();
EnumSet<mbEnums.DisciplineType> workSet;
if (item.template.item_disc_req.isEmpty() && item.template.item_disc_res.isEmpty())
if (disciplines.isEmpty())
return true;
EnumSet<mbEnums.DisciplineType> workSet = EnumSet.copyOf(discs);
workSet = EnumSet.copyOf(discs);
workSet.retainAll(disciplines);
if (item.template.item_disc_req.isEmpty() == false) {
workSet.retainAll(item.template.item_disc_req);
if (workSet.isEmpty() == false)
return true;
}
if (item.template.item_disc_res.isEmpty() == false) {
workSet.retainAll(item.template.item_disc_res);
if (workSet.isEmpty() == false)
return true;
}
return false;
return workSet.isEmpty() == restrict;
}
public static Boolean canCharacterEquip(Item item, AbstractCharacter character) {
@@ -91,12 +72,12 @@ public enum ItemManager {
CharacterSkill characterSkill;
if (item.template.item_skill_required.isEmpty())
if (item.template.getItemSkillReq().isEmpty())
return true;
for (String skillRequired : item.template.item_skill_required.keySet()) {
for (String skillRequired : item.template.getItemSkillReq().keySet()) {
int required_value = item.template.item_skill_required.get(skillRequired);
int required_value = item.template.getItemSkillReq().get(skillRequired);
characterSkill = skills.get(skillRequired);
if (characterSkill == null)
@@ -112,31 +93,31 @@ public enum ItemManager {
public static boolean isTwoHanded(Item item) {
if (!item.template.item_type.equals(mbEnums.ItemType.WEAPON))
if (!item.template.getItemType().equals(mbEnums.ItemType.WEAPON))
return false;
return item.template.item_eq_slots_and.contains(EnumSet.of(mbEnums.EquipSlotType.LHELD, mbEnums.EquipSlotType.RHELD));
return item.template.getItemEqSlotsAnd().containsAll(EnumSet.of(mbEnums.EquipSlotType.LHELD, mbEnums.EquipSlotType.RHELD));
}
public static boolean isTwoHanded(ItemTemplate template) {
public static boolean isTwoHanded(CObjectTemplate template) {
if (!template.item_type.equals(mbEnums.ItemType.WEAPON))
if (!template.getItemType().equals(mbEnums.ItemType.WEAPON))
return false;
return template.item_eq_slots_and.contains(EnumSet.of(mbEnums.EquipSlotType.LHELD, mbEnums.EquipSlotType.RHELD));
return template.getItemEqSlotsAnd().containsAll(EnumSet.of(mbEnums.EquipSlotType.LHELD, mbEnums.EquipSlotType.RHELD));
}
public static boolean isShield(Item item) {
if (item.template.item_skill_required.containsKey("Block"))
if (item.template.getItemSkillReq().containsKey("Block"))
return true;
return false;
}
public static boolean isShield(ItemTemplate template) {
public static boolean isShield(CObjectTemplate template) {
if (template.item_skill_required.containsKey("Block"))
if (template.getItemSkillReq().containsKey("Block"))
return true;
return false;
@@ -150,7 +131,7 @@ public enum ItemManager {
return false;
//Item not valid for slot
if (item.template.item_eq_slots_or.contains(slot) == false)
if (item.template.getItemEqSlotsOr().contains(slot) == false)
return false;
// Slot is taken
@@ -165,7 +146,7 @@ public enum ItemManager {
(slot == mbEnums.EquipSlotType.RHELD && equipped.get(mbEnums.EquipSlotType.LHELD) != null)))
return false;
if (item.template.item_type.equals(mbEnums.ItemType.WEAPON))
if (item.template.getItemType().equals(mbEnums.ItemType.WEAPON))
if (equipped.get(slot) != null && equipped.get(slot).equals(item) == false)
return false;
@@ -197,7 +178,7 @@ public enum ItemManager {
//players can't wear 0 value items.
return item.template.item_value != 0 || Kit.IsNoobGear(item.templateID);
return item.template.getItemValue() != 0 || Kit.IsNoobGear(item.templateID);
}
+9 -8
View File
@@ -13,6 +13,7 @@ import engine.mbEnums;
import engine.net.client.msg.ErrorPopupMsg;
import engine.net.client.msg.chat.ChatSystemMsg;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import org.pmw.tinylog.Logger;
import java.util.ArrayList;
@@ -91,8 +92,8 @@ public enum LootManager {
//lastly, check mobs inventory for godly or disc runes to send a server announcement
for (Item item : mob.getInventory()) {
if ((item.templateID > 2499 && item.templateID <= 3050) || item.template.item_base_name.toLowerCase().contains("of the gods")) {
ChatSystemMsg chatMsg = new ChatSystemMsg(null, mob.getName() + " in " + mob.getParentZone().zoneName + " has found the " + item.template.item_base_name + ". Are you tough enough to take it?");
if ((item.templateID > 2499 && item.templateID <= 3050) || item.template.getItemBaseName().toLowerCase().contains("of the gods")) {
ChatSystemMsg chatMsg = new ChatSystemMsg(null, mob.getName() + " in " + mob.getParentZone().zoneName + " has found the " + item.template.getItemBaseName() + ". Are you tough enough to take it?");
chatMsg.setMessageType(10);
chatMsg.setChannel(mbEnums.ChatChannelType.SYSTEM.getChannelID());
DispatchManager.dispatchMsgToAll(chatMsg);
@@ -179,12 +180,12 @@ public enum LootManager {
if (itemUUID == 0)
return null;
if (ItemTemplate.templates.get(itemUUID).item_type.equals(mbEnums.ItemType.RESOURCE)) {
if (CObjectTemplate.lookup(itemUUID).getItemType().equals(mbEnums.ItemType.RESOURCE)) {
int amount = ThreadLocalRandom.current().nextInt(tableRow.minSpawn, tableRow.maxSpawn + 1);
return new MobLoot(mob, ItemTemplate.templates.get(itemUUID), amount, false);
return new MobLoot(mob, CObjectTemplate.lookup(itemUUID), amount, false);
}
outItem = new MobLoot(mob, ItemTemplate.templates.get(itemUUID), false);
outItem = new MobLoot(mob, CObjectTemplate.lookup(itemUUID), false);
if (selectedRow.pModTable != 0)
try {
@@ -374,7 +375,7 @@ public enum LootManager {
if (chanceRoll > bse.dropChance)
return;
MobLoot lootItem = new MobLoot(mob, ItemTemplate.templates.get(bse.templateID), true);
MobLoot lootItem = new MobLoot(mob, CObjectTemplate.lookup(bse.templateID), true);
if (lootItem != null)
mob.charItemManager.addItemToInventory(lootItem);
@@ -422,14 +423,14 @@ public enum LootManager {
//create the item from the table, quantity is always 1
MobLoot winnings = new MobLoot(playerCharacter, ItemTemplate.templates.get(selectedItem.templateID), 1, false);
MobLoot winnings = new MobLoot(playerCharacter, CObjectTemplate.lookup(selectedItem.templateID), 1, false);
if (winnings == null)
return;
//early exit if the inventory of the player will not old the item
if (itemMan.hasRoomInventory(winnings.template.item_wt) == false) {
if (itemMan.hasRoomInventory(winnings.template.getItemWt()) == false) {
ErrorPopupMsg.sendErrorPopup(playerCharacter, 21);
return;
}
+26 -21
View File
@@ -19,12 +19,14 @@ import engine.net.client.ClientConnection;
import engine.net.client.msg.ErrorPopupMsg;
import engine.net.client.msg.PetMsg;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import engine.powers.EffectsBase;
import engine.powers.RuneSkillAdjustEntry;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
@@ -315,26 +317,26 @@ public enum NPCManager {
//has main hand weapon
Item weapon = guard.charItemManager.equipped.get(mbEnums.EquipSlotType.RHELD);
if (weapon.template.item_primary_attr.equals(mbEnums.AttributeType.Strength))
if (engine.parsers.objects.ItemTemplate._items.get(weapon.templateID).item_primary_attr == mbEnums.AttributeType.Strength)
primaryStat = guard.getStatStrCurrent();
else
primaryStat = guard.getStatDexCurrent();
guard.minDamageHandOne = (int) ((guard.mobBase.getDamageMin() + weapon.template.item_weapon_damage.values().iterator().next()[0]) * rankModifier) + primaryStat;
guard.maxDamageHandOne = (int) ((guard.mobBase.getDamageMax() + weapon.template.item_weapon_damage.values().iterator().next()[1]) * rankModifier) + primaryStat;
guard.speedHandOne = weapon.template.item_weapon_wepspeed;
guard.rangeHandOne = weapon.template.item_weapon_max_range;
guard.minDamageHandOne = (int) ((guard.mobBase.getDamageMin() + engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_damage.get(0).damage_min) * rankModifier) + primaryStat;
guard.maxDamageHandOne = (int) ((guard.mobBase.getDamageMax() + engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_damage.get(0).damage_max) * rankModifier) + primaryStat;
guard.speedHandOne = engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_wepspeed;
guard.rangeHandOne = engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_max_range;
} else if (guard.charItemManager.equipped.containsKey(mbEnums.EquipSlotType.LHELD) && !ItemManager.isShield(guard.charItemManager.equipped.get(mbEnums.EquipSlotType.LHELD).template)) {
//has off hand weapon
Item weapon = guard.charItemManager.equipped.get(mbEnums.EquipSlotType.LHELD);
if (weapon.template.item_primary_attr.equals(mbEnums.AttributeType.Strength))
if (engine.parsers.objects.ItemTemplate._items.get(weapon.templateID).item_primary_attr == mbEnums.AttributeType.Strength)
primaryStat = guard.getStatStrCurrent();
else
primaryStat = guard.getStatDexCurrent();
guard.minDamageHandOne = (int) ((guard.mobBase.getDamageMin() + weapon.template.item_weapon_damage.values().iterator().next()[0]) * rankModifier) + primaryStat;
guard.maxDamageHandOne = (int) ((guard.mobBase.getDamageMax() + weapon.template.item_weapon_damage.values().iterator().next()[1]) * rankModifier) + primaryStat;
guard.speedHandOne = weapon.template.item_weapon_wepspeed;
guard.rangeHandOne = weapon.template.item_weapon_max_range;
guard.minDamageHandOne = (int) ((guard.mobBase.getDamageMin() + engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_damage.get(0).damage_min) * rankModifier) + primaryStat;
guard.maxDamageHandOne = (int) ((guard.mobBase.getDamageMax() + engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_damage.get(0).damage_max) * rankModifier) + primaryStat;
guard.speedHandOne = engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_wepspeed;
guard.rangeHandOne = engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_max_range;
} else {
primaryStat = guard.getStatStrCurrent();
guard.minDamageHandOne = (int) ((guard.mobBase.getDamageMin()) * rankModifier) + primaryStat;
@@ -352,8 +354,8 @@ public enum NPCManager {
int baseDef = guard.mobBase.getDefenseRating();
int armorDefense = 0;
for (Item equipped : guard.charItemManager.equipped.values())
if (equipped.template.item_type.equals(mbEnums.ItemType.ARMOR) || ItemManager.isShield(equipped.template))
armorDefense += equipped.template.item_defense_rating;
if (equipped.template.getItemType().equals(mbEnums.ItemType.ARMOR) || ItemManager.isShield(equipped.template))
armorDefense += engine.parsers.objects.ItemTemplate._items.get(equipped.templateID).item_defense_rating;
guard.defenseRating = dexterity + baseDef + armorDefense;
}
@@ -361,9 +363,9 @@ public enum NPCManager {
int strength = guard.getStatStrCurrent();
int baseAtr = guard.mobBase.getAttackRating();
if (guard.charItemManager.equipped.get(mbEnums.EquipSlotType.RHELD) != null)
guard.atrHandOne = baseAtr + (int) ((strength * 0.5f) + ((int) guard.charItemManager.equipped.get(mbEnums.EquipSlotType.RHELD).template.item_skill_required.values().toArray()[0] * 4) + ((int) guard.charItemManager.equipped.get(mbEnums.EquipSlotType.RHELD).template.item_skill_required.values().toArray()[0] * 3));
guard.atrHandOne = baseAtr + (int) ((strength * 0.5f) + ((int) guard.charItemManager.equipped.get(mbEnums.EquipSlotType.RHELD).template.getItemSkillReq().values().toArray()[0] * 4) + ((int) guard.charItemManager.equipped.get(mbEnums.EquipSlotType.RHELD).template.getItemSkillReq().values().toArray()[0] * 3));
else if (guard.charItemManager.equipped.get(mbEnums.EquipSlotType.LHELD) != null && !ItemManager.isShield(guard.charItemManager.equipped.get(mbEnums.EquipSlotType.LHELD).template))
guard.atrHandTwo = baseAtr + (int) ((strength * 0.5f) + ((int) guard.charItemManager.equipped.get(mbEnums.EquipSlotType.LHELD).template.item_skill_required.values().toArray()[0] * 4) + ((int) guard.charItemManager.equipped.get(mbEnums.EquipSlotType.LHELD).template.item_skill_required.values().toArray()[0] * 3));
guard.atrHandTwo = baseAtr + (int) ((strength * 0.5f) + ((int) guard.charItemManager.equipped.get(mbEnums.EquipSlotType.LHELD).template.getItemSkillReq().values().toArray()[0] * 4) + ((int) guard.charItemManager.equipped.get(mbEnums.EquipSlotType.LHELD).template.getItemSkillReq().values().toArray()[0] * 3));
else
guard.atrHandOne = baseAtr;
}
@@ -438,14 +440,17 @@ public enum NPCManager {
public static void applyEquipmentResists(Mob mob) {
EnumMap<mbEnums.DamageType, Float> armorResists;
if (mob.charItemManager.equipped.isEmpty())
return;
for (Item equipped : mob.charItemManager.equipped.values()) {
if (equipped.template.item_type.equals(mbEnums.ItemType.ARMOR)) {
mob.resists.setResist(mbEnums.DamageType.SLASHING, mob.resists.getResist(mbEnums.DamageType.SLASHING, 0) + equipped.template.combat_attack_resist.get("SLASHING"));
mob.resists.setResist(mbEnums.DamageType.CRUSHING, mob.resists.getResist(mbEnums.DamageType.CRUSHING, 0) + equipped.template.combat_attack_resist.get("CRUSHING"));
mob.resists.setResist(mbEnums.DamageType.PIERCING, mob.resists.getResist(mbEnums.DamageType.PIERCING, 0) + equipped.template.combat_attack_resist.get("PIERCING"));
if (equipped.template.getItemType().equals(mbEnums.ItemType.ARMOR)) {
armorResists = equipped.template.getCombatAttackResist();
mob.resists.setResist(mbEnums.DamageType.SLASHING, mob.resists.getResist(mbEnums.DamageType.SLASHING, 0) + armorResists.getOrDefault(mbEnums.DamageType.SLASHING, 0f));
mob.resists.setResist(mbEnums.DamageType.CRUSHING, mob.resists.getResist(mbEnums.DamageType.CRUSHING, 0) + armorResists.getOrDefault(mbEnums.DamageType.CRUSHING, 0f));
mob.resists.setResist(mbEnums.DamageType.PIERCING, mob.resists.getResist(mbEnums.DamageType.PIERCING, 0) + armorResists.getOrDefault(mbEnums.DamageType.PIERCING, 0f));
}
}
}
@@ -541,14 +546,14 @@ public enum NPCManager {
public static long calcRollingDuration(NPC vendor, int templateID) {
ItemTemplate template = ItemTemplate.templates.get(templateID);
CObjectTemplate template = CObjectTemplate.lookup(templateID);
float rollingDuration;
if (template == null)
return 0;
if (template.item_bane_rank > 0)
return (long) (template.item_bane_rank * 60 * 60 * 3 * 1000 * Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue()));
if (template.getItemBaneRank() > 0)
return (long) (template.getItemBaneRank() * 60 * 60 * 3 * 1000 * Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue()));
if (vendor.building == null)
return 600;
+3 -3
View File
@@ -2255,12 +2255,12 @@ public enum PowersManager {
if (pb.targetItem())
return true;
// TODO add these checks later
else if (pb.targetArmor() && item.template.item_type.equals(ItemType.ARMOR))
else if (pb.targetArmor() && item.template.getItemType().equals(ItemType.ARMOR))
return true;
else if (pb.targetJewelry() && item.template.item_type.equals(ItemType.JEWELRY))
else if (pb.targetJewelry() && item.template.getItemType().equals(ItemType.JEWELRY))
return true;
else
return pb.targetWeapon() && item.template.item_type.equals(ItemType.WEAPON);
return pb.targetWeapon() && item.template.getItemType().equals(ItemType.WEAPON);
} // How did we get here? all valid targets have been covered
else
return false;
+4 -4
View File
@@ -12,9 +12,9 @@ import engine.gameManager.DbManager;
import engine.gameManager.ForgeManager;
import engine.mbEnums;
import engine.objects.Item;
import engine.objects.ItemTemplate;
import engine.objects.NPC;
import engine.objects.Warehouse;
import engine.parsers.objects.CObjectTemplate;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -127,16 +127,16 @@ public class WorkOrder implements Delayed {
int validation_result = 0;
ItemTemplate template = ItemTemplate.templates.get(workOrder.templateID);
CObjectTemplate template = CObjectTemplate.lookup(workOrder.templateID);
if (workOrder.vendor.getBuilding() == null)
return 58; //58: The formula is beyond the means of this facility
if (!workOrder.vendor.charItemManager.hasRoomInventory(template.item_wt))
if (!workOrder.vendor.charItemManager.hasRoomInventory(template.getItemWt()))
return 30; //30: That person cannot carry that item
if ((workOrder.prefixToken != 0 || workOrder.suffixToken != 0) &&
!workOrder.vendor.getItemModTable().contains((template.modTable)))
!workOrder.vendor.getItemModTable().contains(ForgeManager.getVendorModTable(workOrder.templateID)))
return 59; //59: This hireling does not have this formula
if (!Warehouse.calcCostOverrun(workOrder).isEmpty())
@@ -46,7 +46,7 @@ public class ActivateNPCMsgHandler extends AbstractClientMsgHandler {
// Filter hirelings by slot type
for (Item hirelings : player.getInventory()) {
if (hirelings.template.item_type.equals(ItemType.EMPLOYMENTCONTRACT)) {
if (hirelings.template.getItemType().equals(ItemType.EMPLOYMENTCONTRACT)) {
contract = DbManager.ContractQueries.GET_CONTRACT(hirelings.templateID);
@@ -78,7 +78,7 @@ public class AddItemToTradeWindowMsgHandler extends AbstractClientMsgHandler {
if (!item.validForInventory(source.getClientConnection(), source, source.charItemManager))
return false;
if (!tradingWith.hasRoomTrade(item.template.item_wt)) {
if (!tradingWith.hasRoomTrade(item.template.getItemWt())) {
dispatch = Dispatch.borrow(source, msg);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.PRIMARY);
return false;
@@ -24,6 +24,7 @@ import engine.net.client.msg.guild.GuildInfoMsg;
import engine.objects.Guild;
import engine.objects.Item;
import engine.objects.PlayerCharacter;
import engine.parsers.objects.DeedTemplate;
import engine.util.StringUtils;
public class GuildCreationFinalizeHandler extends AbstractClientMsgHandler {
@@ -42,6 +43,7 @@ public class GuildCreationFinalizeHandler extends AbstractClientMsgHandler {
Guild playerGuild;
Item charter;
DeedTemplate charterDeed;
Dispatch dispatch;
msg = (GuildCreationFinalizeMsg) baseMsg;
@@ -65,7 +67,9 @@ public class GuildCreationFinalizeHandler extends AbstractClientMsgHandler {
// Item must be a valid charterType (type 10 in db)
if (charter == null || (charter.template.item_type.equals(ItemType.CHARTER) == false)) {
charterDeed = DeedTemplate._deeds.get(charter.templateID);
if (charterDeed == null || (charterDeed.item_type.equals(ItemType.CHARTER) == false)) {
ErrorPopupMsg.sendErrorPopup(player, GuildManager.NO_CHARTER_FOUND);
return true;
}
@@ -238,10 +238,10 @@ public class ItemProductionMsgHandler extends AbstractClientMsgHandler {
if (targetItem == null)
return;
if (targetItem.template.item_type.equals(ItemType.GOLD))
if (targetItem.template.getItemType().equals(ItemType.GOLD))
return;
if (!vendor.charItemManager.hasRoomInventory(targetItem.template.item_wt)) {
if (!vendor.charItemManager.hasRoomInventory(targetItem.template.getItemWt())) {
ErrorPopupMsg.sendErrorPopup(player, 21);
return;
}
@@ -319,7 +319,7 @@ public class ItemProductionMsgHandler extends AbstractClientMsgHandler {
if (targetItem == null)
continue;
if (targetItem.template.item_type.equals(ItemType.GOLD))
if (targetItem.template.getItemType().equals(ItemType.GOLD))
return;
if (!vendor.charItemManager.doesCharOwnThisItem(targetItem.getObjectUUID()))
@@ -328,14 +328,14 @@ public class ItemProductionMsgHandler extends AbstractClientMsgHandler {
if (!vendor.charItemManager.inventoryContains(targetItem))
continue;
itemValue = targetItem.template.item_value;
itemValue = targetItem.template.getItemValue();
if (vendor.getBuilding().getStrongboxValue() + itemValue > vendor.getBuilding().getMaxGold()) {
ErrorPopupMsg.sendErrorPopup(player, 201);
break;
}
switch (targetItem.template.item_type) {
switch (targetItem.template.getItemType()) {
case EMPLOYMENTCONTRACT:
case CHARTER:
case DEED:
@@ -465,13 +465,13 @@ public class ItemProductionMsgHandler extends AbstractClientMsgHandler {
if (targetItem == null)
return;
if (targetItem.template.item_type.equals(ItemType.GOLD))
if (targetItem.template.getItemType().equals(ItemType.GOLD))
return;
if (!vendor.charItemManager.inventoryContains(targetItem))
return;
if (!player.charItemManager.hasRoomInventory(targetItem.template.item_wt))
if (!player.charItemManager.hasRoomInventory(targetItem.template.getItemWt()))
return;
player.charItemManager.buyFromNPC(targetItem, vendor);
@@ -161,7 +161,7 @@ public class LootMsgHandler extends AbstractClientMsgHandler {
if (itemRet == null)
return true;
if (item.template.item_type.equals(mbEnums.ItemType.GOLD)) {
if (item.template.getItemType().equals(mbEnums.ItemType.GOLD)) {
// this is done to prevent the temporary goldItem item
// (from the mob) from appearing in player's inventory.
// It also updates the goldItem quantity display
@@ -187,7 +187,7 @@ public class LootMsgHandler extends AbstractClientMsgHandler {
//TODO send group loot message if player is grouped and visible
Group group = GroupManager.getGroup(player);
if (group != null && group.getSplitGold() && (item.template.item_type.equals(mbEnums.ItemType.GOLD) == false)) {
if (group != null && group.getSplitGold() && (item.template.getItemType().equals(mbEnums.ItemType.GOLD) == false)) {
String name = item.getName();
String text = player.getFirstName() + " has looted " + name + '.';
ChatManager.chatGroupInfoCanSee(player, text);
@@ -202,7 +202,7 @@ public class LootMsgHandler extends AbstractClientMsgHandler {
if (itemRet == null)
return true;
if (item.template.item_type.equals(mbEnums.ItemType.GOLD)) {
if (item.template.getItemType().equals(mbEnums.ItemType.GOLD)) {
// this is done to prevent the temporary goldItem item
// (from the mob) from appearing in player's inventory.
// It also updates the goldItem quantity display
@@ -241,7 +241,7 @@ public class LootMsgHandler extends AbstractClientMsgHandler {
//TODO send group loot message if player is grouped and visible
Group group = GroupManager.getGroup(player);
if (group != null && group.getSplitGold() && (item.template.item_type.equals(mbEnums.ItemType.GOLD) == false)) {
if (group != null && group.getSplitGold() && (item.template.getItemType().equals(mbEnums.ItemType.GOLD) == false)) {
String name = item.getName();
String text = player.getFirstName() + " has looted " + name + '.';
ChatManager.chatGroupInfoCanSee(player, text);
@@ -8,6 +8,7 @@ import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.*;
import engine.objects.*;
import engine.parsers.objects.DeedTemplate;
import engine.powers.PowersBase;
import java.util.ArrayList;
@@ -78,7 +79,7 @@ public class ObjectActionMsgHandler extends AbstractClientMsgHandler {
int uuid = item.templateID;
switch (item.template.item_type) {
switch (item.template.getItemType()) {
case DEED: //buildings
//Call add building screen here, ib.getUseID() get's building ID
@@ -99,7 +100,7 @@ public class ObjectActionMsgHandler extends AbstractClientMsgHandler {
pam.setX(loc.getX() + 64); //offset grid from tol
pam.setY(loc.getY());
pam.setZ(loc.getZ() + 64); //offset grid from tol
pam.addPlacementInfo(item.template.deed_structure_id);
pam.addPlacementInfo(DeedTemplate._deeds.get(item.templateID).deed_structure_id);
dispatch = Dispatch.borrow(player, pam);
DispatchManager.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
@@ -187,14 +188,14 @@ public class ObjectActionMsgHandler extends AbstractClientMsgHandler {
if (uuid > 680069 && uuid < 680074) //Handle Charter, Deed, Petition, Warrant here
break;
if (item.template.item_bane_rank > 0) {
if (item.template.getItemBaneRank() > 0) {
// Only one banestone at a time
lock.writeLock().lock();
try {
if (Bane.summonBanestone(player, origin, item.template.item_bane_rank) == true)
if (Bane.summonBanestone(player, origin, item.template.getItemBaneRank()) == true)
itemMan.consume(item);
} finally {
lock.writeLock().unlock();
@@ -225,7 +226,7 @@ public class ObjectActionMsgHandler extends AbstractClientMsgHandler {
// Bypass for waterbuckets
if (item.template.item_type.equals(mbEnums.ItemType.BUCKET)) {
if (item.template.getItemType().equals(mbEnums.ItemType.BUCKET)) {
// test for valid target type
if (target.getObjectType() == mbEnums.GameObjectType.PlayerCharacter)
@@ -250,10 +251,9 @@ public class ObjectActionMsgHandler extends AbstractClientMsgHandler {
}
if (waterbucketBypass == false) {
String powerString = item.template.item_power_grant.keySet().iterator().next();
PowersBase powerAction = PowersManager.powersBaseByIDString.get(powerString);
int powerValue = item.template.item_power_grant.get(powerString);
PowersManager.applyPower(player, target, Vector3fImmutable.ZERO, powerAction.getToken(), powerValue, true);
engine.parsers.objects.PowerGrant powerGrant = item.template.getItemPowerGrant().get(0);
PowersBase powerAction = PowersManager.powersBaseByIDString.get(powerGrant.power_type);
PowersManager.applyPower(player, target, Vector3fImmutable.ZERO, powerAction.getToken(), powerGrant.power_value, true);
}
itemMan.consume(item);
} else //just remove the item at this point
@@ -266,7 +266,8 @@ public class ObjectActionMsgHandler extends AbstractClientMsgHandler {
case RUNE:
if(ApplyRuneMsg.applyRune(uuid, origin, player)) {
itemMan.consume(item);
}
} else
ErrorPopupMsg.sendErrorPopup(player, 161);
break;
default: //shouldn't be here, consume item
dispatch = Dispatch.borrow(player, msg);
@@ -18,6 +18,7 @@ import engine.net.client.msg.ErrorPopupMsg;
import engine.net.client.msg.PlaceAssetMsg;
import engine.net.client.msg.PlaceAssetMsg.PlacementInfo;
import engine.objects.*;
import engine.parsers.objects.DeedTemplate;
import engine.server.MBServerStatics;
import org.joda.time.DateTime;
import org.pmw.tinylog.Logger;
@@ -356,8 +357,8 @@ public class PlaceAssetMsgHandler extends AbstractClientMsgHandler {
Item contract = null;
for (Item inventoryItem : playerCharacter.getInventory()) {
if (inventoryItem.template.item_type.equals(ItemType.DEED) &&
inventoryItem.template.deed_structure_id == buildingList.getBlueprintUUID()) {
if (inventoryItem.template.getItemType().equals(ItemType.DEED) &&
DeedTemplate._deeds.get(inventoryItem.templateID).deed_structure_id == buildingList.getBlueprintUUID()) {
contract = inventoryItem;
break;
}
@@ -85,7 +85,7 @@ public class RepairMsgHandler extends AbstractClientMsgHandler {
//make sure item is damaged and not destroyed
short dur = (short) toRepair.combat_health_current;
short max = (short) toRepair.template.combat_health_full;
short max = (short) toRepair.template.getCombatHealthFull();
//account for durability modifications
@@ -68,7 +68,7 @@ public class TransferItemFromBankMsgHandler extends AbstractClientMsgHandler {
return true;
;
if (item.template.item_type.equals(mbEnums.ItemType.GOLD)) {
if (item.template.getItemType().equals(mbEnums.ItemType.GOLD)) {
if (!itemManager.moveGoldToInventory(item, msg.getNumItems()))
return true;
@@ -85,7 +85,7 @@ public class TransferItemFromBankMsgHandler extends AbstractClientMsgHandler {
// Not gold, process update here
if (!itemManager.hasRoomInventory(item.template.item_wt))
if (!itemManager.hasRoomInventory(item.template.getItemWt()))
return true;
if (itemManager.moveItemToInventory(item) == false)
@@ -65,7 +65,7 @@ public class TransferItemToBankMsgHandler extends AbstractClientMsgHandler {
return true;
if (item.containerType == mbEnums.ItemContainerType.INVENTORY && itemManager.isBankOpen())
if (item.template.item_type.equals(mbEnums.ItemType.GOLD)) {
if (item.template.getItemType().equals(mbEnums.ItemType.GOLD)) {
if (!itemManager.moveGoldToBank(item, msg.getNumItems()))
return true;
UpdateGoldMsg goldMes = new UpdateGoldMsg(player);
@@ -76,7 +76,7 @@ public class TransferItemToBankMsgHandler extends AbstractClientMsgHandler {
} else {
if (!itemManager.hasRoomBank(item.template.item_wt))
if (!itemManager.hasRoomBank(item.template.getItemWt()))
return true;
if (!itemManager.moveItemToBank(item))
@@ -86,7 +86,7 @@ public class VendorBuyMsgHandler extends AbstractClientMsgHandler {
if (me.objectUUID == vendorBuyMsg.getItemID()) {
//test room available for item
if (!itemMan.hasRoomInventory(me.template.item_wt))
if (!itemMan.hasRoomInventory(me.template.getItemWt()))
return true;
int cost = me.getValue();
@@ -148,10 +148,10 @@ public class VendorBuyMsgHandler extends AbstractClientMsgHandler {
//test room available for item
if (!itemMan.hasRoomInventory(vendorItem.template.item_wt))
if (!itemMan.hasRoomInventory(vendorItem.template.getItemWt()))
return true;
int cost = vendorItem.template.item_value;
int cost = vendorItem.template.getItemValue();
if (vendorItem.flags.contains(mbEnums.ItemFlags.Identified) || vendorItem.isCustomValue())
cost = vendorItem.magicValue;
@@ -94,7 +94,7 @@ public class VendorSellMsgHandler extends AbstractClientMsgHandler {
return true;
if (npc.getParentZone() != null && npc.getParentZone().playerCityUUID != 0)
if (!npc.charItemManager.hasRoomInventory(sell.template.item_wt)) {
if (!npc.charItemManager.hasRoomInventory(sell.template.getItemWt())) {
ErrorPopupMsg.sendErrorPopup(player, 21);
return true;
}
@@ -104,7 +104,7 @@ public class VendorSellMsgHandler extends AbstractClientMsgHandler {
//get goldItem cost to sell
cost = (int) (sell.template.item_value * (sell.combat_health_current / sell.template.combat_health_full));
cost = (int) (sell.template.getItemValue() * (sell.combat_health_current / sell.template.getCombatHealthFull()));
float bargain = player.getBargain();
float profit = npc.getBuyPercent(player) + bargain;
+73 -72
View File
@@ -22,10 +22,17 @@ import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class ApplyRuneMsg extends ClientNetMsg {
// Client ArcCharacter_AddRune (0x4695D0)
private static final int MAX_TALENTS = 8;
private int targetType;
private int targetID;
private int removeRuneBase;
@@ -78,67 +85,28 @@ public class ApplyRuneMsg extends ClientNetMsg {
return false;
}
//Check race is met
ConcurrentHashMap<Integer, Boolean> races = rb.getRace();
if (races.size() > 0) {
int raceID = playerCharacter.getRaceID();
boolean valid = false;
for (int validID : races.keySet()) {
if (validID == raceID) {
valid = true;
break;
}
}
if (!valid) {
return false;
}
}
//Check base class is met
ConcurrentHashMap<Integer, Boolean> baseClasses = rb.getBaseClass();
if (baseClasses.size() > 0) {
int baseClassID = playerCharacter.getBaseClassID();
boolean valid = false;
for (int validID : baseClasses.keySet()) {
if (validID == baseClassID) {
valid = true;
break;
}
}
if (!valid) {
return false;
}
}
//Check promotion class is met
ConcurrentHashMap<Integer, Boolean> promotionClasses = rb.getPromotionClass();
if (promotionClasses.size() > 0) {
int promotionClassID = playerCharacter.getPromotionClassID();
boolean valid = false;
for (int validID : promotionClasses.keySet()) {
if (validID == promotionClassID) {
valid = true;
break;
}
}
if (!valid) {
return false;
}
}
//Check disciplines are met
ArrayList<CharacterRune> runes = playerCharacter.getRunes();
ConcurrentHashMap<Integer, Boolean> disciplines = rb.getDiscipline();
if (disciplines.size() > 0) {
for (CharacterRune cr : runes) {
int runeBaseID = cr.getRuneBaseID();
for (Integer prohID : disciplines.keySet()) {
if (runeBaseID == prohID) {
return false; //Prohibited rune
}
}
}
}
HashSet<Integer> classIDs = new HashSet<>();
HashSet<Integer> heldDiscIDs = new HashSet<>();
// Client ArcCharacter_MeetsItemReqs (0x475F40)
if (!meetsListReq(rb.getRace().keySet(), rb.isRaceRestrict(), Collections.singleton(playerCharacter.getRaceID())))
return false;
// A listed base class covers its promotions: base or promotion listed is enough
classIDs.addAll(rb.getBaseClass().keySet());
classIDs.addAll(rb.getPromotionClass().keySet());
if (!meetsListReq(classIDs, rb.isClassRestrict(), Arrays.asList(playerCharacter.getBaseClassID(), playerCharacter.getPromotionClassID())))
return false;
// Only the new rune's list is checked, not the held discs' lists
for (CharacterRune cr : runes)
if (isDiscipline(cr.getRuneBaseID()))
heldDiscIDs.add(cr.getRuneBaseID());
if (!meetsListReq(rb.getDiscipline().keySet(), rb.isDiscRestrict(), heldDiscIDs))
return false;
int discCount = 0;
for (CharacterRune cr : runes) {
@@ -158,6 +126,32 @@ public class ApplyRuneMsg extends ClientNetMsg {
return false;
}
// Client 0x4768D0 refuses a talent unless its rank beats the held one with the same sub type (rank 0 included);
// AddRune (0x4695D0) then replaces the held talent
CharacterRune replacedRune = null;
if (rb.getRuneType() == mbEnums.RuneType.TALENT) {
int talentCount = 0;
for (CharacterRune cr : runes) {
RuneBase heldRuneBase = cr.getRuneBase();
if (heldRuneBase == null || heldRuneBase.getRuneType() != mbEnums.RuneType.TALENT)
continue;
talentCount++;
if (rb.getSubTypeToken() != 0 && heldRuneBase.getSubTypeToken() == rb.getSubTypeToken())
replacedRune = cr;
}
if (replacedRune != null) {
if (rb.getRank() <= replacedRune.getRuneBase().getRank())
return false;
} else if (talentCount >= MAX_TALENTS)
return false;
}
int strTotal = 0;
int dexTotal = 0;
int conTotal = 0;
@@ -227,11 +221,6 @@ public class ApplyRuneMsg extends ClientNetMsg {
}
}
//Check if max number runes already reached
if (runes.size() > 12) {
return false;
}
//if discipline, check number applied
if (isDiscipline(runeID)) {
if (playerCharacter.getLevel() < 70) {
@@ -259,13 +248,10 @@ public class ApplyRuneMsg extends ClientNetMsg {
return false;
}
//remove any overridden runes from player
ArrayList<Integer> overwrite = rb.getOverwrite();
//remove the replaced talent from player
CharacterRune toRemove = null;
if (overwrite.size() > 0) {
for (int overwriteID : overwrite) {
toRemove = playerCharacter.removeRune(overwriteID);
}
if (replacedRune != null) {
toRemove = playerCharacter.removeRune(replacedRune.getRuneBaseID());
}
//add rune to player
@@ -331,6 +317,21 @@ public class ApplyRuneMsg extends ClientNetMsg {
return runeID > 3000 && runeID < 3050;
}
// Client ArcItemRestrict_Check (0x4BAC60). An empty list has no requirement; no rune has an empty whitelist.
private static boolean meetsListReq(Set<Integer> listedIDs, boolean restrict, Collection<Integer> heldIDs) {
boolean listed = false;
if (listedIDs.isEmpty())
return true;
for (int heldID : heldIDs)
if (listedIDs.contains(heldID))
listed = true;
return listed != restrict;
}
/**
* Serializes the subclass specific items to the supplied NetMsgWriter.
*/
+9 -7
View File
@@ -19,6 +19,7 @@ import engine.net.ByteBufferReader;
import engine.net.ByteBufferWriter;
import engine.net.Protocol;
import engine.objects.*;
import engine.parsers.objects.CObjectTemplate;
import org.joda.time.DateTime;
import org.joda.time.Period;
import org.joda.time.Seconds;
@@ -420,17 +421,18 @@ public class ManageNPCMsg extends ClientNetMsg {
writer.putInt(rollableSet.size());
for (int templateID : rollableSet) {
ItemTemplate template = ItemTemplate.templates.get(templateID);
CObjectTemplate template = CObjectTemplate.lookup(templateID);
int modTable = ForgeManager.getVendorModTable(templateID);
writer.put((byte) 1);
writer.putInt(0);
writer.putInt(templateID); //itemID
writer.putInt(template.item_value);
writer.putInt(template.getItemValue());
writer.putInt(600);
writer.put((byte) 1);
writer.put((byte) template.modTable);
writer.put((byte) template.modTable);
writer.put((byte) template.modTable);
writer.put((byte) template.modTable);//EffectItemType
writer.put((byte) modTable);
writer.put((byte) modTable);
writer.put((byte) modTable);
writer.put((byte) modTable);//EffectItemType
}
ArrayList<Item> cooking = NPCManager.getAllCookingForVendor(npc);
@@ -444,7 +446,7 @@ public class ManageNPCMsg extends ClientNetMsg {
writer.putInt(0);
writer.putInt(item.templateID);
writer.putInt(item.template.item_value);
writer.putInt(item.template.getItemValue());
long timeLife = item.getDateToUpgrade() - System.currentTimeMillis();
@@ -54,7 +54,7 @@ public class UpdateInventoryMsg extends ClientNetMsg {
for (Item item : list) {
if (item.template.item_type.equals(ItemType.GOLD)) {
if (item.template.getItemType().equals(ItemType.GOLD)) {
if (item.getNumOfItems() == 0)
continue;
}
+6 -1
View File
@@ -16,6 +16,7 @@ import engine.net.ByteBufferWriter;
import engine.net.Protocol;
import engine.objects.Item;
import engine.objects.PlayerCharacter;
import engine.parsers.objects.DeedTemplate;
public class UseCharterMsg extends ClientNetMsg {
@@ -61,9 +62,13 @@ public class UseCharterMsg extends ClientNetMsg {
public void configure() {
DeedTemplate charterDeed;
if (close) {
for (Item i : player.getInventory()) {
if (i.template.item_type.equals(ItemType.CHARTER)) {
charterDeed = DeedTemplate._deeds.get(i.templateID);
if (charterDeed != null && charterDeed.item_type.equals(ItemType.CHARTER)) {
charterUUID = i.getObjectUUID();
break;
}
@@ -156,7 +156,7 @@ public class VendorBuyWindowMsg extends ClientNetMsg {
for (Item item : inventory) {
if (item.getOwnerID() != ownerID)
continue;
if (item.template.item_type.equals(ItemType.GOLD)) {
if (item.template.getItemType().equals(ItemType.GOLD)) {
if (item.getNumOfItems() == 0)
continue;
}
+41 -40
View File
@@ -29,6 +29,8 @@ import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.ApplyRuneMsg;
import engine.net.client.msg.UpdateStateMsg;
import engine.parsers.objects.CObjectTemplate;
import engine.parsers.objects.RuneTemplate;
import engine.powers.EffectsBase;
import engine.powers.PowersBase;
import engine.powers.effectmodifiers.AbstractEffectModifier;
@@ -652,11 +654,11 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
AssignDefenseValue(abstractCharacter);
// calculate atr and damage for each hand
ConcurrentHashMap<EquipSlotType, Item> equipped = abstractCharacter.charItemManager.getEquipped();
AssignDamageAtrForPlayers(abstractCharacter, equipped.get(EquipSlotType.RHELD), true, equipped.get(EquipSlotType.RHELD));
AssignDamageAtrForPlayers(abstractCharacter, equipped.get(EquipSlotType.LHELD), false, equipped.get(EquipSlotType.LHELD));
AssignDamageAtrForPlayers(abstractCharacter, equipped.get(EquipSlotType.RHELD), true, equipped.get(EquipSlotType.LHELD));
AssignDamageAtrForPlayers(abstractCharacter, equipped.get(EquipSlotType.LHELD), false, equipped.get(EquipSlotType.RHELD));
if (abstractCharacter.getObjectType().equals(GameObjectType.Mob)) {
Mob mob = (Mob) abstractCharacter;
ItemTemplate rune = ItemTemplate.templates.get(mob.loadID);
RuneTemplate rune = RuneTemplate._runes.get(mob.loadID);
float minDamage = mob.mobBase.getDamageMin();
float maxDamage = mob.mobBase.getDamageMax();
int attackRating = mob.mobBase.getAttackRating();
@@ -691,16 +693,17 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
// make sure weapon exists
boolean noWeapon = false;
ItemTemplate weaponTemplate = null;
CObjectTemplate weaponTemplate = null;
engine.parsers.objects.ItemTemplate.Damage damage;
if (weapon == null)
noWeapon = true;
else {
ItemTemplate itemTemplate = weapon.template;
CObjectTemplate itemTemplate = weapon.template;
if (itemTemplate == null)
noWeapon = true;
else if (!weapon.template.item_type.equals(ItemType.WEAPON)) {
else if (!weapon.template.getItemType().equals(ItemType.WEAPON)) {
defaultAtrAndDamage(abstractCharacter, mainHand);
return;
} else
@@ -717,7 +720,7 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
if (noWeapon) {
if (mainHand) {
Item off = abstractCharacter.charItemManager.getEquipped().get(EquipSlotType.LHELD);
if (off != null && off.template != null && off.template.item_type.equals(ItemType.WEAPON))
if (off != null && off.template != null && off.template.getItemType().equals(ItemType.WEAPON))
abstractCharacter.rangeHandOne = 10 * (1 + (abstractCharacter.statStrBase / 600)); // Set
// to
// no
@@ -750,9 +753,9 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
} else {
if (mainHand)
abstractCharacter.rangeHandOne = weapon.template.item_weapon_max_range * (1 + (abstractCharacter.statStrBase / 600));
abstractCharacter.rangeHandOne = engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_max_range * (1 + (abstractCharacter.statStrBase / 600));
else
abstractCharacter.rangeHandTwo = weapon.template.item_weapon_max_range * (1 + (abstractCharacter.statStrBase / 600));
abstractCharacter.rangeHandTwo = engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_max_range * (1 + (abstractCharacter.statStrBase / 600));
if (abstractCharacter.bonuses != null) {
float range_bonus = 1 + abstractCharacter.bonuses.getFloatPercentAll(ModType.WeaponRange, SourceType.None);
@@ -764,20 +767,20 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
}
skillPercentage = getModifiedAmount(abstractCharacter.skills.get(weapon.template.item_skill_used));
masteryPercentage = getModifiedAmount(abstractCharacter.skills.get(weaponTemplate.item_skill_mastery_used));
skillPercentage = getModifiedAmount(abstractCharacter.skills.get(weapon.template.getItemSkillUsed()));
masteryPercentage = getModifiedAmount(abstractCharacter.skills.get(weaponTemplate.getItemSkillMasteryUsed()));
if (masteryPercentage == 0f)
mastDam = 0f;
// mastDam = CharacterSkill.getQuickMastery(this, wb.getMastery());
else
mastDam = masteryPercentage;
int[] damages = (int[]) weaponTemplate.item_weapon_damage.values().toArray()[0];
damage = engine.parsers.objects.ItemTemplate.getWeapon(weapon.templateID).weapon_damage.get(0);
min = damages[0];
max = damages[1];
min = damage.damage_min;
max = damage.damage_max;
strBased = weaponTemplate.item_primary_attr.equals(AttributeType.Strength);
strBased = engine.parsers.objects.ItemTemplate._items.get(weapon.templateID).item_primary_attr == AttributeType.Strength;
}
if (abstractCharacter.effects != null && abstractCharacter.effects.containsKey("DeathShroud"))
@@ -826,7 +829,7 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
//calculate speed
if (weaponTemplate != null)
speed = weaponTemplate.item_weapon_wepspeed;
speed = engine.parsers.objects.ItemTemplate.getWeapon(weaponTemplate.getTemplateId()).weapon_wepspeed;
else
speed = 20f; //unarmed attack speed
if (weapon != null)
@@ -881,15 +884,12 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
max *= percentMaxDamage;
}
//if duel wielding, cut damage by 30%
// Client (0x466EB0 sets char+0xD0C, 0x46D200 applies it): 30% cut only when both hands hold a WEAPON.
if (otherHand != null) {
ItemTemplate ibo = otherHand.template;
if (ibo != null && otherHand.template.equals(ItemType.WEAPON)) {
min *= 0.7f;
max *= 0.7f;
}
if (!noWeapon && otherHand != null && otherHand.template != null
&& otherHand.template.getItemType().equals(ItemType.WEAPON)) {
min *= 0.7f;
max *= 0.7f;
}
// calculate damage
@@ -955,7 +955,7 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
if (ItemManager.isShield(shield) == false)
return 0;
ItemTemplate template = shield.template;
CObjectTemplate template = shield.template;
if (template == null)
return 0;
@@ -969,7 +969,7 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
} else
skillMod = blockSkill.getModifiedAmount();
float def = template.item_defense_rating;
float def = engine.parsers.objects.ItemTemplate._items.get(shield.templateID).item_defense_rating;
//apply item defense bonuses
@@ -991,29 +991,30 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
if (armor == null)
return 0;
ItemTemplate template = armor.template;
CObjectTemplate template = armor.template;
if (template == null)
return 0;
if (!armor.template.item_type.equals(ItemType.ARMOR))
if (!armor.template.getItemType().equals(ItemType.ARMOR))
return 0;
if (armor.template.item_skill_used.isEmpty())
return template.item_defense_rating;
// "Cloth" is a placeholder in Skills.cfg (token 0) that no character learns
if (armor.template.getItemSkillUsed().isEmpty() || armor.template.getItemSkillUsed().equals("Cloth"))
return engine.parsers.objects.ItemTemplate._items.get(armor.templateID).item_defense_rating;
CharacterSkill armorSkill = abstractCharacter.skills.get(armor.template.item_skill_used);
CharacterSkill armorSkill = abstractCharacter.skills.get(armor.template.getItemSkillUsed());
if (abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) && armorSkill == null) {
Logger.error("Player " + abstractCharacter.getObjectUUID()
+ " has armor equipped without the nescessary skill to equip it");
return template.item_defense_rating;
return engine.parsers.objects.ItemTemplate._items.get(armor.templateID).item_defense_rating;
}
if (armorSkill == null)
return template.item_defense_rating; // Mobiles do not have armor skills @TODO
return engine.parsers.objects.ItemTemplate._items.get(armor.templateID).item_defense_rating; // Mobiles do not have armor skills @TODO
float def = template.item_defense_rating;
float def = engine.parsers.objects.ItemTemplate._items.get(armor.templateID).item_defense_rating;
//apply item defense bonuses
@@ -1031,7 +1032,7 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
private static float getWeaponDefense(AbstractCharacter abstractCharacter, ConcurrentHashMap<EquipSlotType, Item> equipped) {
Item weapon = equipped.get(EquipSlotType.RHELD);
ItemTemplate weaponTemplate = null;
CObjectTemplate weaponTemplate = null;
CharacterSkill skill, mastery;
float val = 0;
boolean unarmed = false;
@@ -1052,8 +1053,8 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
skill = abstractCharacter.skills.get("Unarmed Combat");
mastery = abstractCharacter.skills.get("Unarmed Combat Mastery");
} else {
skill = abstractCharacter.skills.get(weapon.template.item_skill_used);
mastery = abstractCharacter.skills.get(weaponTemplate.item_skill_mastery_used);
skill = abstractCharacter.skills.get(weapon.template.getItemSkillUsed());
mastery = abstractCharacter.skills.get(weaponTemplate.getItemSkillMasteryUsed());
}
if (skill != null)
@@ -1088,7 +1089,7 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
for (Item item : equipped.values()) {
ItemTemplate ib = item.template;
CObjectTemplate ib = item.template;
if (ib == null)
continue;
@@ -1507,9 +1508,9 @@ public abstract class AbstractCharacter extends AbstractWorldObject {
}
float range = 8;
if ((this).charItemManager.equipped.get(EquipSlotType.RHELD) != null) {
range = ((Mob) this).charItemManager.equipped.get(EquipSlotType.RHELD).template.item_weapon_max_range;
range = engine.parsers.objects.ItemTemplate.getWeapon(((Mob) this).charItemManager.equipped.get(EquipSlotType.RHELD).templateID).weapon_max_range;
} else if (((Mob) this).charItemManager.equipped.get(EquipSlotType.LHELD) != null) {
range = ((Mob) this).charItemManager.equipped.get(EquipSlotType.LHELD).template.item_weapon_max_range;
range = engine.parsers.objects.ItemTemplate.getWeapon(((Mob) this).charItemManager.equipped.get(EquipSlotType.LHELD).templateID).weapon_max_range;
} else {
// Unarmed: use the rune natural attack range when it has one (e.g. Gasser 150), not clamped.
+3 -3
View File
@@ -172,7 +172,7 @@ public class Account extends AbstractGameObject {
this.vault = DbManager.ItemQueries.GET_ITEMS_FOR_ACCOUNT(this.getObjectUUID());
for (Item item : this.vault) {
if (item.template.item_type.equals(mbEnums.ItemType.GOLD)) {
if (item.template.getItemType().equals(mbEnums.ItemType.GOLD)) {
this.vaultGold = item;
}
}
@@ -216,7 +216,7 @@ public class Account extends AbstractGameObject {
return;
if (item.containerType == mbEnums.ItemContainerType.INVENTORY && player.charItemManager.isVaultOpen()) {
if (!player.charItemManager.hasRoomVault(item.template.item_wt)) {
if (!player.charItemManager.hasRoomVault(item.template.getItemWt())) {
ItemToVaultMsgHandler.forceTransferFromVaultToInventory(msg, origin, "There is no room in your vault.");
return;
}
@@ -262,7 +262,7 @@ public class Account extends AbstractGameObject {
return;
if (item.containerType == mbEnums.ItemContainerType.VAULT && itemManager.isVaultOpen()) {
if (!itemManager.hasRoomInventory(item.template.item_wt)) {
if (!itemManager.hasRoomInventory(item.template.getItemWt())) {
ItemFromVaultMsgHandler.forceTransferFromInventoryToVault(msg, origin, "There is no room in your inventory.");
return;
}
+39 -1
View File
@@ -10,7 +10,11 @@
package engine.objects;
import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.net.ByteBufferWriter;
import engine.parsers.objects.RuneTemplate;
import engine.util.Hasher;
import org.pmw.tinylog.Logger;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -104,8 +108,42 @@ public class BaseClass extends AbstractGameObject {
}
/**
* Rune Template Constructor
*/
public BaseClass(RuneTemplate rune) {
super(rune.template_id);
this.name = rune.obj_name;
this.description = rune.rune_dsc;
this.strMod = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Strength, 0).byteValue();
this.dexMod = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Dexterity, 0).byteValue();
this.conMod = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Constitution, 0).byteValue();
this.intMod = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Intelligence, 0).byteValue();
this.spiMod = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Spirit, 0).byteValue();
this.token = Hasher.SBStringHash(rune.rune_sub_type);
this.healthMod = rune.rune_health;
this.manaMod = rune.rune_mana;
this.staminaMod = rune.rune_stamina;
this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.getObjectUUID());
this.powersGranted = PowerReq.getPowerReqsForRune(this.getObjectUUID());
this.effectsList = (DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.getObjectUUID()));
}
public static void LoadAllBaseClasses() {
DbManager.BaseClassQueries.GET_ALL_BASE_CLASSES();
int loadedCount = 0;
// Promotion classes and the Pet class are also CLASS runes, but not standard creation runes
for (RuneTemplate rune : RuneTemplate._runes.values()) {
if (rune.rune_type != mbEnums.RuneType.CLASS || !rune.rune_is_standard_character_creation)
continue;
DbManager.addToCache(new BaseClass(rune));
loadedCount++;
}
Logger.info("Loaded " + loadedCount + " base classes from runes");
}
public static void serializeForClientMsg(BaseClass baseClass, ByteBufferWriter writer) {
+34 -33
View File
@@ -17,6 +17,7 @@ import engine.mbEnums.ItemType;
import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.*;
import engine.parsers.objects.CObjectTemplate;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
@@ -130,19 +131,19 @@ public class CharacterItemManager {
this.equipped.put(i.equipSlot, i);
break;
case BANK:
if (i.template.item_type.equals(ItemType.GOLD))
if (i.template.getItemType().equals(ItemType.GOLD))
this.goldBank = i;
else if (this.bank.contains(i) == false)
this.bank.add(i);
break;
case INVENTORY:
if (i.template.item_type.equals(ItemType.GOLD))
if (i.template.getItemType().equals(ItemType.GOLD))
this.goldInventory = i;
else if (this.inventory.contains(i) == false)
this.inventory.add(i);
break;
case VAULT:
if (i.template.item_type.equals(ItemType.GOLD))
if (i.template.getItemType().equals(ItemType.GOLD))
this.goldVault = i;
else if (this.vault.contains(i) == false)
this.vault.add(i);
@@ -175,13 +176,13 @@ public class CharacterItemManager {
this.equipped.put(i.equipSlot, i);
break;
case BANK:
if (i.template.item_type.equals(ItemType.GOLD))
if (i.template.getItemType().equals(ItemType.GOLD))
this.goldBank = i;
else if (this.bank.contains(i) == false)
this.bank.add(i);
break;
case INVENTORY:
if (i.template.item_type.equals(ItemType.GOLD))
if (i.template.getItemType().equals(ItemType.GOLD))
this.goldInventory = i;
else if (this.inventory.contains(i) == false)
this.inventory.add(i);
@@ -511,7 +512,7 @@ public class CharacterItemManager {
if (i == null)
return false;
if (i.template.item_type.equals(ItemType.GOLD)) {
if (i.template.getItemType().equals(ItemType.GOLD)) {
if (this.getGoldInventory() != null) {
if (i.getObjectUUID() == this.getGoldInventory().getObjectUUID())
this.goldInventory = null;
@@ -541,7 +542,7 @@ public class CharacterItemManager {
}
private synchronized boolean junk(Item i, boolean updateInventory) {
if (i.template.item_type.equals(ItemType.GOLD)) {
if (i.template.getItemType().equals(ItemType.GOLD)) {
if (this.getGoldInventory().getObjectUUID() == i.getObjectUUID())
if (DbManager.ItemQueries.UPDATE_GOLD(i, 0)) {
this.getGoldInventory().setNumOfItems(0);
@@ -596,7 +597,7 @@ public class CharacterItemManager {
if (equippedContains(item)) {
fromEquip = true;
if (item.template.item_type.equals(ItemType.GOLD))
if (item.template.getItemType().equals(ItemType.GOLD))
this.absCharacter.cancelOnUnEquip();
}
@@ -814,7 +815,7 @@ public class CharacterItemManager {
// This removes ingame item from inventory for loot.
private synchronized boolean removeItemFromInventory(Item i) {
if (i.template.item_type.equals(ItemType.GOLD)) {
if (i.template.getItemType().equals(ItemType.GOLD)) {
if (i.getObjectUUID() != this.getGoldInventory().getObjectUUID())
return false;
if (!DbManager.ItemQueries.UPDATE_GOLD(this.goldInventory, 0)) {
@@ -837,7 +838,7 @@ public class CharacterItemManager {
// This adds item to inventory for loot. Validity checks already handled
public synchronized boolean addItemToInventory(Item i) {
if (i.template.item_type.equals(ItemType.GOLD))
if (i.template.getItemType().equals(ItemType.GOLD))
if (this.absCharacter.getObjectType() == GameObjectType.Mob) {
if (this.goldInventory == null)
loadGoldItems();
@@ -856,7 +857,7 @@ public class CharacterItemManager {
this.itemIDtoType.put(i.getObjectUUID(), i.getObjectType().ordinal());
if (i.template != null)
this.inventoryWeight += i.template.item_wt;
this.inventoryWeight += i.template.getItemWt();
return true;
}
@@ -1041,7 +1042,7 @@ public class CharacterItemManager {
if (npc.getContractID() >= 1900 && npc.getContractID() <= 1906)
eatItem = true;
if (itemToSell.template.item_type.equals(ItemType.POTION))
if (itemToSell.template.getItemType().equals(ItemType.POTION))
eatItem = true;
if (eatItem) {
@@ -1085,14 +1086,14 @@ public class CharacterItemManager {
synchronized (this) {
synchronized (itemMan) {
ItemTemplate template = purchasedItem.template;
CObjectTemplate template = purchasedItem.template;
if (template == null)
return false;
//test inventory is not full
if (!hasRoomInventory(template.item_wt))
if (!hasRoomInventory(template.getItemWt()))
return false;
if (!itemMan.inventory.contains(purchasedItem))
@@ -1105,8 +1106,8 @@ public class CharacterItemManager {
// Reset value
purchasedItem.value = (int) (purchasedItem.template.item_value * (purchasedItem.combat_health_current / purchasedItem.template.combat_health_full));
purchasedItem.magicValue = purchasedItem.template.item_value + purchasedItem.calcMagicValue();
purchasedItem.value = (int) (purchasedItem.template.getItemValue() * (purchasedItem.combat_health_current / purchasedItem.template.getCombatHealthFull()));
purchasedItem.magicValue = purchasedItem.template.getItemValue() + purchasedItem.calcMagicValue();
// db transfer successfully, remove from this character
// skip this check if this is a mobLoot item (which is not in any inventory)
@@ -1184,9 +1185,9 @@ public class CharacterItemManager {
synchronized (lockFirst) {
synchronized (lockSecond) {
// make sure current player has item in inventory
if (lootItem.template.item_type.equals(ItemType.GOLD) && lootItem.getObjectUUID() != this.getGoldInventory().getObjectUUID() && !(this.absCharacter.getObjectType().equals(GameObjectType.Mob)))
if (lootItem.template.getItemType().equals(ItemType.GOLD) && lootItem.getObjectUUID() != this.getGoldInventory().getObjectUUID() && !(this.absCharacter.getObjectType().equals(GameObjectType.Mob)))
return null;
else if (!this.inventory.contains(lootItem) && !this.equipped.containsValue(lootItem) && !lootItem.template.item_type.equals(ItemType.GOLD))
else if (!this.inventory.contains(lootItem) && !this.equipped.containsValue(lootItem) && !lootItem.template.getItemType().equals(ItemType.GOLD))
return null;
// get weight of item
@@ -1194,13 +1195,13 @@ public class CharacterItemManager {
if (lootItem.template == null)
return null;
int weight = lootItem.template.item_wt;
int weight = lootItem.template.getItemWt();
// make sure lootingPlayer has room for item
if (!lootItem.template.item_type.equals(ItemType.GOLD) && !looterItems.hasRoomInventory(weight))
if (!lootItem.template.getItemType().equals(ItemType.GOLD) && !looterItems.hasRoomInventory(weight))
return null;
if (lootItem.template.item_type.equals(ItemType.GOLD))
if (lootItem.template.getItemType().equals(ItemType.GOLD))
if (amount != -1) { //from steal
int total = lootItem.getNumOfItems();
amount = (amount > total) ? total : amount;
@@ -1263,19 +1264,19 @@ public class CharacterItemManager {
* Delegates
*/
public synchronized boolean bankContains(Item item) {
if (item.template.item_type.equals(ItemType.GOLD))
if (item.template.getItemType().equals(ItemType.GOLD))
return (this.getGoldBank() != null && this.goldBank.getObjectUUID() == item.getObjectUUID());
return bank.contains(item);
}
public synchronized boolean inventoryContains(Item item) {
if (item.template.item_type.equals(ItemType.GOLD))
if (item.template.getItemType().equals(ItemType.GOLD))
return (this.getGoldInventory() != null && this.goldInventory.getObjectUUID() == item.getObjectUUID());
return inventory.contains(item);
}
public synchronized boolean vaultContains(Item item) {
if (item.template.item_type.equals(ItemType.GOLD))
if (item.template.getItemType().equals(ItemType.GOLD))
return (this.getGoldVault() != null && this.goldVault.getObjectUUID() == item.getObjectUUID());
return this.account.getVault().contains(item);
}
@@ -1518,13 +1519,13 @@ public class CharacterItemManager {
item.zeroItem();
item.containerType = mbEnums.ItemContainerType.INVENTORY;
if (item.template.item_type.equals(ItemType.GOLD))
if (item.template.getItemType().equals(ItemType.GOLD))
//only add gold item once
if (!corpse.hasGold())
corpse.setHasGold(true);
newInventory.add(item);
} else //item
if (item.template.item_type.equals(ItemType.GOLD)) {
if (item.template.getItemType().equals(ItemType.GOLD)) {
int amt = item.getNumOfItems();
item.setNumOfItems(0);
MobLoot ml = new MobLoot(this.absCharacter, amt);
@@ -1676,7 +1677,7 @@ public class CharacterItemManager {
if (item == null)
continue;
weight += item.template.item_wt;
weight += item.template.getItemWt();
}
return weight;
}
@@ -1696,7 +1697,7 @@ public class CharacterItemManager {
this.bankWeight = 0;
for (Item item : this.bank) {
if (item.template != null)
this.bankWeight += item.template.item_wt;
this.bankWeight += item.template.getItemWt();
}
}
@@ -1707,7 +1708,7 @@ public class CharacterItemManager {
while (it.hasNext()) {
Item item = it.next();
if (item.template != null)
this.equipWeight += item.template.item_wt;
this.equipWeight += item.template.getItemWt();
}
}
@@ -1715,7 +1716,7 @@ public class CharacterItemManager {
this.inventoryWeight = 0;
for (Item item : this.inventory) {
if (item.template != null)
this.inventoryWeight += item.template.item_wt;
this.inventoryWeight += item.template.getItemWt();
}
}
@@ -1723,7 +1724,7 @@ public class CharacterItemManager {
this.vaultWeight = 0;
for (Item item : this.vault) {
if (item.template != null)
this.vaultWeight += item.template.item_wt;
this.vaultWeight += item.template.getItemWt();
}
}
@@ -1755,7 +1756,7 @@ public class CharacterItemManager {
return;
if (!item.moveItemToInventory(player))
Logger.error("templateL " + item.template.template_id);
Logger.error("templateL " + item.template.getTemplateId());
// remove it from other lists:
this.remItemFromLists(item);
@@ -1932,7 +1933,7 @@ public class CharacterItemManager {
return;
//don't damage noob gear, hair or beards.
if (item.template.combat_health_full == 0)
if (item.template.getCombatHealthFull() == 0)
return;
if (!item.isCanDestroy())
+3 -3
View File
@@ -296,14 +296,14 @@ public class Corpse extends AbstractWorldObject {
//get weight of item
int weight = item.template.item_wt;
int weight = item.template.getItemWt();
//make sure looter has room for item
if (item.template.item_type.equals(ItemType.GOLD) == false && !looterItems.hasRoomInventory(weight))
if (item.template.getItemType().equals(ItemType.GOLD) == false && !looterItems.hasRoomInventory(weight))
return null;
//attempt to transfer item in db
if (item.template.item_type.equals(ItemType.GOLD)) {
if (item.template.getItemType().equals(ItemType.GOLD)) {
if (!looterItems.moveGoldToInventory(item, item.getNumOfItems()))
return null;
} else if (!item.moveItemToInventory(looter))
+30 -23
View File
@@ -20,6 +20,8 @@ import engine.net.ByteBufferWriter;
import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.DeleteItemMsg;
import engine.parsers.objects.CObjectTemplate;
import engine.parsers.objects.UserPowerAction;
import engine.powers.EffectsBase;
import engine.powers.effectmodifiers.AbstractEffectModifier;
import engine.powers.poweractions.AbstractPowerAction;
@@ -31,6 +33,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
@@ -55,7 +58,7 @@ public class Item extends AbstractWorldObject {
public int templateID;
private long dateToUpgrade;
public int magicValue;
public ItemTemplate template;
public CObjectTemplate template;
public String name = "";
public int prefixToken; // Forge support
public int suffixToken; // Forge support
@@ -68,14 +71,14 @@ public class Item extends AbstractWorldObject {
public Item(int templateID) {
super();
this.templateID = templateID;
this.template = ItemTemplate.templates.get(templateID);
this.chargesRemaining = this.template.item_initial_charges;
this.combat_health_current = this.template.combat_health_full;
this.template = CObjectTemplate.lookup(templateID);
this.chargesRemaining = this.template.getItemInitialCharges();
this.combat_health_current = this.template.getCombatHealthFull();
this.equipSlot = EquipSlotType.NONE;
this.containerType = ItemContainerType.NONE;
this.numberOfItems = 1;
this.flags.addAll(this.template.item_flags);
this.value = this.template.item_value;
this.flags.addAll(this.template.getItemFlags());
this.value = this.template.getItemValue();
this.magicValue = this.value + calcMagicValue();
loadTemplateEnchantments();
}
@@ -87,7 +90,7 @@ public class Item extends AbstractWorldObject {
super(rs);
this.templateID = rs.getInt("templateID");
this.template = ItemTemplate.templates.get(this.templateID);
this.template = CObjectTemplate.lookup(this.templateID);
if (this.template == null)
Logger.error("Null template of " + this.templateID);
@@ -166,7 +169,7 @@ public class Item extends AbstractWorldObject {
this.value = rs.getInt("value");
if (this.value == 0) {
this.value = this.template.item_value;
this.value = this.template.getItemValue();
this.magicValue = this.value + calcMagicValue();
} else
this.magicValue = this.value; // Value was overridden in vendor inventory
@@ -238,7 +241,7 @@ public class Item extends AbstractWorldObject {
writer.putString(item.name); // Unknown. pad?
writer.put((byte) 1); // End Datablock byte
writer.putFloat(item.template.combat_health_full);
writer.putFloat(item.template.getCombatHealthFull());
writer.putFloat((float) item.combat_health_current);
writer.put((byte) 1); // End Datablock byte
@@ -255,7 +258,7 @@ public class Item extends AbstractWorldObject {
} else
writer.putInt(item.numberOfItems); // Amount of gold
} else
writer.putInt(item.template.item_value);
writer.putInt(item.template.getItemValue());
writer.putInt(item.getValue());
@@ -288,7 +291,7 @@ public class Item extends AbstractWorldObject {
writer.putInt(36); //Magical, blue name
else
writer.putInt(40); //Magical, unidentified
else if (item.template.item_user_power_action.size() > 0)
else if (item.template.getItemUserPowerAction().isEmpty() == false)
writer.putInt(36); //Magical, blue name
else
writer.putInt(4); //Non-Magical, grey name
@@ -298,7 +301,7 @@ public class Item extends AbstractWorldObject {
writer.put((byte) 0);
if (item.template.item_type.equals(ItemType.EMPLOYMENTCONTRACT) == false) {
if (item.template.getItemType().equals(ItemType.EMPLOYMENTCONTRACT) == false) {
writer.putShort((short) 0);
return;
}
@@ -463,7 +466,7 @@ public class Item extends AbstractWorldObject {
int serialized = 0;
for (Item item : list) {
if (item.template.item_type.equals(ItemType.GOLD))
if (item.template.getItemType().equals(ItemType.GOLD))
if (item.numberOfItems == 0)
continue;
try {
@@ -497,7 +500,7 @@ public class Item extends AbstractWorldObject {
int serialized = 0;
for (Item item : list) {
if (item.template.item_type.equals(ItemType.GOLD))
if (item.template.getItemType().equals(ItemType.GOLD))
if (item.numberOfItems == 0)
continue;
try {
@@ -931,12 +934,16 @@ public class Item extends AbstractWorldObject {
private void loadTemplateEnchantments() {
EffectsBase effect;
LinkedHashMap<String, int[]> templateEnchantments = new LinkedHashMap<>();
if (ConfigManager.serverType.equals(mbEnums.ServerType.LOGINSERVER))
return;
// Duplicate powers collapse to the last entry; applying one twice would double its magic value.
for (UserPowerAction userPowerAction : this.template.getItemUserPowerAction())
templateEnchantments.put(userPowerAction.power, userPowerAction.arguments);
for (String effectID : this.template.item_user_power_action.keySet()) {
for (String effectID : templateEnchantments.keySet()) {
effect = PowersManager.getEffectByIDString(effectID);
@@ -946,7 +953,7 @@ public class Item extends AbstractWorldObject {
}
AbstractPowerAction apa = PowersManager.getPowerActionByIDString(effect.getIDString());
apa.applyBakedInStatsForItem(this, this.template.item_user_power_action.get(effectID)[0]);
apa.applyBakedInStatsForItem(this, templateEnchantments.get(effectID)[0]);
}
}
@@ -955,7 +962,7 @@ public class Item extends AbstractWorldObject {
// No enchantments for negative id items in the db. Early exit.
if (this.objectUUID < 0) {
this.magicValue = this.template.item_value + calcMagicValue();
this.magicValue = this.template.getItemValue() + calcMagicValue();
return;
}
@@ -971,8 +978,8 @@ public class Item extends AbstractWorldObject {
// Value is overridden the db
if (this.value == this.template.item_value)
this.magicValue = this.template.item_value + calcMagicValue();
if (this.value == this.template.getItemValue())
this.magicValue = this.template.getItemValue() + calcMagicValue();
}
public void addPermanentEnchantment(String enchantID, int rank) {
@@ -1116,11 +1123,11 @@ public class Item extends AbstractWorldObject {
else
modifiedValue = this.value;
if (this.template.item_initial_charges > 0)
modifiedValue = modifiedValue * (this.chargesRemaining / this.template.item_initial_charges);
if (this.template.getItemInitialCharges() > 0)
modifiedValue = modifiedValue * (this.chargesRemaining / this.template.getItemInitialCharges());
if (this.template.combat_health_full > 0)
modifiedValue = (int) (modifiedValue * (this.combat_health_current / this.template.combat_health_full));
if (this.template.getCombatHealthFull() > 0)
modifiedValue = (int) (modifiedValue * (this.combat_health_current / this.template.getCombatHealthFull()));
return modifiedValue;
}
-649
View File
@@ -1,649 +0,0 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.objects;
import engine.math.Vector3fImmutable;
import engine.mbEnums;
import org.json.JSONArray;
import org.json.JSONObject;
import org.pmw.tinylog.Logger;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
public class ItemTemplate {
// MB Dev Notes:
// This class parses JSON data generated by mbEditor Pro 2.1.
// Cache definitions are loaded for ItemType.ITEM, ItemType,DEED
// and ItemType.RUNE.
//
// Exported JSON data is stored in db table static_item_templates
// and can be easily mass updated using tooling available on the Wiki.
//
// Always read never write!
// Use copies of fields to avoid side effects.
// Global template lookup
public static HashMap<Integer, ItemTemplate> templates = new HashMap<>();
public int template_id;
// Template Properties
public String obj_name;
public boolean obj_pickable;
public Vector3fImmutable obj_scale;
public Vector3fImmutable obj_forward_vector;
public Vector3fImmutable obj_default_alignment;
public int obj_render_object;
public int obj_icon;
public final HashMap<String, String> obj_sparse_data = new HashMap<>();
public float combat_health_current;
public float combat_health_full;
public final HashMap<String, Float> combat_attack_resist = new HashMap<>();
public mbEnums.ItemType item_type;
public int item_eq_slots_value;
public boolean item_eq_slots_type;
public final EnumSet<mbEnums.EquipSlotType> item_eq_slots_or = EnumSet.noneOf(mbEnums.EquipSlotType.class);
public final EnumSet<mbEnums.EquipSlotType> item_eq_slots_and = EnumSet.noneOf(mbEnums.EquipSlotType.class);
public boolean item_takeable;
public int item_value;
public int item_wt;
public float item_passive_defense_mod;
public String item_base_name;
public String item_dsc;
public int item_render_object_female;
public float item_health_full;
public String item_skill_used = "";
public String item_skill_mastery_used = "";
public int item_parry_anim_id;
public float item_bulk_factor;
public final HashMap<mbEnums.ShrineType, Integer> item_offering_info = new HashMap<>();
public int item_defense_rating;
public float item_weapon_wepspeed;
public float item_weapon_max_range;
public int item_weapon_projectile_id;
public float item_weapon_projectile_speed;
public int item_weapon_combat_idle_anim;
public final HashMap<mbEnums.DamageType, int[]> item_weapon_damage = new HashMap<>();
public final ArrayList<int[]> weapon_attack_anim_right = new ArrayList<>();
public final ArrayList<int[]> weapon_attack_anim_left = new ArrayList<>();
public mbEnums.AttributeType item_primary_attr = mbEnums.AttributeType.None;
public mbEnums.AttributeType item_secondary_attr = mbEnums.AttributeType.None;
public final EnumSet<mbEnums.ItemFlags> item_flags = EnumSet.noneOf(mbEnums.ItemFlags.class);
public final EnumSet<mbEnums.ItemUseFlags> item_use_flags = EnumSet.noneOf(mbEnums.ItemUseFlags.class);
public int item_initial_charges;
public final HashMap<String, Integer> item_skill_required = new HashMap<>();
public final EnumSet<mbEnums.MonsterType> item_race_req = EnumSet.noneOf(mbEnums.MonsterType.class);
public final EnumSet<mbEnums.MonsterType> item_race_res = EnumSet.noneOf(mbEnums.MonsterType.class);
public final EnumSet<mbEnums.ClassType> item_class_req = EnumSet.noneOf(mbEnums.ClassType.class);
public final EnumSet<mbEnums.ClassType> item_class_res = EnumSet.noneOf(mbEnums.ClassType.class);
public final EnumSet<mbEnums.DisciplineType> item_disc_req = EnumSet.noneOf(mbEnums.DisciplineType.class);
public final EnumSet<mbEnums.DisciplineType> item_disc_res = EnumSet.noneOf(mbEnums.DisciplineType.class);
public int item_level_req;
public mbEnums.SexType item_sex_req;
// Lossy: some runes list the same power several times with different
// arguments (a step table, e.g. PET_DISC). Only the last entry is kept.
// engine.parsers.objects.RuneTemplate keeps the full list.
public final HashMap<String, int[]> item_user_power_action = new HashMap<>();
public final HashMap<String, Integer> item_power_grant = new HashMap<>();
// Lossy: some runes carry up to 35 power_actions entries per power
// (a step table, e.g. HNT-031). Only the first entry is kept.
// engine.parsers.objects.RuneTemplate keeps the full list.
public final HashMap<String, int[]> item_power_action = new HashMap<>();
public final HashMap<mbEnums.ResourceType, Integer> item_resource_cost = new HashMap<>();
public int modTable;
public int deed_type;
public int deed_furniture_id;
public int deed_target_id;
public int deed_employment;
public int deed_start_rank;
public int deed_name_lookup;
public boolean deed_indoors;
public boolean deed_is_fortress;
public float deed_namelookup_val;
public boolean deed_custom_city;
public int deed_structure_id;
public final HashMap<mbEnums.AttributeType, Integer> rune_attr_adj = new HashMap<>();
public final HashMap<mbEnums.AttributeType, Integer> rune_max_attr_adj = new HashMap<>();
public final HashMap<String, Integer> rune_skill_grant = new HashMap<>();
public final HashMap<String, Integer> skill_granted_skills = new HashMap<>();
public final HashMap<String, Integer> power_granted_skills = new HashMap<>();
public String rune_type;
public String rune_sub_type;
public boolean rune_is_standard_character_creation = false;
public int rune_creation_cost;
public int rune_rank;
public int rune_pracs_per_level;
public float rune_exp_req_to_level;
public mbEnums.SexType rune_sex;
public int rune_class_icon;
public int rune_health;
public int rune_mana;
public int rune_stamina;
public float rune_min_damage;
public float rune_max_damage;
public int rune_attack;
public int rune_defense;
public int rune_level;
public final HashMap<mbEnums.MovementType, Float> rune_speed = new HashMap<>();
public int rune_group_type;
public boolean rune_group_is_faction = false;
public boolean rune_group_is_guild = false;
public String rune_dsc;
public String rune_fx_txt;
public final EnumSet<mbEnums.MobBehaviourType> rune_group_tactics = EnumSet.noneOf(mbEnums.MobBehaviourType.class);
public final EnumSet<mbEnums.MobBehaviourType> rune_group_role_set = EnumSet.noneOf(mbEnums.MobBehaviourType.class);
public boolean rune_renderable = false;
public int rune_natural_power_attack;
// One float[] per rune_naturalattacks entry. Same "attack info" struct as a weapon (verified in IDA, see
// MBDATA/docs/client-map.md): [0] attack speed (WEPSPEED), [1] range (MAXRANGE), [2] unknown int,
// [3] equip slot (1 = RHELD, 2 = LHELD), [4] projectile template id, [5] projectile speed, [6] swing anim,
// [7] damage type, [8] min damage, [9] max damage
public final ArrayList<float[]> rune_natural_attacks = new ArrayList<>();
public final HashMap<String, String> rune_sparse_data = new HashMap<>();
public final HashMap<String, int[]> rune_skill_adj = new HashMap<>();
public final EnumSet<mbEnums.MonsterType> rune_enemy_monster_types = EnumSet.noneOf(mbEnums.MonsterType.class);
public final EnumSet<mbEnums.MonsterType> rune_not_enemy_monster_types = EnumSet.noneOf(mbEnums.MonsterType.class);
public final ArrayList<Integer> rune_groupee_monster_types = new ArrayList<>();
public final ArrayList<Integer> rune_helper_monster_types = new ArrayList<>();
public int item_bane_rank;
public ItemTemplate(JSONObject jsonObject) {
try {
// Reading a String
obj_name = jsonObject.getString("obj_name");
// Reading a boolean
obj_pickable = jsonObject.getBoolean("obj_pickable");
// Reading floats from an array (note always check for empty arrays)
JSONArray scaleData = jsonObject.getJSONArray("obj_scale");
if (scaleData.isEmpty() == false)
obj_scale = new Vector3fImmutable(scaleData.getFloat(0), scaleData.getFloat(1),
scaleData.getFloat(2));
JSONArray forwardVector = jsonObject.getJSONArray("obj_forward_vector");
if (forwardVector.isEmpty() == false)
obj_forward_vector = new Vector3fImmutable(forwardVector.getFloat(0), forwardVector.getFloat(1),
forwardVector.getFloat(2));
JSONArray defaultAlignment = jsonObject.getJSONArray("obj_default_alignment");
if (defaultAlignment.isEmpty() == false)
obj_default_alignment = new Vector3fImmutable(defaultAlignment.getFloat(0), defaultAlignment.getFloat(1),
defaultAlignment.getFloat(2));
// Reading an integer value
obj_render_object = jsonObject.getInt("obj_render_object");
obj_icon = jsonObject.getInt("obj_icon");
JSONObject obj_sparse_json = jsonObject.getJSONObject("obj_sparse_data");
for (String sparseType : obj_sparse_json.keySet()) {
Object sparseValue = obj_sparse_json.get(sparseType);
obj_sparse_data.put(sparseType, sparseValue.toString());
}
// Banes are defined by their sparse data field
if (obj_sparse_data.get("ACTIONRESPONSE") != null)
if (obj_sparse_data.get("ACTIONRESPONSE").equals("4250517122"))
item_bane_rank = jsonObject.getInt("item_bane_rank");
// Reading float values
combat_health_current = jsonObject.getFloat("combat_health_current");
combat_health_full = jsonObject.getFloat("combat_health_full");
// Reading a hashmap of floats
JSONObject resist_json = jsonObject.getJSONObject("combat_attack_resist");
for (String resistType : resist_json.keySet()) {
float resistValue = resist_json.getFloat(resistType);
combat_attack_resist.put(resistType, resistValue);
}
// Parsing an enum
item_type = mbEnums.ItemType.valueOf(jsonObject.getString("item_type"));
item_eq_slots_value = jsonObject.getInt("item_eq_slots_value");
item_eq_slots_type = jsonObject.getBoolean("item_eq_slots_type");
// Parsing an enumset
JSONArray eq_slots_or = jsonObject.getJSONArray("item_eq_slots_or");
if (eq_slots_or.isEmpty() == false)
for (Object o : eq_slots_or)
item_eq_slots_or.add(mbEnums.EquipSlotType.valueOf((String) o));
JSONArray eq_slots_and = jsonObject.getJSONArray("item_eq_slots_and");
if (eq_slots_and.isEmpty() == false)
for (Object o : eq_slots_and)
item_eq_slots_and.add(mbEnums.EquipSlotType.valueOf((String) o));
item_takeable = jsonObject.getBoolean("item_takeable");
item_value = (jsonObject.getInt("item_value"));
item_wt = jsonObject.getInt("item_wt");
item_passive_defense_mod = jsonObject.getFloat("item_passive_defense_mod");
item_base_name = jsonObject.getString("item_base_name");
item_dsc = jsonObject.getString("item_dsc");
item_render_object_female = jsonObject.getInt("item_render_object_female");
item_health_full = jsonObject.getFloat("item_health_full");
Object skill_used = jsonObject.get("item_skill_used");
if (skill_used instanceof String) {
if (skill_used.equals("Cloth") == false)
item_skill_used = (String) skill_used;
}
Object mastery_used = jsonObject.get("item_skill_mastery_used");
if (mastery_used instanceof String)
item_skill_mastery_used = (String) mastery_used;
item_parry_anim_id = jsonObject.getInt("item_parry_anim_id");
// Reading offering data
JSONArray offering_data = jsonObject.getJSONArray("item_offering_info");
for (Object entry : offering_data) {
JSONObject offering_entry = (JSONObject) entry;
String offering_type = offering_entry.getString("offering_type").replaceAll("-", "");
int offering_value = offering_entry.getInt("offering_value");
item_offering_info.put(mbEnums.ShrineType.valueOf(offering_type), offering_value);
}
// Fields only present for ARMOR
if (item_type.equals(mbEnums.ItemType.ARMOR)) {
item_bulk_factor = jsonObject.getFloat("item_bulk_factor");
item_defense_rating = jsonObject.getInt("item_defense_rating");
}
// Fields only present for WEAPON
if (item_type.equals(mbEnums.ItemType.WEAPON)) {
JSONObject item_weapon = jsonObject.getJSONObject("item_weapon");
item_weapon_wepspeed = item_weapon.getFloat("weapon_wepspeed");
item_weapon_max_range = item_weapon.getFloat("weapon_max_range");
item_weapon_projectile_id = item_weapon.getInt("weapon_projectile_id");
item_weapon_projectile_speed = item_weapon.getFloat("weapon_projectile_speed");
item_weapon_combat_idle_anim = item_weapon.getInt("weapon_combat_idle_anim");
JSONArray weapon_damage = item_weapon.getJSONArray("weapon_damage");
if (weapon_damage.isEmpty() == false)
for (Object o : weapon_damage) {
JSONObject damage_entry = (JSONObject) o;
mbEnums.DamageType damageType = mbEnums.DamageType.getDamageType(damage_entry.getString("damage_type"));
int min = damage_entry.getInt("damage_min");
int max = damage_entry.getInt("damage_max");
int[] minMax = {min, max};
item_weapon_damage.put(damageType, minMax);
}
JSONArray attack_anim_right = item_weapon.getJSONArray("weapon_attack_anim_right");
if (attack_anim_right.isEmpty() == false)
for (Object o : attack_anim_right) {
JSONArray animationEntry = (JSONArray) o;
int animation = animationEntry.getInt(0);
int duration = animationEntry.getInt(1);
weapon_attack_anim_right.add(new int[]{animation, duration});
}
JSONArray attack_anim_left = item_weapon.getJSONArray("weapon_attack_anim_left");
if (attack_anim_left.isEmpty() == false)
for (Object o : attack_anim_left) {
JSONArray animationEntry = (JSONArray) o;
int animation = animationEntry.getInt(0);
int duration = animationEntry.getInt(1);
weapon_attack_anim_left.add(new int[]{animation, duration});
}
item_primary_attr = mbEnums.AttributeType.valueOf(jsonObject.getString("item_primary_attr"));
item_secondary_attr = mbEnums.AttributeType.valueOf(jsonObject.getString("item_secondary_attr"));
}
JSONArray itemflags = jsonObject.getJSONArray("item_flags");
if (itemflags.isEmpty() == false)
for (Object o : itemflags) {
String flag = (String) o;
if (flag.equals("None") == false)
item_flags.add(mbEnums.ItemFlags.valueOf((String) o));
}
JSONArray itemUseflags = jsonObject.getJSONArray("item_use_flags");
if (itemUseflags.isEmpty() == false)
for (Object o : itemUseflags)
item_use_flags.add(mbEnums.ItemUseFlags.valueOf((String) o));
item_initial_charges = jsonObject.getInt("item_initial_charges");
JSONArray skill_required = jsonObject.getJSONArray("item_skill_req");
if (skill_required.isEmpty() == false)
for (Object o : skill_required) {
JSONObject skill_req = (JSONObject) o;
String skill_type = skill_req.getString("skill_type");
int skill_level = skill_req.getInt("skill_level");
if (skill_type.equals("Cloth") == false)
item_skill_required.put(skill_type, skill_level);
}
JSONObject race_required = jsonObject.getJSONObject("item_race_req");
boolean restrict = race_required.getBoolean("restrict");
JSONArray races = race_required.getJSONArray("races");
if (races.isEmpty() == false)
for (Object o : races) {
String race = (String) o;
race = race.replaceAll("\\s", "");
race = race.replaceAll(",", "");
race = race.replaceAll("-", "");
if (restrict)
item_race_res.add(mbEnums.MonsterType.valueOf(race));
else
item_race_req.add(mbEnums.MonsterType.valueOf(race));
}
JSONObject class_required = jsonObject.getJSONObject("item_class_req");
restrict = class_required.getBoolean("restrict");
JSONArray classes = class_required.getJSONArray("classes");
if (classes.isEmpty() == false)
for (Object o : classes) {
String classEntry = (String) o;
classEntry = classEntry.replaceAll("\\s", "");
classEntry = classEntry.replaceAll(",", "");
if (restrict)
item_class_res.add(mbEnums.ClassType.valueOf(classEntry));
else
item_class_req.add(mbEnums.ClassType.valueOf(classEntry));
}
JSONObject disc_required = jsonObject.getJSONObject("item_disc_req");
restrict = disc_required.getBoolean("restrict");
JSONArray discs = disc_required.getJSONArray("discs");
if (discs.isEmpty() == false)
for (Object o : discs) {
String disc = (String) o;
disc = disc.replaceAll("\\s", "");
disc = disc.replaceAll(",", "");
if (restrict)
item_disc_res.add(mbEnums.DisciplineType.valueOf(disc));
else
item_disc_req.add(mbEnums.DisciplineType.valueOf(disc));
}
item_level_req = jsonObject.getInt("item_level_req");
item_sex_req = mbEnums.SexType.valueOf(jsonObject.getString("item_sex_req"));
JSONArray userPowerActions = jsonObject.getJSONArray("item_user_power_action");
if (userPowerActions.isEmpty() == false)
for (Object o : userPowerActions) {
JSONObject powerActionEntry = (JSONObject) o;
String power = powerActionEntry.getString("power");
JSONArray args = powerActionEntry.getJSONArray("arguments");
int[] arguments = {args.getInt(0), args.getInt(1)};
item_user_power_action.put(power, arguments);
}
JSONArray powerGrantsArray = jsonObject.getJSONArray("item_power_grant");
if (powerGrantsArray.isEmpty() == false) {
for (Object grantArrayEntry : powerGrantsArray) {
JSONObject powerGrantEntry = (JSONObject) grantArrayEntry;
String power_type = powerGrantEntry.getString("power_type");
int power_value = powerGrantEntry.getInt("power_value");
item_power_grant.put(power_type, power_value);
JSONArray skill_granted_array = powerGrantEntry.getJSONArray("power_granted_skills");
for (Object skillGrantEntry : skill_granted_array) {
JSONArray skill_entry = (JSONArray) skillGrantEntry;
String skill_type = skill_entry.getString(0);
int skill_level = skill_entry.getInt(1);
power_granted_skills.put(skill_type, skill_level);
}
}
}
JSONArray item_power_actions = jsonObject.getJSONArray("item_power_action");
if (item_power_actions.isEmpty() == false)
for (Object o : item_power_actions) {
JSONObject powerActionEntry = (JSONObject) o;
String power = powerActionEntry.getString("power_type");
JSONArray power_actions = powerActionEntry.getJSONArray("power_actions");
JSONObject argument_entry = power_actions.getJSONObject(0);
JSONArray power_args = argument_entry.getJSONArray("power_arguments");
int[] power_arguments = {power_args.getInt(0), power_args.getInt(1)};
item_power_action.put(power, power_arguments);
}
JSONArray resource_costs = jsonObject.getJSONArray("item_resource_costs");
if (resource_costs.isEmpty() == false)
for (Object o : resource_costs) {
JSONObject resource_entry = (JSONObject) o;
mbEnums.ResourceType resource_type = mbEnums.ResourceType.valueOf(resource_entry.getString("resource_type").toUpperCase());
int resource_value = resource_entry.getInt("resource_value");
item_resource_cost.put(resource_type, resource_value);
}
// Deed related fields
if (item_type.equals(mbEnums.ItemType.DEED)) {
deed_type = jsonObject.getInt("deed_type");
deed_furniture_id = jsonObject.getInt("deed_furniture_id");
deed_target_id = jsonObject.getInt("deed_target_id");
deed_employment = jsonObject.getInt("deed_employment");
deed_start_rank = jsonObject.getInt("deed_start_rank");
deed_name_lookup = jsonObject.getInt("deed_name_lookup");
deed_indoors = jsonObject.getBoolean("deed_indoors");
deed_is_fortress = jsonObject.getBoolean("deed_is_fortress");
deed_namelookup_val = jsonObject.getFloat("deed_namelookup_val");
deed_custom_city = jsonObject.getBoolean("deed_custom_city");
deed_structure_id = jsonObject.getInt("deed_structure_id");
}
if (item_type.equals(mbEnums.ItemType.RUNE)) {
rune_type = jsonObject.getString("rune_type");
Object subType = jsonObject.get("rune_sub_type");
if (subType instanceof String)
rune_sub_type = (String) subType;
rune_is_standard_character_creation = jsonObject.getBoolean("rune_is_standard_character_creation");
rune_creation_cost = jsonObject.getInt("rune_creation_cost");
rune_rank = jsonObject.getInt("rune_rank");
rune_pracs_per_level = jsonObject.getInt("rune_pracs_per_level");
rune_exp_req_to_level = jsonObject.getFloat("rune_exp_req_to_level");
rune_sex = mbEnums.SexType.valueOf(jsonObject.getString("rune_sex"));
rune_class_icon = jsonObject.getInt("rune_class_icon");
rune_health = jsonObject.getInt("rune_health");
rune_mana = jsonObject.getInt("rune_mana");
rune_stamina = jsonObject.getInt("rune_stamina");
rune_min_damage = jsonObject.getFloat("rune_min_damage");
rune_max_damage = jsonObject.getFloat("rune_max_damage");
rune_attack = jsonObject.getInt("rune_attack");
rune_defense = jsonObject.getInt("rune_defense");
rune_level = jsonObject.getInt("rune_level");
JSONObject rune_speed_json = jsonObject.getJSONObject("rune_speed");
for (String key : rune_speed_json.keySet()) {
mbEnums.MovementType movementType = mbEnums.MovementType.valueOf(key);
float speed = rune_speed_json.getFloat(key);
rune_speed.put(movementType, speed);
}
JSONObject rune_group = jsonObject.getJSONObject("rune_group");
rune_group_type = rune_group.getInt("group_type");
rune_group_is_faction = rune_group.getBoolean("group_is_faction");
rune_group_is_guild = rune_group.getBoolean("group_is_guild");
rune_dsc = jsonObject.getString("rune_dsc");
rune_fx_txt = jsonObject.getString("rune_fx_txt");
long group_tactics_bitvector = jsonObject.getLong("rune_group_tactics");
rune_group_tactics.addAll(mbEnums.fromLong(group_tactics_bitvector, mbEnums.MobBehaviourType.class));
long group_role_bitvector = jsonObject.getLong("rune_group_role_set");
rune_group_role_set.addAll(mbEnums.fromLong(group_role_bitvector, mbEnums.MobBehaviourType.class));
JSONArray enemy_types_json = jsonObject.getJSONArray("rune_enemy_monster_types");
for (Object o : enemy_types_json) {
String enemy = (String) o;
enemy = enemy.replaceAll("-", "");
mbEnums.MonsterType monsterType = mbEnums.MonsterType.valueOf(enemy);
rune_enemy_monster_types.add(monsterType);
}
JSONArray not_enemy_types_json = jsonObject.getJSONArray("rune_not_enemy_monster_types");
for (Object o : not_enemy_types_json) {
String notenemy = (String) o;
notenemy = notenemy.replaceAll("-", "");
mbEnums.MonsterType monsterType = mbEnums.MonsterType.valueOf(notenemy);
rune_not_enemy_monster_types.add(monsterType);
}
JSONArray groupee_types_json = jsonObject.getJSONArray("rune_groupee_monster_types");
for (Object o : groupee_types_json) {
int groupeeId = (int) o;
rune_groupee_monster_types.add(groupeeId);
}
JSONArray helper_types_json = jsonObject.getJSONArray("rune_helper_monster_types");
for (Object o : helper_types_json) {
int helperId = (int) o;
rune_helper_monster_types.add(helperId);
}
rune_renderable = jsonObject.getBoolean("rune_renderable");
rune_natural_power_attack = jsonObject.getInt("rune_natural_power_attack");
if (jsonObject.has("rune_naturalattacks")) {
JSONArray natural_attacks_json = jsonObject.getJSONArray("rune_naturalattacks");
for (Object attackEntry : natural_attacks_json) {
JSONArray attackArray = (JSONArray) attackEntry;
float[] attack = new float[attackArray.length()];
for (int i = 0; i < attackArray.length(); i++)
attack[i] = attackArray.getFloat(i);
rune_natural_attacks.add(attack);
}
}
JSONObject rune_sparse_json = jsonObject.getJSONObject("rune_sparse_data");
for (String sparseType : rune_sparse_json.keySet()) {
Object sparseValue = rune_sparse_json.get(sparseType);
rune_sparse_data.put(sparseType, sparseValue.toString());
}
JSONArray skill_adj_json = (JSONArray) jsonObject.get("rune_skill_adj");
for (Object skillEntry : skill_adj_json) {
JSONObject skill = (JSONObject) skillEntry;
String typeString = skill.getString("skill_type");
JSONArray adjArray = skill.getJSONArray("skill_adjusts");
for (Object adjustList : adjArray) {
JSONArray adjustEntry = (JSONArray) adjustList;
int[] adjust = {adjustEntry.getInt(0), adjustEntry.getInt(1)};
rune_skill_adj.put(typeString, adjust);
}
}
JSONArray attr_adj_json = jsonObject.getJSONArray("rune_attr_adj");
for (Object attributeEntry : attr_adj_json) {
JSONObject attribute = (JSONObject) attributeEntry;
String typeString = attribute.getString("attr_type");
mbEnums.AttributeType attributeType = mbEnums.AttributeType.valueOf(typeString);
int attributeValue = attribute.getInt("attr_value");
rune_attr_adj.put(attributeType, attributeValue);
}
JSONArray max_attr_adj_json = jsonObject.getJSONArray("rune_max_attr_adj");
for (Object attributeEntry : max_attr_adj_json) {
JSONObject attribute = (JSONObject) attributeEntry;
String typeString = attribute.getString("attr_type");
mbEnums.AttributeType attributeType = mbEnums.AttributeType.valueOf(typeString);
int attributeValue = attribute.getInt("attr_value");
rune_max_attr_adj.put(attributeType, attributeValue);
}
JSONArray skill_grant_json = jsonObject.getJSONArray("rune_skill_grant");
if (skill_grant_json.isEmpty() == false)
for (Object runeSkillEntry : skill_grant_json) {
JSONObject skill_granted = (JSONObject) runeSkillEntry;
String typeString = skill_granted.getString("skill_type");
int skill_level = skill_granted.getInt("skill_value");
rune_skill_grant.put(typeString, skill_level);
JSONArray skill_granted_array = skill_granted.getJSONArray("skill_granted_skills");
for (Object skillGrantEntry : skill_granted_array) {
JSONArray skill_entry = (JSONArray) skillGrantEntry;
typeString = skill_entry.getString(0);
skill_level = skill_entry.getInt(1);
skill_granted_skills.put(typeString, skill_level);
}
}
}
} catch (Exception e) {
Logger.error(obj_name + ":" + e);
}
}
}
+5 -1
View File
@@ -127,9 +127,13 @@ public class MobBase extends AbstractGameObject {
for (BootySetEntry equipmentSetEntry : equipList) {
Item item = new Item(equipmentSetEntry.templateID);
if (item.template.getItemEqSlotsOr().isEmpty())
continue;
item.objectUUID = ItemManager.lastNegativeID.decrementAndGet();
item.drop_chance = equipmentSetEntry.dropChance;
item.equipSlot = item.template.item_eq_slots_or.iterator().next();
item.equipSlot = item.template.getItemEqSlotsOr().iterator().next();
equip.put(item.equipSlot, item);
}
+7 -6
View File
@@ -15,6 +15,7 @@ import engine.gameManager.PowersManager;
import engine.mbEnums;
import engine.mbEnums.ItemType;
import engine.mbEnums.OwnerType;
import engine.parsers.objects.CObjectTemplate;
import engine.powers.poweractions.AbstractPowerAction;
import org.pmw.tinylog.Logger;
@@ -30,22 +31,22 @@ public final class MobLoot extends Item {
private String prefix = "";
private String suffix = "";
public MobLoot(AbstractCharacter mob, ItemTemplate template, boolean noSteal) {
public MobLoot(AbstractCharacter mob, CObjectTemplate template, boolean noSteal) {
this(mob, template, 0, noSteal);
}
public MobLoot(AbstractCharacter mob, int qtyOfGold) {
this(mob, ItemTemplate.templates.get(mbEnums.ResourceType.GOLD.templateID), qtyOfGold, false);
this(mob, CObjectTemplate.lookup(mbEnums.ResourceType.GOLD.templateID), qtyOfGold, false);
}
public MobLoot(AbstractCharacter mob, ItemTemplate template, int quantity, boolean noSteal) {
public MobLoot(AbstractCharacter mob, CObjectTemplate template, int quantity, boolean noSteal) {
super(template.template_id);
super(template.getTemplateId());
this.ownerType = OwnerType.Mob;
this.ownerID = mob.getObjectUUID();
this.objectUUID = generateId();
if (quantity == 0 && template.item_type == ItemType.RESOURCE)
if (quantity == 0 && template.getItemType() == ItemType.RESOURCE)
quantity = 1;
if (quantity > 0)
@@ -105,7 +106,7 @@ public final class MobLoot extends Item {
if (isDeleted)
return null;
if (this.template.item_type.equals(ItemType.GOLD))
if (this.template.getItemType().equals(ItemType.GOLD))
return null;
Item item = this;
+6 -5
View File
@@ -23,6 +23,7 @@ import engine.net.ByteBufferWriter;
import engine.net.Dispatch;
import engine.net.client.msg.PetMsg;
import engine.net.client.msg.PlaceAssetMsg;
import engine.parsers.objects.CObjectTemplate;
import engine.server.MBServerStatics;
import org.joda.time.DateTime;
import org.pmw.tinylog.Logger;
@@ -961,15 +962,15 @@ public class NPC extends AbstractCharacter {
maxSkill = 110;
break;
}
ItemTemplate itemTemplate;
CObjectTemplate itemTemplate;
int skillRequired = 0;
for (Integer itemID : fullItemList) {
itemTemplate = ItemTemplate.templates.get(itemID);
itemTemplate = CObjectTemplate.lookup(itemID);
if (itemTemplate.item_skill_required.values().iterator().hasNext())
skillRequired = itemTemplate.item_skill_required.values().iterator().next();
if (itemTemplate.getItemSkillReq().values().iterator().hasNext())
skillRequired = itemTemplate.getItemSkillReq().values().iterator().next();
boolean exclude = skillRequired == 0 && ItemTemplate.templates.get(itemID).item_type.equals(ItemType.WEAPON);
boolean exclude = skillRequired == 0 && CObjectTemplate.lookup(itemID).getItemType().equals(ItemType.WEAPON);
if (skillRequired <= maxSkill && !exclude)
filteredItemList.add(itemID);
}
+12 -11
View File
@@ -32,6 +32,7 @@ import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.*;
import engine.net.client.msg.login.CommitNewCharacterMsg;
import engine.parsers.objects.CObjectTemplate;
import engine.powers.EffectsBase;
import engine.server.MBServerStatics;
import engine.server.login.LoginServer;
@@ -1353,10 +1354,10 @@ public class PlayerCharacter extends AbstractCharacter {
public static float getDexPenalty(Item armor) {
if (armor == null)
return 0f;
ItemTemplate ab = armor.template;
CObjectTemplate ab = armor.template;
if (ab == null)
return 0f;
return ab.item_bulk_factor;
return engine.parsers.objects.ItemTemplate._items.get(armor.templateID).item_bulk_factor;
}
public static void InitializeSkillsOnLoad(PlayerCharacter pc) {
@@ -2816,15 +2817,15 @@ public class PlayerCharacter extends AbstractCharacter {
if (item == null)
return false;
ItemTemplate ib = item.template;
CObjectTemplate ib = item.template;
if (ib != null) {
if ((item.template.item_type.equals(ItemType.WEAPON))
&& (item.template.item_skill_used.equals(type) || ib.item_skill_mastery_used.equals(type)))
if ((item.template.getItemType().equals(ItemType.WEAPON))
&& (item.template.getItemSkillUsed().equals(type) || ib.getItemSkillMasteryUsed().equals(type)))
return true;
return (item.template.item_type.equals(ItemType.ARMOR))
&& (item.template.item_skill_used.equals(type));
return (item.template.getItemType().equals(ItemType.ARMOR))
&& (item.template.getItemSkillUsed().equals(type));
}
return false;
@@ -3768,8 +3769,8 @@ public class PlayerCharacter extends AbstractCharacter {
ConcurrentHashMap<EquipSlotType, Item> equipped = this.charItemManager.getEquipped();
Item off = equipped.get(EquipSlotType.LHELD);
Item main = equipped.get(EquipSlotType.RHELD);
ItemTemplate wbMain = null;
ItemTemplate wbOff = null;
CObjectTemplate wbMain = null;
CObjectTemplate wbOff = null;
if (main != null)
wbMain = main.template;
if (off != null)
@@ -3791,8 +3792,8 @@ public class PlayerCharacter extends AbstractCharacter {
this.bonuses.setBool(ModType.Parry, SourceType.None, false);
if ((this.baseClass != null && this.baseClass.getObjectUUID() == 2500)
|| (this.promotionClass != null && this.promotionClass.getObjectUUID() == 2520))
if (wbMain == null || wbMain.item_weapon_max_range < MBServerStatics.RANGED_WEAPON_RANGE)
if (wbOff == null || wbOff.item_weapon_max_range < MBServerStatics.RANGED_WEAPON_RANGE)
if (wbMain == null || engine.parsers.objects.ItemTemplate.getWeapon(wbMain.getTemplateId()).weapon_max_range < MBServerStatics.RANGED_WEAPON_RANGE)
if (wbOff == null || engine.parsers.objects.ItemTemplate.getWeapon(wbOff.getTemplateId()).weapon_max_range < MBServerStatics.RANGED_WEAPON_RANGE)
this.bonuses.setBool(ModType.Parry, SourceType.None, true);
}
+78
View File
@@ -10,8 +10,13 @@
package engine.objects;
import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.mbEnums.GameObjectType;
import engine.net.ByteBufferWriter;
import engine.parsers.cache.CObjectIndex;
import engine.parsers.objects.RuneTemplate;
import engine.util.Hasher;
import org.pmw.tinylog.Logger;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -103,6 +108,79 @@ public class PromotionClass extends AbstractGameObject {
}
/**
* Rune Template Constructor
*/
public PromotionClass(RuneTemplate rune) {
super(rune.template_id);
this.name = rune.obj_name;
this.description = rune.rune_dsc;
this.token = Hasher.SBStringHash(rune.rune_sub_type);
this.healthMod = rune.rune_health;
this.manaMod = rune.rune_mana;
this.staminaMod = rune.rune_stamina;
this.allowedRunes = getAllowedRuneTokens(rune);
this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(this.getObjectUUID());
this.powersGranted = PowerReq.getPowerReqsForRune(this.getObjectUUID());
this.effectsGranted = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE(this.getObjectUUID());
this.effectsGrantedFighter = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2500);
this.effectsGrantedHealer = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2501);
this.effectsGrantedRogue = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2502);
this.effectsGrantedMage = DbManager.RuneBaseEffectQueries.GET_EFFECTS_FOR_RUNEBASE((this.getObjectUUID() * 10) + 2503);
this.effectsList = DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.getObjectUUID());
}
// Promotions are the CLASS runes that require a base class; this skips the creation classes and Pet 2522.
public static boolean isPromotionRune(RuneTemplate rune) {
return rune.rune_type == mbEnums.RuneType.CLASS && !rune.rune_is_standard_character_creation
&& !rune.item_class_req.isEmpty();
}
// Race and base class tokens are SBStringHash(rune_sub_type) of the runes named in the req lists.
// Every promotion rune's lists are whitelists (restrict false).
private static ArrayList<Integer> getAllowedRuneTokens(RuneTemplate rune) {
ArrayList<Integer> tokens = new ArrayList<>();
Integer classRuneId;
int token;
for (mbEnums.MonsterType race : rune.item_race_req)
for (int raceRuneId : CObjectIndex.getRaceRuneIds(race)) {
token = Hasher.SBStringHash(RuneTemplate._runes.get(raceRuneId).rune_sub_type);
if (!tokens.contains(token))
tokens.add(token);
}
for (mbEnums.ClassType classType : rune.item_class_req) {
classRuneId = CObjectIndex.getClassRuneId(classType);
if (classRuneId != null)
tokens.add(Hasher.SBStringHash(RuneTemplate._runes.get(classRuneId).rune_sub_type));
}
return tokens;
}
public static void LoadAllPromotions() {
int loadedCount = 0;
for (RuneTemplate rune : RuneTemplate._runes.values()) {
if (!isPromotionRune(rune))
continue;
DbManager.addToCache(new PromotionClass(rune));
loadedCount++;
}
Logger.info("Loaded " + loadedCount + " promotion classes from runes");
// The CSR/admin rows 2901+ have no promotion rune; the DB loader skips ids already cached
DbManager.PromotionQueries.GET_ALL_PROMOTIONS();
}
public static void serializeForClientMsg(PromotionClass promotionClass, ByteBufferWriter writer) {
writer.putInt(3); // For BaseClass
writer.putInt(0); // Pad
+106 -1
View File
@@ -13,7 +13,11 @@ import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.mbEnums.RaceType;
import engine.net.ByteBufferWriter;
import engine.parsers.cache.CacheParserUtil;
import engine.parsers.objects.RuneTemplate;
import engine.server.MBServerStatics;
import engine.util.Hasher;
import org.pmw.tinylog.Logger;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -27,6 +31,9 @@ public class Race {
private static ConcurrentHashMap<Integer, Race> _raceByID = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
// Creation points before the race rune's rune_creation_cost is taken off
private static final int STARTING_POINT_POOL = 30;
private final String name;
private final String description;
private final int raceRuneID;
@@ -107,8 +114,106 @@ public class Race {
}
/**
* Rune Template Constructor
* minHeight and strHeightMod are server-only and come from the static_rune_race row.
*/
public Race(RuneTemplate rune, Race dbRace) {
this.raceRuneID = rune.template_id;
this.raceType = mbEnums.RaceType.getRaceTypebyRuneID(raceRuneID);
this.name = rune.obj_name;
this.description = rune.rune_dsc;
this.strStart = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Strength, 0).shortValue();
this.strMin = 0;
this.strMax = rune.rune_max_attr_adj.getOrDefault(mbEnums.AttributeType.Strength, 0).shortValue();
this.dexStart = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Dexterity, 0).shortValue();
this.dexMin = 0;
this.dexMax = rune.rune_max_attr_adj.getOrDefault(mbEnums.AttributeType.Dexterity, 0).shortValue();
this.conStart = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Constitution, 0).shortValue();
this.conMin = 0;
this.conMax = rune.rune_max_attr_adj.getOrDefault(mbEnums.AttributeType.Constitution, 0).shortValue();
this.intStart = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Intelligence, 0).shortValue();
this.intMin = 0;
this.intMax = rune.rune_max_attr_adj.getOrDefault(mbEnums.AttributeType.Intelligence, 0).shortValue();
this.spiStart = rune.rune_attr_adj.getOrDefault(mbEnums.AttributeType.Spirit, 0).shortValue();
this.spiMin = 0;
this.spiMax = rune.rune_max_attr_adj.getOrDefault(mbEnums.AttributeType.Spirit, 0).shortValue();
this.token = Hasher.SBStringHash(rune.rune_sub_type);
this.healthBonus = (short) rune.rune_health;
this.manaBonus = (short) rune.rune_mana;
this.staminaBonus = (short) rune.rune_stamina;
this.startingPoints = (byte) (STARTING_POINT_POOL - rune.rune_creation_cost);
this.minHeight = dbRace.minHeight;
this.strHeightMod = dbRace.strHeightMod;
this.hairStyles = new HashSet<>(rune.rune_hair);
this.beardStyles = new HashSet<>(rune.rune_beard);
this.skinColors = DbManager.RaceQueries.SKIN_COLOR_FOR_RACE(raceRuneID);
this.beardColors = DbManager.RaceQueries.BEARD_COLORS_FOR_RACE(raceRuneID);
this.hairColors = DbManager.RaceQueries.HAIR_COLORS_FOR_RACE(raceRuneID);
this.baseClasses = getBaseClassesForRace(rune);
this.skillsGranted = DbManager.SkillReqQueries.GET_REQS_FOR_RUNE(raceRuneID);
this.powersGranted = PowerReq.getPowerReqsForRune(raceRuneID);
this.effectsList = DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.raceRuneID);
// Races without hair styles (Aracoix, Shade) select style 0, which creation validates
if (this.hairStyles.isEmpty())
this.hairStyles.add(0);
}
// Base classes are the creation class runes whose item_race_req names this race's sub type.
// Every creation class rune's race list is a whitelist (restrict false).
private static ArrayList<BaseClass> getBaseClassesForRace(RuneTemplate rune) {
ArrayList<BaseClass> baseClasses = new ArrayList<>();
mbEnums.MonsterType monsterType;
BaseClass baseClass;
monsterType = CacheParserUtil.readEnum(mbEnums.MonsterType.class, rune.rune_sub_type, rune.template_id, "rune_sub_type");
for (RuneTemplate classRune : RuneTemplate._runes.values()) {
if (classRune.rune_type != mbEnums.RuneType.CLASS || !classRune.rune_is_standard_character_creation)
continue;
if (!classRune.item_race_req.contains(monsterType))
continue;
baseClass = DbManager.BaseClassQueries.GET_BASE_CLASS(classRune.template_id);
if (baseClass != null)
baseClasses.add(baseClass);
}
return baseClasses;
}
// Iterates the static_rune_race rows: no rune field separates 2018-2021 from the mob race runes,
// and each row still holds the server-only heights.
public static void loadAllRaces() {
Race._raceByID = DbManager.RaceQueries.LOAD_ALL_RACES();
ConcurrentHashMap<Integer, Race> dbRaces;
ConcurrentHashMap<Integer, Race> races;
RuneTemplate rune;
dbRaces = DbManager.RaceQueries.LOAD_ALL_RACES();
races = new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_LOW);
for (Race dbRace : dbRaces.values()) {
rune = RuneTemplate._runes.get(dbRace.raceRuneID);
if (rune == null || rune.rune_type != mbEnums.RuneType.RACE) {
Logger.warn("Race " + dbRace.raceRuneID + " has no race rune; loaded from static_rune_race");
races.put(dbRace.raceRuneID, dbRace);
continue;
}
races.put(dbRace.raceRuneID, new Race(rune, dbRace));
}
Race._raceByID = races;
Logger.info("Loaded " + races.size() + " races from runes");
}
public static Race getRace(int id) {
+8 -4
View File
@@ -19,6 +19,7 @@ import org.pmw.tinylog.Logger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
@@ -159,13 +160,16 @@ public class Resists {
private static float[] getArmorResists(Item armor, float[] phys) {
EnumMap<mbEnums.DamageType, Float> armorResists;
if (armor == null)
return phys;
if (armor.template.item_type.equals(mbEnums.ItemType.ARMOR)) {
phys[0] += armor.template.combat_attack_resist.get("SLASHING");
phys[1] += armor.template.combat_attack_resist.get("CRUSHING");
phys[2] += armor.template.combat_attack_resist.get("PIERCING");
if (armor.template.getItemType().equals(mbEnums.ItemType.ARMOR)) {
armorResists = armor.template.getCombatAttackResist();
phys[0] += armorResists.getOrDefault(mbEnums.DamageType.SLASHING, 0f);
phys[1] += armorResists.getOrDefault(mbEnums.DamageType.CRUSHING, 0f);
phys[2] += armorResists.getOrDefault(mbEnums.DamageType.PIERCING, 0f);
}
return phys;
+139 -1
View File
@@ -12,7 +12,11 @@ package engine.objects;
import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.net.ByteBufferWriter;
import engine.parsers.cache.CObjectIndex;
import engine.parsers.objects.RuneTemplate;
import engine.server.MBServerStatics;
import engine.util.Hasher;
import org.pmw.tinylog.Logger;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -36,6 +40,12 @@ public class RuneBase extends AbstractGameObject {
private final ArrayList<Integer> overwrite = new ArrayList<>();
private int levelRequired = 1;
private ArrayList<MobBaseEffects> effectsList = new ArrayList<>();
private mbEnums.RuneType runeType;
private int subTypeToken;
private int rank;
private boolean raceRestrict;
private boolean classRestrict;
private boolean discRestrict;
/**
* No Table ID Constructor
@@ -74,10 +84,82 @@ public class RuneBase extends AbstractGameObject {
this.type = rs.getInt("type");
this.subtype = rs.getByte("subtype");
// static_rune_runereq race/class rows are whitelists, its discipline rows prohibitions
this.discRestrict = true;
DbManager.RuneBaseQueries.GET_RUNE_REQS(this);
this.effectsList = DbManager.MobBaseQueries.GET_RUNEBASE_EFFECTS(this.getObjectUUID());
}
/**
* Rune Template Constructor
* type and subtype are server enums that don't map cleanly from rune_type/rune_sub_type, so they
* come from the static_rune_runebase row, as do the effects until 2.10.
* Talents with the same sub type token replace each other by rank (client 0x4695D0), so no overwrite list.
*/
public RuneBase(RuneTemplate rune, RuneBase dbRuneBase) {
super(rune.template_id);
Integer classRuneID;
Integer discRuneID;
ArrayList<Integer> raceRuneIDs;
RuneTemplate classRune;
this.name = rune.item_base_name;
this.description = rune.rune_dsc;
this.type = dbRuneBase.type;
this.subtype = dbRuneBase.subtype;
this.effectsList = dbRuneBase.effectsList;
this.runeType = rune.rune_type;
this.rank = rune.rune_rank;
this.levelRequired = rune.item_level_req;
this.raceRestrict = rune.item_race_req_restrict;
this.classRestrict = rune.item_class_req_restrict;
this.discRestrict = rune.item_disc_req_restrict;
for (mbEnums.MonsterType raceType : rune.item_race_req) {
raceRuneIDs = CObjectIndex.getRaceRuneIds(raceType);
if (raceRuneIDs.isEmpty())
Logger.warn("RuneBase " + rune.template_id + ": race " + raceType + " has no race rune");
for (int raceRuneID : raceRuneIDs)
this.race.put(raceRuneID, true);
}
for (mbEnums.ClassType classType : rune.item_class_req) {
classRuneID = CObjectIndex.getClassRuneId(classType);
if (classRuneID == null) {
Logger.warn("RuneBase " + rune.template_id + ": class " + classType + " has no class rune");
continue;
}
classRune = RuneTemplate._runes.get(classRuneID);
if (classRune.rune_is_standard_character_creation)
this.baseClass.put(classRuneID, true);
else
this.promotionClass.put(classRuneID, true);
}
for (mbEnums.DisciplineType discType : rune.item_disc_req) {
discRuneID = CObjectIndex.getDisciplineRuneId(discType);
if (discRuneID == null) {
Logger.warn("RuneBase " + rune.template_id + ": discipline " + discType + " has no discipline rune");
continue;
}
this.discipline.put(discRuneID, true);
}
// Same token as CObjectIndex: unresolved sub types are stored as their hash
if (!rune.rune_sub_type.isEmpty())
this.subTypeToken = Hasher.SBStringHash(rune.rune_sub_type);
else
this.subTypeToken = rune.rune_sub_type_hash;
}
public static RuneBase getRuneBase(int tableId) {
if (tableId == 0)
@@ -100,9 +182,39 @@ public class RuneBase extends AbstractGameObject {
}
// Iterates the static_rune_runebase rows: no rune field separates the player runes from the mob
// talents, and each row still holds the server type/subtype.
public static void LoadAllRuneBases() {
DbManager.RuneBaseQueries.LOAD_ALL_RUNEBASES();
ArrayList<RuneBase> dbRuneBases;
ArrayList<RuneBaseAttribute> attributes;
RuneTemplate rune;
int loadedCount = 0;
dbRuneBases = DbManager.RuneBaseQueries.LOAD_ALL_RUNEBASES();
RuneBaseAttribute.runeBaseAttributeMap.clear();
for (RuneBase dbRuneBase : dbRuneBases) {
rune = RuneTemplate._runes.get(dbRuneBase.getObjectUUID());
if (rune == null) {
Logger.warn("RuneBase " + dbRuneBase.getObjectUUID() + " has no rune; loaded from static_rune_runebase without attributes");
continue;
}
DbManager.addToCache(new RuneBase(rune, dbRuneBase));
attributes = RuneBaseAttribute.getAttributesForRune(rune);
// getAttrs() returns null for a rune without attributes, as the DB map did
if (!attributes.isEmpty())
RuneBaseAttribute.runeBaseAttributeMap.put(rune.template_id, attributes);
loadedCount++;
}
Logger.info("Loaded " + loadedCount + " rune bases from runes");
RuneBase.AllowedBaseClassRunesMap = DbManager.RuneBaseQueries.LOAD_ALLOWED_STARTING_RUNES_FOR_BASECLASS();
RuneBase.AllowedRaceRunesMap = DbManager.RuneBaseQueries.LOAD_ALLOWED_STARTING_RUNES_FOR_RACE();
}
@@ -188,6 +300,32 @@ public class RuneBase extends AbstractGameObject {
return this.overwrite;
}
public mbEnums.RuneType getRuneType() {
return this.runeType;
}
// 0 when the rune has no sub type; talents sharing a non-zero token replace each other
public int getSubTypeToken() {
return this.subTypeToken;
}
public int getRank() {
return this.rank;
}
// restrict true: the list is a blacklist; false: a whitelist
public boolean isRaceRestrict() {
return this.raceRestrict;
}
public boolean isClassRestrict() {
return this.classRestrict;
}
public boolean isDiscRestrict() {
return this.discRestrict;
}
/*
* Serializing
*/
+31
View File
@@ -10,11 +10,15 @@
package engine.objects;
import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.mbEnums.GameObjectType;
import engine.parsers.objects.RuneTemplate;
import engine.server.MBServerStatics;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
@@ -57,6 +61,33 @@ public class RuneBaseAttribute extends AbstractGameObject {
this.runeBaseID = rs.getInt("RuneBaseID");
}
// Attribute ids: 0 = rune_creation_cost, 1-5 = rune_attr_adj, 6-10 = rune_max_attr_adj,
// 11-15 = item_attr_req; the AttributeType ordinals Strength..Spirit are 1-5.
public static ArrayList<RuneBaseAttribute> getAttributesForRune(RuneTemplate rune) {
ArrayList<RuneBaseAttribute> attributes = new ArrayList<>();
if (rune.rune_creation_cost != 0)
attributes.add(new RuneBaseAttribute((short) MBServerStatics.RUNE_COST_ATTRIBUTE_ID, (short) rune.rune_creation_cost));
addAttributes(attributes, rune.rune_attr_adj, 0);
addAttributes(attributes, rune.rune_max_attr_adj, 5);
addAttributes(attributes, rune.item_attr_req, 10);
return attributes;
}
private static void addAttributes(ArrayList<RuneBaseAttribute> attributes, EnumMap<mbEnums.AttributeType, Integer> values, int idOffset) {
for (mbEnums.AttributeType attributeType : values.keySet()) {
if (values.get(attributeType) == 0)
continue;
attributes.add(new RuneBaseAttribute((short) (attributeType.ordinal() + idOffset), values.get(attributeType).shortValue()));
}
}
public static void LoadAllAttributes() {
DbManager.RuneBaseAttributeQueries.GET_ATTRIBUTES_FOR_RUNEBASE();
+2 -2
View File
@@ -256,13 +256,13 @@ public class Shrine extends AbstractWorldObject implements Comparable<Shrine> {
if (!boonOwner.charItemManager.doesCharOwnThisItem(boonItem.getObjectUUID()))
return false;
for (ShrineType boonShrineType : boonItem.template.item_offering_info.keySet()) {
for (ShrineType boonShrineType : boonItem.template.getItemOfferingInfo().keySet()) {
if (boonShrineType != shrineType)
continue;
//Same Shrine Type, add favors and stop loop.
int amount = boonItem.template.item_offering_info.get(boonShrineType) * boonItem.getNumOfItems();
int amount = boonItem.template.getItemOfferingInfo().get(boonShrineType) * boonItem.getNumOfItems();
int oldAmount = this.favors;
if (!DbManager.ShrineQueries.updateFavors(this, this.favors + amount, oldAmount)) {
+7 -6
View File
@@ -17,6 +17,7 @@ import engine.mbEnums;
import engine.net.Dispatch;
import engine.net.client.ClientConnection;
import engine.net.client.msg.*;
import engine.parsers.objects.CObjectTemplate;
import engine.server.MBServerStatics;
import org.joda.time.DateTime;
import org.json.JSONArray;
@@ -304,10 +305,10 @@ public class Warehouse {
return false;
}
if (resource.template.item_type.equals(mbEnums.ItemType.GOLD))
if (resource.template.getItemType().equals(mbEnums.ItemType.GOLD))
resourceType = mbEnums.ResourceType.GOLD;
else
resourceType = mbEnums.ResourceType.valueOf(ItemTemplate.templates.get(resource.templateID).item_base_name.toUpperCase());
resourceType = mbEnums.ResourceType.valueOf(CObjectTemplate.lookup(resource.templateID).getItemBaseName().toUpperCase());
if (transaction)
AddTransactionToWarehouse(warehouse, playerCharacter.getObjectType(), playerCharacter.getObjectUUID(), mbEnums.TransactionType.DEPOSIT, resourceType, amount);
@@ -418,7 +419,7 @@ public class Warehouse {
if (playerCharacter == null)
return false;
ItemTemplate template = ItemTemplate.templates.get(resourceType.templateID);
CObjectTemplate template = CObjectTemplate.lookup(resourceType.templateID);
if (warehouse.resources.get(resourceType) == null)
return false;
@@ -432,7 +433,7 @@ public class Warehouse {
return false;
if (addToInventory)
if (!itemMan.hasRoomInventory(template.item_wt)) {
if (!itemMan.hasRoomInventory(template.getItemWt())) {
ChatManager.chatSystemInfo(playerCharacter, "You can not carry any more of that item.");
return false;
}
@@ -501,7 +502,7 @@ public class Warehouse {
if (playerCharacter == null)
return false;
ItemTemplate template = ItemTemplate.templates.get(resourceType.templateID);
CObjectTemplate template = CObjectTemplate.lookup(resourceType.templateID);
if (template == null)
return false;
@@ -517,7 +518,7 @@ public class Warehouse {
if (itemMan == null)
return false;
if (!itemMan.hasRoomInventory(template.item_wt)) {
if (!itemMan.hasRoomInventory(template.getItemWt())) {
ChatManager.chatSystemInfo(playerCharacter, "You can not carry any more of that item.");
return false;
}
+6
View File
@@ -97,8 +97,14 @@ public class DeedParser {
deed.item_type = CacheParserUtil.readEnum(mbEnums.ItemType.class, deedJson.getString("item_type"), templateId, "item_type");
deed.item_eq_slots_value = deedJson.getInt("item_eq_slots_value");
deed.item_eq_slots_type = deedJson.getBoolean("item_eq_slots_type");
// The exporter writes ["NONE"] for an empty slot mask
deed.item_eq_slots_or.addAll(CacheParserUtil.readEnumSet(mbEnums.EquipSlotType.class, deedJson.getJSONArray("item_eq_slots_or"), templateId, "item_eq_slots_or"));
deed.item_eq_slots_or.remove(mbEnums.EquipSlotType.NONE);
deed.item_eq_slots_and.addAll(CacheParserUtil.readEnumSet(mbEnums.EquipSlotType.class, deedJson.getJSONArray("item_eq_slots_and"), templateId, "item_eq_slots_and"));
deed.item_eq_slots_and.remove(mbEnums.EquipSlotType.NONE);
deed.item_takeable = deedJson.getBoolean("item_takeable");
deed.item_value = deedJson.getInt("item_value");
deed.item_wt = deedJson.getInt("item_wt");
-34
View File
@@ -9,16 +9,10 @@
package engine.parsers.cache;
import engine.gameManager.ConfigManager;
import engine.gameManager.DbManager;
import engine.mbEnums;
import engine.objects.AbstractGameObject;
import engine.objects.BaseClass;
import engine.parsers.objects.RuneTemplate;
import org.pmw.tinylog.Logger;
import java.util.LinkedHashMap;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
// Dev-only check that compares DB-built objects with the cache JSON before a loader is switched.
@@ -34,10 +28,6 @@ public class ParityCheck {
private int differenceCount = 0;
private int missingCount = 0;
static {
register("static_rune_baseclass", ParityCheck::compareBaseClasses);
}
private ParityCheck(String table) {
this.table = table;
}
@@ -91,28 +81,4 @@ public class ParityCheck {
differenceCount++;
Logger.warn("Parity: " + table + "/" + id + "/" + field + ": db=" + dbValue + " json=" + jsonValue);
}
private static void compareBaseClasses(ParityCheck check) {
ConcurrentHashMap<Integer, AbstractGameObject> cache;
BaseClass baseClass;
RuneTemplate rune;
cache = DbManager.getMap(mbEnums.GameObjectType.BaseClass);
if (cache == null) {
Logger.warn("Parity: static_rune_baseclass: no BaseClass objects loaded");
return;
}
for (AbstractGameObject cached : cache.values()) {
baseClass = (BaseClass) cached;
rune = RuneTemplate._runes.get(baseClass.getObjectUUID());
if (!check.compareRow(baseClass.getObjectUUID(), rune))
continue;
check.compareField(baseClass.getObjectUUID(), "name", baseClass.getName(), rune.obj_name);
}
}
}
+6
View File
@@ -112,8 +112,14 @@ public class RuneParser {
rune.item_type = CacheParserUtil.readEnum(mbEnums.ItemType.class, runeJson.getString("item_type"), templateId, "item_type");
rune.item_eq_slots_value = runeJson.getInt("item_eq_slots_value");
rune.item_eq_slots_type = runeJson.getBoolean("item_eq_slots_type");
// The exporter writes ["NONE"] for an empty slot mask
rune.item_eq_slots_or.addAll(CacheParserUtil.readEnumSet(mbEnums.EquipSlotType.class, runeJson.getJSONArray("item_eq_slots_or"), templateId, "item_eq_slots_or"));
rune.item_eq_slots_or.remove(mbEnums.EquipSlotType.NONE);
rune.item_eq_slots_and.addAll(CacheParserUtil.readEnumSet(mbEnums.EquipSlotType.class, runeJson.getJSONArray("item_eq_slots_and"), templateId, "item_eq_slots_and"));
rune.item_eq_slots_and.remove(mbEnums.EquipSlotType.NONE);
rune.item_takeable = runeJson.getBoolean("item_takeable");
rune.item_value = runeJson.getInt("item_value");
rune.item_wt = runeJson.getInt("item_wt");
@@ -8,8 +8,92 @@
package engine.parsers.objects;
import engine.math.Vector3fImmutable;
import engine.mbEnums;
import engine.parsers.cache.CObjectIndex;
import org.json.JSONArray;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
// Common type for ITEM, DEED and RUNE cache templates, which share one id space.
// Shared obj_/combat_/item_ getters arrive with JSON-Refactor-Plan Task 1.1.
// Getters cover only the obj_/combat_/item_ fields all three define (JSON-Refactor-Plan D1).
// Returned collections are the template's own; never modify them.
public interface CObjectTemplate {
static CObjectTemplate lookup(int templateId) {
return CObjectIndex.lookup(templateId);
}
int getTemplateId();
String getObjName();
boolean getObjPickable();
float getObjGravity();
float getObjCullDistance();
Vector3fImmutable getObjScale();
int getObjRenderObject();
boolean getObjDoubleFusion();
Vector3fImmutable getObjForwardVector();
String getObjTrackingName();
float getObjMaxTrackingDistance();
int getObjIcon();
float getObjGravityF();
LinkedHashMap<String, Integer> getObjSoundEvents();
JSONArray getObjArcHardpointList();
HashMap<String, String> getObjSparseData();
int getObjRenderObjectLowDetail();
Vector3fImmutable getObjDefaultAlignment();
String getObjSoundTable();
float getCombatHealthCurrent();
float getCombatHealthFull();
EnumMap<mbEnums.DamageType, Float> getCombatAttackResist();
JSONArray getCombatPowers();
mbEnums.ItemType getItemType();
int getItemEqSlotsValue();
boolean getItemEqSlotsType();
EnumSet<mbEnums.EquipSlotType> getItemEqSlotsOr();
EnumSet<mbEnums.EquipSlotType> getItemEqSlotsAnd();
boolean getItemTakeable();
int getItemValue();
int getItemWt();
float getItemPassiveDefenseMod();
String getItemBaseName();
String getItemDsc();
int getItemRenderObjectFemale();
float getItemHealthFull();
String getItemSkillUsed();
String getItemSkillMasteryUsed();
int getItemParryAnimId();
EnumMap<mbEnums.ShrineType, Integer> getItemOfferingInfo();
EnumSet<mbEnums.ItemFlags> getItemFlags();
EnumSet<mbEnums.ItemUseFlags> getItemUseFlags();
int getItemPostItemId();
int getItemInitialCharges();
int getItemBookArcana();
LinkedHashMap<String, Integer> getItemSkillReq();
boolean getItemRaceReqRestrict();
EnumSet<mbEnums.MonsterType> getItemRaceReq();
boolean getItemClassReqRestrict();
EnumSet<mbEnums.ClassType> getItemClassReq();
boolean getItemDiscReqRestrict();
EnumSet<mbEnums.DisciplineType> getItemDiscReq();
EnumMap<mbEnums.AttributeType, Integer> getItemAttrReq();
int getItemLevelReq();
int getItemRankReq();
mbEnums.SexType getItemSexReq();
ArrayList<UserPowerAction> getItemUserPowerAction();
ArrayList<PowerGrant> getItemPowerGrant();
LinkedHashMap<String, ArrayList<int[]>> getItemPowerAction();
boolean getItemSheathable();
boolean getItemHasStub();
EnumMap<mbEnums.ShrineType, Integer> getItemOfferingAdjustments();
EnumMap<mbEnums.ResourceType, Integer> getItemResourceCosts();
int getItemBaneRank();
boolean getItemIgnoreSavedActions();
}
@@ -119,4 +119,329 @@ public class DeedTemplate implements CObjectTemplate {
public float deed_namelookup_val;
public boolean deed_custom_city;
public int deed_structure_id;
@Override
public int getTemplateId() {
return template_id;
}
@Override
public String getObjName() {
return obj_name;
}
@Override
public boolean getObjPickable() {
return obj_pickable;
}
@Override
public float getObjGravity() {
return obj_gravity;
}
@Override
public float getObjCullDistance() {
return obj_cull_distance;
}
@Override
public Vector3fImmutable getObjScale() {
return obj_scale;
}
@Override
public int getObjRenderObject() {
return obj_render_object;
}
@Override
public boolean getObjDoubleFusion() {
return obj_double_fusion;
}
@Override
public Vector3fImmutable getObjForwardVector() {
return obj_forward_vector;
}
@Override
public String getObjTrackingName() {
return obj_tracking_name;
}
@Override
public float getObjMaxTrackingDistance() {
return obj_max_tracking_distance;
}
@Override
public int getObjIcon() {
return obj_icon;
}
@Override
public float getObjGravityF() {
return obj_gravity_f;
}
@Override
public LinkedHashMap<String, Integer> getObjSoundEvents() {
return obj_sound_events;
}
@Override
public JSONArray getObjArcHardpointList() {
return obj_arc_hardpoint_list;
}
@Override
public HashMap<String, String> getObjSparseData() {
return obj_sparse_data;
}
@Override
public int getObjRenderObjectLowDetail() {
return obj_render_object_low_detail;
}
@Override
public Vector3fImmutable getObjDefaultAlignment() {
return obj_default_alignment;
}
@Override
public String getObjSoundTable() {
return obj_sound_table;
}
@Override
public float getCombatHealthCurrent() {
return combat_health_current;
}
@Override
public float getCombatHealthFull() {
return combat_health_full;
}
@Override
public EnumMap<mbEnums.DamageType, Float> getCombatAttackResist() {
return combat_attack_resist;
}
@Override
public JSONArray getCombatPowers() {
return combat_powers;
}
@Override
public mbEnums.ItemType getItemType() {
return item_type;
}
@Override
public int getItemEqSlotsValue() {
return item_eq_slots_value;
}
@Override
public boolean getItemEqSlotsType() {
return item_eq_slots_type;
}
@Override
public EnumSet<mbEnums.EquipSlotType> getItemEqSlotsOr() {
return item_eq_slots_or;
}
@Override
public EnumSet<mbEnums.EquipSlotType> getItemEqSlotsAnd() {
return item_eq_slots_and;
}
@Override
public boolean getItemTakeable() {
return item_takeable;
}
@Override
public int getItemValue() {
return item_value;
}
@Override
public int getItemWt() {
return item_wt;
}
@Override
public float getItemPassiveDefenseMod() {
return item_passive_defense_mod;
}
@Override
public String getItemBaseName() {
return item_base_name;
}
@Override
public String getItemDsc() {
return item_dsc;
}
@Override
public int getItemRenderObjectFemale() {
return item_render_object_female;
}
@Override
public float getItemHealthFull() {
return item_health_full;
}
@Override
public String getItemSkillUsed() {
return item_skill_used;
}
@Override
public String getItemSkillMasteryUsed() {
return item_skill_mastery_used;
}
@Override
public int getItemParryAnimId() {
return item_parry_anim_id;
}
@Override
public EnumMap<mbEnums.ShrineType, Integer> getItemOfferingInfo() {
return item_offering_info;
}
@Override
public EnumSet<mbEnums.ItemFlags> getItemFlags() {
return item_flags;
}
@Override
public EnumSet<mbEnums.ItemUseFlags> getItemUseFlags() {
return item_use_flags;
}
@Override
public int getItemPostItemId() {
return item_post_item_id;
}
@Override
public int getItemInitialCharges() {
return item_initial_charges;
}
@Override
public int getItemBookArcana() {
return item_book_arcana;
}
@Override
public LinkedHashMap<String, Integer> getItemSkillReq() {
return item_skill_req;
}
@Override
public boolean getItemRaceReqRestrict() {
return item_race_req_restrict;
}
@Override
public EnumSet<mbEnums.MonsterType> getItemRaceReq() {
return item_race_req;
}
@Override
public boolean getItemClassReqRestrict() {
return item_class_req_restrict;
}
@Override
public EnumSet<mbEnums.ClassType> getItemClassReq() {
return item_class_req;
}
@Override
public boolean getItemDiscReqRestrict() {
return item_disc_req_restrict;
}
@Override
public EnumSet<mbEnums.DisciplineType> getItemDiscReq() {
return item_disc_req;
}
@Override
public EnumMap<mbEnums.AttributeType, Integer> getItemAttrReq() {
return item_attr_req;
}
@Override
public int getItemLevelReq() {
return item_level_req;
}
@Override
public int getItemRankReq() {
return item_rank_req;
}
@Override
public mbEnums.SexType getItemSexReq() {
return item_sex_req;
}
@Override
public ArrayList<UserPowerAction> getItemUserPowerAction() {
return item_user_power_action;
}
@Override
public ArrayList<PowerGrant> getItemPowerGrant() {
return item_power_grant;
}
@Override
public LinkedHashMap<String, ArrayList<int[]>> getItemPowerAction() {
return item_power_action;
}
@Override
public boolean getItemSheathable() {
return item_sheathable;
}
@Override
public boolean getItemHasStub() {
return item_has_stub;
}
@Override
public EnumMap<mbEnums.ShrineType, Integer> getItemOfferingAdjustments() {
return item_offering_adjustments;
}
@Override
public EnumMap<mbEnums.ResourceType, Integer> getItemResourceCosts() {
return item_resource_costs;
}
@Override
public int getItemBaneRank() {
return item_bane_rank;
}
@Override
public boolean getItemIgnoreSavedActions() {
return item_ignore_saved_actions;
}
}
+340 -4
View File
@@ -25,14 +25,15 @@ import java.util.LinkedHashMap;
// Values are kept as the client defines them: no req/res splitting,
// no defaults substituted. Strings with a matching enum in mbEnums
// are stored as that enum; everything else stays a String.
//
// Not to be confused with engine.objects.ItemTemplate, which is loaded
// from the database. Read only once loaded.
// Read only once loaded.
public class ItemTemplate implements CObjectTemplate {
public static HashMap<Integer, ItemTemplate> _items = new HashMap<>();
// Legacy templates zero-filled the weapon block on non-weapons; held shields and the like rely on it.
private static final Weapon NO_WEAPON = new Weapon();
public int template_id;
// obj_
@@ -138,6 +139,341 @@ public class ItemTemplate implements CObjectTemplate {
public int item_bane_rank;
public boolean item_ignore_saved_actions;
@Override
public int getTemplateId() {
return template_id;
}
@Override
public String getObjName() {
return obj_name;
}
@Override
public boolean getObjPickable() {
return obj_pickable;
}
@Override
public float getObjGravity() {
return obj_gravity;
}
@Override
public float getObjCullDistance() {
return obj_cull_distance;
}
@Override
public Vector3fImmutable getObjScale() {
return obj_scale;
}
@Override
public int getObjRenderObject() {
return obj_render_object;
}
@Override
public boolean getObjDoubleFusion() {
return obj_double_fusion;
}
@Override
public Vector3fImmutable getObjForwardVector() {
return obj_forward_vector;
}
@Override
public String getObjTrackingName() {
return obj_tracking_name;
}
@Override
public float getObjMaxTrackingDistance() {
return obj_max_tracking_distance;
}
@Override
public int getObjIcon() {
return obj_icon;
}
@Override
public float getObjGravityF() {
return obj_gravity_f;
}
@Override
public LinkedHashMap<String, Integer> getObjSoundEvents() {
return obj_sound_events;
}
@Override
public JSONArray getObjArcHardpointList() {
return obj_arc_hardpoint_list;
}
@Override
public HashMap<String, String> getObjSparseData() {
return obj_sparse_data;
}
@Override
public int getObjRenderObjectLowDetail() {
return obj_render_object_low_detail;
}
@Override
public Vector3fImmutable getObjDefaultAlignment() {
return obj_default_alignment;
}
@Override
public String getObjSoundTable() {
return obj_sound_table;
}
@Override
public float getCombatHealthCurrent() {
return combat_health_current;
}
@Override
public float getCombatHealthFull() {
return combat_health_full;
}
@Override
public EnumMap<mbEnums.DamageType, Float> getCombatAttackResist() {
return combat_attack_resist;
}
@Override
public JSONArray getCombatPowers() {
return combat_powers;
}
@Override
public mbEnums.ItemType getItemType() {
return item_type;
}
@Override
public int getItemEqSlotsValue() {
return item_eq_slots_value;
}
@Override
public boolean getItemEqSlotsType() {
return item_eq_slots_type;
}
@Override
public EnumSet<mbEnums.EquipSlotType> getItemEqSlotsOr() {
return item_eq_slots_or;
}
@Override
public EnumSet<mbEnums.EquipSlotType> getItemEqSlotsAnd() {
return item_eq_slots_and;
}
@Override
public boolean getItemTakeable() {
return item_takeable;
}
@Override
public int getItemValue() {
return item_value;
}
@Override
public int getItemWt() {
return item_wt;
}
@Override
public float getItemPassiveDefenseMod() {
return item_passive_defense_mod;
}
@Override
public String getItemBaseName() {
return item_base_name;
}
@Override
public String getItemDsc() {
return item_dsc;
}
@Override
public int getItemRenderObjectFemale() {
return item_render_object_female;
}
@Override
public float getItemHealthFull() {
return item_health_full;
}
@Override
public String getItemSkillUsed() {
return item_skill_used;
}
@Override
public String getItemSkillMasteryUsed() {
return item_skill_mastery_used;
}
@Override
public int getItemParryAnimId() {
return item_parry_anim_id;
}
@Override
public EnumMap<mbEnums.ShrineType, Integer> getItemOfferingInfo() {
return item_offering_info;
}
@Override
public EnumSet<mbEnums.ItemFlags> getItemFlags() {
return item_flags;
}
@Override
public EnumSet<mbEnums.ItemUseFlags> getItemUseFlags() {
return item_use_flags;
}
@Override
public int getItemPostItemId() {
return item_post_item_id;
}
@Override
public int getItemInitialCharges() {
return item_initial_charges;
}
@Override
public int getItemBookArcana() {
return item_book_arcana;
}
@Override
public LinkedHashMap<String, Integer> getItemSkillReq() {
return item_skill_req;
}
@Override
public boolean getItemRaceReqRestrict() {
return item_race_req_restrict;
}
@Override
public EnumSet<mbEnums.MonsterType> getItemRaceReq() {
return item_race_req;
}
@Override
public boolean getItemClassReqRestrict() {
return item_class_req_restrict;
}
@Override
public EnumSet<mbEnums.ClassType> getItemClassReq() {
return item_class_req;
}
@Override
public boolean getItemDiscReqRestrict() {
return item_disc_req_restrict;
}
@Override
public EnumSet<mbEnums.DisciplineType> getItemDiscReq() {
return item_disc_req;
}
@Override
public EnumMap<mbEnums.AttributeType, Integer> getItemAttrReq() {
return item_attr_req;
}
@Override
public int getItemLevelReq() {
return item_level_req;
}
@Override
public int getItemRankReq() {
return item_rank_req;
}
@Override
public mbEnums.SexType getItemSexReq() {
return item_sex_req;
}
@Override
public ArrayList<UserPowerAction> getItemUserPowerAction() {
return item_user_power_action;
}
@Override
public ArrayList<PowerGrant> getItemPowerGrant() {
return item_power_grant;
}
@Override
public LinkedHashMap<String, ArrayList<int[]>> getItemPowerAction() {
return item_power_action;
}
@Override
public boolean getItemSheathable() {
return item_sheathable;
}
@Override
public boolean getItemHasStub() {
return item_has_stub;
}
@Override
public EnumMap<mbEnums.ShrineType, Integer> getItemOfferingAdjustments() {
return item_offering_adjustments;
}
@Override
public EnumMap<mbEnums.ResourceType, Integer> getItemResourceCosts() {
return item_resource_costs;
}
@Override
public int getItemBaneRank() {
return item_bane_rank;
}
@Override
public boolean getItemIgnoreSavedActions() {
return item_ignore_saved_actions;
}
public static Weapon getWeapon(int templateID) {
ItemTemplate template = _items.get(templateID);
if (template == null || template.item_weapon == null)
return NO_WEAPON;
return template.item_weapon;
}
// item_weapon (ArcAttackInfo_LoadWeapon, sb.exe 0x61FA60).
public static class Weapon {
@@ -149,7 +485,7 @@ public class ItemTemplate implements CObjectTemplate {
public int weapon_combat_idle_anim;
public final ArrayList<Damage> weapon_damage = new ArrayList<>();
// [animation, duration] pairs in file order; names from engine.objects.ItemTemplate, not the client.
// [animation, duration] pairs in file order; names from the old static_item_templates export, not the client.
public final ArrayList<int[]> weapon_attack_anim_right = new ArrayList<>();
public final ArrayList<int[]> weapon_attack_anim_left = new ArrayList<>();
}
+326 -1
View File
@@ -141,7 +141,7 @@ public class RuneTemplate implements CObjectTemplate {
public String rune_dsc;
public String rune_fx_txt;
// Bit vectors. The existing ItemTemplate decodes these as MobBehaviourType.
// Bit vectors. The old static_item_templates export decoded these as MobBehaviourType.
public long rune_group_tactics;
public long rune_group_role_set;
@@ -191,6 +191,331 @@ public class RuneTemplate implements CObjectTemplate {
public int rune_natural_power_attack;
public final HashMap<String, String> rune_sparse_data = new HashMap<>();
@Override
public int getTemplateId() {
return template_id;
}
@Override
public String getObjName() {
return obj_name;
}
@Override
public boolean getObjPickable() {
return obj_pickable;
}
@Override
public float getObjGravity() {
return obj_gravity;
}
@Override
public float getObjCullDistance() {
return obj_cull_distance;
}
@Override
public Vector3fImmutable getObjScale() {
return obj_scale;
}
@Override
public int getObjRenderObject() {
return obj_render_object;
}
@Override
public boolean getObjDoubleFusion() {
return obj_double_fusion;
}
@Override
public Vector3fImmutable getObjForwardVector() {
return obj_forward_vector;
}
@Override
public String getObjTrackingName() {
return obj_tracking_name;
}
@Override
public float getObjMaxTrackingDistance() {
return obj_max_tracking_distance;
}
@Override
public int getObjIcon() {
return obj_icon;
}
@Override
public float getObjGravityF() {
return obj_gravity_f;
}
@Override
public LinkedHashMap<String, Integer> getObjSoundEvents() {
return obj_sound_events;
}
@Override
public JSONArray getObjArcHardpointList() {
return obj_arc_hardpoint_list;
}
@Override
public HashMap<String, String> getObjSparseData() {
return obj_sparse_data;
}
@Override
public int getObjRenderObjectLowDetail() {
return obj_render_object_low_detail;
}
@Override
public Vector3fImmutable getObjDefaultAlignment() {
return obj_default_alignment;
}
@Override
public String getObjSoundTable() {
return obj_sound_table;
}
@Override
public float getCombatHealthCurrent() {
return combat_health_current;
}
@Override
public float getCombatHealthFull() {
return combat_health_full;
}
@Override
public EnumMap<mbEnums.DamageType, Float> getCombatAttackResist() {
return combat_attack_resist;
}
@Override
public JSONArray getCombatPowers() {
return combat_powers;
}
@Override
public mbEnums.ItemType getItemType() {
return item_type;
}
@Override
public int getItemEqSlotsValue() {
return item_eq_slots_value;
}
@Override
public boolean getItemEqSlotsType() {
return item_eq_slots_type;
}
@Override
public EnumSet<mbEnums.EquipSlotType> getItemEqSlotsOr() {
return item_eq_slots_or;
}
@Override
public EnumSet<mbEnums.EquipSlotType> getItemEqSlotsAnd() {
return item_eq_slots_and;
}
@Override
public boolean getItemTakeable() {
return item_takeable;
}
@Override
public int getItemValue() {
return item_value;
}
@Override
public int getItemWt() {
return item_wt;
}
@Override
public float getItemPassiveDefenseMod() {
return item_passive_defense_mod;
}
@Override
public String getItemBaseName() {
return item_base_name;
}
@Override
public String getItemDsc() {
return item_dsc;
}
@Override
public int getItemRenderObjectFemale() {
return item_render_object_female;
}
@Override
public float getItemHealthFull() {
return item_health_full;
}
@Override
public String getItemSkillUsed() {
return item_skill_used;
}
@Override
public String getItemSkillMasteryUsed() {
return item_skill_mastery_used;
}
@Override
public int getItemParryAnimId() {
return item_parry_anim_id;
}
@Override
public EnumMap<mbEnums.ShrineType, Integer> getItemOfferingInfo() {
return item_offering_info;
}
@Override
public EnumSet<mbEnums.ItemFlags> getItemFlags() {
return item_flags;
}
@Override
public EnumSet<mbEnums.ItemUseFlags> getItemUseFlags() {
return item_use_flags;
}
@Override
public int getItemPostItemId() {
return item_post_item_id;
}
@Override
public int getItemInitialCharges() {
return item_initial_charges;
}
@Override
public int getItemBookArcana() {
return item_book_arcana;
}
@Override
public LinkedHashMap<String, Integer> getItemSkillReq() {
return item_skill_req;
}
@Override
public boolean getItemRaceReqRestrict() {
return item_race_req_restrict;
}
@Override
public EnumSet<mbEnums.MonsterType> getItemRaceReq() {
return item_race_req;
}
@Override
public boolean getItemClassReqRestrict() {
return item_class_req_restrict;
}
@Override
public EnumSet<mbEnums.ClassType> getItemClassReq() {
return item_class_req;
}
@Override
public boolean getItemDiscReqRestrict() {
return item_disc_req_restrict;
}
@Override
public EnumSet<mbEnums.DisciplineType> getItemDiscReq() {
return item_disc_req;
}
@Override
public EnumMap<mbEnums.AttributeType, Integer> getItemAttrReq() {
return item_attr_req;
}
@Override
public int getItemLevelReq() {
return item_level_req;
}
@Override
public int getItemRankReq() {
return item_rank_req;
}
@Override
public mbEnums.SexType getItemSexReq() {
return item_sex_req;
}
@Override
public ArrayList<UserPowerAction> getItemUserPowerAction() {
return item_user_power_action;
}
@Override
public ArrayList<PowerGrant> getItemPowerGrant() {
return item_power_grant;
}
@Override
public LinkedHashMap<String, ArrayList<int[]>> getItemPowerAction() {
return item_power_action;
}
@Override
public boolean getItemSheathable() {
return item_sheathable;
}
@Override
public boolean getItemHasStub() {
return item_has_stub;
}
@Override
public EnumMap<mbEnums.ShrineType, Integer> getItemOfferingAdjustments() {
return item_offering_adjustments;
}
@Override
public EnumMap<mbEnums.ResourceType, Integer> getItemResourceCosts() {
return item_resource_costs;
}
@Override
public int getItemBaneRank() {
return item_bane_rank;
}
@Override
public boolean getItemIgnoreSavedActions() {
return item_ignore_saved_actions;
}
// One entry of rune_skill_grant; same layout as PowerGrant.
// skill_monster_types is empty in every RUNE file, so it is kept as raw JSON.
@@ -63,7 +63,7 @@ public class StealPowerAction extends AbstractPowerAction {
//called to get amount of gold to steal between 0 and max gold
protected static int getAmountToSteal(Item item) {
if (item.template.item_type.equals(ItemType.GOLD)) {
if (item.template.getItemType().equals(ItemType.GOLD)) {
int amount = item.getNumOfItems();
@@ -159,7 +159,7 @@ public class StealPowerAction extends AbstractPowerAction {
if (myCIM == null || ownerCIM == null)
return;
if (tar.template.item_type.equals(ItemType.GOLD)) {
if (tar.template.getItemType().equals(ItemType.GOLD)) {
//stealing gold
if (!myCIM.transferGoldToMyInventory((AbstractCharacter) owner, amount))
return;
+4 -7
View File
@@ -26,6 +26,7 @@ import engine.parsers.cache.DeedParser;
import engine.parsers.cache.ItemParser;
import engine.parsers.cache.RuneParser;
import engine.parsers.objects.DeedTemplate;
import engine.parsers.objects.ItemTemplate;
import engine.parsers.objects.RuneTemplate;
import engine.server.MBServerStatics;
import engine.server.world.WorldServer;
@@ -287,9 +288,6 @@ public class LoginServer {
Logger.info("Initializing Database layer");
initDatabaseLayer();
Logger.info("Loading Item Templates");
DbManager.ItemQueries.LOAD_ITEM_TEMPLATES();
Logger.info("Loading Deed Templates");
DeedTemplate._deeds = DeedParser.parseDeedFolder();
@@ -306,11 +304,10 @@ public class LoginServer {
exit(1);
}
// Qualified: engine.objects.ItemTemplate is the database-loaded class
Logger.info("Loading Cache Item Templates");
engine.parsers.objects.ItemTemplate._items = ItemParser.parseItemFolder();
ItemTemplate._items = ItemParser.parseItemFolder();
if (engine.parsers.objects.ItemTemplate._items == null || engine.parsers.objects.ItemTemplate._items.isEmpty()) {
if (ItemTemplate._items == null || ItemTemplate._items.isEmpty()) {
Logger.error("ABORT! Cache item templates failed to load!");
exit(1);
}
@@ -334,9 +331,9 @@ public class LoginServer {
Logger.info("Initializing PowersManager.");
PowersManager.initPowersManager(false);
RuneBaseAttribute.LoadAllAttributes();
RuneBase.LoadAllRuneBases();
BaseClass.LoadAllBaseClasses();
PromotionClass.LoadAllPromotions();
Race.loadAllRaces();
RuneBaseEffect.LoadRuneBaseEffects();
+6 -10
View File
@@ -37,6 +37,7 @@ import engine.parsers.cache.ItemParser;
import engine.parsers.cache.ParityCheck;
import engine.parsers.cache.RuneParser;
import engine.parsers.objects.DeedTemplate;
import engine.parsers.objects.ItemTemplate;
import engine.parsers.objects.RuneTemplate;
import engine.server.MBServerStatics;
import engine.util.MapLoader;
@@ -304,11 +305,8 @@ public class WorldServer {
Logger.info("Initializing MeshBounds");
MeshBounds.InitializeBuildingBounds();
Logger.info("Loading Item Templates");
DbManager.ItemQueries.LOAD_ITEM_TEMPLATES();
Logger.info("Back fill template modTables");
DbManager.ItemQueries.LOAD_TEMPLATE_MODTABLES();
Logger.info("Loading vendor modTables");
ForgeManager.vendorModTables = DbManager.ItemQueries.LOAD_TEMPLATE_MODTABLES();
Logger.info("Loading Deed Templates");
DeedTemplate._deeds = DeedParser.parseDeedFolder();
@@ -326,11 +324,10 @@ public class WorldServer {
exit(1);
}
// Qualified: engine.objects.ItemTemplate is the database-loaded class
Logger.info("Loading Cache Item Templates");
engine.parsers.objects.ItemTemplate._items = ItemParser.parseItemFolder();
ItemTemplate._items = ItemParser.parseItemFolder();
if (engine.parsers.objects.ItemTemplate._items == null || engine.parsers.objects.ItemTemplate._items.isEmpty()) {
if (ItemTemplate._items == null || ItemTemplate._items.isEmpty()) {
Logger.error("ABORT! Cache item templates failed to load!");
exit(1);
}
@@ -339,7 +336,7 @@ public class WorldServer {
CObjectIndex.build();
Logger.info("Loading PromotionClasses");
DbManager.PromotionQueries.GET_ALL_PROMOTIONS();
PromotionClass.LoadAllPromotions();
Logger.info("Loading NPC and Mob Rune Sets");
NPCManager._runeSetMap = dbNPCHandler.LOAD_RUNES_FOR_NPC_AND_MOBS();
@@ -351,7 +348,6 @@ public class WorldServer {
Logger.info("Initializing Loot Manager");
LootManager.init();
RuneBaseAttribute.LoadAllAttributes();
RuneBase.LoadAllRuneBases();
BaseClass.LoadAllBaseClasses();
Race.loadAllRaces();