Commit Graph
3452 Commits
Author SHA1 Message Date
MagicBotandClaude Opus 5.5 807a806a0d Check building access on every vendor production action
ItemProductionMsgHandler trusted the client to send production, recycle,
junk, deposit, price and take requests only from an open management
window. It now applies the window's own rule (owner, guild leader or IC,
or building friends) before any action.

Value changes: none for players who can open the window; others are
silently ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 21:50:31 -04:00
MagicBotandClaude Opus 5.5 4b50a4f74b Skip the strongbox of NPC buildings when forging and recycling
Forge submit and vendor recycle took gold from, or refunded gold to, the
vendor building's strongbox with no protection check. NPC-city vendors
could roll on the static building's 2000 gold. Vendor buy, repair and train
already skip NPC buildings.

Value changes: forging at a vendor in a protection-NPC building now fails
with popup 58; recycling there does nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 21:38:05 -04:00
MagicBotandClaude Opus 5.5 afb911cb72 Reword the static guild hash comment
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 21:16:06 -04:00
MagicBotandClaude Opus 5.5 42a6070d76 Load static NPC guilds from the guild seed at world server boot
GuildManager.loadStaticGuilds builds the map's NPC guilds from
maps/<MB_WORLD_MAPID>.guild through Guild.createStaticGuild, before zones,
so they take the first static IDs (-2 down, file order). Each is flagged
static, is its own nation (as live and in the DB) and is indexed by its
CZONE guild and nation keys. Boot aborts on a missing or bad seed file or a
duplicate key. After zones load, every CITY-prop guild key with no seed
entry is logged once with its zones; known no-guild keys are only counted.

Nothing references the static guilds yet: NPC cities, NPCs and players
still use the DB guilds (UID 2-25) until 2.5, so there is no gameplay change.

Seed vs the 24 DB NPC guilds (scripts/task16_guild_seed.py): name, crest,
leadership type, motto and repledge/teleport limits identical. Charter
differs on all 24: DB 562 (BARBARIAN placeholder) -> guilddata value, read
as the client guild type index (sb.exe g_GuildTypeNames 0x175B8E0), e.g.
Kingscross NOBLE, Bastion CATHEDRAL, Khar Th'Sekt VIRAKT, hamlets MILITARY.
Static guilds have no warehouse hash yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 21:15:09 -04:00
MagicBotandClaude Opus 5.5 4839883530 Parse the map guild seed file (maps/<map>.guild)
GuildSeedParser reads the server-owned NPC guild seed (W7): guilds in
static ID order, each with its CZONE city_prop_gm_guild keys, nation key,
name, crest, charter (GuildCharterType name), leadership type, motto and
repledge/teleport limits, plus the known no-guild keys. An unknown charter,
a guild with no keys or a crest without five values fails the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 21:15:09 -04:00
MagicBotandClaude Opus 5.5 21975e0bed Flush the log before a world server boot abort
tinylog's writing thread dropped queued lines on System.exit, so boot
aborts died with no error in the log. exitServer shuts the writing thread
down (flushing it) before exiting; every exit in WorldServer uses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 20:23:57 -04:00
MagicBotandClaude Opus 5.5 c66ce6e777 Build static zones from the zone tree at world server boot
ZoneManager.loadZones builds the 1,062 static zones from
maps/<MB_WORLD_MAPID>.tree through Zone.createStaticZone, flagged static
and keeping their positive tree UIDs (negative IDs come with W10 in 2.4).
Player-city zones still load from obj_zone (template 0). Boot aborts on a
missing tree file, a missing CZONE template or a missing parent.
GET_ALL_ZONES is kept until the removal task.

Value changes vs obj_zone (parity: scripts/task14_zone_parity.py):
- 44 zones: x/z offsets take the live values; obj_zone had them rounded
  to whole units (e.g. 279: 192.0 -> 192.48, 274: -238.0 -> -237.653).
- Static zones no longer write their hash to obj_zone at boot; the hash
  is computed in memory (same value).
- min/max level 30/60 for the 50 icon zones now comes from constants
  (no client data carries zone levels); same values as obj_zone.
Everything else identical: parent (except the unused root parent),
template, name, peace flag, y offset, icons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 20:07:21 -04:00
MagicBotandClaude Opus 5.5 f7b81f46a5 Parse the map zone tree file (maps/<map>.tree)
Reads uid, parent, template, name, isPlayerCity, offset, peaceZone and
the three icons. Skips childCount (derived), objectId (live NEWWORLD ID,
not needed yet) and extents (player-city only). A non-zero yaw, a missing
offset or a map ID that differs from MB_WORLD_MAPID fails the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 20:07:21 -04:00
MagicBotandClaude Opus 5.5 9419d02f42 Remove the unused ZoneTemplate class
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 19:28:58 -04:00
MagicBotandClaude Opus 5.5 4299b27422 Build zone templates from CZONE instead of static_zone_templates
ZoneManager._zone_templates now holds the parser's CZoneTemplate: every
CZONE template plus the player-city template 0, still built in code.
Zone and Terrain read the mbEnums sea level and terrain types instead of
strings. The static_zone_templates loader goes with the old map type;
the table stays until Task 4.2.

Parity: all 861 templates, every column the server read (type, name,
peace, radii, blends, sea level and type, water, terrain gen, type,
max_y, image) identical to the JSON. Value changes: none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 19:28:58 -04:00
MagicBotandClaude Opus 5.5 ac1fcaecf5 Parse CZONE zone templates at world server boot
ZoneParser reads mb.data/CZONE/<id>.json into CZoneTemplate with its
props, child props, mobiles and point sets (World-Boot-Plan Task 1.1).
Only the agreed server-needed fields are parsed; textures, water,
weather, sound and always-default fields are skipped. Boot aborts if
any file fails. Nothing uses the data yet (Task 1.2).

New mbEnums: ZoneShapeType, SeaLevelType, TerrainGenType, ZonePropType,
ZoneMobileType, PointSetType. root FSM maps to MobBehaviourType and
monster types to MonsterType.

Value changes: none (load only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 19:11:45 -04:00
MagicBotandClaude Opus 5.5 a8b8858f0e Revert the temporary /staticflag test command
Task 0.6 is tested; the command is no longer needed.

This reverts commit d295d044.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 19:03:01 -04:00
MagicBotandClaude Opus 5.5 d295d044bc TEMP: /staticflag toggles the static flag for testing Task 0.6
Revert after the 0.6 test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 18:58:02 -04:00
MagicBotandClaude Opus 5.5 bd3787f9d5 Refuse dev commands that would change static world objects
World Boot Task 0.6. Static objects are built from client data each boot,
so dev commands answer "Must use external tooling to modify gameworld" and
log the command, target and caller instead of changing them.

- /addbuilding, /mob and /npc always refuse (addsWorldContent hook in doCmd).
- rotate, pull, setbuildingaltitude, remove, rename, npcname, setowner,
  setguild, setrank and forcerename refuse a static target.
- purge skips static objects and reports how many it skipped.

Value changes: none for non-static objects; the purge message now also
reports the skipped count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 18:57:40 -04:00
MagicBotandClaude Opus 5.5 49ad45d179 Show "none" for the database ID of unsaved mobs and NPCs in /info
Pets, guard and siege minions have no DB row and printed
NO_DB_ROW_ASSIGNED_YET (2147483647).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 18:40:32 -04:00
MagicBotandClaude Opus 5.5 731425e85a Refuse DB writes for static world objects
DbManager.refuseStaticWrite logs the handler method and its caller and
stops the write (W3). It guards every write whose row belongs to a
Building, Zone, City, Guild, Mob or NPC: 69 handler methods, including
friends/condemned/patrol lists, guard minions, NPC profits and guild
banish/alliance lists. ID-only methods look the object up in the cache.
Writes that only reference a static object (character guild history,
another guild's alliance row) are not guarded.

Value changes: none; nothing is flagged static yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 07:45:31 -04:00
MagicBotandClaude Opus 5.5 c89bda35da Add the static world ID counter and static flag
Static world objects take IDs from DbManager.nextStaticID(), counting down
from -2 in a fixed load order (W2). Runtime negative IDs (loot, forged and
contract items, pets and minions) now share ItemManager.lastNegativeID,
which starts at -1,000,000; Mob.staticID is gone. Boot fails if the static
counter reaches the runtime range. AbstractWorldObject gets a
staticWorldObject flag for the factories to set. Nothing uses either yet.

Value changes: runtime negative IDs start at -1,000,001 (-1,000,000 for
forged items) instead of -1; pets and minions draw from the item counter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 07:43:17 -04:00
MagicBotandClaude Opus 5.5 740719aeb2 Remove the rotation parity check
The boot run was clean: 187,824 building quaternion parts and 11,738 mob
yaws matched exactly. The two NPC reports (14876, 16700) were a flaw in the
replay, not the switch: their contract 2100 does not exist, so
NPC.runAfterLoad returns before slotting, and old and new both send pi/2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 06:50:32 -04:00
MagicBotandClaude Opus 5.5 bbf978872b Fix /rotate for NPCs, mobs and buildings past 180 degrees
/rotate took degrees but stored them on NPCs and mobs where the code expects
cos of half the yaw, so the client got NaN and the DB got degrees; /rotate
face passed radians the same way. Buildings used |cos| for w, which mirrored
any angle above 180 (270 became 90). Every kind now takes a yaw in radians
(degrees from the command, the facing angle for "face") through
Quaternion.fromYaw. NPC and mob yaws are wrapped to [0, 2 pi) so the stored
cos reloads to the same angle.

Value changes: /rotate N on NPCs/mobs now faces N degrees (was NaN);
buildings past 180 degrees now face the given angle; npc_rotation and
mob_rotation saved by /rotate hold cos of half the yaw instead of the raw
input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 06:43:28 -04:00
MagicBotandClaude Opus 5.5 e288524ec0 Add a boot rotation parity check for the quaternion switch
Under MB_PARITY_CHECK, after the mobs load, replays the old rotation code
(building w/rotY, npc_rotation as cos of half the yaw, mob default 0, the old
slot math and serializer) on the DB values and compares it with what the
loaded objects now send. Yaws are compared modulo 2 pi within 1e-5. Dev-only;
to be deleted once a run is clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 06:42:31 -04:00
MagicBotandClaude Opus 5.5 bb76596a9a Store Building, Mob and NPC rotation as a client-order quaternion
AbstractWorldObject.rot (a Vector3f holding one quaternion part) becomes a
Quaternion. Building drops its separate w field; Mob and NPC serialize
through AbstractCharacter.getClientYaw. DB columns are unchanged and are
converted at load and save: building (w, rotY) map to the quaternion's w and
y, npc_rotation (cos of half the yaw) becomes fromYaw(2 acos(value)).

Wire values are meant to be unchanged; a parity run follows. Behaviour kept
on purpose:
- mob_rotation is still not loaded, so every mob defaults to yaw pi as before
- the slot rotation still reads the product's sin as a cos
- /rotate on NPCs and mobs still treats its input as cos of half the yaw

Value changes: building serializer writes the quaternion's x and z instead
of literal zeros (both are 0 for every building); /info "Rot" for NPCs and
mobs prints the quaternion y (sin of half the yaw) instead of the stored cos.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 06:40:59 -04:00
MagicBotandClaude Opus 5.5 2078a5c344 Add Transform and client yaw helpers to Quaternion
Transform holds location, rotation and scale as the client's ArcObjectInfo
does, with parent x local composition (sb.exe 0x6516C0). Quaternion.fromYaw
matches the client's MakeRotateY and getYaw returns the angle it would need.
Nothing uses them yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 06:40:59 -04:00
MagicBotandClaude Opus 5.5 74b2eaaaf2 Read SetItemFlag, Track and TransferStat header flags as integers
The client parses these with String::ToInt(token) != 0, not ToBool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 20:41:30 -04:00
MagicBotandClaude Opus 5.5 c9474cf042 Read wpak booleans the way the client does
boolToken follows Core.dll String::ToBool with a false default instead of
throwing, and the five PowerActions keys the client compares to exact
"TRUE" use the new optionalExactTrueToken.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 20:33:10 -04:00
MagicBotandClaude Opus 5.5 99e384cfe1 Point the WpakLine number comment at the client's strtol/strtod parsers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 20:31:27 -04:00
MagicBotandClaude Opus 5.5 03399bece7 Clear the NPC building owner set after the NPC load
After boot the NPC cache answers ownerIsNPC, so the set is not kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:20:03 -04:00
MagicBotandClaude Opus 5.5 35f1738ee4 Read NPC building owners once instead of per building at boot
Building.runAfterLoad ran GET_UID_ENUM for every building (~47k queries).
One query now reads the NPC owner IDs before the building load; runtime
loads also check the NPC cache. City load reads ownerIsNPC before NPCs
are cached, so a pass after the NPC load would not work.

Value changes: none. ownerIsNPC is still true exactly when the owner's
object.type is 'npc' (dev DB: 388 buildings, 24 owners).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 19:16:53 -04:00
MagicBotandClaude Opus 5.5 015d536337 Remove CREATE_BUILDING
Every caller now goes through Building.createBuilding and persistBuilding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 16:02:17 -04:00
MagicBotandClaude Opus 5.5 d586536c74 Create player buildings in memory with a single DB write
Building gets a no-arg constructor, a createBuilding factory (in the style
of Mob.createMob) and persistBuilding, which inserts the row through
building_CREATE, sets the returned UID, caches the object and runs
runAfterLoad. The GET_BUILDINGBYUUID re-select after the insert is gone.

PlaceAssetMsgHandler (createStructure, createBarracks, createWarehouse),
Bane and AddBuildingCmd move onto it.

Value changes:
- floor/level start at -1 and the other unset fields at the obj_building
  column defaults, same as the reloaded row.
- upgradeDateTime keeps milliseconds instead of the DATETIME-rounded value
  (sub-second difference in the upgrade job time).
- Warehouse placement no longer runs runAfterLoad twice, which submitted
  the warehouse upgrade job twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 16:02:10 -04:00
MagicBotandClaude Opus 5.5 15437246ce Remove the DB power requirement loader
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 12:22:15 -04:00
MagicBotandClaude Opus 5.5 3f6c75b363 Import ErrorPopupMsg in RefineMsgHandler
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 12:09:01 -04:00
MagicBotandClaude Opus 5.5 9e50d0a5ad Build power requirements from rune item_power_grant
PowerReq lists now come from the rune JSON on world and login: level from
power_value, skill and power prereqs from power_granted_skills/powers.
The DB table is no longer read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 11:42:09 -04:00
MagicBotandClaude Opus 5.5 24e91cc0a9 Block skill refines that break a trained power's skill requirement
The old check looked up power requirements by power id instead of rune id,
so it never ran. Refining a skill below a trained power's requirement let
calculatePowers delete the power and its trains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 11:41:23 -04:00
MagicBotandClaude Opus 5.5 17d82066d6 Remove the DB power action list loader
Deletes ActionsBase.getActionsBase (static_power_action) and the
ResultSet constructor; the 4.9 switch (2a19a851) builds the lists from
Powers.cfg and was tested. No value change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 11:26:04 -04:00
MagicBotandClaude Opus 5.5 2a19a85108 Build power action lists from Powers.cfg ACTION= lines
ActionsBase(ActionEntry, powerID, id, powerAction) with the duration ramp
from CurveRamp; PowersManager.loadActionLists fills PowersBase.actions in
Powers.cfg order (ids 1..2278 + 10000), then the OPENGATE actions
(5001-5008, unchanged). StackType gains the 10 cfg stack types it lacked
and loses the DB-only ResistanceDebuff spelling.

Parity (JSON-Refactor-Plan/scripts/p49.py, dev export, every column):
2287 DB rows vs 2278 cfg lines; 1453/1455 powers have the same action
list; all curves have a ramp form, every action id exists.

Value changes (cfg wins):
- TRA-003..010 lose their DB OPENGATE row; the synthetic OPENGATE action
  was already added, so each gate power applied OPENGATE twice
- HFG-001 loses the DB-only third action HFG-001B (Str +25, attack
  delay -15, melee damage +15)
- SCT-004 applies SCT-004A (Speed SL0125Up, Buff/Effect) instead of
  RNG-033A (Speed SL0100Up, also Chant)
- durations: ALF-001A 180->60, COM-001A 60->30, FGM-002A/003A
  1800->900, RNG-033A 60->30
- stack priority: DEF/OFF/PRE/NOSTANCE A and B Always->GreaterThanOrEqualTo,
  BRD-016A and PRS-031A GreaterThan->GreaterThanOrEqualTo
- stack types: the 16 resist debuffs split from ResistanceDeBuff/
  ResistanceDebuff into Cold/Fire/Lightning/Holy/Unholy/Magic/Mental
  DeBuff (FUR-029A IgnoreStack->ElementalDeBuff), so debuffs of different
  elements now stack; HNT-023A..C StackableAttr*Buff->PersonalAttr*Buff
- IgnoreStack effect keys: action line ids follow cfg order (runtime only)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 10:20:34 -04:00
MagicBotandClaude Opus 5.5 f44f1ca138 Remove the DB power, cast prereq and power fail condition loaders
getAllPowersBase, getFailConditions, PowerPrereq.getAllPowerPrereqs and
the PowersBase/PowerPrereq ResultSet constructors. Powers come from
Powers.cfg since ac72d2c8 (tested).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 10:09:31 -04:00
MagicBotandClaude Opus 5.5 ac72d2c8d2 Build powers, cast prereqs and power fail conditions from Powers.cfg
PowersBase(PowerTemplate, id) and PowerPrereq template constructors;
PowersManager.loadPowers fills both maps on the world and login servers
(ids 1..1465 in file order, identical to the DB IDs). The 3.10 target
type exceptions and applyClientTargetTypes are gone; the dead
BLEED-DOT-10.5-RANGE token override is removed (the power is
BLEED-DOT-10.5-RANGED and its DB token is already its hash).

Parity (JSON-Refactor-Plan/scripts/p48.py, every column): 1465/1465 ids,
tokens = SBStringHash(id), flags 1465/1465, prereqs 720/720 rows, fail
conditions 103/105 powers. Absent MAXNUM*TARGETS stay 7 (patch notes).

Value changes (cfg wins, user 2026-10-26):
- target types HLR-025 PC->CORPSE, HNT-023 PC->SELF, RNG-033 GROUP->SELF
- HNT-023 SPELL->SKILL, target select CLICK->NONE
- cast time RNG-033 3->0.2, ROG-002 10->12, WIZ-067 6->7
- WIZ-067 release 1->0, init ramp 0->-0.13; SCT-004 cost ramp 0.5->0
- recycle ALF-001 300->180, FGM-002/003 600->900
- WLR-018 category HEAL->none
- BRD-016, PRS-031 lose the power CastSpell fail condition
- 4 name typos and 167 descriptions from the cfg; 2 effect prereq
  messages (SAB-001, SAP-004) untruncated (unread)
- canCastWhileFlying true by default (client ctor; no live reader)
- category BOON-CLASS/BOON-RACE -> BOONCLASS/BOONRACE: GetSourceType now
  resolves them instead of logging an error (no effect blocks them)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:57:46 -04:00
MagicBotandClaude Opus 5.5 3cf966d0eb Keep Powers.cfg file order and the client's power key defaults
PowerParser fills a LinkedHashMap so boot-assigned power IDs follow the
file order (the same order as static_power_powerbase IDs 1..1465).
PowerTemplate defaults follow ArcPower_ctor 0x56CD40: MAXLEVEL 9999,
CANCASTWHILEFLYING, SHOULDCHECKPATH and CANTARGETADMIN TRUE. No reader
yet; every power sets MaxLevel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:55:38 -04:00
MagicBotandClaude Opus 5.5 025a451770 Remove the DB power action loader
Power actions come from PowerActions.cfg (3fc5e725, tested). Deletes
AbstractPowerAction.getAllPowerActions and the ResultSet constructor on
every power action class. static_power_poweraction is dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:41:17 -04:00
MagicBotandClaude Opus 5.5 29e1e51b04 Check the teleport block against the power's category
City spire effects give players BlockedPowerType TELEPORT. TeleportPowerAction
checked TELEPORT for every teleport, so Infiltrate (SAB-001, category
SPIREPROOFTELEPORT) passed the cast check but never moved. Use the power's
category as finishUsePower does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:26:32 -04:00
MagicBotandClaude Opus 5.5 093b68258b Fall back to the default swing anim for weapons with none
310 cache weapons have empty attack anim lists, which threw
IndexOutOfBoundsException in getSwingAnimation when a guard swung one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 08:24:10 -04:00
MagicBotandClaude Opus 5.5 3fc5e7253a Build power actions from PowerActions.cfg
PowersManager.loadPowerActions builds every AbstractPowerAction from
PowerActionTemplate (template ctor on all subclasses), in id order with IDs
1..3046; OPENGATE keeps 5000. The ResultSet loader stays until removal.
The parser now reads the SimpleDamage (min, max), Teleport (bool), Track
(two bools) and TransferStat (bool at token 8) header fields.

Parity (JSON-Refactor-Plan/scripts/p47.py, every column): ids 3046/3046 plus
the DB-only AELFBORN row, which nothing references (the cfg has
BOON-AELFBORN). Effect ids, deferred powers, RemoveEffect source/REMOVEALL,
track filter/max, transfer types/ramps/ramp-add, mob level identical. The old
flags match the new header fields: Track 1024/2048 = tokens 3/4,
TransferStat 16384 = token 8, Teleport 32768 = SAB-001A's TRUE.

Value changes (PowerActions.cfg wins):
- DamageOverTime/TransferStatOT: the cfg value is the tick count; tick length
  = action duration / ticks. The DB stored cfg+1 as tick seconds, which only
  worked for 30s DoTs (5 x 6s, unchanged). HOLY-DOT-17 (60s) goes 10 -> 5
  ticks, BLEED-DOT (4s) 0 -> 4, TransferStatOT 30s 6 -> 5 ticks.
- TransferStat amounts are read with ToInt as the client does: 16 actions
  drop by 0.5 (BEL-001A 31.5 -> 31, CON-001A 13.5 -> 13, ...).
- PRE-334..337 become ApplyEffects with one effect; ApplyEffectsPowerAction
  now allows a missing second effect on baked-in items.
- Charm level cap ramp from the LEVELCAP= curve (same values as the DB).
- Charm actions with effect FEAR/CONFUSION (12, DB type Confusion) stay
  ConfusionPowerAction no-ops; CHARM ones use CharmPowerAction (decided).
- CreateMob: the cfg client mob id (49..74, 3500, 3501) maps to the mobbase
  through a map in CreateMobPowerAction, from the old DB values.
- The 50 Charm/Block/SpireDisable rows with no DB effect id get the cfg id
  (unread by those classes); isAggressive differs on 330 rows (unread).
- SimpleDamage keeps min/max instead of the always-0 simpleDamage (unread).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:37:36 -04:00
MagicBotandClaude Opus 5.5 b5c3d8c3fd Read the Charm/Fear level cap ramp as a float, fix the Track flag tests
levelCapRamp is a float column (e.g. 0.65) but was read with getInt, so the
Charm level cap never grew with trains. Charm caps now add ramp * trains.
FearPowerAction gets the same fix; nothing builds it today.

TrackPowerAction tested (flags & 1024) == 1, which is always false. No
caller reads trackPlayer/trackCorpse, so that part changes nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:33:29 -04:00
MagicBotandClaude Opus 5.5 dee936d8a5 Remove the DB effect loaders
Effects, modifiers, source types, effect fail conditions and item
enchant values all come from Effects.cfg (4.6b). Removes
dbEffectsBaseHandler, dbPowerHandler, the EffectsBase and effect
modifier ResultSet constructors, EffectsBase.getFailConditions and
LOAD_ENCHANT_VALUES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:17:24 -04:00
MagicBotandClaude Opus 5.5 e2ec8eb8a6 Build effect modifiers from Effects.cfg
Each EffectsBase builds its modifiers from its EffectTemplate MODS
lines, with ramps from the CompoundCurve helper. The Value mods now
fill Item.enchantValues, replacing LOAD_ENCHANT_VALUES. The ItemName
skip list of DB row IDs is gone; it only hid Centurion's and
Imperturbable from ./makeitem.

Value changes (cfg wins): weapon power X effects lose the DB-only OCV
mod; PRE-210A..237A resists and SUF-153A..171A resists (doubled);
Speed on SPRINT, SNT-010A, BHN-001A, HNT/RNG-037A, BRD-016A, PRS-031A,
and RNG-049A loses Speed; ARM-058A Necromancy +1; TMP-040A, WLR-018A;
PARRY/CRU-032A gain their Parry line. RESTORE's damage type is NONE
(cfg typo, as the client reads it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 04:40:43 -04:00
MagicBotandClaude Opus 5.5 27c59749fa Build EffectsBase from Effects.cfg
Effects, their fail conditions and source types come from
EffectTemplate. IDs are assigned at boot in file order. The power
actions that the server also keys as effects (item enchantment parents
and the action tokens sent in effect messages) are built from the
ApplyEffect/ApplyEffects/Invis actions with no Effects.cfg block: 776
keys, the 712 old registry rows plus 64 such as BRD-014A, which live
also sent.

Value changes (cfg wins): BRD-016A/PRS-031A lose the Chant source,
RUC-002A..004A lose Buff/Effect, and PRS-031A/RNG-037A no longer break
on Attack. Effect modifiers still load from the DB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 04:37:58 -04:00
MagicBotandClaude Opus 5.5 f8d764b5f5 Keep action line IDs apart from effect IDs
ActionsBase IDs (IgnoreStack effect keys) and EffectsBase IDs are both
written as Integer.toString keys into the same effects map, and their
ranges overlapped. Action lines now start at 10001. Also drops the
unused EffectsBase copy constructor and its NewID counter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 04:35:33 -04:00
MagicBotandClaude Opus 5.5 073417567f Remove the dead power 334 chant branches
Both branches of the getUUID() != 334 checks were identical, and they
tied the code to a DB row ID.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 04:35:10 -04:00
MagicBotandClaude Opus 5.5 c450af73af Remove the SafeMode and INVIS-D overrides from SBStringHash
The client hash (0x5547E0) has no overrides. Hashing the power id
SafeMode must give the real power token for 4.8. Source types are now
keyed by the effect's own token, which keeps the SafeModeA/B override
in EffectsBase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 04:34:35 -04:00
MagicBotandClaude Opus 5.5 aef3212b06 Remove the static_effect_costmaps loader
Deletes dbEffectsResourceCostHandler, EffectsResourceCosts and
DbManager.EffectsResourceCostsQueries. GameObjectType.EffectsResourceCosts
stays because the enum ordinals are sent to the client and stored in the DB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 00:58:39 -04:00