diff options
author | Bardur Arantsson <bardur@scientician.net> | 2010-01-08 20:28:34 +0100 |
---|---|---|
committer | Bardur Arantsson <bardur@scientician.net> | 2010-01-08 23:46:06 +0100 |
commit | 6aa48afdd57d03314fdf4be6c9da911c32277c84 (patch) | |
tree | 2dc401f9aae2dc6736d2fc3811c8f8099d3eabe6 /lib/scpt |
Import tome-2.3.5.
Diffstat (limited to 'lib/scpt')
39 files changed, 9216 insertions, 0 deletions
diff --git a/lib/scpt/bounty.lua b/lib/scpt/bounty.lua new file mode 100644 index 00000000..94c15598 --- /dev/null +++ b/lib/scpt/bounty.lua @@ -0,0 +1,90 @@ +-- The bounty quest! bring back corpses to increase your monster lore skill + +add_quest +{ + ["global"] = "BOUNTY_QUEST", + ["name"] = "Bounty quest", + ["desc"] = function() + if quest(BOUNTY_QUEST).status == QUEST_STATUS_TAKEN then + print_hook("#####yBounty quest!\n") + print_hook("You must bring back "..monster_race_desc(bounty_quest_monster, 0).." corpse to the beastmaster.\n") + print_hook("\n") + end + end, + ["level"] = -1, + ["data"] = { + ["bounty_quest_monster"] = 0, + }, + ["hooks"] = { + -- Start the game without the quest, need to request it + [HOOK_BIRTH_OBJECTS] = function() + quest(BOUNTY_QUEST).status = QUEST_STATUS_UNTAKEN + end, + }, +} + +add_building_action +{ + -- Index is used in ba_info.txt to set the actions + ["index"] = 54, + ["action"] = function() + if quest(BOUNTY_QUEST).status == QUEST_STATUS_UNTAKEN then + quest(BOUNTY_QUEST).status = QUEST_STATUS_TAKEN + bounty_quest_monster = get_new_bounty_monster(3 + ((player.lev * 3) / 2)) + + msg_print("You must bring me back "..monster_race_desc(bounty_quest_monster, 0).." corpse.") + else + msg_print("You still must bring me back "..monster_race_desc(bounty_quest_monster, 0).." corpse.") + end + end +} + +add_building_action +{ + -- Index is used in ba_info.txt to set the actions + ["index"] = 55, + ["action"] = function() + if quest(BOUNTY_QUEST).status == QUEST_STATUS_TAKEN then + local ret, item + + -- Ask for an item + ret, item = get_item("What corpse to return?", + "You have no corpse to return.", + bor(USE_INVEN), + function (obj) + if (obj.tval == TV_CORPSE) and (obj.pval2 == bounty_quest_monster) then + return TRUE + end + return FALSE + end + ) + + -- Ok we got the corpse! + if ret == TRUE then + -- Take the corpse from the inventory + inven_item_increase(item, -1) + inven_item_optimize(item) + + msg_print("Ah well done adventurer!") + msg_print("As a reward I will teach you a bit of monster lore.") + + if skill(SKILL_LORE).mod == 0 then + skill(SKILL_LORE).mod = 900 + skill(SKILL_LORE).dev = TRUE + end + skill(SKILL_LORE).value = skill(SKILL_LORE).value + skill(SKILL_LORE).mod + if skill(SKILL_PRESERVATION).mod == 0 then + skill(SKILL_PRESERVATION).value = 800 + skill(SKILL_PRESERVATION).mod = 800 + skill(SKILL_PRESERVATION).dev = TRUE + msg_print("I see you don't know the corpse preservation skill, I shall teach you it too.") + end + + quest(BOUNTY_QUEST).status = QUEST_STATUS_UNTAKEN + bounty_quest_monster = 0 + end + else + msg_print("You do not have any bounty quest yet.") + end + end +} diff --git a/lib/scpt/corrupt.lua b/lib/scpt/corrupt.lua new file mode 100644 index 00000000..6a7ae0e5 --- /dev/null +++ b/lib/scpt/corrupt.lua @@ -0,0 +1,440 @@ +-- Definition of the corruptions + +-- The Balrog corruptions +CORRUPT_BALROG_AURA = add_corruption +{ + ["color"] = TERM_ORANGE, + ["name"] = "Balrog Aura", + ["get_text"] = "A corrupted wall of flames surrounds you.", + ["lose_text"] = "The wall of corrupted flames abandons you.", + ["desc"] = + { + " Surrounds you with a fiery aura", + " But it can burn scrolls when you read them" + }, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + player.xtra_f3 = bor(player.xtra_f3, TR3_SH_FIRE) + player.xtra_f3 = bor(player.xtra_f3, TR3_LITE1) + end, + [HOOK_READ] = function(obj) + if magik(5) == TRUE then + msg_print("Your demon aura burns the scroll before you read it!") + return TRUE, TRUE, FALSE + else + return FALSE + end + end, + }, +} + +CORRUPT_BALROG_WINGS = add_corruption +{ + ["color"] = TERM_ORANGE, + ["name"] = "Balrog Wings", + ["get_text"] = "Wings of shadow grow in your back.", + ["lose_text"] = "The wings in your back fall apart.", + ["desc"] = + { + " Creates ugly, but working, wings allowing you to fly", + " But it reduces charisma by 4 and dexterity by 2" + }, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + player.xtra_f4 = bor(player.xtra_f4, TR4_FLY) + player.modify_stat(A_CHR, -4) + player.modify_stat(A_DEX, -2) + end, + }, +} + +CORRUPT_BALROG_STRENGTH = add_corruption +{ + ["color"] = TERM_ORANGE, + ["name"] = "Balrog Strength", + ["get_text"] = "Your muscles get unnatural strength.", + ["lose_text"] = "Your muscles get weaker again.", + ["desc"] = + { + " Provides 3 strength and 1 constitution", + " But it reduces charisma by 1 and dexterity by 3" + }, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + player.modify_stat(A_STR, 3) + player.modify_stat(A_CON, 1) + player.modify_stat(A_DEX, -3) + player.modify_stat(A_CHR, -1) + end, + }, +} + +CORRUPT_BALROG_FORM = add_corruption +{ + ["color"] = TERM_YELLOW, + ["name"] = "Balrog Form", + ["get_text"] = "You feel the might of a Balrog inside you.", + ["lose_text"] = "The presence of the Balrog seems to abandon you.", + ["desc"] = + { + " Allows you to turn into a Balrog at will", + " You need Balrog Wings, Balrog Aura and Balrog Strength to activate it" + }, + ["depends"] = + { + [CORRUPT_BALROG_AURA] = TRUE, + [CORRUPT_BALROG_WINGS] = TRUE, + [CORRUPT_BALROG_STRENGTH] = TRUE + }, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + player.xtra_f2 = bor(player.xtra_f2, TR2_IM_ACID) + player.xtra_f2 = bor(player.xtra_f2, TR2_IM_FIRE) + player.xtra_f2 = bor(player.xtra_f2, TR2_IM_ELEC) + player.xtra_f2 = bor(player.xtra_f2, TR2_RES_DARK) + player.xtra_f2 = bor(player.xtra_f2, TR2_RES_CHAOS) + end, + [HOOK_CALC_POWERS] = function() + player.add_power(PWR_BALROG) + end, + }, +} + + +-- The Demon corruptions +CORRUPT_DEMON_SPIRIT = add_corruption +{ + ["color"] = TERM_RED, + ["name"] = "Demon Spirit", + ["get_text"] = "Your spirit opens to corrupted thoughts.", + ["lose_text"] = "Your spirit closes again to the corrupted thoughts.", + ["desc"] = + { + " Increases your intelligence by 1", + " But reduce your charisma by 2", + }, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + player.modify_stat(A_INT, 1) + player.modify_stat(A_CHR, -2) + end, + }, +} + +CORRUPT_DEMON_HIDE = add_corruption +{ + ["color"] = TERM_RED, + ["name"] = "Demon Hide", + ["get_text"] = "Your skin grows into a thick hide.", + ["lose_text"] = "Your skin returns to a natural state.", + ["desc"] = + { + " Increases your armour class by your level", + " Provides immunity to fire at level 40", + " But reduces speed by your level / 7", + }, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + player.to_a = player.to_a + player.lev + player.dis_to_a = player.dis_to_a + player.lev + player.pspeed = player.pspeed - (player.lev / 7) + if player.lev >= 40 then player.xtra_f2 = bor(player.xtra_f2, TR2_IM_FIRE) end + end, + }, +} + +CORRUPT_DEMON_BREATH = add_corruption +{ + ["color"] = TERM_RED, + ["name"] = "Demon Breath", + ["get_text"] = "Your breath becomes mephitic.", + ["lose_text"] = "Your breath is once again normal.", + ["desc"] = + { + " Provides fire breath", + " But gives a small chance to spoil potions when you quaff them", + }, + ["hooks"] = + { + [HOOK_CALC_POWERS] = function() + player.add_power(PWR_BR_FIRE) + end, + [HOOK_QUAFF] = function(obj) + if magik(9) == TRUE then + msg_print("Your demon breath spoils the potion!") + return TRUE, FALSE + else + return FALSE + end + end, + }, +} + +CORRUPT_DEMON_REALM = add_corruption +{ + ["color"] = TERM_L_RED, + ["name"] = "Demon Realm", + ["get_text"] = "You feel more attuned to the demon realm.", + ["lose_text"] = "You lose your attunement to the demon realm.", + ["desc"] = + { + " Provides access to the demon school skill and the use of demonic equipment", + " You need Demon Spirit, Demon Hide and Demon Breath to activate it" + }, + ["depends"] = + { + [CORRUPT_DEMON_SPIRIT] = TRUE, + [CORRUPT_DEMON_HIDE] = TRUE, + [CORRUPT_DEMON_BREATH] = TRUE + }, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + -- 1500 may seem a lot, but people are rather unlikely to get the corruption very soon + -- due to the dependencies + if s_info[SKILL_DAEMON + 1].mod == 0 then s_info[SKILL_DAEMON + 1].mod = 1500 end + s_info[SKILL_DAEMON + 1].hidden = FALSE; + end, + }, +} + + +-- Teleportation corruptions + +-- Random teleportation will ask for confirmation 70% of the time +-- But 30% of the time it will teleport, without asking +CORRUPT_RANDOM_TELEPORT = add_corruption +{ + ["color"] = TERM_GREEN, + ["name"] = "Random teleportation", + ["get_text"] = "Space seems to fizzle around you.", + ["lose_text"] = "Space solidify again around you.", + ["desc"] = + { + " Randomly teleports you around", + }, + -- No oppose field, it will be automatically set when we declare the anti-telep corruption to oppose us + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + player.xtra_f3 = bor(player.xtra_f3, TR3_TELEPORT) + end, + [HOOK_PROCESS_WORLD] = function() + if rand_int(300) == 1 then + if magik(70) == TRUE then + if get_check("Teleport?") == TRUE then + teleport_player(50) + end + else + disturb(0, 0) + msg_print("Your corruption takes over you, you teleport!") + teleport_player(50) + end + end + end, + }, +} + +-- Anti-teleportation corruption, can be stopped with this power +CORRUPT_ANTI_TELEPORT = add_corruption +{ + ["color"] = TERM_GREEN, + ["name"] = "Anti-teleportation", + ["get_text"] = "Space continuum freezes around you.", + ["lose_text"] = "Space continuum can once more be altered around you.", + ["desc"] = + { + " Prevents all teleportations, be it of you or monsters", + }, + ["oppose"] = + { + [CORRUPT_RANDOM_TELEPORT] = TRUE + }, + ["hooks"] = + { + [HOOK_BIRTH_OBJECTS] = function() + player.corrupt_anti_teleport_stopped = FALSE + end, + [HOOK_CALC_POWERS] = function() + player.add_power(POWER_COR_SPACE_TIME) + end, + [HOOK_CALC_BONUS] = function() + if player.corrupt_anti_teleport_stopped == FALSE then + player.resist_continuum = TRUE + end + end, + [HOOK_PROCESS_WORLD] = function() + if player.corrupt_anti_teleport_stopped == TRUE then + local amt = player.msp + player.csp + amt = amt / 100 + if (amt < 1) then amt = 1 end + increase_mana(-amt) + if player.csp == 0 then + player.corrupt_anti_teleport_stopped = FALSE + msg_print("You stop controlling your corruption.") + player.update = bor(player.update, PU_BONUS) + end + end + end, + }, +} + + +-- Troll blood +CORRUPT_TROLL_BLOOD = add_corruption +{ + ["color"] = TERM_GREEN, + ["name"] = "Troll Blood", + ["get_text"] = "Your blood thickens, you sense corruption in it.", + ["lose_text"] = "Your blood returns to a normal state.", + ["desc"] = + { + " Troll blood flows in your veins, granting increased regeneration", + " It also enables you to feel the presence of other troll beings", + " But it will make your presence more noticeable and aggravating", + }, + ["can_gain"] = function() + -- Ok trolls should not get this one. never. + if get_race_name() == "Troll" then + return nil + else + return not nil + end + end, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + player.xtra_f3 = bor(player.xtra_f3, TR3_REGEN, TR3_AGGRAVATE) + player.xtra_esp = bor(player.xtra_esp, ESP_TROLL) + end, + }, +} + +-- The vampire corruption set +CORRUPT_VAMPIRE_TEETH = add_corruption +{ + ["group"] = "Vampire", + ["removable"] = FALSE, + ["color"] = TERM_L_DARK, + ["name"] = "Vampiric Teeth", + ["get_text"] = "You grow vampiric teeth!", + ["lose_text"] = "BUG! this should not happen", + ["desc"] = + { + " Your teeth allow you to drain blood to feed yourself", + " However your stomach now only accepts blood.", + }, + ["allow"] = function() + if test_race_flags(1, PR1_NO_SUBRACE_CHANGE) == FALSE then return not nil else return nil end + end, + ["gain"] = function() + switch_subrace(SUBRACE_SAVE, TRUE); + + subrace_add_power(subrace(SUBRACE_SAVE), PWR_VAMPIRISM) + subrace(SUBRACE_SAVE).flags1 = bor(subrace(SUBRACE_SAVE).flags1, PR1_VAMPIRE, PR1_UNDEAD, PR1_NO_SUBRACE_CHANGE) + end, + ["hooks"] = + { + }, +} +CORRUPT_VAMPIRE_STRENGTH = add_corruption +{ + ["group"] = "Vampire", + ["removable"] = FALSE, + ["color"] = TERM_L_DARK, + ["name"] = "Vampiric Strength", + ["get_text"] = "Your body seems more dead than alive.", + ["lose_text"] = "BUG! this should not happen", + ["desc"] = + { + " Your body seems somewhat dead", + " In this near undead state it has improved strength, constitution and intelligence", + " But reduced dexterity, wisdom and charisma.", + }, + ["depends"] = + { + [CORRUPT_VAMPIRE_TEETH] = TRUE, + }, + ["gain"] = function() + -- Apply the bonuses/penalities + subrace(SUBRACE_SAVE).r_mhp = subrace(SUBRACE_SAVE).r_mhp + 1 + subrace(SUBRACE_SAVE).r_exp = subrace(SUBRACE_SAVE).r_exp + 100 + + subrace(SUBRACE_SAVE).r_adj[A_STR + 1] = subrace(SUBRACE_SAVE).r_adj[A_STR + 1] + 3 + subrace(SUBRACE_SAVE).r_adj[A_INT + 1] = subrace(SUBRACE_SAVE).r_adj[A_INT + 1] + 2 + subrace(SUBRACE_SAVE).r_adj[A_WIS + 1] = subrace(SUBRACE_SAVE).r_adj[A_WIS + 1] - 3 + subrace(SUBRACE_SAVE).r_adj[A_DEX + 1] = subrace(SUBRACE_SAVE).r_adj[A_DEX + 1] - 2 + subrace(SUBRACE_SAVE).r_adj[A_CON + 1] = subrace(SUBRACE_SAVE).r_adj[A_CON + 1] + 1 + subrace(SUBRACE_SAVE).r_adj[A_CHR + 1] = subrace(SUBRACE_SAVE).r_adj[A_CHR + 1] - 4 + + -- be reborn! + do_rebirth() + cmsg_print(TERM_L_DARK, "You feel death slipping inside.") + end, + ["hooks"] = + { + }, +} +CORRUPT_VAMPIRE_VAMPIRE = add_corruption +{ + ["group"] = "Vampire", + ["removable"] = FALSE, + ["color"] = TERM_L_DARK, + ["name"] = "Vampire", + ["get_text"] = "You die to be reborn in a Vampire form.", + ["lose_text"] = "BUG! this should not happen", + ["desc"] = + { + " You are a Vampire. As such you resist cold, poison, darkness and nether.", + " Your life is sustained, but you cannot stand the light of the sun." + }, + ["depends"] = + { + [CORRUPT_VAMPIRE_STRENGTH] = TRUE, + }, + ["gain"] = function() + -- Be a Vampire and be proud of it + local title = get_subrace_title(SUBRACE_SAVE) + if title == " " or title == "Vampire" then + title = "Vampire" + subrace(SUBRACE_SAVE).place = FALSE + else + title = "Vampire "..title + end + set_subrace_title(SUBRACE_SAVE, title) + + -- Bonus/and .. not bonus :) + subrace(SUBRACE_SAVE).flags1 = bor(subrace(SUBRACE_SAVE).flags1, PR1_HURT_LITE) + subrace(SUBRACE_SAVE).oflags2[2] = bor(subrace(SUBRACE_SAVE).oflags2[2], TR2_RES_POIS, TR2_RES_NETHER, TR2_RES_COLD, TR2_RES_DARK, TR2_HOLD_LIFE) + subrace(SUBRACE_SAVE).oflags3[2] = bor(subrace(SUBRACE_SAVE).oflags3[2], TR3_LITE1) + end, + ["hooks"] = + { + }, +} + + +--[[ +CORRUPT_ = add_corruption +{ + ["color"] = TERM_GREEN, + ["name"] = "", + ["get_text"] = "", + ["lose_text"] = "", + ["desc"] = + { + " ", + }, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + end, + }, +} +]] diff --git a/lib/scpt/drunk.lua b/lib/scpt/drunk.lua new file mode 100644 index 00000000..7d90af8d --- /dev/null +++ b/lib/scpt/drunk.lua @@ -0,0 +1,21 @@ +-- silly function that allows a drunk to take a bottle of wine/ale from the player + +function drunk_takes_wine(m_idx, item) + + m_ptr = monster(m_idx) + o_ptr = get_object(item) + + if (m_ptr.r_idx == test_monster_name("Singing, happy drunk")) + and (o_ptr.tval == TV_FOOD) and ((o_ptr.sval == 38) or (o_ptr.sval == 39)) then + + cmsg_print(TERM_YELLOW, "'Hic!'") + + inven_item_increase(item, -1) + inven_item_optimize(item) + return TRUE + else + return FALSE + end +end + +add_hook_script(HOOK_GIVE, "drunk_takes_wine", "drunk_takes_wine") diff --git a/lib/scpt/fireprof.lua b/lib/scpt/fireprof.lua new file mode 100644 index 00000000..79a0747b --- /dev/null +++ b/lib/scpt/fireprof.lua @@ -0,0 +1,480 @@ +-- The Old Mages/Fireproofing quest: Bring back an essence from a fiery cave and get some books/scrolls/staves fireproofed in return + +fireproof_quest = {} + +-- The map definition itself +fireproof_quest.MAP = +[[#!map +# Created by fearoffours (fearoffours@moppy.co.uk) +# Made for ToME 2.1.x on 03/09/02 + +# Permanent wall +F:X:63:3 + +# Floor with dirt +F:.:88:3 + +# shallow lava +F:f:86:3 + +# Deep lava +F:F:85:3 + +### Random Monsters and/or Items +# Random object (upto 3 levels ood) +F:!:88:5:0:*21 + +# red mold +F:m:88:5:324 + +# Chimaera +F:H:88:5:341 + +# Red dragon bat +F:b:88:5:377 + +# Hellhound and +# Random object (upto 7 levels ood) on normal floor +F:C:88:5:613:*25 + +# Quest exit +F:<:6:3 + +D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +D:X......m.................H.........b...m......X +D:X.............b...............................X +D:X.......................m.....m.......H...b.C.X +D:X...............m.!........b.............FFFf.X +D:X.........m!............H....!........fffFFFffX +D:X..................................fffFFFFFFfFX +D:XFFf..............................fFFFFff..fffX +D:XFFFff........FFFFFF...........fffFFFfff......X +D:XfFFFFfff....FFFFFFFf.......fffFFFFFf.........X +D:X.fFFFFFFff.FFFFFFFFFfF..fffFFFFFFff..........X +D:X..fFFFFFFFffFFFfffFFFfffFFFFFFFFf............X +D:X...fFFFFFFFFFFff.ffFFFFFFFFFFFff.............X +D:X....fffFFFFFFff...ffFFFFFFFFFf...............X +D:X.......ffFFFf.......ffffFFfff................X +D:X.........fff.................................X +D:X.............................................X +D:X.............................................X +D:X.............................................X +D:X..................................<..........X +D:X.............................................X +D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +# Starting position +P:22:26 +]] + + +-- change this constant (and the FOO_POINTS ones) to adjust the no of items fire-proofed as a reward +fireproof_quest.TOTAL_ITEM_POINTS = 12 + +-- These constants are how many 'points' each type of item will take up. So currently, you can fireproof 3 books, 4 staves or 12 scrolls. +fireproof_quest.BOOK_POINTS = 4 +fireproof_quest.STAFF_POINTS = 3 +fireproof_quest.SCROLL_POINTS = 1 + +add_quest +{ + ["global"] = "FIREPROOF_QUEST", + ["name"] = "Old Mages quest", + ["desc"] = function() + local num_books, num_staff, num_scroll + + num_books = fireproof_quest.item_points_remaining / fireproof_quest.BOOK_POINTS + num_staff = fireproof_quest.item_points_remaining / fireproof_quest.STAFF_POINTS + num_scroll = fireproof_quest.item_points_remaining / fireproof_quest.SCROLL_POINTS + + -- Quest taken + if (quest(FIREPROOF_QUEST).status == QUEST_STATUS_TAKEN) then + print_hook("#####yAn Old Mages Quest!\n") + print_hook("Retrieve the strange essence for the old mage in Lothlorien.\n") + print_hook("\n") + -- essence retrieved, not taken to mage + elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_COMPLETED) then + print_hook("#####yAn Old Mages Quest!\n") + print_hook("You have retrieved the essence for the old mage in Lothlorien. Perhaps you \n") + print_hook("should see about a reward.\n") + print_hook("\n") + -- essence returned, not all books fireproofed + elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_FINISHED) and (fireproof_quest.item_points_remaining > 0) then + print_hook("#####yAn Old Mages Quest!\n") + print_hook("You have retrieved the essence for the old mage in Lothlorien. He will still \n") + print_hook("fireproof "..num_books.." book(s) or "..num_staff.." staff/staves or "..num_scroll.." scroll(s) for you.\n") + print_hook("\n") + end + end, + ["level"] = 20, + ["data"] = { + -- store some variables + ["fireproof_quest.item_points_remaining"] = fireproof_quest.TOTAL_ITEM_POINTS, + ["fireproof_quest.essence"] = 0, + }, + ["hooks"] = { + -- Start the game without the quest, need to request it + [HOOK_BIRTH_OBJECTS] = function() + quest(FIREPROOF_QUEST).status = QUEST_STATUS_UNTAKEN + + -- reset some variables on birth + fireproof_quest.item_points_remaining = fireproof_quest.TOTAL_ITEM_POINTS + fireproof_quest.essence = 0 + end, + [HOOK_GEN_QUEST] = function() + local essence, y, x, traps, tries, trap_y, trap_x, grid + + -- Only if player doing this quest + if (player.inside_quest ~= FIREPROOF_QUEST) then + return FALSE + else + -- load the map + load_map(fireproof_quest.MAP, 2, 2) + + -- no teleport + level_flags2 = DF2_NO_TELEPORT + + -- determine type of essence + fireproof_quest.essence = randint(18) + + -- create essence + essence = create_object(TV_BATERIE, fireproof_quest.essence) + + -- mark essence + essence.pval2 = fireproof_quest.essence + essence.note = quark_add("quest") + + -- roll for co-ordinates in top half of map + y = randint(3) + 2 + x = randint(45) + 2 + + -- drop it + drop_near(essence, -1, y, x) + + -- how many traps to generate + traps = rand_range(10, 30) + + -- generate the traps + while (traps > 0) do + + -- initialise tries variable + tries = 0 + + -- make sure it's a safe place + while (tries == 0) do + + -- get grid coordinates + trap_y = randint(19) + 2 + trap_x = randint(45) + 2 + grid = cave(trap_y, trap_x) + + -- are the coordinates on a stair, or a wall? + if (cave_is(grid, FF1_PERMANENT) ~= 0) or (cave_is(grid, FF1_FLOOR) == 0) then + + -- try again + tries = 0 + else + -- not a stair, then stop this 'while' + tries = 1 + end + end + + -- randomise level of trap + trap_level = rand_range(20, 40) + + -- put the trap there + place_trap(trap_y, trap_x, trap_level) + + -- that's one less trap to place + traps = traps - 1 + end + return TRUE + end + end, + [HOOK_STAIR] = function() + local ret + + -- only ask this if player about to go up stairs of quest and hasn;t retrieved essence + if (player.inside_quest ~= FIREPROOF_QUEST) or + (quest(FIREPROOF_QUEST).status == QUEST_STATUS_COMPLETED) then + return FALSE + else + if cave(player.py, player.px).feat ~= FEAT_LESS then return end + + -- flush all pending input + flush() + + -- confirm + ret = get_check("Really abandon the quest?") + + -- if yes, then + if (ret == TRUE) then + + -- fail the quest + quest(FIREPROOF_QUEST).status = QUEST_STATUS_FAILED + return FALSE + else + -- if no, they stay in the quest + return TRUE + end + end + end, + [HOOK_GET] = function(o_ptr) + + -- if they're in the quest and haven't picked up the essence already, continue + if (player.inside_quest ~= FIREPROOF_QUEST) or + (quest(FIREPROOF_QUEST).status == QUEST_STATUS_COMPLETED) then + return FALSE + else + + -- check that it's the real essence and not another one generated via the random object placing in fireproof.map + if (o_ptr.pval2 == fireproof_quest.essence) then + + -- ok mark the quest 'completed' + quest(FIREPROOF_QUEST).status = QUEST_STATUS_COMPLETED + msg_print(TERM_YELLOW, "Fine! Looks like you've found it.") + end + end + end, + + }, +} + +-- add the bit that determines what happens when the request 'q'uest bit is done in the wizard spire +add_building_action +{ + -- Index is used in ba_info.txt to set the actions + ["index"] = 56, + ["action"] = function() + + local num_books, num_staff, num_scroll + + num_books = fireproof_quest.item_points_remaining / fireproof_quest.BOOK_POINTS + num_staff = fireproof_quest.item_points_remaining / fireproof_quest.STAFF_POINTS + num_scroll = fireproof_quest.item_points_remaining / fireproof_quest.SCROLL_POINTS + + -- the quest hasn;t been requested already, right? + if quest(FIREPROOF_QUEST).status == QUEST_STATUS_UNTAKEN then + + -- quest has been taken now + quest(FIREPROOF_QUEST).status = QUEST_STATUS_TAKEN + fireproof_quest.item_points_remaining = fireproof_quest.TOTAL_ITEM_POINTS + + -- issue instructions + msg_print("I need a very special essence for a spell I am working on. I am too old to ") + msg_print("fetch it myself. Please bring it back to me. You can find it north of here.") + msg_print("Be careful with it, it's fragile and might be destroyed easily.") + + return TRUE, FALSE, TRUE + -- if quest completed (essence was retrieved) + elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_COMPLETED) then + + -- ask for essence + ret, item = get_item("Which essence?", + "You have no essences to return", + bor(USE_INVEN), + function (obj) + + -- check it's the 'marked' essence + if (obj.tval == TV_BATERIE) and (obj.sval == fireproof_quest.essence) and (obj.pval2 == fireproof_quest.essence) then + return TRUE + end + return FALSE + end + ) + + -- didn't get the essence? + if (ret == FALSE) then + return TRUE + + -- got the essence! + else + + -- take essence + inven_item_increase(item, -1) + inven_item_optimize(item) + msg_print("Great! Let me fireproof some of your items in thanks. I can do "..num_books.." books, ") + msg_print(num_staff.." staves, or "..num_scroll.." scrolls.") + + -- how many items to proof? + local items = fireproof_quest.item_points_remaining + + -- repeat till up to 3 (value defined as TOTAL_ITEM_POINTS constant) books fireproofed + while items > 0 do + ret = fireproof() + + -- don't loop the fireproof if there's nothing to fireproof + if ret == FALSE then + break + end + + -- subtract item points + items = fireproof_quest.item_points_remaining + end + + -- have they all been done? + if (fireproof_quest.item_points_remaining == 0) then + -- mark quest to make sure no more quests are given + quest(FIREPROOF_QUEST).status = QUEST_STATUS_REWARDED + else + -- mark in preparation of anymore books to fireproof + quest(FIREPROOF_QUEST).status = QUEST_STATUS_FINISHED + end + + + end + + -- if the player asks for a quest when they already have it, but haven't failed it, give them some extra instructions + elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_TAKEN) then + msg_print("The essence is in a cave just behind the shop.") + + -- ok not all books have been fireproofed... lets do the rest + elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_FINISHED) then + + -- how many books still to proof? + local items = fireproof_quest.item_points_remaining + + -- repeat as necessary + while items > 0 do + ret = fireproof() + + -- don't loop the fireproof if there's nothing to fireproof + if ret == FALSE then + break + else + -- have they all been done? + if (fireproof_quest.item_points_remaining == 0) then quest(FIREPROOF_QUEST).status = QUEST_STATUS_REWARDED end + end + + -- subtract item points + items = fireproof_quest.item_points_remaining + end + + -- quest failed or completed, then give no more quests + elseif (quest(FIREPROOF_QUEST).status == QUEST_STATUS_FAILED) or (quest(FIREPROOF_QUEST).status == QUEST_STATUS_REWARDED) then + msg_print("I have no more quests for you") + end + return TRUE + end, +} + +-- the routine that checks for a book and actually fireproofs it +function fireproof() + + local ret, item, obj2, stack, obj3, carry_it + + ret, item = get_item("Which item shall I fireproof?", + "You have no more items I can fireproof, come back when you have some.", + bor(USE_INVEN), + function (obj) + + -- get some flags + local f1, f2, f3, f4, f5, esp = object_flags(obj) + + -- is it a book/staff/scroll, is it already fireproof? + if ((obj.tval == TV_BOOK) or (obj.tval == TV_SCROLL) or (obj.tval == TV_STAFF)) and (band(f3, TR3_IGNORE_FIRE) == 0) then + return TRUE + end + return FALSE + end + ) + + -- get the object type from the number + obj2 = get_object(item) + + -- check we have enough points (if we 'got' an item) + if (ret == TRUE) then + ret2, stack = enough_points(obj2) + end + + -- did either routine fail? + if (ret == FALSE) or (ret2 == FALSE) then + return FALSE + else + + -- are we part of the items from a stack? + if (obj2.number ~= stack) then + + -- make a new object to handle + object_copy(obj_forge, obj2) + + -- give it the right number of items + obj_forge.number = stack + + -- adjust for number of items in pack not to be fireproofed + obj2.number = obj2.number - stack + obj3 = obj_forge + + -- we'll need to add this to the inventory after fireproofing + carry_it = TRUE + else + + -- use the whole stack + obj3 = obj2 + + -- we'll be dealing this while it's still in the inventory + carry_it = FALSE + end + + -- make it fireproof + obj3.name2 = 149 + + -- apply it, making sure the pvals don't change with apply_magic (it would change the type of book!) + local oldpval = obj3.pval + local oldpval2 = obj3.pval2 + local oldpval3 = obj3.pval3 + apply_magic(obj3, -1, FALSE, FALSE, FALSE) + obj3.pval = oldpval + obj3.pval2 = oldpval2 + obj3.pval3 = oldpval3 + + -- put it in the inventory if it's only part of a stack + if (carry_it == TRUE) then + inven_carry(obj3, TRUE) + end + + -- id and notice it + set_known(obj3) + set_aware(obj3) + + return TRUE + end +end + +-- This function makes sure the player has enough 'points' left to fireproof stuff. +function enough_points(obj) + local item_value, stack + + -- are the items in a stack? + if (obj.number > 1) then + + -- how many to fireproof? + stack = get_quantity("How many would you like fireproofed?", obj.number) + else + stack = 1 + end + + -- check for item type and multiply number in the stack by the amount of points per item of that type + if (obj.tval == TV_BOOK) then + item_value = fireproof_quest.BOOK_POINTS * stack + elseif (obj.tval == TV_STAFF) then + item_value = fireproof_quest.STAFF_POINTS * stack + elseif (obj.tval == TV_SCROLL) then + item_value = fireproof_quest.SCROLL_POINTS * stack + end + + -- do we have enough points? + if (item_value > fireproof_quest.item_points_remaining) then + msg_print("I do not have enough fireproofing material for that.") + return FALSE + else + -- if so then subtract those points before we do the fireproofing + fireproof_quest.item_points_remaining = fireproof_quest.item_points_remaining - item_value + end + + -- Used all the points? the quest is completely rewarded. + if fireproof_quest.item_points_remaining == 0 then quest(FIREPROOF_QUEST).status = QUEST_STATUS_REWARDED end + + return TRUE, stack +end + diff --git a/lib/scpt/god.lua b/lib/scpt/god.lua new file mode 100644 index 00000000..3f32888b --- /dev/null +++ b/lib/scpt/god.lua @@ -0,0 +1,640 @@ +-- The god quest: find randomly placed relic in a randomly placed dungeon! + +-- set some global variables (stored in the save file via the ["data"] key) +god_quest = {} + +-- increase this number to make god quests more common, to a max value of 100 +god_quest.CHANCE_OF_GOD_QUEST = 21 + +-- increase this number to make more quests +god_quest.MAX_NUM_GOD_QUESTS = 5 + +-- d_idx of the god_quest (Lost Temple) dungeon +god_quest.DUNGEON_GOD = 30 + +add_quest +{ + ["global"] = "GOD_QUEST", + ["name"] = "God quest", + ["desc"] = function() + + if quest(GOD_QUEST).status == QUEST_STATUS_TAKEN then + + -- get the direction that the dungeon lies from lothlorien/angband + local home, home_axis, home_distance, home2, home2_axis, home2_distance = get_god_quest_axes() + + print_hook("#####yGod quest "..god_quest.quests_given.."!\n") + print_hook("Thou art to find the lost temple of thy God and\n"); + print_hook("to retrieve the lost part of the relic for thy God! \n") + if home_axis ~= "close" then + print_hook("The temple lies "..home_distance.." to the "..home_axis.." of "..home..", \n") + else + print_hook("The temple lies very close to "..home..", \n") + end + if home2_axis ~= "close" then + print_hook( "and "..home2_distance.." to the "..home2_axis.." of "..home2..".\n") + else + print_hook("and very close to "..home2..".\n") + end + print_hook("\n") + end + end, + ["level"] = -1, + ["data"] = { + ["god_quest.relic_num"] = 1, + ["god_quest.quests_given"] = 0, + ["god_quest.relics_found"] = 0, + ["god_quest.dun_mindepth"] = 1, + ["god_quest.dun_maxdepth"] = 4, + ["god_quest.dun_minplev"] = 0, + ["god_quest.relic_gen_tries"] = 0, + ["god_quest.relic_generated"] = FALSE, + ["god_quest.dung_x"] = 1, + ["god_quest.dung_y"] = 1, + ["god_quest.player_x"] = 0, + ["god_quest.player_y"] = 0, + }, + ["hooks"] = { + -- Start the game without the quest, given it by chance + [HOOK_BIRTH_OBJECTS] = function() + quest(GOD_QUEST).status = QUEST_STATUS_UNTAKEN + + -- initialise save-file stored variables when new character is created + god_quest.relic_num = 1 + god_quest.quests_given = 0 + god_quest.relics_found = 0 + god_quest.dun_mindepth = 1 + god_quest.dun_maxdepth = 4 + god_quest.dun_minplev = 0 + god_quest.relic_gen_tries = 0 + god_quest.relic_generated = FALSE + end, + [HOOK_PLAYER_LEVEL] = function(gained) + local home_axis, home + + if gained > 0 then + -- roll for chance of quest + local give_god_quest = magik(god_quest.CHANCE_OF_GOD_QUEST) + + -- check player is worshipping a god, not already on a god quest. + if (player.astral ~= FALSE) or (player.pgod <= 0) + or (quest(GOD_QUEST).status == QUEST_STATUS_TAKEN) or (quest(GOD_QUEST).status == QUEST_STATUS_FAILED) + or (god_quest.quests_given >= god_quest.MAX_NUM_GOD_QUESTS) or (give_god_quest == FALSE) + or ((current_dungeon_idx == god_quest.DUNGEON_GOD) and (dun_level > 0)) or (player.lev <= god_quest.dun_minplev) then + -- Don't let a player get quests with trickery + if player.lev > god_quest.dun_minplev then + god_quest.dun_minplev = player.lev + end + return + else + -- each god has different characteristics, so the quests are differnet depending on your god + if player.pgod == GOD_ERU then + god_quest.relic_num = 7 + elseif player.pgod == GOD_MANWE then + god_quest.relic_num = 8 + elseif player.pgod == GOD_TULKAS then + god_quest.relic_num = 9 + elseif player.pgod == GOD_MELKOR then + god_quest.relic_num = 10 + elseif player.pgod == GOD_YAVANNA then + god_quest.relic_num =11 + end + + -- This var will need resetting + god_quest.relic_generated = FALSE + quest(GOD_QUEST).status = QUEST_STATUS_TAKEN + god_quest.quests_given = god_quest.quests_given + 1 + + -- actually place the dungeon in a random place + place_rand_dung() + + -- store the variables of the coords where the player was given the quest + god_quest.player_y, god_quest.player_x = player.get_wild_coord() + + -- establish direction of player and 'home' from dungeon + local home, home_axis, home_distance, home2, home2_axis, home2_distance = get_god_quest_axes() + + -- God issues instructions + cmsg_print(TERM_L_BLUE, "The voice of "..deity(player.pgod).name.." booms in your head:") + + cmsg_print(TERM_YELLOW, "'I have a task for thee.") + cmsg_print(TERM_YELLOW, "Centuries ago an ancient relic of mine was broken apart.") + cmsg_print(TERM_YELLOW, "The pieces of it have been lost in fallen temples.") + cmsg_print(TERM_YELLOW, "Thou art to find my lost temple and retrieve a piece of the relic.") + cmsg_print(TERM_YELLOW, "When thy task is done, thou art to lift it in the air and call upon my name.") + cmsg_print(TERM_YELLOW, "I shall then come to reclaim what is mine!") + if home_axis ~= "close" then + cmsg_print(TERM_YELLOW, "The temple lies "..home_distance.." to the "..home_axis.." of "..home..", ") + else + cmsg_print(TERM_YELLOW, "The temple lies very close to "..home..",") + end + + if home2_axis ~= "close" then + cmsg_print(TERM_YELLOW, "and "..home2_distance.." to the "..home2_axis.." of "..home2..", I can feel it.'") + else + cmsg_print(TERM_YELLOW, "and very close to "..home2..", I can feel it.'") + end + + -- Prepare depth of dungeon. If this was generated in set_god_dungeon_attributes(), + -- then we'd have trouble if someone levelled up in the dungeon! + god_quest.dun_mindepth = player.lev*2/3 + god_quest.dun_maxdepth = god_quest.dun_mindepth + 4 + end + end + end, + [HOOK_LEVEL_END_GEN] = function() + local chance + + -- Check for dungeon + if (current_dungeon_idx ~= god_quest.DUNGEON_GOD) or (quest(GOD_QUEST).status == QUEST_STATUS_UNTAKEN) then + return + -- if the relic has been created at this point, then it was created on the *PREVIOUS* call of HOOK_LEVEL_END_GEN, and + -- therefore the player has caused another level generation in the temple and hence failed the quest. + elseif (god_quest.relic_generated == TRUE) and quest(GOD_QUEST).status ~= QUEST_STATUS_FAILED then + + -- fail the quest, don't give another one, don't give this message again + quest(GOD_QUEST).status = QUEST_STATUS_FAILED + -- God issues instructions + cmsg_print(TERM_L_BLUE, "The voice of "..deity(player.pgod).name.." booms in your head:") + + cmsg_print(TERM_YELLOW, "'Thou art a fool!") + cmsg_print(TERM_YELLOW, "I told thee to look carefully for the relic. It appears thou hast missed the") + cmsg_print(TERM_YELLOW, "opportunity to claim it in my name, as I sense that those monsters who ") + cmsg_print(TERM_YELLOW, "have overrun my temple have destroyed it themselves.") + cmsg_print(TERM_YELLOW, "I shall not ask thee to do such a thing again, as thou hast failed me in this") + cmsg_print(TERM_YELLOW, "simple task!'") + else + -- Force relic generation on 5th attempt if others have been unsuccessful. + if (god_quest.relic_gen_tries == 4) and (god_quest.relic_generated == FALSE) then + generate_relic() + else + -- 1/5 chance of generation + chance = randint(5) + if (chance == 5) then + generate_relic() + else + god_quest.relic_gen_tries = god_quest.relic_gen_tries + 1 + end + end + end + end, + [HOOK_ENTER_DUNGEON] = function(d_idx) + -- call the function to set the dungeon variables (dependant on pgod) the first time we enter the dungeon + if d_idx ~= god_quest.DUNGEON_GOD then + return + else + set_god_dungeon_attributes() + end + end, + [HOOK_GEN_LEVEL_BEGIN] = function() + -- call the function to set the dungeon variables (dependant on pgod) when we WoR back into the dungeon + if current_dungeon_idx ~= god_quest.DUNGEON_GOD then + return + else + set_god_dungeon_attributes() + end + end, + [HOOK_STAIR] = function() + -- call the function to set the dungeon variables (dependant on pgod) every time we go down a level + if current_dungeon_idx ~= god_quest.DUNGEON_GOD then + return + else + set_god_dungeon_attributes() + end + end, + [HOOK_GET] = function(o_ptr, item) + -- Is it the relic, and check to make sure the relic hasn't already been identified + if (quest(GOD_QUEST).status == QUEST_STATUS_TAKEN) and (o_ptr.tval == TV_JUNK) and (o_ptr.sval == god_quest.relic_num) + and (o_ptr.pval ~= TRUE) and (god_quest.relics_found < god_quest.quests_given) then + + -- more God talky-talky + cmsg_print(TERM_L_BLUE, deity(player.pgod).name.." speaks to you:") + + -- Is it the last piece of the relic? + if (god_quest.quests_given == god_quest.MAX_NUM_GOD_QUESTS) then + cmsg_print(TERM_YELLOW, "'At last! Thou hast found all of the relic pieces.") + + -- reward player by increasing prayer skill + cmsg_print(TERM_YELLOW, "Thou hast done exceptionally well! I shall increase thy prayer skill even more!'") + skill(SKILL_PRAY).value = skill(SKILL_PRAY).value + (10 * (skill(SKILL_PRAY).mod)) + + -- Take the relic piece + floor_item_increase(item, -1) + floor_item_optimize(item) + else + cmsg_print(TERM_YELLOW, "'Well done! Thou hast found part of the relic.") + cmsg_print(TERM_YELLOW, "I shall surely ask thee to find more of it later!") + cmsg_print(TERM_YELLOW, "I will take it from thee for now'") + + -- Take the relic piece + floor_item_increase(item, -1) + floor_item_optimize(item) + + -- reward player by increasing prayer skill + cmsg_print(TERM_YELLOW, "'As a reward, I shall teach thee how to pray better'") + skill(SKILL_PRAY).value = skill(SKILL_PRAY).value + (5 * (skill(SKILL_PRAY).mod)) + end + + -- relic piece has been identified + o_ptr.pval = TRUE + god_quest.relics_found = god_quest.relics_found + 1 + + -- Make sure quests can be given again if neccesary + quest(GOD_QUEST).status = QUEST_STATUS_UNTAKEN + return TRUE + end + end, + [HOOK_CHAR_DUMP] = function() + + if (god_quest.quests_given > 0) then + + local relics = god_quest.relics_found + local append_text = "" + if (god_quest.relics_found == god_quest.MAX_NUM_GOD_QUESTS) then + relics = "all" + append_text = " and pleased your god" + else + if (god_quest.relics_found == 0) then + relics = "none" + end + if (quest(GOD_QUEST).status == QUEST_STATUS_FAILED) then + append_text = " and failed in your quest" + end + end + + print_hook("\n You found "..(relics).." of the relic pieces"..(append_text)..".") + + end + end, + }, +} + +-- this function places the lost temple at a randomly determined place. +function place_rand_dung() + local tries, grid + + -- erase old dungeon + if (god_quest.quests_given > 0) then + place_dungeon(god_quest.dung_y, god_quest.dung_x) + + -- erase old recall level + max_dlv[god_quest.DUNGEON_GOD + 1] = 0 + end + + -- initialise tries variable + tries = 1000 + + while tries > 0 do + + tries = tries - 1 + -- get grid coordinates, within a range which prevents dungeon being generated at the very edge of the wilderness (would crash the game). + god_quest.dung_x = rand_range(1, max_wild_x-2) + god_quest.dung_y = rand_range(1, max_wild_y-2) + + -- Is there a town/dungeon/potentially impassable feature there, ? + if (wild_map(god_quest.dung_y, god_quest.dung_x).entrance ~= 0) + or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).entrance ~= 0) + or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_EDGE) + or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_DEEP_WATER) + or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_TREES) + or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_SHALLOW_LAVA) + or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_DEEP_LAVA) + or (wild_feat(wild_map(god_quest.dung_y, god_quest.dung_x)).terrain_idx == TERRAIN_MOUNTAIN) then + -- try again + else + --neither player, nor wall, then stop this 'while' + break + end + end + + -- Uhuh BAD ! lets use the default location up bree + if tries == 0 then + god_quest.dung_x = 32 + god_quest.dung_y = 19 + end + + -- create god dungeon in that place + place_dungeon(god_quest.dung_y, god_quest.dung_x, god_quest.DUNGEON_GOD) + +end + +-- this function generates the relic at a randomly determined place in the temple. +function generate_relic() + local tries, grid, x, y, relic + + -- initialise tries variable + tries = 1000 + + while (tries > 0) do + + tries = tries - 1 + -- get grid coordinates from current height/width, minus one to prevent relic being generated in outside wall. (would crash the game) + y = randint(cur_hgt-1) + x = randint(cur_wid-1) + grid = cave(y, x) + + -- are the coordinates on a floor, not on a permanent feature (eg stairs), and not on a trap ? + if (cave_is(grid, FF1_FLOOR) == TRUE) and (cave_is(grid, FF1_PERMANENT) == FALSE) and (grid.t_idx == 0) then break end + + end + + -- create relic + relic = create_object(TV_JUNK, god_quest.relic_num) + + -- inscribe it to prevent automatizer 'accidents' + relic.note = quark_add("quest") + + -- If no safe co-ords were found, put it in the players backpack + if tries == 0 then + + -- explain it + msg_print(TERM_L_BLUE, "You luckily stumble across the relic on the stairs!") + + if (inven_carry_okay(relic)) then + inven_carry(relic, FALSE) + else + -- no place found, drop it on the stairs + drop_near(relic, -1, player.py, player.px) + end + + else + -- drop it + drop_near(relic, -1, y, x) + end + + -- Only generate once! + god_quest.relic_generated = TRUE + + -- Reset some variables + god_quest.relic_gen_tries = 0 + +end + + + + +function set_god_dungeon_attributes() + + -- dungeon properties altered according to which god player is worshipping, + if player.pgod == GOD_ERU then + + -- The Eru temple is based on Meneltarma. + -- W: Not too many monsters (they'll be tough though, with big levels) + dungeon(god_quest.DUNGEON_GOD).min_m_alloc_level = 14 + dungeon(god_quest.DUNGEON_GOD).min_m_alloc_chance = 200 + + -- L: Dirt and grass. More dirt at bottom, more grass at top. rocky ground would be nice + dungeon(god_quest.DUNGEON_GOD).floor1 = 88 + dungeon(god_quest.DUNGEON_GOD).floor2 = 89 + dungeon(god_quest.DUNGEON_GOD).floor_percent1[1] = 70 + dungeon(god_quest.DUNGEON_GOD).floor_percent2[1] = 30 + dungeon(god_quest.DUNGEON_GOD).floor_percent1[2] = 10 + dungeon(god_quest.DUNGEON_GOD).floor_percent2[2] = 90 + + -- A: Outer wall mountain chain. other walls granite + dungeon(god_quest.DUNGEON_GOD).fill_type1 = 97 + dungeon(god_quest.DUNGEON_GOD).fill_percent1[1] = 100 + dungeon(god_quest.DUNGEON_GOD).outer_wall = 57 + dungeon(god_quest.DUNGEON_GOD).inner_wall = 97 + dungeon(god_quest.DUNGEON_GOD).fill_method = 2 + + -- O: "At Meneltarma no weapon or tool had ever been borne" (but invaders would have left a small number) + dungeon(god_quest.DUNGEON_GOD).objs.treasure = 45 + dungeon(god_quest.DUNGEON_GOD).objs.combat = 5 + dungeon(god_quest.DUNGEON_GOD).objs.magic = 45 + dungeon(god_quest.DUNGEON_GOD).objs.tools = 5 + + -- F: A large pillar, with stairs created at edges. (You can't climb a rock through the middle, can you?) + dungeon(god_quest.DUNGEON_GOD).flags1 = bor(DF1_BIG, DF1_NO_DOORS, DF1_CIRCULAR_ROOMS, DF1_EMPTY, DF1_TOWER, DF1_FLAT, DF1_ADJUST_LEVEL_2) + dungeon(god_quest.DUNGEON_GOD).flags2 = bor(DF2_ADJUST_LEVEL_1_2, DF2_NO_SHAFT, DF2_ADJUST_LEVEL_PLAYER) + + -- R: + dungeon(god_quest.DUNGEON_GOD).rules[1].mode = 3 + dungeon(god_quest.DUNGEON_GOD).rules[1].percent = 50 + + -- M: We want evil or flying characters + dungeon(god_quest.DUNGEON_GOD).rules[1].mflags3 = RF3_EVIL + + dungeon(god_quest.DUNGEON_GOD).rules[2].mode = 3 + dungeon(god_quest.DUNGEON_GOD).rules[2].percent = 50 + + -- M: We want evil or flying characters + dungeon(god_quest.DUNGEON_GOD).rules[2].mflags7 = RF7_CAN_FLY + + + elseif player.pgod == GOD_MANWE then + + -- Manwe's lost temple is high in the clouds + -- W: Has average number of monsters. + dungeon(god_quest.DUNGEON_GOD).min_m_alloc_level = 18 + dungeon(god_quest.DUNGEON_GOD).min_m_alloc_chance = 160 + + + -- L: floor will be 'cloud-like vapour' and pools of 'condensing water' + dungeon(god_quest.DUNGEON_GOD).floor1 = 208 + dungeon(god_quest.DUNGEON_GOD).floor2 = 209 + dungeon(god_quest.DUNGEON_GOD).floor_percent1[1] = 85 + dungeon(god_quest.DUNGEON_GOD).floor_percent2[1] = 15 + + -- A: Outer wall is 'hail stone wall', inner wall 'dense fog'. FIlled at max smoothing, like islands. + dungeon(god_quest.DUNGEON_GOD).fill_type1 = 211 + dungeon(god_quest.DUNGEON_GOD).fill_percent1[1] = 100 + dungeon(god_quest.DUNGEON_GOD).outer_wall = 210 + dungeon(god_quest.DUNGEON_GOD).inner_wall = 211 + dungeon(god_quest.DUNGEON_GOD).fill_method = 4 + + -- O: Can't imagine Manwe having much treasure. Little need for tools in a cloud temple. lots of magical stuff though... + dungeon(god_quest.DUNGEON_GOD).objs.treasure = 15 + dungeon(god_quest.DUNGEON_GOD).objs.combat = 25 + dungeon(god_quest.DUNGEON_GOD).objs.magic = 55 + dungeon(god_quest.DUNGEON_GOD).objs.tools = 5 + + -- F: It's open, goes up like a tower, give it a few interesting rooms, make the monsters hard(ish). + dungeon(god_quest.DUNGEON_GOD).flags1 = bor(DF1_NO_DOORS, DF1_TOWER, DF1_CAVERN, DF1_ADJUST_LEVEL_2) + dungeon(god_quest.DUNGEON_GOD).flags2 = bor(DF2_NO_SHAFT, DF2_ADJUST_LEVEL_PLAYER) + + -- R: + dungeon(god_quest.DUNGEON_GOD).rules[1].mode = 3 + dungeon(god_quest.DUNGEON_GOD).rules[1].percent = 20 + dungeon(god_quest.DUNGEON_GOD).rules[2].mode = 3 + dungeon(god_quest.DUNGEON_GOD).rules[2].percent = 20 + dungeon(god_quest.DUNGEON_GOD).rules[3].mode = 3 + dungeon(god_quest.DUNGEON_GOD).rules[3].percent = 20 + dungeon(god_quest.DUNGEON_GOD).rules[4].mode = 3 + dungeon(god_quest.DUNGEON_GOD).rules[4].percent = 20 + dungeon(god_quest.DUNGEON_GOD).rules[5].mode = 3 + dungeon(god_quest.DUNGEON_GOD).rules[5].percent = 20 + + -- M: We want air(poison-type) or flying characters. Orcs too. They would have ransacked his elf-loving temple :) + dungeon(god_quest.DUNGEON_GOD).rules[1].mflags2 = RF2_INVISIBLE + dungeon(god_quest.DUNGEON_GOD).rules[2].mflags3 = bor(RF3_ORC, RF3_IM_POIS) + dungeon(god_quest.DUNGEON_GOD).rules[3].mflags4 = bor(RF4_BR_POIS, RF4_BR_GRAV) + dungeon(god_quest.DUNGEON_GOD).rules[4].mflags5 = RF5_BA_POIS + dungeon(god_quest.DUNGEON_GOD).rules[5].mflags7 = RF7_CAN_FLY + + + elseif player.pgod == GOD_TULKAS then + + -- Tulkas dungeon is quite normal, possibly a bit boring to be honest. Maybe I should add something radical to it. + -- 'The house of Tulkas in the midmost of Valmar was a house of mirth and revelry. It sprang into the air with many storeys, + -- and had a tower of bronze and pillars of copper in a wide arcade' + -- W: but with lots of monsters + dungeon(god_quest.DUNGEON_GOD).min_m_alloc_level = 20 + dungeon(god_quest.DUNGEON_GOD).min_m_alloc_chance = 120 + + -- L: floor is normal + dungeon(god_quest.DUNGEON_GOD).floor1 = 1 + dungeon(god_quest.DUNGEON_GOD).floor_percent1[1] = 100 + + -- A: Granite walls + dungeon(god_quest.DUNGEON_GOD).fill_type1 = 56 + dungeon(god_quest.DUNGEON_GOD).fill_percent1[1] = 100 + dungeon(god_quest.DUNGEON_GOD).outer_wall = 58 + dungeon(god_quest.DUNGEON_GOD).inner_wall = 57 + dungeon(god_quest.DUNGEON_GOD).fill_method = 0 + + -- O: Loads of combat drops + dungeon(god_quest.DUNGEON_GOD).objs.treasure = 10 + dungeon(god_quest.DUNGEON_GOD).objs.combat = 70 + dungeon(god_quest.DUNGEON_GOD).objs.magic = 5 + dungeon(god_quest.DUNGEON_GOD).objs.tools = 15 + + -- F: fairly standard + dungeon(god_quest.DUNGEON_GOD).flags1 = bor(DF1_NO_DESTROY, DF1_ADJUST_LEVEL_2) + dungeon(god_quest.DUNGEON_GOD).flags2 = DF2_ADJUST_LEVEL_PLAYER + + -- R: + dungeon(god_quest.DUNGEON_GOD).rules[1].mode = 3 + dungeon(god_quest.DUNGEON_GOD).rules[1].percent = 100 + + -- M: plenty demons please + dungeon(god_quest.DUNGEON_GOD).rules[1].mflags3 = bor(RF3_DEMON, RF3_EVIL) + + + elseif player.pgod == GOD_MELKOR then + + -- Melkors dungeon will be dark, fiery and stuff + -- Many many monsters! (but prob ADJUST_LEVEL_1_2) + dungeon(god_quest.DUNGEON_GOD).min_m_alloc_level = 24 + dungeon(god_quest.DUNGEON_GOD).min_m_alloc_chance = 80 + + + -- L: floor is dirt/mud/nether + dungeon(god_quest.DUNGEON_GOD).floor1 = 88 + dungeon(god_quest.DUNGEON_GOD).floor2 = 94 + dungeon(god_quest.DUNGEON_GOD).floor3 = 102 + dungeon(god_quest.DUNGEON_GOD).floor_percent1[1] = 45 + dungeon(god_quest.DUNGEON_GOD).floor_percent2[1] = 45 + dungeon(god_quest.DUNGEON_GOD).floor_percent3[1] = 10 + dungeon(god_quest.DUNGEON_GOD).floor_percent1[2] = 35 + dungeon(god_quest.DUNGEON_GOD).floor_percent2[2] = 35 + dungeon(god_quest.DUNGEON_GOD).floor_percent3[2] = 30 + + -- A: Granite walls to fill but glass walls for room perimeters (you can see the nasty monsters coming) + dungeon(god_quest.DUNGEON_GOD).fill_type1 = 188 + dungeon(god_quest.DUNGEON_GOD).fill_percent1[1] = 100 + dungeon(god_quest.DUNGEON_GOD).outer_wall = 188 + dungeon(god_quest.DUNGEON_GOD).inner_wall = 57 + dungeon(god_quest.DUNGEON_GOD).fill_method = 1 + + -- O: Even drops + dungeon(god_quest.DUNGEON_GOD).objs.treasure = 25 + dungeon(god_quest.DUNGEON_GOD).objs.combat = 25 + dungeon(god_quest.DUNGEON_GOD).objs.magic = 25 + dungeon(god_quest.DUNGEON_GOD).objs.tools = 25 + + -- F: Small, lava rivers, nasty monsters hehehehehe + dungeon(god_quest.DUNGEON_GOD).flags1 = bor(DF1_SMALL, DF1_LAVA_RIVERS, DF1_ADJUST_LEVEL_1) + dungeon(god_quest.DUNGEON_GOD).flags2 = bor(DF2_ADJUST_LEVEL_1_2, DF2_ADJUST_LEVEL_PLAYER) + + -- R: No restrictions on monsters here + dungeon(god_quest.DUNGEON_GOD).rules[1].mode = 0 + dungeon(god_quest.DUNGEON_GOD).rules[1].percent = 80 + + -- R: Apart from making sure we have some GOOD ones + dungeon(god_quest.DUNGEON_GOD).rules[2].mode = 3 + dungeon(god_quest.DUNGEON_GOD).rules[2].percent = 20 + + -- M: + dungeon(god_quest.DUNGEON_GOD).rules[2].mflags3 = RF3_GOOD + + elseif player.pgod == GOD_YAVANNA then + + -- Yavannas dungeon will be very natural, tress and stuff. + dungeon(god_quest.DUNGEON_GOD).min_m_alloc_level = 22 + dungeon(god_quest.DUNGEON_GOD).min_m_alloc_chance = 100 + + -- L: floor is grass/flowers, plus dirt so not always regenerating quick! + dungeon(god_quest.DUNGEON_GOD).floor1 = 89 + dungeon(god_quest.DUNGEON_GOD).floor2 = 199 + dungeon(god_quest.DUNGEON_GOD).floor3 = 88 + dungeon(god_quest.DUNGEON_GOD).floor_percent1[1] = 40 + dungeon(god_quest.DUNGEON_GOD).floor_percent2[1] = 15 + dungeon(god_quest.DUNGEON_GOD).floor_percent3[1] = 45 + + -- A: Tree walls to fill, small trees for inner walls + dungeon(god_quest.DUNGEON_GOD).fill_type1 = 96 + dungeon(god_quest.DUNGEON_GOD).fill_percent1[1] = 100 + dungeon(god_quest.DUNGEON_GOD).outer_wall = 202 + dungeon(god_quest.DUNGEON_GOD).inner_wall = 96 + dungeon(god_quest.DUNGEON_GOD).fill_method = 1 + + -- O: nt much combat.. tools where ransackers have tried to chop trees down. + dungeon(god_quest.DUNGEON_GOD).objs.treasure = 20 + dungeon(god_quest.DUNGEON_GOD).objs.combat = 10 + dungeon(god_quest.DUNGEON_GOD).objs.magic = 30 + dungeon(god_quest.DUNGEON_GOD).objs.tools = 40 + + -- F: Natural looking + dungeon(god_quest.DUNGEON_GOD).flags1 = bor(DF1_NO_DOORS, DF1_WATER_RIVERS, DF1_NO_DESTROY, DF1_ADJUST_LEVEL_1, DF1_NO_RECALL) + dungeon(god_quest.DUNGEON_GOD).flags2 = bor(DF2_ADJUST_LEVEL_1_2, DF2_NO_SHAFT, DF2_NO_GENO, DF2_ADJUST_LEVEL_PLAYER) + + -- R: Demons, Undead, non-living + dungeon(god_quest.DUNGEON_GOD).rules[1].mode = 3 + dungeon(god_quest.DUNGEON_GOD).rules[1].percent = 100 + + -- M: + dungeon(god_quest.DUNGEON_GOD).rules[1].mflags3 = bor(RF3_DEMON, RF3_UNDEAD, RF3_NONLIVING) + + end + + -- W: All dungeons are 5 levels deep, and created at 2/3 of the player clvl when the quest is given + dungeon(god_quest.DUNGEON_GOD).mindepth = god_quest.dun_mindepth + dungeon(god_quest.DUNGEON_GOD).maxdepth = god_quest.dun_maxdepth + dungeon(god_quest.DUNGEON_GOD).minplev = god_quest.dun_minplev + +end + +-- Calling this function returns the direction the dungeon is in from the players position at the time +-- the quest was given, and also the direction from angband (if the player is worshipping Melkor) or lothlorien. +function get_god_quest_axes() + local home, home_y_coord, home_x_coord, home_axis, home2, home2_y_coord, home2_x_coord, home2_axis, mydistance + + -- different values for different gods... + if player.pgod ~= GOD_MELKOR then + + -- one of the valar, "home" is lothlorien, home2 is Minas Arnor + home = "Bree" + home_y_coord = 21 + home_x_coord = 34 + home2 = "Minas Anor" + home2_y_coord = 56 + home2_x_coord = 60 + else + -- Melkor, "home" is angband, home2 is Barad-dur + home = "the Pits of Angband" + home_y_coord = 7 + home_x_coord = 34 + home2 = "the Land of Mordor" + home2_y_coord = 58 + home2_x_coord = 65 + end + + home_axis = compass(home_y_coord, home_x_coord, god_quest.dung_y, god_quest.dung_x) + home2_axis = compass(home2_y_coord, home2_x_coord, god_quest.dung_y, god_quest.dung_x) + + home_distance = approximate_distance(home_y_coord, home_x_coord, god_quest.dung_y, god_quest.dung_x) + home2_distance = approximate_distance(home2_y_coord, home2_x_coord, god_quest.dung_y, god_quest.dung_x) + + return home, home_axis, home_distance, home2, home2_axis, home2_distance +end diff --git a/lib/scpt/gods.lua b/lib/scpt/gods.lua new file mode 100644 index 00000000..014a4423 --- /dev/null +++ b/lib/scpt/gods.lua @@ -0,0 +1,26 @@ +add_hooks +{ + [HOOK_FOLLOW_GOD] = function(god, action) + if action == "ask" then + if not (god == GOD_MELKOR) then + local i = INVEN_WIELD + while i < INVEN_TOTAL do + -- 13 is ART_POWER + if player.inventory(i).name1 == 13 then + msg_print("The One Ring has corrupted you, and you are rejected.") + return TRUE + end + i = i + 1 + end + end + end + return FALSE + end, + [HOOK_RECALC_SKILLS] = function() + if not (player.pgod == GOD_NONE) and (get_skill(SKILL_ANTIMAGIC) > 0) then + msg_print("You no longer believe.") + abandon_god(GOD_ALL) + end + return FALSE + end, +} diff --git a/lib/scpt/help.lua b/lib/scpt/help.lua new file mode 100644 index 00000000..5350fec8 --- /dev/null +++ b/lib/scpt/help.lua @@ -0,0 +1,411 @@ +-- Ingame contextual help + +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +-----------------------Here comes the definition of help----------------------- +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- + +ingame_help +{ + ["hook"] = HOOK_MOVE, + ["event"] = function(y, x) if cave(y, x).feat == FEAT_BETWEEN then return TRUE end end, + ["desc"] = + { + "Void Jumpgates can be entered by pressing the > key. They will transport", + "you to another jumpgate, but beware of the cold damage that might kill you.", + } +} + +ingame_help +{ + ["hook"] = HOOK_MOVE, + ["event"] = function(y, x) if cave(y, x).feat == FEAT_FOUNTAIN then return TRUE end end, + ["desc"] = + { + "Fountains are always magical. You can quaff from them by pressing H.", + "Beware that unlike potions they cannot be identified.", + } +} + +ingame_help +{ + ["hook"] = HOOK_MOVE, + ["event"] = function(y, x) if cave(y, x).o_idx ~= 0 then return TRUE end end, + ["desc"] = + { + "So you found your first item! Nice, eh? Now when you stumble across", + "objects, you can pick them up by pressing g, and if you are wondering", + "what they do, press I (then *, then the letter for the item) to get", + "some basic information. You may also want to identify them with scrolls,", + "staves, rods or spells.", + } +} + +ingame_help +{ + ["hook"] = HOOK_MOVE, + ["event"] = function(y, x) if (cave(y, x).feat >= FEAT_ALTAR_HEAD) and (cave(y, x).feat <= FEAT_ALTAR_TAIL) then return TRUE end end, + ["desc"] = + { + "Altars are the way to reach the Valar, powers of the world,", + "usualy called Gods. You can press O to become a follower.", + "Beware that once you follow a god, you are not allowed to change.", + "For an exact description of what gods do and want, read the documentation." + } +} + +-- Beware this one, if Bree is moved from 21, 34 (y, x) on the wilderness map it will break +ingame_help +{ + ["hook"] = HOOK_END_TURN, + ["event"] = function(y, x) + if ((player.wilderness_x ~= 34) or (player.wilderness_y ~= 21) and (player.astral == FALSE)) then return TRUE end + end, + ["desc"] = + { + "Ahh wilderness travel... The overview mode will allow you to travel", + "fast, but that comes to the cost of GREATLY increased food consumption.", + "So you should bring lots of food and really watch your hunger status.", + "To enter the overview mode, press < while in the wilderness.", + } +} + +ingame_help +{ + ["hook"] = HOOK_PLAYER_LEVEL, + ["event"] = function(y, x) if player.lev > 1 then return TRUE end end, + ["desc"] = + { + "Ok, so you now gained a level, and you have skill points to spend.", + "To do so simply press G to learn skills. Reading the documentation", + "about skills and abilities is also strongly recommended.", + } +} + +ingame_help +{ + ["hook"] = HOOK_MOVE, + ["event"] = function(y, x) if cave(y, x).feat == FEAT_MORE then return TRUE end end, + ["desc"] = + { + "Ah, this is a stair, or a way into something. Press > to enter it.", + "But be ready to fight what lies within, for it might not be too friendly.", + } +} + +ingame_help +{ + ["callback"] = "monster_chat", + ["desc"] = + { + "Somebody is speaking to you it seems. You can talk back with the Y key.", + "This can lead to quests. You can also give items to 'monsters' with the y key.", + } +} + +ingame_help +{ + ["hook"] = HOOK_END_TURN, + ["event"] = function(y, x) return TRUE end, + ["desc"] = + { + "Welcome to ToME! I am the spirit of knowledge and my task is to help you", + "to get used to how to play. I have prepared a #vparchment#y for you to #vread#y.", + "Press r, then space then select it. You can also check the documentation", + "by pressing ? at (nearly) any time.", + "The first place you can explore is Barrow-downs. Go to the west of town", + "and you should see a #v>#y there.", + "If you miss any of this you can press ctrl+p to see your message log.", + "Now I must reveal your task here. You are on a quest to investigate", + "the dreadful tower of Dol Guldur in the Mirkwood forest to see what evil", + "lurks there, but beware, you are not yet ready.", + "If you do not want me to bother you any more with tips, press = then go", + "into the ToME options and deactivate the ingame_help option.", + "You can see your quest log by pressing ctrl+q. Now go to your destiny!", + } +} + +ingame_help +{ + ["no_test"] = TRUE, + ["callback"] = "select_context", + ["fct"] = function(typ, name) + -- list of files for classes, { filename, anchor } + local t = + { + ["race"] = + { + ["Beorning"] = { "r_beorn.txt", 0 }, + ["DeathMold"] = { "r_deathm.txt", 0 }, + ["Dark-Elf"] = { "r_drkelf.txt", 0 }, + ["Dunadan"] = { "r_dunad.txt", 0 }, + ["Dwarf"] = { "r_dwarf.txt", 0 }, + ["Elf"] = { "r_elf.txt", 0 }, + ["Ent"] = { "r_ent.txt", 0 }, + ["Gnome"] = { "r_gnome.txt", 0 }, + ["Half-Elf"] = { "r_hafelf.txt", 0 }, + ["Half-Ogre"] = { "r_hafogr.txt", 0 }, + ["High-Elf"] = { "r_hielf.txt", 0 }, + ["Hobbit"] = { "r_hobbit.txt", 0 }, + ["Human"] = { "r_human.txt", 0 }, + ["Kobold"] = { "r_kobold.txt", 0 }, + ["Maia"] = { "r_maia.txt", 0 }, + ["Orc"] = { "r_orc.txt", 0 }, + ["Petty-Dwarf"] = { "r_pettyd.txt", 0 }, + ["RohanKnight"] = { "r_rohank.txt", 0 }, + ["Thunderlord"] = { "r_thlord.txt", 0 }, + ["Troll"] = { "r_troll.txt", 0 }, + ["Wood-Elf"] = { "r_wodelf.txt", 0 }, + ["Yeek"] = { "r_yeek.txt", 0 }, + }, + ["subrace"] = + { + ["Barbarian"] = { "rm_barb.txt", 0 }, + ["Classical"] = { "rm_class.txt", 0 }, + ["Corrupted"] = { "rm_corru.txt", 0 }, + ["Hermit"] = { "rm_herm.txt", 0 }, + ["LostSoul"] = { "rm_lsoul.txt", 0 }, + ["Skeleton"] = { "rm_skel.txt", 0 }, + ["Spectre"] = { "rm_spec.txt", 0 }, + ["Vampire"] = { "rm_vamp.txt", 0 }, + ["Zombie"] = { "rm_zomb.txt", 0 }, + }, + ["class"] = + { + ["Alchemist"] = { "c_alchem.txt", 0 }, + ["Archer"] = { "c_archer.txt", 0 }, + ["Assassin"] = { "c_assass.txt", 0 }, + ["Axemaster"] = { "c_axemas.txt", 0 }, + ["Bard"] = { "c_bard.txt", 0 }, + ["Dark-Priest"] = { "c_pr_drk.txt", 0 }, + ["Demonologist"] = { "c_demono.txt", 0 }, + ["Druid"] = { "c_druid.txt", 0 }, + ["Geomancer"] = { "c_geoman.txt", 0 }, + ["Haftedmaster"] = { "c_hafted.txt", 0 }, + ["Loremaster"] = { "c_lorema.txt", 0 }, + ["Mage"] = { "c_mage.txt", 0 }, + ["Mimic"] = { "c_mimic.txt", 0 }, + ["Mindcrafter"] = { "c_mindcr.txt", 0 }, + ["Monk"] = { "c_monk.txt", 0 }, + ["Necromancer"] = { "c_necro.txt", 0 }, + ["Paladin"] = { "c_palad.txt", 0 }, + ["Polearmmaster"] = { "c_polear.txt", 0 }, + ["Possessor"] = { "c_posses.txt", 0 }, + ["Priest"] = { "c_priest.txt", 0 }, + ["Priest(Eru)"] = { "c_pr_eru.txt", 0 }, + ["Priest(Manwe)"] = { "c_pr_man.txt", 0 }, + ["Ranger"] = { "c_ranger.txt", 0 }, + ["Rogue"] = { "c_rogue.txt", 0 }, + ["Runecrafter"] = { "c_runecr.txt", 0 }, + ["Sorceror"] = { "c_sorcer.txt", 0 }, + ["Summoner"] = { "c_summon.txt", 0 }, + ["Swordmaster"] = { "c_swordm.txt", 0 }, + ["Symbiant"] = { "c_symbia.txt", 0 }, + ["Thaumaturgist"] = { "c_thaum.txt", 0 }, + ["Unbeliever"] = { "c_unbel.txt", 0 }, + ["Warper"] = { "c_warper.txt", 0 }, + ["Warrior"] = { "c_warrio.txt", 0 }, + }, + ["god"] = + { + ["Eru Iluvatar"] = { "g_eru.txt", 0 }, + ["Manwe Sulimo"] = { "g_manwe.txt", 0 }, + ["Tulkas"] = { "g_tulkas.txt", 0 }, + ["Melkor Bauglir"] = { "g_melkor.txt", 0 }, + ["Yavanna Kementari"] = { "g_yavann.txt", 0 }, + }, + ["skill"] = + { + ["Air"] = { "skills.txt", 27 }, + ["Alchemy"] = { "skills.txt", 49 }, + ["Antimagic"] = { "skills.txt", 50 }, + ["Archery"] = { "skills.txt", 08 }, + ["Axe-mastery"] = { "skills.txt", 05 }, + ["Backstab"] = { "skills.txt", 18 }, + ["Barehand-combat"] = { "skills.txt", 13 }, + ["Boomerang-mastery"] = { "skills.txt", 12 }, + ["Boulder-throwing"] = { "skills.txt", 58 }, + ["Bow-mastery"] = { "skills.txt", 10 }, + ["Combat"] = { "skills.txt", 01 }, + ["Conveyance"] = { "skills.txt", 30 }, + ["Corpse-preservation"] = { "skills.txt", 44 }, + ["Critical-hits"] = { "skills.txt", 04 }, + ["Crossbow-mastery"] = { "skills.txt", 11 }, + ["Demonology"] = { "skills.txt", 52 }, + ["Disarming"] = { "skills.txt", 16 }, + ["Divination"] = { "skills.txt", 31 }, + ["Dodging"] = { "skills.txt", 20 }, + ["Druidistic"] = { "skills.txt", 40 }, + ["Earth"] = { "skills.txt", 28 }, + ["Fire"] = { "skills.txt", 25 }, + ["Geomancy"] = { "skills.txt", 60 }, + ["Hafted-mastery"] = { "skills.txt", 06 }, + ["Magic"] = { "skills.txt", 21 }, + ["Magic-Device"] = { "skills.txt", 54 }, + ["Mana"] = { "skills.txt", 24 }, + ["Meta"] = { "skills.txt", 29 }, + ["Mimicry"] = { "skills.txt", 47 }, + ["Mind"] = { "skills.txt", 33 }, + ["Mindcraft"] = { "skills.txt", 41 }, + ["Monster-lore"] = { "skills.txt", 42 }, + ["Music"] = { "skills.txt", 59 }, + ["Nature"] = { "skills.txt", 34 }, + ["Necromancy"] = { "skills.txt", 35 }, + ["Polearm-mastery"] = { "skills.txt", 07 }, + ["Possession"] = { "skills.txt", 45 }, + ["Prayer"] = { "skills.txt", 39 }, + ["Runecraft"] = { "skills.txt", 36 }, + ["Sling-mastery"] = { "skills.txt", 09 }, + ["Sneakiness"] = { "skills.txt", 14 }, + ["Spell-power"] = { "skills.txt", 22 }, + ["Spirituality"] = { "skills.txt", 38 }, + ["Sorcery"] = { "skills.txt", 23 }, + ["Stealing"] = { "skills.txt", 19 }, + ["Stealth"] = { "skills.txt", 15 }, + ["Stunning-blows"] = { "skills.txt", 53 }, + ["Summoning"] = { "skills.txt", 43 }, + ["Sword-mastery"] = { "skills.txt", 03 }, + ["Symbiosis"] = { "skills.txt", 46 }, + ["Temporal"] = { "skills.txt", 32 }, + ["Thaumaturgy"] = { "skills.txt", 37 }, + ["Udun"] = { "skills.txt", 48 }, + ["Weaponmastery"] = { "skills.txt", 02 }, + ["Water"] = { "skills.txt", 26 }, + }, + ["ability"] = + { + ["Spread blows"] = { "ability.txt", 02 }, + ["Tree walking"] = { "ability.txt", 03 }, + ["Perfect casting"] = { "ability.txt", 04 }, + ["Extra Max Blow(1)"] = { "ability.txt", 05 }, + ["Extra Max Blow(2)"] = { "ability.txt", 06 }, + ["Ammo creation"] = { "ability.txt", 07 }, + ["Touch of death"] = { "ability.txt", 08 }, + ["Artifact Creation"] = { "ability.txt", 09 }, + ["Far reaching attack"] = { "ability.txt", 10 }, + ["Trapping"] = { "ability.txt", 11 }, + ["Undead Form"] = { "ability.txt", 12 }, + }, + } + + if t[typ][name] then ingame_help_doc(t[typ][name][1], t[typ][name][2]) + else ingame_help_doc("help.hlp", 0) + end + end, +} + +ingame_help +{ + ["hook"] = HOOK_IDENTIFY, + ["event"] = function(i, mode) + if mode == "full" then + local obj = get_object(i) + local f1, f2, f3, f4, f5, esp = object_flags(obj) + if band(f5, TR5_SPELL_CONTAIN) ~= 0 then return TRUE end + end + end, + ["desc"] = + { + "Ah, an item that can contain a spell. To use it you must have some levels of", + "Magic skill and then you get the option to copy a spell when pressing m.", + "Then just select which spell to copy and to which object. Note that doing so", + "is permanent; the spell cannot be removed or changed later.", + } +} + +ingame_help +{ + ["hook"] = HOOK_GET, + ["event"] = function(obj, idx) if obj.tval == TV_BATERIE then return TRUE end end, + ["desc"] = + { + "Ah, an essence! Those magical containers stores energies. They are used", + "with the Alchemy skill to create or modify the powers of items.", + } +} + +ingame_help +{ + ["hook"] = HOOK_GET, + ["event"] = function(obj, idx) if obj.tval == TV_RUNE1 or obj.tval == TV_RUNE2 then return TRUE end end, + ["desc"] = + { + "Ah, a rune! Runes are used with the Runecraft skill to allow you to", + "create spells on your own.", + } +} + +ingame_help +{ + ["hook"] = HOOK_GET, + ["event"] = function(obj, idx) if obj.tval == TV_ROD_MAIN then return TRUE end end, + ["desc"] = + { + "This is a rod. You will need to attach a rod tip to it before you", + "can use it. This main part of the rod may give the rod bonuses", + "like quicker charging time, or a larger capacity for charges.", + } +} + +ingame_help +{ + ["hook"] = HOOK_GET, + ["event"] = function(obj, idx) if obj.tval == TV_ROD then return TRUE end end, + ["desc"] = + { + "You've found a rod-tip! You will need to attach it to a rod base", + "before you can use it. Once it has been attatched (use the 'z' key)", + "you cannot unattach it! The rod tip will determine the effect of", + "the rod. To use your rod, 'z'ap it once it has been assembled.", + } +} + +ingame_help +{ + ["hook"] = HOOK_GET, + ["event"] = function(obj, idx) if obj.tval == TV_TRAPKIT then return TRUE end end, + ["desc"] = + { + "Ooooh, a trapping kit. If you have ability in the trapping skill,", + "you can lay this trap (via the 'm' key) to harm unsuspecting foes.", + "You'll generally need either some ammo or magic device depending", + "on the exact type of trap kit.", + } +} + +ingame_help +{ + ["hook"] = HOOK_RECALC_SKILLS, + ["event"] = function() if game.started and (get_melee_skills() > 1) then return TRUE end end, + ["desc"] = + { + "Ah, you now possess more than one melee type. To switch between them press m", + "and select the switch melee type option.", + } +} + +ingame_help +{ + ["hook"] = HOOK_GET, + ["event"] = function(obj, idx) if obj.tval == TV_WAND or obj.tval == TV_STAFF then return TRUE end end, + ["desc"] = + { + "You've found a magical device, either a staff or a wand. Each staff", + "contains a spell, often from one of the primary magic schools. There", + "is a lot of information you can find about this object if you identify", + "it and 'I'nspect it. Check the help file on Magic for more about these.", + } +} + +ingame_help +{ + ["hook"] = HOOK_PLAYER_LEVEL, + ["event"] = function(y, x) if player.lev >= 20 then return TRUE end end, + ["desc"] = + { + "I see you are now at least level 20. Nice! If you want to gloat about your", + "character you could press 'C' then 'f' to make a character dump and post it to", + "http://angband.oook.cz/ where it will end up in the ladder.", + } +} diff --git a/lib/scpt/init.lua b/lib/scpt/init.lua new file mode 100644 index 00000000..a6f3f8ab --- /dev/null +++ b/lib/scpt/init.lua @@ -0,0 +1,46 @@ +-- +-- This file is loaded at the initialisation of ToME +-- + +-- Load the class specific stuff +tome_dofile("player.lua") + +-- Load the ingame contextual help +tome_dofile("help.lua") + +-- let the store specific stuff happen! +tome_dofile("stores.lua") + +-- Add various 'U' powers +tome_dofile("powers.lua") + +-- Add the mimic shapes +tome_dofile("mimic.lua") + +-- Add the corruptions +tome_dofile("corrupt.lua") + +-- Add the mkey activations +tome_dofile("mkeys.lua") + +-- Add the schools of magic +tome_dofile("spells.lua") + +-- Add god stuff +tome_dofile("gods.lua") + +-- Add some quests +tome_dofile("bounty.lua") +tome_dofile("god.lua") +tome_dofile("fireprof.lua") +tome_dofile("library.lua") + +-- Add joke stuff +tome_dofile("drunk.lua") +tome_dofile("joke.lua") + +-- Some tests, if the file is not present, this is fine +tome_dofile_anywhere(ANGBAND_DIR_SCPT, "dg_test.lua", FALSE) + +-- A nice custom intro :) +tome_dofile("intro.lua") diff --git a/lib/scpt/intro.lua b/lib/scpt/intro.lua new file mode 100644 index 00000000..2c4d3def --- /dev/null +++ b/lib/scpt/intro.lua @@ -0,0 +1,105 @@ +function drop_text_left(c, str, y, o) + local i = strlen(str) + local x = 39 - (strlen(str) / 2) + o + while (i > 0) + do + local a = 0 + local time = 0 + + if (strbyte(str, i) ~= strbyte(" ", 1)) then + while (a < x + i - 1) + do + Term_putch(a - 1, y, c, 32) + Term_putch(a, y, c, strbyte(str, i)) + time = time + 1 + if time >= 4 then + Term_xtra(TERM_XTRA_DELAY, 1) + time = 0 + end + Term_redraw_section(a - 1, y, a, y) + a = a + 1 + + inkey_scan = TRUE + if (inkey() ~= 0) then + return TRUE + end + end + end + + i = i - 1 + end + return FALSE +end + +function drop_text_right(c, str, y, o) + local x = 39 - (strlen(str) / 2) + o + local i = 1 + while (i <= strlen(str)) + do + local a = 79 + local time = 0 + + if (strbyte(str, i) ~= strbyte(" ", 1)) then + while (a >= x + i - 1) + do + Term_putch(a + 1, y, c, 32) + Term_putch(a, y, c, strbyte(str, i)) + time = time + 1 + if time >= 4 then + Term_xtra(TERM_XTRA_DELAY, 1) + time = 0 + end + Term_redraw_section(a, y, a + 1, y) + a = a - 1 + + inkey_scan = TRUE + if (inkey() ~= 0) then + return TRUE + end + end + end + + i = i + 1 + end + return FALSE +end + +function tome_intro() + screen_save() + Term_clear() + + if (TRUE == drop_text_left(TERM_L_BLUE, "Art thou an adventurer,", 10, 0)) then screen_load() return end + if (TRUE == drop_text_right(TERM_L_BLUE, "One who passes through the waterfalls we call danger", 11, -1)) then screen_load() return end + if (TRUE == drop_text_left(TERM_L_BLUE, "to find the true nature of the legends beyond them?", 12, 0)) then screen_load() return end + if (TRUE == drop_text_right(TERM_L_BLUE, "If this is so, then seeketh me.", 13, -1)) then screen_load() return end + + if (TRUE == drop_text_left(TERM_WHITE, "[Press any key to continue]", 23, -1)) then screen_load() return end + + Term_putch(0, 0, TERM_DARK, 32) + inkey_scan = FALSE + inkey() + + Term_clear() + + if (TRUE == drop_text_left(TERM_L_BLUE, "DarkGod", 8, 0)) then screen_load() return end + if (TRUE == drop_text_right(TERM_WHITE, "in collaboration with", 9, -1)) then screen_load() return end + if (TRUE == drop_text_left(TERM_L_GREEN, "Eru Iluvatar,", 10, 0)) then screen_load() return end + if (TRUE == drop_text_right(TERM_L_GREEN, "Manwe", 11, -1)) then screen_load() return end + if (TRUE == drop_text_left(TERM_WHITE, "and", 12, 0)) then screen_load() return end + if (TRUE == drop_text_right(TERM_L_GREEN, "All the T.o.M.E. contributors(see credits.txt)", 13, -1)) then screen_load() return end + + if (TRUE == drop_text_left(TERM_WHITE, "present", 15, 1)) then screen_load() return end + if (TRUE == drop_text_right(TERM_YELLOW, "T.o.M.E.", 16, 0)) then screen_load() return end + + if (TRUE == drop_text_left(TERM_WHITE, "[Press any key to continue]", 23, -1)) then screen_load() return end + Term_putch(0, 0, TERM_DARK, 32) + + inkey_scan = FALSE + + inkey() + + screen_load() + return +end + +add_hook_script(HOOK_INIT, "tome_intro", "lua_intro_init") diff --git a/lib/scpt/joke.lua b/lib/scpt/joke.lua new file mode 100644 index 00000000..2d87b651 --- /dev/null +++ b/lib/scpt/joke.lua @@ -0,0 +1,31 @@ +-- Place a monster in a good spot +function gen_joke_place_monster(r_idx) + local try = 1000 + local x + local y + while try > 0 do + x = randint(cur_hgt - 4) + 2 + y = randint(cur_wid - 4) + 2 + if not (0 == place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY)) then + return + end + try = try - 1 + end +end + +-- Check if a special joke monster can be generated here +function gen_joke_monsters() + if joke_monsters == FALSE then + return + end + + -- Neil + if (current_dungeon_idx == 20) and (dun_level == 72) then + neil = test_monster_name("Neil, the Sorceror") + m_allow_special[neil + 1] = TRUE + gen_joke_place_monster(neil) + m_allow_special[neil + 1] = FALSE + end +end + +add_hook_script(HOOK_LEVEL_END_GEN, "gen_joke_monsters", "gen_joke_monsters") diff --git a/lib/scpt/library.lua b/lib/scpt/library.lua new file mode 100644 index 00000000..99c2c0f4 --- /dev/null +++ b/lib/scpt/library.lua @@ -0,0 +1,513 @@ +-- Library quest in Minas Anor + +-- Partially based on Fireproofing quest + +library_quest = {} + +-- The map definition itself +library_quest.MAP = +[[#!map + +# Permanent wall +F:X:63:3 + +# Granite Wall +F:#:57:3 + +# Cobblestone Road +F:O:200:3 + +# Floor +F:.:1:3 + +# Lich +F:l:200:3:518 + +# Master lich +F:L:200:3:658 + +# Quest exit +F:<:6:3 + +D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +D:X###############################################################X +D:X#<OlOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#O#.###.#.###.#.###.#.###.#.###.O.###.#.###.#.###.#.###.#.###O#X +D:X#OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOL#X +D:X###############################################################X +D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +# Starting position +P:4:4 +]] + +-- Map helper +library_quest.place_random = function(minY, minX, maxY, maxX, monster) + y = randint(maxY - minY + 1) + minY + x = randint(maxX - minX + 1) + minX + return place_monster_one(y, x, monster, 0, TRUE, MSTATUS_ENEMY) +end + +-- Book creation helpers +library_quest.bookable_spells = +{ + MANATHRUST, DELCURSES, + GLOBELIGHT, FIREGOLEM, FIREFLASH, FIREWALL, + GEYSER, VAPOR, ENTPOTION, + NOXIOUSCLOUD, POISONBLOOD, + STONESKIN, DIG, + RECHARGE, DISPERSEMAGIC, + BLINK, DISARM, TELEPORT, + SENSEMONSTERS, SENSEHIDDEN, REVEALWAYS, IDENTIFY, VISION, + MAGELOCK, SLOWMONSTER, ESSENCESPEED, + CHARM, CONFUSE, ARMOROFFEAR, STUN, + GROWTREE, HEALING, RECOVERY, + ERU_SEE, ERU_LISTEN, + MANWE_BLESS, MANWE_SHIELD, + YAVANNA_CHARM_ANIMAL, YAVANNA_GROW_GRASS, YAVANNA_TREE_ROOTS, + TULKAS_AIM, TULKAS_SPIN, + MELKOR_CURSE, MELKOR_CORPSE_EXPLOSION, + DRAIN +} + +library_quest.get_term_size = function() + local width = 0 + local height = 0 + ret, width, height = Term_get_size(width, height) + return width, height +end + +library_quest.book_slots_left = function() + if school_book[61][1] == -1 then + return 3 + elseif school_book[61][2] == -1 then + return 2 + elseif school_book[61][3] == -1 then + return 1 + else + return 0 + end +end + +library_quest.book_contains_spell = function(spell) + if school_book[61][1] == spell then + return TRUE + elseif school_book[61][2] == spell then + return TRUE + elseif school_book[61][3] == spell then + return TRUE + else + return FALSE + end +end + +library_quest.add_spell = function(spell) + if school_book[61][1] == -1 then + school_book[61][1] = spell + return TRUE + elseif school_book[61][2] == -1 then + school_book[61][2] = spell + return TRUE + elseif school_book[61][3] == -1 then + school_book[61][3] = spell + return TRUE + else + return FALSE + end +end + +library_quest.remove_spell = function(spell) + if school_book[61][1] == spell then + school_book[61][1] = school_book[61][2] + school_book[61][2] = school_book[61][3] + school_book[61][3] = -1 + return TRUE + elseif school_book[61][2] == spell then + school_book[61][2] = school_book[61][3] + school_book[61][3] = -1 + return TRUE + elseif school_book[61][3] == spell then + school_book[61][3] = -1 + return TRUE + else + return FALSE + end +end + +-- Print a spell (taken from s_aux) +function library_quest.print_spell(color, y, spl) + local x, index, sch, size, s + + x = 0 + size = 0 + book = 255 + obj = nil + + -- Hack if the book is 255 it is a random book + if book == 255 then + school_book[book] = {spl} + end + + -- Parse all spells + for index, s in school_book[book] do + local lvl, na = get_level_school(s, 50, -50) + local xx, sch_str + + xx = nil + sch_str = "" + for index, sch in __spell_school[s] do + if xx then + sch_str = sch_str.."/"..school(sch).name + else + xx = 1 + sch_str = sch_str..school(sch).name + end + end + + if s == spl then + if na then + c_prt(color, format("%-20s%-16s %3s %4s %3d%s %s", spell(s).name, sch_str, na, get_mana(s), spell_chance(s), "%", __spell_info[s]()), y, x) + else + c_prt(color, format("%-20s%-16s %3d %4s %3d%s %s", spell(s).name, sch_str, lvl, get_mana(s), spell_chance(s), "%", __spell_info[s]()), y, x) + end + y = y + 1 + size = size + 1 + end + end + return y +end + +-- spell selection routines inspired by skills.c +library_quest.print_spells = function(first, current) + Term_clear() + width, height = library_quest.get_term_size() + slots = library_quest.book_slots_left() + + c_prt(TERM_WHITE, "Book Creation Screen", 0, 0); + c_prt(TERM_WHITE, "Up/Down to move, Right/Left to modify, I to describe, Esc to Save/Cancel", 1, 0); + + if slots == 0 then + c_prt(TERM_L_RED, "The book can hold no more spells.", 2, 0); + elseif slots == 1 then + c_prt(TERM_L_BLUE, "The book can hold 1 more spell.", 2, 0); + else + c_prt(TERM_L_BLUE, "The book can hold "..slots.." more spells.", 2, 0); + end + + row = 3; + for index, spell in library_quest.bookable_spells do + if index >= first then + if index == current then + color = TERM_GREEN + elseif library_quest.book_contains_spell(spell) == TRUE then + color = TERM_WHITE + else + color = TERM_ORANGE + end + library_quest.print_spell(color, row, spell) + + if row == height - 1 then + return + end + row = row + 1 + end + end +end + +library_quest.fill_book = function() + -- Always start with a cleared book + school_book[61] = {-1, -1, -1} + + screen_save() + width, height = library_quest.get_term_size() + -- room for legend + margin = 3 + + first = 1 + current = 1 + done = FALSE + + while done == FALSE do + library_quest.print_spells(first, current) + + inkey_scan = FALSE + inkey_base = TRUE + char = inkey() + dir = get_keymap_dir(char) + if char == ESCAPE then + if library_quest.book_slots_left() == 0 then + flush() + done = get_check("Really create the book?") + else + done = TRUE + end + elseif char == strbyte('\r') then + -- TODO: make tree of schools + elseif char == strbyte('n') then + current = current + height + elseif char == strbyte('p') then + current = current - height + elseif char == strbyte('I') then + print_spell_desc(library_quest.bookable_spells[current], 0) + inkey() + elseif dir == 2 then + current = current + 1 + elseif dir == 8 then + current = current - 1 + elseif dir == 6 then + if library_quest.book_contains_spell(library_quest.bookable_spells[current]) == FALSE then + library_quest.add_spell(library_quest.bookable_spells[current]) + end + elseif dir == 4 then + library_quest.remove_spell(library_quest.bookable_spells[current]) + end + total = getn(library_quest.bookable_spells) + if current > total then + current = total + elseif current < 1 then + current = 1 + end + + if current > (first + height - margin - 1) then + first = current - height + margin + 1 + elseif first > current then + first = current + end + end + + screen_load() +end + +-- Quest data and hooks +add_quest +{ + ["global"] = "LIBRARY_QUEST", + ["name"] = "Library quest", + ["desc"] = function() + -- Quest taken + if (quest(LIBRARY_QUEST).status == QUEST_STATUS_TAKEN) then + print_hook("#####yAn Old Mages Quest! (Danger Level: 35)\n") + print_hook("Make the library safe for the old mage in Minas Anor.\n") + print_hook("\n") + -- Quest done, book not gotten yet + elseif (quest(LIBRARY_QUEST).status == QUEST_STATUS_COMPLETED) then + print_hook("#####yAn Old Mages Quest!\n") + print_hook("You have made the library safe for the old mage in Minas Anor.\n") + print_hook("Perhaps you should see about a reward.\n") + print_hook("\n") + end + end, + ["level"] = 35, + ["data"] = + { + ["school_book[61][1]"] = -1, + ["school_book[61][2]"] = -1, + ["school_book[61][3]"] = -1 + }, + ["hooks"] = + { + -- Start the game without the quest, need to request it + [HOOK_BIRTH_OBJECTS] = function() + quest(LIBRARY_QUEST).status = QUEST_STATUS_UNTAKEN + school_book[61] = {-1, -1, -1} + end, + + [HOOK_GEN_QUEST] = function() + -- Only if player doing this quest + if (player.inside_quest ~= LIBRARY_QUEST) then + return FALSE + end + + load_map(library_quest.MAP, 2, 2) + level_flags2 = DF2_NO_GENO + + -- generate the Liches 518 + liches = damroll(4, 2) -- plus one on the map + while(liches > 0) do + if 0 < library_quest.place_random(4, 4, 14, 37, 518) then + liches = liches - 1 + end + end + + -- generate the Monastic liches 611 + liches = damroll(1, 2) + while(liches > 0) do + if 0 < library_quest.place_random(14, 34, 37, 67, 611) then + liches = liches - 1 + end + end + + -- generate more Monastic liches 611 + liches = damroll(1, 2) - 1 + while(liches > 0) do + if 0 < library_quest.place_random(4, 34, 14, 67, 611) then + liches = liches - 1 + end + end + + -- generate even more Monastic liches 611 + liches = damroll(1, 2) - 1 + while(liches > 0) do + if 0 < library_quest.place_random(14, 4, 37, 34, 611) then + liches = liches - 1 + end + end + + -- Flesh golem 256 + golems = 2 + while(golems > 0) do + if 0 < library_quest.place_random(10, 10, 37, 67, 256) then + golems = golems - 1 + end + end + + -- Clay golem 261 + golems = 2 + while(golems > 0) do + if 0 < library_quest.place_random(10, 10, 37, 67, 261) then + golems = golems - 1 + end + end + + -- Iron golem 367 + golems = 2 + while(golems > 0) do + if 0 < library_quest.place_random(10, 10, 37, 67, 367) then + golems = golems - 1 + end + end + + -- Mithril Golem 464 + golems = 1 + while(golems > 0) do + if 0 < library_quest.place_random(10, 10, 37, 67, 464) then + golems = golems - 1 + end + end + + -- one Master lich is on the map + + return TRUE + end, + [HOOK_STAIR] = function() + local ret + + -- only ask this if player about to go up stairs of quest and hasn't won yet + if (player.inside_quest ~= LIBRARY_QUEST) or (quest(LIBRARY_QUEST).status == QUEST_STATUS_COMPLETED) then + return FALSE + end + + if cave(player.py, player.px).feat ~= FEAT_LESS then return end + + -- flush all pending input + flush() + + -- confirm + ret = get_check("Really abandon the quest?") + + -- if yes, then + if ret == TRUE then + -- fail the quest + quest(LIBRARY_QUEST).status = QUEST_STATUS_FAILED + return FALSE + else + -- if no, they stay in the quest + return TRUE + end + end, + [HOOK_MONSTER_DEATH] = function() + -- if they're in the quest and haven't won, continue + if (player.inside_quest ~= LIBRARY_QUEST) or (quest(LIBRARY_QUEST).status == QUEST_STATUS_COMPLETED) then + return FALSE + end + + i = 1 + count = -1 + while i <= m_max do + local monster = m_list[i] + if (monster.r_idx > 0) and (monster.status <= MSTATUS_ENEMY) then + count = count + 1 + end + i = i + 1 + end + + if count == 0 then + quest(LIBRARY_QUEST).status = QUEST_STATUS_COMPLETED + msg_print(TERM_YELLOW, "The library is safe now.") + end + end, + }, +} + +-- Library store action +add_building_action +{ + ["index"] = 61, + ["action"] = function() + -- the quest hasn't been requested already, right? + if quest(LIBRARY_QUEST).status == QUEST_STATUS_UNTAKEN then + -- quest has been taken now + quest(LIBRARY_QUEST).status = QUEST_STATUS_TAKEN + + -- issue instructions + msg_print("I need get some stock from my main library, but it is infested with monsters!") + msg_print("Please use the side entrance and vanquish the intruders for me.") + + return TRUE, FALSE, TRUE + -- if quest completed + elseif (quest(LIBRARY_QUEST).status == QUEST_STATUS_COMPLETED) then + msg_print("Thank you! Let me make a special book for you.") + msg_print("Tell me three spells and I will write them in the book.") + library_quest.fill_book() + if library_quest.book_slots_left() == 0 then + quest(LIBRARY_QUEST).status = QUEST_STATUS_REWARDED + book = create_object(TV_BOOK, 61) + book.art_name = quark_add(player_name()) + book.found = OBJ_FOUND_REWARD + set_aware(book) + set_known(book) + inven_carry(book, FALSE) + end + + -- if the player asks for a quest when they already have it, but haven't failed it, give them some extra instructions + elseif (quest(LIBRARY_QUEST).status == QUEST_STATUS_TAKEN) then + msg_print("Please use the side entrance and vanquish the intruders for me.") + + -- quest failed or completed, then give no more quests + elseif (quest(LIBRARY_QUEST).status == QUEST_STATUS_FAILED) or (quest(LIBRARY_QUEST).status == QUEST_STATUS_REWARDED) then + msg_print("I have no more quests for you.") + end + return TRUE + end, +} diff --git a/lib/scpt/mimic.lua b/lib/scpt/mimic.lua new file mode 100644 index 00000000..be969ad5 --- /dev/null +++ b/lib/scpt/mimic.lua @@ -0,0 +1,385 @@ +-- Define the various possible mimic shapes + +-- Nature shapes +add_mimic_shape +{ + ["name"] = "Mouse", + ["obj_name"] = "Mouse Fur", + ["desc"] = "Mice are small, fast and very stealthy", + ["realm"] = "nature", + ["level"] = 1, + ["rarity"] = 10, + ["duration"] = {20, 40}, + ["calc"] = function () + -- Mice run! + player.pspeed = player.pspeed + 5 + (player.mimic_level / 7) + + -- They can crtawl under your armor to hit you ;) + player.to_h = player.to_h + 10 + (player.mimic_level / 5) + player.dis_to_h = player.dis_to_h + 10 + (player.mimic_level / 5) + + -- But they are not very powerfull + player.to_d = player.to_d / 5 + player.dis_to_d = player.dis_to_d / 5 + + -- But they are stealthy + player.skill_stl = player.skill_stl + 10 + (player.mimic_level / 5) + + -- Stat mods + player.modify_stat(A_STR, -5) + player.modify_stat(A_DEX, 3) + player.modify_stat(A_CON, 1) + + end, + ["power"] = function() + if player.mimic_level >= 30 then + player.add_power(POWER_INVISIBILITY) + end + end, +} + +add_mimic_shape +{ + ["name"] = "Eagle", + ["obj_name"] = "Feathers Cloak", + ["desc"] = "Eagles are master of the air, good hunters with excellent vision.", + ["realm"] = "nature", + ["level"] = 10, + ["rarity"] = 30, + ["duration"] = {10, 50}, + ["calc"] = function () + player.ffall = TRUE + player.pspeed = player.pspeed + 2 + (player.mimic_level / 6) + + player.modify_stat(A_STR, -3) + player.modify_stat(A_DEX, 2 + (player.mimic_level / 15)) + player.modify_stat(A_CON, 4 + (player.mimic_level / 20)) + player.modify_stat(A_INT, -1) + player.modify_stat(A_WIS, 1) + player.modify_stat(A_CHR, -1) + + if player.mimic_level >= 20 then player.fly = TRUE end + if player.mimic_level >= 20 then player.see_inv = TRUE end + if player.mimic_level >= 25 then player.free_act = TRUE end + if player.mimic_level >= 30 then player.resist_elec = TRUE end + if player.mimic_level >= 40 then player.sh_elec = TRUE end + + end, +} + +add_mimic_shape +{ + ["name"] = "Wolf", + ["obj_name"] = "Wolf Pelt", + ["desc"] = "Wolves are masters of movement, strong and have excellent eyesight.", + ["realm"] = "nature", + ["level"] = 20, + ["rarity"] = 40, + ["duration"] = {10, 50}, + ["calc"] = function () + player.modify_stat(A_STR, 2 + (player.mimic_level / 20)) + player.modify_stat(A_DEX, 3 + (player.mimic_level / 20)) + player.modify_stat(A_INT, -3) + player.modify_stat(A_CHR, -2) + + player.pspeed = player.pspeed + 10 + (player.mimic_level / 5) + + player.free_act = TRUE + player.resist_fear = TRUE + + if player.mimic_level >= 10 then player.resist_cold = TRUE end + if player.mimic_level >= 15 then player.see_inv = TRUE end + if player.mimic_level >= 30 then player.resist_dark = TRUE end + if player.mimic_level >= 35 then player.resist_conf = TRUE end + + end, +} + +add_mimic_shape +{ + ["name"] = "Spider", + ["obj_name"] = "Spider Web", + ["desc"] = "Spiders are clever and become good climbers.", + ["realm"] = "nature", + ["level"] = 25, + ["rarity"] = 50, + ["duration"] = {10, 50}, + ["calc"] = function () + player.modify_stat(A_STR, -4) + player.modify_stat(A_DEX, 1 + (player.mimic_level / 8)) + player.modify_stat(A_INT, 1 + (player.mimic_level / 5)) + player.modify_stat(A_WIS, 1 + (player.mimic_level / 5)) + player.modify_stat(A_CON, -5) + player.modify_stat(A_CHR, -10) + + player.pspeed = player.pspeed + 5 + + player.resist_pois = TRUE + player.resist_fear = TRUE + player.resist_dark = TRUE + + if player.mimic_level >= 40 then player.climb = TRUE end + + end, + ["power"] = function() + if player.mimic_level >= 25 then + player.add_power(POWER_WEB) + end + end, +} + +add_mimic_shape +{ + ["name"] = "Elder Ent", + ["obj_name"] = "Entish Bark", + ["desc"] = "Ents are powerful tree-like beings dating from the dawn of time.", + ["realm"] = "nature", + ["level"] = 40, + ["rarity"] = 60, + ["duration"] = {10, 30}, + ["limit"] = TRUE, + ["calc"] = function () + player.pspeed = player.pspeed - 5 - (player.mimic_level / 10) + + player.to_a = player.to_a + 10 + player.mimic_level + player.dis_to_a = player.dis_to_a + 10 + player.mimic_level + + player.resist_pois = TRUE + player.resist_cold = TRUE + player.free_act = TRUE + player.regenerate = TRUE + player.see_inv = TRUE + player.sensible_fire = TRUE + + player.modify_stat(A_STR, player.mimic_level / 5) + player.modify_stat(A_INT, - (player.mimic_level / 7)) + player.modify_stat(A_WIS, - (player.mimic_level / 7)) + player.modify_stat(A_DEX, -4) + player.modify_stat(A_CON, player.mimic_level / 5) + player.modify_stat(A_CHR, -7) + + end, + ["power"] = function () + player.add_power(PWR_GROW_TREE) + end, +} + +add_mimic_shape +{ + ["name"] = "Vapour", + ["obj_name"] = "Cloak of Mist", + ["desc"] = "A sentient cloud, darting around", + ["realm"] = "nature", + ["level"] = 15, + ["rarity"] = 10, + ["duration"] = {10, 40}, + ["calc"] = function () + + player.pspeed = player.pspeed + 5 + + --Try to hit a cloud! + player.to_a = player.to_a + 40 + player.mimic_level + player.dis_to_a = player.dis_to_a + 40 + player.mimic_level + + --Try to hit WITH a cloud! + player.to_h = player.to_h - 40 + player.dis_to_h = player.dis_to_h -40 + + -- But they are stealthy + player.skill_stl = player.skill_stl + 10 + (player.mimic_level / 5) + player.resist_pois = TRUE + player.immune_cold = TRUE + player.resist_shard = TRUE + player.free_act = TRUE + player.regenerate = TRUE + player.see_inv = TRUE + player.sensible_fire = TRUE + player.levitate = TRUE + + -- Stat mods + player.modify_stat(A_STR, -4) + player.modify_stat(A_DEX, 5) + player.modify_stat(A_CON, -4) + player.modify_stat(A_CHR, -10) + end, +} + +add_mimic_shape +{ + ["name"] = "Serpent", + ["obj_name"] = "Snakeskin Cloak", + ["desc"] = "Serpents are fast, lethal predators.", + ["realm"] = "nature", + ["level"] = 30, + ["rarity"] = 25, + ["duration"] = {15, 20}, + ["calc"] = function () + player.pspeed = player.pspeed + 10 + (player.mimic_level / 6) + player.to_a = player.to_a + 3 + (player.mimic_level / 8) + player.dis_to_a = player.dis_to_a + 3 + (player.mimic_level / 8) + + player.modify_stat(A_STR, player.mimic_level / 8) + player.modify_stat(A_INT, -6) + player.modify_stat(A_WIS, -6) + player.modify_stat(A_DEX, -4) + player.modify_stat(A_CON, player.mimic_level / 7) + player.modify_stat(A_CHR, -6) + + player.resist_pois = TRUE + if player.mimic_level >= 25 then player.free_act = TRUE end + end, +} + +add_mimic_shape +{ + ["name"] = "Mumak", + ["obj_name"] = "Mumak Hide", + ["desc"] = "A giant, elaphantine form.", + ["realm"] = "nature", + ["level"] = 40, + ["rarity"] = 40, + ["duration"] = {15, 20}, + ["calc"] = function () + player.pspeed = player.pspeed - 5 - (player.mimic_level / 10) + player.to_a = player.to_a + 10 + (player.mimic_level / 6) + player.dis_to_a = player.dis_to_a + 10 + (player.mimic_level / 6) + player.to_d = player.to_d + 5 + ((player.mimic_level * 2) / 3) + player.dis_to_d = player.dis_to_d + 5 + ((player.mimic_level * 2) / 3) + + player.modify_stat(A_STR, player.mimic_level / 4) + player.modify_stat(A_INT, -8) + player.modify_stat(A_WIS, -4) + player.modify_stat(A_DEX, -5) + player.modify_stat(A_CON, player.mimic_level / 3) + player.modify_stat(A_CHR, -10) + + if player.mimic_level >= 10 then player.resist_fear = TRUE end + if player.mimic_level >= 25 then player.resist_conf = TRUE end + if player.mimic_level >= 30 then player.free_act = TRUE end + if player.mimic_level >= 35 then player.resist_nexus = TRUE end + end, +} + +--------- Extra shapes ----------- + +-- For Beornings +add_mimic_shape +{ + ["name"] = "Bear", + ["desc"] = "A fierce, terrible bear.", + ["realm"] = nil, + ["level"] = 1, + ["rarity"] = 101, + ["duration"] = {50, 200}, + ["limit"] = TRUE, + ["calc"] = function () + player.pspeed = player.pspeed - 5 + (player.mimic_level / 5) + + player.to_a = player.to_a + 5 + ((player.mimic_level * 2) / 3) + player.dis_to_a = player.dis_to_a + 5 + ((player.mimic_level * 2) / 3) + + player.modify_stat(A_STR, player.mimic_level / 11) + player.modify_stat(A_INT, player.mimic_level / 11) + player.modify_stat(A_WIS, player.mimic_level / 11) + player.modify_stat(A_DEX, -1) + player.modify_stat(A_CON, player.mimic_level / 11) + player.modify_stat(A_CHR, -10) + + if player.mimic_level >= 10 then player.free_act = TRUE end + if player.mimic_level >= 20 then player.regenerate = TRUE end + if player.mimic_level >= 30 then player.resist_conf = TRUE end + if player.mimic_level >= 35 then player.resist_nexus = TRUE end + + -- activate the skill + skill(SKILL_BEAR).hidden = FALSE + end, +} + +-- For balrog corruptions +add_mimic_shape +{ + ["name"] = "Balrog", + ["desc"] = "A corrupted maia.", + ["realm"] = nil, + ["level"] = 1, + ["rarity"] = 101, + ["duration"] = {30, 70}, + ["limit"] = TRUE, + ["calc"] = function () + player.modify_stat(A_STR, 5 + player.mimic_level / 5) + player.modify_stat(A_INT, player.mimic_level / 10) + player.modify_stat(A_WIS, - ( 5 + player.mimic_level / 10)) + player.modify_stat(A_DEX, player.mimic_level / 10) + player.modify_stat(A_CON, 5 + player.mimic_level / 5) + player.modify_stat(A_CHR, - ( 5 + player.mimic_level / 10)) + + player.immune_fire = TRUE + player.immune_elec = TRUE + player.immune_acid = TRUE + player.resist_pois = TRUE + player.resist_dark = TRUE + player.resist_chaos = TRUE + player.hold_life = TRUE + player.ffall = TRUE + player.regenerate = TRUE + player.sh_fire = TRUE + return 1 + end, +} + +-- For avatar spell +add_mimic_shape +{ + ["name"] = "Maia", + ["desc"] = "A near god-like being.", + ["realm"] = nil, + ["level"] = 1, + ["rarity"] = 101, + ["duration"] = {30, 70}, + ["limit"] = TRUE, + ["calc"] = function () + player.modify_stat(A_STR, 5 + player.mimic_level / 5) + player.modify_stat(A_INT, 5 + player.mimic_level / 5) + player.modify_stat(A_WIS, 5 + player.mimic_level / 5) + player.modify_stat(A_DEX, 5 + player.mimic_level / 5) + player.modify_stat(A_CON, 5 + player.mimic_level / 5) + player.modify_stat(A_CHR, 5 + player.mimic_level / 5) + + player.immune_fire = TRUE + player.immune_elec = TRUE + player.immune_acid = TRUE + player.immune_cold = TRUE + player.resist_pois = TRUE + player.resist_lite = TRUE + player.resist_dark = TRUE + player.resist_chaos = TRUE + player.hold_life = TRUE + player.ffall = TRUE + player.regenerate = TRUE + return 2 + end, +} + +-- For Geomancy +add_mimic_shape +{ + ["name"] = "Fire Elem.", + ["desc"] = "A towering column of flames", + ["realm"] = nil, + ["level"] = 1, + ["rarity"] = 101, + ["duration"] = {10, 10}, + ["limit"] = TRUE, + ["calc"] = function () + player.modify_stat(A_STR, 5 + (player.mimic_level / 5)) + player.modify_stat(A_DEX, 5 + (player.mimic_level / 5)) + player.modify_stat(A_WIS, -5 - (player.mimic_level / 5)) + + player.immune_fire = TRUE + -- was immune to poison in the 3.0.0 version + player.resist_pois = TRUE + player.sh_fire = TRUE + player.lite = TRUE + return 0 + end, +} diff --git a/lib/scpt/mkeys.lua b/lib/scpt/mkeys.lua new file mode 100644 index 00000000..15e23546 --- /dev/null +++ b/lib/scpt/mkeys.lua @@ -0,0 +1,95 @@ +-- Mkeys for skills & abilities + +GF_INSTA_DEATH = add_spell_type +{ + ["color"] = { TERM_DARK, 0 }, + ["angry"] = function() return TRUE, TRUE end, + ["monster"] = function(who, dam, rad, y, x, monst) + local race = race_info_idx(monst.r_idx, monst.ego) + if magik(5) == FALSE or band(race.flags1, RF1_UNIQUE) ~= FALSE or band(race.flags3, RF3_UNDEAD) ~= FALSE or band(race.flags3, RF3_NONLIVING) ~= FALSE then + return TRUE, FALSE + else + -- Reduce the exp gained this way + monst.level = monst.level / 3 + return TRUE, FALSE, 32535, 0, 0, 0, 0, 0, 0, 0, " faints.", " is sucked out of life." + end + end, +} + +-- Death touch ability +add_mkey +{ + ["mkey"] = 100, + ["fct"] = function() + if player.csp > 40 then + increase_mana(-40) + set_project(randint(30) + 10, GF_INSTA_DEATH, 1, 0, bor(PROJECT_STOP, PROJECT_KILL)) + energy_use = 100 + else + msg_print("You need at least 40 mana.") + end + end, +} + + +-- Geomancy skill +add_mkey +{ + ["mkey"] = 101, + ["fct"] = function() + local s + + -- No magic + if (player.antimagic > 0) then + msg_print("Your anti-magic field disrupts any magic attempts.") + return + end + + local obj = get_object(INVEN_WIELD) + if (obj.k_idx <= 0) or (obj.tval ~= TV_MSTAFF) then + msg_print('You must wield a magestaff to use Geomancy.') + return + end + + s = get_school_spell("cast", "is_ok_spell", 62); + + -- Actualy cast the choice + if (s ~= -1) then + cast_school_spell(s, spell(s)) + end + end, +} + +-- Far reaching attack of polearms +add_mkey +{ + ["mkey"] = 102, + ["fct"] = function() + local weapon = get_object(INVEN_WIELD); + if weapon.tval == TV_POLEARM and (weapon.sval == SV_HALBERD or weapon.sval == SV_PIKE or weapon.sval == SV_HEAVY_LANCE or weapon.sval == SV_LANCE) then + else + msg_print("You will need a long polearm for this!") + return + end + + ret, dir = get_rep_dir() + if ret == FALSE then return end + + local dy, dx = explode_dir(dir) + dy = dy * 2 + dx = dx * 2 + targety = player.py + dy + targetx = player.px + dx + + local max_blows = get_skill_scale(SKILL_POLEARM, player.num_blow / 2) + if max_blows == 0 then max_blows = 1 end + + if get_skill(SKILL_POLEARM) >= 40 then + energy_use = energy_use + 200 + return project(0, 0, targety, targetx, max_blows, GF_ATTACK, bor(PROJECT_BEAM, PROJECT_KILL)) + else + energy_use = energy_use + 200 + return project(0, 0, targety, targetx, max_blows, GF_ATTACK, bor(PROJECT_BEAM, PROJECT_STOP, PROJECT_KILL)) + end + end, +} diff --git a/lib/scpt/player.lua b/lib/scpt/player.lua new file mode 100644 index 00000000..2a617608 --- /dev/null +++ b/lib/scpt/player.lua @@ -0,0 +1,76 @@ +------------------------------------------------------------------------------ +----------------------- Hook to create birth objects ------------------------- +------------------------------------------------------------------------------ +function __birth_hook_objects() + -- Provide a book of blink to rangers + if get_class_name() == "Ranger" then + local obj = create_object(TV_BOOK, 255); + obj.pval = find_spell("Phase Door") + obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN) + inven_carry(obj, FALSE) + end_object(obj) + end + + -- Provide a book of Geyser to Geomancers + if get_class_name() == "Geomancer" then + local obj = create_object(TV_BOOK, 255); + obj.pval = find_spell("Geyser") + obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN) + inven_carry(obj, FALSE) + end_object(obj) + end + + -- Provide a book of prayer to priests + if get_class_name() == "Priest(Eru)" then + local obj = create_object(TV_BOOK, 255); + obj.pval = find_spell("See the Music") + obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN) + inven_carry(obj, FALSE) + end_object(obj) + end + if get_class_name() == "Priest(Manwe)" then + local obj = create_object(TV_BOOK, 255); + obj.pval = find_spell("Manwe's Blessing") + obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN) + inven_carry(obj, FALSE) + end_object(obj) + end + if get_class_name() == "Druid" then + local obj = create_object(TV_BOOK, 255); + obj.pval = find_spell("Charm Animal") + obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN) + inven_carry(obj, FALSE) + end_object(obj) + end + if get_class_name() == "Dark-Priest" then + local obj = create_object(TV_BOOK, 255); + obj.pval = find_spell("Curse") + obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN) + inven_carry(obj, FALSE) + end_object(obj) + end + if get_class_name() == "Paladin" then + local obj = create_object(TV_BOOK, 255); + obj.pval = find_spell("Divine Aim") + obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN) + inven_carry(obj, FALSE) + end_object(obj) + end + if get_class_name() == "Mimic" then + local obj = create_object(TV_CLOAK, 100); + obj.pval2 = resolve_mimic_name("Mouse") + obj.ident = bor(obj.ident, IDENT_MENTAL, IDENT_KNOWN) + inven_carry(obj, FALSE) + end_object(obj) + end + + -- Start the undeads, as undeads with the corruptions + if get_subrace_name() == "Vampire" then + player.corruption(CORRUPT_VAMPIRE_TEETH, TRUE) + player.corruption(CORRUPT_VAMPIRE_STRENGTH, TRUE) + player.corruption(CORRUPT_VAMPIRE_VAMPIRE, TRUE) + end +end + +-- Register in the hook list +add_hook_script(HOOK_BIRTH_OBJECTS, "__birth_hook_objects", "__birth_hook_objects") diff --git a/lib/scpt/powers.lua b/lib/scpt/powers.lua new file mode 100644 index 00000000..ff4c62c5 --- /dev/null +++ b/lib/scpt/powers.lua @@ -0,0 +1,61 @@ +-- Various 'U' powers + +-- Invisibility power, for the mouse mimic shape +POWER_INVISIBILITY = add_power +{ + ["name"] = "invisibility", + ["desc"] = "You are able melt into the shadows to become invisible.", + ["desc_get"] = "You suddenly become able to melt into the shadows.", + ["desc_lose"] = "You lose your shadow-melting ability.", + ["level"] = 30, + ["cost"] = 10, + ["stat"] = A_DEX, + ["fail"] = 20, + ["power"] = function() + set_invis(20 + randint(30), 30) + end, +} + +-- Web power, for the spider mimic shape +POWER_WEB = add_power +{ + ["name"] = "web", + ["desc"] = "You are able throw a thick and very resistant spider web.", + ["desc_get"] = "You suddenly become able to weave webs.", + ["desc_lose"] = "You lose your web-weaving capability.", + ["level"] = 25, + ["cost"] = 30, + ["stat"] = A_DEX, + ["fail"] = 20, + ["power"] = function() + -- Warning, beware of f_info changes .. I hate to do that .. + grow_things(16, 1 + (player.lev / 10)) + end, +} + +-- Activating/stopping space-continuum +-- When stopped it will induce constant mana loss +player.corrupt_anti_teleport_stopped = FALSE +add_loadsave("player.corrupt_anti_teleport_stopped", FALSE) +POWER_COR_SPACE_TIME = add_power +{ + ["name"] = "control space/time continuum", + ["desc"] = "You are able to control the space/time continuum.", + ["desc_get"] = "You become able to control the space/time continuum.", + ["desc_lose"] = "You are no more able to control the space/time continuum.", + ["level"] = 1, + ["cost"] = 10, + ["stat"] = A_WIS, + ["fail"] = 10, + ["power"] = function() + if player.corrupt_anti_teleport_stopped == TRUE then + player.corrupt_anti_teleport_stopped = FALSE + msg_print("You stop controlling your corruption.") + player.update = bor(player.update, PU_BONUS) + else + player.corrupt_anti_teleport_stopped = TRUE + msg_print("You start controlling your corruption, teleportation works once more.") + player.update = bor(player.update, PU_BONUS) + end + end, +} diff --git a/lib/scpt/s_air.lua b/lib/scpt/s_air.lua new file mode 100644 index 00000000..afd1f584 --- /dev/null +++ b/lib/scpt/s_air.lua @@ -0,0 +1,193 @@ +-- handle the air school + +NOXIOUSCLOUD = add_spell +{ + ["name"] = "Noxious Cloud", + ["school"] = {SCHOOL_AIR}, + ["level"] = 3, + ["mana"] = 3, + ["mana_max"] = 30, + ["fail"] = 20, + ["stick"] = + { + ["charge"] = { 5, 7 }, + [TV_WAND] = + { + ["rarity"] = 15, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 25, 50 }, + }, + }, + ["spell"] = function() + local ret, dir, type + + ret, dir = get_aim_dir() + if ret == FALSE then return end + if get_level(NOXIOUSCLOUD, 50) >= 30 then type = GF_UNBREATH + else type = GF_POIS end + fire_cloud(type, dir, 7 + get_level(NOXIOUSCLOUD, 150), 3, 5 + get_level(NOXIOUSCLOUD, 40)) + return TRUE + end, + ["info"] = function() + return "dam "..(7 + get_level(NOXIOUSCLOUD, 150)).." rad 3 dur "..(5 + get_level(NOXIOUSCLOUD, 40)) + end, + ["desc"] = { + "Creates a cloud of poison", + "The cloud will persist for some turns, damaging all monsters passing by", + "At spell level 30 it turns into a thick gas attacking all living beings" + } +} + +AIRWINGS = add_spell +{ + ["name"] = "Wings of Winds", + ["school"] = {SCHOOL_AIR, SCHOOL_CONVEYANCE}, + ["level"] = 22, + ["mana"] = 30, + ["mana_max"] = 40, + ["fail"] = 60, + ["stick"] = + { + ["charge"] = { 7, 5 }, + [TV_STAFF] = + { + ["rarity"] = 27, + ["base_level"] = { 1, 10 }, + ["max_level"] = { 20, 50 }, + }, + }, + ["inertia"] = { 1, 10 }, + ["spell"] = function() + if get_level(AIRWINGS, 50) >= 16 then + if player.tim_fly == 0 then return set_tim_fly(randint(10) + 5 + get_level(AIRWINGS, 25)) end + else + if player.tim_ffall == 0 then return set_tim_ffall(randint(10) + 5 + get_level(AIRWINGS, 25)) end + end + return FALSE + end, + ["info"] = function() + return "dur "..(5 + get_level(AIRWINGS, 25)).."+d10" + end, + ["desc"] = { + "Grants the power of levitation", + "At level 16 it grants the power of controlled flight" + } +} + +INVISIBILITY = add_spell +{ + ["name"] = "Invisibility", + ["school"] = {SCHOOL_AIR}, + ["level"] = 16, + ["mana"] = 10, + ["mana_max"] = 20, + ["fail"] = 50, + ["inertia"] = { 1, 30 }, + ["spell"] = function() + if player.tim_invisible == 0 then return set_invis(randint(20) + 15 + get_level(INVISIBILITY, 50), 20 + get_level(INVISIBILITY, 50)) end + end, + ["info"] = function() + return "dur "..(15 + get_level(INVISIBILITY, 50)).."+d20 power "..(20 + get_level(INVISIBILITY, 50)) + end, + ["desc"] = { + "Grants invisibility" + } +} + +POISONBLOOD = add_spell +{ + ["name"] = "Poison Blood", + ["school"] = {SCHOOL_AIR}, + ["level"] = 12, + ["mana"] = 10, + ["mana_max"] = 20, + ["fail"] = 30, + ["stick"] = + { + ["charge"] = { 10, 15 }, + [TV_WAND] = + { + ["rarity"] = 45, + ["base_level"] = { 1, 25 }, + ["max_level"] = { 35, 50 }, + }, + }, + ["inertia"] = { 1, 35 }, + ["spell"] = function() + local obvious = nil + if player.oppose_pois == 0 then obvious = set_oppose_pois(randint(30) + 25 + get_level(POISONBLOOD, 25)) end + if (player.tim_poison == 0) and (get_level(POISONBLOOD, 50) >= 15) then obvious = is_obvious(set_poison(randint(30) + 25 + get_level(POISONBLOOD, 25)), obvious) end + return obvious + end, + ["info"] = function() + return "dur "..(25 + get_level(POISONBLOOD, 25)).."+d30" + end, + ["desc"] = { + "Grants resist poison", + "At level 15 it provides poison branding to wielded weapon" + } +} + +THUNDERSTORM = add_spell +{ + ["name"] = "Thunderstorm", + ["school"] = {SCHOOL_AIR, SCHOOL_NATURE}, + ["level"] = 25, + ["mana"] = 40, + ["mana_max"] = 60, + ["fail"] = 60, + ["stick"] = + { + ["charge"] = { 5, 5 }, + [TV_WAND] = + { + ["rarity"] = 85, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 25, 50 }, + }, + }, + ["inertia"] = { 2, 15 }, + ["spell"] = function() + if player.tim_thunder == 0 then return set_tim_thunder(randint(10) + 10 + get_level(THUNDERSTORM, 25), 5 + get_level(THUNDERSTORM, 10), 10 + get_level(THUNDERSTORM, 25)) end + return FALSE + end, + ["info"] = function() + return "dam "..(5 + get_level(THUNDERSTORM, 10)).."d"..(10 + get_level(THUNDERSTORM, 25)).." dur "..(10 + get_level(THUNDERSTORM, 25)).."+d10" + end, + ["desc"] = { + "Charges up the air around you with electricity", + "Each turn it will throw a thunder bolt at a random monster in sight", + "The thunder does 3 types of damage, one third of lightning", + "one third of sound and one third of light" + } +} + +STERILIZE = add_spell +{ + ["name"] = "Sterilize", + ["school"] = {SCHOOL_AIR}, + ["level"] = 20, + ["mana"] = 10, + ["mana_max"] = 100, + ["fail"] = 50, + ["stick"] = + { + ["charge"] = { 7, 5 }, + [TV_STAFF] = + { + ["rarity"] = 20, + ["base_level"] = { 1, 10 }, + ["max_level"] = { 20, 50 }, + }, + }, + ["spell"] = function() + set_no_breeders((30) + 20 + get_level(STERILIZE, 70)) + return TRUE + end, + ["info"] = function() + return "dur "..(20 + get_level(STERILIZE, 70)).."+d30" + end, + ["desc"] = { + "Prevents explosive breeding for a while." + } +} diff --git a/lib/scpt/s_convey.lua b/lib/scpt/s_convey.lua new file mode 100644 index 00000000..e7856c43 --- /dev/null +++ b/lib/scpt/s_convey.lua @@ -0,0 +1,227 @@ +-- handle the conveyance school + +BLINK = add_spell +{ + ["name"] = "Phase Door", + ["school"] = {SCHOOL_CONVEYANCE}, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 3, + ["fail"] = 10, + ["inertia"] = { 1, 5 }, + ["spell"] = function() + if get_level(BLINK, 50) >= 30 then + local oy, ox = player.py, player.px + + teleport_player(10 + get_level(BLINK, 8)) + create_between_gate(0, oy, ox) + return TRUE + else + teleport_player(10 + get_level(BLINK, 8)) + return TRUE + end + end, + ["info"] = function() + return "distance "..(10 + get_level(BLINK, 8)) + end, + ["desc"] = { + "Teleports you on a small scale range", + "At level 30 it creates void jumpgates", + } +} + +DISARM = add_spell +{ + ["name"] = "Disarm", + ["school"] = {SCHOOL_CONVEYANCE}, + ["level"] = 3, + ["mana"] = 2, + ["mana_max"] = 4, + ["fail"] = 15, + ["stick"] = + { + ["charge"] = { 10, 15 }, + [TV_STAFF] = + { + ["rarity"] = 4, + ["base_level"] = { 1, 10 }, + ["max_level"] = { 10, 50 }, + }, + }, + ["spell"] = function() + local obvious + obvious = destroy_doors_touch() + if get_level(DISARM, 50) >= 10 then obvious = is_obvious(destroy_traps_touch(), obvious) end + return obvious + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Destroys doors and traps", + "At level 10 it destroys doors and traps, then reveals and unlocks any secret", + "doors" + } +} + +TELEPORT = add_spell +{ + ["name"] = "Teleportation", + ["school"] = {SCHOOL_CONVEYANCE}, + ["level"] = 10, + ["mana"] = 8, + ["mana_max"] = 14, + ["fail"] = 30, + ["stick"] = + { + ["charge"] = { 7, 7 }, + [TV_STAFF] = + { + ["rarity"] = 50, + ["base_level"] = { 1, 20 }, + ["max_level"] = { 20, 50 }, + }, + }, + ["inertia"] = { 1, 10 }, + ["spell"] = function() + player.energy = player.energy - (25 - get_level(TELEPORT, 50)) + teleport_player(100 + get_level(TELEPORT, 100)) + return TRUE + end, + ["info"] = function() + return "distance "..(100 + get_level(TELEPORT, 100)) + end, + ["desc"] = { + "Teleports you around the level. The casting time decreases with level", + } +} + +TELEAWAY = add_spell +{ + ["name"] = "Teleport Away", + ["school"] = {SCHOOL_CONVEYANCE}, + ["level"] = 23, + ["mana"] = 15, + ["mana_max"] = 40, + ["fail"] = 60, + ["stick"] = + { + ["charge"] = { 3, 5 }, + [TV_WAND] = + { + ["rarity"] = 75, + ["base_level"] = { 1, 20 }, + ["max_level"] = { 20, 50 }, + }, + }, + ["spell"] = function() + local ret, dir + + if get_level(TELEAWAY, 50) >= 20 then + return project_los(GF_AWAY_ALL, 100) + elseif get_level(TELEAWAY, 50) >= 10 then + ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_ball(GF_AWAY_ALL, dir, 100, 3 + get_level(TELEAWAY, 4)) + else + ret, dir = get_aim_dir() + if ret == FALSE then return end + return teleport_monster(dir) + end + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Teleports a line of monsters away", + "At level 10 it turns into a ball", + "At level 20 it teleports all monsters in sight" + } +} + +RECALL = add_spell +{ + ["name"] = "Recall", + ["school"] = {SCHOOL_CONVEYANCE}, + ["level"] = 30, + ["mana"] = 25, + ["mana_max"] = 25, + ["fail"] = 60, + ["spell"] = function() + local ret, x, y, c_ptr + ret, x, y = tgt_pt() + if ret == FALSE then return end + c_ptr = cave(y, x) + if (y == player.py) and (x == player.px) then + local d = 21 - get_level(RECALL, 15) + if d < 0 then + d = 0 + end + local f = 15 - get_level(RECALL, 10) + if f < 1 then + f = 1 + end + recall_player(d, f) + return TRUE + elseif c_ptr.m_idx > 0 then + swap_position(y, x) + return TRUE + elseif c_ptr.o_idx > 0 then + set_target(y, x) + if get_level(RECALL, 50) >= 15 then + fetch(5, 10 + get_level(RECALL, 150), FALSE) + else + fetch(5, 10 + get_level(RECALL, 150), TRUE) + end + return TRUE + end + end, + ["info"] = function() + local d = 21 - get_level(RECALL, 15) + if d < 0 then + d = 0 + end + local f = 15 - get_level(RECALL, 10) + if f < 1 then + f = 1 + end + return "dur "..f.."+d"..d.." weight "..(1 + get_level(RECALL, 15)).."lb" + end, + ["desc"] = { + "Cast on yourself it will recall you to the surface/dungeon.", + "Cast at a monster you will swap positions with the monster.", + "Cast at an object it will fetch the object to you." + } +} + +PROBABILITY_TRAVEL = add_spell +{ + ["name"] = "Probability Travel", + ["school"] = {SCHOOL_CONVEYANCE}, + ["level"] = 35, + ["mana"] = 30, + ["mana_max"] = 50, + ["fail"] = 90, + ["stick"] = + { + ["charge"] = { 1, 2 }, + [TV_STAFF] = + { + ["rarity"] = 97, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 8, 25 }, + }, + }, + ["inertia"] = { 6, 40 }, + ["spell"] = function() + return set_prob_travel(randint(20) + get_level(PROBABILITY_TRAVEL, 60)) + end, + ["info"] = function() + return "dur "..get_level(PROBABILITY_TRAVEL, 60).."+d20" + end, + ["desc"] = { + "Renders you immaterial, when you hit a wall you travel through it and", + "instantly appear on the other side of it. You can also float up and down", + "at will" + } +} diff --git a/lib/scpt/s_demon.lua b/lib/scpt/s_demon.lua new file mode 100644 index 00000000..ada97310 --- /dev/null +++ b/lib/scpt/s_demon.lua @@ -0,0 +1,337 @@ +-- handle the demonology school + +-- Demonblade +DEMON_BLADE = add_spell +{ + ["name"] = "Demon Blade", + ["school"] = {SCHOOL_DEMON}, + ["level"] = 1, + ["mana"] = 4, + ["mana_max"] = 44, + ["fail"] = 10, + ["random"] = 0, + ["stick"] = + { + ["charge"] = { 3, 7 }, + [TV_WAND] = + { + ["rarity"] = 75, + ["base_level"] = { 1, 17 }, + ["max_level"] = { 20, 40 }, + }, + }, + ["spell"] = function() + local type, rad + + type = GF_FIRE + if get_level(DEMON_BLADE) >= 30 then type = GF_HELL_FIRE end + + rad = 0 + if get_level(DEMON_BLADE) >= 45 then rad = 1 end + + return set_project(randint(20) + get_level(DEMON_BLADE, 80), + type, + 4 + get_level(DEMON_BLADE, 40), + rad, + bor(PROJECT_STOP, PROJECT_KILL)) + end, + ["info"] = function() + return "dur "..(get_level(DEMON_BLADE, 80)).."+d20 dam "..(4 + get_level(DEMON_BLADE, 40)).."/blow" + end, + ["desc"] = { + "Imbues your blade with fire to deal more damage", + "At level 30 it deals hellfire damage", + "At level 45 it spreads over a 1 radius zone around your target", + } +} + +DEMON_MADNESS = add_spell +{ + ["name"] = "Demon Madness", + ["school"] = {SCHOOL_DEMON}, + ["level"] = 10, + ["mana"] = 5, + ["mana_max"] = 20, + ["fail"] = 25, + ["random"] = 0, + ["spell"] = function() + local ret, dir, type, y1, x1, y2, x2 + + ret, dir = get_aim_dir() + if ret == FALSE then return end + + type = GF_CHAOS + if magik(33) == TRUE then type = GF_CONFUSION end + if magik(33) == TRUE then type = GF_CHARM end + + -- Calc the coordinates of arrival + y1, x1 = get_target(dir) + y2 = player.py - (y1 - player.py) + x2 = player.px - (x1 - player.px) + + local obvious = nil + obvious = project(0, 1 + get_level(DEMON_MADNESS, 4, 0), + y1, x1, + 20 + get_level(DEMON_MADNESS, 200), + type, bor(PROJECT_STOP, PROJECT_GRID, PROJECT_ITEM, PROJECT_KILL)) + obvious = is_obvious(project(0, 1 + get_level(DEMON_MADNESS, 4, 0), + y2, x2, + 20 + get_level(DEMON_MADNESS, 200), + type, bor(PROJECT_STOP, PROJECT_GRID, PROJECT_ITEM, PROJECT_KILL)), obvious) + return obvious + end, + ["info"] = function() + return "dam "..(20 + get_level(DEMON_MADNESS, 200)).." rad "..(1 + get_level(DEMON_MADNESS, 4, 0)) + end, + ["desc"] = { + "Fire 2 balls in opposite directions of randomly chaos, confusion or charm", + } +} + +DEMON_FIELD = add_spell +{ + ["name"] = "Demon Field", + ["school"] = {SCHOOL_DEMON}, + ["level"] = 20, + ["mana"] = 20, + ["mana_max"] = 60, + ["fail"] = 60, + ["random"] = 0, + ["spell"] = function() + local ret, dir + + ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_cloud(GF_NEXUS, dir, 20 + get_level(DEMON_FIELD, 70), 7, 30 + get_level(DEMON_FIELD, 100)) + end, + ["info"] = function() + return "dam "..(20 + get_level(DEMON_FIELD, 70)).." dur "..(30 + get_level(DEMON_FIELD, 100)) + end, + ["desc"] = { + "Fires a cloud of deadly nexus over a radius of 7", + } +} + +-- Demonshield + +DOOM_SHIELD = add_spell +{ + ["name"] = "Doom Shield", + ["school"] = {SCHOOL_DEMON}, + ["level"] = 1, + ["mana"] = 2, + ["mana_max"] = 30, + ["fail"] = 10, + ["random"] = 0, + ["spell"] = function() + return set_shield(randint(10) + 20 + get_level(DOOM_SHIELD, 100), -300 + get_level(DOOM_SHIELD, 100), SHIELD_COUNTER, 1 + get_level(DOOM_SHIELD, 14), 10 + get_level(DOOM_SHIELD, 15)) + end, + ["info"] = function() + return "dur "..(20 + get_level(DOOM_SHIELD, 100)).."+d10 dam "..(1 + get_level(DOOM_SHIELD, 14)).."d"..(10 + get_level(DOOM_SHIELD, 15)) + end, + ["desc"] = { + "Raises a mirror of pain around you, doing very high damage to your foes", + "that dare hit you, but greatly reduces your armour class", + } +} + +UNHOLY_WORD = add_spell +{ + ["name"] = "Unholy Word", + ["school"] = {SCHOOL_DEMON}, + ["level"] = 25, + ["mana"] = 15, + ["mana_max"] = 45, + ["fail"] = 55, + ["random"] = 0, + ["spell"] = function() + local ret, x, y, c_ptr + ret, x, y = tgt_pt() + if ret == FALSE then return end + c_ptr = cave(y, x) + + -- ok that is a monster + if c_ptr.m_idx > 0 then + local m_ptr = monster(c_ptr.m_idx) + if m_ptr.status ~= MSTATUS_PET then + msg_print("You can only target a pet.") + return + end + + -- Oups he is angry now + if magik(30 - get_level(UNHOLY_WORD, 25, 0)) == TRUE then + local m_name = monster_desc(m_ptr, 0).." turns against you." + msg_print(strupper(strsub(m_name, 0, 1))..strsub(m_name, 2)) + else + local m_name = monster_desc(m_ptr, 0) + msg_print("You consume "..m_name..".") + + local heal = (m_ptr.hp * 100) / m_ptr.maxhp + heal = ((30 + get_level(UNHOLY_WORD, 50, 0)) * heal) / 100 + + hp_player(heal) + + delete_monster_idx(c_ptr.m_idx) + end + return TRUE + end + end, + ["info"] = function() + return "heal mhp% of "..(30 + get_level(UNHOLY_WORD, 50, 0)).."%" + end, + ["desc"] = { + "Kills a pet to heal you", + "There is a chance that the pet won't die but will turn against you", + "it will decrease with higher level", + } +} + +DEMON_CLOAK = add_spell +{ + ["name"] = "Demon Cloak", + ["school"] = {SCHOOL_DEMON}, + ["level"] = 20, + ["mana"] = 10, + ["mana_max"] = 40, + ["fail"] = 70, + ["random"] = 0, + ["spell"] = function() + return set_tim_reflect(randint(5) + 5 + get_level(DEMON_CLOAK, 15, 0)) + end, + ["info"] = function() + return "dur "..(5 + get_level(DEMON_CLOAK, 15, 0)).."+d5" + end, + ["desc"] = { + "Raises a mirror that can reflect bolts and arrows for a time", + } +} + + +-- Demonhorn +DEMON_SUMMON = add_spell +{ + ["name"] = "Summon Demon", + ["school"] = {SCHOOL_DEMON}, + ["level"] = 5, + ["mana"] = 10, + ["mana_max"] = 50, + ["fail"] = 30, + ["random"] = 0, + ["spell"] = function() + local type = SUMMON_DEMON + local level = dun_level + local minlevel = 4 + if level < minlevel then level=minlevel end + summon_specific_level = 5 + get_level(DEMON_SUMMON, 100) + if get_level(DEMON_SUMMON) >= 35 then type = SUMMON_HI_DEMON end + if summon_monster(player.py, player.px, level, TRUE, type) == TRUE then + return TRUE + else + msg_print("Something blocks your summoning!") + return FALSE + end + end, + ["info"] = function() + return "level "..(5 + get_level(DEMON_SUMMON, 100)) + end, + ["desc"] = { + "Summons a leveled demon to your side", + "At level 35 it summons a high demon", + } +} + +DISCHARGE_MINION = add_spell +{ + ["name"] = "Discharge Minion", + ["school"] = {SCHOOL_DEMON}, + ["level"] = 10, + ["mana"] = 20, + ["mana_max"] = 50, + ["fail"] = 30, + ["random"] = 0, + ["spell"] = function() + local ret, x, y, c_ptr + ret, x, y = tgt_pt() + if ret == FALSE then return end + c_ptr = cave(y, x) + + -- ok that is a monster + if c_ptr.m_idx > 0 then + local m_ptr = monster(c_ptr.m_idx) + if m_ptr.status ~= MSTATUS_PET then + msg_print("You can only target a pet.") + return + end + + local dam = m_ptr.hp + delete_monster_idx(c_ptr.m_idx) + dam = (dam * (20 + get_level(DISCHARGE_MINION, 60, 0))) / 100 + if dam > 100 + get_level(DISCHARGE_MINION, 500, 0) then + dam = 100 + get_level(DISCHARGE_MINION, 500, 0) + end + + -- We use project instead of fire_ball because we must tell it exactly where to land + return project(0, 2, + y, x, + dam, + GF_GRAVITY, bor(PROJECT_STOP, PROJECT_GRID, PROJECT_ITEM, PROJECT_KILL)) + end + end, + ["info"] = function() + return "dam "..(20 + get_level(DISCHARGE_MINION, 60, 0)).."% max "..(100 + get_level(DISCHARGE_MINION, 500, 0)) + end, + ["desc"] = { + "The targeted pet will explode in a burst of gravity", + } +} + +CONTROL_DEMON = add_spell +{ + ["name"] = "Control Demon", + ["school"] = {SCHOOL_DEMON}, + ["level"] = 25, + ["mana"] = 30, + ["mana_max"] = 70, + ["fail"] = 55, + ["random"] = 0, + ["spell"] = function() + local ret, dir = get_aim_dir() + return fire_ball(GF_CONTROL_DEMON, dir, 50 + get_level(CONTROL_DEMON, 250), 0) + end, + ["info"] = function() + return "power "..(50 + get_level(CONTROL_DEMON, 250)) + end, + ["desc"] = { + "Attempts to control a demon", + } +} + +-- ok we need to have different wield slots +add_hooks +{ + [HOOK_WIELD_SLOT] = function (obj, ideal) + if (obj.tval == TV_DAEMON_BOOK) then + local slot + if (obj.sval == SV_DEMONBLADE) then + if(ideal == TRUE) then + slot = INVEN_WIELD + else + slot = get_slot(INVEN_WIELD) + end + elseif (obj.sval == SV_DEMONSHIELD) then + if(ideal == TRUE) then + slot = INVEN_ARM + else + slot = get_slot(INVEN_ARM) + end + elseif (obj.sval == SV_DEMONHORN) then + if(ideal == TRUE) then + slot = INVEN_HEAD + else + slot = get_slot(INVEN_HEAD) + end + end + return TRUE, slot + end + end, +} diff --git a/lib/scpt/s_divin.lua b/lib/scpt/s_divin.lua new file mode 100644 index 00000000..60b0275f --- /dev/null +++ b/lib/scpt/s_divin.lua @@ -0,0 +1,230 @@ +-- Handles thhe divination school + + +STARIDENTIFY = add_spell +{ + ["name"] = "Greater Identify", + ["school"] = {SCHOOL_DIVINATION}, + ["level"] = 35, + ["mana"] = 30, + ["mana_max"] = 30, + ["fail"] = 80, + ["spell"] = function() + if get_check("Cast on yourself?") == TRUE then + self_knowledge() + else + identify_fully() + end + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Asks for an object and fully identify it, providing the full list of powers", + "Cast at yourself it will reveal your powers" + } +} + +IDENTIFY = add_spell +{ + ["name"] = "Identify", + ["school"] = {SCHOOL_DIVINATION}, + ["level"] = 8, + ["mana"] = 10, + ["mana_max"] = 50, + ["fail"] = 40, + ["stick"] = + { + ["charge"] = { 7, 10 }, + [TV_STAFF] = + { + ["rarity"] = 45, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 15, 40 }, + }, + }, + ["spell"] = function() + if get_level(IDENTIFY, 50) >= 27 then + local obvious + obvious = identify_pack() + obvious = is_obvious(fire_ball(GF_IDENTIFY, 0, 1, get_level(IDENTIFY, 3)), obvious) + if obvious == TRUE then + player.notice = bor(player.notice, PN_COMBINE, PN_REORDER) + end + return obvious + elseif get_level(IDENTIFY, 50) >= 17 then + local obvious + obvious = identify_pack() + obvious = is_obvious(fire_ball(GF_IDENTIFY, 0, 1, 0), obvious) + if obvious == TRUE then + player.notice = bor(player.notice, PN_COMBINE, PN_REORDER) + end + return obvious + else + if ident_spell() == TRUE then return TRUE else return end + end + end, + ["info"] = function() + if get_level(IDENTIFY, 50) >= 27 then + return "rad "..(get_level(IDENTIFY, 3)) + else + return "" + end + end, + ["desc"] = { + "Asks for an object and identifies it", + "At level 17 it identifies all objects in the inventory", + "At level 27 it identifies all objects in the inventory and in a", + "radius on the floor, as well as probing monsters in that radius" + } +} + +VISION = add_spell +{ + ["name"] = "Vision", + ["school"] = {SCHOOL_DIVINATION}, + ["level"] = 15, + ["mana"] = 7, + ["mana_max"] = 55, + ["fail"] = 45, + ["stick"] = + { + ["charge"] = { 4, 6 }, + [TV_STAFF] = + { + ["rarity"] = 60, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 10, 30 }, + }, + }, + ["inertia"] = { 2, 200 }, + ["spell"] = function() + if get_level(VISION, 50) >= 25 then + wiz_lite_extra() + else + map_area() + end + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Detects the layout of the surrounding area", + "At level 25 it maps and lights the whole level", + } +} + +SENSEHIDDEN = add_spell +{ + ["name"] = "Sense Hidden", + ["school"] = {SCHOOL_DIVINATION}, + ["level"] = 5, + ["mana"] = 2, + ["mana_max"] = 10, + ["fail"] = 25, + ["stick"] = + { + ["charge"] = { 1, 15 }, + [TV_STAFF] = + { + ["rarity"] = 20, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 10, 50 }, + }, + }, + ["inertia"] = { 1, 10 }, + ["spell"] = function() + local obvious = nil + obvious = detect_traps(15 + get_level(SENSEHIDDEN, 40, 0)) + if get_level(SENSEHIDDEN, 50) >= 15 then + obvious = is_obvious(set_tim_invis(10 + randint(20) + get_level(SENSEHIDDEN, 40)), obvious) + end + return obvious + end, + ["info"] = function() + if get_level(SENSEHIDDEN, 50) >= 15 then + return "rad "..(15 + get_level(SENSEHIDDEN, 40)).." dur "..(10 + get_level(SENSEHIDDEN, 40)).."+d20" + else + return "rad "..(15 + get_level(SENSEHIDDEN, 40)) + end + end, + ["desc"] = { + "Detects the traps in a certain radius around you", + "At level 15 it allows you to sense invisible for a while" + } +} + +REVEALWAYS = add_spell +{ + ["name"] = "Reveal Ways", + ["school"] = {SCHOOL_DIVINATION}, + ["level"] = 9, + ["mana"] = 3, + ["mana_max"] = 15, + ["fail"] = 20, + ["stick"] = + { + ["charge"] = { 6, 6 }, + [TV_STAFF] = + { + ["rarity"] = 35, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 25, 50 }, + }, + }, + ["inertia"] = { 1, 10 }, + ["spell"] = function() + local obvious + obvious = detect_doors(10 + get_level(REVEALWAYS, 40, 0)) + obvious = is_obvious(detect_stairs(10 + get_level(REVEALWAYS, 40, 0)), obvious) + return obvious + end, + ["info"] = function() + return "rad "..(10 + get_level(REVEALWAYS, 40)) + end, + ["desc"] = { + "Detects the doors/stairs/ways in a certain radius around you", + } +} + +SENSEMONSTERS = add_spell +{ + ["name"] = "Sense Monsters", + ["school"] = {SCHOOL_DIVINATION}, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 20, + ["fail"] = 10, + ["stick"] = + { + ["charge"] = { 5, 10 }, + [TV_STAFF] = + { + ["rarity"] = 37, + ["base_level"] = { 1, 10 }, + ["max_level"] = { 15, 40 }, + }, + }, + ["inertia"] = { 1, 10 }, + ["spell"] = function() + local obvious + obvious = detect_monsters_normal(10 + get_level(SENSEMONSTERS, 40, 0)) + if get_level(SENSEMONSTERS, 50) >= 30 then + obvious = is_obvious(set_tim_esp(10 + randint(10) + get_level(SENSEMONSTERS, 20)), obvious) + end + return obvious + end, + ["info"] = function() + if get_level(SENSEMONSTERS, 50) >= 30 then + return "rad "..(10 + get_level(SENSEMONSTERS, 40)).." dur "..(10 + get_level(SENSEMONSTERS, 20)).."+d10" + else + return "rad "..(10 + get_level(SENSEMONSTERS, 40)) + end + end, + ["desc"] = { + "Detects all monsters near you", + "At level 30 it allows you to sense monster minds for a while" + } +} diff --git a/lib/scpt/s_earth.lua b/lib/scpt/s_earth.lua new file mode 100644 index 00000000..23aa001c --- /dev/null +++ b/lib/scpt/s_earth.lua @@ -0,0 +1,184 @@ +-- The earth school + +STONESKIN = add_spell +{ + ["name"] = "Stone Skin", + ["school"] = SCHOOL_EARTH, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 50, + ["fail"] = 10, + ["inertia"] = { 2, 50 }, + ["spell"] = function() + local type + if get_level(STONESKIN, 50) >= 25 then + type = SHIELD_COUNTER + else + type = 0 + end + return set_shield(randint(10) + 10 + get_level(STONESKIN, 100), 10 + get_level(STONESKIN, 50), type, 2 + get_level(STONESKIN, 5), 3 + get_level(STONESKIN, 5)) + end, + ["info"] = function() + if get_level(STONESKIN, 50) >= 25 then + return "dam "..(2 + get_level(STONESKIN, 5)).."d"..(3 + get_level(STONESKIN, 5)).." dur "..(10 + get_level(STONESKIN, 100)).."+d10 AC "..(10 + get_level(STONESKIN, 50)) + else + return "dur "..(10 + get_level(STONESKIN, 100)).."+d10 AC "..(10 + get_level(STONESKIN, 50)) + end + end, + ["desc"] = { + "Creates a shield of earth around you to protect you", + "At level 25 it starts dealing damage to attackers" + } +} + +DIG = add_spell +{ + ["name"] = "Dig", + ["school"] = SCHOOL_EARTH, + ["level"] = 12, + ["mana"] = 14, + ["mana_max"] = 14, + ["fail"] = 20, + ["stick"] = + { + ["charge"] = { 15, 5 }, + [TV_WAND] = + { + ["rarity"] = 25, + ["base_level"] = { 1, 1 }, + ["max_level"] = { 1, 1 }, + }, + }, + ["spell"] = function() + local ret, dir + ret, dir = get_aim_dir() + if ret == FALSE then return end + return wall_to_mud(dir) + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Digs a hole in a wall much faster than any shovels", + } +} + +STONEPRISON = add_spell +{ + ["name"] = "Stone Prison", + ["school"] = SCHOOL_EARTH, + ["level"] = 25, + ["mana"] = 30, + ["mana_max"] = 50, + ["fail"] = 65, + ["stick"] = + { + ["charge"] = { 5, 3 }, + [TV_WAND] = + { + ["rarity"] = 57, + ["base_level"] = { 1, 3 }, + ["max_level"] = { 5, 20 }, + }, + }, + ["spell"] = function() + local ret, x, y + if get_level(STONEPRISON, 50) >= 10 then + ret, x, y = tgt_pt() + else + y = player.py + x = player.px + end + wall_stone(y, x) + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Creates a prison of walls around you", + "At level 10 it allows you to target a monster" + } +} + +STRIKE = add_spell +{ + ["name"] = "Strike", + ["school"] = {SCHOOL_EARTH}, + ["level"] = 30, + ["mana"] = 30, + ["mana_max"] = 50, + ["fail"] = 60, + ["stick"] = + { + ["charge"] = { 2, 6 }, + [TV_WAND] = + { + ["rarity"] = 635, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 10, 50 }, + }, + }, + ["spell"] = function() + local ret, dir, rad + ret, dir = get_aim_dir() + if ret == FALSE then return end + if get_level(STRIKE, 50) >= 12 then + return fire_ball(GF_FORCE, dir, 50 + get_level(STRIKE, 50), 1) + else + return fire_ball(GF_FORCE, dir, 50 + get_level(STRIKE, 50), 0) + end + end, + ["info"] = function() + if get_level(STRIKE, 50) >= 12 then + return "dam "..(50 + get_level(STRIKE, 50)).." rad 1" + else + return "dam "..(50 + get_level(STRIKE, 50)) + end + end, + ["desc"] = { + "Creates a micro-ball of force that will push monsters backwards", + "If the monster is caught near a wall, it'll be crushed against it", + "At level 12 it turns into a ball of radius 1" + } +} + +SHAKE = add_spell +{ + ["name"] = "Shake", + ["school"] = {SCHOOL_EARTH}, + ["level"] = 27, + ["mana"] = 25, + ["mana_max"] = 30, + ["fail"] = 60, + ["stick"] = + { + ["charge"] = { 5, 10 }, + [TV_STAFF] = + { + ["rarity"] = 75, + ["base_level"] = { 1, 3 }, + ["max_level"] = { 9, 20 }, + }, + }, + ["inertia"] = { 2, 50 }, + ["spell"] = function() + local ret, x, y + if get_level(SHAKE, 50) >= 10 then + ret, x, y = tgt_pt() + if ret == FALSE then return end + else + x = player.px + y = player.py + end + earthquake(y, x, 4 + get_level(SHAKE, 10)); + return TRUE + end, + ["info"] = function() + return "rad "..(4 + get_level(SHAKE, 10)) + end, + ["desc"] = { + "Creates a localised earthquake", + "At level 10 it can be targeted at any location" + } +} diff --git a/lib/scpt/s_eru.lua b/lib/scpt/s_eru.lua new file mode 100644 index 00000000..c0cb0aaf --- /dev/null +++ b/lib/scpt/s_eru.lua @@ -0,0 +1,130 @@ +-- Handle Eru Iluvatar magic school + +ERU_SEE = add_spell +{ + ["name"] = "See the Music", + ["school"] = {SCHOOL_ERU}, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 50, + ["fail"] = 20, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + -- Unnafected by blindness + ["blind"] = FALSE, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local obvious + obvious = set_tim_invis(randint(20) + 10 + get_level(ERU_SEE, 100)) + if get_level(ERU_SEE) >= 30 then + wiz_lite_extra() + obvious = TRUE + elseif get_level(ERU_SEE) >= 10 then + map_area() + obvious = TRUE + end + if get_level(ERU_SEE) >= 20 then + obvious = is_obvious(set_blind(0), obvious) + end + return obvious + end, + ["info"] = function() + return "dur "..(10 + get_level(ERU_SEE, 100)).."+d20" + end, + ["desc"] = { + "Allows you to 'see' the Great Music from which the world", + "originates, allowing you to see unseen things", + "At level 10 it allows you to see your surroundings", + "At level 20 it allows you to cure blindness", + "At level 30 it allows you to fully see all the level" + } +} + +ERU_LISTEN = add_spell +{ + ["name"] = "Listen to the Music", + ["school"] = {SCHOOL_ERU}, + ["level"] = 7, + ["mana"] = 15, + ["mana_max"] = 200, + ["fail"] = 25, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + if get_level(ERU_LISTEN) >= 30 then + ident_all() + identify_pack() + return TRUE + elseif get_level(ERU_LISTEN) >= 14 then + identify_pack() + return TRUE + else + return ident_spell() + end + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Allows you to listen to the Great Music from which the world", + "originates, allowing you to understand the meaning of things", + "At level 14 it allows you to identify all your pack", + "At level 30 it allows you to identify all items on the level", + } +} + +ERU_UNDERSTAND = add_spell +{ + ["name"] = "Know the Music", + ["school"] = {SCHOOL_ERU}, + ["level"] = 30, + ["mana"] = 200, + ["mana_max"] = 600, + ["fail"] = 50, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + if get_level(ERU_UNDERSTAND) >= 10 then + identify_pack_fully() + return TRUE + else + return identify_fully() + end + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Allows you to understand the Great Music from which the world", + "originates, allowing you to know the full abilities of things", + "At level 10 it allows you to *identify* all your pack", + } +} + +ERU_PROT = add_spell +{ + ["name"] = "Lay of Protection", + ["school"] = {SCHOOL_ERU}, + ["level"] = 35, + ["mana"] = 400, + ["mana_max"] = 400, + ["fail"] = 80, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + return fire_ball(GF_MAKE_GLYPH, 0, 1, 1 + get_level(ERU_PROT, 2, 0)) + end, + ["info"] = function() + return "rad "..(1 + get_level(ERU_PROT, 2, 0)) + end, + ["desc"] = { + "Creates a circle of safety around you", + } +} diff --git a/lib/scpt/s_fire.lua b/lib/scpt/s_fire.lua new file mode 100644 index 00000000..dbbf7604 --- /dev/null +++ b/lib/scpt/s_fire.lua @@ -0,0 +1,227 @@ +-- handle the fire school + +GLOBELIGHT = add_spell +{ + ["name"] = "Globe of Light", + ["school"] = {SCHOOL_FIRE}, + ["level"] = 1, + ["mana"] = 2, + ["mana_max"] = 15, + ["fail"] = 10, + ["stick"] = + { + ["charge"] = { 10, 5 }, + [TV_STAFF] = + { + ["rarity"] = 7, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 10, 45 }, + }, + }, + ["inertia"] = { 1, 40 }, + ["spell"] = function() + local obvious + if get_level(GLOBELIGHT, 50) >= 3 then + obvious = lite_area(10, 4) + else + lite_room(player.py, player.px) + obvious = TRUE + end + if get_level(GLOBELIGHT, 50) >= 15 then + obvious = is_obvious(fire_ball(GF_LITE, 0, 10 + get_level(GLOBELIGHT, 100), 5 + get_level(GLOBELIGHT, 6)), obvious) + player.update = bor(player.update, PU_VIEW) + end + return obvious + end, + ["info"] = function() + if get_level(GLOBELIGHT, 50) >= 15 then + return "dam "..(10 + get_level(GLOBELIGHT, 100)).." rad "..(5 + get_level(GLOBELIGHT, 6)) + else + return "" + end + end, + ["desc"] = { + "Creates a globe of pure light", + "At level 3 it starts damaging monsters", + "At level 15 it starts creating a more powerful kind of light", + } +} + +FIREFLASH = add_spell +{ + ["name"] = "Fireflash", + ["school"] = {SCHOOL_FIRE}, + ["level"] = 10, + ["mana"] = 5, + ["mana_max"] = 70, + ["fail"] = 35, + ["stick"] = + { + ["charge"] = { 5, 5 }, + [TV_WAND] = + { + ["rarity"] = 35, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 15, 35 }, + }, + }, + ["spell"] = function() + local ret, dir, type + if (get_level(FIREFLASH, 50) >= 20) then + type = GF_HOLY_FIRE + else + type = GF_FIRE + end + ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_ball(type, dir, 20 + get_level(FIREFLASH, 500), 2 + get_level(FIREFLASH, 5)) + end, + ["info"] = function() + return "dam "..(20 + get_level(FIREFLASH, 500)).." rad "..(2 + get_level(FIREFLASH, 5)) + end, + ["desc"] = { + "Conjures a ball of fire to burn your foes to ashes", + "At level 20 it turns into a ball of holy fire" + } +} + +FIERYAURA = add_spell +{ + ["name"] = "Fiery Shield", + ["school"] = {SCHOOL_FIRE}, + ["level"] = 20, + ["mana"] = 20, + ["mana_max"] = 60, + ["fail"] = 50, + ["stick"] = + { + ["charge"] = { 3, 5 }, + [TV_STAFF] = + { + ["rarity"] = 50, + ["base_level"] = { 1, 10 }, + ["max_level"] = { 5, 40 }, + }, + }, + ["inertia"] = { 2, 15 }, + ["spell"] = function() + local type + if (get_level(FIERYAURA, 50) >= 8) then + type = SHIELD_GREAT_FIRE + else + type = SHIELD_FIRE + end + return set_shield(randint(20) + 10 + get_level(FIERYAURA, 70), 10, type, 5 + get_level(FIERYAURA, 10), 5 + get_level(FIERYAURA, 7)) + end, + ["info"] = function() + return "dam "..(5 + get_level(FIERYAURA, 15)).."d"..(5 + get_level(FIERYAURA, 7)).." dur "..(10 + get_level(FIERYAURA, 70)).."+d20" + end, + ["desc"] = { + "Creates a shield of fierce flames around you", + "At level 8 it turns into a greater kind of flame that can not be resisted" + } +} + +FIREWALL = add_spell +{ + ["name"] = "Firewall", + ["school"] = {SCHOOL_FIRE}, + ["level"] = 15, + ["mana"] = 25, + ["mana_max"] = 100, + ["fail"] = 40, + ["stick"] = + { + ["charge"] = { 4, 5 }, + [TV_WAND] = + { + ["rarity"] = 55, + ["base_level"] = { 1, 10 }, + ["max_level"] = { 5, 40 }, + }, + }, + ["spell"] = function() + local ret, dir, type + if (get_level(FIREWALL, 50) >= 6) then + type = GF_HELL_FIRE + else + type = GF_FIRE + end + ret, dir = get_aim_dir() + if ret == FALSE then return end + fire_wall(type, dir, 40 + get_level(FIREWALL, 150), 10 + get_level(FIREWALL, 14)) + return TRUE + end, + ["info"] = function() + return "dam "..(40 + get_level(FIREWALL, 150)).." dur "..(10 + get_level(FIREWALL, 14)) + end, + ["desc"] = { + "Creates a fiery wall to incinerate monsters stupid enough to attack you", + "At level 6 it turns into a wall of hell fire" + } +} + +FIREGOLEM = add_spell +{ + ["name"] = "Fire Golem", + ["school"] = {SCHOOL_FIRE, SCHOOL_MIND}, + ["level"] = 7, + ["mana"] = 16, + ["mana_max"] = 70, + ["fail"] = 40, + ["spell"] = function() + local m_idx, y, x, ret, item + + -- Can we reconnect ? + if do_control_reconnect() == TRUE then + msg_print("Control re-established.") + return + end + + ret, item = get_item("Which light source do you want to use to create the golem?", + "You have no light source for the golem", + bor(USE_INVEN, USE_EQUIP), + function (obj) + if (obj.tval == TV_LITE) and ((obj.sval == SV_LITE_TORCH) or (obj.sval == SV_LITE_LANTERN)) then + return TRUE + end + return FALSE + end + ) + if ret == FALSE then return TRUE end + inven_item_increase(item, -1) + inven_item_describe(item) + inven_item_optimize(item) + + -- Summon it + m_allow_special[1043 + 1] = TRUE + y, x = find_position(player.py, player.px) + m_idx = place_monster_one(y, x, 1043, 0, FALSE, MSTATUS_FRIEND) + m_allow_special[1043 + 1] = FALSE + + -- level it + if m_idx ~= 0 then + monster_set_level(m_idx, 7 + get_level(FIREGOLEM, 70)) + player.control = m_idx + monster(m_idx).mflag = bor(monster(m_idx).mflag, MFLAG_CONTROL) + end + return TRUE + end, + ["info"] = function() + return "golem level "..(7 + get_level(FIREGOLEM, 70)) + end, + ["desc"] = { + "Creates a fiery golem and controls it", + "During the control the available keylist is:", + "Movement keys: movement of the golem(depending on its speed", + " it can move more than one square)", + ", : pickup all items on the floor", + "d : drop all carried items", + "i : list all carried items", + "m : end the possession/use golem powers", + "Most of the other keys are disabled, you cannot interact with your", + "real body while controlling the golem", + "But to cast the spell you will need a lantern or a wooden torch to", + "Create the golem from" + } +} diff --git a/lib/scpt/s_geom.lua b/lib/scpt/s_geom.lua new file mode 100644 index 00000000..b9730318 --- /dev/null +++ b/lib/scpt/s_geom.lua @@ -0,0 +1,656 @@ +-- Geomancy school + +function geomancy_random_wall(y, x) + local c_ptr = cave(y, x) + + -- Do not destroy permanent things + if cave_is(c_ptr, FF1_PERMANENT) ~= FALSE then return end + + local feat = nil + local table = + { + [1] = { SKILL_FIRE, FEAT_SANDWALL, 1}, + + [2] = { SKILL_WATER, FEAT_TREES, 1}, + [3] = { SKILL_WATER, FEAT_ICE_WALL, 12}, + + [4] = { SKILL_EARTH, FEAT_WALL_EXTRA, 1}, + } + + while feat == nil do + local t = table[randint(getn(table))] + + -- Do we meet the requirements ? + -- And then select the features based on skill proportions + if get_skill(t[1]) >= t[3] and magik(get_skill_scale(t[1], 100)) == TRUE then + feat = t[2] + end + end + + cave_set_feat(y, x, feat) +end + + +GF_ELEMENTAL_WALL = add_spell_type +{ + ["color"] = { TERM_GREEN, 0 }, + ["angry"] = function() return TRUE, FALSE end, + ["grid"] = function(who, dam, rad, y, x) + if player.py ~= y or player.px ~= x then + geomancy_random_wall(y, x) + end + end, +} + +function geomancy_random_floor(y, x, kill_wall) + local c_ptr = cave(y, x) + + -- Do not destroy permanent things + if cave_is(c_ptr, FF1_PERMANENT) ~= FALSE then return end + if not kill_wall then + if cave_is(c_ptr, FF1_FLOOR) ~= TRUE then return end + end + + local feat = nil + local table = + { + [1] = { SKILL_FIRE, FEAT_SAND, 1}, + [2] = { SKILL_FIRE, FEAT_SHAL_LAVA, 8}, + [3] = { SKILL_FIRE, FEAT_DEEP_LAVA, 18}, + + [4] = { SKILL_WATER, FEAT_SHAL_WATER, 1}, + [5] = { SKILL_WATER, FEAT_DEEP_WATER, 8}, + [6] = { SKILL_WATER, FEAT_ICE, 18}, + + [7] = { SKILL_EARTH, FEAT_GRASS, 1}, + [8] = { SKILL_EARTH, FEAT_FLOWER, 8}, + [9] = { SKILL_EARTH, FEAT_DARK_PIT, 18}, + } + + while feat == nil do + local t = table[randint(getn(table))] + + -- Do we meet the requirements ? + -- And then select the features based on skill proportions + if get_skill(t[1]) >= t[3] and magik(get_skill_scale(t[1], 100)) == TRUE then + feat = t[2] + end + end + + cave_set_feat(y, x, feat) +end + + +GF_ELEMENTAL_GROWTH = add_spell_type +{ + ["color"] = { TERM_GREEN, 0 }, + ["angry"] = function() return TRUE, FALSE end, + ["grid"] = function(who, dam, rad, y, x) + geomancy_random_floor(y, x) + end, +} + +CALL_THE_ELEMENTS = add_spell +{ + ["name"] = "Call the Elements", + ["school"] = {SCHOOL_GEOMANCY}, + ["level"] = 1, + ["mana"] = 2, + ["mana_max"] = 20, + ["fail"] = 10, + -- Unnafected by blindness + ["blind"] = FALSE, + ["random"] = 0, + ["spell"] = function() + local ret, dir = 0, 0 + + if get_level(CALL_THE_ELEMENTS) >= 17 then + ret, dir = get_aim_dir() + if ret == FALSE then return end + end + + fire_ball(GF_ELEMENTAL_GROWTH, dir, 1, 1 + get_level(CALL_THE_ELEMENTS, 5, 0)) + return TRUE + end, + ["info"] = function() + return "rad "..(1 + get_level(CALL_THE_ELEMENTS, 5, 0)) + end, + ["desc"] = { + "Randomly creates various elements around you", + "Each type of element chance is controlled by your level", + "in the corresponding skill", + "At level 17 it can be targeted", + } +} + +-- Seperate function because an other spell needs it +function channel_the_elements(y, x, level, silent) + local t = + { + -- Earth stuff + [FEAT_GRASS] = function() + hp_player(player.mhp * (5 + get_skill_scale(SKILL_EARTH, 20)) / 100) + end, + [FEAT_FLOWER] = function() + hp_player(player.mhp * (5 + get_skill_scale(SKILL_EARTH, 30)) / 100) + end, + [FEAT_DARK_PIT] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + local type = GF_DARK + if get_skill(SKILL_EARTH) >= 18 then type = GF_NETHER end + fire_bolt(type, dir, damroll(10, get_skill(SKILL_EARTH))) + end, + + -- Water stuff + [FEAT_SHAL_WATER] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + local type = GF_WATER + if get_skill(SKILL_WATER) >= 18 then type = GF_WAVE end + + if get_skill(SKILL_WATER) >= 8 then + fire_beam(type, dir, damroll(3, get_skill(SKILL_WATER))) + else + fire_bolt(type, dir, damroll(3, get_skill(SKILL_WATER))) + end + end, + [FEAT_DEEP_WATER] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + local type = GF_WATER + if get_skill(SKILL_WATER) >= 18 then type = GF_WAVE end + + if get_skill(SKILL_WATER) >= 8 then + fire_beam(type, dir, damroll(5, get_skill(SKILL_WATER))) + else + fire_bolt(type, dir, damroll(5, get_skill(SKILL_WATER))) + end + end, + [FEAT_ICE] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + if get_skill(SKILL_WATER) >= 12 then + fire_ball(GF_ICE, dir, get_skill_scale(SKILL_WATER, 340), 3) + else + fire_bolt(GF_ICE, dir, damroll(3, get_skill(SKILL_WATER))) + end + end, + + -- Fire stuff + [FEAT_SAND] = function() + local type + if (get_level(FIERYAURA, 50) >= 8) then + type = SHIELD_GREAT_FIRE + else + type = SHIELD_FIRE + end + local dur = randint(20) + %level + get_skill(SKILL_AIR) + set_shield(dur, 0, type, 5 + get_skill_scale(SKILL_FIRE, 20), 5 + get_skill_scale(SKILL_FIRE, 14)) + set_blind(dur) + end, + [FEAT_SHAL_LAVA] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + if get_skill(SKILL_FIRE) >= 15 then + fire_bolt(GF_HELL_FIRE, dir, damroll(get_skill_scale(SKILL_FIRE, 30), 15)) + else + fire_bolt(GF_FIRE, dir, damroll(get_skill_scale(SKILL_FIRE, 30), 15)) + end + end, + [FEAT_DEEP_LAVA] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + if get_skill(SKILL_FIRE) >= 15 then + fire_ball(GF_HELL_FIRE, dir, damroll(get_skill_scale(SKILL_FIRE, 30), 15), 3) + else + fire_ball(GF_FIRE, dir, damroll(get_skill_scale(SKILL_FIRE, 30), 15), 3) + end + end, + } + + if t[cave(y, x).feat] then + t[cave(y, x).feat]() + + if magik(100 - level) == TRUE then + if cave(y, x).feat == FEAT_FLOWER then + cave_set_feat(y, x, FEAT_GRASS) + else + cave_set_feat(y, x, FEAT_FLOOR) + end + msg_print("The area is drained.") + end + elseif not silent then + msg_print("You cannot channel this area.") + end +end + +CHANNEL_ELEMENTS = add_spell +{ + ["name"] = "Channel Elements", + ["school"] = {SCHOOL_GEOMANCY}, + ["level"] = 3, + ["mana"] = 3, + ["mana_max"] = 30, + ["fail"] = 20, + -- Unnafected by blindness + ["blind"] = FALSE, + ["random"] = 0, + ["spell"] = function() + channel_the_elements(player.py, player.px, get_level(CHANNEL_ELEMENTS), nil) + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Draws on the caster's immediate environs to form an attack or other effect.", + "Grass/Flower heals.", + "Water creates water bolt attacks.", + "Ice creates ice bolt attacks.", + "Sand creates a wall of thick, blinding, burning sand around you.", + "Lava creates fire bolt attacks.", + "Deep lava creates fire ball attacks.", + "Chasm creates darkness bolt attacks.", + "At Earth level 18, darkness becomes nether.", + "At Water level 8, water attacks become beams with a striking effect.", + "At Water level 12, ice attacks become balls of ice shards.", + "At Water level 18, water attacks push monsters back.", + "At Fire level 15, fire become hellfire.", + } +} + +ELEMENTAL_WAVE = add_spell +{ + ["name"] = "Elemental Wave", + ["school"] = {SCHOOL_GEOMANCY}, + ["level"] = 15, + ["mana"] = 15, + ["mana_max"] = 50, + ["fail"] = 20, + -- Unnafected by blindness + ["blind"] = FALSE, + ["random"] = 0, + ["spell"] = function() + local ret, dir = get_rep_dir() + if ret == FALSE then return end + + local y, x = explode_dir(dir) + y = y + player.py + x = x + player.px + + local t = + { + -- Earth stuff + [FEAT_GRASS] = { GF_POIS, GF_POIS, 10 + get_skill_scale(SKILL_EARTH, 200) }, + [FEAT_FLOWER] = { GF_POIS, GF_POIS, 10 + get_skill_scale(SKILL_EARTH, 300) }, + -- cannot turn chasm into a wave + + -- Water stuff + [FEAT_SHAL_WATER] = { GF_WATER, GF_WATER, 10 + get_skill_scale(SKILL_WATER, 200) }, + [FEAT_DEEP_WATER] = { GF_WATER, GF_WATER, 10 + get_skill_scale(SKILL_WATER, 300) }, + [FEAT_ICE] = { GF_ICE, GF_ICE, 10 + get_skill_scale(SKILL_WATER, 200) }, + + -- Fire stuff + [FEAT_SAND] = { GF_LITE, GF_LITE, 10 + get_skill_scale(SKILL_FIRE, 400) }, + [FEAT_SHAL_LAVA] = { GF_FIRE, GF_HOLY_FIRE, 10 + get_skill_scale(SKILL_FIRE, 200) }, + [FEAT_DEEP_LAVA] = { GF_FIRE, GF_HOLY_FIRE, 10 + get_skill_scale(SKILL_FIRE, 300) }, + } + + + local effect = t[cave(y, x).feat] + if not effect then + msg_print("You cannot channel this area.") + else + local typ = effect[1] + if get_level(ELEMENTAL_WAVE) >= 20 then typ = effect[2] end + + cave_set_feat(y, x, FEAT_FLOOR) + + fire_wave(typ, 0, effect[3], 0, 6 + get_level(ELEMENTAL_WAVE, 20), EFF_WAVE + EFF_LAST + getglobal("EFF_DIR"..dir)) + end + + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Draws on an adjacent special square to project a slow-moving", + "wave of that element in that direction", + "Abyss squares cannot be channeled into a wave.", + } +} + +VAPORIZE = add_spell +{ + ["name"] = "Vaporize", + ["school"] = {SCHOOL_GEOMANCY}, + ["level"] = 4, + ["mana"] = 3, + ["mana_max"] = 30, + ["fail"] = 15, + -- Unnafected by blindness + ["blind"] = FALSE, + -- Must have at least 4 Air + ["random"] = 0, + ["depend"] = function() + if get_skill(SKILL_AIR) >= 4 then return TRUE end + end, + ["spell"] = function() + local t = + { + -- Earth stuff + [FEAT_GRASS] = { GF_POIS, GF_POIS, 5 + get_skill_scale(SKILL_EARTH, 100) }, + [FEAT_FLOWER] = { GF_POIS, GF_POIS, 5 + get_skill_scale(SKILL_EARTH, 150) }, + [FEAT_DARK_PIT] = { GF_DARK, GF_DARK, 5 + get_skill_scale(SKILL_EARTH, 200) }, + + -- Water stuff + [FEAT_SHAL_WATER] = { GF_WATER, GF_WATER, 5 + get_skill_scale(SKILL_WATER, 100) }, + [FEAT_DEEP_WATER] = { GF_WATER, GF_WATER, 5 + get_skill_scale(SKILL_WATER, 150) }, + [FEAT_ICE] = { GF_ICE, GF_ICE, 5 + get_skill_scale(SKILL_WATER, 100) }, + + -- Fire stuff + [FEAT_SAND] = { GF_LITE, GF_LITE, 5 + get_skill_scale(SKILL_FIRE, 200) }, + [FEAT_SHAL_LAVA] = { GF_FIRE, GF_HOLY_FIRE, 5 + get_skill_scale(SKILL_FIRE, 100) }, + [FEAT_DEEP_LAVA] = { GF_FIRE, GF_HOLY_FIRE, 5 + get_skill_scale(SKILL_FIRE, 150) }, + } + + local effect = t[cave(player.py, player.px).feat] + if not effect then + msg_print("You cannot channel this area.") + else + local typ = effect[1] + if get_level(VAPORIZE) >= 20 then typ = effect[2] end + + cave_set_feat(player.py, player.px, FEAT_FLOOR) + + fire_cloud(typ, 0, effect[3], 1 + get_level(VAPORIZE, 4), 10 + get_level(VAPORIZE, 20)) + end + + return TRUE + end, + ["info"] = function() + return "rad "..(1 + get_level(VAPORIZE, 4)).." dur "..(10 + get_level(VAPORIZE, 20)) + end, + ["desc"] = { + "Draws upon your immediate environs to form a cloud of damaging vapors", + } +} + +geomancy_can_tunnel = +{ + [FEAT_WALL_EXTRA] = TRUE, + [FEAT_WALL_OUTER] = TRUE, + [FEAT_WALL_INNER] = TRUE, + [FEAT_WALL_SOLID] = TRUE, + + [FEAT_MAGMA] = TRUE, + [FEAT_QUARTZ] = TRUE, + [FEAT_MAGMA_H] = TRUE, + [FEAT_QUARTZ_H] = TRUE, + [FEAT_MAGMA_K] = TRUE, + [FEAT_QUARTZ_K] = TRUE, + + [FEAT_TREES] = TRUE, + [FEAT_DEAD_TREE] = TRUE, + + [FEAT_SANDWALL] = TRUE, + [FEAT_SANDWALL_H] = TRUE, + [FEAT_SANDWALL_K] = TRUE, + + [FEAT_ICE_WALL] = TRUE, +} + +-- Dig & sprew +function geomancy_dig(oy, ox, dir, length) + local dy, dx = explode_dir(dir) + local y = dy + oy + local x = dx + ox + + for i = 1, length do + local c_ptr = cave(y, x) + local ox = x - dx + local oy = y - dy + + -- stop at the end of tunnelable things + if not geomancy_can_tunnel[c_ptr.feat] then break end + + if geomancy_can_tunnel[cave(y - 1, x - 1).feat] then geomancy_random_wall(y - 1, x - 1) end + if geomancy_can_tunnel[cave(y - 1, x).feat] then geomancy_random_wall(y - 1, x) end + if geomancy_can_tunnel[cave(y - 1, x + 1).feat] then geomancy_random_wall(y - 1, x + 1) end + + if geomancy_can_tunnel[cave(y, x - 1).feat] then geomancy_random_wall(y, x - 1) end + if geomancy_can_tunnel[cave(y, x + 1).feat] then geomancy_random_wall(y, x + 1) end + + if geomancy_can_tunnel[cave(y + 1, x - 1).feat] then geomancy_random_wall(y + 1, x - 1) end + if geomancy_can_tunnel[cave(y + 1, x).feat] then geomancy_random_wall(y + 1, x) end + if geomancy_can_tunnel[cave(y + 1, x + 1).feat] then geomancy_random_wall(y + 1, x + 1) end + + y = y + dy + x = x + dx + end + + y = y - dy + x = x - dx + while (y ~= oy) or (x ~= ox) do + geomancy_random_floor(y, x, TRUE) + + -- Should we branch ? + if magik(20) == TRUE then + local rot = 1 + if magik(50) == TRUE then rot = -1 end + geomancy_dig(y, x, rotate_dir(dir, rot), length / 3) + end + + y = y - dy + x = x - dx + end +end + +GEOLYSIS = add_spell +{ + ["name"] = "Geolysis", + ["school"] = {SCHOOL_GEOMANCY}, + ["level"] = 7, + ["mana"] = 15, + ["mana_max"] = 40, + ["fail"] = 15, + -- Unnafected by blindness + ["blind"] = FALSE, + ["random"] = 0, + -- Must have at least 7 Earth + ["depend"] = function() + if get_skill(SKILL_EARTH) >= 7 then return TRUE end + end, + ["spell"] = function() + local ret, dir = get_rep_dir() + if ret == FALSE then return end + + msg_print("Elements recombine before you, laying down an open path.") + geomancy_dig(player.py, player.px, dir, 5 + get_level(GEOLYSIS, 12)) + + return TRUE + end, + ["info"] = function() + return "length "..(5 + get_level(GEOLYSIS, 12)) + end, + ["desc"] = { + "Burrows deeply and slightly at random into a wall,", + "leaving behind tailings of various different sorts of walls in the passage.", + } +} + +player.dripping_tread = 0 +add_loadsave("player.dripping_tread", 0) +add_hooks +{ + [HOOK_MOVED] = function(oy, ox) + if player.dripping_tread > 0 then + geomancy_random_floor(oy, ox) + player.dripping_tread = player.dripping_tread - 1 + if player.dripping_tread == 0 then + msg_print("You stop dripping raw elemental energies.") + end + end + end +} +DRIPPING_TREAD = add_spell +{ + ["name"] = "Dripping Tread", + ["school"] = {SCHOOL_GEOMANCY}, + ["level"] = 10, + ["mana"] = 15, + ["mana_max"] = 25, + ["fail"] = 15, + -- Unnafected by blindness + ["blind"] = FALSE, + ["random"] = 0, + -- Must have at least 10 Water + ["depend"] = function() + if get_skill(SKILL_WATER) >= 10 then return TRUE end + end, + ["spell"] = function() + if player.dripping_tread == 0 then + player.dripping_tread = randint(15) + 10 + get_level(DRIPPING_TREAD) + msg_print("You start dripping raw elemental energies.") + else + player.dripping_tread = 0 + msg_print("You stop dripping raw elemental energies.") + end + return TRUE + end, + ["info"] = function() + return "dur "..(10 + get_level(DRIPPING_TREAD)).."+d15 movs" + end, + ["desc"] = { + "Causes you to leave random elemental forms behind as you walk", + } +} + +GROW_BARRIER = add_spell +{ + ["name"] = "Grow Barrier", + ["school"] = {SCHOOL_GEOMANCY}, + ["level"] = 12, + ["mana"] = 30, + ["mana_max"] = 40, + ["fail"] = 15, + -- Unnafected by blindness + ["blind"] = FALSE, + ["random"] = 0, + -- Must have at least 12 Earth + ["depend"] = function() + if get_skill(SKILL_EARTH) >= 12 then return TRUE end + end, + ["spell"] = function() + local ret, dir = 0, 0 + + if get_level(GROW_BARRIER) >= 20 then + ret, dir = get_aim_dir() + if ret == FALSE then return end + end + + fire_ball(GF_ELEMENTAL_WALL, dir, 1, 1) + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Creates impassable terrain (walls, trees, etc.) around you.", + "At level 20 it can be projected around another area.", + } +} + +ELEMENTAL_MINION = add_spell +{ + ["name"] = "Elemental Minion", + ["school"] = {SCHOOL_GEOMANCY}, + ["level"] = 20, + ["mana"] = 40, + ["mana_max"] = 80, + ["fail"] = 25, + -- Unnafected by blindness + ["random"] = 0, + -- Must have at least 12 Earth + ["spell"] = function() + local ret, dir = 0, 0 + + ret, dir = get_rep_dir() + if ret == FALSE then return end + + local t = + { + [FEAT_WALL_EXTRA] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } }, + [FEAT_WALL_OUTER] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } }, + [FEAT_WALL_INNER] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } }, + [FEAT_WALL_SOLID] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } }, + [FEAT_MAGMA] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } }, + [FEAT_QUARTZ] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } }, + [FEAT_MAGMA_H] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } }, + [FEAT_QUARTZ_H] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } }, + [FEAT_MAGMA_K] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } }, + [FEAT_QUARTZ_K] = { SKILL_EARTH, { "Earth elemental", "Xorn", "Xaren" } }, + + [FEAT_DARK_PIT] = { SKILL_AIR, { "Air elemental", "Ancient blue dragon", "Great Storm Wyrm", "Sky Drake" } }, + + [FEAT_SANDWALL] = { SKILL_FIRE, { "Fire elemental", "Ancient red dragon" } }, + [FEAT_SANDWALL_H] = { SKILL_FIRE, { "Fire elemental", "Ancient red dragon" } }, + [FEAT_SANDWALL_K] = { SKILL_FIRE, { "Fire elemental", "Ancient red dragon" } }, + [FEAT_SHAL_LAVA] = { SKILL_FIRE, { "Fire elemental", "Ancient red dragon" } }, + [FEAT_DEEP_LAVA] = { SKILL_FIRE, { "Fire elemental", "Ancient red dragon" } }, + + [FEAT_ICE_WALL] = { SKILL_WATER, { "Water elemental", "Water troll", "Water demon" } }, + [FEAT_SHAL_WATER] = { SKILL_WATER, { "Water elemental", "Water troll", "Water demon" } }, + [FEAT_DEEP_WATER] = { SKILL_WATER, { "Water elemental", "Water troll", "Water demon" } }, + } + + local y, x = explode_dir(dir) + y = y + player.py + x = x + player.px + + local effect = t[cave(y, x).feat] + if not effect then + msg_print("You cannot summon from this area.") + else + local skill = effect[1] + local types = effect[2] + + local max = get_skill_scale(skill, getn(types)) + if max == 0 then max = 1 end + + local r_idx = test_monster_name(types[rand_range(1, max)]) + + -- Summon it + local my, mx = find_position(y, x) + local m_idx = place_monster_one(my, mx, r_idx, 0, FALSE, MSTATUS_FRIEND) + + -- level it + if m_idx ~= 0 then + monster_set_level(m_idx, 10 + get_level(ELEMENTAL_MINION, 120)) + end + + cave_set_feat(y, x, FEAT_FLOOR) + end + + return TRUE + end, + ["info"] = function() + return "min level "..(10 + get_level(ELEMENTAL_MINION, 120)) + end, + ["desc"] = { + "Summons a minion from a nearby element.", + "Walls can summon Earth elmentals, Xorns and Xarens", + "Dark Pits can summon Air elementals, Ancient blue dragons, Great Storm Wyrms", + "and Sky Drakes", + "Sandwalls and lava can summon Fire elementals and Ancient red dragons", + "Icewall, and water can summon Water elementals, Water trolls and Water demons", + } +} diff --git a/lib/scpt/s_mana.lua b/lib/scpt/s_mana.lua new file mode 100644 index 00000000..736b06b0 --- /dev/null +++ b/lib/scpt/s_mana.lua @@ -0,0 +1,132 @@ +-- The mana school + +function get_manathrust_dam() + return 3 + get_level(MANATHRUST, 50), 1 + get_level(MANATHRUST, 20) +end + +MANATHRUST = add_spell +{ + ["name"] = "Manathrust", + ["school"] = SCHOOL_MANA, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 25, + ["fail"] = 10, + ["stick"] = + { + ["charge"] = { 7, 10 }, + [TV_WAND] = + { + ["rarity"] = 5, + ["base_level"] = { 1, 20 }, + ["max_level"] = { 15, 33 }, + }, + }, + ["spell"] = function() + local ret, dir + + ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_bolt(GF_MANA, dir, damroll(get_manathrust_dam())) + end, + ["info"] = function() + local x, y + + x, y = get_manathrust_dam() + return "dam "..x.."d"..y + end, + ["desc"] = { + "Conjures up mana into a powerful bolt", + "The damage is irresistible and will increase with level" + } +} + +DELCURSES = add_spell +{ + ["name"] = "Remove Curses", + ["school"] = SCHOOL_MANA, + ["level"] = 10, + ["mana"] = 20, + ["mana_max"] = 40, + ["fail"] = 30, + ["stick"] = + { + ["charge"] = { 3, 8 }, + [TV_STAFF] = + { + ["rarity"] = 70, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 15, 50 }, + }, + }, + ["inertia"] = { 1, 10 }, + ["spell"] = function() + local done + + if get_level(DELCURSES, 50) >= 20 then done = remove_all_curse() + else done = remove_curse() end + if done == TRUE then msg_print("The curse is broken!") end + return done + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Remove curses of worn objects", + "At level 20 switches to *remove curses*" + } +} + +RESISTS = add_spell +{ + ["name"] = "Elemental Shield", + ["school"] = SCHOOL_MANA, + ["level"] = 20, + ["mana"] = 17, + ["mana_max"] = 20, + ["fail"] = 40, + ["inertia"] = { 2, 25 }, + ["spell"] = function() + local obvious + if player.oppose_fire == 0 then obvious = set_oppose_fire(randint(10) + 15 + get_level(RESISTS, 50)) end + if player.oppose_cold == 0 then obvious = is_obvious(set_oppose_cold(randint(10) + 15 + get_level(RESISTS, 50)), obvious) end + if player.oppose_elec == 0 then obvious = is_obvious(set_oppose_elec(randint(10) + 15 + get_level(RESISTS, 50)), obvious) end + if player.oppose_acid == 0 then obvious = is_obvious(set_oppose_acid(randint(10) + 15 + get_level(RESISTS, 50)), obvious) end + return obvious + end, + ["info"] = function() + return "dur "..(15 + get_level(RESISTS, 50)).."+d10" + end, + ["desc"] = { + "Provide resistances to the four basic elements", + } +} + +MANASHIELD = add_spell +{ + ["name"] = "Disruption Shield", + ["school"] = SCHOOL_MANA, + ["level"] = 45, + ["mana"] = 50, + ["mana_max"] = 50, + ["fail"] = 90, + ["inertia"] = { 9, 10}, + ["spell"] = function() + if get_level(MANASHIELD, 50) >= 5 then + if (player.invuln == 0) then + return set_invuln(randint(5) + 3 + get_level(MANASHIELD, 10)) + end + else + if (player.disrupt_shield == 0) then return set_disrupt_shield(randint(5) + 3 + get_level(MANASHIELD, 10)) end + end + end, + ["info"] = function() + return "dur "..(3 + get_level(MANASHIELD, 10)).."+d5" + end, + ["desc"] = { + "Uses mana instead of hp to take damage", + "At level 5 switches to Globe of Invulnerability.", + "The spell breaks as soon as a melee, shooting, throwing or magical", + "skill action is attempted, and lasts only a short time." + } +} diff --git a/lib/scpt/s_manwe.lua b/lib/scpt/s_manwe.lua new file mode 100644 index 00000000..6f0f9661 --- /dev/null +++ b/lib/scpt/s_manwe.lua @@ -0,0 +1,144 @@ +-- Handle Manwe Sulimo magic school + +MANWE_SHIELD = add_spell +{ + ["name"] = "Wind Shield", + ["school"] = {SCHOOL_MANWE}, + ["level"] = 10, + ["mana"] = 100, + ["mana_max"] = 500, + ["fail"] = 30, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local dur = get_level(MANWE_SHIELD, 50) + 10 + randint(20) + local obvious + + obvious = set_protevil(dur) + if get_level(MANWE_SHIELD) >= 10 then + local type + + type = 0 + if get_level(MANWE_SHIELD) >= 20 then + type = SHIELD_COUNTER + end + obvious = is_obvious(set_shield(dur, get_level(MANWE_SHIELD, 30), type, 1 + get_level(MANWE_SHIELD, 2), 1 + get_level(MANWE_SHIELD, 6)), obvious) + end + return obvious + end, + ["info"] = function() + local desc = "dur "..(get_level(MANWE_SHIELD, 50) + 10).."+d20" + + if get_level(MANWE_SHIELD) >= 10 then + desc = desc.." AC "..(get_level(MANWE_SHIELD, 30)) + end + if get_level(MANWE_SHIELD) >= 20 then + desc = desc.." dam "..(1 + get_level(MANWE_SHIELD, 2)).."d"..(1 + get_level(MANWE_SHIELD, 6)) + end + return desc + end, + ["desc"] = { + "It surrounds you with a shield of wind that deflects blows from evil monsters", + "At level 10 it increases your armour rating", + "At level 20 it retaliates against monsters that melee you", + } +} + +MANWE_AVATAR = add_spell +{ + ["name"] = "Avatar", + ["school"] = {SCHOOL_MANWE}, + ["level"] = 35, + ["mana"] = 1000, + ["mana_max"] = 1000, + ["fail"] = 80, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + return set_mimic(get_level(MANWE_AVATAR, 20) + randint(10), resolve_mimic_name("Maia"), player.lev) + end, + ["info"] = function() + return "dur "..(get_level(MANWE_AVATAR, 20)).."+d10" + end, + ["desc"] = { + "It turns you into a full grown Maia", + } +} + +MANWE_BLESS = add_spell +{ + ["name"] = "Manwe's Blessing", + ["school"] = {SCHOOL_MANWE}, + ["level"] = 1, + ["mana"] = 10, + ["mana_max"] = 100, + ["fail"] = 20, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local dur = get_level(MANWE_BLESS, 70) + 30 + randint(40) + local obvious + + obvious = set_blessed(dur) + obvious = is_obvious(set_afraid(0), obvious) + obvious = is_obvious(set_lite(0), obvious) + if get_level(MANWE_BLESS) >= 10 then + obvious = is_obvious(set_hero(dur), obvious) + end + if get_level(MANWE_BLESS) >= 20 then + obvious = is_obvious(set_shero(dur), obvious) + end + if get_level(MANWE_BLESS) >= 30 then + obvious = is_obvious(set_holy(dur), obvious) + end + return obvious + end, + ["info"] = function() + return "dur "..(get_level(MANWE_BLESS, 70) + 30).."+d40" + end, + ["desc"] = { + "Manwe's Blessing removes your fears, blesses you and surrounds you with", + "holy light", + "At level 10 it also grants heroism", + "At level 20 it also grants super heroism", + "At level 30 it also grants holy luck and life protection", + } +} + +MANWE_CALL = add_spell +{ + ["name"] = "Manwe's Call", + ["school"] = {SCHOOL_MANWE}, + ["level"] = 20, + ["mana"] = 200, + ["mana_max"] = 500, + ["fail"] = 40, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local y, x, m_idx + + y, x = find_position(player.py, player.px) + m_idx = place_monster_one(y, x, test_monster_name("Great eagle"), 0, FALSE, MSTATUS_FRIEND) + + if m_idx ~= 0 then + monster_set_level(m_idx, 20 + get_level(MANWE_CALL, 70, 0)) + return TRUE + end + end, + ["info"] = function() + return "level "..(get_level(MANWE_CALL, 70) + 20) + end, + ["desc"] = { + "Manwe's Call summons a Great Eagle to help you battle the forces", + "of Morgoth" + } +} diff --git a/lib/scpt/s_melkor.lua b/lib/scpt/s_melkor.lua new file mode 100644 index 00000000..b2c693dd --- /dev/null +++ b/lib/scpt/s_melkor.lua @@ -0,0 +1,154 @@ +-- handle the melkor school + +-- Not included in the spell code directly because I need to call it from somewhere else too +function do_melkor_curse(who) + local m_ptr = monster(who) + + if get_level(MELKOR_CURSE) >= 35 then + local r_ptr = race_info_idx(m_ptr.r_idx, m_ptr.ego) + + m_ptr.maxhp = m_ptr.maxhp - r_ptr.hside; + if m_ptr.maxhp < 1 then m_ptr.maxhp = 1 end + if m_ptr.hp > m_ptr.maxhp then m_ptr.hp = m_ptr.maxhp end + player.redraw = bor(player.redraw, PR_HEALTH) + end + if get_level(MELKOR_CURSE) >= 25 then + m_ptr.speed = m_ptr.speed - get_level(MELKOR_CURSE, 7) + m_ptr.mspeed = m_ptr.mspeed - get_level(MELKOR_CURSE, 7) + + if m_ptr.speed < 70 then m_ptr.speed = 70 end + if m_ptr.mspeed < 70 then m_ptr.mspeed = 70 end + end + if get_level(MELKOR_CURSE) >= 15 then + m_ptr.ac = m_ptr.ac - get_level(MELKOR_CURSE, 50) + + if m_ptr.ac < -70 then m_ptr.ac = -70 end + end + + local i, pow + i = 1 + pow = get_level(MELKOR_CURSE, 2) + while (i <= 4) do + if m_ptr.blow[i].d_dice > 0 then + if m_ptr.blow[i].d_dice < pow then + pow = m_ptr.blow[i].d_dice + end + if m_ptr.blow[i].d_side < pow then + pow = m_ptr.blow[i].d_side + end + m_ptr.blow[i].d_dice = m_ptr.blow[i].d_dice - pow + end + i = i + 1 + end + + local m_name = monster_desc(m_ptr, 0).." looks weaker." + msg_print(strupper(strsub(m_name, 0, 1))..strsub(m_name, 2)) + + -- wake it + m_ptr.csleep = 0; +end + +MELKOR_CURSE = add_spell +{ + ["name"] = "Curse", + ["school"] = {SCHOOL_MELKOR}, + ["level"] = 1, + ["mana"] = 50, + ["mana_max"] = 300, + ["fail"] = 20, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + if target_who == -1 then + msg_print("You must target a monster.") + else + do_melkor_curse(target_who) + end + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "It curses a monster, reducing its melee power", + "At level 5 it can be auto-casted (with no piety cost) while fighting", + "At level 15 it also reduces armor", + "At level 25 it also reduces speed", + "At level 35 it also reduces max life (but it is never fatal)", + } +} + +MELKOR_CORPSE_EXPLOSION = add_spell +{ + ["name"] = "Corpse Explosion", + ["school"] = {SCHOOL_MELKOR}, + ["level"] = 10, + ["mana"] = 100, + ["mana_max"] = 500, + ["fail"] = 45, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + return fire_ball(GF_CORPSE_EXPL, 0, 20 + get_level(MELKOR_CORPSE_EXPLOSION, 70), 2 + get_level(MELKOR_CORPSE_EXPLOSION, 5)) + end, + ["info"] = function() + return "dam "..(20 + get_level(MELKOR_CORPSE_EXPLOSION, 70)).."%" + end, + ["desc"] = { + "It makes corpses in an area around you explode for a percent of their", + "hit points as damage", + } +} + +MELKOR_MIND_STEAL = add_spell +{ + ["name"] = "Mind Steal", + ["school"] = {SCHOOL_MELKOR}, + ["level"] = 20, + ["mana"] = 1000, + ["mana_max"] = 3000, + ["fail"] = 90, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + if target_who == -1 then + msg_print("You must target a monster.") + else + local chance, m_ptr, r_ptr + + m_ptr = monster(target_who) + r_ptr = race_info_idx(m_ptr.r_idx, m_ptr.ego) + chance = get_level(MELKOR_MIND_STEAL) + if (randint(m_ptr.level) < chance) and (band(r_ptr.flags1, RF1_UNIQUE) == 0) then + player.control = target_who + m_ptr.mflag = bor(m_ptr.mflag, MFLAG_CONTROL) + + local m_name = monster_desc(m_ptr, 0).." falls under your control." + msg_print(strupper(strsub(m_name, 0, 1))..strsub(m_name, 2)) + else + local m_name = monster_desc(m_ptr, 0).." resists." + msg_print(strupper(strsub(m_name, 0, 1))..strsub(m_name, 2)) + end + return TRUE + end + end, + ["info"] = function() + return "chance 1d(mlvl)<"..(get_level(MELKOR_MIND_STEAL)) + end, + ["desc"] = { + "It allows your spirit to temporarily leave your own body, which will", + "be vulnerable, to control one of your enemies body." + } +} diff --git a/lib/scpt/s_meta.lua b/lib/scpt/s_meta.lua new file mode 100644 index 00000000..eab691d8 --- /dev/null +++ b/lib/scpt/s_meta.lua @@ -0,0 +1,287 @@ +-- handle the meta school + +RECHARGE = add_spell +{ + ["name"] = "Recharge", + ["school"] = {SCHOOL_META}, + ["level"] = 5, + ["mana"] = 10, + ["mana_max"] = 100, + ["fail"] = 20, + ["spell"] = function() + return recharge(60 + get_level(RECHARGE, 140)) + end, + ["info"] = function() + return "power "..(60 + get_level(RECHARGE, 140)) + end, + ["desc"] = { + "Taps on the ambient mana to recharge an object's power (charges or mana)", + } +} + +function get_spellbinder_max() + local i + + i = get_level(SPELLBINDER, 4) + if i > 4 then i = 4 end + return i +end +--' +SPELLBINDER = add_spell +{ + ["name"] = "Spellbinder", + ["school"] = {SCHOOL_META}, + ["level"] = 20, + ["mana"] = 100, + ["mana_max"] = 300, + ["fail"] = 85, + ["spell"] = function() + local i, ret, c + + if player.spellbinder_num ~= 0 then + local t = + { + [SPELLBINDER_HP75] = "75% HP", + [SPELLBINDER_HP50] = "50% HP", + [SPELLBINDER_HP25] = "25% HP", + } + msg_print("The spellbinder is already active.") + msg_print("It will trigger at "..t[player.spellbinder_trigger]..".") + msg_print("With the spells: ") + for i = 1, player.spellbinder_num do + msg_print(spell(player.spellbinder[i]).name) + end + return TRUE + end + + ret, c = get_com("Trigger at [a]75% hp [b]50% hp [c]25% hp?", strbyte("a")) + if ret == FALSE then return TRUE end + + if c == strbyte("a") then + player.spellbinder_trigger = SPELLBINDER_HP75 + elseif c == strbyte("b") then + player.spellbinder_trigger = SPELLBINDER_HP50 + elseif c == strbyte("c") then + player.spellbinder_trigger = SPELLBINDER_HP25 + else + return + end + player.spellbinder_num = get_spellbinder_max() + i = player.spellbinder_num + while i > 0 do + local s + + s = get_school_spell("bind", "is_ok_spell", 0) + if s == -1 then + player.spellbinder_trigger = 0 + player.spellbinder_num = 0 + return TRUE + else + if spell(s).skill_level > 7 + get_level(SPELLBINDER, 35) then + msg_print("You are only allowed spells with a base level of "..(7 + get_level(SPELLBINDER, 35)).."."); + return TRUE + end + end + player.spellbinder[i] = s + i = i - 1 + end + player.energy = player.energy - 3100; + msg_print("Spellbinder ready.") + return TRUE + end, + ["info"] = function() + return "number "..(get_spellbinder_max()).." max level "..(7 + get_level(SPELLBINDER, 35)) + end, + ["desc"] = { + "Stores spells in a trigger.", + "When the condition is met all spells fire off at the same time", + "This spell takes a long time to cast so you are advised to prepare it", + "in a safe area.", + "Also it will use the mana for the Spellbinder and the mana for the", + "selected spells" + } +} + +DISPERSEMAGIC = add_spell +{ + ["name"] = "Disperse Magic", + ["school"] = {SCHOOL_META}, + ["level"] = 15, + ["mana"] = 30, + ["mana_max"] = 60, + ["fail"] = 40, + -- Unnafected by blindness + ["blind"] = FALSE, + -- Unnafected by confusion + ["confusion"] = FALSE, + ["stick"] = + { + ["charge"] = { 5, 5 }, + [TV_WAND] = + { + ["rarity"] = 25, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 5, 40 }, + }, + }, + ["inertia"] = { 1, 5 }, + ["spell"] = function() + local obvious + obvious = set_blind(0) + obvious = is_obvious(set_lite(0), obvious) + if get_level(DISPERSEMAGIC, 50) >= 5 then + obvious = is_obvious(set_confused(0), obvious) + obvious = is_obvious(set_image(0), obvious) + end + if get_level(DISPERSEMAGIC, 50) >= 10 then + obvious = is_obvious(set_slow(0), obvious) + obvious = is_obvious(set_fast(0, 0), obvious) + obvious = is_obvious(set_light_speed(0), obvious) + end + if get_level(DISPERSEMAGIC, 50) >= 15 then + obvious = is_obvious(set_stun(0), obvious) + obvious = is_obvious(set_meditation(0), obvious) + obvious = is_obvious(set_cut(0), obvious) + end + if get_level(DISPERSEMAGIC, 50) >= 20 then + obvious = is_obvious(set_hero(0), obvious) + obvious = is_obvious(set_shero(0), obvious) + obvious = is_obvious(set_blessed(0), obvious) + obvious = is_obvious(set_shield(0, 0, 0, 0, 0), obvious) + obvious = is_obvious(set_afraid(0), obvious) + obvious = is_obvious(set_parasite(0, 0), obvious) + obvious = is_obvious(set_mimic(0, 0, 0), obvious) + end + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Dispels a lot of magic that can affect you, be it good or bad", + "Level 1: blindness and light", + "Level 5: confusion and hallucination", + "Level 10: speed (both bad or good) and light speed", + "Level 15: stunning, meditation, cuts", + "Level 20: hero, super hero, bless, shields, afraid, parasites, mimicry", + } +} + +TRACKER = add_spell +{ + ["name"] = "Tracker", + ["school"] = {SCHOOL_META, SCHOOL_CONVEYANCE}, + ["level"] = 30, + ["mana"] = 50, + ["mana_max"] = 50, + ["fail"] = 95, + ["spell"] = function() + if last_teleportation_y == -1 then + msg_print("There has not been any teleporatation here.") + return TRUE + end + teleport_player_to(last_teleportation_y, last_teleportation_x) + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Tracks down the last teleportation that happened on the level and teleports", + "you to it", + } +} + +-- Saves the values of the timer +save_timer("TIMER_INERTIA_CONTROL") +add_loadsave("player.inertia_controlled_spell", -1) +player.inertia_controlled_spell = -1 + +-- Automatically cast the inertia controlled spells +TIMER_INERTIA_CONTROL = new_timer +{ + ["enabled"] = FALSE, + ["delay"] = 10, + ["callback"] = function() + -- Don't cast a controlled spell in wilderness mode + if player.antimagic then + msg_print("Your anti-magic field disrupts any magic attempts.") + elseif player.anti_magic then + msg_print("Your anti-magic shell disrupts any magic attempts.") + elseif (player.inertia_controlled_spell ~= -1) and (player.wild_mode == FALSE) then + __spell_spell[player.inertia_controlled_spell]() + end + end, +} + +stop_inertia_controlled_spell = function() + player.inertia_controlled_spell = -1 + TIMER_INERTIA_CONTROL.enabled = FALSE + player.update = bor(player.update, PU_MANA) + return TRUE +end + +add_hooks +{ + -- Reduce the mana by four times the cost of the spell + [HOOK_CALC_MANA] = function(msp) + if player.inertia_controlled_spell ~= -1 then + msp = msp - (get_mana(player.inertia_controlled_spell) * 4) + if msp < 0 then msp = 0 end + return TRUE, msp + end + end, + + -- Stop a previous spell at birth + [HOOK_BIRTH_OBJECTS] = function() + stop_inertia_controlled_spell() + end, +} + +INERTIA_CONTROL = add_spell +{ + ["name"] = "Inertia Control", + ["school"] = {SCHOOL_META}, + ["level"] = 37, + ["mana"] = 300, + ["mana_max"] = 700, + ["fail"] = 95, + ["spell"] = function() + if player.inertia_controlled_spell ~= -1 then + msg_print("You cancel your inertia flow control.") + return stop_inertia_controlled_spell() + end + + local s = get_school_spell("control", "is_ok_spell", 0) + if s == -1 then + return stop_inertia_controlled_spell() + end + + local inertia = __tmp_spells[s].inertia + + if inertia == nil then + msg_print("This spell inertia flow can not be controlled.") + return stop_inertia_controlled_spell() + end + if inertia[1] > get_level(INERTIA_CONTROL, 10) then + msg_print("This spell inertia flow("..inertia[1]..") is too strong to be controlled by your current spell.") + return stop_inertia_controlled_spell() + end + + player.inertia_controlled_spell = s + TIMER_INERTIA_CONTROL.enabled = TRUE + TIMER_INERTIA_CONTROL.delay = inertia[2] + TIMER_INERTIA_CONTROL.countdown = TIMER_INERTIA_CONTROL.delay + player.update = bor(player.update, PU_MANA) + msg_print("Inertia flow controlling spell "..spell(s).name..".") + return TRUE + end, + ["info"] = function() + return "level "..get_level(INERTIA_CONTROL, 10) + end, + ["desc"] = { + "Changes the energy flow of a spell to be continuously recasted", + "at a given interval. The inertia controlled spell reduces your", + "maximum mana by four times its cost.", + } +} diff --git a/lib/scpt/s_mind.lua b/lib/scpt/s_mind.lua new file mode 100644 index 00000000..d1b25e9e --- /dev/null +++ b/lib/scpt/s_mind.lua @@ -0,0 +1,132 @@ +-- handle the mind school + +CHARM = add_spell +{ + ["name"] = "Charm", + ["school"] = {SCHOOL_MIND}, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 20, + ["fail"] = 10, + ["stick"] = + { + ["charge"] = { 7, 5 }, + [TV_WAND] = + { + ["rarity"] = 35, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 20, 40 }, + }, + }, + ["spell"] = function() + if get_level(CHARM, 50) >= 35 then + return project_los(GF_CHARM, 10 + get_level(CHARM, 150)) + elseif get_level(CHARM, 50) >= 15 then + local ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_ball(GF_CHARM, dir, 10 + get_level(CHARM, 150), 3) + else + local ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_bolt(GF_CHARM, dir, 10 + get_level(CHARM, 150)) + end + end, + ["info"] = function() + return "power "..(10 + get_level(CHARM, 150)) + end, + ["desc"] = { + "Tries to manipulate the mind of a monster to make it friendly", + "At level 15 it turns into a ball", + "At level 35 it affects all monsters in sight" + } +} + +CONFUSE = add_spell +{ + ["name"] = "Confuse", + ["school"] = {SCHOOL_MIND}, + ["level"] = 5, + ["mana"] = 5, + ["mana_max"] = 30, + ["fail"] = 20, + ["stick"] = + { + ["charge"] = { 3, 4 }, + [TV_WAND] = + { + ["rarity"] = 45, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 20, 40 }, + }, + }, + ["spell"] = function() + if get_level(CONFUSE, 50) >= 35 then + return project_los(GF_OLD_CONF, 10 + get_level(CONFUSE, 150)) + elseif get_level(CONFUSE, 50) >= 15 then + local ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_ball(GF_OLD_CONF, dir, 10 + get_level(CONFUSE, 150), 3) + else + local ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_bolt(GF_OLD_CONF, dir, 10 + get_level(CONFUSE, 150)) + end + end, + ["info"] = function() + return "power "..(10 + get_level(CONFUSE, 150)) + end, + ["desc"] = { + "Tries to manipulate the mind of a monster to confuse it", + "At level 15 it turns into a ball", + "At level 35 it affects all monsters in sight" + } +} + +ARMOROFFEAR = add_spell +{ + ["name"] = "Armor of Fear", + ["school"] = SCHOOL_MIND, + ["level"] = 10, + ["mana"] = 10, + ["mana_max"] = 50, + ["fail"] = 35, + ["inertia"] = { 2, 20 }, + ["spell"] = function() + return set_shield(randint(10) + 10 + get_level(ARMOROFFEAR, 100), 10, SHIELD_FEAR, 1 + get_level(ARMOROFFEAR, 7), 5 + get_level(ARMOROFFEAR, 20)) + end, + ["info"] = function() + return "dur "..(10 + get_level(ARMOROFFEAR, 100)).." power "..(1 + get_level(ARMOROFFEAR, 7)).."d"..(5 + get_level(ARMOROFFEAR, 20)) + end, + ["desc"] = { + "Creates a shield of pure fear around you. Any monster attempting to hit you", + "must save or flee", + } +} + +STUN = add_spell +{ + ["name"] = "Stun", + ["school"] = {SCHOOL_MIND}, + ["level"] = 15, + ["mana"] = 10, + ["mana_max"] = 90, + ["fail"] = 45, + ["spell"] = function() + if get_level(STUN, 50) >= 20 then + local ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_ball(GF_STUN, dir, 10 + get_level(STUN, 150), 3) + else + local ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_bolt(GF_STUN, dir, 10 + get_level(STUN, 150)) + end + end, + ["info"] = function() + return "power "..(10 + get_level(STUN, 150)) + end, + ["desc"] = { + "Tries to manipulate the mind of a monster to stun it", + "At level 20 it turns into a ball", + } +} diff --git a/lib/scpt/s_music.lua b/lib/scpt/s_music.lua new file mode 100644 index 00000000..9da6393a --- /dev/null +++ b/lib/scpt/s_music.lua @@ -0,0 +1,443 @@ +-- handle the music school +-- *ALL* lasting spell must return the mana cost in the lasting function + +MUSIC_STOP = add_spell +{ + ["name"] = "Stop singing(I)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 1, + ["mana"] = 0, + ["mana_max"] = 0, + ["fail"] = -400, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 1, + ["blind"] = FALSE, + ["spell"] = function() + player.start_lasting_spell(0) + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Stops the current song, if any." + } +} + +--- Drums +MUSIC_HOLD = add_spell +{ + ["name"] = "Holding Pattern(I)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 10, + ["fail"] = 20, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 1, + ["blind"] = FALSE, + ["lasting"] = function() + project_los(GF_OLD_SLOW, 10 + get_level(MUSIC_HOLD, 100)) + return get_mana(MUSIC_HOLD) + end, + ["spell"] = function() + player.start_lasting_spell(MUSIC_HOLD) + return TRUE + end, + ["info"] = function() + return "power "..(10 + get_level(MUSIC_HOLD, 100)) + end, + ["desc"] = { + "Slows down all monsters listening the song.", + "Consumes the amount of mana each turn.", + } +} + +MUSIC_CONF = add_spell +{ + ["name"] = "Illusion Pattern(II)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 5, + ["mana"] = 2, + ["mana_max"] = 15, + ["fail"] = 30, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 2, + ["blind"] = FALSE, + ["lasting"] = function() + project_los(GF_OLD_CONF, 10 + get_level(MUSIC_CONF, 100)) + return get_mana(MUSIC_CONF) + end, + ["spell"] = function() + player.start_lasting_spell(MUSIC_CONF) + return TRUE + end, + ["info"] = function() + return "power "..(10 + get_level(MUSIC_CONF, 100)) + end, + ["desc"] = { + "Tries to confuse all monsters listening the song.", + "Consumes the amount of mana each turn.", + } +} + +MUSIC_STUN = add_spell +{ + ["name"] = "Stun Pattern(IV)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 10, + ["mana"] = 3, + ["mana_max"] = 25, + ["fail"] = 45, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 4, + ["blind"] = FALSE, + ["lasting"] = function() + project_los(GF_STUN, 10 + get_level(MUSIC_STUN, 90)) + return get_mana(MUSIC_STUN) + end, + ["spell"] = function() + player.start_lasting_spell(MUSIC_STUN) + return TRUE + end, + ["info"] = function() + return "power "..(10 + get_level(MUSIC_STUN, 90)) + end, + ["desc"] = { + "Stuns all monsters listening the song.", + "Consumes the amount of mana each turn.", + } +} + +--- Harps +MUSIC_LITE = add_spell +{ + ["name"] = "Song of the Sun(I)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 1, + ["fail"] = 20, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["blind"] = FALSE, + ["pval"] = 1, + ["lasting"] = function() + set_lite(5) + return 1 + end, + ["spell"] = function() + player.start_lasting_spell(MUSIC_LITE) + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Provides light as long as you sing.", + "Consumes the amount of mana each turn.", + } +} + +MUSIC_HEAL = add_spell +{ + ["name"] = "Flow of Life(II)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 7, + ["mana"] = 5, + ["mana_max"] = 30, + ["fail"] = 35, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 2, + ["lasting"] = function() + hp_player(7 + get_level(MUSIC_HEAL, 100)) + return get_mana(MUSIC_HEAL) + end, + ["spell"] = function() + player.start_lasting_spell(MUSIC_HEAL) + return TRUE + end, + ["info"] = function() + return "heal "..(7 + get_level(MUSIC_HEAL, 100)).."/turn" + end, + ["desc"] = { + "Heals you as long as you sing.", + "Consumes the amount of mana each turn.", + } +} + +MUSIC_HERO = add_spell +{ + ["name"] = "Heroic Ballad(II)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 10, + ["mana"] = 4, + ["mana_max"] = 14, + ["fail"] = 45, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 2, + ["lasting"] = function() + set_hero(5) + if get_level(MUSIC_HERO) >= 10 then + set_shero(5) + end + if get_level(MUSIC_HERO) >= 20 then + set_strike(5) + end + if get_level(MUSIC_HERO) >= 25 then + set_oppose_cc(5) + end + return get_mana(MUSIC_HERO) + end, + ["spell"] = function() + player.start_lasting_spell(MUSIC_HERO) + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Increases melee accuracy", + "At level 10 it increases it even more and reduces armour a bit", + "At level 20 it increases it again", + "At level 25 it grants protection against chaos and confusion", + "Consumes the amount of mana each turn.", + } +} + +MUSIC_TIME = add_spell +{ + ["name"] = "Hobbit Melodies(III)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 20, + ["mana"] = 10, + ["mana_max"] = 30, + ["fail"] = 70, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 3, + ["lasting"] = function() + set_shield(5, 10 + get_level(MUSIC_TIME, 50), 0, 0, 0) + if get_level(MUSIC_TIME) >= 15 then + set_fast(5, 7 + get_level(MUSIC_TIME, 10)) + end + return get_mana(MUSIC_TIME) + end, + ["spell"] = function() + player.start_lasting_spell(MUSIC_TIME) + return TRUE + end, + ["info"] = function() + if get_level(MUSIC_TIME) >= 15 then + return "AC "..(10 + get_level(MUSIC_TIME, 50)).." speed "..(7 + get_level(MUSIC_TIME, 10)) + else + return "AC "..(10 + get_level(MUSIC_TIME, 50)) + end + end, + ["desc"] = { + "Greatly increases your reflexes allowing you to block more melee blows.", + "At level 15 it also makes you faster.", + "Consumes the amount of mana each turn.", + } +} + +MUSIC_MIND = add_spell +{ + ["name"] = "Clairaudience(IV)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 25, + ["mana"] = 15, + ["mana_max"] = 30, + ["fail"] = 75, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 4, + ["lasting"] = function() + set_tim_esp(5) + if get_level(MUSIC_MIND) >= 10 then + fire_ball(GF_IDENTIFY, 0, 1, 1 + get_level(MUSIC_MIND, 3, 0)) + end + return get_mana(MUSIC_MIND) + end, + ["spell"] = function() + player.start_lasting_spell(MUSIC_MIND) + return TRUE + end, + ["info"] = function() + if get_level(MUSIC_MIND) >= 10 then + return "rad "..(1 + get_level(MUSIC_MIND, 3, 0)) + else + return "" + end + end, + ["desc"] = { + "Allows you to sense monster minds as long as you sing.", + "At level 10 it identifies all objects in a radius on the floor,", + "as well as probing monsters in that radius.", + "Consumes the amount of mana each turn.", + } +} + +--- Horns + +MUSIC_BLOW = add_spell +{ + ["name"] = "Blow(I)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 4, + ["mana"] = 3, + ["mana_max"] = 30, + ["fail"] = 20, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 1, + ["spell"] = function() + fire_ball(GF_SOUND, + 0, + damroll(2 + get_level(MUSIC_BLOW, 10, 0), 4 + get_level(MUSIC_BLOW, 40, 0)), + 1 + get_level(MUSIC_BLOW, 12, 0) + ) + return TRUE + end, + ["info"] = function() + return "dam "..(2 + get_level(MUSIC_BLOW, 10, 0)).."d"..(4 + get_level(MUSIC_BLOW, 40, 0)).." rad "..(1 + get_level(MUSIC_BLOW, 12, 0)) + end, + ["desc"] = { + "Produces a powerful, blowing, sound all around you.", + } +} + +MUSIC_WIND = add_spell +{ + ["name"] = "Gush of Wind(II)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 14, + ["mana"] = 15, + ["mana_max"] = 45, + ["fail"] = 30, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 2, + ["spell"] = function() + fire_ball(GF_AWAY_ALL, + 0, + 10 + get_level(MUSIC_BLOW, 40, 0), + 1 + get_level(MUSIC_BLOW, 12, 0) + ) + return TRUE + end, + ["info"] = function() + return "dist "..(10 + get_level(MUSIC_BLOW, 40, 0)).." rad "..(1 + get_level(MUSIC_BLOW, 12, 0)) + end, + ["desc"] = { + "Produces a outgoing gush of wind that sends monsters away.", + } +} + +MUSIC_YLMIR = add_spell +{ + ["name"] = "Horns of Ylmir(III)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 20, + ["mana"] = 25, + ["mana_max"] = 30, + ["fail"] = 20, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 3, + ["spell"] = function() + earthquake(player.py, player.px, 2 + get_level(SHAKE, 10)) + return TRUE + end, + ["info"] = function() + return "rad "..(2 + get_level(SHAKE, 10)) + end, + ["desc"] = { + "Produces an earth shaking sound.", + } +} + +MUSIC_AMBARKANTA = add_spell +{ + ["name"] = "Ambarkanta(IV)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 25, + ["mana"] = 70, + ["mana_max"] = 70, + ["fail"] = 60, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 4, + ["spell"] = function() + alter_reality() + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Produces a reality shaking sound that transports you to a nearly", + "identical reality.", + } +} + + +--[[ +MUSIC_ = add_spell +{ + ["name"] = "(I)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 1, + ["mana"] = 0, + ["mana_max"] = 0, + ["fail"] = 20, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 1, + ["lasting"] = function() + return get_mana(MUSIC_) + end, + ["spell"] = function() + player.start_lasting_spell(MUSIC_) + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "", + "Consumes the amount of mana each turn.", + } +} + +or + +MUSIC_ = add_spell +{ + ["name"] = "(I)", + ["school"] = {SCHOOL_MUSIC}, + ["level"] = 1, + ["mana"] = 0, + ["mana_max"] = 0, + ["fail"] = 20, + ["stat"] = A_CHR, + ["random"] = SKILL_MUSIC, + ["pval"] = 1, + ["spell"] = function() + + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "", + } +} +]] diff --git a/lib/scpt/s_nature.lua b/lib/scpt/s_nature.lua new file mode 100644 index 00000000..e71a89bf --- /dev/null +++ b/lib/scpt/s_nature.lua @@ -0,0 +1,152 @@ +-- handle the nature school + +GROWTREE = add_spell +{ + ["name"] = "Grow Trees", + ["school"] = {SCHOOL_NATURE, SCHOOL_TEMPORAL}, + ["level"] = 6, + ["mana"] = 6, + ["mana_max"] = 30, + ["fail"] = 35, + ["inertia"] = { 5, 50 }, + ["spell"] = function() + grow_trees(2 + get_level(GROWTREE, 7)) + return TRUE + end, + ["info"] = function() + return "rad "..(2 + get_level(GROWTREE, 7)) + end, + ["desc"] = { + "Makes trees grow extremely quickly around you", + } +} + +HEALING = add_spell +{ + ["name"] = "Healing", + ["school"] = {SCHOOL_NATURE}, + ["level"] = 10, + ["mana"] = 15, + ["mana_max"] = 50, + ["fail"] = 45, + ["stick"] = + { + ["charge"] = { 2, 3 }, + [TV_STAFF] = + { + ["rarity"] = 90, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 20, 40 }, + }, + }, + ["spell"] = function() + return hp_player(player.mhp * (15 + get_level(HEALING, 35)) / 100) + end, + ["info"] = function() + return "heal "..(15 + get_level(HEALING, 35)).."% = "..(player.mhp * (15 + get_level(HEALING, 35)) / 100).."hp" + end, + ["desc"] = { + "Heals a percent of hitpoints", + } +} + +RECOVERY = add_spell +{ + ["name"] = "Recovery", + ["school"] = {SCHOOL_NATURE}, + ["level"] = 15, + ["mana"] = 10, + ["mana_max"] = 25, + ["fail"] = 60, + ["stick"] = + { + ["charge"] = { 5, 10 }, + [TV_STAFF] = + { + ["rarity"] = 50, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 10, 30 }, + }, + }, + ["inertia"] = { 2, 100 }, + ["spell"] = function() + local obvious + obvious = set_poisoned(player.poisoned / 2) + if get_level(RECOVERY, 50) >= 5 then + obvious = is_obvious(set_poisoned(0), obvious) + obvious = is_obvious(set_cut(0), obvious) + end + if get_level(RECOVERY, 50) >= 10 then + obvious = is_obvious(do_res_stat(A_STR, TRUE), obvious) + obvious = is_obvious(do_res_stat(A_CON, TRUE), obvious) + obvious = is_obvious(do_res_stat(A_DEX, TRUE), obvious) + obvious = is_obvious(do_res_stat(A_WIS, TRUE), obvious) + obvious = is_obvious(do_res_stat(A_INT, TRUE), obvious) + obvious = is_obvious(do_res_stat(A_CHR, TRUE), obvious) + end + if get_level(RECOVERY, 50) >= 15 then + obvious = is_obvious(restore_level(), obvious) + end + return obvious + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Reduces the length of time that you are poisoned", + "At level 5 it cures poison and cuts", + "At level 10 it restores drained stats", + "At level 15 it restores lost experience" + } +} + +REGENERATION = add_spell +{ + ["name"] = "Regeneration", + ["school"] = {SCHOOL_NATURE}, + ["level"] = 20, + ["mana"] = 30, + ["mana_max"] = 55, + ["fail"] = 70, + ["inertia"] = { 4, 40 }, + ["spell"] = function() + if player.tim_regen == 0 then return set_tim_regen(randint(10) + 5 + get_level(REGENERATION, 50), 300 + get_level(REGENERATION, 700)) end + end, + ["info"] = function() + return "dur "..(5 + get_level(REGENERATION, 50)).."+d10 power "..(300 + get_level(REGENERATION, 700)) + end, + ["desc"] = { + "Increases your body's regeneration rate", + } +} + + +SUMMONANNIMAL = add_spell +{ + ["name"] = "Summon Animal", + ["school"] = {SCHOOL_NATURE}, + ["level"] = 25, + ["mana"] = 25, + ["mana_max"] = 50, + ["fail"] = 90, + ["stick"] = + { + ["charge"] = { 1, 3 }, + [TV_WAND] = + { + ["rarity"] = 85, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 15, 45 }, + }, + }, + ["spell"] = function() + summon_specific_level = 25 + get_level(SUMMONANNIMAL, 50) + return summon_monster(player.py, player.px, dun_level, TRUE, SUMMON_ANIMAL) + end, + ["info"] = function() + return "level "..(25 + get_level(SUMMONANNIMAL, 50)) + end, + ["desc"] = { + "Summons a leveled animal to your aid", + } +} diff --git a/lib/scpt/s_stick.lua b/lib/scpt/s_stick.lua new file mode 100644 index 00000000..e9d994e9 --- /dev/null +++ b/lib/scpt/s_stick.lua @@ -0,0 +1,444 @@ +-- Spells that are stick or artifacts/... only + +DEVICE_HEAL_MONSTER = add_spell +{ + ["name"] = "Heal Monster", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 3, + ["mana"] = 5, + ["mana_max"] = 20, + ["fail"] = 15, + ["random"] = -1, + ["stick"] = + { + ["charge"] = { 10, 10 }, + [TV_WAND] = + { + ["rarity"] = 17, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 20, 50 }, + }, + }, + ["spell"] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + return fire_ball(GF_OLD_HEAL, dir, 20 + get_level(DEVICE_HEAL_MONSTER, 380), 0) + end, + ["info"] = function() + return "heal "..(20 + get_level(DEVICE_HEAL_MONSTER, 380)) + end, + ["desc"] = { + "Heals a monster", + } +} + +DEVICE_SPEED_MONSTER = add_spell +{ + ["name"] = "Haste Monster", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 10, + ["mana"] = 10, + ["mana_max"] = 10, + ["fail"] = 30, + ["random"] = -1, + ["stick"] = + { + ["charge"] = { 10, 5 }, + [TV_WAND] = + { + ["rarity"] = 7, + ["base_level"] = { 1, 1 }, + ["max_level"] = { 20, 50 }, + }, + }, + ["spell"] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + return fire_ball(GF_OLD_SPEED, dir, 1, 0) + end, + ["info"] = function() + return "speed +10" + end, + ["desc"] = { + "Haste a monster", + } +} + +DEVICE_WISH = add_spell +{ + ["name"] = "Wish", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 50, + ["mana"] = 400, + ["mana_max"] = 400, + ["fail"] = 99, + ["random"] = -1, + ["stick"] = + { + ["charge"] = { 1, 2 }, + [TV_STAFF] = + { + ["rarity"] = 98, + ["base_level"] = { 1, 1 }, + ["max_level"] = { 1, 1 }, + }, + }, + ["spell"] = function() + make_wish() + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "This grants you a wish, beware of what you ask for!", + } +} + +DEVICE_SUMMON = add_spell +{ + ["name"] = "Summon", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 5, + ["mana"] = 5, + ["mana_max"] = 25, + ["fail"] = 20, + ["random"] = -1, + ["stick"] = + { + ["charge"] = { 1, 20 }, + [TV_STAFF] = + { + ["rarity"] = 13, + ["base_level"] = { 1, 40 }, + ["max_level"] = { 25, 50 }, + }, + }, + ["spell"] = function() + local i, obvious + obvious = nil + for i = 1, 4 + get_level(DEVICE_SUMMON, 30) do + obvious = is_obvious(summon_specific(player.py, player.px, dun_level, 0), obvious) + end + return obvious + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Summons hostile monsters near you", + } +} + +DEVICE_MANA = add_spell +{ + ["name"] = "Mana", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 30, + ["mana"] = 1, + ["mana_max"] = 1, + ["fail"] = 80, + ["random"] = -1, + ["stick"] = + { + ["charge"] = { 2, 3 }, + [TV_STAFF] = + { + ["rarity"] = 78, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 20, 35 }, + }, + }, + ["spell"] = function() + increase_mana((player.msp * (20 + get_level(DEVICE_MANA, 50))) / 100) + return TRUE + end, + ["info"] = function() + return "restore "..(20 + get_level(DEVICE_MANA, 50)).."%" + end, + ["desc"] = { + "Restores a part(or all) of your mana", + } +} + +DEVICE_NOTHING = add_spell +{ + ["name"] = "Nothing", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 1, + ["mana"] = 0, + ["mana_max"] = 0, + ["fail"] = 0, + ["random"] = -1, + ["stick"] = + { + ["charge"] = { 0, 0 }, + [TV_WAND] = + { + ["rarity"] = 3, + ["base_level"] = { 1, 1 }, + ["max_level"] = { 1, 1 }, + }, + [TV_STAFF] = + { + ["rarity"] = 3, + ["base_level"] = { 1, 1 }, + ["max_level"] = { 1, 1}, + }, + }, + ["spell"] = function() + return FALSE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "It does nothing.", + } +} + +DEVICE_LEBOHAUM = add_spell +{ + ["name"] = "Artifact Lebauhaum", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 1, + ["mana"] = 0, + ["mana_max"] = 0, + ["fail"] = 0, + ["random"] = -1, + ["activate"] = 3, + ["spell"] = function() + msg_print("You hear a little song in your head in some unknown tongue:") + msg_print("'Avec le casque Lebohaum y a jamais d'anicroches, je parcours les dongeons,") + msg_print("j'en prend plein la caboche. Avec le casque Lebohaum, tout ces monstres a la") + msg_print("con, je leur met bien profond: c'est moi le maitre du dongeon!'") + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "sing a cheerful song", + } +} + +DEVICE_MAGGOT = add_spell +{ + ["name"] = "Artifact Maggot", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 1, + ["mana"] = 7, + ["mana_max"] = 7, + ["fail"] = 20, + ["random"] = -1, + ["activate"] = { 10, 50 }, + ["spell"] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_ball(GF_TURN_ALL, dir, 40, 2) + end, + ["info"] = function() + return "power 40 rad 2" + end, + ["desc"] = { + "terrify", + } +} + +DEVICE_HOLY_FIRE = add_spell +{ + ["name"] = "Holy Fire of Mithrandir", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 30, + ["mana"] = 50, + ["mana_max"] = 150, + ["fail"] = 75, + ["random"] = -1, + ["stick"] = + { + ["charge"] = { 2, 5 }, + [TV_STAFF] = + { + -- Rarity higher than 100 to be sure to not have it generated randomly + ["rarity"] = 999, + ["base_level"] = { 1, 1 }, + ["max_level"] = { 35, 35 }, + }, + }, + ["spell"] = function() + return project_los(GF_HOLY_FIRE, 50 + get_level(DEVICE_HOLY_FIRE, 300)) + end, + ["info"] = function() + return "dam "..(50 + get_level(DEVICE_HOLY_FIRE, 250)) + end, + ["desc"] = { + "The Holy Fire created by this staff will deeply(double damage) burn", + "all that is evil.", + } +} + +-- Ok the Eternal Flame, to craete one of the 4 ultimate arts +-- needed to enter the last level of the Void +DEVICE_ETERNAL_FLAME = add_spell +{ + ["name"] = "Artifact Eternal Flame", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 1, + ["mana"] = 0, + ["mana_max"] = 0, + ["fail"] = 0, + ["random"] = -1, + ["activate"] = { 0, 0 }, + ["spell"] = function(flame_item) + local ret, item, obj + + ret, item = get_item("Which object do you want to imbue?", + "You have no objects to imbue.", + bor(USE_INVEN), + function (obj) + if obj.name1 > 0 or obj.name2 > 0 then return FALSE end + if (obj.tval == TV_SWORD) and (obj.sval == SV_LONG_SWORD) then + return TRUE + elseif (obj.tval == TV_MSTAFF) and (obj.sval == SV_MSTAFF) then + return TRUE + elseif (obj.tval == TV_BOW) and (obj.sval == SV_HEAVY_XBOW) then + return TRUE + elseif (obj.tval == TV_DRAG_ARMOR) and (obj.sval == SV_DRAGON_POWER) then + return TRUE + end + return FALSE + end + ) + if ret == FALSE then return FALSE end + + obj = get_object(item) + + if (obj.tval == TV_SWORD) and (obj.sval == SV_LONG_SWORD) then + obj.name1 = 147 + elseif (obj.tval == TV_MSTAFF) and (obj.sval == SV_MSTAFF) then + obj.name1 = 127 + elseif (obj.tval == TV_BOW) and (obj.sval == SV_HEAVY_XBOW) then + obj.name1 = 152 + elseif (obj.tval == TV_DRAG_ARMOR) and (obj.sval == SV_DRAGON_POWER) then + obj.name1 = 17 + end + apply_magic(obj, -1, TRUE, TRUE, TRUE) + + obj.found = OBJ_FOUND_SELFMADE + + inven_item_increase(flame_item, -1) + inven_item_describe(flame_item) + inven_item_optimize(flame_item) + + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "imbuing an object with the eternal fire", + } +} + +-- And one more silly activation :) +DEVICE_DURANDIL = add_spell +{ + ["name"] = "Artifact Durandil", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 1, + ["mana"] = 0, + ["mana_max"] = 0, + ["fail"] = 0, + ["random"] = -1, + ["activate"] = 3, + ["spell"] = function() + msg_print("You hear a little song in your head in some unknown tongue:") + msg_print("'Les epees Durandils sont forgees dans les mines par des nains.") + msg_print("Avec ca c'est facile de tuer un troll avec une seule main. Pas besoin") + msg_print("de super entrainement nis de niveau 28. Quand tu sors l'instrument") + msg_print("c'est l'ennemi qui prend la fuite! Avec ton epee Durandil quand tu") + msg_print("parcours les chemins, tu massacre sans peine les brigands et les gobelins,") + msg_print("les rats geants, les ogres mutants, les zombies et les liches, tu les") + msg_print("decoupe en tranches comme si c'etait des parts de quiches.") + msg_print("Les epees Durandil! Les epees Durandil!") + msg_print("Quand tu la sort dans un dongeon au moins t'as pas l'air debile.") + msg_print("C'est l'arme des bourins qui savent etre subtils.") + msg_print("Ne partez pas a l'aventure sans votre epee Durandil!'") + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "sing a cheerful song", + } +} + +DEVICE_THUNDERLORDS = add_spell +{ + ["name"] = "Artifact Thunderlords", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 1, + ["fail"] = 20, + ["random"] = -1, + ["stick"] = + { + ["charge"] = { 3, 3 }, + [TV_STAFF] = + { + -- Rarity higher than 100 to be sure to not have it generated randomly + ["rarity"] = 999, + ["base_level"] = { 1, 1 }, + ["max_level"] = { 1, 1 }, + }, + }, + ["spell"] = function() + if dun_level > 0 then + msg_print("As you blow the horn a thunderlord pops out of nowhere and grabs you.") + recall_player(0, 1) + else + msg_print("You cannot use it there.") + end + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "A thunderlord will appear to transport you quickly to the surface.", + } +} + +--[[ Template +DEVICE_ = add_spell +{ + ["name"] = "", + ["school"] = {SCHOOL_DEVICE}, + ["level"] = 1, + ["mana"] = 2, + ["mana_max"] = 15, + ["fail"] = 10, + ["random"] = -1, + ["stick"] = + { + ["charge"] = { 10, 5 }, + [TV_STAFF] = + { + ["rarity"] = 7, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 25, 50 }, + }, + }, + ["spell"] = function() + return FALSE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "", + } +} +]] diff --git a/lib/scpt/s_tempo.lua b/lib/scpt/s_tempo.lua new file mode 100644 index 00000000..d3d2fbb5 --- /dev/null +++ b/lib/scpt/s_tempo.lua @@ -0,0 +1,162 @@ +-- Handles thhe temporal school + + +MAGELOCK = add_spell +{ + ["name"] = "Magelock", + ["school"] = {SCHOOL_TEMPORAL}, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 35, + ["fail"] = 10, + ["stick"] = + { + ["charge"] = { 7, 5 }, + [TV_WAND] = + { + ["rarity"] = 30, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 15, 45 }, + }, + }, + ["spell"] = function() + if get_level(MAGELOCK, 50) >= 30 then + local ret, x, y, c_ptr + + if get_level(MAGELOCK, 50) >= 40 then + ret, x, y = tgt_pt() + if ret == FALSE then return end + if cave_is(cave(y, x), FF1_FLOOR) == FALSE or cave_is(cave(y, x), FF1_PERMANENT) == TRUE or los(player.py, player.px, y, x) == FALSE then + msg_print("You cannot place it there.") + return TRUE + end + else + y = player.py + x = player.px + end + cave_set_feat(y, x, 3) + return TRUE + else + ret, dir = get_aim_dir() + if ret == FALSE then return end + return wizard_lock(dir) + end + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Magically locks a door", + "At level 30 it creates a glyph of warding", + "At level 40 the glyph can be placed anywhere in the field of vision" + } +} + +SLOWMONSTER = add_spell +{ + ["name"] = "Slow Monster", + ["school"] = {SCHOOL_TEMPORAL}, + ["level"] = 10, + ["mana"] = 10, + ["mana_max"] = 15, + ["fail"] = 35, + ["stick"] = + { + ["charge"] = { 5, 5 }, + [TV_WAND] = + { + ["rarity"] = 23, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 20, 50 }, + }, + }, + ["spell"] = function() + local ret, dir + + ret, dir = get_aim_dir() + if ret == FALSE then return end + if get_level(SLOWMONSTER, 50) >= 20 then + return fire_ball(GF_OLD_SLOW, dir, 40 + get_level(SLOWMONSTER, 160), 1) + else + return fire_bolt(GF_OLD_SLOW, dir, 40 + get_level(SLOWMONSTER, 160)) + end + end, + ["info"] = function() + if get_level(SLOWMONSTER, 50) >= 20 then + return "power "..(40 + get_level(SLOWMONSTER, 160)).." rad 1" + else + return "power "..(40 + get_level(SLOWMONSTER, 160)) + end + end, + ["desc"] = { + "Magically slows down the passing of time around a monster", + "At level 20 it affects a zone" + } +} + +ESSENCESPEED = add_spell +{ + ["name"] = "Essence of Speed", + ["school"] = {SCHOOL_TEMPORAL}, + ["level"] = 15, + ["mana"] = 20, + ["mana_max"] = 40, + ["fail"] = 50, + ["stick"] = + { + ["charge"] = { 3, 3 }, + [TV_WAND] = + { + ["rarity"] = 80, + ["base_level"] = { 1, 1 }, + ["max_level"] = { 10, 39 }, + }, + }, + ["inertia"] = { 5, 20 }, + ["spell"] = function() + if player.fast == 0 then return set_fast(10 + randint(10) + get_level(ESSENCESPEED, 50), 5 + get_level(ESSENCESPEED, 20)) end + end, + ["info"] = function() + return "dur "..(10 + get_level(ESSENCESPEED, 50)).."+d10 speed "..(5 + get_level(ESSENCESPEED, 20)) + end, + ["desc"] = { + "Magically decreases the passing of time around you, making you move faster with", + "respect to the rest of the universe." + } +} + +BANISHMENT = add_spell +{ + ["name"] = "Banishment", + ["school"] = {SCHOOL_TEMPORAL, SCHOOL_CONVEYANCE}, + ["level"] = 30, + ["mana"] = 30, + ["mana_max"] = 40, + ["fail"] = 95, + ["stick"] = + { + ["charge"] = { 1, 3 }, + [TV_WAND] = + { + ["rarity"] = 98, + ["base_level"] = { 1, 15 }, + ["max_level"] = { 10, 36 }, + }, + }, + ["inertia"] = { 5, 50 }, + ["spell"] = function() + local obvious + obvious = project_los(GF_AWAY_ALL, 40 + get_level(BANISHMENT, 160)) + if get_level(BANISHMENT, 50) >= 15 then + obvious = is_obvious(project_los(GF_STASIS, 20 + get_level(BANISHMENT, 120)), obvious) + end + return obvious + end, + ["info"] = function() + return "power "..(40 + get_level(BANISHMENT, 160)) + end, + ["desc"] = { + "Disrupts the space/time continuum in your area and teleports all monsters away.", + "At level 15 it may also lock them in a time bubble for a while." + } +} diff --git a/lib/scpt/s_tulkas.lua b/lib/scpt/s_tulkas.lua new file mode 100644 index 00000000..4afa8082 --- /dev/null +++ b/lib/scpt/s_tulkas.lua @@ -0,0 +1,81 @@ +-- Handle Tulkas magic school + +TULKAS_AIM = add_spell +{ + ["name"] = "Divine Aim", + ["school"] = {SCHOOL_TULKAS}, + ["level"] = 1, + ["mana"] = 30, + ["mana_max"] = 500, + ["fail"] = 20, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local dur = get_level(TULKAS_AIM, 50) + randint(10) + local obvious + + obvious = set_strike(dur) + if get_level(TULKAS_AIM) >= 20 then + obvious = is_obvious(set_tim_deadly(dur), obvious) + end + return obvious + end, + ["info"] = function() + return "dur "..(get_level(TULKAS_AIM, 50)).."+d10" + end, + ["desc"] = { + "It makes you more accurate in combat", + "At level 20 all your blows are critical hits", + } +} + +TULKAS_WAVE = add_spell +{ + ["name"] = "Wave of Power", + ["school"] = {SCHOOL_TULKAS}, + ["level"] = 20, + ["mana"] = 200, + ["mana_max"] = 200, + ["fail"] = 75, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + return fire_bolt(GF_ATTACK, dir, get_level(TULKAS_WAVE, player.num_blow)) + end, + ["info"] = function() + return "blows "..(get_level(TULKAS_WAVE, player.num_blow)) + end, + ["desc"] = { + "It allows you to project a number of melee blows across a distance", + } +} + +TULKAS_SPIN = add_spell +{ + ["name"] = "Whirlwind", + ["school"] = {SCHOOL_TULKAS}, + ["level"] = 10, + ["mana"] = 100, + ["mana_max"] = 100, + ["fail"] = 45, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + return fire_ball(GF_ATTACK, 0, 1, 1) + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "It allows you to spin around and hit all monsters nearby", + } +} diff --git a/lib/scpt/s_udun.lua b/lib/scpt/s_udun.lua new file mode 100644 index 00000000..c4266239 --- /dev/null +++ b/lib/scpt/s_udun.lua @@ -0,0 +1,180 @@ +-- handle the udun school + +DRAIN = add_spell +{ + ["name"] = "Drain", + ["school"] = {SCHOOL_UDUN, SCHOOL_MANA}, + ["level"] = 1, + ["mana"] = 0, + ["mana_max"] = 0, + ["fail"] = 20, + ["spell"] = function() + local ret, item, obj, o_name, add + + -- Ask for an item + ret, item = get_item("What item to drain?", "You have nothing you can drain", USE_INVEN, + function (obj) + if (obj.tval == TV_WAND) or (obj.tval == TV_ROD_MAIN) or (obj.tval == TV_STAFF) then + return TRUE + end + return FALSE + end + ) + + if ret == TRUE then + -- get the item + obj = get_object(item) + + add = 0 + if (obj.tval == TV_STAFF) or (obj.tval == TV_WAND) then + local kind = get_kind(obj) + + add = kind.level * obj.pval * obj.number + + -- Destroy it! + inven_item_increase(item, -99) + inven_item_describe(item) + inven_item_optimize(item) + end + if obj.tval == TV_ROD_MAIN then + add = obj.timeout + obj.timeout = 0; + + --Combine / Reorder the pack (later) + player.notice = bor(player.notice, PN_COMBINE, PN_REORDER) + player.window = bor(player.window, PW_INVEN, PW_EQUIP, PW_PLAYER) + end + increase_mana(add) + end + return TRUE + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Drains the mana contained in wands, staves and rods to increase yours", + } +} + +GENOCIDE = add_spell +{ + ["name"] = "Genocide", + ["school"] = {SCHOOL_UDUN, SCHOOL_NATURE}, + ["level"] = 25, + ["mana"] = 50, + ["mana_max"] = 50, + ["fail"] = 90, + ["stick"] = + { + ["charge"] = { 2, 2 }, + [TV_STAFF] = + { + ["rarity"] = 85, + ["base_level"] = { 1, 1 }, + ["max_level"] = { 5, 15 }, + }, + }, + ["spell"] = function() + local type + + type = 0 + if get_level(GENOCIDE) >= 10 then type = 1 end + if type == 0 then + genocide(TRUE) + return TRUE + else + if get_check("Genocide all monsters near you? ") == TRUE then + mass_genocide(TRUE) + else + genocide(TRUE) + end + return TRUE + end + end, + ["info"] = function() + return "" + end, + ["desc"] = { + "Genocides all monsters of a race on the level", + "At level 10 it can genocide all monsters near you" + } +} + +WRAITHFORM = add_spell +{ + ["name"] = "Wraithform", + ["school"] = {SCHOOL_UDUN, SCHOOL_CONVEYANCE}, + ["level"] = 30, + ["mana"] = 20, + ["mana_max"] = 40, + ["fail"] = 95, + ["inertia"] = { 4, 30 }, + ["spell"] = function() + return set_shadow(randint(30) + 20 + get_level(WRAITHFORM, 40)) + end, + ["info"] = function() + return "dur "..(20 + get_level(WRAITHFORM, 40)).."+d30" + end, + ["desc"] = { + "Turns you into an immaterial being", + } +} + +FLAMEOFUDUN = add_spell +{ + ["name"] = "Flame of Udun", + ["school"] = {SCHOOL_UDUN, SCHOOL_FIRE}, + ["level"] = 35, + ["mana"] = 70, + ["mana_max"] = 100, + ["fail"] = 95, + ["inertia"] = { 7, 15 }, + ["spell"] = function() + return set_mimic(randint(15) + 5 + get_level(FLAMEOFUDUN, 30), resolve_mimic_name("Balrog"), get_level(FLAMEOFUDUN)) + end, + ["info"] = function() + return "dur "..(5 + get_level(FLAMEOFUDUN, 30)).."+d15" + end, + ["desc"] = { + "Turns you into a powerful Balrog", + } +} + + +-- Return the number of Udun/Melkor spells in a given book +function udun_in_book(sval, pval) + local i, y, index, sch, s + + i = 0 + + -- Hack if the book is 255 it is a random book + if sval == 255 then + school_book[sval] = {pval} + end + -- Parse all spells + for index, s in school_book[sval] do + for index, sch in __spell_school[s] do + if sch == SCHOOL_UDUN then i = i + 1 end + if sch == SCHOOL_MELKOR then i = i + 1 end + end + end + return i +end + +-- Return the total level of spells +function levels_in_book(sval, pval) + local i, y, index, sch, s + + i = 0 + + -- Hack if the book is 255 it is a random book + if sval == 255 then + school_book[sval] = {pval} + end + + -- Parse all spells + for index, s in school_book[sval] do + i = i + __tmp_spells[s].level + end + return i +end diff --git a/lib/scpt/s_water.lua b/lib/scpt/s_water.lua new file mode 100644 index 00000000..88305201 --- /dev/null +++ b/lib/scpt/s_water.lua @@ -0,0 +1,154 @@ +-- handle the water school + +TIDALWAVE = add_spell +{ + ["name"] = "Tidal Wave", + ["school"] = {SCHOOL_WATER}, + ["level"] = 16, + ["mana"] = 16, + ["mana_max"] = 40, + ["fail"] = 65, + ["stick"] = + { + ["charge"] = { 6, 5 }, + [TV_WAND] = + { + ["rarity"] = 54, + ["base_level"] = { 1, 10 }, + ["max_level"] = { 20, 50 }, + }, + }, + ["inertia"] = { 4, 100 }, + ["spell"] = function() + fire_wave(GF_WAVE, 0, 40 + get_level(TIDALWAVE, 200), 0, 6 + get_level(TIDALWAVE, 10), EFF_WAVE) + return TRUE + end, + ["info"] = function() + return "dam "..(40 + get_level(TIDALWAVE, 200)).." rad "..(6 + get_level(TIDALWAVE, 10)) + end, + ["desc"] = { + "Summons a monstrous tidal wave that will expand and crush the", + "monsters under its mighty waves." + } +} + +ICESTORM = add_spell +{ + ["name"] = "Ice Storm", + ["school"] = {SCHOOL_WATER}, + ["level"] = 22, + ["mana"] = 30, + ["mana_max"] = 60, + ["fail"] = 80, + ["stick"] = + { + ["charge"] = { 3, 7 }, + [TV_WAND] = + { + ["rarity"] = 65, + ["base_level"] = { 1, 5 }, + ["max_level"] = { 25, 45 }, + }, + }, + ["inertia"] = { 3, 40 }, + ["spell"] = function() + local type + + if get_level(ICESTORM, 50) >= 10 then type = GF_ICE + else type = GF_COLD end + fire_wave(type, 0, 80 + get_level(ICESTORM, 200), 1 + get_level(ICESTORM, 3, 0), 20 + get_level(ICESTORM, 70), EFF_STORM) + return TRUE + end, + ["info"] = function() + return "dam "..(80 + get_level(ICESTORM, 200)).." rad "..(1 + get_level(ICESTORM, 3, 0)).." dur "..(20 + get_level(ICESTORM, 70)) + end, + ["desc"] = { + "Engulfs you in a storm of roaring cold that strikes your foes.", + "At level 10 it turns into shards of ice." + } +} + +ENTPOTION = add_spell +{ + ["name"] = "Ent's Potion", + ["school"] = {SCHOOL_WATER}, + ["level"] = 6, + ["mana"] = 7, + ["mana_max"] = 15, + ["fail"] = 35, + ["inertia"] = { 1, 30 }, + ["spell"] = function() + set_food(PY_FOOD_MAX - 1) + msg_print("The Ent's Potion fills your stomach.") + if get_level(ENTPOTION, 50) >= 5 then + set_afraid(0) + end + if get_level(ENTPOTION, 50) >= 12 then + set_hero(player.hero + randint(25) + 25 + get_level(ENTPOTION, 40)) + end + return TRUE + end, + ["info"] = function() + if get_level(ENTPOTION, 50) >= 12 then + return "dur "..(25 + get_level(ENTPOTION, 40)).."+d25" + else + return "" + end + end, + ["desc"] = { + "Fills up your stomach.", + "At level 5 it boldens your heart.", + "At level 12 it make you heroic." + } +} + +VAPOR = add_spell +{ + ["name"] = "Vapor", + ["school"] = {SCHOOL_WATER}, + ["level"] = 2, + ["mana"] = 2, + ["mana_max"] = 12, + ["fail"] = 20, + ["inertia"] = { 1, 30 }, + ["spell"] = function() + fire_cloud(GF_WATER, 0, 3 + get_level(VAPOR, 20), 3 + get_level(VAPOR, 9, 0), 5) + return TRUE + end, + ["info"] = function() + return "dam "..(3 + get_level(VAPOR, 20)).." rad "..(3 + get_level(VAPOR, 9, 0)).." dur 5" + end, + ["desc"] = { + "Fills the air with toxic moisture to eradicate annoying critters." + } +} + +function get_geyser_damage() + return get_level(GEYSER, 10), 3 + get_level(GEYSER, 35) +end + +GEYSER = add_spell +{ + ["name"] = "Geyser", + ["school"] = SCHOOL_WATER, + ["level"] = 1, + ["mana"] = 1, + ["mana_max"] = 35, + ["fail"] = 5, + ["spell"] = function() + local ret, dir + ret, dir = get_aim_dir() + if ret == FALSE then return end + return fire_bolt_or_beam(2 * get_level(GEYSER, 85), GF_WATER, dir, damroll(get_geyser_damage())) + end, + ["info"] = function() + local n, d + n, d = get_geyser_damage() + return "dam "..n.."d"..d + end, + ["desc"] = + { + "Shoots a geyser of water from your fingertips.", + "Sometimes it can blast through its first target." + }, +} diff --git a/lib/scpt/s_yavann.lua b/lib/scpt/s_yavann.lua new file mode 100644 index 00000000..2f594e85 --- /dev/null +++ b/lib/scpt/s_yavann.lua @@ -0,0 +1,157 @@ +-- Handle Yavanna kementari magic school + +YAVANNA_CHARM_ANIMAL = add_spell +{ + ["name"] = "Charm Animal", + ["school"] = {SCHOOL_YAVANNA}, + ["level"] = 1, + ["mana"] = 10, + ["mana_max"] = 100, + ["fail"] = 30, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local ret, dir = get_aim_dir() + if ret == FALSE then return end + + return fire_ball(GF_CONTROL_ANIMAL, dir, 10 + get_level(YAVANNA_CHARM_ANIMAL, 170), get_level(YAVANNA_CHARM_ANIMAL, 2)) + end, + ["info"] = function() + return "power "..(10 + get_level(YAVANNA_CHARM_ANIMAL, 170)).." rad "..(get_level(YAVANNA_CHARM_ANIMAL, 2)) + end, + ["desc"] = { + "It tries to tame an animal", + } +} + +YAVANNA_GROW_GRASS = add_spell +{ + ["name"] = "Grow Grass", + ["school"] = {SCHOOL_YAVANNA}, + ["level"] = 10, + ["mana"] = 70, + ["mana_max"] = 150, + ["fail"] = 65, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + grow_grass(get_level(YAVANNA_GROW_GRASS, 4)) + return TRUE + end, + ["info"] = function() + return "rad "..(get_level(YAVANNA_GROW_GRASS, 4)) + end, + ["desc"] = { + "Create a floor of grass around you. While on grass and praying", + "a worshipper of Yavanna will know a greater regeneration rate" + } +} + +YAVANNA_TREE_ROOTS = add_spell +{ + ["name"] = "Tree Roots", + ["school"] = {SCHOOL_YAVANNA}, + ["level"] = 15, + ["mana"] = 50, + ["mana_max"] = 1000, + ["fail"] = 70, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + return set_roots(10 + get_level(YAVANNA_TREE_ROOTS, 30), 10 + get_level(YAVANNA_TREE_ROOTS, 60), 10 + get_level(YAVANNA_TREE_ROOTS, 20)) + end, + ["info"] = function() + return "dur "..(10 + get_level(YAVANNA_TREE_ROOTS, 30)).." AC "..(10 + get_level(YAVANNA_TREE_ROOTS, 60)).." dam "..(10 + get_level(YAVANNA_TREE_ROOTS, 20)) + end, + ["desc"] = { + "Creates roots deep in the floor from your feet, making you more stable and able", + "to make stronger attacks, but prevents any movement (even teleportation).", + "It also makes you recover from stunning almost immediately." + } +} + +YAVANNA_WATER_BITE = add_spell +{ + ["name"] = "Water Bite", + ["school"] = {SCHOOL_YAVANNA}, + ["level"] = 20, + ["mana"] = 150, + ["mana_max"] = 300, + ["fail"] = 90, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local rad + + rad = 0 + if get_level(YAVANNA_WATER_BITE) >= 25 then rad = 1 end + + return set_project(randint(30) + 30 + get_level(YAVANNA_WATER_BITE, 150), + GF_WATER, + 10 + get_level(YAVANNA_WATER_BITE), + rad, + bor(PROJECT_STOP, PROJECT_KILL)) + end, + ["info"] = function() + return "dur "..(30 + get_level(YAVANNA_WATER_BITE, 150)).."+d30 dam "..(10 + get_level(YAVANNA_WATER_BITE)).."/blow" + end, + ["desc"] = { + "Imbues your melee weapon with a natural stream of water", + "At level 25, it spreads over a 1 radius zone around your target" + } +} + +YAVANNA_UPROOT = add_spell +{ + ["name"] = "Uproot", + ["school"] = {SCHOOL_YAVANNA}, + ["level"] = 35, + ["mana"] = 250, + ["mana_max"] = 350, + ["fail"] = 95, + -- Uses piety to cast + ["piety"] = TRUE, + ["stat"] = A_WIS, + ["random"] = SKILL_SPIRITUALITY, + ["spell"] = function() + local m_idx, x, y, c_ptr, ret, dir + + ret, dir = get_rep_dir() + if ret == FALSE then return end + y, x = explode_dir(dir) + y, x = y + player.py, x + player.px + c_ptr = cave(y, x) + + if c_ptr.feat == FEAT_TREES then + cave_set_feat(y, x, FEAT_GRASS); + + -- Summon it + y, x = find_position(y, x) + m_idx = place_monster_one(y, x, test_monster_name("Ent"), 0, FALSE, MSTATUS_FRIEND) + + -- level it + if m_idx ~= 0 then + monster_set_level(m_idx, 30 + get_level(YAVANNA_UPROOT, 70)) + end + + msg_print("The tree awakes!"); + else + msg_print("There is no tree there.") + end + return TRUE + end, + ["info"] = function() + return "lev "..(30 + get_level(YAVANNA_UPROOT, 70)) + end, + ["desc"] = { + "Awakes a tree to help you battle the forces of Morgoth", + } +} diff --git a/lib/scpt/spells.lua b/lib/scpt/spells.lua new file mode 100644 index 00000000..8727d282 --- /dev/null +++ b/lib/scpt/spells.lua @@ -0,0 +1,475 @@ +-- +-- This file takes care of the schools of magic +-- + +-- Create the schools +SCHOOL_MANA = add_school +{ + ["name"] = "Mana", + ["skill"] = SKILL_MANA, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, + ["gods"] = + { + -- Eru Iluvatar provides the Mana school at half the prayer skill + [GOD_ERU] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 1, + ["div"] = 2, + }, + }, + ["hooks"] = + { + [HOOK_CALC_MANA] = function(msp) + if get_skill(SKILL_MANA) >= 35 then + msp = msp + (msp * ((get_skill(SKILL_MANA) - 34)) / 100) + return TRUE, msp + end + end + }, +} +SCHOOL_FIRE = add_school +{ + ["name"] = "Fire", + ["skill"] = SKILL_FIRE, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, +} +SCHOOL_AIR = add_school +{ + ["name"] = "Air", + ["skill"] = SKILL_AIR, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + if get_skill(SKILL_AIR) >= 50 then + player.magical_breath = TRUE + end + end, + }, + ["gods"] = + { + -- Manwe Sulimo provides the Air school at 2/3 the prayer skill + [GOD_MANWE] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 2, + ["div"] = 3, + }, + }, +} +SCHOOL_WATER = add_school +{ + ["name"] = "Water", + ["skill"] = SKILL_WATER, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, + ["hooks"] = + { + [HOOK_CALC_BONUS] = function() + if get_skill(SKILL_WATER) >= 30 then + player.water_breath = TRUE + end + end, + }, + ["gods"] = + { + -- Yavanna Kementari provides the Water school at 1/2 the prayer skill + [GOD_YAVANNA] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 1, + ["div"] = 2, + }, + }, +} +SCHOOL_EARTH = add_school +{ + ["name"] = "Earth", + ["skill"] = SKILL_EARTH, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, + ["gods"] = + { + -- Tulkas provides the Earth school at 4/5 the prayer skill + [GOD_TULKAS] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 4, + ["div"] = 5, + }, + -- Yavanna Kementari provides the Earth school at 1/2 the prayer skill + [GOD_YAVANNA] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 1, + ["div"] = 2, + }, + }, +} +SCHOOL_CONVEYANCE = add_school +{ + ["name"] = "Conveyance", + ["skill"] = SKILL_CONVEYANCE, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, + ["gods"] = + { + -- Manwe Sulimo provides the Conveyance school at 1/2 the prayer skill + [GOD_MANWE] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 1, + ["div"] = 2, + }, + }, +} +SCHOOL_GEOMANCY = add_school +{ + ["name"] = "Geomancy", + ["skill"] = SKILL_GEOMANCY, + ["spell_power"] = TRUE, + -- Require to wield a Mage Staff, as the spells requries the caster to stomp the floor with it + ["depend"] = function() + -- Require at least one point in each school + if get_skill(SKILL_FIRE) == 0 then return end + if get_skill(SKILL_AIR) == 0 then return end + if get_skill(SKILL_EARTH) == 0 then return end + if get_skill(SKILL_WATER) == 0 then return end + + local obj = get_object(INVEN_WIELD) + if (obj.k_idx > 0) and (obj.tval == TV_MSTAFF) then return TRUE end + end, +} +SCHOOL_DIVINATION = add_school +{ + ["name"] = "Divination", + ["skill"] = SKILL_DIVINATION, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, + ["gods"] = + { + -- Eru Iluvatar provides the Divination school at 2/3 the prayer skill + [GOD_ERU] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 2, + ["div"] = 3, + }, + }, +} +SCHOOL_TEMPORAL = add_school +{ + ["name"] = "Temporal", + ["skill"] = SKILL_TEMPORAL, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, + ["gods"] = + { + -- Yavanna Kementari provides the Temoral school at 1/6 the prayer skill + [GOD_YAVANNA] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 1, + ["div"] = 6, + }, + }, +} +SCHOOL_NATURE = add_school +{ + ["name"] = "Nature", + ["skill"] = SKILL_NATURE, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, + ["gods"] = + { + -- Yavanna Kementari provides the Nature school at 1/2 the prayer skill + [GOD_YAVANNA] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 1, + ["div"] = 2, + }, + }, +} +SCHOOL_META = add_school +{ + ["name"] = "Meta", + ["skill"] = SKILL_META, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, + ["gods"] = + { + -- Manwe Sulimo provides the Meta school at 1/3 the prayer skill + [GOD_MANWE] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 1, + ["div"] = 3, + }, + }, +} +SCHOOL_MIND = add_school +{ + ["name"] = "Mind", + ["skill"] = SKILL_MIND, + ["spell_power"] = TRUE, + ["sorcery"] = TRUE, + ["gods"] = + { + -- Eru Iluvatar provides the Mind school at 1/3 the prayer skill + [GOD_ERU] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 1, + ["div"] = 3, + }, + -- Melkor Bauglir provides the Mind school at 1/3 the prayer skill + [GOD_MELKOR] = + { + ["skill"] = SKILL_PRAY, + ["mul"] = 1, + ["div"] = 3, + }, + }, +} +SCHOOL_UDUN = add_school +{ + ["name"] = "Udun", + ["skill"] = SKILL_UDUN, + ["bonus_level"] = function() + return ((player.lev * 2) / 3) + end, +} +SCHOOL_DEMON = add_school +{ + ["name"] = "Demon", + ["skill"] = SKILL_DAEMON, + ["no_random"] = TRUE, +} + +-- The God specific schools, all tied to the prayer skill +SCHOOL_ERU = add_school +{ + ["name"] = "Eru Iluvatar", + ["skill"] = SKILL_PRAY, + ["spell_power"] = TRUE, + ["god"] = GOD_ERU, +} +SCHOOL_MANWE = add_school +{ + ["name"] = "Manwe Sulimo", + ["skill"] = SKILL_PRAY, + ["spell_power"] = TRUE, + ["god"] = GOD_MANWE, +} +SCHOOL_TULKAS = add_school +{ + ["name"] = "Tulkas", + ["skill"] = SKILL_PRAY, + ["spell_power"] = TRUE, + ["god"] = GOD_TULKAS, +} +SCHOOL_MELKOR = add_school +{ + ["name"] = "Melkor Bauglir", + ["skill"] = SKILL_PRAY, + ["spell_power"] = TRUE, + ["god"] = GOD_MELKOR, +} +SCHOOL_YAVANNA = add_school +{ + ["name"] = "Yavanna Kementari", + ["skill"] = SKILL_PRAY, + ["spell_power"] = TRUE, + ["god"] = GOD_YAVANNA, +} + +-- Not a real school, rather a palcehodler for stick only spells +SCHOOL_DEVICE = add_school +{ + ["name"] = "Device", + ["skill"] = SKILL_DEVICE, +} + +-- Music "spells" +SCHOOL_MUSIC = add_school +{ + ["name"] = "Music", + ["skill"] = SKILL_MUSIC, +} + +-- Put some spells +tome_dofile("s_fire.lua") +tome_dofile("s_mana.lua") +tome_dofile("s_water.lua") +tome_dofile("s_air.lua") +tome_dofile("s_earth.lua") +tome_dofile("s_convey.lua") +tome_dofile("s_nature.lua") +tome_dofile("s_divin.lua") +tome_dofile("s_tempo.lua") +tome_dofile("s_meta.lua") +tome_dofile("s_mind.lua") +tome_dofile("s_udun.lua") +tome_dofile("s_geom.lua") + +-- God's specific spells +tome_dofile("s_eru.lua") +tome_dofile("s_manwe.lua") +tome_dofile("s_tulkas.lua") +tome_dofile("s_melkor.lua") +tome_dofile("s_yavann.lua") + +-- Specific schools +tome_dofile("s_demon.lua") + +-- Device spells +tome_dofile("s_stick.lua") + +-- Musics +tome_dofile("s_music.lua") + +-- List of spellbooks + +-- Create the crystal of mana +school_book[0] = { + MANATHRUST, DELCURSES, RESISTS, MANASHIELD, +} + +-- The book of the eternal flame +school_book[1] = { + GLOBELIGHT, FIREGOLEM, FIREFLASH, FIREWALL, FIERYAURA, +} + +-- The book of the blowing winds +school_book[2] = { + NOXIOUSCLOUD, POISONBLOOD, INVISIBILITY, STERILIZE, AIRWINGS, THUNDERSTORM, +} + +-- The book of the impenetrable earth +school_book[3] = { + STONESKIN, DIG, STONEPRISON, SHAKE, STRIKE, +} + +-- The book of the unstopable wave +school_book[4] = { + GEYSER, VAPOR, ENTPOTION, TIDALWAVE, ICESTORM +} + +-- Create the book of translocation +school_book[5] = { + BLINK, DISARM, TELEPORT, TELEAWAY, RECALL, PROBABILITY_TRAVEL, +} + +-- Create the book of the tree +school_book[6] = { + GROWTREE, HEALING, RECOVERY, REGENERATION, SUMMONANNIMAL, +} + +-- Create the book of Knowledge +school_book[7] = { + SENSEMONSTERS, SENSEHIDDEN, REVEALWAYS, IDENTIFY, VISION, STARIDENTIFY, +} + +-- Create the book of the Time +school_book[8] = { + MAGELOCK, SLOWMONSTER, ESSENCESPEED, BANISHMENT, +} + +-- Create the book of meta spells +school_book[9] = { + RECHARGE, DISPERSEMAGIC, SPELLBINDER, TRACKER, INERTIA_CONTROL, +} + +-- Create the book of the mind +school_book[10] = { + CHARM, CONFUSE, ARMOROFFEAR, STUN, +} + +-- Create the book of hellflame +school_book[11] = { + DRAIN, GENOCIDE, WRAITHFORM, FLAMEOFUDUN, +} + +-- Create the book of eru +school_book[20] = { + ERU_SEE, ERU_LISTEN, ERU_UNDERSTAND, ERU_PROT, +} + +-- Create the book of manwe +school_book[21] = { + MANWE_BLESS, MANWE_SHIELD, MANWE_CALL, MANWE_AVATAR, +} + +-- Create the book of tulkas +school_book[22] = { + TULKAS_AIM, TULKAS_SPIN, TULKAS_WAVE, +} + +-- Create the book of melkor +school_book[23] = { + MELKOR_CURSE, MELKOR_CORPSE_EXPLOSION, MELKOR_MIND_STEAL, +} + +-- Create the book of yavanna +school_book[24] = { + YAVANNA_CHARM_ANIMAL, YAVANNA_GROW_GRASS, YAVANNA_TREE_ROOTS, YAVANNA_WATER_BITE, YAVANNA_UPROOT, +} + +-- Create the book of beginner's cantrip +school_book[50] = { + MANATHRUST, GLOBELIGHT, ENTPOTION, BLINK, SENSEMONSTERS, SENSEHIDDEN, +} + +-- Create the book of teleporatation +school_book[51] = { + BLINK, TELEPORT, TELEAWAY +} + +-- Create the book of summoning +school_book[52] = { + FIREGOLEM, SUMMONANNIMAL +} + + +-- Create the Armageddon Demonblade +school_book[55] = { + DEMON_BLADE, DEMON_MADNESS, DEMON_FIELD, +} + +-- Create the Shield Demonblade +school_book[56] = { + DOOM_SHIELD, DEMON_CLOAK, UNHOLY_WORD, +} + +-- Create the Control Demonblade +school_book[57] = { + DEMON_SUMMON, DISCHARGE_MINION, CONTROL_DEMON, +} + +-- Create the Drums +school_book[58] = { + MUSIC_STOP, MUSIC_HOLD, MUSIC_CONF, MUSIC_STUN, +} + +-- Create the Harps +school_book[59] = { + MUSIC_STOP, MUSIC_LITE, MUSIC_HERO, MUSIC_HEAL, MUSIC_TIME, MUSIC_MIND, +} + +-- Create the Horns +school_book[60] = { + MUSIC_STOP, MUSIC_BLOW, MUSIC_WIND, MUSIC_YLMIR, MUSIC_AMBARKANTA, +} + +-- Book of the Player, filled in by the Library Quest +school_book[61] = { } + +-- Geomancy spells, not a real book +school_book[62] = { + CALL_THE_ELEMENTS, CHANNEL_ELEMENTS, ELEMENTAL_WAVE, VAPORIZE, GEOLYSIS, DRIPPING_TREAD, GROW_BARRIER, ELEMENTAL_MINION +} diff --git a/lib/scpt/stores.lua b/lib/scpt/stores.lua new file mode 100644 index 00000000..713b3c01 --- /dev/null +++ b/lib/scpt/stores.lua @@ -0,0 +1,151 @@ +-- Whats shops can buy what +store_buy_list +{ + ["General Store"] = + { + TV_CORPSE, + TV_FOOD, + TV_LITE, + TV_FLASK, + TV_SPIKE, + TV_SHOT, + TV_ARROW, + TV_BOLT, + TV_DIGGING, + TV_CLOAK, + TV_BOTTLE, + }, + ["Armoury"] = + { + TV_BOOTS, + TV_GLOVES, + TV_CROWN, + TV_HELM, + TV_SHIELD, + TV_CLOAK, + TV_SOFT_ARMOR, + TV_HARD_ARMOR, + TV_DRAG_ARMOR, + }, + ["Weaponsmith"] = + { + TV_SHOT, + TV_BOLT, + TV_ARROW, + TV_BOOMERANG, + TV_BOW, + TV_DIGGING, + TV_HAFTED, + TV_POLEARM, + TV_SWORD, + TV_AXE, + TV_MSTAFF, + }, + -- We use a function because we want to restrict to blessed weapons and god spells + ["Temple"] = function (obj) + if obj.tval == TV_DRUID_BOOK then return TRUE + elseif obj.tval == TV_BOOK and obj.sval == 255 and (can_spell_random(obj.pval) == SKILL_SPIRITUALITY) then return TRUE + elseif obj.tval == TV_SCROLL then return TRUE + elseif obj.tval == TV_POTION2 then return TRUE + elseif obj.tval == TV_POTION then return TRUE + elseif obj.tval == TV_HAFTED then return TRUE + elseif obj.tval == TV_POLEARM and is_blessed(obj) == TRUE then return TRUE + elseif obj.tval == TV_SWORD and is_blessed(obj) == TRUE then return TRUE + elseif obj.tval == TV_AXE and is_blessed(obj) == TRUE then return TRUE + elseif obj.tval == TV_BOOMERANG and is_blessed(obj) == TRUE then return TRUE + end + end, + ["Alchemy shop"] = + { + TV_SCROLL, + TV_POTION2, + TV_POTION, + TV_BATERIE, + TV_BOTTLE, + }, + -- We use a function because we dont want god spells + ["Magic shop"] = function (obj) + local buy = + { + [TV_SYMBIOTIC_BOOK] = TRUE, + [TV_AMULET] = TRUE, + [TV_RING] = TRUE, + [TV_STAFF] = TRUE, + [TV_WAND] = TRUE, + [TV_ROD] = TRUE, + [TV_ROD_MAIN] = TRUE, + [TV_SCROLL] = TRUE, + [TV_POTION2] = TRUE, + [TV_POTION] = TRUE, + [TV_MSTAFF] = TRUE, + [TV_RANDART] = TRUE, + } + + if obj.tval == TV_BOOK and obj.sval == 255 and (can_spell_random(obj.pval) == SKILL_MAGIC) then return TRUE + elseif obj.tval == TV_BOOK and obj.sval ~= 255 then return TRUE + elseif buy[obj.tval] == TRUE then return TRUE + end + end, + -- Black markets wants ALL! + ["Black Market"] = function (obj) + return TRUE + end, + ["Book Store"] = + { + TV_BOOK, + TV_SYMBIOTIC_BOOK, + TV_MUSIC_BOOK, + TV_DAEMON_BOOK, + TV_DRUID_BOOK, + }, + ["Pet Shop"] = + { + TV_EGG, + }, +} + +-- Test only +function out_sticks() + local i + for i = 0, __tmp_spells_num - 1 do + if __tmp_spells[i].stick then + if __tmp_spells[i].stick[TV_WAND] then + print("Wand: " .. __tmp_spells[i].name) + end + end + end + for i = 0, __tmp_spells_num - 1 do + if __tmp_spells[i].stick then + if __tmp_spells[i].stick[TV_STAFF] then + print("Staff: " .. __tmp_spells[i].name) + end + end + end +end + +-- Take care to have Magic shop/Temple have specific spells only +add_hooks +{ + [HOOK_STORE_STOCK] = function (index, name, level) + if name == "Magic shop" then + -- Books + if magik(20) == TRUE then + object_prep(obj_forge, lookup_kind(TV_BOOK, 255)) + local spell = get_random_spell(SKILL_MAGIC, 20) + if spell > -1 then + obj_forge.pval = spell + return TRUE, obj_forge + end + end + elseif name == "Temple" then + if magik(20) == TRUE then + object_prep(obj_forge, lookup_kind(TV_BOOK, 255)) + local spell = get_random_spell(SKILL_SPIRITUALITY, 20) + if spell > -1 then + obj_forge.pval = spell + return TRUE, obj_forge + end + end + end + end, +} diff --git a/lib/scpt/test.lua b/lib/scpt/test.lua new file mode 100644 index 00000000..81dd5c03 --- /dev/null +++ b/lib/scpt/test.lua @@ -0,0 +1,364 @@ +-- +-- This file takes care of providing the Shiny-Test class +-- with .. erm .. powerful spells +-- + +------------------------------ MAGICTYPE -- M KEY --------------------------------- + +zog_magic = add_magic +{ + ["fail"] = function(chance) + msg_print("So bad, we had "..chance.." chances to succeed.") + msg_print("Hooo bad luck the spell backfires !.") + take_hit("stupidity", 5) + end, + ["stat"] = A_CON, + -- Must return a number between 0 and 50 representing a level + ["get_level"] = function() + return get_skill_scale(SKILL_MAGIC, 25) + get_skill_scale(SKILL_SPIRITUALITY, 25) + end, + ["spell_list"] = + { + { + ["name"] = "Zog1", + ["desc"] = "dessssc zog1", + ["mana"] = 1, + ["level"] = 1, + ["fail"] = 10, + ["spell"] = function() + local ret, dir + -- Get a direction + ret, dir = get_aim_dir(); + if (ret == FALSE) then return end + fire_ball(GF_MANA, dir, 2000, 10) + end, + ["info"] = function() + return " dam 2000" + end, + }, + { + ["name"] = "Zog2", + ["desc"] = "dessssc zog2", + ["mana"] = 3, + ["level"] = 3, + ["fail"] = 30, + ["spell"] = function() + local ret, item, obj, o_name + + -- Ask for an item + ret, item = get_item("What to uber-ize?", + "You have nothing you can uber-ize", + bor(USE_INVEN, USE_EQUIP), + function (obj) + if (obj.tval == TV_HAFTED) or (obj.tval == TV_SWORD) or (obj.tval == TV_POLEARM) or (obj.tval == TV_AXE) then + return TRUE + end + return FALSE + end + ) + + if ret == TRUE then + -- get the item + obj = get_object(item) + -- modify it + obj.dd = 255 + obj.ds = 255 + obj.to_d = 1000 + obj.to_h = 1000 + + -- get the name + o_name = object_desc(obj, FALSE, 0); + msg_print("Your "..o_name.." is hit by a pure wave of uber-ification!") + end + end, + ["info"] = function() + return " cooool" + end, + }, + { + ["name"] = "Zog3", + ["desc"] = "dessssc zog3", + ["mana"] = 4, + ["level"] = 5, + ["fail"] = 50, + ["spell"] = function() + local list = {[1] = "Novice Warrior", [2] = "Novice Mage"} + local x, y, num, max + + num = rand_range(1, 2) + max = damroll(1, 2) + while (max > 0) do + y, x = find_position(player.py, player.px) + place_monster_one(y, x, test_monster_name(list[num]), 0, FALSE, MSTATUS_FRIEND) + max = max - 1 + end + end, + ["info"] = function() + return " summons 1d2 monsters" + end, + }, + }, +} + +-- Register a new magic type +MKEY_SHINY_TEST = 1000 +add_mkey +{ + ["mkey"] = MKEY_SHINY_TEST, + ["fct"] = function() + execute_magic(zog_magic) + + -- use up some energy + energy_use = energy_use + 100; + end +} + + +------------------------------ EXTRA POWERS --------------------------------- + + +-- Register a new power (the 'U' menu) +POWER_TEST = add_power +{ + ["name"] = "Test power", + ["desc"] = "You are a shinny test", + ["desc_get"] = "You become a shinny test", + ["desc_lose"] = "You are no more a shinny test", + ["level"] = 1, + ["cost"] = 5, + ["stat"] = A_CON, + ["fail"] = 6, + ["power"] = function() + msg_print("Zogzog !") + end, +} + + +---- tests + +function test_write() + local conn = zsock:new_connection() + zsock:setup(conn, "192.168.0.200", 2262, ZSOCK_TYPE_TCP, FALSE) + zsock:open(conn) + + local res, len = zsock:write(conn, "footest", FALSE) + msg_print("res "..res.." :: len "..len) + + zsock:close(conn) + zsock:unsetup(conn) + zsock:free_connection(conn); +end + +function test_read() + local conn = zsock:new_connection() + zsock:setup(conn, "192.168.0.200", 2262, ZSOCK_TYPE_TCP, FALSE) + zsock:open(conn) + + + zsock:wait(conn, 50) + local res, str, len = zsock:read(conn, 9, TRUE) + msg_print("res "..res.." :: len "..len.." '"..str.."'") + + zsock:close(conn) + zsock:unsetup(conn) + zsock:free_connection(conn); +end + + +-- A level generator being tested + +CORRIDOR = 1 +ROOM = 2 + +possible_walls = {} + +function level_generator_dungeon2_room(feat, y, x, h, w) + if feat == CORRIDOR then + -- Add the possible walls + for i = x, x + w - 1 do + tinsert(possible_walls, {y, i}) + tinsert(possible_walls, {y + h - 1, i}) + end + for i = x, x + w - 1 do + tinsert(possible_walls, {x, i}) + tinsert(possible_walls, {x, i + h - 1}) + end + + for i = x + 1, x + w - 2 do + for j = y + 1, y + h - 2 do + place_floor(j, i) + end + end + else + -- Add the possible walls + for i = x, x + w - 1 do + tinsert(possible_walls, {y, i}) + tinsert(possible_walls, {y + h - 1, i}) + end + for i = x, x + w - 1 do + tinsert(possible_walls, {x, i}) + tinsert(possible_walls, {x, i + h - 1}) + end + + for i = x, x + w - 1 do + for j = y, y + h - 1 do + cave(j, i).feat = 56 + end + end + for i = x + 1, x + w - 2 do + for j = y + 1, y + h - 2 do + place_floor(j, i) + end + end + end +end + +function select_feature(dir) + if magik(30) == TRUE then + return ROOM, rand_range(5, 12), rand_range(7, 17) + + -- Corridor selection + elseif dir == "up" or dir == "down" then + return CORRIDOR, rand_range(5, 17), 3 + else + return CORRIDOR, 3, rand_range(3, 15) + end +end + +function put_feature(feat, y, x, h, w) + level_generator_dungeon2_room(feat, y, x, h, w) +end + +function can_feature(y, x, h, w) + for i = x, x + w - 1 do + for j = y, y + h - 1 do + if j <= 0 or i <= 0 or i >= cur_wid - 1 or j >= cur_hgt - 1 then return nil end + + if cave_is(cave(j, i), FF1_WALL) == FALSE then return nil end + end + end + return not nil +end + +function is_near_wall(y, x) + if y <= 0 or x <= 0 or x >= cur_wid - 1 or y >= cur_hgt - 1 then return nil end + + if cave_is(cave(y, x), FF1_WALL) == FALSE then return nil end + if cave_is(cave(y - 1, x), FF1_FLOOR) == TRUE then return "down" end + if cave_is(cave(y + 1, x), FF1_FLOOR) == TRUE then return "up" end + if cave_is(cave(y, x - 1), FF1_FLOOR) == TRUE then return "right" end + if cave_is(cave(y, x + 1), FF1_FLOOR) == TRUE then return "left" end + return nil +end + +function find_spot() +--[[ local y, x = 1, 1 + + while not is_near_wall(y, x) do + y, x = rand_range(1, cur_hgt - 2), rand_range(1, cur_wid - 2) + end]] + local i = rand_range(1, getn(possible_walls)) + local y, x = possible_walls[i][1], possible_walls[i][2] + + while not is_near_wall(y, x) do + i = rand_range(1, getn(possible_walls)) + y, x = possible_walls[i][1], possible_walls[i][2] + end + + tremove(possible_walls, i) + return is_near_wall(y, x), y, x +end + +function adjust_dir(dir, y, x, h, w) + if dir == "up" then + y = y - (h - 1) + x = x - (w / 2) + elseif dir == "down" then + x = x - (w / 2) + elseif dir == "left" then + y = y - (h / 2) + x = x - (w - 1) + elseif dir == "up" then + y = y - (h / 2) + end + return y, x +end + +level_generator +{ + ["name"] = "dungeon2", + ["stairs"] = FALSE, + ["monsters"] = FALSE, + ["objects"] = FALSE, + ["miscs"] = FALSE, + ["gen"] = function() + for i = 1, cur_wid - 1 do + for j = 1, cur_hgt - 1 do + place_filler(j, i) + end + end + + + -- the first room + level_generator_dungeon2_room(ROOM, cur_hgt / 2, cur_wid / 2, rand_range(3, 10), rand_range(5, 15)) + + + -- Place 10 features + for nb = 1, 0 do + -- Find a spot near an empty space + local dir, y, x = find_spot() + + local feat, h, w = select_feature(dir) + + local sy, sx = adjust_dir(dir, y, x, h, w) + + if can_feature(sy, sx, h, w) then + put_feature(feat, sy, sx, h, w) + cave(y, x).feat = 32 + end + end + + player.py = cur_hgt / 2 + player.px = cur_wid / 2 + return TRUE + end, +} + + +-- exmaple of display_list +function input_list_example() + local list = { "a", "b", "c", "d", "e", "f", "h", "g" } + local sel = 1 + local begin = 1 + local res = nil + + screen_save() + + while not nil do + display_list(1, 0, 5, 9, "select", list, begin, sel, TERM_L_BLUE) + + local key = inkey() + + if key == ESCAPE then break + elseif key == strbyte("8") then + sel = sel - 1 + if sel < 1 then sel = 1 end + if sel < begin then + begin = begin - 1 + end + elseif key == strbyte("2") then + sel = sel + 1 + if sel > getn(list) then sel = getn(list) end + if sel >= begin + 4 then + begin = begin + 1 + end + elseif key == strbyte("\r") then + res = list[sel] + break + end + end + + screen_load() + + if res then msg_print("Selected: " .. res) end +end |