? Show All
Use non-blocking stream API here and everywhere. 
usleep(1000 * mt_rand(100000, 100000)); 
fwrite() may not be able to write the entire buffer and may return number lower than its length (verified to happen on Windows when fwrite() buffer is longer than 65536 bytes). This likely needs to be fixed but to do so we need to add buffers and retransmissions across a lot of places, as well as figure if fwrite() of 0 means an error or not (a comment in PHP docs suggests that 0, not false is returned on any problem with the stream). See HeroWO's api.php's WatchdogSseClient for what it may look like. 
Consider common ancestor for Plugins/Extensions/Protocols. 
function suggestParams() { 
If an unknown opcode is received, the receiving endpoint MUST _Fail the WebSocket Connection_. 
Must include the relevant HTTP code instead of 204 because: "Servers should use a 5xx status code to indicate capacity problems, as this will prevent conforming clients from reconnecting automatically." https://www.w3.org/TR/eventsource/#iana-considerations 
Check if this is implemented: "If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer." 
Looks like $handle context must be changed to supply cert path, etc. in 'ssl' group. See comments on the stream_socket_enable_crypto()'s help page. "If this fails (e.g., the client indicated a host name in the extended client hello "server_name" extension that the server does not host), then close the connection [...]" 
PHP ignores multiple headers, only keeping last as 'HTTP_' in $_SERVER. This can create problems because WebSocket spec explicitly allows some headers to duplicate (like Sec-WebSocket-Protocols). HTTP spec specifies that headers that are comma-separated list of values can be merged into one header without side effects, but PHP doesn't do that, dropping all values but last header's. See Section 9.1 (page 49). 
Also test fractional timeout. 
Test class constants (e.g. Status::SWITCHING). 
Ensure all accessors are defined for public properties in BaseTun, etc. 
Ensure all events are listed in Plugin. 
HeroWO is a work in progress and includes hundreds of small and large to-do tasks (`XXX`). These are embedded directly in code to make them versioned, easy to modify en masse and tightly bound to their context. **noXXXep** is what [presents them nicely](https://herowo.io/noXXXep/). 
RH
$SAVE_VERSION = 1; // XXX 
RH
$REPLAY_VERSION = 1; // XXX 
I
case 'highScores': // XXX 
I
case 'newCampaign': // XXX 
this assumes human players are selected on game start/load and remain the same until game session ends or this page is unloaded 
#clsi IC
// as well. XXX When a separate _opt for simultaneous mode is created, do 
we are currently creating a Screen per each playable player, which is to support hot-seat, and it works but lagging due to inefficient DOM.Mini/Map implementations; for now try to reuse map instance (or at least DOM subtree) in different screens and hide all Screen-s except the one used for the first player of currently interactive-able players 
I
//set('quickCombat', false) // XXX 
keystrokes must be handled by currently visible Screen only 
when running on the server (websocket server), strings will be delivered in server's language, for events generated on the server (like combat) 
//.append(HeroWO.strings) // XXX finish localization 
recreate on change_team, change_controller/s 
finish localization 
// observer may change on the server but sans admin's intervention this may only happen via do=configure by host, and that involves resetting client's connection, carrying new observer state on reconnect. Therefore not listening to change_observer. XXX 
R
replace with picker()/at() of _/Sqimitive? 
O
Optimization idea: store a compiled test closure within Effect that does all the tests and returns a result indicating if the Effect matches current Calculator options or not. Or implement calculation in webasm. 
There is currently no way to signal recalculation if whatever data test() depends on has changed. 
Detect circular dependency between sub-mcalc'ulators (e.g. when calculating creature_damageMin/Max - first has a modifier [custom, rules, damageMax] while second has [..., damageMin] so they continue to create calculators infinitely (if using oneShotEffectCalculator()). 
#rm
the same applies pretty much to every other calculator user (in Bits, etc.); need to review and add cascade remove where needed (some places like updateOn() already do it properly) 
// This optimization is disabled because it seems to slow down map loading by ~15%. XXX 
case this._constants.operation.spellSpec: // XXX H3 subsystem 
RH
var obj = this.map._actionableAtter(id, 0, 0, 0) 
party, creature cannot change? 
As explained in rm, technically we should not expect that an object explicitly specified as _opt.iCC is not present in combat - client of this calc must remove calc when iCC is removed; however, this is not properly done ATM and causes failure in walkImpassable() below (for example, try casting Armageddon - Combat.State's _calcs holds creature_flying and others with explicit iCC and are not removed at all). 
R
rep.walkImpassable(cr, function (o) { 
instanceof would be better but needs module require; add "isCreature" like in other Map classes? 
many of the above is H3 subsystem 
should not depend on H3 (Bits.Window) 
// There's actually CHAT.WAV in Heroes3.snd but it's 3 times larger. There's also DEFAULT.WAV that is suspiciously similar to the real ICQ sound (but more intrusive). Replace? XXX 
direct DOM access bypassing gridCellAt() 
should also re-format() Message-s 
RH
var players = ['', 'red', 'blue', 'tan', 'green', 'orange', 'purple', 'teal', 'pink'] 
R
copy/paste of Sqimitive.Ordered.staticProps.indexFor(); could use toString() + replace() but that won't work when minified due to name mangling 
#ll R
Change "_.log && _.log(...)" paradigm to "_.L# && _.log(...)" where L# is a bunch of properties on _ (alike to _.debug) that toggle logging of specific channels (e.g. Map loading). This will also remove the need for oldLog() - log() will be always available. 
R
Previous rule was that Map doesn't change between first init and last render so it's possible to set up hooks in attach and iterate in render (like Bits.ObjectList does), but this is no longer correct and such old code (chiefly in DOM.Bits) should be updated. `#Module can produce "sub-modules". Each module is owned by a `#ModuleContainer (such as `#Context or `#Screen), not the module that has produced it. Children (`'nest()) of `#Module can be only other `#Module-s, not unrelated classes (such as `@DOM.Slider`@) - these must be stored in `#Module's properties, possibly in a separate `@Common.Sqimitive`@ collection. Submodules are automatically removed when `'this is removed and are removed from `'this when removed from `#ModuleContainer. However, their `'el can be attached to any point in DOM (by default it appends to `#Module's `'el). This is how submodules are usually created: 
R
var cls = calc 
to avoid lags in single-player mode we might try implementing master-slave based on Web Workers (master as a Worker, main page as a slave to it, communicating by postMessage() rather than WS) 
R
classic: false, // XXX move to Screen? 
creating an instance every time calculator() is called just to normalize _opt, call key() and throw it away sounds wasteful 
temporary, until counter-based _opt.update is implemented; client may specify desired mode and shared calc will adjust its _opt.update automatically since a user of listening calc with update = false can transparently use an existing calc with update = 'defer' or true (updateIfNeeded() will be a no-op in this case), and a user of update of 'defer' can work with update of true; removing requirement that shared listening calcs must have update of true will allow better calc sharing 
move to a better place 
5 should client include actionableSpot in from when giving this arg? 
5 should pathFindFor return de-adjusted by act. spot? 
5 it seems adjusting breaks AI 
C, O
doing this in-thread has significant impact (100s of ms) on initial Context rendering; _.defer() fixes that 
this doesn't affect CreatureAnimation because it's using a timer, not CSS 
IC
Difference: in SoD RMB info box on ADVMAP never leaves the active area of the map, i.e. never overlaps EDG, right-side panel and the bottom help bar. 
I
add list of AObject->$pending 
I
case 'hasBuilt': // XXX 
I
DOM.Map draws shadows incorrectly: SoD separates shadow from picture while def2png.php combines them. If an object overlaps another, its shadow should be drawn behind the other, not on top of it like it currently happens. This won't be fixed in DOM.Map (as it will double the required number of nodes). 
C
XXX maybe add a databank property and move there 
R
// XXX XXX maybe add a databank property and move there 
I
SoD does smooth scroll (not tile-by-tile) - add "mapLockMove" to Screen, set it during transition and manually animate scroll position 
R
4 duplicates with _moveOpt() 
4 H3 subsystem 
I
// This is technically incorrect but XXX transitions currently don't 
R
shroud logic is H3-specific; refactor it to a subclass or, more likely, to a dynamic mixin since DOM.Map is nested by environment 
I
should respect transition ticks, i.e. gradually open the shroud during moveHero 
R
duplicates with H3.Rules 
// DEF's duration is too long so take 67% of it to obtain some normalized, good for defaults duration. XXX fix def2png.php? 
R
Take Z into account on small maps only. On over/underworld switching, relying on Z causes much bigger amount of nodes to be updated, making the switch very slow. 
H3 subsystem 
B, R
for mapMove this currently plays one tick earlier (two ticks at the beginning before DOM.Map starts animating the movement, then each tick at N-1 when Map is animating N, then completion one tick earlier while Map is still playing animation); either mapMove ticks should be adjusted (e.g. transitionFace with tick of N, subsequent coords change at N+1, then transitionFace before next move at N+1, coords at N+2, etc.) or DOM.Map should play them differently 
H3-specific 
I
should respect transition ticks, i.e. gradually open the shroud during moveHero 
H3 subsystem? 
I
partial shroud currently reveals presence of objects like border guard, hero, boat (albeit not their ownership thanks to grayscale(100%)); but they can't be simply hidden (like DOM.Map does) because map.miniMap stores info about either the impassable object or the terrain so if we hide the object we don't know what type of terrain to show; MiniMapTile can be (and should be) changed to store both fields 
IC
wheelScroll: 1, // XXX should be disabled everywhere in classic mode 
We'd use CSS to specify when animations should play and when not, but this is a no goer given it may affect thousands of nodes. One way would be to iterate over visible DOM.Map object nodes and set style.animationPlayState but that breaks into DOM.Map's domain (what is "object node"? when is it "visible"?) and besides, changing style still triggers partial reflow. Recent Web Animations draft (2020) helps but getAnimation/s() methods are extremely slow if results (i.e. animated map objects) are counted in hundreds. https://drafts.csswg.org/web-animations-1/#example-e7bbf635 Update: this was a good try but it causes bizarre effects on hero animations (AH??_ objects): enable mapAnimate, then drag ADVMAP or open a dialog (these call pauseAnimations(true)), then stop/close (pauseAnimations(false)). Observe that heroes no longer reflect changes to className - they are always in idle animation group, and even disabling mapAnimate or meddling with CSS via the inspector doesn't prevent their idle animation! Update: another approach making animation-duration dependent on --var (as generated by def2png.php) works but is as slow as manipulating CSS classes. 
C, O
doing in-thread has significant impact (100s of ms) on initial Context rendering; _.defer() would fix this 
I
proper unhooking 
if old and new are in byTarget, there's no need to update anything because $target cannot change; however, this breaks Shroud.Effects because it no longer detects changes in Effects in non-bySpot mode (when global Effects exist); see the comment in _effectsChanged()'s switch; fixing it is not trivial because Shroud doesn't reconstruct old object to see if the change would be handled by _byTargetChanged(), moreover, byTarget's ochange may occur both before and after _effectsChanged() so it needs to defer the decision smartly 
RH
state: this.rpc._createCombatState(combat, this.player), 
decision should be based on ally/enemy strength ratio 
I
this.do('tacticsEnd') 
R
2 _controlCreature: function () { 
2 This is using databank stats (rules) for speed, instead of factual combat values. Should be revised in the future, once calculators are fast enough. 
R
duplicates with _updateAttackable; XXX slow 
R
// XXX duplicates with _updateAttackable; XXX slow 
RH
return this.rpc._controlCreature(this.combat, creature) 
// Logically thinking, we should also cap if cannot reach for attack (!enemy.damage) but I'm not sure how. XXX 
R
8 _castSpell: function (enemies, allCost, done) { 
I
8 check immunities when estimating targets 
I
8 currently spells are checked in fixed order; they should be examined all together, sorted and best chosen based on damage to SP ratio (e.g. if spell A deals 10 damage and costs 5 SP while B deals 15 damage and costs 10 SP then A is better because for 10 SP during 2 turns we inflict 20 damage, not 15) 
this is necessary until databank access here is replaced by calculating actual values so e.g. after applying slow the target's speed will drop so much that normal speed threshold check will (?) suffice 
this is under assumption chain lightning strikes random creatures (as it currently does) 
this uses enemies order which is dependent on current creature and, for example, a less suitable enemy may be ranked top because it's within current creature's reach 
for simplicity only check casting with eye = enemy's spot; there may be more optimal cases when casting spot is shifted but they are not checked 
R
duplicates with H3.Rules.RPC 
R
may be merged with fireball's calculations 
R
may be merged with fireball's calculations 
RH
state: this.rpc._createCombatState(combat, this.player), 
clumsy, need some unification of transitions' option names 
Similarly, when changing vehicles, we should record the original vehicle type without spot since there is (usually) more than one spot to transfer between ground and water. But it seems to work fine? 
I
currently buying the most suitable creature only; should check all other available creatures and buy multiple during one visit 
use databank 
R
_refreshHero: function (hero) { 
R
_controlTown: function (town) { 
rely on building features/effects rather than hardcoded IDs 
this is trying listed buildings in sequential order and presence of a permanently unavailable/disabled building will be a roadblock 
R
_controlHero: function (hero) { 
RH
move this and other constants to _opt 
I
12 would be good to exchange garrisons directly with heroes (heroTrade) without using town as a relay 
12 this assumes garrison commands are executed in the same order as called (IdleTasks guarantees that but `#RPC doesn't) 
I
Ideally should check aiValue and swap with town's garrison if hero's creature is stronger. 
R
This ensures combined garrisons of hero and town don't have duplicate creature IDs. It doesn't reorder creatures in any particular way so for example if hero and town had 1*C1 then town will have 0*C1, hero - 2*C1, or if only town had two slots, each 1*C1 then it will have one slot 2*C1. Reordering must be done later, once the returned async completes. 
R
Note: heroTotal, [2] is old (current) aiValue's of hero's garrison, not the value it will have after combining (this value is total, [0]). 
R
//> maxCost `- from `'spot (the town), not `'hero, measured over any terrain and thus only an estimate 
// When on ship, add all terrain tiles that can be disembarked upon. In any case, add all guarded tiles - because our path finder currently doesn't allow building path to a monster through its impassable guarded area, the AI will never interact with monsters if we don't add them (XXX). 
R
Doesn't check reachability. 
_triggerSpotEffects() has more complex logic determining if there will be a fight when interacting with this tile; the check below works when disembarking but on land it erroneously treats an object that causes hero to stop rather than step on its tile as guarded, such as an artifact: [M][A][H] - Monster guards Artifact but Hero can pick it without a fight 
garrison may not be initialized before encounter? 
hardcoded check for town's buildings; check the Effect target for fortifications instead 
case '_dwelling': // XXX garrison isn't initialized before encounter 
I
check if guards were already defeated, if yes then set guards=0 
R
25 replace with ShipState? 
25 check actionable spot? 
I
tactics for distance from combat field edge (currently only implemented for distance from creature's original position) 
C
First Aid Tent's shadow is much more dark than in SoD. Check def2png.php? 
RH
to databank? 
add Generator constructor options to control this behaviour in detail 
RH
artifact ID 
#ddd R
props.width = props.width || 1 // databank defaults; XXX why do we need them? just put 1 into databank (also check other similar places) 
R
set to 1 explicitly 
C
review and make more in line with SoD 
I
options.formation 
C
8 currently assuming shoot penalty if distance (diagonal) is >= half of the combat field's width 
I
8 check for obstacles/fortifications along the trajectory 
C
formulae 
R
this calculator thing remains from pre-rewrite of Calc.Effect where Calc was much less flexible in terms of listen and update; it's likely that members of _calcs and calcs of attackTargets can be merged or have their options optimized (e.g. might not need to listen on WS server) 
In SoD there's always exactly 2 parties so determining which party's stats affect whom is trivial. Not so in HeroWO since we allow arbitrary combat configuration. Currently we take just the first enemy's stats. 
R
if (this.combat.tc) { // XXX duplicates in H3.Rules.RPC 
RH
return this._pathFind._neighboursOf([ 
IC
SoD plays last BGM (XXX) if there's no current object (e.g. after dismissing a hero, new object isn't automatically selected and previous sounds continue) 
C
// XXX SoD plays last BGM (XXX) if there's no current object (e.g. after dismissing a hero, new object isn't automatically selected and previous sounds continue) 
C
// Rules for audibility were determined empirically. Recheck (XXX). 
R
it's probably more flexible and less hardcoded to define max audible distance in AClass on the per-object basis (rationale: big loud objects are heard from afar); add a new field to map.byPassable (or create a new index) telling which sounds (or objects) are heard on a given spot, and at what volume; this will allow very far-heard objects and Audio won't need to traverse the map on every update to determine them 
IC
// We play music until switch to non-town or to another town of different type (i.e. switch from Castle to Castle doesn't restart the track). XXX implement as SoD in classic mode 
IC
In SoD, if you set music volume to 0 and then to non-0, you will hear the same combat track (not new random track picked) continuing from the same position. We should maintain a per-combat resumePositions, also because our combat window can be hidden and shown at will and it's best if showing a previously viewed window resumes the track rather than starts a random one from 0. 
IC
SoD blocks any user interaction until BATTLE*.WAV is played (though I find it quite annoying). We should do this at least in classic mode. 
IC
SoD seems to start certain BGM tracks from random position. For example, town themes start from 0 (unless already started in this game) while terrain isn't resumed, as if started from random position every time instead. 
R
duplicates with H3.Rules 
DEF images are large (450x400) and the game somehow decides where the center is individually (compare how it shows CPKMAN and CABEHE and you'll see that their offsets are different, as seen in creature info animation) 
R
duplicates with H3.Rules.RPC 
R
duplicates with H3.DOM.UI 
I
4 support SoD help bar here and in all other windows/screens 
R
4 add helper methods to quickly build MessageBox'es with frequently used layouts (e.g. OK/Cancel) 
6 revise margins of inline boxes; currently it depends on whitespace between them but it's not ideal and not all boxes are split by whitespace 
R
6 uniformize all format functions to create boxes with captions (like SpellImage), not only images 
#mmrl I
morale, luck (sign() in classic), atk/def/knw/spp ("+# Defense [Skill]"), skill ("Advanced Wisdom"), spell 
default: 
R
duplicates with `{Checks`} 
// as multiple images since we (SoD) lack negative images for now (XXX). 
I
that's still a problem if a party is removed mid-combat (e.g. due to fleeing); we don't have this feature yet so not addressing it Also not clearing _animateHero timers because that method is playing cheers animation in non-classic mode upon combat end. 
I
aborting doesn't work well ATM (at least on combatMove), likely due to improper handling of transitions abort 
I
2 even though mode is disabled while transitions are pending, bottom panel buttons are currently kept enabled 
IC
2 SoD also hides active outline (yellow) of current creature during animations 
R
_cast: function (book, spell) { 
I
// Barely adequate (XXX) preloading. Without this animations even with local server are horrid. 
R
duplicates with CreatureInfo's 
I
show total "(hp)" of top stack in nonclassic mode 
R
clicked: function () { 
IC
// SoD displays a more detailed message: XXX 
IC
// SoD displays a more detailed message: XXX 
IC
SoD also has a similar off-field area for hovering of the top tower; currently we only allow hovering of it by the impassable cell it stands on (11;0) - while in SoD hovering it produces impassable cursor without targeting the tower 
R
SpellAffectorList's logic must be extracted into a H3.Rules calculator and used here 
IC
SoD employs some uncertain logic in regards to the Y coord (sometimes the box is on top of the hex cell, sometimes on the bottom) 
I
currently affectors are not updated within a transition 
I
Lower and mid-lower walls should use a kind of "inverse Z": unlike normal order, they overlay creatures standing on the right of them and underlay those on the left. This troublesome exception isn't implemented currently except for lower wall that overlays creatures both on the right (good) and left (bad). 
IC
in classic mode arrow towers should not play idle (shuffle) and on-hover animations 
I
outlines of activeTurn and hover must be pulsating like in SoD 
ID
case 'combatRamHit': // XXX draw animations for this group 
ID
we lack impact animations for walls so not playing them (group.hit is used for damaged wall) 
ID
case 'combatRamMiss': // XXX draw animations for this group 
ID
var sound = sound || 'XXX
RH
sound = 'ATK2' // XXX exceptions to file names should be defined in databank, not in code 
I
shooter must turn before and after shooting if target is on the left (like in combatMove) 
C
adjusting by TXT-provided X seems to cause more problems than good 
I
add shootingCloud effect at this point and wait for completion before continuing execution below (+ DEATHCLD sfx) 
IC
for catapult, SoD pauses at hurling climax frame and waits until impact animation ends playing, then finishes hurling animation (we play this ending in parallel with impact) 
RH
var def = def || 'CSGRCK' // XXX 
RH
var def = def || 'SGEXPL' // XXX 
RH
var sound = sound || 'WALLHIT' // XXX 
B
case 'combatMoraleGood': // XXX should play over creature's spot at the time of transition, not current 
RH
var def = def || 'C09SPW0' // XXX 
RH
var sound = sound || 'GOODMRLE' // XXX 
RH
var def = def || 'C14SPE0' // XXX 
RH
var sound = sound || 'BADMRLE' // XXX 
C
if need to play on-hit animation for bad morale and other similar statuses 
RH
var def = def || 'C09SPA0' // XXX 
RH
var sound = sound || 'GOODLUCK' // XXX 
RH
var def = def || 'SP12_' // XXX 
RH
var sound = sound || 'REGENER' // XXX 
I
for better effect unlock/parallel should happen in impact's climax frame (some point in the middle of playing it), not immediately 
R
this is walking the entire view.path on tick 0 but more correct is to walk one step at a time at the specific tick to allow other consumers of combatMove update precisely in sync with the creature move animation; there are no such consumers currently (except mapCountImage but it's hidden until last tick) 
I
update Z index during move (not so trivial for flying) 
I
43 It appears making perfect move animation requires too much time so using quick and dirty method for now: force 70 ms per walk frame and 0.1 second per moved cell, then adjust movement speed ($.animate()) so that move animation ends on the last frame. SoD is using a more complex and fluent approach which remains to be researched. 
43 Use CreatureAnimation's walkTime/attackTime to reduce animation interval used by def2png.php and remember about --HC. 
RH
var distance = pathFind._heuristic(path[pathIndex - 1], path[pathIndex]) 
I
when a creature is hit or is dying, it should turn to face the attacker 
RH
'SP09_' 
C
// In SoD, hero cast animation pauses on the fifth (XXX) frame and waits until other cast animations finish. 
R
partially duplicates with _playTransitionCreatureOverlay() 
RH
var ai = !angle ? 1 : angle <= 27 ? 2 : angle <= 45 ? 3 : angle <= 72 ? 4 : 5 
I
duration depends on pixels, not the best concept for portability 
R
partially duplicates with _playTransitionCreatureOverlay 
RH
bottom: 560, // XXX 
R
Duplicates with cast_missileEvery(). 
B
user clicked before Map has reacted to mousemove and updated mouseCell which is a problem because it means mouseSide (any others) that we need here are out of date; need to figure why there is a delay 
IC
SoD shows creature info at a fixed position outside of combat but (unlike us) inside of combat it shows info near the creature 
IC
SoD draws on-hover outline for creatures of different damageGroup (e.g. if currently active creature is Griffin, hover of Catapult is outlined in SoD but not in HeroWO; same if active is Catapult and hovered is Griffin) 
RH
if (pos[0] == 12 && pos[1] == 10) { // XXX 
R
'=_updateCursor': function (sup) { 
RH
if (cur[0] == 9 && cur[1] == 5) { // XXX 
RH
} else if (walls.length && ((cur[0] == 10 && cur[1] == 5) || (cur[0] == 12 && cur[1] == 10)) && this.cmap._opt.mouseSide == 'bl') { // XXX 
O
// Try from all spots around the enemy (slow). XXX 
I
also highlight cells under creatures that can be healed (in non-classic mode) 
RH
switch (cr.get('special')) { // XXX 
IC
condition: if classic && targeting 1 cell 
IC
currently HeroWO always keeps active turn's outline even for area spells (Fireball) 
O
var source = this.map.effects.propertyIndex('source') 
IC
if there is a log entry from previous turn and _lastNew's height is 1, last line of that entry is displayed; need to add padding --------------------------------------- | "Some entry from the previous turn" | << this should not be visible | "Next round begins." | --------------------------------------- --------------------------------------- | "Next round begins." | << should look like this | | --------------------------------------- 
R
attach: function () { 
R
audio may technically change so must update hooks when it happens 
I
if not supported, must additionally reset Screen's speed options to 1.0 on start; while implementing, remember they're not supported in FF <57, not just in IE 
I
implement quick combat/auto combat and enable all Auto-Combat Options checkboxes 
C
numbers determined arbitrarily 
B
75 must hide duplicate spells 
IC
75 in combat, if hero has spells (set in editor) but no spell book (no artifact), SoD disables the button in the bottom panel but allows clicking on the hero image in the corner to open the book, and upon selecting a spell there it says "%s recites the incantations but they seem to have no effect." XXX also check if this spends SP or not 
C
// XXX in combat, if hero has spells (set in editor) but no spell book (no artifact), SoD disables the button in the bottom panel but allows clicking on the hero image in the corner to open the book, and upon selecting a spell there it says "%s recites the incantations but they seem to have no effect." XXX also check if this spends SP or not 
IC
don't show in wrong context (combat spell on advmap, etc.); SoD shows spell info in this case 
B
as with other CSS animations based on APNG, Chrome behaves strangely: it seems to "play" APNG in background even after the animation ends, so that when the animation is restarted, it "continues" playing APNG instead of playing it from the first frame. likely need to ditch APNG and use CSS animations like doing with DEF animations - possibly putting them to the same animations.css in the databank 
I
when replay playback is implemented, replace the useless Load > Tutorial button with load replay button 
C, I
recheck this and other game screens (ADVMAP, SpellBook, Combat, etc.), add missing hotkeys 
R
use H3.DOM.Audio 
I
playback of replays 
R
duplicates with _uploadSaved() 
I
not implemented because decompression API is recent (Chrome 80+, no FF) and its compatibility with gzencode() is unknown 
R
// May be improved in the future (XXX). 
RH
var REPLAY_VERSION = 1 // XXX 
IC
some of these texts are different in Main Menu and in scenario info window (in-game); for example, 'oc' says: 'Accumulate %d %s in your kingdom's armies.' 
I
storing binary stuff in localStorage is inefficient, IndexedDB is much better and has no quota but the API is troublous 
RH
case 7: // XXX
RH
var msg = _.format(this.cx.s('map', '%s, you only have %d days left to capture a town or you will be banished from this land.'), this.rules.databank.players.atCoords(this.pl.get('player'), 0, 0, 'name', 0), 7 - now) // XXX 
IC
ARRAYTXT.TXT has a message where there are 2 entries for "growth" and the last line says "...increase IN population." (can be seen after building Grail in Inferno) 
I
upon monster encounter, replace its image with AVWATTAK.DEF until the combat ends (or possibly replace the image during any active $pending on the monster's AObject) 
IC
SoD plays this sound N times where N = number of defeated parties (max 2 in SoD). 
IC
SoD seems to select 2nd (random?) town if had a single hero that was selected and now is gone 
C
do we need to scroll immediately or wait (like until uiAudio ends?)? 
C
ensure message text and display of player flag shown in different cases by HeroWO and SoD match 
#wmn IC
SoD message includes the hero's/town's name but it may be removed by now and we can't calculate it 
IC
msg.push(me ? 'You have captured the designated town, and claim victory!' 
IC
msg.push(me ? 'The hero under your protection has suffered defeat.' 
IC
msg.push(me ? 'The principal town has fallen.' 
RH
7 hardcoded; to databank? 
make transition? window will overlap if an event occurs in background, or another player's won changes 
RH
var el = box._inlineBox().appendTo(box.el) // XXX 
I
15 there is currently no way to either cancel message on timeout or bring it up again for user to act (think of event log from AoW), which is a problem if user reloads the page (transitions added before the reload will be dropped, not reprocessed); for now, prefixing most channels here with '!' to ensure cleanup actions are always run (even if after some time, such as when user is in a combat) except if user reloads the page 
#trsr I
15 when saving a game, pending transitions are also saved; they must be picked up when loading and either resumed (if possible) or deleted (if can't resume without data lost at the collect step that happened before load); perhaps add a "resume_..." that fulfills the role of select_... and collect_... for such transitions 
#huic I
these require confirmation to close and confirmation is only possible to non-observer because it involves calling an RPC command; ideally observer should see choices the main player is making by means of a system similar to Tracker 
I
if (this.sc.rpc.get('observer')) { return } 
I
if (this.sc.rpc.get('observer')) { return } 
I
if (this.sc.rpc.get('observer')) { return } 
I
if (this.sc.rpc.get('observer')) { return } 
R
defining strings in code isn't ideal but quest_choices messages can't be part of effects since they often need custom MessageBox set up 
ID
box.addFromMarkup(this.cx.s('map', '`{Audio XXX`}You enter the arena and face a pack of vicious lions. You handily defeat them, to the wild cheers of the crowd. Impressed by your skill, the aged trainer of gladiators agrees to train you in a skill of your choice.')) 
ID
box.addFromMarkup(this.cx.s('map', '`{Audio XXX`}As you reign in your horse, a guard steps up to you, "Welcome. I have received word of your arrival. Do you wish to pass at this time?"')) 
ID
box.addFromMarkup(this.cx.s('map', '`{Audio XXX`}`## War Machine Factory\n\nWould you like to purchase War Machines?')) 
ID
var msg = '`{Audio XXX`}`## School of Magic\n\nThe tingle of magic fills the air of this school of mystical arts. An acolyte offers to sign you up for the next class for `{Checks`}. You will have your choice of increasing your knowledge, or learning to better focus your powers.' 
ID
var msg = '`{Audio XXX`}`## Tree of Knowledge\n\nUpon your approach, the tree opens its eyes in delight. "Ahh, an adventurer! I will be happy to teach you a little of what I have learned over the ages for a mere `{Checks`}." (Just bury it around my roots.)\n\n`< `{StatImage experience`} +1 Level `>' 
RH
while (slot < 7 && slot < sub.size().x && sub.anyAtCoords(slot, 0, 0, 0) && sub.atCoords(slot, 0, 0, 'creature', 0) != this.creature()) { 
I
SoD also allows building ships on owned shipyard by merely clicking on it rather than encountering 
RH
var el = box._inlineBox().appendTo(box.el) // XXX 
RH
var el = box._inlineBox().appendTo(box.el) // XXX 
IC
SoD uses currently selected hero's garrisonSee ability while we use combined of all heroes including allies (XXX do allies in SoD provide garrisonSee?) 
C
// XXX SoD uses currently selected hero's garrisonSee ability while we use combined of all heroes including allies (XXX do allies in SoD provide garrisonSee?) 
RH
// Determined empirically. XXX to databank? 
I
add aggression info (likely to join, pay so much gold) 
C
at least with garrison, rogue's spying seems to take effect not only if it's close to the garrison's actionable spot but also to any of its impassable spots: [ ][#] [ ][@] @ = Garrison [ ][#] [H][ ] H = hero with Rogue 
I
this check is not accurate as certain objects (e.g. Warrior's Tomb, banks) do not disclose fulfillment status until encountered; perhaps add a selector to quest_fulfilled target like ifPeeking that will allow the object determine when it's being previewed and when it's encountered 
perhaps add a $name property to AObject, for $type-s other than ground/hero/town/monster? 
IC
SoD immediately switches to hero/town panel if no object was selected before. 
I
Kingdom Overview 
I
// SoD doesn't permit disembarking on top of an Event (which is the only ground object with passable actionable spot) but allows on top of Grail (which is hidden in HeroWO and ignored by pathfinder, cursor, spot effects, etc.). Currently this disembarking rule is an artificial limitation on the side of UI (XXX should be enforced in PathCost). 
I
show growth affectors 
RH
garList._store.release() // XXX; also grep for other /\._store\./ occurrences 
IC
SoD shows on the right of the portrait 
I
auto-cancel windows if building gets deleted 
I
in SoD it only costs gold; show others in the UI too and make it use calc (artifactCost) 
I
show help box for other buildings 
R, I
review all places where classic is accessed and add listening to change_classic (possibly after/together with #clsi
RH
var backgrounds = ['TPTHBKCS', 'TPTHBKRM', 'TPTHBKTW', 'TPTHBKIN', 
ID
same as Conflux (TPTHBKEL) but we don't have Complete version's resources yet so using that one 
#huig IC
put Gold first in classic (implement in EntityCost?) 
// SoD puts Gold first (XXX). 
C
in combat, SoD shows in brackets attack/defense adjusted by hero stats and terrain info (+1 for creatures native to combat terrain); need to research other bonuses and implement showing them 
R
refactor upgraded creature forms into a Rules Calculator 
IC
SoD doesn't draw boat's shadow in this place 
RH
duplicates elsewhere; move "trophy exclusion list" to databank 
RH
move limit to databank 
RH
_.times(5, function (level) { // XXX 
RH
var windows = ['TPMAGECS', 'TPMAGERM', 'TPMAGETW', 'TPMAGEIN', 
RH
var byLevel = [0, 0, 0, 0, 0, 0] // XXX 
PlayerName calc to allow players specifying custom names rather than using hardcoded colors like in SoD? 
IC
until we use SoD's font 
C
numbers determined arbitrarily 
C
numbers determined arbitrarily 
I
//this.nested('quickCombat').set('checked', this.sc.get('quickCombat')) 
I
//this.nested('subtitles').set('checked', this.sc.get('subtitles')) 
I
determine if replay saving is available; if not, disable, else on click reroute directly to replay saving 
R
center: true, // XXX use _opt.center on all other H3 windows and remove left/top from CSS? 
SoD doesn't permit more than 8 heroes per player; we do (sans the artificial limit) so display scroll buttons for future proofness (they won't look too good right now but they're easy to redraw) 
IC
SoD draws transparent flags instead of gray, and no shadow 
RH
opt.slider = {height: 7} // XXX 
IC
for ballista/FAT/ammo cart the message appears on attempt to drop to backpack while dragging starts successfully 
I
This is fine except it allows bypassing Artifact->$slots requirement of not accepting backpack. It's expected that the user has to put the artifact back to some slot eventually, but he can easily avoid this (e.g. by cancelling HeroInfo). It's probably a good idea to forcefully put "in-progress" artifacts back after some timeout, upon change of Player._opt.screen, pending, etc. (but then what to do if another artifact already occupies that slot and that artifact can't fit into backpack either?). 
IC
SoD shows this message in the top part of the screen rather than centered 
RH
box._markUp_BonusesImages(null, box.el, {bonuses: {heroes: {0: {artifacts: [this.get('artifact')]}}}}) 
IC
in SoD if backpack has visible scroll buttons then using them wraps around the artifact list (i.e. there's no start/end restrictions) 
R
Very lazy update. 
RH
icon detection logic is similar to H3.DOM.MainMenu's 
I
account for garrison_reinforce/reduce 
I
don't allow garrison to consist only of creatures with maxCombats <> false; also update checks in RPC 
I
draw popup 
IC
SoD allows splitting even if stack count is 1 
I
also disable when have no AP to travel first route segment 
R
this old code was supposed to notify us when hero becomes or ceases to be garrisoned/visiting but it's flaky because hero can enter/leave town without moving on ADVMAP; we now have visiting/garrisoned properties for hero AObject-s so should instead hook ochange_p_VIS/GAR 
IC
in classic mode replace all available with hourglass panel indicating turn of another player (animated hourglass if AI's turn) 
// If set, invisible bits in map.shroud for hero's owner are regarded as impassable; with fog though this doesn't fully work (XXX): DOM.Mini/Map hide non-permanent objects in fog (like heroes, not trees), yet info about which is leaked through PathCost, which checks byPassable and may report an explored tile as impassable even though there's no object shown standing on it in ADVMAP. 
this is now !delayRender so no need for workaround 
I
Pathfinder should take into account target object's passability. However, it is returned by triggerSpotEffects ('stand', 'stop', etc.) which is not known beforehand (and it may depend on arbitrary world properties, moreover - those set after part of the move route was traversed). For example, in SoD placing a Scholar and wearing Angel Wings or Boots of Levitation results in the following move route (¹ to ³): [_²[S³[_] _ = ground, S = scholar [ ¹[ ][ ] water [_][H][_] H = hero Interestingly, placing an actionable spot of some object (like Windmill) on the left of Scholar (in ²) makes the latter unreachable (even though ² itself is reachable and can be triggered as normal travel destination). For this to work, we likely need to move passability detection from triggerSpotEffects into a more specialized, almost constant method. 
I, C
Some diagonal movements (where any adjacent tile is a ground?) are prohibited on water. For example, one cannot interact with Mermaids after boarding [S] here (Mermaids shifted by one to the left are reachable): [_][_][ ][ ] _ = ground [ ][ ][ ][ ] [ ] = water [ ][#][@][#] [#@#] = Mermaids [ ][S][H][_] S = ship, H = hero on ground [ ][ ][_][_] A simpler example where ² is only reachable via ¹: [ ²[_] _ = ground [ ¹[S] 
here and in other places: replace such calls with map.to/fromContiguous? 
RH
vehicle characteristics 
consider eliminating bySpot usage in PathCost, extending byPassable properties instead 
I
Since a monster prevents movement around itself, it is impossible to encounter the monster's own actionable spot: [ ][.][.][.] [.] = guarded [H][.][@][.] H = hero, @ = monster (unreachable in HeroWO) [ ][.][.][.] However, SoD makes an exception, allowing building route to [@] through any [.] (guarded by target [@] only) if [@] is the travel destination. This purportedly improves user experience even though technically it works exactly as if moving onto a [.] (the hero stops and combat starts without reaching [@]). 
R
we currently have a variety of tracing log methods: here, AI, Calculator, etc. try to unify them; also see ll 
R
refactor and move generic methods to `#RPC, extract and move logic to H3.Rules 
B
3 Hero will pause over invalid spot (impassable, different terrain, etc.) if features change in such a way that continued movement is impossible (movement-triggered changes - a new impassable appears, terrain changes, etc.) or if client-supplied route is suboptimal. While some cases can be potentially solved by examining the move route ahead, it still won't protect against map changes unless we somehow lock certain map regions. Perhaps if this happens hero should be transported back to the last valid spot. 
B
3 a similar problem is that PathCost is not aware of APs, they may run out over water with Boots of Levitation or over impassable with that or Angel Wings, and hero will also stop 
R
should pathFindFor() adjust returned path to subtract actionableSpot? just like it automatically adds it. 
R
do_hireHero: function (args) { 
RH
if (this.get('player').heroes.length >= 8) { // XXX limit 
RH
hardcoded class ID 
R
hardcoded building ID, need to make an Effect target 
R
In SoD all types of taverns and towns are on solid ground so vehicle will be always horse. However, we allow water-based taverns in custom maps. This is still assuming only water terrain needs ship and others need horse because there's no terrain type -> vehicle index. We could figure that by iterating over all vehicle types and querying hero_walkTerrain Effect target but doesn't sound good either. 
R
do_heroLevelSkill: function (args) { 
R
do_heroArtifactSwap: function (args) { 
I
14 receiving hero must be adjacent or be garrisoned while other is visiting (same as in do=garrison, see comments there) 
I
14 check Player._opt.screen* 
IC
SoD allows buying only if the slot is free 
I
check $pending for both bonus and hero objects; here and in other do_... 
IC
SoD allows buying only 1 and only if the slot is free (since can't fit these into backpack) 
R
Very similar to _disembark(). 
I
implement encounter of standalone tradingPost 
RH
this.rules._erect(town.get('id'), [args.building], calc._buildings._calc.get('affectors')) // XXX 
R
see if this conundrum can be eased by using the new "^" event prefix in Sqimitive 
R
logically, this should be moved to: Combat.State = Common.Sqimitive.extend('HeroWO.H3.Combat.State', { _update: function () { this.batch(null, function () { <here> }) this way, changes done after batch() ends but before _update's override handler is reached will be noticed 
R
this and all other combat-related methods do not need to be part of RPC, they are not using anything except _opt.context plus do_combat can be called for player different from H3 RPC's player; make a separate class This assumes AObject-s of all parties are frozen with $pending. 
I
allow decisionMaker make a counter-offer with up to +30% difference from the counted price Normally, pendingSurrender cannot be serialized as part of Party because surrenderAsk's Async is part of WS pending until surrenderAccept is called, which unsets pendingSurrender. 
C
giving bonus = 20% of defense 
C
does SoD allow defending artifact creatures (Ballista, etc.)? in-combat log says it isn't gaining +N defense but DEF do have defending animation 
R
duplicates with tacticsNext 
RH
var max = doCalc(Calculator.Effect.GenericNumber, c.cx.map.constants.effect.target.spellMastery) >= c.cx.map.constants.spell.mastery.advanced ? 5 : 4 
IC
SoD seeks nearest, not random 
O
c.cx.map.effects.find(0, function ($, effect) { 
R
calc is removed but expandModifier() is semi-static so can use it like that 
R
this partially matches Effect::fromShort() and it only supports some modifier formats (which our spells currently evaluate to); need to implement generic priority calculation like in PHP 
O
c.cx.map.effects.find(0, function ($, effect) { 
O
c.cx.map.effects.find(0, function ($, effect) { 
R
First aid should be reworked: add a new creature-only spell "heal" (similar to dispel <-> dispel helpful) and add it to FAT's creature_spells. This way there will be no stat interference (currently what affects damageMin/Max also affects FAT's efficiency) and no checks for creature type (like one below) or team will be needed (can use spellImmune), plus other spell targets will be possible to use (like making healing global using spellGlobal). This is not done now because creature spells are not implemented yet. 
I
add check or Effects for disabling healing Ballista and other artifacts 
I
refresh the others var since _makeDamage() (hooks) could have some side effects on arbitrary creatures 
C, I
looks like in SoD some creatures' cloud (Magog) has friendly fire, some (Lich) doesn't 
#ang R
var angle = Math.atan2(target.get('y') - attacker.get('y'), target.get('x') - target.get('y') % 2 / 2 - attacker.get('x') + attacker.get('y') % 2 / 2) * (180 / Math.PI) 
R
var angle = Math.atan2(cell.y - attacker[1], cell.x - cell.y % 2 / 2 - attacker[0] - attacker[1] % 2 / 2) * (180 / Math.PI) 
R
var angle = Math.atan2(curTarget.get('y') - attacker.get('y'), curTarget.get('x') - curTarget.get('y') % 2 / 2 - attacker.get('x') + attacker.get('y') % 2 / 2) * (180 / Math.PI) 
#rrl R, I
as it stands now, msg[0] cannot be localized because it joins many targets together: "The ... do ... damage. [Target1 perish] [Target2 perish] ..." 
R, I
SoD has different messages for ripple-type spells: - The Death spell does %d damage \n to all living creatures. - The %s spell does %d damage \n to all undead creatures. - The Armageddon does %d damage. We're using the same message for ripple- and arrow-type spells since HeroWO has a complex system of deciding which creature is hit (livind, undead, etc.), and we also explain how many targets perished. 
R
//= targets array`, Set 
IC
SoD logs different messages for artifact creatures (First Aid Tent, etc.) 
R
_startCombat: function (combat) { 
C
If a garrison (creature.party) entirely consists of creatures due for un-summoning (maxCombats < 2) then such a party loses (not part of alive). Perhaps it should not lose, but then it's unclear how to address the now-empty garrison (perhaps can give one first-level creature as when fleeing). Empty alive indicates a tie. 
C
sum up all fallen enemy creatures' aiValue 5% and divide by number of alive heroes 
RH
51 case c.rules.artifactSlotsID.warMachine1: 
I
51 this currently examines slots where the artifact is in, allowing blacklisted artifacts if they're in backpack (e.g. editor allows placing Ballista in Backpack) 
I
this rounding creates a disparity because artifacts are distributed from each hero's pool: suppose there are 3 defeated heroes, each with 10 artifacts, and 3 alive heroes; 3rd alive hero receives 4*3=12 artifacts while others - 3*3=9; if we were to collect all enemy artifacts in a common pool and only then distribute, then every hero would receive the same amount of artifacts (3*10/3=10) 
RH
var spots = [10, 10, 9, 9, 8, null, 8, 9, 9, 10, 10] // XXX 
RH
var sx = creature.get('x') - 1 // XXX 
C
SoD doesn't seem to choose random enemy for every shot, instead it picks some (random one?) at the beginning and shoots it until it dies (or for several turns at least) 
C
if FAT and Ammo Cart affect allies too; currently they affect their own party only. 
R
review H3.*.js Bits and Calcs to check if owner changes are correctly listened to 
R
_applyModifier: function (o, operation, params) { 
C
This doesn't take into account hero_experienceGain (Learning skill). As a result, visiting Tree of Knowledge will add more experience than needed to reach the next level. Conversely, if hero_experienceGain is < 1 then Tree will give less experience and no level up will occur - but maybe this is actually a good feature? Regardless, need to check how Tree + Learning works in SoD. 
RH
return o.value = Math.max(10, o.subCalc('knowledge').calc.updateIfNeeded().get('value') * 10) // XXX consts 
RH
prop + (level + 1 < 10 ? 'L' : 'H'), // XXX 10 
C
Apparently, SoD adjusts tower damage based on buildings constructed in the town (base + upgraded count as one). For example, with Castle alone damages are 10-15/6-9 (main/other towers) but with Tavern they are 12-18/6-9, with Blacksmith 14-21/8-12. Based on tests with Castle (town type) in the editor, main tower's damage grows at +2/+3 min/max for every building (not counting the Castle/Citadel) while other towers' damage grows at the same rate but for every other building (Castle = 6/9, Tavern = same, Blacksmith = 8/12, Marketplace = same, Guardhouse = 10/15, etc.). However, there are either special exceptions or bugs. For example, Citadel + Griffin Tower + Bastion gives 14/21 as expected, but Citadel + Upgraded Griffin Tower + Bastion gives 12/18 (i.e. UGT is not counted as a building). For classic mode such exceptions should be determined and implemented (XXX). 
IC
// However, there are either special exceptions or bugs. For example, Citadel + Griffin Tower + Bastion gives 14/21 as expected, but Citadel + Upgraded Griffin Tower + Bastion gives 12/18 (i.e. UGT is not counted as a building). For classic mode such exceptions should be determined and implemented (XXX). 
C
formulae 
RH
var markets = 5 // Trading Post; XXX to databank 
RH
var rates = [ // XXX to databank 
R
consider reworking ..._message into general purpose handlers with some kind of selectors/filters without a hardcoded mode number 
I
// 3 is used for Pandora's Box. Conditions are matched in order. (*) are not implemented conditions because they are Effects and not tracked by addedBonuses (XXX). 
not sure if this should be a subcalc or if a one-off calculation is fine 
C
do upgraded versions count? e.g. quest needs 10 pikemen and hero has 10 halberdiers 
R
var HeroItemCollection = Effects.Collection.extend({ 
fixups untested 
maybe require do=leave and keep dwelling and hero locked (GE alive), then hook just one unpending_... and do screen reset there, like done with townscape? 
I
Must ignore guards when the triggered spot effects resulted in the hero not stepping on this spot. This means guards around the entered and exited monoliths are ignored, as are guards when attacking/trading with a hero. As a special exception, water-based guards around the boat are ignored when embarking (but ground guards around the disembarkation spot are not). But, for example, a guarded Lean To can be only interacted after defeating the guards. Currently we ignore guards when there's anything that will be acted upon except actionable without impassable (Event). UI cursor detection should be updated too. 
C, I
which monster of multiple guards is chosen? with max object ID (closest to right-bottom map corner)? 
C
19 does doubling happen before or after adding creature growth? 
C
19 does horde affect dwellings in the way it's implemented? 
R
// SoD doesn't allow picking hero for CPU. XXX Duplicates with WebSocket.Server.Client. 
RH
strings here and above 
I
newly built building should also trigger its encounter, immediately 
C
24 SoD usually automatically adds spells into visiting and garrisoned heroes' books but sometimes this happens only if explicitly opening Mage Guild. Research. 
24 Spells should be also automatically transferred after building or upgrading Mage Guild (right?). Maybe remake do=openMageGuild into an encounter effect? 
R
duplicates with H3.DOM.Bits 
R
combine with existing effect instead of always adding new (as in other places) 
I
SoD has special behaviour when defending a town with visiting hero but none garrisoned: it combines garrisons of town and hero; if there are more different creatures then slots then some algorithm is used to leave certain town creatures in town, i.e. omit from combat; if defender wins, those extra creatures remain in town intact; if he loses, they are removed (town's garrison is cleared upon defeat); we currently don't do any of this - if the hero loses, the attacker will have to attack the town's garrison next (both combats happen with fortifications) 
#h3t R
cls = _.sample(this.objectsID['hero_' + cls]) 
R
cls = _.sample(this.rules.objectsID['hero_' + cls]) 
R
_.each(['texture', 'animation'], function (prop) { 
R
var cls = _.sample(self.objectsID['artifact_' + art[1]]) 
R
var cls = self.objectsID['resource_' + type] 
R
var cls = _.sample(self.objectsID['monster_' + creature]) 
R
var cls = _.sample(self.objectsID['town_' + town]) 
R
var cls = _.sample(self.objectsID['hero_' + hero]) 
RH
var obj = this.map._actionableAtter(_.sample(pool), 0, 0, 0) 
C
recheck how SoD determines the "weakest" stack; it seems not by fightValue/aiValue but simply by level and then by creature ID 
currently we treat low number as "set count to 1" if original count is above 1; think how to extend creature_whirlpoolPenalty to be able to specify removal even if original is > 1 
need to apply creature_whirlpoolPenalty to count == 1 as well to allow Effects cancel stack removal 
IC
37 SoD: not shown if have Admiral's Hat but shown if don't have it and no stack was reduced 
IC
37 SoD plays the sound upon showing the message if message is shown, or upon mapTeleport if not (we play on mapTeleport always) 
I
39 altarOfSacrifice 
I
39 blackMarket 
I
39 denOfThieves 
I
39 freelancerGuild 
I
39 hillFort 
I
39 obelisk 
I
39 refugeeCamp 
I
39 sirens 
I
39 tradingPost 
I
39 university 
I
pathfinder should regard a fulfilled borderGate as fully passable (even though cursor shows it's interactive it does nothing) 
#mof I
implement monster fleeing (using retreatCan) 
I
0 25%/50%/100% of creatures normally fleeing from your army offer to join 
I
0 Some creatures will ask for money for joining, but most of them will not, especially if they are weak enough. 
IC
SoD shows the message on ADVMAP's background, we show on combat 
I
new hero object should respect quest removal transition (currently prison disappears when actor moves to it but new hero appears immediately, before that) 
I
count should be a word: "few", "several", etc. (XXX respecting garrisonSee?) 
C
// XXX count should be a word: "few", "several", etc. (XXX respecting garrisonSee?) 
ID
var msg = _.format(this.rules.cx.s('map', '`{Audio XXX`}Much to your dismay, the %s is guarded by %s %s.\n\nDo you wish to fight the guards?'), this.rules.classes.atCoords(this.map.objects.atCoords(this.get('bonus'), 0, 0, 'class', 0), 0, 0, 'name', 0), count, name) 
R
duplicates with `{Checks`} 
ID
var msg = _.format(this.rules.cx.s('map', '`{Audio XXX`}`## %s\n\nWould you like to recruit %s?'), this.rules.classes.atCoords(this.map.objects.atCoords(this.get('bonus'), 0, 0, 'class', 0), 0, 0, 'name', 0), wordJoin(names, ', or ')) 
IC
SoD shows the message on ADVMAP's background, we show on combat 
RH
gar.extendTo(7-1) // XXX 
RH
if (slot >= 7) { // XXX 
IC
for single creature show: "A %s joins your army." ADVEVENT.TXT[186] 
R
_grantExperience: function (hero, delta) { 
R
duplicates with h3m2herowo.php 
RH
if (skills.length < 8) { // XXX 
RH
while (potential.length < 2) { // XXX 
R
implement merging with previously set Effect as in _initializeTownSpells() 
RH
this.rpc.do_heroLevelSkill({hero: hero.get('id'), skill: potential[0].skill}) 
I, C
70 SoD updates state of heroes at the beginning of their turn rather than the day. example: have P1, P2; P1's turn is before P2; do P2's spellpoints regen when P1 starts turn or this happens only after P1 ends turn and P2's turn starts? 
I, C
70 same for timed events: they must occur at the beginning of turn (in non-simultaneous/classic turn mode), not daybreak 
C
is this correct? since all creatures share the same count pool for buildings, we determine the best growth and increment the count by it; keep in mind this method is used for week/month bonus growth (and plague) too; same question applies to _erect() 
C
73 on-map monsters should grow too but it has some uncertain algorithm as to when it happens (not every Monday and/or not for all; in fact, sometimes monsters diminish!); when implementing this, account for GenericEncounter's garrison renewal mechanism 
C
73 what hordeGrowth in CRTRAITS.TXT (creature_hordeGrowth) is for? on 'horde' world bonus all counts supposedly just double 
R
duplicates with Effects.Collection 
O
this.map.effects.find(0, function ($1, $2, $3, $4, $5, n) { 
R
to avoid conflicts there likely needs to be a central hook on all AObject fields affecting appearance, such as on subclass, that will update texture/animation; this is not implemented currently, as only the town fort/less factor affects object appearance in SoD 
RH
_.times(5 /*XXX*/, function (level) { 
RH
taken from SoD editor's help 
I
update duration 
I
support hero placeholders 
R
duplicates with _initializePlayers() 
I
update duration 
I
this works if initial experience is < level 2; if not, SoD does some kind of (XXX random-based?) "unattended level-up" (automatically choosing stats and skills); at very least, $level should be bumped here 
C
// XXX this works if initial experience is < level 2; if not, SoD does some kind of (XXX random-based?) "unattended level-up" (automatically choosing stats and skills); at very least, $level should be bumped here 
C
check what happens if experience is set for an on-map hero (not in custom heroes dialog) - maybe he also does the same unattended level-up, if so then h3m2herowo.php should be updated 
C
// Also unlike others, new dwelling may be smaller than the random placeholder (which is 3x3 with 2 actionable spots but, for example, Goblin Barracts is 2x2 with just one spot). XXX How does SoD adjust position of the new object? 
RH
var size = 2 // XXX to databank 
R
need to ensure artifacts has minimal strideX; this is set in core.php's AObject::$compact but we have to duplicate it here 
C
no idea how SoD handicap works since I have never seen it in the wild; is it even supported? 
RH
to databank? Determined empirically. 
add an atter to provide defaults to props by copying fields from AClass? like done in _initializePlayers() 
RH
if (now == 7) { // XXX 
IC
In SoD, if you have no initial town on map, the warning message appears not immediately on game start but after a certain action (at least after a combat). If no action is done until end of the day, no initial warning message appears. 
RH
!!self.rpc._endTurn(self.cx, self.map.players.nested(lastPlayer)) 
IC
// SoD doesn't recognize creatures put (hired) in the town's own garrison (not hero's) but if you enter a visiting hero and then just close the town's screen, the condition will trigger. We don't have this quirk currently (XXX). 
remove Effect target, make a field on AObject instead? 
I
Determines if hero's encounter with this object will be allowed to produce bonuses. This is currently used to check if a Windmill was visited this week, if Border Guard can be passed, etc. but it has several issues, like revealing info that the player shouldn't see until he carries the visit - like if a Shipwreck has been already sacked. 
RH
sub.extendTo(7 - 1) // XXX 
RH
var slots = placement == 'middle' ? 5 : 7 
relying on databank data rather than creature_shots 
R
The fact that we are modifying entries when evaluating is against HeroWO's general rule that all data should stored already prepared. Ideally, entries should be marked right when they are added (possibly using a new Effects property); this would need updating of all places where bonus_effects are produced (or at least some generic mechanism on both PHP and JS sides). For now, the entry detection mechanism is an implementation detail that might change. 
I
SoD shows messages for both bonus buildings and available (but make sure to not show message for buildings created as a result of Random town initialization in h3m2herowo.php) 
This assumes all upgraded building forms have the same growth rate as is the case in SoD. If not, available count will use rate of an arbitrary building from bonus_buildings of this timed event. 
I
currently we're pushing creatures to hero's garrison until it's full; the remainder is lost; implement SoD's exchange dialog (GARRISON) - but only for some encounters (like bank reward), not others (like 1-st level dwellings) 
RH
sub.extendTo(7-1) // XXX 
RH
if (slot >= 7 /*XXX*/) { return true } 
C
SoD's editor help suggests that artifacts may be lost due to full backpack but I have never seen this in game so not implementing it 
I
granting during handleInitiallyOwned should be "non-interactive" (no transitions) 
make min/max normalization universal in GenericNumber? 
I
should appear before levelup Messages are processed in the end, once all addedBonuses were filled. 
R
need to implement generic priority calculation like in PHP 
#mk R
refactor other methods to uniform "makeXXX" 
R
move to make...() factory on cx 
R
state: 'created', // XXX replace with just 'loading' bool 
I
turnLength: 0, // XXX 
I
rather than serializing no transitions, we should serialize those with unset collect (not yet selected); also, perhaps skipTransitions is unnecessary at all, and that all transitions should be serialized in all cases to allow the client to resume some of them (trsr
R
Code is no longer used. 
R
here and in other files: anyAtCoords doesn't check for bounds but very often need to check both in-bounds and "any object"; need to either add another method that checks for both or make any... check bounds 
R
replace neutral checks that look like so: !p.get('player') with a new prop/method check: p.isNeutral 
we should have a property for custom player names, to replace obtaining display name from rules (Player->$name) 
R
duplicates with CombatLog in databank.php but there's no way to transfer this schema given MapBuilder doesn't create any combats 
R
since these are mutually exclusive, combine into one option with 'r'/'s'/null (or numeric consts) values? 
review and add missing "#-bounds". 
O
an easy optimization would be possible: redefine _padding slots, if any; this will require no _layers shifting (but traversal is still needed to "zero them out", i.e. set values of former _padding slots to false or null) 
I
This implementation supports pathfinding within the same Z only. 
C
Is it okay for the algorithm that costFunc's result varies per neigh depending on current (as in H3.PathCost)? 
B
This still does not work in all cases. 
C
likely not good enough 
C
/\ /\ /\ /\ /\ hex square | |3.|4.| | | < odd 1 L = L /\ /\./\./\ /\ / 2 R = R 
R
Refactor: improve code quality • client/RPC.js 
RH
schema: store._schema == superStore._subSchemas[prop] ? null : store._schema, 
R
more correct would be to have some kind of "mini-server" where clients could be attached, ot a list of all created syncs; current implementation works for visual clients (a client per Screen) but not for special clients (like Replay); "mini-server" might also allow cleaner rewrite of H3.Rules' "mini-server" (initializeSinglePlayer()), possibly merging some single/multi-player code 
I
3 require giving hero to be controller by current player but allow receiving hero to be anyone - except for 'swap' where both heroes must be controlled 
I
3 check spatial relations of receiver/giver (if both are heroes - they must be adjacent, if one is town - another (hero) must be garrisoned or visiting) 
I
3 check Player._opt.screen* 
I
6 add checks 
I
6 check that hero being entered/left and town are not $pending 
RH
var maxSlots = 7 // XXX 
this triggers encounters with town's buildings and opens the town's view so user can manipulate it. but there are no checks on whether such manipulations come as a result of do_townscape or not. for SoD this is not a problem since buildings' encounters are all positive (more AP, SP, etc.) but technically this is not correct. it seems the master must track clients' open/close actions of townscapes and require that all town commands happen while a view is opened, and also prevent having more than one town views simultaneously opened 
RH
gar.extendTo(7-1) // XXX 
RH
if (slot >= 7) { // XXX 
I
Add more features, such as: add switching buttons on the right panel of ADVMAP (a button per observable player - neutral observer = all players, others = players in their team). All buttons are in one group and only 0 or 1 in the group must be pressed; if all are off then automatic following of the player's screen is disabled, if 1 then restore() is called. 
I
mapHideEnemy: false, // during enemy turn hides ADVMAP (shows fog of war everywhere) and shows shield in place of minimap, even when enemy moves in places explored by the player (makes mapEnemySpeed ineffective) 
C
operation of target=shroud Effects was not tested 
R
This works for bySpot because every change (i.e. assignment of value different from old) causes ochange on bySpot. However, it is fragile with byTarget and depends on implementation details of Effects' BatchIndexUpdater: if $ifX is set while $ifY is not, and $ifY is being set while $ifX is unset - such Effect is still part of byTarget but as of now the updater does remove + add so we still receive ochange. If the updater becomes more intelligent, we would skip change in this switch without receiving ochange in byTarget and doing _fire_changes(). 
O
Would be interesting to learn which of the two (from() or toString()) is faster. 
R
0 check existing code and extract HTML <chunks> (usually fed to el.html()) into templates 
0 consider putting HTML and CSS (extracted from herowo.css) into separate files retireved and bundled using require.js 
param = param || 'Hh3-btn_cur' // XXX H3 subsystem 
I
$(document) // XXX no off() 
H3 subsystem 
R
// unused values (if they're known). However, some modules wrongly access H3-specific constants as map.constants so that needs to be fixed first (XXX). 
H3 subsystem 
I
public $maxLevel; // 0/null/false unlimited XXX 
RH
public $won = false; // false; 0/1/2 loss/victory/both (XXX to const?) 
R
fix this and other similar consts to count from 1? 
R
make float short modifier use two's complement (will allow zero const) Float-fix note: even though PHP and JSON have distinct types for representing integer and float values, JavaScript does not (Number.isInteger(1.0) === true). To work around this, if a float has 0 fraction then it's increased by 0.0001 - this is too little to have any affect on final values but enough to recognize it as float. Note: in PHP, do not use round()/floor()/etc. to specify short integer modifier (`'delta) because they take float and return float. Use `[(integer)`] instead: 
confusing to name priority 'defaults'/'initial' but source 'initial'/'initialize' 
how does compact work with string labels in front of bonus_effects? fromShort() returns an Effect object so it should be converted to flat store and then leading "labels" prepended to it 
since 2D cannot grow, dimensions must be determined right now, but this prevents JS modules from using custom target identifiers; same with other stores here and in AObject::makeIndexes() 
I
// There may be multiple `'``> lines with the same object type if the type is further split into subtypes (for example, `'hero is the type but a property may have different sets of value types for regular heroes and random heroes). Currently (XXX) typesets of all object type's lines are internally merged into one so it doesn't make a difference, but in the future the schema generation algorithm may be improved to account for subtypes and generate more compact schemas. For example, if regular hero uses property `'$A but not `'$B while random hero uses `'$B but not `'$A - current algorithm allocates separate slots for `'hero object type for both `'$A and `'$B, instead of placing them on the same slot. 
I
refugeeCamp - must reset each Monday 
I
// to 1 for dwellings, to XXX for heroes. 
R
$subclass turned out to be a "union in itself" property, i.e. multiple type-dependent properties combined into one slot under the same name. It may be worth breaking it into different properties, each with a proper name (like "heroID" for prison or "animationGroup" for terrain). 
R
replace mirrorX/mirrorY with $mirror array, $compact'ed like $passable: [true, false] = '10'; $mirror[0] is x, [1] is y, or with a bitfield 
RH
make these indexes consts (same for $animation) 
retrieving an object's actionable spot is a very common task currently done on run-time (Map.actionableSpot()); put it into a property? 
I
public $patrol; // XXX 
I
`> hero ditto `- as `'dwelling; not implemented (XXX
RH
consts 
// simplicity (XXX) we assume they are point (1x1) because this is the 
C
[['surrenderCost', 0.95, true]], 
C
[['surrenderCost', 0.90, true]], 
C
[['surrenderCost', 0.85, true]], 
C
does it affect creatures' spells? 
I
need to use certain Effect priority/stack to clear only creatures' immunities and keep immunities like those of Sphere of Permanence 
I
// Angelic Alliance XXX 
I
// Cloak of the Undead King XXX 
I
// Elixir of Life XXX 
I
// Titan's Thunder XXX 
#hoshi I
when it's on, SoD seems to maintain two AP values: one for ship, one for horse; when dis/embarking, it just swaps the two so that after embark you get a lot of APs and if you immediately disembark, you get the same amount of land APs as before; or, if the artifact's description is right, the game maintains one AP value but "converts" between them when dis/embarking 
#grl I
grail buildings: TOCS/TOF/...+HOLY except: TOFHLYAA TONHOLYA TOSHOLYA TOTHOLYA; icons: BOEGRAIL BOFGRAIL BONHOLYG BOTGRAIL 
I
create new grail object and place it anywhere if got no premade object, provided there is any obelisk object 
I
//buildings.push(self.grailBuildings) 
I
with Aurora Borealis, replace all spells' icons with SPELLSCR.DEF frame 70 
I
//array_fill_keys($c_grail, [XXX), 
I
'skyship' => 13, // XXX 
C
costs were obtained from the in-game dialog; need to validate against BUILDING.TXT 
B
in Castle on-hover is handled by Hall even though Blacksmith overlaps it 
all four shapes/outlines NN/NS/MS/MK are identical $z + 1 (25 for $castle: 24 + 1) is reserved for overlaying shipyard outline when a ship is present (TBCSBOAT). See TownBuildingList.Item. If changing $z here, update CSS. 
I
hallFrame 20 if with ship, also different outline (TOELBOAT) 
C
5 hero_actionPoints 
C
5 does this stack? if you have 2 towns with 2 lighthouses 
B
fix $z, it must overlay shipyard 
#dbst I
there also exists BOCSCV2S.BMP and it's likely an $icon version used when trainingGroundsU is present but I dunno where BO* appear in SoD so can't check that, plus we don't have $iconU 
I
$icon variants exist: BORDWF1T BORDWF1H BORDWF1B BORDWF1 BORDWF2T BORDWF2H BORDWF2B BORDWF2 
I
$icon variant exists: BOSMRK2C 
C
10 hero_actionPoints 
C
10 does this stack? if you have 2 towns with 2 stables Matches effects of on-map Stables. 
B
has some problems with the outline (geojson?) in the townscape 
I
'mysticPond' => [ 
I
'treasury' => [ 
I
'castleGate' => [ 
C
is it applied immediately or on next turn of this player? 
I
'skeletonTransformer' => [ 
B
overlays cursedTempleU/U, preventing on-click event (in SoD click on unearthedGraves acts as a click on cursedTempleU) 
I
'portalOfSummoning' => [ 
B
overlays warren/U, preventing on-click event 
IC
warren/U border the screen edge and have outline partially cropped (not so in SoD) 
I
'freelancerGuild' => [ 
I
'ballistaYard' => [ 
B
overlays goblinBarracks/U, preventing on-click event 
B
overlays gnollHut/U, preventing on-click event 
I
'magicUniversity' => [ 
B
overlays magicLantern/U, preventing on-click event 
C, ID
Must be rechecked and remaining OB* filled in. 
C
'missileOfCreature' => [ 
I
in SoD is using a beam attack 
ID
$enchanter => 'PMAGEX', // XXX 
I
in SoD is using a beam attack 
ID
$evilEye => 'PMAGEX', // XXX 
I
new spells provided or existing spells overridden by the map are ignored because databank spells are hardcoded; same issue in defining object effects (Corpse), etc. 
C
While doing this, I have noticed that casting Armageddon on Diamond Golems deals different damage per group. If you fight 3 groups of Golems and cast it, some groups may have 4 creatures perished, some 5, some 6. Not sure what causes this - Armageddon is supposed to deal the same damage to every party sans their resistance % (which is the same for the same type of creature). 
C
'LCRS', // XXX does sharpshooter use the same sounds as archer? 
I
// 20% chance. XXX 
I
// 20% chance. XXX 
I
// 20% chance. XXX 
I
// 20% chance. XXX 
I
// 20% chance. XXX 
I
// 20% chance. XXX 
I
// 20% chance for both spells. XXX 
I
// 20% chance. XXX 
I
// 20% chance. XXX 
I
// 10% chance. XXX 
I
// 20% chance. XXX 
I
// 20% chance. XXX 
I, ID
Unknown chance. 
IC
Magic Arrow belongs to all 4 schools but casting it on Firebird is impossible in SoD. Let's suppose the hero has Expert Air Magic and no other skills; HeroWO would choose ifSpellSchool = [Air] and allow casting but SoD seems to check all schools and if the creature has immunity against any of them the spell cannot be cast. 
I, ID
Unknown chance. 
I
// 20% chance. XXX 
C
if destroyed - cancel effect? if so then can add new Effect->whileCombatCreature and use it here 
#dcj C
review jousting bonus 
C
$champion => ['jousting' => 2, 'width' => 2], 
I
Implement extra skill (Rebirth). 
I
Implement extra skill (drains spellpoints). 
I
Implement extra skill (Fire Shield). 
I
"channel 20% of spell points spent by enemy spellcasters directly into their hero’s spell point pool". 
C
damage numbers 
I
hide these from H3.DOM.Combat.Queue 
C
are towers affected by hero's attack and other stats? currently they are, using the standard damageRange() formulas 
C
numbers 
#twm I
tower's fortification must be land mines placed along the walls, doing 150 damage but only once per spot/mine per combat; this should be implemented after spells like land mines and quicksand are implemented 
I
//$siegeObstacles[] = ["SG{$s}MOAT", $bmp, $c_moat, ${"g_$town"}, 0, 0, 1, 1, '1']; 
C
is tower affected by luck? in H2 it was 
C
validate actually generated Effects against % in BALLIST.TXT 
C
chances of bonuses 
C
Is the upgraded creature growth boosted (at least weekly) if base creature was selected? SoD's message includes only the base creature name but I haven't checked actual numbers in dwellings; I assume in towns both creatures grow (since they share the same building's availability number) but what about on-map dwellings? Also strange that the only case (?) when the message includes both Imp and Familiar is after building Grail in Inferno. In any case, the inverse probably doesn't apply (i.e. Black Dragon grows alone). 
R
change to a recurring target similarly to creature_strikes and combatCasts 
C
3 ['hero_actionCost', -50.0, 'ifRoad' => array_search('dirt', AClass::road), 'stack' => [$st_terrain, 1]], 
I
3 Angel Wings affects path cost 
C
See if there are movement bonuses for different heroes much like native terrain to creatures. 
C
"$planeswalker $male" => "CH17", // XXX and update Images.txt 
C
"$planeswalker $female" => "CH17", // XXX and update Images.txt 
C
"$elementalist $male" => "CH16", // XXX and update Images.txt 
C
"$elementalist $female" => "CH16", // XXX and update Images.txt 
C
// Audio data is coming from Sounds.txt. XXX `{Audio`}-s need to be checked with the actual game sounds. 
I
//array_fill_keys($c_altarofSacrifice, [XXX]), 
IC
2 SoD filters learned secondary skills in the message by ignoring ones the hero already has (or all, if he has 8 skills already) 
IC
2 SoD may show multiple messages if it deems the content to be displayed is too large; each message uses its own text detection (i.e. if first bonus shown on that message is 'creatures' that it'd use that text, no matter what was the first bonus of the first message) 
C
need to check for spell book and wisdom (quest_fulfilled)? 
IC
Slightly different message from SoD (SoD shows "A" instead of count "1"); also it has a bug: it only checks first creature's stack size; if it's 1 then this message is used even if there are other creatures granted (or the same creature but in another stack) 
IC
Slightly different message from SoD (SoD shows no count) 
I
//array_fill_keys($c_blackMarket, [XXX]), 
B
HeroAP remains full at the end of the move because its update is called twice: first in response to GenericEncounter setting AP to 0, then during running transaction of do=heroMove that triggered the encounter 
IC
Slightly different from SoD: it shows one neutral luck (0) icon for -1 luck or one +1 for others. We in classic mode show 2/3 +1 icons for +2/+3 luck. 
I
//array_fill_keys($c_hillFort, [XXX]), 
#eyom I
SoD also shows every Eye, pausing for several seconds before going to next; we should do the same, plus allow breaking out of this by clicking anywhere (in non-classic mode) 
I
//array_fill_keys($c_marketOfTime, [XXX]), 
I
//array_fill_keys($c_obelisk, [XXX]), 
IC
Slightly different message from SoD 
IC
SoD doesn't play sound for Spell Scroll 
C, I
SoD sometimes (randomly?) adds quests to some artifact objects (have Wisdom, have Leadership, have gold, have gold and precious resource, etc(?)); this needs research randomArtifact: Places a random artifact on the Adventure Map. Artifact can be any class. Grail is excluded. randomTreasureArtifact: Places a random Treasure class artifact on the Adventure Map. randomMinorArtifact: Places a random Minor class artifact on the Adventure Map. randomMajorArtifact: Places a random Major class artifact on the Adventure Map. randomRelic: Places a random Relic class artifact on the Adventure Map. spellScroll: This scroll contains a spell, which is added into a hero’s spell book for as long as you carry the scroll. I think I've seen another (unused?) class for spell scroll somewhere. At least the editor's help has this second entry: 01 spell per scroll. Appears in hero's spell book if artifact is equipped. 
I
instead of modal message show it in the right-side panel 
I
//array_fill_keys($c_refugeeCamp, [XXX]), 
#sclr C
62 what happens if hero has no room for more secondary skills? 
I
62 a cap on the number of secondary skills should be enforced like in SoD 
#sclr I
62 SoD never chooses a spell bonus if hero's no book 
I
62 a cap on the number of secondary skills should be enforced like in SoD 
C
// Don't know about 3+ (XXX). 
C
can backpack be full? see also other effects here 
#shwr C
is shipwreck impassable from ground? it stands on water and pathcost may consider it passable for water only (in SoD it's passable for both because the hero doesn't move onto the object when interacting with it) 
I
spells are not handled by Bonuses/Images 
I
//array_fill_keys($c_sirens, [XXX]), 
#stcv I
upgrade cavaliers 
I
//array_fill_keys($c_denOfThieves, [XXX]), 
I
//array_fill_keys($c_tradingPost, [XXX]), 
I
82 the message should appear before LevelUp window 
82 Audio for lupDo is the same as for choices with lupG/J 
I
//array_fill_keys($c_university, [XXX]), 
IC
Slightly different message from SoD 
C
ADVEVENT.TXT[190] suggests that Witch Hut can be deserted 
should count "visible" skills, filtered by source? 
I
//array_fill_keys($c_freelancerGuild, [XXX]), 
I
//array_fill_keys($c_heroPlaceholder, [XXX]), 
I
103 a visit to already owned mine allows leaving garrison (for abandoned too) 
IC
103 SoD doesn't play FLAGMINE for Abandoned Mine (but it plays it when entering an already owned AM to leave garrison) 
C
['quest_garrison', [$troglodyte => [0, $random, 100, 200]], 'ifGrantedMax' => 0], // XXX garrison count numbers 
ADVMAP's status bar text and RMB help box is as such: if player owns the mine "Abandoned Mine Owned by red player (Gems)", if doesn't and the mine has guards "Abandoned Mine Guarded by a throng of Troglodytes", if not guarded "Abandoned Mine Owned by red player" 
C
affects creature spells? 
I
add growth effect in towns for these creatures whileOwned, and to their upgraded forms 
#dbs ID
//'sound' => 'XXX', 
ID
['quest_message', [$const, [toMarkup($adve[165], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[164], '`{BonusesImages`}`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[167], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[166], '`{MoraleImage +1`}`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[169], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[170], '`{BonusesImages`}`{Audio XXX`}')]]], 
ID
//'sound' => 'XXX', 
ID
//'sound' => 'XXX', 
ID
//array_fill_keys($c_garrison_0, ['sound' => 'XXX']), 
ID
//array_fill_keys($c_creatureBank_0, ['sound' => 'XXX']), 
ID
//array_fill_keys($c_creatureBank_3, ['sound' => 'XXX']), 
ID
//array_fill_keys($c_creatureBank_4, ['sound' => 'XXX']), 
ID
//array_fill_keys($c_creatureBank_5, ['sound' => 'XXX']), 
ID
//array_fill_keys($c_creatureBank_6, ['sound' => 'XXX']), 
ID
//array_fill_keys($c_monolithTwoWay, ['sound' => 'LOOPMON2']), XXX 
ID
// array_fill_keys($c_airElementalConflux, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_alchemistLab, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_altarOfEarth, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_altarOfSacrifice, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_altarOfThought, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_altarOfWater, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_altarOfWishes, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_archersTower, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_barracks, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_basiliskPit, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_behemothCrag, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_blackMarket, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_boarGlen, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_boat, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_borderGate, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_borderGuard, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_buoy, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_cartographer, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_centaurStables, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_chapelOfStilledVoices, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_cliffNest, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_cloudTemple, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_cloverField, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_corpse, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_coverOfDarkness, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_crypt, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_crystalCavern, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_cursedGround, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_cursedTemple, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_cyclopsCave, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_demonGate, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_dendroidArches, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_derelictShip, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_dragonCave, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_dragonCliffs, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_dragonUtopia, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_dragonVault, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_dwarfCottage, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_earthElementalConflux, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_elementalConflux, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_enchantedSpring, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_enchanterHollow, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_estate, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_evilFog, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_eyeOfMagi, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_favorableWinds, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_fieryFields, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_fireElementalConflux, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_fireLake, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_flotsam, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_forsakenPalace, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_freelancerGuild, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_frozenLake, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_garrison, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_gnollHut, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_goblinBarracks, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_goldenPavilion, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_golemFactory, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_gorgonLair, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_graveyard, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_griffinTower, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_guardhouse, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_hallOfDarkness, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_hallOfSins, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_harpyLoft, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_hellHole, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_hillFort, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_holyGround, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_homestead, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_hutOfMagi, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_hydraPond, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_idolOfFortune, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_impCrucible, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_kennels, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_keymasterTent, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_labyrinth, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_leanTo, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_libraryOfEnlightenment, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_lighthouse, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_lizardDen, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_lucidPools, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_mageTower, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_magicClouds, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_magicForest, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_magicLantern, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_magicPlains, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_magicWell, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_manticoreLair, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_mausoleum, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_mermaids, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_monastery, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_monster, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_nomadTent, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_oasis, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_obelisk, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_oceanBottle, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_ogreFort, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_orcTower, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_orePit, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_pandoraBox, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_parapet, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_pillarOfEyes, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_pillarOfFire, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_portalOfGlory, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_prison, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_pyramid, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_pyre, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_questGuard, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_redwoodObservatory, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_refugeeCamp, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_resource, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_rocklands, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_rogueCavern, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_scholar, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_seaChest, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_seerHut, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_serpentFlyHive, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_shipwreck, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_shipwreckSurvivor, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_sign, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_sirens, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_spellScroll, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_sulfurousLair, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_swanPond, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_thatchedHut, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_tombOfCurses, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_tombOfSouls, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_trainingGrounds, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_treasureChest, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_treeOfKnowledge, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_treetopTower, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_trollBridge, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_unicornGlade, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_university, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_volcano, ['sound' => 'XXX']), LOOPVOLC? 
ID
// array_fill_keys($c_wagon, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_warren, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_warriorTomb, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_waterElementalConflux, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_wateringHole, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_witchHut, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_wolfPen, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_workshop, ['sound' => 'XXX']), 
ID
// array_fill_keys($c_wyvernNest, ['sound' => 'XXX']), 
C, ID
add missing 
R
do we need it, given Lich's attach is implemented using shootingCloud/creature_shootingCloud? 
R
do we need it, given it doesn't have (XXX) any special effects other than damage? 
C
// XXX do we need it, given it doesn't have (XXX) any special effects other than damage? 
C
the game seems to play, on impact, ICERAY followed by ICERAYEX 
C
the game seems to play another sound in parallel with this one (WGHTKILL?) 
C
'castAnimationOfSpell' => [ 
C
when does SoD show the first part (drop) of the animation? I did see it a few times; if it didn't show it, this would be just: 
I
must use custom animation (polyline) 
I
custom animation (red overlay fade-in-out) 
I
custom animation (fade-out) 
I
custom animation (fade-in) 
I
custom animation (fade-in) 
I
custom animation (fade-in) 
I
custom animation (fade-in) 
#sssl I
H3.Rules is calculating creature_attack and other creature_... stat targets without respect to who is being attacked 
C
no giant? 
#sscs I
this must increment the recurring Garrison->retaliating so that target may retaliate again this turn 
C
recheck order (precedence, e.g. cartographer vs cover of darkness) 
R
use 'terrain' rather than 'other' for non-interactive objects like Kelp 
C
public $isGround; // purpose not entirely clear/implemented XXX 
make $sound/Group part of AObject similarly to $texture? 
ID
foreach ($signs as &$ref) { $ref .= '`{Audio XXX`}'; } 
perhaps also normalize to a valid identifier (remove spaces)? 
I
$ifVisiting may be also set to the hero that visits a garrison (gates). 
I
//> spellMastery `- if casting by creature XXX creature casting 
I
//> artifactChance `- if buying from Artifact Merchant XXX 
I
//> creature_hordeGrowth int `- not currently used (horde world bonus = double creature_growth) XXX 
I
//> retreatCan bool `- possibility for fleeing, for combat hero and for ADVMAP monster (XXX) before combat (this happens after check for 'creature_join') 
I
//> creature_join int % multiplier `- chance whether monster group will join hero on encounter (XXX
I, R
// Archers to Sharpshooters, etc. (XXX also merge with creature_upgradeCan?) 
//> creature_dispelImmune bool `- immunity to Dispel; in SoD, this is separate from regular spell immunity (creature_spellImmune); XXX replace with special spell context/ifContext? 
I
//> creature_reanimate int `- number of new creatures to create after combat (XXX
RH
//> creature_hitChance array associative type => int % multiplier `- for wall attacks (hurl/ram); 0 = chance of a strike doing no damage, 1 = hitting another wall (ignored if there're none or if 'ram'; if picked, a wall is chosen from all alive walls' combined creature_hitChance 2 chances), 2 = hitting the user-specified wall (XXX to const?) 
IC
SoD shows "Spell Scroll" for all scrolls on ADVMAP and "<spell name>" in message boxes. 
like with Creature->$effects, these 4 can be split into dynamic and static ($ifHero); this will allow faster map initialization since less Effects will be $dynamic (but will also increase the number of static ones, most of which won't be used, just like with Creature - not sure how problematic is that) 
C
SoD seems to determine combat terrain based on other things, not only underlying terrain. For example, a lava near water has another combat background ("beach") and creatures native to lava (like Imps) don't get bonuses there. Not sure if HeroWO respects this, need to check type of tile generated on shores - maybe the editor changes it from lava to other, then we're good. 
C
public $hordeGrowth; // 0 for most creatures; unknown purpose XXX 
C
public $flightDistance; // all 1.00 (1000) in SoD; unknown purpose XXX 
ID
$labeled[] = ['quest_message', [$const, [sprintf($adve[33].'`{Audio XXX`}', $bank->name)]]]; 
C
public $upgrade; // 0-100 (%) // XXX some kind of chance 
C
which OB* obstacles can be removed with the spell in SoD? 
CROGUE.DEF is the only DEF missing turnRight. Since it has turnLeft, can add mirrored turnRight. 
R
DefPreview 1.0.0 stores groups sequentially while 1.2.1 properly indexes them, resulting in gaps (that we skip over here). For 1.2.1, we could skip reading H3L but hopefully we'll move from DefPreview to some console tool in a future rather than fixing this. 
C
According to h3m-The-Corpus.txt, if last number in OBJECTS.TXT is not 0 then shadow should not be drawn. def2png.php doesn't process OBJECTS.TXT and it seems that DEF shadows of all such objects don't stick out anyway. 
validate speed against real in-game animations 
I
{Checks} only works for existing objects; it will fail when hero/monster was defeated 
#hhqm IC
Slightly different message in SoD. 
IC
'This land is menaced by `{Checks`}. If you could be so bold as to defeat them, I would reward you richly.', 
IC
'Faugh. You again. Come back when you are `{Checks`}, as I told you.', 
IC
'Not even close to `{Checks`}, leave me until you are there!', 
IC
'You are unworthy. Only someone who is `{Checks`} will be worthy enough.', 
IC
'Don\'t lose heart. Defeating `{Checks`} is a difficult task, but you will surely succeed.', 
IC
'No, `{Checks`} have not been driven off. Until then we cannot go home.', 
IC
'My route is still infested with `{Checks`}. Please hurry, mother becomes more ill each day.', 
IC
'No luck in finding `{Checks`}? Please hurry, the empire depends on you.', 
IC
'I am sorry, but we really want `{Checks`} as guards.', 
IC
'No, those will simply not do. You must bring me `{Checks`} before I can go to my bride to be.', 
IC
'I thought you had promise. You have indeed reached `{Checks`}. Come in, come in. Here, I have something to reward you for your efforts. Do you accept?', 
IC
'Ahhh, you have reached `{Checks`}. Would you like to receive a reward?', 
IC
'Finally, there is someone to whom I can bequeath my worldly possessions, now that you have achieved `{Checks`} do you wish to inherit?', 
IC
'At last, you defeated `{Checks`}, and the countryside is safe again! Are you ready to accept the reward?', 
IC
'Finally, `{Checks`} are gone from our home and we can return! Will you accept this reward?', 
IC
'The route is clear, I thank you deeply. Take this as a symbol of my gratitude.', 
IC
'Yes! `{Checks`} is perfect! Now if you\'ll kindly give it to me, I shall pay what I promised.', 
IC
'I am sorry, but this is a guildhouse, and only those who are experienced enough can join. Only those who are part of the guildhouse may pass. Until you reach `{Checks`}, you may not join.', 
IC
'We have a problem with our King. He doesn\'t like to be surrounded by immature people. Therefore you need to be of `{Checks`} in order to pass through.', 
IC
'The Belted Knights of Erathia guard this tower. They will only let one of their own pass. To join the order, you must first defeat `{Checks`}.', 
IC
'Beware, `{Checks`} are running loose out there. We can\'t open the doors until each and every one is driven from the land.', 
IC
'A small, henpecked man preers over the gate. "No one may pass. My dog ate my wife\'s, `{Checks`}, and I\'m not leaving here until I find a replacement.', 
IC
'The guards here simply will not permit anyone below `{Checks`} to pass.', 
IC
'There is no way we\'re going to let a wimp like you into our guild. Not until you are of `{Checks`} can you join.', 
IC
'Only when you are of `{Checks`} will our King stand for your presence.', 
IC
'The Belted Knights still will not let you pass, so you have not conquered `{Checks`}.', 
IC
'No, `{Checks`} are still running loose.', 
IC
'I am sorry, but the King wants to only see `{Checks`}, nothing else will do.', 
IC
'The guards acknowledge that you have indeed reached `{Checks`}. Do you wish to pass at this time?', 
IC
'Now that you have reached `{Checks`} level you may join our guild. Membership is free. Do you wish to pass at this time?', 
IC
'Excellent! Now that you are of `{Checks`} our King will not have any problems with you. Do you wish to pass at this time?', 
IC
'News of your defeat of `{Checks`} traveled quickly. Do you wish to pass, oh newly Belted Knight?', 
IC
'"Give it here and you can pass. Want a dog? Just kidding, will you give `{Checks`} to me?"', 
#dor R
$obj->displayOrder = $this->objectDisplayOrder($obj, $object, $object->index + 1); 
R
res.displayOrder = 1 << 26 | spot[1] + opt.height - 1 << 18 | 3 << 16 | spot[0] << 2 
R
displayOrder: 1 << 26 | spot[1] + hero.get('height') - 1 << 18 | 3 << 16 | spot[0] << 2, 
R
displayOrder: 1 << 26 | state.get('y') - act[1] + boat.height - 1 << 18 | 3 << 16 | (state.get('x') - act[0]) << 2, 
R
displayOrder: 1 << 26 | from[1] - act[1] + boat.height - 1 << 18 | 3 << 16 | (from[0] - act[0]) << 2, 
R
displayOrder: 1 << 26 | dest[1] + actor.get('height') - 1 << 18 | 3 << 16 | dest[0] << 2, 
R
this.map.objects.setAtCoords(hero, 0, 0, 0, 'displayOrder', 1 << 26 | this._bonusSpot[1] + this.map.objects.atCoords(hero, 0, 0, 'height', 0) - 1 << 18 | 3 << 16 | this._bonusSpot[0] << 2) 
#hhsi R
Databank's Spell->$id is declared as different from SPTRAITS.TXT index. However, we don't have means to map the two other than by idName which for some spells is different from makeIdentifier(). But not only that, we don't have access to SPTRAITS.TXT and can't look up spell name (or other info, e.g. $description). All we have is SoD's spell ID. Leaving fixing this for later since as of now Spell->$id-s of "userland" spells do match. 
R
$spells = [['hero_spells', $this->o_append($hero->spells), 'ifObject' => true, 'stack' => $this->const('effect.stack.classStats')]]; 
R
'spells' => $details->spells, 
R
$this->effect(['town_spells', $this->o_append($details->existingSpells), 'ifObject' => $obj->id, 'source' => $this->const('effect.source.initialize')]); 
R
$this->effect(['town_spellChance', $this->o_clamp00, 'ifSpell' => $spell, 'ifObject' => $obj->id, 'source' => $this->const('effect.source.initialize')]); 
R
$this->effect(['hero_spells', $this->o_append($details->spells), 'ifObject' => $obj->id, 'source' => $this->const('effect.source.initialize'), 'stack' => [array_search('classStats', H3Effect::stack), 1]]); 
R
if (isset($details->spell)) { 
R
$artifact = $this->spells->atCoords($details->spell, 0, 0, 'scroll'); 
C
38 do map-provided rumors have a higher chance of being shown in Tavern? 
C
38 do they override standard rumors? 
#clc R
$copyProps = [ 
R
'type', 'texture', 'animation', 'duration', 'width', 'height', 'miniMap', 'passableType', 'passable', 'actionable', 'actionableFromTop']) 
R
'type', 'texture', 'animation', 'duration', 'width', 'height', 'miniMap', 'passableType', 'passable', 'actionable', 'actionableFromTop']) 
R
'type', 'texture', 'animation', 'duration', 'width', 'height', 'miniMap', 'passableType', 'passable', 'actionable', 'actionableFromTop']) 
R
var props = ['type', 'duration', 'width', 'height', 'miniMap', 'passableType', 'passable', 'actionable', 'actionableFromTop'] 
R
_.each(['type', 'texture', 'animation', 'duration', 'width', 'height', 'miniMap', 'passableType', 'passable', 'actionable', 'actionableFromTop'], function (prop) { 
R
'type', 'texture', 'animation', 'duration', 'width', 'height', 'miniMap', 'passableType', 'passable', 'actionable', 'actionableFromTop']) 
C
foreach ($objects as $from) { 
R
This should be generally addressed by parsing RoE's and AB's OBJECTS.TXT and creating a compatibility list rather than hardcoding an array here. 
C
If SoD uses a universal algorithm for all objects then for the life of me I cannot figure it out. It seems there are either exceptions for hardcoded object classes (notably mountains and trees) or final order depends on individual passability bits. It's easy to spot by comparing against the upper level of "Adventures of Jared Haret" - if you change this calculation, some objects become correctly positioned while others break. For now, this formula at least makes (most) maps playable. 26 18 17 16 2 0 1 11111111b 1 0 11111111111111b 00 !ground y actionable * h3mID zero (*) set for objects moved on run-time; h3mID then specifies X (H3.Rules) If changing this calculation, update addMapMargin() and copies of this in JS. 
RH
slot 
ID
$this->effects($this->bonusEffects(['message' => $details->message.'`{Audio XXX`}'], $obj->id)); 
IC
compare the shape of our circle with the algorithm SoD uses circle() relies on the fact map margin wasn't yet added so comparing with 0 and width/height compares with the playable area. We don't want to place Grail inside margin, where players cannot reach it. "+ 1" is for [ ][X] - Grail MapObject's actionable spot. 
I
Must remove Lighthouse and Shipyard if the town is not near water (SoD allows them but factually ignores "Built" state for these two in Town Properties). XXX However, this likely has to be done on the JS side because this has to apply to Timed Event buildings as well. 
C
// XXX Must remove Lighthouse and Shipyard if the town is not near water (SoD allows them but factually ignores "Built" state for these two in Town Properties). XXX However, this likely has to be done on the JS side because this has to apply to Timed Event buildings as well. 
R
duplicates with JS code Determines hero level by the number of experience points. 
C
is it purely random or depends on other factors? like hero's army strength 
count should depend on garrisonSee and could be a word: "few", "several", etc. 
R
duplicates with `{Checks`} 
hardcoding numbers of the original reward; they may be changed by Effects but we don't know actual numbers at this point because the message (prompt) appears before giving out bonuses (filling addedBonuses); not sure if this should be fixed at all since it might be expected, plus the only way to do it is to carry out a fake _handle_bonus() session 
#hhlm IC
SoD shows 1 icon of +1 morale no matter the number; we show 3 +1 icons in classic mode 
IC
return $s .= "`{LuckImage $reward->luck`}"; 
META_OBJECT_DWELLING_ABSOD XXX not defined in h3m! 
META_OBJECT_TOWN_ABSOD XXX not defined in h3m! 
META_OBJECT_MONSTER_ABSOD XXX not defined in h3m! 
META_OBJECT_ARTIFACT_AB XXX not defined in h3m! 
META_OBJECT_ARTIFACT_SOD XXX not defined in h3m! 
I
//0x1D => 'CHR', // XXX 
I
//0x33 => 'WoG', // XXX 
I
HotA? 
I
throw new CliError('Converting from JSON (-ij) is not implemented yet.'); // XXX 
I
protected function collectStatistics(H3M $h3m, array &$stats) { 
I
protected function printStatistics(array &$stats) { 
C
check if this file and index.php templates use hardcoded values (flag images, etc.) rather than relying on databank */ 
consider contain for performance: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Containment 
split CSS and templates (HTML) into individual files per module (*.js); create a common CSS file for styles shared by multiple modules (like for text-overflow: ellipsis) */ 
Chrome and FF (Safar unknown) have a weird bug. If backdrop-filter: grayscale (with any argument) is applied to any node (i.e. there is at least one .Hgrid__cell_fog in the document) and there is an element (such as body or .Hweb-top) with background: teal then this element's content becomes blurry (especially easy to notice if text color is red). Doesn't happen with white instead of teal. 
animation: 10s Hh3-menu-p steps(20) alternate; /* XXX reflect real loading progress */ 
RH
practically, this stuff should be part of the databank; extract gameplay-specific rules to a separate CSS file included from the databank */ 
I
in SoD width depends on amount of content; also need to add scrollbar if content is too long */ 
#fish R
margin-left: calc(-274px + 40em); /* XXX fishy calculation */ 
R
all this shifting smells real peccant */ 
R
more peccant shifting, yay */ 
IC
/* SoD shows empty boxes in places of empty spell slots (up to 3). We don't but need to, in classic mode. XXX */ 
auto is supposed to fill columns as they go but as of now Chrome has buggy support for this - when there are 8 visible spells, it shows them in columns of 4 rather than two columns 6+2. I say "buggy" because if you add padding-bottom: 100px to the 6th spell and then change 100px to 0px, Chrome rebalances columns to be exactly 6+2. Hopefully this will be fixed soon and we won't have to contrive a workaround. */ 
doc
<!--https://XXX/docs/map.html#Context--> 
I
<!-- SoD allows customizing difficulty mode without opening Advanced Options. XXX --> 
I
On (not implemented) <!--XXX--> 
$lastForumText = ''; // XXX 
": "" 
": "", Show Available Scenarios": "Показать сценарии" 
#ART
#art0 COMPATIBILITY, Unused
Spell Book 
#art1 COMPATIBILITY
Spell Scroll 
#art2 COMPATIBILITY
The Grail (grl
#art3 COMPATIBILITY
Catapult 
#art4 COMPATIBILITY
Ballista 
#art5 COMPATIBILITY
Ammo Cart 
#art6 COMPATIBILITY
First Aid Tent 
#art7 COMPATIBILITY, Common
Centaurs Axe 
#art8 COMPATIBILITY, Minor
Blackshard of the Dead Knight 
#art9 COMPATIBILITY, Minor
Greater Gnoll's Flail 
#art10 COMPATIBILITY, Major
Ogre's Club of Havoc 
#art11 COMPATIBILITY, Major
Sword of Hellfire 
#art12 COMPATIBILITY, Relic
Titan's Gladius 
#art13 COMPATIBILITY, Common
Shield of the Dwarven Lords 
#art14 COMPATIBILITY, Minor
Shield of the Yawning Dead 
#art15 COMPATIBILITY, Minor
Buckler of the Gnoll King 
#art16 COMPATIBILITY, Major
Targ of the Rampaging Ogre 
#art17 COMPATIBILITY, Major
Shield of the Damned 
#art18 COMPATIBILITY, Relic
Sentinel's Shield 
#art19 COMPATIBILITY, Common
Helm of the Alabaster Unicorn 
#art20 COMPATIBILITY, Common
Skull Helmet 
#art21 COMPATIBILITY, Minor
Helm of Chaos 
#art22 COMPATIBILITY, Minor
Crown of the Supreme Magi 
#art23 COMPATIBILITY, Major
Hellstorm Helmet 
#art24 COMPATIBILITY, Relic
Thunder Helmet 
#art25 COMPATIBILITY, Common
Breastplate of Petrified Wood 
#art26 COMPATIBILITY, Minor
Rib Cage 
#art27 COMPATIBILITY, Minor
Scales of the Greater Basilisk 
#art28 COMPATIBILITY, Major
Tunic of the Cyclops King 
#art29 COMPATIBILITY, Major
Breastplate of Brimstone 
#art30 COMPATIBILITY, Relic
Titan's Cuirass 
#art31 COMPATIBILITY, Minor
Armor of Wonder 
#art32 COMPATIBILITY, Relic
Sandals of the Saint 
#art33 COMPATIBILITY, Relic
Celestial Necklace of Bliss 
#art34 COMPATIBILITY, Relic
Lion's Shield of Courage 
#art35 COMPATIBILITY, Relic
Sword of Judgement 
#art36 COMPATIBILITY, Relic
Helm of Heavenly Enlightenment 
#art37 COMPATIBILITY, Common
Quiet Eye of the Dragon 
#art38 COMPATIBILITY, Minor
Red Dragon Flame Tongue 
#art39 COMPATIBILITY, Major
Dragon Scale Shield 
#art40 COMPATIBILITY, Relic
Dragon Scale Armor 
#art41 COMPATIBILITY, Common
Dragonbone Greaves 
#art42 COMPATIBILITY, Minor
Dragon Wing Tabard 
#art43 COMPATIBILITY, Major
Necklace of Dragonteeth 
#art44 COMPATIBILITY, Relic
Crown of Dragontooth 
#art45 COMPATIBILITY, Common
Still Eye of the Dragon 
#art46 COMPATIBILITY, Common
Clover of Fortune 
#art47 COMPATIBILITY, Common
Cards of Prophecy 
#art48 COMPATIBILITY, Common
Ladybird of Luck 
#art49 COMPATIBILITY, Common
Badge of Courage 
#art50 COMPATIBILITY, Common
Crest of Valor 
#art51 COMPATIBILITY, Common
Glyph of Gallantry 
#art52 COMPATIBILITY, Common
Speculum 
#art53 COMPATIBILITY, Common
Spyglass 
#art54 COMPATIBILITY, Common
Amulet of the Undertaker 
#art55 COMPATIBILITY, Minor
Vampire's Cowl 
#art56 COMPATIBILITY, Major
Dead Man's Boots 
#art57 COMPATIBILITY, Major
Garniture of Interference 
#art58 COMPATIBILITY, Major
Surcoat of Counterpoise 
#art59 COMPATIBILITY, Relic
Boots of Polarity 
#art60 COMPATIBILITY, Common
Bow of Elven Cherrywood 
#art61 COMPATIBILITY, Minor
Bowstring of the Unicorn's Mane 
#art62 COMPATIBILITY, Major
Angel Feather Arrows 
#art63 COMPATIBILITY, Common
Bird of Perception 
#art64 COMPATIBILITY, Common
Stoic Watchman 
#art65 COMPATIBILITY, Minor
Emblem of Cognizance 
#art66 COMPATIBILITY, Major
Statesman's Medal 
#art67 COMPATIBILITY, Major
Diplomat's Ring 
#art68 COMPATIBILITY, Major
Ambassador's Sash 
#art69 COMPATIBILITY, Major
Ring of the Wayfarer 
#art70 COMPATIBILITY, Minor
Equestrian's Gloves 
#art71 COMPATIBILITY, Major
Necklace of Ocean Guidance 
#art72 COMPATIBILITY, Relic
Angel Wings 
#art73 COMPATIBILITY, Common
Charm of Mana 
#art74 COMPATIBILITY, Common
Talisman of Mana 
#art75 COMPATIBILITY, Common
Mystic Orb of Mana 
#art76 COMPATIBILITY, Common
Collar of Conjuring 
#art77 COMPATIBILITY, Common
Ring of Conjuring 
#art78 COMPATIBILITY, Common
Cape of Conjuring 
#art79 COMPATIBILITY, Major
Orb of the Firmament 
#art80 COMPATIBILITY, Major
Orb of Silt 
#art81 COMPATIBILITY, Major
Orb of Tempestuous Fire 
#art82 COMPATIBILITY, Major
Orb of Driving Rain 
#art83 COMPATIBILITY, Major
Recanter's Cloak 
#art84 COMPATIBILITY, Common
Spirit of Oppression 
#art85 COMPATIBILITY, Common
Hourglass of the Evil Hour 
#art86 COMPATIBILITY, Relic
Tome of Fire Magic 
#art87 COMPATIBILITY, Relic
Tome of Air Magic 
#art88 COMPATIBILITY, Relic
Tome of Water Magic 
#art89 COMPATIBILITY, Relic
Tome of Earth Magic 
#art90 COMPATIBILITY, Relic
Boots of Levitation 
#art91 COMPATIBILITY, Major
Golden Bow 
#art92 COMPATIBILITY, Major
Sphere of Permanence 
#art93 COMPATIBILITY, Relic
Orb of Vulnerability 
#art94 COMPATIBILITY, Common
Ring of Vitality 
#art95 COMPATIBILITY, Minor
Ring of Life 
#art96 COMPATIBILITY, Major
Vial of Lifeblood 
#art97 COMPATIBILITY, Common
Necklace of Swiftness 
#art98 COMPATIBILITY, Minor
Boots of Speed 
#art99 COMPATIBILITY, Major
Cape of Velocity 
#art100 COMPATIBILITY, Common
Pendant of Dispassion 
#art101 COMPATIBILITY, Major
Pendant of Second Sight 
#art102 COMPATIBILITY, Common
Pendant of Holiness 
#art103 COMPATIBILITY, Common
Pendant of Life 
#art104 COMPATIBILITY, Common
Pendant of Death 
#art105 COMPATIBILITY, Common
Pendant of Free Will 
#art106 COMPATIBILITY, Major
Pendant of Negativity 
#art107 COMPATIBILITY, Common
Pendant of Total Recall 
#art108 COMPATIBILITY, Major
Pendant of Courage 
#art109 COMPATIBILITY, Major
Everflowing Crystal Cloak 
#art110 COMPATIBILITY, Major
Ring of Infinite Gems 
#art111 COMPATIBILITY, Major
Everpouring Vial of Mercury 
#art112 COMPATIBILITY, Minor
Inexhaustible Cart of Ore 
#art113 COMPATIBILITY, Major
Eversmoking Ring of Sulfur 
#art114 COMPATIBILITY, Minor
Inexhaustible Cart of Lumber 
#art115 COMPATIBILITY, Relic
Endless Sack of Gold 
#art116 COMPATIBILITY, Major
Endless Bag of Gold 
#art117 COMPATIBILITY, Major
Endless Purse of Gold 
#art118 COMPATIBILITY, Common
Legs of Legion 
#art119 COMPATIBILITY, Minor
Loins of Legion 
#art120 COMPATIBILITY, Minor
Torso of Legion 
#art121 COMPATIBILITY, Major
Arms of Legion 
#art122 COMPATIBILITY, Major
Head of Legion 
#art123 COMPATIBILITY, Relic
Sea Captain's Hat 
#art124 COMPATIBILITY, Relic
Spellbinder's Hat 
#art125 COMPATIBILITY, Major
Shackles of War 
#art126 COMPATIBILITY, Relic
Orb of Inhibition 
#art127 COMPATIBILITY, Relic
Vial of Dragon Blood 
#art128 COMPATIBILITY, Relic
Armageddon's Blade 
#art129 COMPATIBILITY, Relic
Angelic Alliance 
#art130 COMPATIBILITY, Relic
Cloak of the Undead King 
#art131 COMPATIBILITY, Relic
Elixir of Life 
#art132 COMPATIBILITY, Relic
Armor of the Damned 
#art133 COMPATIBILITY, Relic
Statue of Legion 
#art134 COMPATIBILITY, Relic
Power of the Dragon Father 
#art135 COMPATIBILITY, Relic
Titan's Thunder 
#art136 COMPATIBILITY, Relic
Admiral's Hat (hoshi
#art137 COMPATIBILITY, Relic
Bow of the Sharpshooter 
#art138 COMPATIBILITY, Relic
Wizard's Well 
#art139 COMPATIBILITY, Relic
Ring of the Magi 
#art140 COMPATIBILITY, Relic
Cornucopia 
#art141 COMPATIBILITY, Unused
Diplomat's Suit 
#art142 COMPATIBILITY, Unused
Mired in Neutrality 
#art143 COMPATIBILITY, Unused
Ironfist of the Ogre 
#BLD
COMPATIBILITY
Village Hall 
COMPATIBILITY
Town Hall 
COMPATIBILITY
City Hall 
COMPATIBILITY
Capitol 
COMPATIBILITY
Fort 
COMPATIBILITY
Citadel 
COMPATIBILITY
Castle 
COMPATIBILITY
Tavern 
COMPATIBILITY
Blacksmith 
COMPATIBILITY
Marketplace 
COMPATIBILITY, Conflux, Fortress, Necropolis, Castle
Shipyard 
COMPATIBILITY, Castle
Lighthouse 
COMPATIBILITY, Conflux, Dungeon, Tower
Artifact Merchants 
COMPATIBILITY, Fortress, Stronghold, Necropolis, Castle
Resource Silo 
COMPATIBILITY, Rampart
Resource Silo 
COMPATIBILITY, Tower
Resource Silo 
COMPATIBILITY, Conflux, Inferno
Resource Silo 
COMPATIBILITY, Dungeon
Resource Silo 
COMPATIBILITY
Mage Guild Level 1 
COMPATIBILITY
Mage Guild Level 2 
COMPATIBILITY
Mage Guild Level 3 
COMPATIBILITY, Conflux, Dungeon, Necropolis, Inferno, Tower, Rampart, Castle
Mage Guild Level 4 
COMPATIBILITY, Conflux, Dungeon, Necropolis, Inferno, Tower, Rampart
Mage Guild Level 5 
COMPATIBILITY, Castle
Brotherhood of the Sword 
COMPATIBILITY, Castle
Stables 
COMPATIBILITY, Castle
Griffin Bastion 
COMPATIBILITY, Castle
Guardhouse 
COMPATIBILITY, Castle
Upg. Guardhouse 
COMPATIBILITY, Castle
Archers' Tower 
COMPATIBILITY, Castle
Upg. Archers' Tower 
COMPATIBILITY, Castle
Griffin Tower 
COMPATIBILITY, Castle
Upg. Griffin Tower 
COMPATIBILITY, Castle
Barracks 
COMPATIBILITY, Castle
Upg. Barracks 
COMPATIBILITY, Castle
Monastery 
COMPATIBILITY, Castle
Upg. Monastery 
COMPATIBILITY, Castle
Training Grounds 
COMPATIBILITY, Castle
Upg. Training Grounds 
COMPATIBILITY, Castle
Portal of Glory 
COMPATIBILITY, Castle
Upg. Portal of Glory 
COMPATIBILITY, Rampart
Mystic Pond 
COMPATIBILITY, Rampart
Fountain of Fortune 
COMPATIBILITY, Rampart
Treasury 
COMPATIBILITY, Rampart
Miners' Guild 
COMPATIBILITY, Rampart
Dendroid Saplings 
COMPATIBILITY, Rampart
Centaur Stables 
COMPATIBILITY, Rampart
Upg. Centaur Stables 
COMPATIBILITY, Rampart
Dwarf Cottage 
COMPATIBILITY, Rampart
Upg. Dwarf Cottage 
COMPATIBILITY, Rampart
Homestead 
COMPATIBILITY, Rampart
Upg. Homestead 
COMPATIBILITY, Rampart
Enchanted Spring 
COMPATIBILITY, Rampart
Upg. Enchanted Spring 
COMPATIBILITY, Rampart
Dendroid Arches 
COMPATIBILITY, Rampart
Upg. Dendroid Arches 
COMPATIBILITY, Rampart
Unicorn Glade 
COMPATIBILITY, Rampart
Upg. Unicorn Glade 
COMPATIBILITY, Rampart
Dragon Cliffs 
COMPATIBILITY, Rampart
Upg. Dragon Cliffs 
COMPATIBILITY, Tower
Library 
COMPATIBILITY, Tower
Wall of Knowledge 
COMPATIBILITY, Tower
Lookout Tower 
COMPATIBILITY, Tower
Sculptor's Wings 
COMPATIBILITY, Tower
Workshop 
COMPATIBILITY, Tower
Upg. Workshop 
COMPATIBILITY, Tower
Parapet 
COMPATIBILITY, Tower
Upg. Parapet 
COMPATIBILITY, Tower
Golem Factory 
COMPATIBILITY, Tower
Upg. Golem Factory 
COMPATIBILITY, Tower
Mage Tower 
COMPATIBILITY, Tower
Upg. Mage Tower 
COMPATIBILITY, Tower
Altar of Wishes 
COMPATIBILITY, Tower
Upg. Altar of Wishes 
COMPATIBILITY, Tower
Golden Pavilion 
COMPATIBILITY, Tower
Upg. Golden Pavilion 
COMPATIBILITY, Tower
Cloud Temple 
COMPATIBILITY, Tower
Upg. Cloud Temple 
COMPATIBILITY, Inferno
Order of Fire 
COMPATIBILITY, Inferno
Brimstone Stormclouds 
COMPATIBILITY, Inferno
Castle Gate 
COMPATIBILITY, Inferno
Birthing Pools 
COMPATIBILITY, Inferno
Cages 
COMPATIBILITY, Inferno
Imp Crucible 
COMPATIBILITY, Inferno
Upg. Imp Crucible 
COMPATIBILITY, Inferno
Hall of Sins 
COMPATIBILITY, Inferno
Upg. Hall of Sins 
COMPATIBILITY, Inferno
Kennels 
COMPATIBILITY, Inferno
Upg. Kennels 
COMPATIBILITY, Inferno
Demon Gate 
COMPATIBILITY, Inferno
Upg. Demon Gate 
COMPATIBILITY, Inferno
Hell Hole 
COMPATIBILITY, Inferno
Upg. Hell Hole 
COMPATIBILITY, Inferno
Fire Lake 
COMPATIBILITY, Inferno
Upg. Fire Lake 
COMPATIBILITY, Inferno
Forsaken Palace 
COMPATIBILITY, Inferno
Upg. Forsaken Palace 
COMPATIBILITY, Necropolis
Necromancy Amplifier 
COMPATIBILITY, Necropolis
Cover of Darkness 
COMPATIBILITY, Necropolis
Skeleton Transformer 
COMPATIBILITY, Necropolis
Unearthed Graves 
COMPATIBILITY, Necropolis
Cursed Temple 
COMPATIBILITY, Necropolis
Upg. Cursed Temple 
COMPATIBILITY, Necropolis
Graveyard 
COMPATIBILITY, Necropolis
Upg. Graveyard 
COMPATIBILITY, Necropolis
Tomb of Souls 
COMPATIBILITY, Necropolis
Upg. Tomb of Souls 
COMPATIBILITY, Necropolis
Estate 
COMPATIBILITY, Necropolis
Upg. Estate 
COMPATIBILITY, Necropolis
Mausoleum 
COMPATIBILITY, Necropolis
Upg. Mausoleum 
COMPATIBILITY, Necropolis
Hall of Darkness 
COMPATIBILITY, Necropolis
Upg. Hall of Darkness 
COMPATIBILITY, Necropolis
Dragon Vault 
COMPATIBILITY, Necropolis
Upg. Dragon Vault 
COMPATIBILITY, Dungeon
Mana Vortex 
COMPATIBILITY, Dungeon
Portal of Summoning 
COMPATIBILITY, Dungeon
Battle Scholar Academy 
COMPATIBILITY, Dungeon
Mushroom Rings 
COMPATIBILITY, Dungeon
Warren 
COMPATIBILITY, Dungeon
Upg. Warren 
COMPATIBILITY, Dungeon
Harpy Loft 
COMPATIBILITY, Dungeon
Upg. Harpy Loft 
COMPATIBILITY, Dungeon
Pillar of Eyes 
COMPATIBILITY, Dungeon
Upg. Pillar of Eyes 
COMPATIBILITY, Dungeon
Chapel of Stilled Voices 
COMPATIBILITY, Dungeon
Upg. Stilled Voices 
COMPATIBILITY, Dungeon
Labyrinth 
COMPATIBILITY, Dungeon
Upg. Labyrinth 
COMPATIBILITY, Dungeon
Manticore Lair 
COMPATIBILITY, Dungeon
Upg. Manticore Lair 
COMPATIBILITY, Dungeon
Dragon Cave 
COMPATIBILITY, Dungeon
Upg. Dragon Cave 
COMPATIBILITY, Stronghold
Hall of Valhalla 
COMPATIBILITY, Stronghold
Escape Tunnel 
COMPATIBILITY, Stronghold
Freelancer's Guild 
COMPATIBILITY, Stronghold
Ballista Yard 
COMPATIBILITY, Stronghold
Mess Hall 
COMPATIBILITY, Stronghold
Goblin Barracks 
COMPATIBILITY, Stronghold
Upg. Goblin Barracks 
COMPATIBILITY, Stronghold
Wolf Pen 
COMPATIBILITY, Stronghold
Upg. Wolf Pen 
COMPATIBILITY, Stronghold
Orc Tower 
COMPATIBILITY, Stronghold
Upg. Orc Tower 
COMPATIBILITY, Stronghold
Ogre Fort 
COMPATIBILITY, Stronghold
Upg. Ogre Fort 
COMPATIBILITY, Stronghold
Cliff Nest 
COMPATIBILITY, Stronghold
Upg. Cliff Nest 
COMPATIBILITY, Stronghold
Cyclops Cave 
COMPATIBILITY, Stronghold
Upg. Cyclops Cave 
COMPATIBILITY, Stronghold
Behemoth Lair 
COMPATIBILITY, Stronghold
Upg. Behemoth Lair 
COMPATIBILITY, Fortress
Cage of Warlords 
COMPATIBILITY, Fortress
Glyphs of Fear 
COMPATIBILITY, Fortress
Blood Obelisk 
COMPATIBILITY, Fortress
Captain's Quarters 
COMPATIBILITY, Fortress
Gnoll Hut 
COMPATIBILITY, Fortress
Upg. Gnoll Hut 
COMPATIBILITY, Fortress
Lizard Den 
COMPATIBILITY, Fortress
Upg. Lizard Den 
COMPATIBILITY, Fortress
Serpent Fly Hive 
COMPATIBILITY, Fortress
Upg. Serpent Fly Hive 
COMPATIBILITY, Fortress
Basilisk Pit 
COMPATIBILITY, Fortress
Upg. Basilisk Pit 
COMPATIBILITY, Fortress
Gorgon Lair 
COMPATIBILITY, Fortress
Upg. Gorgon Lair 
COMPATIBILITY, Fortress
Wyvern Nest 
COMPATIBILITY, Fortress
Upg. Wyvern Nest 
COMPATIBILITY, Fortress
Hydra Pond 
COMPATIBILITY, Fortress
Upg. Hydra Pond 
COMPATIBILITY, Conflux
Magic University 
COMPATIBILITY, Conflux
Garden of Life 
COMPATIBILITY, Conflux
Magic Lantern 
COMPATIBILITY, Conflux
Upg. Magic Lantern 
COMPATIBILITY, Conflux
Altar of Air 
COMPATIBILITY, Conflux
Upg. Altar of Air 
COMPATIBILITY, Conflux
Altar of Water 
COMPATIBILITY, Conflux
Upg. Altar of Water 
COMPATIBILITY, Conflux
Altar of Fire 
COMPATIBILITY, Conflux
Upg. Altar of Fire 
COMPATIBILITY, Conflux
Altar of Earth 
COMPATIBILITY, Conflux
Upg. Altar of Earth 
COMPATIBILITY, Conflux
Altar of Thought 
COMPATIBILITY, Conflux
Upg. Altar of Thought 
COMPATIBILITY, Conflux
Pyre 
COMPATIBILITY, Conflux
Upg. Pyre 
COMPATIBILITY, Conflux
Aurora Borealias (grl
COMPATIBILITY, Fortress
Carnivorous Plant (grl
COMPATIBILITY, Castle
Colossus (grl
COMPATIBILITY, Inferno
Deity of Fire (grl
COMPATIBILITY, Dungeon
Guardian of Earth (grl
COMPATIBILITY, Tower
Skyship (grl
COMPATIBILITY, Necropolis
Soul Prison (grl
COMPATIBILITY, Rampart
Spirit Guardian (grl
COMPATIBILITY, Stronghold
Warlords' Monument (grl
COMPATIBILITY
Moat - not done for Tower (twm
#CR
#cr0 COMPATIBILITY, L1, Castle
Pikeman 
#cr1 COMPATIBILITY, L1, Castle
Halberdier 
#cr2 COMPATIBILITY, L2, Castle
Archer 
#cr3 COMPATIBILITY, L2, Castle
Marksman 
#cr4 COMPATIBILITY, L3, Castle
Griffin 
#cr5 COMPATIBILITY, L3, Castle
Royal Griffin 
#cr6 COMPATIBILITY, L4, Castle
Swordsman 
#cr7 COMPATIBILITY, L4, Castle
Crusader 
#cr8 COMPATIBILITY, L5, Castle
Monk 
#cr9 COMPATIBILITY, L5, Castle
Zealot 
#cr10 COMPATIBILITY, L6, Castle
Cavalier (dcj
#cr11 COMPATIBILITY, L6, Castle
Champion (dcj
#cr12 COMPATIBILITY, L7, Castle
Angel 
#cr13 COMPATIBILITY, L7, Castle
Archangel 
#cr14 COMPATIBILITY, L1, Rampart
Centaur 
#cr15 COMPATIBILITY, L1, Rampart
Centaur Captain 
#cr16 COMPATIBILITY, L2, Rampart
Dwarf 
#cr17 COMPATIBILITY, L2, Rampart
Battle Dwarf 
#cr18 COMPATIBILITY, L3, Rampart
Wood Elf 
#cr19 COMPATIBILITY, L3, Rampart
Grand Elf 
#cr20 COMPATIBILITY, L4, Rampart
Pegasus 
#cr21 COMPATIBILITY, L4, Rampart
Silver Pegasus 
#cr22 COMPATIBILITY, L5, Rampart
Dendroid Guard 
#cr23 COMPATIBILITY, L5, Rampart
Dendroid Soldier 
#cr24 COMPATIBILITY, L6, Rampart
Unicorn (no ability) 
#cr25 COMPATIBILITY, L6, Rampart
War Unicorn (no ability) 
#cr26 COMPATIBILITY, L7, Rampart
Green Dragon 
#cr27 COMPATIBILITY, L7, Rampart
Gold Dragon 
#cr28 COMPATIBILITY, L1, Tower
Gremlin 
#cr29 COMPATIBILITY, L1, Tower
Master Gremlin 
#cr30 COMPATIBILITY, L2, Tower
Stone Gargoyle 
#cr31 COMPATIBILITY, L2, Tower
Obsidian Gargoyle 
#cr32 COMPATIBILITY, L3, Tower
Stone Golem 
#cr33 COMPATIBILITY, L3, Tower
Iron Golem 
#cr34 COMPATIBILITY, L4, Tower
Mage 
#cr35 COMPATIBILITY, L4, Tower
Arch Mage 
#cr36 COMPATIBILITY, L5, Tower
Genie 
#cr37 COMPATIBILITY, L5, Tower
Master Genie 
#cr38 COMPATIBILITY, L6, Tower
Naga 
#cr39 COMPATIBILITY, L6, Tower
Naga Queen 
#cr40 COMPATIBILITY, L7, Tower
Giant 
#cr41 COMPATIBILITY, L7, Tower
Titan 
#cr42 COMPATIBILITY, L1, Inferno
Imp 
#cr43 COMPATIBILITY, L1, Inferno
Familiar (no ability) 
#cr44 COMPATIBILITY, L2, Inferno
Gog 
#cr45 COMPATIBILITY, L2, Inferno
Magog 
#cr46 COMPATIBILITY, L3, Inferno
Hell Hound 
#cr47 COMPATIBILITY, L3, Inferno
Cerberus 
#cr48 COMPATIBILITY, L4, Inferno
Demon 
#cr49 COMPATIBILITY, L4, Inferno
Horned Demon 
#cr50 COMPATIBILITY, L5, Inferno
Pit Fiend 
#cr51 COMPATIBILITY, L5, Inferno
Pit Lord 
#cr52 COMPATIBILITY, L6, Inferno
Efreeti 
#cr53 COMPATIBILITY, L6, Inferno
Efreet Sultan (no ability) 
#cr54 COMPATIBILITY, L7, Inferno
Devil 
#cr55 COMPATIBILITY, L7, Inferno
Arch Devil 
#cr56 COMPATIBILITY, L1, Necropolis
Skeleton 
#cr57 COMPATIBILITY, L1, Necropolis
Skeleton Warrior 
#cr58 COMPATIBILITY, L2, Necropolis
Walking Dead 
#cr59 COMPATIBILITY, L2, Necropolis
Zombie (no ability) 
#cr60 COMPATIBILITY, L3, Necropolis
Wight 
#cr61 COMPATIBILITY, L3, Necropolis
Wraith (no ability) 
#cr62 COMPATIBILITY, L4, Necropolis
Vampire 
#cr63 COMPATIBILITY, L4, Necropolis
Vampire Lord 
#cr64 COMPATIBILITY, L5, Necropolis
Lich 
#cr65 COMPATIBILITY, L5, Necropolis
Power Lich 
#cr66 COMPATIBILITY, L6, Necropolis
Black Knight (no ability) 
#cr67 COMPATIBILITY, L6, Necropolis
Dread Knight (no ability) 
#cr68 COMPATIBILITY, L7, Necropolis
Bone Dragon 
#cr69 COMPATIBILITY, L7, Necropolis
Ghost Dragon (no ability) 
#cr70 COMPATIBILITY, L1, Dungeon
Troglodyte 
#cr71 COMPATIBILITY, L1, Dungeon
Infernal Troglodyte 
#cr72 COMPATIBILITY, L2, Dungeon
Harpy 
#cr73 COMPATIBILITY, L2, Dungeon
Harpy Hag 
#cr74 COMPATIBILITY, L3, Dungeon
Beholder 
#cr75 COMPATIBILITY, L3, Dungeon
Evil Eye 
#cr76 COMPATIBILITY, L4, Dungeon
Medusa (no ability) 
#cr77 COMPATIBILITY, L4, Dungeon
Medusa Queen (no ability) 
#cr78 COMPATIBILITY, L5, Dungeon
Minotaur 
#cr79 COMPATIBILITY, L5, Dungeon
Minotaur King 
#cr80 COMPATIBILITY, L6, Dungeon
Manticore 
#cr81 COMPATIBILITY, L6, Dungeon
Scorpicore (no ability) 
#cr82 COMPATIBILITY, L7, Dungeon
Red Dragon 
#cr83 COMPATIBILITY, L7, Dungeon
Black Dragon 
#cr84 COMPATIBILITY, L1, Stronghold
Goblin 
#cr85 COMPATIBILITY, L1, Stronghold
Hobgoblin 
#cr86 COMPATIBILITY, L2, Stronghold
Wolf Rider 
#cr87 COMPATIBILITY, L2, Stronghold
Wolf Raider 
#cr88 COMPATIBILITY, L3, Stronghold
Orc 
#cr89 COMPATIBILITY, L3, Stronghold
Orc Chieftain 
#cr90 COMPATIBILITY, L4, Stronghold
Ogre 
#cr91 COMPATIBILITY, L4, Stronghold
Ogre Mage 
#cr92 COMPATIBILITY, L5, Stronghold
Roc 
#cr93 COMPATIBILITY, L5, Stronghold
Thunderbird (no ability) 
#cr94 COMPATIBILITY, L6, Stronghold
Cyclops 
#cr95 COMPATIBILITY, L6, Stronghold
Cyclops King 
#cr96 COMPATIBILITY, L7, Stronghold
Behemoth 
#cr97 COMPATIBILITY, L7, Stronghold
Ancient Behemoth 
#cr98 COMPATIBILITY, L1, Fortress
Gnoll 
#cr99 COMPATIBILITY, L1, Fortress
Gnoll Marauder 
#cr100 COMPATIBILITY, L2, Fortress
Lizardman 
#cr101 COMPATIBILITY, L2, Fortress
Lizard Warrior 
#cr102 COMPATIBILITY, L5, Fortress
Gorgon 
#cr103 COMPATIBILITY, L5, Fortress
Mighty Gorgon (no ability) 
#cr104 COMPATIBILITY, L3, Fortress
Serpent Fly 
#cr105 COMPATIBILITY, L3, Fortress
Dragon Fly 
#cr106 COMPATIBILITY, L4, Fortress
Basilisk (no ability) 
#cr107 COMPATIBILITY, L4, Fortress
Greater Basilisk (no ability) 
#cr108 COMPATIBILITY, L6, Fortress
Wyvern 
#cr109 COMPATIBILITY, L6, Fortress
Wyvern Monarch (no ability) 
#cr110 COMPATIBILITY, L7, Fortress
Hydra 
#cr111 COMPATIBILITY, L7, Fortress
Chaos Hydra 
#cr112 COMPATIBILITY, L2, Conflux
Air Elemental 
#cr113 COMPATIBILITY, L5, Conflux
Earth Elemental 
#cr114 COMPATIBILITY, L4, Conflux
Fire Elemental 
#cr115 COMPATIBILITY, L3, Conflux
Water Elemental 
#cr116 COMPATIBILITY, L4, Neutral
Gold Golem 
#cr117 COMPATIBILITY, L5, Neutral
Diamond Golem 
#cr118 COMPATIBILITY, L1, Conflux
Pixie 
#cr119 COMPATIBILITY, L1, Conflux
Sprite 
#cr120 COMPATIBILITY, L6, Conflux
Psychic Elemental 
#cr121 COMPATIBILITY, L6, Conflux
Magic Elemental 
#cr122 COMPATIBILITY, L0, Neutral
NOT USED (1) 
#cr123 COMPATIBILITY, L3, Conflux
Ice Elemental 
#cr124 COMPATIBILITY, L0, Neutral
NOT USED (2) 
#cr125 COMPATIBILITY, L5, Conflux
Magma Elemental 
#cr126 COMPATIBILITY, L0, Neutral
NOT USED (3) 
#cr127 COMPATIBILITY, L2, Conflux
Storm Elemental 
#cr128 COMPATIBILITY, L0, Neutral
NOT USED (4) 
#cr129 COMPATIBILITY, L4, Conflux
Energy Elemental 
#cr130 COMPATIBILITY, L7, Conflux
Firebird 
#cr131 COMPATIBILITY, L7, Conflux
Phoenix (no ability) 
#cr132 COMPATIBILITY, L10, Neutral
Azure Dragon (no ability) 
#cr133 COMPATIBILITY, L10, Neutral
Crystal Dragon 
#cr134 COMPATIBILITY, L8, Neutral
Faerie Dragon 
#cr135 COMPATIBILITY, L10, Neutral
Rust Dragon 
#cr136 COMPATIBILITY, L6, Neutral
Enchanter 
#cr137 COMPATIBILITY, L4, Neutral
Sharpshooter 
#cr138 COMPATIBILITY, L1, Neutral
Halfling 
#cr139 COMPATIBILITY, L1, Neutral
Peasant 
#cr140 COMPATIBILITY, L2, Neutral
Boar 
#cr141 COMPATIBILITY, L3, Neutral
Mummy 
#cr142 COMPATIBILITY, L3, Neutral
Nomad 
#cr143 COMPATIBILITY, L2, Neutral
Rogue 
#cr144 COMPATIBILITY, L5, Neutral
Troll 
#cr145 COMPATIBILITY, L0, Neutral
Catapult 
#cr146 COMPATIBILITY, L0, Neutral
Ballista 
#cr147 COMPATIBILITY, L0, Neutral
First Aid Tent 
#cr148 COMPATIBILITY, L0, Neutral
Ammo Cart 
#cr149 COMPATIBILITY, L0, Neutral
Arrow Tower 
#OBJ
#class0 COMPATIBILITY, Unused
Nothing 
#class2 COMPATIBILITY
Altar of Sacrifice 
#class3 COMPATIBILITY
Anchor Point 
#class4 COMPATIBILITY
Arena 
#class5 COMPATIBILITY
Artifact 
#class6 COMPATIBILITY
Pandora's Box 
#class7 COMPATIBILITY
Black Market 
#class8 COMPATIBILITY
Boat 
#class9 COMPATIBILITY
Border Guard 
#class10 COMPATIBILITY, Actionable
Keymaster's Tent 
#class11 COMPATIBILITY, Actionable
Buoy 
#class12 COMPATIBILITY, Actionable
Campfire 
#class13 COMPATIBILITY, Actionable
Cartographer 
#class14 COMPATIBILITY, Actionable
Swan Pond 
#class15 COMPATIBILITY, Actionable
Cover of Darkness 
#class16 COMPATIBILITY, Actionable
Creature Bank 
#class17 COMPATIBILITY, Actionable
Creature Generator 1 
#class18 COMPATIBILITY, Actionable
Creature Generator 2 
#class19 COMPATIBILITY, Actionable
Creature Generator 3 
#class20 COMPATIBILITY, Actionable
Creature Generator 4 
#class21 COMPATIBILITY
Cursed Ground 
#class22 COMPATIBILITY, Actionable
Corpse 
#class23 COMPATIBILITY, Actionable
Marletto Tower 
#class24 COMPATIBILITY, Actionable
Derelict Ship 
#class25 COMPATIBILITY, Actionable
Dragon Utopia 
#class26 COMPATIBILITY, Actionable
Event 
#class27 COMPATIBILITY, Actionable
Eye of the Magi (eyom
#class28 COMPATIBILITY, Actionable
Faerie Ring 
#class29 COMPATIBILITY, Actionable
Flotsam 
#class30 COMPATIBILITY, Actionable
Fountain of Fortune 
#class31 COMPATIBILITY, Actionable
Fountain of Youth 
#class32 COMPATIBILITY, Actionable
Garden of Revelation 
#class33 COMPATIBILITY, Actionable
Garrison 
#class34 COMPATIBILITY, Actionable
Hero 
#class35 COMPATIBILITY, Actionable
Hill Fort 
#class36 COMPATIBILITY, Actionable
Grail (grl
#class37 COMPATIBILITY, Actionable
Hut of the Magi 
#class38 COMPATIBILITY, Actionable
Idol of Fortune 
#class39 COMPATIBILITY, Actionable
Lean To 
#class41 COMPATIBILITY, Actionable
Library of Enlightenment 
#class42 COMPATIBILITY, Actionable
Lighthouse 
#class43 COMPATIBILITY, Actionable
Monolith One Way Entrance 
#class44 COMPATIBILITY, Actionable
Monolith One Way Exit 
#class45 COMPATIBILITY, Actionable
Monolith Two Way 
#class46 COMPATIBILITY
Magic Plains 
#class47 COMPATIBILITY, Actionable
School of Magic 
#class48 COMPATIBILITY, Actionable
Magic Spring 
#class49 COMPATIBILITY, Actionable
Magic Well 
#class50 COMPATIBILITY, Actionable, Unused
Market of Time 
#class51 COMPATIBILITY, Actionable
Mercenary Camp 
#class52 COMPATIBILITY, Actionable
Mermaids 
#class53 COMPATIBILITY, Actionable
Mine 
#class54 COMPATIBILITY, Actionable
Monster 
#class55 COMPATIBILITY, Actionable
Mystical Garden 
#class56 COMPATIBILITY, Actionable
Oasis 
#class57 COMPATIBILITY, Actionable
Obelisk 
#class58 COMPATIBILITY, Actionable
Redwood Observatory 
#class59 COMPATIBILITY, Actionable
Ocean Bottle 
#class60 COMPATIBILITY, Actionable
Pillar of Fire 
#class61 COMPATIBILITY, Actionable
Star Axis 
#class62 COMPATIBILITY, Actionable
Prison 
#class63 COMPATIBILITY, Actionable
Pyramid 
#class64 COMPATIBILITY, Actionable
Rally Flag 
#class65 COMPATIBILITY, Actionable
Random Artifact 
#class66 COMPATIBILITY, Actionable
Random Treasure Artifact 
#class67 COMPATIBILITY, Actionable
Random Minor Artifact 
#class68 COMPATIBILITY, Actionable
Random Major Artifact 
#class69 COMPATIBILITY, Actionable
Random Relic 
#class70 COMPATIBILITY, Actionable
Random Hero 
#class71 COMPATIBILITY, Actionable
Random Monster 
#class72 COMPATIBILITY, Actionable
Random Monster 1 
#class73 COMPATIBILITY, Actionable
Random Monster 2 
#class74 COMPATIBILITY, Actionable
Random Monster 3 
#class75 COMPATIBILITY, Actionable
Random Monster 4 
#class76 COMPATIBILITY, Actionable
Random Resource 
#class77 COMPATIBILITY, Actionable
Random Town 
#class78 COMPATIBILITY, Actionable
Refugee Camp 
#class79 COMPATIBILITY, Actionable
Resource 
#class80 COMPATIBILITY, Actionable
Sanctuary 
#class81 COMPATIBILITY, Actionable
Scholar (sclr
#class82 COMPATIBILITY, Actionable
Sea Chest 
#class83 COMPATIBILITY, Actionable
Seer's Hut 
#class84 COMPATIBILITY, Actionable
Crypt 
#class85 COMPATIBILITY, Actionable
Shipwreck (shwr
#class86 COMPATIBILITY, Actionable
Shipwreck Survivor 
#class87 COMPATIBILITY, Actionable
Shipyard 
#class88 COMPATIBILITY, Actionable
Shrine of Magic Incantation 
#class89 COMPATIBILITY, Actionable
Shrine of Magic Gesture 
#class90 COMPATIBILITY, Actionable
Shrine of Magic Thought 
#class91 COMPATIBILITY, Actionable
Sign 
#class92 COMPATIBILITY, Actionable
Sirens 
#class93 COMPATIBILITY, Actionable
Spell Scroll 
#class94 COMPATIBILITY, Actionable
Stables (stcv
#class95 COMPATIBILITY, Actionable
Tavern 
#class96 COMPATIBILITY, Actionable
Temple 
#class97 COMPATIBILITY, Actionable
Den of Thieves 
#class98 COMPATIBILITY, Actionable
Town 
#class99 COMPATIBILITY, Actionable
Trading Post 
#class100 COMPATIBILITY, Actionable
Learning Stone 
#class101 COMPATIBILITY, Actionable
Treasure Chest 
#class102 COMPATIBILITY, Actionable
Tree of Knowledge 
#class103 COMPATIBILITY, Actionable
Subterranean Gate 
#class104 COMPATIBILITY, Actionable
University 
#class105 COMPATIBILITY, Actionable
Wagon 
#class106 COMPATIBILITY, Actionable
War Machine Factory 
#class107 COMPATIBILITY, Actionable
School of War 
#class108 COMPATIBILITY, Actionable
Warrior's Tomb 
#class109 COMPATIBILITY, Actionable
Water Wheel 
#class110 COMPATIBILITY, Actionable
Watering Hole 
#class111 COMPATIBILITY, Actionable
Whirlpool 
#class112 COMPATIBILITY, Actionable
Windmill 
#class113 COMPATIBILITY, Actionable
Witch Hut 
#class114 COMPATIBILITY, Unused
Brush 
#class115 COMPATIBILITY, Unused
Bush 
#class116 COMPATIBILITY
Cactus 
#class117 COMPATIBILITY
Canyon 
#class118 COMPATIBILITY
Crater 
#class119 COMPATIBILITY
Dead Vegetation 
#class120 COMPATIBILITY
Flowers 
#class121 COMPATIBILITY
Frozen Lake 
#class122 COMPATIBILITY, Unused
Hedge 
#class123 COMPATIBILITY, Unused
Hill 
#class124 COMPATIBILITY
Hole 
#class125 COMPATIBILITY
Kelp 
#class126 COMPATIBILITY
Lake 
#class127 COMPATIBILITY
Lava Flow 
#class128 COMPATIBILITY
Lava Lake 
#class129 COMPATIBILITY
Mushrooms 
#class130 COMPATIBILITY
Log 
#class131 COMPATIBILITY
Mandrake 
#class132 COMPATIBILITY
Moss 
#class133 COMPATIBILITY
Mound 
#class134 COMPATIBILITY
Mountain 
#class135 COMPATIBILITY
Oak Trees 
#class136 COMPATIBILITY
Outcropping 
#class137 COMPATIBILITY
Pine Trees 
#class138 COMPATIBILITY, Unused
Plant 
#class143 COMPATIBILITY
River Delta 
#class147 COMPATIBILITY
Rock 
#class148 COMPATIBILITY
Sand Dune 
#class149 COMPATIBILITY
Sand Pit 
#class150 COMPATIBILITY
Shrub 
#class151 COMPATIBILITY
Skull 
#class152 COMPATIBILITY, Unused
Stalagmite 
#class153 COMPATIBILITY
Stump 
#class154 COMPATIBILITY, Unused
Tar Pit 
#class155 COMPATIBILITY
Trees 
#class156 COMPATIBILITY, Unused
Vine 
#class157 COMPATIBILITY, Unused
Volcanic Vent 
#class158 COMPATIBILITY
Volcano 
#class159 COMPATIBILITY, Unused
Willow Trees 
#class160 COMPATIBILITY, Unused
Yucca Trees 
#class161 COMPATIBILITY
Reef 
#class162 COMPATIBILITY, Actionable
Random Monster 5 
#class163 COMPATIBILITY, Actionable
Random Monster 6 
#class164 COMPATIBILITY, Actionable
Random Monster 7 
#class165 COMPATIBILITY, Unused
Brush 
#class166 COMPATIBILITY, Unused
Bush 
#class167 COMPATIBILITY, Unused
Cactus 
#class168 COMPATIBILITY, Unused
Canyon 
#class169 COMPATIBILITY, Unused
Crater 
#class170 COMPATIBILITY, Unused
Dead Vegetation 
#class171 COMPATIBILITY, Unused
Flowers 
#class172 COMPATIBILITY, Unused
Frozen Lake 
#class173 COMPATIBILITY, Unused
Hedge 
#class174 COMPATIBILITY, Unused
Hill 
#class175 COMPATIBILITY, Unused
Hole 
#class176 COMPATIBILITY, Unused
Kelp 
#class177 COMPATIBILITY
Lake 
#class178 COMPATIBILITY, Unused
Lava Flow 
#class179 COMPATIBILITY, Unused
Lava Lake 
#class180 COMPATIBILITY, Unused
Mushrooms 
#class181 COMPATIBILITY, Unused
Log 
#class182 COMPATIBILITY, Unused
Mandrake 
#class183 COMPATIBILITY, Unused
Moss 
#class184 COMPATIBILITY, Unused
Mound 
#class185 COMPATIBILITY, Unused
Mountain 
#class186 COMPATIBILITY, Unused
Oak Trees 
#class187 COMPATIBILITY, Unused
Outcropping 
#class188 COMPATIBILITY, Unused
Pine Trees 
#class189 COMPATIBILITY, Unused
Plant 
#class190 COMPATIBILITY, Unused
River Delta 
#class191 COMPATIBILITY, Unused
Rock 
#class192 COMPATIBILITY, Unused
Sand Dune 
#class193 COMPATIBILITY, Unused
Sand Pit 
#class194 COMPATIBILITY, Unused
Shrub 
#class195 COMPATIBILITY, Unused
Skull 
#class196 COMPATIBILITY, Unused
Stalagmite 
#class197 COMPATIBILITY, Unused
Stump 
#class198 COMPATIBILITY, Unused
Tar Pit 
#class199 COMPATIBILITY
Trees 
#class200 COMPATIBILITY, Unused
Vine 
#class201 COMPATIBILITY, Unused
Volcanic Vent 
#class202 COMPATIBILITY, Unused
Volcano 
#class203 COMPATIBILITY, Unused
Willow Trees 
#class204 COMPATIBILITY, Unused
Yucca Trees 
#class205 COMPATIBILITY, Unused
Reef 
#class206 COMPATIBILITY
Desert Hills 
#class207 COMPATIBILITY
Dirt Hills 
#class208 COMPATIBILITY
Grass Hills 
#class209 COMPATIBILITY
Rough Hills 
#class210 COMPATIBILITY
Subterranean Rocks 
#class211 COMPATIBILITY
Swamp Foliage 
#class212 COMPATIBILITY, Actionable
Border Gate 
#class213 COMPATIBILITY, Actionable
Freelancer's Guild 
#class214 COMPATIBILITY, Actionable
Hero Placeholder 
#class215 COMPATIBILITY, Actionable
Quest Guard 
#class216 COMPATIBILITY, Actionable
Random Dwelling 
#class219 COMPATIBILITY, Actionable
Garrison 
#class220 COMPATIBILITY, Actionable
Mine 
#class221 COMPATIBILITY, Actionable
Trading Post 
#class222 COMPATIBILITY
Clover Field 
#class223 COMPATIBILITY
Cursed Ground 
#class224 COMPATIBILITY
Evil Fog 
#class225 COMPATIBILITY
Favorable Winds 
#class226 COMPATIBILITY
Fiery Fields 
#class227 COMPATIBILITY
Holy Ground 
#class228 COMPATIBILITY
Lucid Pools 
#class229 COMPATIBILITY
Magic Clouds 
#class230 COMPATIBILITY
Magic Plains 
#class231 COMPATIBILITY
Rocklands 
#SK
#sk0 COMPATIBILITY
Pathfinding 
#sk1 COMPATIBILITY
Archery 
#sk2 COMPATIBILITY
Logistics 
#sk3 COMPATIBILITY
Scouting 
#sk4 COMPATIBILITY
Diplomacy (affects only surrender cost) 
#sk5 COMPATIBILITY
Navigation 
#sk6 COMPATIBILITY
Leadership 
#sk7 COMPATIBILITY
Wisdom 
#sk8 COMPATIBILITY
Mysticism 
#sk9 COMPATIBILITY
Luck 
#sk10 COMPATIBILITY
Ballistics 
#sk11 COMPATIBILITY
Eagle Eye 
#sk12 COMPATIBILITY
Necromancy 
#sk13 COMPATIBILITY
Estates 
#sk14 COMPATIBILITY
Fire Magic 
#sk15 COMPATIBILITY
Air Magic 
#sk16 COMPATIBILITY
Water Magic 
#sk17 COMPATIBILITY
Earth Magic 
#sk18 COMPATIBILITY
Scholar 
#sk19 COMPATIBILITY
Tactics 
#sk20 COMPATIBILITY
Artillery 
#sk21 COMPATIBILITY
Learning 
#sk22 COMPATIBILITY
Offense 
#sk23 COMPATIBILITY
Armorer 
#sk24 COMPATIBILITY
Intelligence 
#sk25 COMPATIBILITY
Sorcery 
#sk26 COMPATIBILITY
Resistance 
#sk27 COMPATIBILITY
First Aid 
#SP
#sp0 COMPATIBILITY, L1, Adventure, Water
Summon Boat 
#sp1 COMPATIBILITY, L2, Adventure, Water
Scuttle Boat 
#sp2 COMPATIBILITY, L2, Adventure, Earth, Water, Fire, Air
Visions 
#sp3 COMPATIBILITY, L1, Adventure, Earth
View Earth 
#sp4 COMPATIBILITY, L2, Adventure, Air
Disguise 
#sp5 COMPATIBILITY, L1, Adventure, Air
View Air 
#sp6 COMPATIBILITY, L5, Adventure, Air
Fly 
#sp7 COMPATIBILITY, L4, Adventure, Water
Water Walk 
#sp8 COMPATIBILITY, L5, Adventure, Air
Dimension Door 
#sp9 COMPATIBILITY, L4, Adventure, Earth
Town Portal 
#sp10 COMPATIBILITY, L2, Combat, Earth
Quicksand 
#sp11 COMPATIBILITY, L3, Combat, Fire
Land Mine 
#sp12 COMPATIBILITY, L3, Combat, Earth
Force Field 
#sp13 COMPATIBILITY, L2, Combat, Fire
Fire Wall 
#sp14 COMPATIBILITY, L3, Combat, Earth
Earthquake 
#sp15 COMPATIBILITY, L1, Combat, Earth, Water, Fire, Air
Magic Arrow 
#sp16 COMPATIBILITY, L2, Combat, Water
Ice Bolt 
#sp17 COMPATIBILITY, L2, Combat, Air
Lightning Bolt 
#sp18 COMPATIBILITY, L5, Combat, Earth
Implosion 
#sp19 COMPATIBILITY, L4, Combat, Air
Chain Lightning 
#sp20 COMPATIBILITY, L3, Combat, Water
Frost Ring 
#sp21 COMPATIBILITY, L3, Combat, Fire
Fireball 
#sp22 COMPATIBILITY, L4, Combat, Fire
Inferno 
#sp23 COMPATIBILITY, L4, Combat, Earth
Meteor Shower 
#sp24 COMPATIBILITY, L2, Combat, Earth
Death Ripple 
#sp25 COMPATIBILITY, L3, Combat, Air
Destroy Undead 
#sp26 COMPATIBILITY, L4, Combat, Fire
Armageddon 
#sp27 COMPATIBILITY, L1, Combat, Earth
Shield 
#sp28 COMPATIBILITY, L3, Combat, Air
Air Shield 
#sp29 COMPATIBILITY, L4, Combat, Fire
Fire Shield 
#sp30 COMPATIBILITY, L2, Combat, Air
Protection from Air 
#sp31 COMPATIBILITY, L1, Combat, Fire
Protection from Fire 
#sp32 COMPATIBILITY, L1, Combat, Water
Prot. from Water 
#sp33 COMPATIBILITY, L3, Combat, Earth
Prot. from Earth 
#sp34 COMPATIBILITY, L3, Combat, Earth
Anti-Magic 
#sp35 COMPATIBILITY, L1, Combat, Water
Dispel 
#sp36 COMPATIBILITY, L5, Combat, Air
Magic Mirror 
#sp37 COMPATIBILITY, L1, Combat, Water
Cure 
#sp38 COMPATIBILITY, L4, Combat, Earth
Resurrection 
#sp39 COMPATIBILITY, L3, Combat, Earth
Animate Dead 
#sp40 COMPATIBILITY, L5, Combat, Fire
Sacrifice 
#sp41 COMPATIBILITY, L1, Combat, Water
Bless 
#sp42 COMPATIBILITY, L1, Combat, Fire
Curse 
#sp43 COMPATIBILITY, L1, Combat, Fire
Bloodlust 
#sp44 COMPATIBILITY, L2, Combat, Air
Precision 
#sp45 COMPATIBILITY, L2, Combat, Water
Weakness 
#sp46 COMPATIBILITY, L1, Combat, Earth
Stone Skin 
#sp47 COMPATIBILITY, L2, Combat, Air
Disrupting Ray 
#sp48 COMPATIBILITY, L4, Combat, Water
Prayer 
#sp49 COMPATIBILITY, L3, Combat, Water
Mirth 
#sp50 COMPATIBILITY, L4, Combat, Earth
Sorrow 
#sp51 COMPATIBILITY, L2, Combat, Air
Fortune 
#sp52 COMPATIBILITY, L3, Combat, Fire
Misfortune 
#sp53 COMPATIBILITY, L1, Combat, Air
Haste 
#sp54 COMPATIBILITY, L1, Combat, Earth
Slow 
#sp55 COMPATIBILITY, L4, Combat, Fire
Slayer (sssl
#sp56 COMPATIBILITY, L4, Combat, Fire
Frenzy 
#sp57 COMPATIBILITY, L5, Combat, Air
Titan's Lightning Bolt 
#sp58 COMPATIBILITY, L4, Combat, Air
Counterstrike (sscs
#sp59 COMPATIBILITY, L4, Combat, Fire
Berserk 
#sp60 COMPATIBILITY, L3, Combat, Air
Hypnotize 
#sp61 COMPATIBILITY, L3, Combat, Water
Forgetfulness 
#sp62 COMPATIBILITY, L2, Combat, Fire
Blind 
#sp63 COMPATIBILITY, L3, Combat, Water
Teleport 
#sp64 COMPATIBILITY, L2, Combat, Water
Remove Obstacle 
#sp65 COMPATIBILITY, L4, Combat, Water
Clone 
#sp66 COMPATIBILITY, L5, Combat, Fire
Fire Elemental 
#sp67 COMPATIBILITY, L5, Combat, Earth
Earth Elemental 
#sp68 COMPATIBILITY, L5, Combat, Water
Water Elemental 
#sp69 COMPATIBILITY, L5, Combat, Air
Air Elemental 
COMPATIBILITY, L3, Creature
Stone Gaze 
COMPATIBILITY, Creature
Poison 
COMPATIBILITY, Creature
Bind 
COMPATIBILITY, L2, Creature
Disease 
COMPATIBILITY, L4, Creature
Paralyze 
COMPATIBILITY, L5, Creature
Age 
COMPATIBILITY, Creature
Death Cloud 
COMPATIBILITY, Creature
Thunderbolt 
COMPATIBILITY, Creature
Dispel Helpful Spells 
COMPATIBILITY, Creature
Death Stare 
COMPATIBILITY, Creature
Acid breath 
#dbos
ID
['quest_message', [$const, [$borderGuard = $adve[18].'`{Audio XXX`}']]], 
ID
$qmsg = ['quest_message', [$const, [$adve[20].'`{Audio XXX`}']]], 
ID
$bmsg = ['bonus_message', [$const, [$adve[19].'`{Audio XXX`}']]], 
ID
['quest_message', [$const, [toMarkup($adve[22], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[21], '`{MoraleImage +1`}`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[30], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[29], '`{LuckImage +2`}`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [$msg = toMarkup($adve[38], '`{Audio XXX`}')]]], 
ID
toMarkup($adve[37])."`{Bonuses`}.\n\n`{BonusesImages`}`{Audio XXX`}", 
ID
['quest_message', [$const, [toMarkup($adve[50], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[49], '`{LuckImage +1`}`{Audio XXX`}')]]], 
ID
toMarkup($adve[52], '`{BonusesImages`}`{Audio XXX`}'), 
ID
toMarkup($adve[53], '`{BonusesImages`}`{Audio XXX`}'), 
ID
toMarkup($adve[51], '`{Audio XXX`}')]], 
ID
['quest_message', [$const, [toMarkup($adve[56], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[55], '`{LuckImage m`}`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[58], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[57], '`{MoraleImage +1`}`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[63], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [$luck = toMarkup($adve[62], '`{LuckImage +1`}`{Audio XXX`}')]], 'ifDateDay' => 1], 
ID
['bonus_message', [$const, [$morale = toMarkup($adve[62], '`{MoraleImage +1`}`{Audio XXX`}')]], 'ifDateDay' => 2], 
ID
['bonus_message', [$const, [toMarkup($adve[62], '`{LuckImage +1`} `{MoraleImage +1`}`{Audio XXX`}')]], 'ifDateDay' => 7], 
ID
['quest_message', [$const, [toMarkup($adve[65], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[64], '`{BonusesImages`}`{Audio XXX`}')]]], 
ID
toMarkup($adve[68], '`{Audio XXX`}'), 
ID
toMarkup($adve[67], '`{Audio XXX`}')]], 
ID
['bonus_message', [$const, [toMarkup($adve[66], '`{Audio XXX`}')]]], 
ID
str_replace('1000 gold', '`{Checks`}', toMarkup($adve[73], '`{Audio XXX`}')), 
ID
toMarkup($adve[72], '`{Audio XXX`}')]], 
ID
toMarkup($adve[76], '`{Audio XXX`}'), 
ID
toMarkup($adve[75], '`{Audio XXX`}')]], 
ID
['bonus_message', [$const, [toMarkup($adve[74], '`{Audio XXX`}')]]], 
ID
toMarkup($adve[78], '`{Audio XXX`}')]], 
ID
['bonus_message', [$const, [toMarkup($adve[77], '`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[82], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[83], '`{LuckImage +1`}`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[93], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[92], '`{BonusesImages`}`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[94], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[95], '`{MoraleImage +1`}`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[110], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[111], '`{MoraleImage +1`} `{LuckImage +1`}`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[115], '`{BonusesImages`}`{Audio XXX`}')]]], 
ID
['quest_message', [$custom, 'rules', 5, null, $seer[1][4].'`{Audio XXX`}'], 'stack' => array_search('quest', H3Effect::stack)], 
ID
['bonus_message', [$const, [sprintf(toMarkup($adve[127], '`{BonusesImages`}`{Audio XXX`}'), '`{Bonuses`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[136], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[137], '`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[144], '`{Audio XXX`}')]]], 
ID
['bonus_message', [$const, [toMarkup($adve[143], '`{BonusesImages`}`{Audio XXX`}')]]], 
ID
str_replace('2000 gold', '`{Checks`}', toMarkup($adve[150], '`{Audio XXX`}')), 
ID
toMarkup($adve[147], '`{Audio XXX`}')]], 
ID
['bonus_message', [$const, [toMarkup($adve[148], '`<`{StatImage experience`} +1 Level`>`{Audio XXX`}')]]], 
ID
['quest_message', [$const, [toMarkup($adve[156], '`{Audio XXX`}')]]], 
ID
str_replace("the '%s'", '`{Bonuses`}', toMarkup($adve[155], '`{BonusesImages`}`{Audio XXX`}')), 
ID
toMarkup($adve[154], '`{BonusesImages`}`{Audio XXX`}')]], 
ID
sprintf(toMarkup($adve[172], '`{Audio XXX`}'), '`SKILL'), 
ID
sprintf(toMarkup($adve[173], '`{Audio XXX`}'), '`SKILL')]], 
ID
[sprintf(toMarkup($adve[171], '`<`{SkillImage `SKILLID`, basic`} Basic `SKILL`>`{Audio XXX`}'), '`SKILL')]]], 
#url
<p><a href="#XXX">Modding samples</a></p> 
<p><a href="#XXX">Documentation</a></p>