This mod aims to implement proper spell slot restrictions for Specialist mages, forcing them to choose a spell from their school for their bonus spell slots.
Weidu should be able to install it so long as the section dealing with the Mage Spellbook hasn't already been altered. It uses REPLACE_TEXTUALLY for all its changes, if there is a better way I am open to suggestions.
The marker effects for this are applied to specialists at level 1 through the CLAB, so games already in progress will require force-casting a spell on existing specialists through the console. This marker effect will remain if the mod is uninstalled, but does nothing by itself and will not affect gameplay otherwise. This marker is a dummy sequencer, so the Sequencer Active portrait Icon will always be in effect, and it will show up as a blank line in the active Contingencies list.
It needs to be installed after any mods that add or alter Mage spells or creatures with specialist kits, so they can receive proper recognition.
ConsoleCast:
If you would prefer to manually edit UI.Menu:
Comment out "INCLUDE ~%MOD_FOLDER%\UIMENU.tpa~" in the "SPECIALIST_BOOK.tp2" file, its the last line.
Replace these three functions:
and all of menu 'MAGE':
Replaced initial version with colored version.
Added option to exclude empty spell levels.
Fixed an issue updating CLAB's.
Fixed issue with allowing non-standard spell filenames in specialist slots.
C:Eval("ActionOverride(Myself,ReallyForceSpellRES(\"MSCHOOL\",Msyelf))")
Only the 8 specialist mage kits will receive the marker, it will have no affect on others, including Wildmages and Mod-added kits. This effect will stack if used multiple times on the same creature.
Two install modes, each with option for 1 or 2 spell slot devotion:
- First option restricts slots even if none of a given school/level exist in game.
- Second does not restrict slots for a given school/level if none exist. This will give free spell selection for Schools(levels): Illusion(9), Transmutation(5, 8), Divination(7-9), and Enchantment(6-9) without modded added/altered spells. At present this option will only detect spell filenames SPWI[1-9][00-50].
Two optional components:
- First causes the extra spells slots from Edwin's Amulet to be treated the same way, requiring Conjuration Spells be put in them. The effect added to the Amulet can be added to anything, using "MSCHOOL#" as the resource, where # is the Spell School(as assigned to spells), forcing an extra slot of every spell level to go to that spell school.
- Second causes items that double spell slots(Evermemory) to also double forced specialist school slots.
Specialist School spells and slots are identified by colored background, specific to each school border:


function magePageInfo()
if bookMode == 0 then -- Regular
if characters[id].hasSorcererBook then
return t("SPELLS_CAN_CAST_LABEL") .. ": " .. characters[id].mageDetails[currentSpellLevel].slotsRemaining .. "/" .. characters[id].mageDetails[currentSpellLevel].maxMemorized
else
local num = 0
for k, v in pairs(specialistSlot) do
if v.resref ~= "NULL" then
num = num + 1
end
end
num = num + #bottomSpells
return t("MEMORIZED_LABEL") .. ": " .. num .. "/" .. characters[id].mageDetails[currentSpellLevel].maxMemorized
end
elseif bookMode == 1 then -- Sequencer/Contingency
return t("SPELLS_LABEL") .. " :" .. #bottomSpells .. "/" .. #bottomSpellsPlaceHolder
end
return ""
end
function refreshMageBook()
if currentSpellLevel == nil then
currentSpellLevel = 1
end
if bookMode == 0 then
if characters[id].hasMageBook then
bookSpells = characters[id].mageSpells[currentSpellLevel]
newBottomSpells = filterMemorizedMageSpells()
if showMageMemorizationFlash == true then
createMageMemorizationSparkle(0,0,36,36,"memorizedListMage", findFirstDifferenceInSpellList(bottomSpells, newBottomSpells))
showMageMemorizationFlash = false
end
bottomSpells = newBottomSpells
getSpecialistSlot()
local x,y,w,z = Infinity_GetArea('memorizedListMage') h = x - 72 newX = #specialistSlot * 36 - h
adjustItemGroup({'memorizedListMage', 'spellframeMageList', },newX,0,0,0)
bottomSpellsPlaceHolder = makeBlankTable(characters[id].mageDetails[currentSpellLevel].maxMemorized)
for index = 1, #currentSchool[currentSpellLevel], 1 do
table.remove (bottomSpellsPlaceHolder, 1)
end
else
bookSpells = characters[id].mageSpells[currentSpellLevel]
bottomSpells = {}
bottomSpellsPlaceHolder = {}
end
elseif bookMode == 1 then
bookSpells = filterContingencyMageSpells()
bottomSpells = sequencerSpells
bottomSpellsPlaceHolder = makeBlankTable(contingencyMaxSpells)
contingencyDescription = mageBookStrings[contingencyResRef].tip
end
end
function setMageBookLevel(num)
currentBookSpell = 0
currentSpellLevel = num
specialistBookFilter()
mageScreen:SetSpellLevel(num-1)
refreshMageBook()
end
menu
{
name 'MAGE'
align center center
greyscale lua "mageBookEnabled == false"
modal lua "bookMode == 1"
onopen "
showMageMemorizationFlash = false
mgpage = nil
setMageBookLevel(1)
if showContingency then
Infinity_SetArea('bookListMage', nil, 374, nil, 200)
Infinity_SetArea('bookDescription', nil, 374, nil, 200)
else
Infinity_SetArea('bookListMage', nil, 174, nil, 400)
Infinity_SetArea('bookDescription', nil, 174, nil, 400)
end
if bookMode == 1 then
contingencyDescription = mageBookStrings[contingencyResRef].tip
end
currentContingencyCondition = 0
currentContingencyTarget = 0
currentAnimationID = 1
updateCounterMemorizationSparkles = 1
"
onclose "
"
button
{
enabled "CurrentlyInGame()"
on escape
action
"
--Return to world screen on escape
e:SelectEngine(worldScreen)
"
}
template
{
label
{
enabled "showMemorizationSparkle(instanceId)"
ignoreEvents
area 0 0 45 42
bam "FLASHBR"
usealpha lua "true"
frame lua "memorizationFlashes[instanceId][2]"
align center center
}
name "TEMPLATE_mageMemorizationSparkle"
}
label
{
area 0 0 864 710
mosaic "GUIMGB2"
}
label
{
area 82 10 700 44
text lua "mageBookTitle()"
text style "title"
}
label
{
area 210 59 446 30
text lua "mageBookAction()"
text style "label"
}
button
{
area 168 110 54 54
bam GUIPRTC
highlightgroup mgpage
sequence 0
enabled "maxMagePage > 0"
action "setMageBookLevel(1)"
}
button
{
area 227 110 54 54
bam GUIPRTC
highlightgroup mgpage
sequence 1
enabled "maxMagePage > 1"
action "setMageBookLevel(2)"
}
button
{
area 286 110 54 54
bam GUIPRTC
highlightgroup mgpage
sequence 2
enabled "maxMagePage > 2"
action "setMageBookLevel(3)"
}
button
{
area 345 110 54 54
bam GUIPRTC
highlightgroup mgpage
sequence 3
enabled "maxMagePage > 3"
action "setMageBookLevel(4)"
}
button
{
area 404 110 54 54
bam GUIPRTC
highlightgroup mgpage
sequence 4
enabled "maxMagePage > 4"
action "setMageBookLevel(5)"
}
button
{
area 464 110 54 54
bam GUIPRTC
highlightgroup mgpage
sequence 5
enabled "maxMagePage > 5"
action "setMageBookLevel(6)"
}
button
{
area 523 110 54 54
bam GUIPRTC
highlightgroup mgpage
sequence 6
enabled "maxMagePage > 6"
action "setMageBookLevel(7)"
}
button
{
area 583 110 54 54
bam GUIPRTC
highlightgroup mgpage
sequence 7
enabled "maxMagePage > 7"
action "setMageBookLevel(8)"
}
button
{
area 644 110 54 54
bam GUIPRTC
highlightgroup mgpage
sequence 8
enabled "maxMagePage > 8"
action "setMageBookLevel(9)"
}
list
{
column
{
width 15
label
{
area 0 0 -1 -1
bam lua "bookSpells[rowNumber].icon"
enabled "specialistIcon(0)"
align center center
}
label { area 2 2 36 36 icon lua "bookSpells[rowNumber].icon" greyscale lua "bookSpells[rowNumber].castable == 0"
enabled "specialistIcon('1')" progressbar 100 progressbar full 0 63 0 0 align center center }
label { area 2 2 36 36 icon lua "bookSpells[rowNumber].icon" greyscale lua "bookSpells[rowNumber].castable == 0"
enabled "specialistIcon('2')" progressbar 100 progressbar full 31 0 63 0 align center center }
label { area 2 2 36 36 icon lua "bookSpells[rowNumber].icon" greyscale lua "bookSpells[rowNumber].castable == 0"
enabled "specialistIcon('3')" progressbar 100 progressbar full 0 0 0 0 align center center }
label { area 2 2 36 36 icon lua "bookSpells[rowNumber].icon" greyscale lua "bookSpells[rowNumber].castable == 0"
enabled "specialistIcon('4')" progressbar 100 progressbar full 63 63 0 0 align center center }
label { area 2 2 36 36 icon lua "bookSpells[rowNumber].icon" greyscale lua "bookSpells[rowNumber].castable == 0"
enabled "specialistIcon('5')" progressbar 100 progressbar full 63 0 63 0 align center center }
label { area 2 2 36 36 icon lua "bookSpells[rowNumber].icon" greyscale lua "bookSpells[rowNumber].castable == 0"
enabled "specialistIcon('6')" progressbar 100 progressbar full 63 0 0 0 align center center }
label { area 2 2 36 36 icon lua "bookSpells[rowNumber].icon" greyscale lua "bookSpells[rowNumber].castable == 0"
enabled "specialistIcon('7')" progressbar 100 progressbar full 0 63 63 0 align center center }
label { area 2 2 36 36 icon lua "bookSpells[rowNumber].icon" greyscale lua "bookSpells[rowNumber].castable == 0"
enabled "specialistIcon('8')" progressbar 100 progressbar full 0 0 127 0 align center center }
}
column
{
width 85
label
{
area 0 0 -1 -1
text lua "Infinity_FetchString( bookSpells[rowNumber].name)"
text style "normal_parchment"
text align left center
}
}
area 94 174 316 400
name "bookListMage"
rowheight 40
table "bookSpells"
var currentBookSpell
scrollbar 'GUISCRC'
action
"
contingencyDescription = 0
if cellNumber == 1 then
if bookMode == 0 then
if #bottomSpells < #bottomSpellsPlaceHolder or SpecialistMemorize() then
createMageMemorizationSparkle(1, 0, 40, 40, 'bookListMage', -1)
Infinity_PlaySound('GAM_24')
showMageMemorizationFlash = true
mageScreen:MemorizeSpell( bookSpells[currentBookSpell].level, bookSpells[currentBookSpell].index )
end
elseif bookMode == 1 and #bottomSpells < #bottomSpellsPlaceHolder then
mageScreen:SequenceSpell( bookSpells[currentBookSpell].resref )
end
end
if lastCurrentBookSpell == currentBookSpell and cellNumber == 2 then
currentBookSpell = 0
contingencyDescription = mageBookStrings[contingencyResRef].tip
end
lastCurrentBookSpell = currentBookSpell
"
actionalt
"
if cellNumber == 1 and bookMode == 0 and characters[id].hasSorcererBook == false then
popup2Button(24485, 'REMOVE_BUTTON', function() mageScreen:EraseKnownSpell(bookSpells[currentBookSpell].resref) end)
end
"
}
label
{
area 100 178 314 192
rectangle 1
rectangle opacity 200
enabled "showContingency"
}
label
{
area 104 182 310 48
enabled "showContingency"
text "CONDITION_NORMAL"
text style "normal"
}
list
{
column
{
width 100
label
{
area 10 0 -1 -1
text lua "Infinity_FetchString( contingencyConditions[rowNumber].strref)"
text style "normal"
text align left center
}
}
area 104 224 310 141
enabled "showContingency"
rowheight 40
table "contingencyConditions"
var currentContingencyCondition
scrollbar 'GUISCRC'
action
"
contingencyDescription = contingencyConditions[currentContingencyCondition].desc
"
}
label
{
area 452 176 322 193
rectangle 1
rectangle opacity 200
enabled "showContingency"
}
label
{
area 464 176 310 48
enabled "showContingency"
text "TARGET_NORMAL"
text style "normal"
}
list
{
column
{
width 100
label
{
area 10 0 -1 -1
text lua "Infinity_FetchString( contingencyTargets[rowNumber].strref)"
text style "normal"
text align left center
}
}
area 458 228 316 141
enabled "showContingency"
rowheight 40
table "contingencyTargets"
var currentContingencyTarget
scrollbar 'GUISCRC'
action
"
contingencyDescription = contingencyTargets[currentContingencyTarget].desc
"
}
text
{
name "bookDescription"
area 452 174 316 400
text lua "mageBookDescription()"
text style "normal_parchment"
scrollbar 'GUISCRC'
}
list
{
column
{
width 100
label
{
area 0 0 -1 -1
bam "SPELFRMS"
sequence 0
align center center
}
}
area 72 658 714 36
name "spellframeMageList"
enabled "#bottomSpellsPlaceHolder ~= 0 or bookMode == 1"
rowwidth 36
table "bottomSpellsPlaceHolder"
}
list
{
column
{
width 100
label { area 0 0 -1 -1 enabled "isSpecialist(rowNumber, '1')" progressbar 100 progressbar full 0 63 0 0 align center center }
label { area 0 0 -1 -1 enabled "isSpecialist(rowNumber, '2')" progressbar 100 progressbar full 31 0 63 0 align center center }
label { area 0 0 -1 -1 enabled "isSpecialist(rowNumber, '3')" progressbar 100 progressbar full 0 0 0 0 align center center }
label { area 0 0 -1 -1 enabled "isSpecialist(rowNumber, '4')" progressbar 100 progressbar full 63 63 0 0 align center center }
label { area 0 0 -1 -1 enabled "isSpecialist(rowNumber, '5')" progressbar 100 progressbar full 63 0 63 0 align center center }
label { area 0 0 -1 -1 enabled "isSpecialist(rowNumber, '6')" progressbar 100 progressbar full 63 0 0 0 align center center }
label { area 0 0 -1 -1 enabled "isSpecialist(rowNumber, '7')" progressbar 100 progressbar full 0 63 63 0 align center center }
label { area 0 0 -1 -1 enabled "isSpecialist(rowNumber, '8')" progressbar 100 progressbar full 0 0 127 0 align center center }
}
name "specframeListMage"
area 72 658 714 36
rowwidth 36
table "specialistSlot"
enabled "not isSpecialist(0, 0) and characters[id].mageDetails[currentSpellLevel].maxMemorized > 0"
}
list
{
column
{
width 100
button { area 0 0 -1 -1 bam lua "specialistSlot[rowNumber].icon" greyscale lua "specialistSlot[rowNumber].castable == 0" enabled "isSpecialist(rowNumber, '1')" align center center }
button { area 0 0 -1 -1 bam lua "specialistSlot[rowNumber].icon" greyscale lua "specialistSlot[rowNumber].castable == 0" enabled "isSpecialist(rowNumber, '2')" align center center }
button { area 0 0 -1 -1 bam lua "specialistSlot[rowNumber].icon" greyscale lua "specialistSlot[rowNumber].castable == 0" enabled "isSpecialist(rowNumber, '3')" align center center }
button { area 0 0 -1 -1 bam lua "specialistSlot[rowNumber].icon" greyscale lua "specialistSlot[rowNumber].castable == 0" enabled "isSpecialist(rowNumber, '4')" align center center }
button { area 0 0 -1 -1 bam lua "specialistSlot[rowNumber].icon" greyscale lua "specialistSlot[rowNumber].castable == 0" enabled "isSpecialist(rowNumber, '5')" align center center }
button { area 0 0 -1 -1 bam lua "specialistSlot[rowNumber].icon" greyscale lua "specialistSlot[rowNumber].castable == 0" enabled "isSpecialist(rowNumber, '6')" align center center }
button { area 0 0 -1 -1 bam lua "specialistSlot[rowNumber].icon" greyscale lua "specialistSlot[rowNumber].castable == 0" enabled "isSpecialist(rowNumber, '7')" align center center }
button { area 0 0 -1 -1 bam lua "specialistSlot[rowNumber].icon" greyscale lua "specialistSlot[rowNumber].castable == 0" enabled "isSpecialist(rowNumber, '8')" align center center }
}
name "specialistListMage"
area 72 658 714 36
enabled "not isSpecialist(0, 0) and characters[id].mageDetails[currentSpellLevel].maxMemorized > 0"
rowwidth 36
table "specialistSlot"
var currentBottomSpell
clickable lua "specialistSlot[rowNumber].resref ~= 'NULL'"
action "actionSpecMemorize(currentBottomSpell)"
}
list
{
column
{
width 100
label
{
area 0 0 -1 -1
bam lua "bottomSpells[rowNumber].icon"
align center center
greyscale lua "bottomSpells[rowNumber].castable == 0"
}
}
area 72 658 718 36
name "memorizedListMage"
enabled "#bottomSpells ~= 0"
rowwidth 36
table "bottomSpells"
var currentBottomSpell
action
"
if bookMode == 0 then
showMageMemorizationFlash = false
mageScreen:UnmemorizeSpell( bottomSpells[currentBottomSpell].level, bottomSpells[currentBottomSpell].memorizedIndex )
Infinity_PlaySound('GAM_44')
elseif bookMode == 1 then
mageScreen:UnSequenceSpell( bottomSpells[currentBottomSpell].resref )
table.remove(sequencerSpells, currentBottomSpell)
bottomSpells = sequencerSpells
currentBottomSpell = 0
end
"
}
label
{
area 282 594 300 40
text lua "magePageInfo()"
text style "label"
rectangle 0
}
button
{
area 582 594 230 44
enabled "bookMode == 0 and (#characters[id].contingencySpells > 0 or #characters[id].sequencerSpells > 0)"
bam GUIBUTMT
text "CONTINGENCY_BUTTON"
text style "button"
action "Infinity_PushMenu('MAGE_CONTINGENCY')"
}
button
{
area 52 594 230 44
bam GUIBUTMT
enabled "bookMode == 1 or characters[id].hasMageBook"
clickable lua "SpecialistMemorize() and currentBookSpell ~= 0"
text "MEMORIZE_BUTTON"
text style "button"
action
"
if bookMode == 0 then
createMageMemorizationSparkle(1, 0, 40, 40, 'bookListMage', -1)
showMageMemorizationFlash = true
mageScreen:MemorizeSpell( characters[id].mageSpells[currentSpellLevel][currentBookSpell].level, characters[id].mageSpells[currentSpellLevel][currentBookSpell].index )
elseif bookMode == 1 then
mageScreen:SequenceSpell( bookSpells[currentBookSpell].resref )
end
"
}
button
{
area 582 594 230 44
enabled "bookMode == 1"
bam GUIBUTMT
text lua "contingencyDoneButtonText()"
text style "button"
action
"
if contingencyComplete() then
mageScreen:DoneSequencingSpells()
else
mageScreen:CancelSequencingSpells()
end
e:SelectEngine(worldScreen)
"
}
}