'OHRRPGCE GAME - Various unsorted routines '(C) Copyright 1997-2005 James Paige and Hamster Republic Productions 'Please read LICENSE.txt for GPL License details and disclaimer of liability 'See README.txt for code docs and apologies for crappyness of this code ;) #include "config.bi" #include "udts.bi" #include "misc.bi" #include "allmodex.bi" #include "common.bi" #include "gglobals.bi" #include "const.bi" #include "scrconst.bi" #include "uiconst.bi" #include "loading.bi" #include "slices.bi" #include "savegame.bi" #include "game.bi" #include "walkabouts.bi" #include "scriptcommands.bi" #include "yetmore2.bi" #include "moresubs.bi" #include "menustuf.bi" #include "bmodsubs.bi" #include "purchase.bi" #include "scripting.bi" '--Local subs and functions DECLARE SUB teleporttool_load_map (map as integer, maptiles2() as TileMap, pass2 as TileMap, tilesets2() as TilesetData ptr) DECLARE SUB teleporttool_generate_minimap(byref mini as Frame Ptr, maptilesX() as TileMap, passX as TileMap, tilesetsX() as TilesetData ptr, byref zoom as integer, byref maxzoom as integer, byref mapsize as XYPair, byref minisize as XYPair, byref offset as XYPair, byref camera as XYPair, dest as XYPair) DECLARE SUB inventory_overflow_handler(byval item_id as integer, byval numitems as integer) DECLARE SUB hero_swap_menu_init(st as OrderTeamState) DECLARE SUB hero_swap_menu_display (st as OrderTeamState) 'who is the hero id + 1! SUB addhero (byval who as integer, byval slot as integer, byval forcelevel as integer=-1) DIM her as HeroDef '--load hero's data loadherodata her, who - 1 '--do level forcing IF forcelevel >= 0 THEN her.def_level = forcelevel '--do average level enforcement IF her.def_level < 0 THEN her.def_level = averagelev '--formally add hero gam.hero(slot).id = who - 1 '---MUST SET DEFAULT EQUIP--- FOR i as integer = 0 TO 4 eqstuf(slot, i) = 0 NEXT i eqstuf(slot, 0) = her.def_weapon + 1 '--fill in stats WITH gam.hero(slot).stat FOR statnum as integer = 0 TO statLast .base.sta(statnum) = atlevel(her.def_level, her.Lev0.sta(statnum), her.LevMax.sta(statnum)) NEXT recompute_hero_max_stats slot FOR statnum as integer = 0 TO statLast .cur.sta(statnum) = .max.sta(statnum) NEXT END WITH '--weapon picture and palette DIM wbuf(dimbinsize(binITM)) as integer loaditemdata wbuf(), her.def_weapon gam.hero(slot).wep_pic = wbuf(52) gam.hero(slot).wep_pal = wbuf(53) '--put spells in spell list FOR i as integer = 0 TO 3 FOR o as integer = 0 TO 23 spell(slot, i, o) = 0 IF her.spell_lists(i,o).attack > 0 AND her.spell_lists(i,o).learned - 1 <= her.def_level AND her.spell_lists(i,o).learned > 0 THEN spell(slot, i, o) = her.spell_lists(i,o).attack NEXT o NEXT i '--damage from elements FOR i as integer = 0 TO gen(genNumElements) - 1 gam.hero(slot).elementals(i) = her.elementals(i) NEXT '--mutable hero bits (not actually mutable) gam.hero(slot).rename_on_status = readbit(her.bits(), 0, 25) '--reset levelmp resetlmp slot, her.def_level '--setup experience gam.hero(slot).exp_mult = her.exp_mult gam.hero(slot).lev = her.def_level gam.hero(slot).lev_gain = 0 gam.hero(slot).exp_cur = 0 gam.hero(slot).exp_next = exptolevel(her.def_level + 1, her.exp_mult) '--clear learnmask slots (just to be really thorough) FOR i as integer = slot * 6 TO slot * 6 + 5 learnmask(i) = 0 NEXT '--heros are added unlocked gam.hero(slot).locked = NO '--appearance settings ' udts are self documenting WITH gam.hero(slot) .battle_pic = her.sprite .battle_pal = her.sprite_pal .pic = her.walk_sprite .pal = her.walk_sprite_pal .def_wep = her.def_weapon + 1 'default weapon FOR i as integer = 0 to 1 .hand_pos(i).x = her.hand_pos(i).x .hand_pos(i).y = her.hand_pos(i).y NEXT i .portrait_pic = her.portrait .portrait_pal = her.portrait_pal END WITH '--read hero's name (doing this last for no real reason) gam.hero(slot).name = her.name '--if renaming is permitted, do it IF readbit(her.bits(), 0, 24) THEN '--add-hero rename is allowed renamehero slot, NO END IF '--update tags party_change_updates END SUB FUNCTION averagelev () as integer DIM average as integer = 0 DIM count as integer = 0 FOR i as integer = 0 TO 3 IF gam.hero(i).id >= 0 THEN average += gam.hero(i).lev: count += 1 NEXT i IF count > 0 THEN average = average / count RETURN average END FUNCTION SUB calibrate DIM state as integer = 0 DIM state_str as string = "Center Joystick and Press Button" DIM midx as integer = 0 DIM midy as integer = 0 DIM button as integer = 0 DIM disabled as integer = 10 DIM tog as integer DIM tx as integer DIM ty as integer setkeys DO setwait speedcontrol setkeys tog = tog XOR 1 IF keyval(scEsc) > 1 THEN EXIT DO FOR i as integer = 0 TO 1 IF readjoy(joy(), i) THEN EXIT FOR NEXT i SELECT CASE button CASE 0'no button IF joy(3) = 0 THEN joy(13) = 3: joy(14) = 2: button = 1 IF joy(2) = 0 THEN joy(13) = 2: joy(14) = 3: button = 1 CASE 1'button down IF joy(2) <> 0 AND joy(3) <> 0 THEN button = 2 CASE 2 button = 0 END SELECT 'button = IIF(keyval(scB) AND 4, 2, 0) 'simulate joystick button with B disabled = disabled - SGN(disabled) SELECT CASE state CASE 0 IF (button = 2) AND (disabled = 0) THEN midx = joy(0) midy = joy(1) state_str = "Push UP and Press Button" tx = 160 ty = 45 state = 1 END IF CASE 1 IF button = 2 THEN joy(9) = joy(1) + (midy - joy(1)) * .33 state_str = "Push DOWN and Press Button" ty = 155 state = 2 END IF CASE 2 IF button = 2 THEN joy(10) = joy(1) - (joy(1) - midy) * .33 state_str = "Push LEFT and Press Button" tx = 50 ty = 110 state = 3 END IF CASE 3 IF button = 2 THEN joy(11) = joy(0) + (midx - joy(0)) * .33 state_str = "Push RIGHT and Press Button" tx = 260 state = 4 END IF CASE 4 IF button = 2 THEN joy(12) = joy(0) - (joy(0) - midx) * .33 state_str = "Press the USE button" state = 5 END IF CASE 5 IF button = 2 THEN disabled = 4 state_str = "" state = 6 END IF CASE 6 IF disabled = 0 THEN writejoysettings gen(genJoy) = 1 EXIT DO END IF END SELECT clearpage dpage centerbox 160, 100, 100, 80, 1, dpage centerbox 160, 100, 20, 20, 3, dpage IF state > 0 THEN centerbox 160 + (joy(0) - midx) * .1, 100 + (joy(1) - midy) * .1, 10, 10, 15, dpage END IF IF state > 0 AND state < 5 THEN edgeprint "This way!", tx - 36, ty - 5, uilook(uiDescription), dpage END IF edgeprint "Calibrate Joystick", 88, 8, uilook(uiText), dpage edgeprint state_str, 160 - 4 * LEN(state_str), 174, uilook(uiSelectedItem + tog), dpage edgeprint "X=" & joy(0) & " Y=" & joy(1), 160 - 4 * LEN("X=" & joy(0) & " Y=" & joy(1)), 184, uilook(uiSelectedItem + tog), dpage SWAP vpage, dpage setvispage vpage dowait LOOP END SUB FUNCTION consumeitem (byval invslot as integer) as bool '--Subtracts one of an item at a location. If the item is depleted, returns true. If there are some of the item left, it returns false '--Argument is the inventory slot index, not the item ID consumeitem = 0 inventory(invslot).num -= 1 IF inventory(invslot).num <= 0 THEN inventory(invslot).used = NO consumeitem = -1 END IF update_inventory_caption invslot END FUNCTION FUNCTION countitem (byval item_id as integer) as integer DIM total as integer = 0 FOR o as integer = 0 TO last_inv_slot() IF inventory(o).used AND item_id = inventory(o).id THEN total += inventory(o).num END IF NEXT o RETURN total END FUNCTION FUNCTION count_equipped_item(byval item_id as integer) as integer DIM count as integer = 0 FOR slot as integer = 0 to 3 IF gam.hero(slot).id >= 0 THEN FOR eqslot as integer = 0 TO UBOUND(eqstuf, 2) IF eqstuf(slot, eqslot) - 1 = item_id THEN count += 1 END IF NEXT eqslot END IF NEXT slot RETURN count END FUNCTION SUB getitem (byval item_id as integer, byval num as integer=1) DIM numitems as integer = num DIM room as integer DIM stacksize as integer = get_item_stack_size(item_id) FOR i as integer = 0 TO last_inv_slot() ' Loop through all inventory slots looking for a slot that already ' contains the item we are adding. If found increment that slot room = stacksize - inventory(i).num IF inventory(i).used AND item_id = inventory(i).id AND room > 0 THEN IF room < numitems THEN inventory(i).num = stacksize update_inventory_caption i numitems -= room ELSE inventory(i).num += numitems update_inventory_caption i EXIT SUB END IF END IF NEXT i FOR i as integer = 0 TO last_inv_slot() 'loop through each inventory slot looking for an empty slot to populate IF inventory(i).used = 0 THEN inventory(i).used = -1 inventory(i).id = item_id inventory(i).num = small(numitems, stacksize) numitems -= inventory(i).num update_inventory_caption i IF numitems = 0 THEN EXIT SUB END IF NEXT 'No slot was found to put this item into! inventory_overflow_handler item_id, numitems END SUB SUB inventory_overflow_handler(byval item_id as integer, byval numitems as integer) debug "Didn't have room for " & readitemname(item_id) & "x" & numitems END SUB FUNCTION room_for_item (byval item_id as integer, byval num as integer = 1) as bool DIM room as integer DIM stacksize as integer = get_item_stack_size(item_id) FOR i as integer = 0 TO last_inv_slot() ' Loop through all inventory slots looking for a slot that already contains the item room = stacksize - inventory(i).num IF inventory(i).used AND item_id = inventory(i).id AND room > 0 THEN IF room >= num THEN RETURN YES END IF num -= room END IF NEXT FOR i as integer = 0 TO last_inv_slot() 'loop through each inventory slot looking for an empty slot to populate IF inventory(i).used = NO THEN IF num <= stacksize THEN RETURN YES END IF num -= stacksize END IF NEXT RETURN NO END FUNCTION SUB delitem (byval item_id as integer, byval amount as integer=1) FOR o as integer = 0 TO last_inv_slot() IF inventory(o).used AND item_id = inventory(o).id THEN IF inventory(o).num <= amount THEN amount -= inventory(o).num inventory(o).num = 0 inventory(o).id = 0 inventory(o).used = NO ELSE inventory(o).num -= amount amount = 0 END IF update_inventory_caption o IF amount = 0 THEN EXIT FOR END IF NEXT o END SUB SUB doswap (byval s as integer, byval d as integer) '---Spell lists FOR i as integer = 0 TO 3 FOR o as integer = 0 TO 23 SWAP spell(s, i, o), spell(d, i, o) NEXT o NEXT i '---hero state and stats SWAP gam.hero(s), gam.hero(d) '---Level-MP FOR i as integer = 0 TO 7 SWAP lmp(s, i), lmp(d, i) NEXT i '--Learnt spells flags FOR i as integer = 0 TO 5 SWAP learnmask(s * 6 + i), learnmask(d * 6 + i) NEXT '---Equipment FOR i as integer = 0 TO 4 SWAP eqstuf(s, i), eqstuf(d, i) NEXT i '--set tags, reload hero pictures and palettes, etc party_change_updates END SUB SUB update_textbox () STATIC tog as integer tog = tog XOR 1 'On the first tick, show_lines is -1 IF txt.fully_shown = NO THEN txt.show_lines += 1 '--play sounds for non-blank lines IF TRIM(txt.box.text(txt.show_lines)) <> "" THEN IF txt.box.line_sound > 0 THEN ' Don't subtract 1, since menusound takes sfx id + 1 menusound txt.box.line_sound ELSEIF txt.box.line_sound = 0 THEN ' Default menusound gen(genTextboxLine) ELSEIF txt.box.line_sound < 0 THEN ' Silence END IF END IF '--note when the display of lines is done IF txt.show_lines >= UBOUND(txt.box.text) THEN txt.fully_shown = YES END IF '--update the slice to show the right number of lines DIM text_sl As Slice Ptr text_sl = LookupSlice(SL_TEXTBOX_TEXT, txt.sl) IF text_sl THEN DIM dat as TextSliceData Ptr dat = text_sl->SliceData IF dat THEN dat->line_limit = txt.show_lines IF txt.fully_shown THEN dat->line_limit = -1 'Show all END IF END IF END IF END IF IF txt.box.choice_enabled THEN '--Make the selected choice flash DIM choice_sl(1) as Slice Ptr choice_sl(0) = LookupSlice(SL_TEXTBOX_CHOICE0, SliceTable.Root) choice_sl(1) = LookupSlice(SL_TEXTBOX_CHOICE1, SliceTable.Root) DIM col as integer IF choice_sl(0) <> 0 AND choice_sl(1) <> 0 THEN FOR i as integer = 0 TO 1 col = uilook(uiMenuItem) IF txt.choicestate.pt = i THEN col = uilook(uiSelectedItem + tog) ChangeTextSlice choice_sl(i), ,col NEXT i END IF END IF END SUB SUB evalherotags () DIM as integer id DIM leaderid as integer = herobyrank(0) FOR i as integer = 0 TO small(gen(genMaxHero), UBOUND(herotags)) '--for each available hero 'unset all tags, including ones used on heroes not in the party settag herotags(i).have_tag, NO settag herotags(i).alive_tag, NO settag herotags(i).leader_tag, NO settag herotags(i).active_tag, NO FOR j as integer = 0 TO v_len(herotags(i).checks) - 1 settag herotags(i).checks[j].tag, NO NEXT j NEXT i 'scan party FOR i as integer = 0 TO UBOUND(gam.hero) id = gam.hero(i).id IF id >= 0 THEN settag herotags(id).have_tag, YES IF gam.hero(i).stat.cur.hp > 0 THEN settag herotags(id).alive_tag, YES IF id = leaderid THEN settag herotags(id).leader_tag, YES IF i < 4 THEN settag herotags(id).active_tag, YES FOR j as integer = 0 TO v_len(herotags(id).checks) - 1 WITH herotags(id).checks[j] SELECT CASE .kind CASE TagRangeCheckKind.level IF gam.hero(i).lev >= .min ANDALSO gam.hero(i).lev <= .max THEN settag .tag, YES END IF CASE ELSE END SELECT END WITH NEXT j END IF NEXT i END SUB 'Call this after a change to the party SUB party_change_updates evalherotags evalitemtags 'Because of items with 'actively equipped' tags vishero tag_updates END SUB SUB evalitemtags DIM as integer id FOR i as integer = 0 TO maxMaxItems 'clear all four tags settag itemtags(i).have_tag, NO settag itemtags(i).in_inventory_tag, NO settag itemtags(i).is_equipped_tag, NO settag itemtags(i).is_actively_equipped_tag, NO NEXT i 'search inventory slots FOR j as integer = 0 TO last_inv_slot() 'get item ID id = inventory(j).id IF inventory(j).used THEN 'there is an item in this slot settag itemtags(id).have_tag, YES settag itemtags(id).in_inventory_tag, YES END IF NEXT j FOR j as integer = 0 TO 40 'search hero list FOR k as integer = 0 TO 4 'search equipment slots id = eqstuf(j, k) - 1 IF id >= 0 THEN ' there is an item equipped in this slot settag itemtags(id).have_tag, YES settag itemtags(id).is_equipped_tag, YES IF j < 4 THEN settag itemtags(id).is_actively_equipped_tag, YES END IF NEXT k NEXT j END SUB ' Find hero by ID, returning party slot or -1. ' NOTE: 'id' is the hero ID + 1! ' Pass id = -1 to find any hero. FUNCTION findhero (byval id as integer, byval first as integer, byval last as integer, byval direction as integer, errlvl as scriptErrEnum = serrIgnore) as integer FOR i as integer = first TO last STEP direction IF gam.hero(i).id + 1 = id ORELSE (id = -1 ANDALSO gam.hero(i).id >= 0) THEN RETURN i END IF NEXT i IF errlvl > serrIgnore THEN reporterr "Couldn't find hero with ID " & id & " in the party", errlvl END IF RETURN -1 'not found END FUNCTION SUB hero_swap_menu (byval reserve_too as bool) DIM st as OrderTeamState st.show_reserve = reserve_too st.reserve.pt = -1 st.reserve.last = -1 st.swapme = -1 st.party.need_update = YES '--Preserve background for display beneath the hero swapper DIM holdscreen as integer st.page = vpage holdscreen = allocatepage copypage st.page, holdscreen hero_swap_menu_init st show_virtual_gamepad() MenuSound gen(genAcceptSFX) setkeys DO setwait speedcontrol setkeys st.party.tog XOR= 1 playtimer control IF carray(ccMenu) > 1 THEN IF st.swapme >= 0 THEN MenuSound gen(genCancelSFX) st.swapme = -1 ELSE EXIT DO END IF END IF IF st.show_reserve THEN IF carray(ccUp) > 1 THEN MenuSound gen(genCursorSFX) IF st.reserve.pt < 0 THEN st.reserve.pt = st.reserve.last st.reserve.need_update = YES ELSE st.reserve.pt = loopvar(st.reserve.pt, -1, st.reserve.last, -1) st.reserve.need_update = YES END IF END IF IF carray(ccDown) > 1 THEN MenuSound gen(genCursorSFX) IF st.reserve.pt < 0 THEN st.reserve.pt = 0 st.reserve.need_update = YES ELSE st.reserve.pt = loopvar(st.reserve.pt, -1, st.reserve.last, 1) st.reserve.need_update = YES END IF END IF END IF IF st.reserve.need_update THEN st.reserve.need_update = NO IF st.reserve.pt < st.reserve.top THEN st.reserve.top = large(st.reserve.pt, 0) IF st.reserve.pt > st.reserve.top + 7 THEN st.reserve.top = st.reserve.pt - 7 st.party.need_update = YES END IF IF carray(ccLeft) > 1 AND st.reserve.pt < 0 THEN MenuSound gen(genCursorSFX) st.party.pt = loopvar(st.party.pt, 0, 3, -1) st.party.need_update = YES END IF IF carray(ccRight) > 1 AND st.reserve.pt < 0 THEN MenuSound gen(genCursorSFX) st.party.pt = loopvar(st.party.pt, 0, 3, 1) st.party.need_update = YES END IF IF carray(ccUse) > 1 THEN DO IF readbit(gen(), genBits2, 4) THEN '--If this bit is set, we refuse to reorder locked heroes IF (gam.hero(st.party.pt).locked ANDALSO gam.hero(st.party.pt).id >= 0) ORELSE (st.swapme >= 0 ANDALSO gam.hero(st.swapme).locked ANDALSO gam.hero(st.swapme).id >= 0) THEN MenuSound gen(genCancelSFX) EXIT DO END IF END IF IF st.swapme = -1 THEN MenuSound gen(genAcceptSFX) IF st.reserve.pt < 0 THEN st.swapme = st.party.pt ELSE st.swapme = 4 + st.reserve.pt END IF ELSE MenuSound gen(genAcceptSFX) DIM swap1 as integer DIM swap2 as integer DO IF st.swapme < 4 THEN IF (herocount() <= 1 ANDALSO st.reserve.pt = st.reserve.last) ORELSE (gam.hero(st.swapme).locked ANDALSO gam.hero(st.swapme).id >= 0 ANDALSO st.reserve.pt > -1) THEN EXIT DO ELSE IF st.swapme - 4 = st.reserve.last AND st.reserve.pt = -1 AND herocount() <= 1 THEN EXIT DO IF gam.hero(st.party.pt).locked ANDALSO gam.hero(st.party.pt).id >= 0 ANDALSO st.reserve.pt = -1 THEN EXIT DO END IF '---IDENTIFY DESTINATION--- IF st.reserve.pt < 0 THEN swap1 = st.party.pt ELSE swap1 = st.swindex(st.reserve.pt) END IF '---IDENTIFY SOURCE--- IF st.swapme < 4 THEN swap2 = st.swapme ELSE swap2 = st.swindex(st.swapme - 4) END IF doswap swap1, swap2 st.swapme = -1 hero_swap_menu_init st EXIT DO LOOP '--this loop just exists for convenient breaking with EXIT DO END IF EXIT DO LOOP '--this loop just exists for convenient breaking with EXIT DO END IF IF st.party.need_update THEN st.party.need_update = NO IF gam.hero(st.party.pt).id >= 0 AND st.reserve.pt < 0 THEN st.info = gam.hero(st.party.pt).name ELSE st.info = "" END IF copypage holdscreen, st.page hero_swap_menu_display st setvispage st.page dowait LOOP carray(4) = 0 carray(5) = 0 MenuSound gen(genCancelSFX) freepage holdscreen party_change_updates END SUB SUB hero_swap_menu_display (st as OrderTeamState) '--DRAWS SWAP MENU AND CURRENT SELECTION DIM centerx as integer = vpages(st.page)->w \ 2 DIM centery as integer = vpages(st.page)->h \ 2 centerbox centerx, centery - 34, 130, 38, 1, st.page DIM cater_slot as integer = 0 FOR i as integer = 0 TO 3 DIM as integer xpos = centerx - 55 + (30 * i), ypos = centery - 40 IF i = st.swapme OR gam.hero(i).id >= 0 THEN rectangle xpos, ypos, 20, 20, boxlook(0).bgcol, st.page IF gam.hero(i).id >= 0 THEN set_walkabout_frame herow(cater_slot).sl, dirDown, 0 IF i = st.swapme THEN ypos -= 6 DrawSliceAt LookupSlice(SL_WALKABOUT_SPRITE_COMPONENT, herow(cater_slot).sl), xpos, ypos, 20, 20, st.page, YES cater_slot += 1 END IF NEXT i IF st.reserve.pt < 0 THEN edgeprint CHR(24), centerx - 49 + 30 * st.party.pt, centery - 48, uilook(uiSelectedItem + st.party.tog), st.page IF st.show_reserve THEN centerbox centerx, centery + small(st.size.x, 8) * 5, st.size.y * 8 + 16, small(st.size.x, 8) * 10 + 10, 1, st.page FOR i as integer = st.reserve.top TO small(st.reserve.top + 7, st.reserve.last) 'Some of the colours are a bit bizarre, here, especially the time bar stuff below DIM menu_color as integer = uilook(uiMenuItem) IF st.swapme = i + 4 THEN menu_color = uilook(uiSelectedDisabled) IF st.reserve.pt = i THEN menu_color = uilook(uiSelectedItem + st.party.tog) IF st.swapme = i + 4 THEN menu_color = uilook(uiSelectedDisabled + st.party.tog) END IF IF st.swapme > -1 AND st.swapme < 4 THEN IF (herocount() <= 1 ANDALSO i = st.reserve.last) ORELSE (gam.hero(st.party.pt).locked ANDALSO gam.hero(st.party.pt).id >= 0) THEN menu_color = uilook(uiTimeBar + ((st.reserve.pt = i) * st.party.tog)) END IF END IF edgeprint st.swname(i), pCentered, centery + (i - st.reserve.top) * 10, menu_color, st.page NEXT i END IF IF LEN(st.info) THEN centerbox centerx, centery - 56, (LEN(st.info) + 2) * 8, 14, 1, st.page edgeprint st.info, pCentered, centery - 61, uilook(uiText), st.page END IF END SUB SUB hero_swap_menu_init(st as OrderTeamState) '--MAPS OUT ONLY VALID SWAPABLE HEROS PLUS A BLANK st.reserve.last = -1 st.size.y = 0 FOR i as integer = 4 TO 40 IF gam.hero(i).locked = NO ANDALSO gam.hero(i).id >= 0 THEN st.reserve.last = st.reserve.last + 1 st.swindex(st.reserve.last) = i st.swname(st.reserve.last) = gam.hero(i).name st.size.y = large(st.size.y, LEN(st.swname(st.reserve.last))) END IF NEXT i st.reserve.last += 1 FOR i as integer = 40 TO 4 STEP -1 IF gam.hero(i).id = -1 THEN st.swindex(st.reserve.last) = i st.swname(st.reserve.last) = readglobalstring(48, "REMOVE", 10) st.size.y = large(st.size.y, 7) END IF NEXT i st.size.x = small(8, st.reserve.last + 1) st.party.need_update = YES END SUB 'Either pass a tag number and specify YES/NO, or pass just a tag number; +ve/-ve indicates value SUB settag (byval tagnum as integer, byval value as integer = 4444) IF ABS(tagnum) <= max_tag() THEN settag tag(), tagnum, value ELSE settag onetime(), tagnum - (max_tag()+1) * SGN(tagnum), value END IF END SUB FUNCTION istag (num as integer, zero as bool=NO) as bool IF ABS(num) <= max_tag() THEN RETURN istag(tag(), num, zero) ELSE RETURN istag(onetime(), num - (max_tag()+1) * SGN(num), zero) END IF END FUNCTION 'Either pass a tag number and specify YES/NO, or pass just a tag number; +ve/-ve indicates value SUB settag (tagbits() as integer, byval tagnum as integer, byval value as integer = 4444) IF value <> 4444 THEN IF ABS(tagnum) > 1 THEN setbit tagbits(), 0, ABS(tagnum), value ELSEIF tagnum < -1 THEN setbit tagbits(), 0, ABS(tagnum), NO ELSEIF tagnum > 1 THEN setbit tagbits(), 0, tagnum, YES END IF END SUB FUNCTION istag (tagbits() as integer, num as integer, zero as bool=NO) as bool IF num = 0 THEN RETURN zero 'why go through all that just to return defaults? IF num = 1 THEN RETURN NO IF num = -1 THEN RETURN YES IF ABS(num) >= UBOUND(tagbits) * 16 + 16 THEN RETURN zero ' use default in case of an invalid tag DIM ret as integer = readbit(tagbits(), 0, ABS(num)) 'raw bit: 0 or 1 IF num > 0 AND ret <> 0 THEN RETURN YES IF num < 0 AND ret = 0 THEN RETURN YES RETURN NO END FUNCTION SUB minimap (byval x as integer, byval y as integer) DIM mini as Frame Ptr DIM zoom as integer = -1 mini = createminimap(maptiles(), tilesets(), @pass, zoom) DIM minisize as XYPair minisize.x = mini->w minisize.y = mini->h DIM offset as XYPair offset.x = rCenter - minisize.x / 2 offset.y = rCenter - minisize.y / 2 edgeboxstyle offset.x - 2, offset.y - 2, minisize.x + 4, minisize.y + 4, 0, vpage frame_draw mini, NULL, offset.x, offset.y, 1, NO, vpage frame_unload @mini MenuSound gen(genAcceptSFX) DIM tog as integer show_virtual_gamepad() setkeys DO setwait speedcontrol setkeys tog = tog XOR 1 playtimer control IF anykeypressed(YES, YES) THEN EXIT DO 'inc. joystick and mouse rectangle offset.x + (x / 20) * zoom, offset.y + (y / 20) * zoom, zoom, zoom, uilook(uiSelectedItem) * tog, vpage setvispage vpage dowait LOOP setkeys flusharray carray(), 7, 0 MenuSound gen(genCancelSFX) END SUB 'The map-teleport debug menu. 'Sets gam.map.id and hero position and returns true if the player wants to teleport. FUNCTION teleporttool () as bool 'The maptiles, passmap and tilesets for displayed map, if not the same as the current in-game map REDIM maptiles2(0) as TileMap DIM pass2 as TileMap DIM tilesets2(maplayerMax) as TilesetData ptr DIM mini as Frame Ptr DIM zoom as integer = -1 'pixels per tile DIM maxzoom as integer DIM mapsize as XYPair 'In tiles DIM minisize as XYPair 'Minimap size in pixels DIM offset as XYPair 'Top-left position where the on-screen map viewport starts DIM camera as XYPair 'In pixels DIM dest as XYPair = herotpos(0) ensure_normal_palette 'Preserve background for display beneath DIM holdscreen as integer holdscreen = duplicatepage(vpage) 'Initially use the real map's maptiles() and tilesets(), which has 'the advantage of making the map look more correct. teleporttool_generate_minimap mini, maptiles(), pass, tilesets(), zoom, maxzoom, mapsize, minisize, offset, camera, dest DIM state as MenuState state.pt = 0 state.top = 0 state.size = 22 state.first = 0 state.last = 1 state.need_update = YES DIM menuopts as MenuOptions menuopts.edged = YES DIM preview_delay as integer = 0 'Ticks before loading new map DIM pickpoint as bool = NO DIM destmap as integer = gam.map.id DIM currentmap as integer = gam.map.id DIM toggle as integer DIM menu(1) as string menu(0) = CHR(27) & "Map " & destmap & CHR(26) & " " & getmapname(destmap) menu(1) = "Position X = " & dest.x & " Y = " & dest.y teleporttool = NO MenuSound gen(genAcceptSFX) setkeys DO setwait speedcontrol setkeys toggle XOR= 1 control 'Cap to a maximum zoom because createminimap draws the whole map and is really slow IF keyval(scMinus) > 1 THEN zoom = large(1, zoom - 1) state.need_update = YES END IF IF keyval(scPlus) > 1 THEN zoom = small(maxzoom, zoom + 1) state.need_update = YES END IF IF preview_delay > 0 THEN preview_delay -= 1 IF preview_delay = 0 THEN teleporttool_load_map destmap, maptiles2(), pass2, tilesets2() state.need_update = YES zoom = -1 'Back to default END IF END IF IF state.need_update THEN state.need_update = NO IF destmap = currentmap THEN teleporttool_generate_minimap mini, maptiles(), pass, tilesets(), zoom, maxzoom, mapsize, minisize, offset, camera, dest ELSE teleporttool_generate_minimap mini, maptiles2(), pass2, tilesets2(), zoom, maxzoom, mapsize, minisize, offset, camera, dest END IF END IF IF pickpoint = NO THEN IF carray(ccMenu) > 1 THEN 'cancel EXIT DO END IF IF intgrabber(destmap, 0, gen(genMaxMap)) THEN preview_delay = 8 menu(0) = CHR(27) & "Map " & destmap & CHR(26) & " " & getmapname(destmap) END IF IF enter_or_space() THEN pickpoint = YES ELSE IF carray(ccUse) > 1 THEN 'confirm and teleport IF gam.map.id <> destmap THEN teleporttool = YES gam.map.id = destmap (heropos(0)) = dest * 20 EXIT DO END IF IF carray(ccMenu) > 1 THEN pickpoint = NO DIM xrate as integer 'Movement rate DIM yrate as integer IF keyval(scShift) > 0 THEN xrate = 8 yrate = 5 ELSE xrate = 1 yrate = 1 END IF IF slowkey(scUp, 45) THEN dest.y = large(dest.y - yrate, 0) IF slowkey(scDown, 45) THEN dest.y = small(dest.y + yrate, mapsize.y - 1) IF slowkey(scLeft, 45) THEN dest.x = large(dest.x - xrate, 0) IF slowkey(scRight, 45) THEN dest.x = small(dest.x + xrate, mapsize.x - 1) DIM temp as XYPair = camera camera.x = bound(camera.x, (dest.x + 1) * zoom + 40 - minisize.x, dest.x * zoom - 40) 'follow dest camera.y = bound(camera.y, (dest.y + 1) * zoom + 30 - minisize.y, dest.y * zoom - 30) camera.x = bound(camera.x, 0, mapsize.x * zoom - minisize.x) 'bound to map edges camera.y = bound(camera.y, 0, mapsize.y * zoom - minisize.y) END IF menu(1) = "Position X = " & dest.x & " Y = " & dest.y ' Draw screen copypage holdscreen, vpage edgeboxstyle offset.x - 2, offset.y - 2, minisize.x + 4, minisize.y + 4, 0, vpage frame_draw mini, NULL, offset.x - camera.x, offset.y - camera.y, 1, NO, vpage 'Hero cursor DIM cursor as XYPair = offset + dest * zoom - camera rectangle cursor.x, cursor.y, zoom, zoom, uilook(uiSelectedItem) * toggle, vpage state.pt = IIF(pickpoint, 1, 0) standardmenu menu(), state, 0, vpages(vpage)->h - 29, vpage, menuopts DIM caption as string = IIF(pickpoint, "Arrows/SHIFT move; ENTER to teleport", "Select map; ENTER to select position") edgeprint caption, 0, pBottom, uilook(uiText), vpage edgeprint "+/- to zoom", pRight - 4, pBottom - 9, uilook(uiText), vpage setvispage vpage dowait LOOP frame_unload @mini freepage holdscreen unloadtilemaps maptiles2() unloadtilemap pass2 unloadmaptilesets tilesets2() restore_previous_palette setkeys flusharray carray(), 7, 0 MenuSound gen(genCancelSFX) END FUNCTION 'Load enough of a map's data to display it SUB teleporttool_load_map (map as integer, maptiles2() as TileMap, pass2 as TileMap, tilesets2() as TilesetData ptr) DIM gmap2(dimbinsize(binMAP)) as integer loadrecord gmap2(), game + ".map", getbinsize(binMAP) \ 2, map loadtilemaps maptiles2(), maplumpname(map, "t") loadmaptilesets tilesets2(), gmap2() loadtilemap pass2, maplumpname(map, "p") END SUB 'Generate the minimap given a zoom factor, and determinate viewport and camera positions SUB teleporttool_generate_minimap(byref mini as Frame Ptr, maptilesX() as TileMap, passX as TileMap, tilesetsX() as TilesetData ptr, byref zoom as integer, byref maxzoom as integer, byref mapsize as XYPair, byref minisize as XYPair, byref offset as XYPair, byref camera as XYPair, dest as XYPair) mapsize.x = maptilesX(0).wide mapsize.y = maptilesX(0).high DIM screenw as integer = vpages(vpage)->w DIM screenh as integer = vpages(vpage)->h DIM screen_ratio as double = small(screenw / maptilesX(0).wide, screenh / maptilesX(0).high) IF zoom = -1 THEN 'minimum zoom level to make tiles easy to pick zoom = bound(INT(screen_ratio), 4, 20) END IF maxzoom = bound(INT(2.5 * screen_ratio), 4, 20) frame_unload @mini mini = createminimap(maptilesX(), tilesetsX(), @passX, zoom) offset.x = large(screenw \ 2 - mini->w \ 2, 0) offset.y = large(screenh \ 2 - mini->h \ 2, 0) minisize.x = screenw - offset.x * 2 minisize.y = screenh - offset.y * 2 camera.x = bound(dest.x * zoom - minisize.x \ 2, 0, mapsize.x * zoom - minisize.x) camera.y = bound(dest.y * zoom - minisize.y \ 2, 0, mapsize.y * zoom - minisize.y) END SUB FUNCTION onwho (caption as string, byval alone as bool) as integer '-- pre-select the first hero DIM w as integer = rank_to_party_slot(0) '-- if there is only one hero, return immediately '--unless we are in alone-mode IF alone = NO AND herocount() <= 1 THEN setkeys RETURN w END IF menusound gen(genAcceptSFX) copypage vpage, dpage DIM page as integer = compatpage DIM tog as integer DIM wtg as integer DIM wt as integer show_virtual_gamepad() setkeys DO setwait speedcontrol setkeys tog = tog XOR 1 playtimer control wtg = loopvar(wtg, 0, 3, 1) IF carray(ccMenu) > 1 THEN onwho = -1 menusound gen(genCancelSFX) EXIT DO END IF IF carray(ccLeft) > 1 THEN DO: w = loopvar(w, 0, 3, -1): LOOP UNTIL gam.hero(w).id >= 0 menusound gen(genCursorSFX) END IF IF carray(ccRight) > 1 THEN DO: w = loopvar(w, 0, 3, 1): LOOP UNTIL gam.hero(w).id >= 0 menusound gen(genCursorSFX) END IF IF carray(ccUse) > 1 THEN onwho = w: EXIT DO centerbox 160, 100, 140, 52, 1, page DIM cater_slot as integer = 0 FOR party_slot as integer = 0 TO 3 IF gam.hero(party_slot).id >= 0 THEN IF w = party_slot THEN wt = wtg \ 2 ELSE wt = 0 set_walkabout_frame herow(cater_slot).sl, dirDown, wt DrawSliceAt LookupSlice(SL_WALKABOUT_SPRITE_COMPONENT, herow(cater_slot).sl), 100 + party_slot * 30, 100, 20, 20, page, YES cater_slot += 1 END IF NEXT edgeprint CHR(25), 106 + w * 30, 90, uilook(uiSelectedItem + tog), page wrapprint caption, pCentered, 80, uilook(uiText), page setvispage vpage dowait LOOP freepage page setkeys flusharray carray(), 7, 0 END FUNCTION SUB readjoysettings IF isfile(exepath & SLASH & "joyset.ini") THEN '--use joyset.ini DIM fh as integer = FREEFILE OPEN exepath & SLASH & "joyset.ini" FOR INPUT as #fh DIM safety as integer = 0 DIM n as string = "" DIM a as string DO WHILE NOT EOF(fh) AND safety < 100 LINE INPUT #fh, a IF settingstring(a, "UPTHRESH", n) THEN joy(9) = str2int(n) END IF IF settingstring(a, "DOWNTHRESH", n) THEN joy(10) = str2int(n) END IF IF settingstring(a, "LEFTTHRESH", n) THEN joy(11) = str2int(n) END IF IF settingstring(a, "RIGHTTHRESH", n) THEN joy(12) = str2int(n) END IF IF settingstring(a, "USEBUTTON", n) THEN joy(13) = bound(str2int(n) + 2, 2, 3) END IF IF settingstring(a, "MENUBUTTON", n) THEN joy(14) = bound(str2int(n) + 2, 2, 3) END IF safety = safety + 1 LOOP CLOSE #fh '--wait a little to make sure the buttons clear setwait speedcontrol dowait gen(genJoy) = 1 ELSE '--no joyset.ini file, must recalibrate 'calibrate END IF END SUB FUNCTION renamehero (byval who as integer, byval escapable as bool) as bool 'If escapable is true, then ESC exits, otherwise it resets the entered name 'Returns true if the player didn't cancel. DIM ret as bool DIM her as herodef loadherodata her, gam.hero(who).id DIM limit as integer = her.max_name_len IF limit = 0 THEN limit = 16 DIM prompt as string = readglobalstring(137, "Name the Hero", 20) DIM entry_box_width as integer = large(limit, LEN(gam.hero(who).name)) * 8 DIM remember as string = gam.hero(who).name DIM need_fade_out as bool IF fadestate = NO THEN setvispage vpage fadein need_fade_out = YES END IF DIM holdscreen as integer holdscreen = allocatepage copypage vpage, holdscreen IF running_on_console() THEN 'native virtual keyboard does not work on OUYA right now, so do this instead. gam.hero(who).name = gamepad_virtual_keyboard(gam.hero(who).name, limit) ret = YES ELSEIF running_on_mobile() THEN hide_virtual_gamepad() gam.pad.being_shown = NO gam.hero(who).name = touch_virtual_keyboard(gam.hero(who).name, limit, prompt) ret = YES ELSE 'On platforms that need it (currently just Android) show the virtual keyboard. 'This does nothing on platforms that don't need it. ' 'FIXME: this is obsoleted by touch_virtual_keyboard() but we may want it again ' in the future when the Android virtual keyboard is working again show_virtual_keyboard '--Wait while the player types a new name setkeys YES DO setwait speedcontrol setkeys YES playtimer control IF carray(ccUse) > 1 AND keyval(scSpace) = 0 THEN IF LEN(gam.hero(who).name) > 0 THEN ret = YES EXIT DO ELSE menusound gen(genCancelSFX) END IF END IF IF carray(ccMenu) > 1 THEN gam.hero(who).name = remember menusound gen(genCancelSFX) IF escapable THEN EXIT DO END IF strgrabber gam.hero(who).name, limit copypage holdscreen, vpage centerbox rCenter, rCenter, 168, 32, 1, vpage edgeprint prompt, pCentered, rCenter - 10, uilook(uiText), vpage rectangle pCentered, rCenter + 1, entry_box_width, 10, uilook(uiHighlight), vpage edgeprint gam.hero(who).name, pCentered, rCenter, uilook(uiMenuItem), vpage setvispage vpage dowait LOOP hide_virtual_keyboard END IF IF ret THEN menusound gen(genAcceptSFX) IF need_fade_out THEN fadeout 0, 0, 0 END IF freepage holdscreen RETURN ret END FUNCTION SUB resetgame () 'Warning: don't put necessary initialisation in here, 'as this is ONLY called AFTER playing a game, on gameover/resetgame/loadgame/quit. 'initgamedefaults is probably a better place for that. 'In order words, be suspicious of anything in here that resets 'to a value other than zero/empty string/etc. gam.map.id = 0 (herox(0)) = 0 (heroy(0)) = 0 (heroz(0)) = 0 (herodir(0)) = 0 gam.getinputtext_enabled = NO script_log_resetgame 'leader = 0 mapx = 0 mapy = 0 gold = 0 gam.showstring = "" gam.debug_camera_pan = NO gam.paused = NO '--return gen to defaults xbload game + ".gen", gen(), "General data is missing from " + sourcerpg CleanNPCL npc() flusharray tag() flusharray onetime() FOR i as integer = 0 TO 40 gam.hero(i).id = -1 gam.hero(i).locked = NO FOR j as integer = 0 TO statLast gam.hero(i).stat.cur.sta(j) = 0 gam.hero(i).stat.max.sta(j) = 0 gam.hero(i).stat.base.sta(j) = 0 NEXT j gam.hero(i).lev = 0 gam.hero(i).lev_gain = 0 gam.hero(i).wep_pic = 0 gam.hero(i).wep_pal = 0 gam.hero(i).battle_pic = 0 gam.hero(i).battle_pal = 0 gam.hero(i).pic = 0 gam.hero(i).pal = 0 gam.hero(i).def_wep = 0 gam.hero(i).rename_on_status = 0 'Hero elementals are actually initialised (to 1 = 100% damage) in addhero. FOR j as integer = 0 TO maxElements - 1 gam.hero(i).elementals(j) = .0f NEXT FOR j as integer = 0 to 1 gam.hero(i).hand_pos(j).x = 0 gam.hero(i).hand_pos(j).y = 0 NEXT j NEXT i FOR i as integer = 0 TO 40 FOR o as integer = 0 TO 3 FOR j as integer = 0 TO 23 spell(i, o, j) = 0 NEXT j NEXT o NEXT i FOR i as integer = 0 TO 40 FOR o as integer = 0 TO 7 lmp(i, o) = 0 NEXT o NEXT i FOR i as integer = 0 TO 40 gam.hero(i).exp_cur = 0 gam.hero(i).exp_next = 0 NEXT i FOR i as integer = 0 TO 40 gam.hero(i).name = "" NEXT i CleanInventory(inventory()) FOR i as integer = 0 TO 40 FOR o as integer = 0 TO 4 eqstuf(i, o) = 0 NEXT o NEXT i FOR i as integer = 0 TO 99 FOR o as integer = 0 TO 49 gam.stock(i, o) = 0 'default of 0 means "unloaded". The actual value will be populated when you first visit the shop NEXT o NEXT i flusharray global(), maxScriptGlobals, 0 reset_vehicle vstate cleanup_text_box txt.showing = NO txt.fully_shown = NO txt.show_lines = 0 FOR i as integer = 0 TO UBOUND(plotstr) WITH plotstr(i) .s = "" .col = -1 'default to uilook(uiText) .BGCol = 0 .X = 0 .Y = 0 .Bits = 0 END WITH NEXT i FOR i as integer = topmenu TO 0 STEP -1 remove_menu i, NO NEXT i 'delete temp files that are part of the game state deletetemps 'doesn't unload scripts: not needed killallscripts 'This only happens when using Test Game IF lump_reloading.hsp.changed THEN reload_scripts FOR i as integer = 0 TO ubound(timers) WITH timers(i) .count = 0 .speed = 0 .ticks = 0 .trigger = 0 .flags = 0 .st = 0 END WITH NEXT i close_persist_reld() cleanup_game_slices() 'plotslices() should now be all zeroed out. Rather than checking, check slicedebug() (if enabled) SliceDebugDump YES SetupGameSlices reset_game_state() END SUB FUNCTION gamepadmap_from_reload(gamepad as NodePtr, byval use_dpad as bool=NO) as GamePadMap DIM gp as GamePadMap IF use_dpad THEN gp.Ud = gamepad."UP".integer gp.Rd = gamepad."RIGHT".integer gp.Dd = gamepad."DOWN".integer gp.Ld = gamepad."LEFT".integer END IF gp.A = gamepad."A".integer gp.B = gamepad."B".integer gp.X = gamepad."X".integer gp.Y = gamepad."Y".integer gp.L1 = gamepad."L1".integer gp.R1 = gamepad."R1".integer gp.L2 = gamepad."L2".integer gp.R2 = gamepad."R2".integer RETURN gp END FUNCTION DESTRUCTOR HeroWalkabout () v_free curzones END DESTRUCTOR 'Unlike resetgame(), this is called before a game first is played, 'in addition to being called after playing (resetgame/gameover/loadgame/quit). SUB reset_game_state () reset_map_state(gam.map) gam.wonbattle = NO gam.remembermusic = -1 gam.random_battle_countdown = range(100, 60) gam.mouse_enabled = NO defaultmousecursor 'init mouse state gam.pad.being_shown = NO 'If we are resetting, the old slices will have already been destroyed 'by cleanup_game_slices() so we just re-assign herow().sl FOR i as integer = 0 TO UBOUND(herow) herow(i).sl = create_walkabout_slices(hero_layer()) DIM meta as HeroSliceContext ptr = NEW HeroSliceContext meta->rank = i herow(i).sl->Context = meta NEXT i DIM root as NodePtr = get_general_reld() DIM gamepad as NodePtr = root."gamepad".ptr remap_android_gamepad 0, gamepadmap_from_reload(gamepad, NO) FOR i as integer = 1 TO 3 'Default the other gamepads to be the same as the first one '(this might be overridden in a moment) remap_android_gamepad i, gamepadmap_from_reload(gamepad, NO) NEXT i DIM player as integer IF NOT root."multiplayer_gamepads".exists THEN SetChildNode root, "multiplayer_gamepads" END IF READNODE root."multiplayer_gamepads" as multi WITHNODE multi."player" as playernode player = GetInteger(playernode) SELECT CASE player CASE 1 TO 3: remap_android_gamepad player, gamepadmap_from_reload(playernode, YES) CASE ELSE: debug "Ignore invalid multiplayer_gamepads.player id of " & player END SELECT END WITHNODE END READNODE remap_virtual_gamepad("virtual_gamepad") END SUB SUB remap_virtual_gamepad(nodename as string) DIM root as NodePtr = get_general_reld() DIM mobile as NodePtr = root."mobile_options".ptr IF mobile = 0 THEN mobile = SetChildNode(root, "mobile_options") DIM vgp as NodePtr = GetChildByName(mobile, nodename) IF vgp = 0 THEN 'node does not exist, create defaults vgp = SetChildNode(mobile, nodename) AppendChildNode(vgp, "button", scEnter) AppendChildNode(vgp, "button", scESC) END IF DIM button_id as integer = 0 READNODE vgp, ignoreall WITHNODE vgp."button" as but remap_touchscreen_button button_id, GetInteger(but) button_id += 1 END WITHNODE END READNODE FOR i as integer = button_id TO 5 remap_touchscreen_button i, 0 NEXT i update_virtual_gamepad_display() END SUB FUNCTION should_disable_virtual_gamepad() as bool IF NOT running_on_mobile() THEN RETURN NO DIM root as NodePtr = get_general_reld() DIM mobile as NodePtr = root."mobile_options".ptr IF mobile = 0 THEN mobile = SetChildNode(root, "mobile_options") RETURN mobile."disable_virtual_gamepad".exists END FUNCTION FUNCTION should_hide_virtual_gamepad_when_suspendplayer() as bool IF NOT running_on_mobile() THEN RETURN NO DIM root as NodePtr = get_general_reld() RETURN root."mobile_options"."hide_virtual_gamepad_when_suspendplayer".integer.default(NO) <> 0 END FUNCTION FUNCTION use_touch_textboxes() as bool 'Also applies to mouse-click textboxes IF running_on_mobile() THEN IF get_gen_bool("/mobile_options/touch_textboxes/enabled") THEN RETURN YES END IF IF get_gen_bool("/mouse/click_textboxes") THEN RETURN YES END IF RETURN NO END FUNCTION SUB reset_map_state (map as MapModeState) map.id = gen(genStartMap) map.lastmap = -1 map.same = NO map.name = "" END SUB 'Returns the maximum level SUB get_max_levelmp (ret() as integer, byval hero_level as integer) FOR mplevel as integer = 0 TO 7 ret(mplevel) = 0 NEXT DIM nextlevel as integer = 0 DIM turn_point as integer = 0 FOR mplevel as integer = 0 TO hero_level ret(nextlevel) += 1 nextlevel += 1 IF nextlevel > turn_point THEN nextlevel = 0: turn_point += 1 IF turn_point > 7 THEN turn_point = 0 NEXT END SUB 'Reset a hero's level mp to maximum amounts SUB resetlmp (byval slot as integer, byval hero_level as integer) DIM ret(7) as integer get_max_levelmp(ret(), hero_level) FOR mplevel as integer = 0 TO 7 lmp(slot, mplevel) = ret(mplevel) NEXT END SUB FUNCTION settingstring (searchee as string, setting as string, result as string) as integer ' (This is used when reading joyset.ini, but could be replaced with read_ini_int) ' checks to see if searchee begins with setting = ' if so, sets result to the uppercased space-trimmed value that ' follows the = sign and returns true. If not found, returns false settingstring = 0 IF UCASE(LEFT(searchee, LEN(setting) + 1)) = setting + "=" THEN result = UCASE(LTRIM(RTRIM(MID(searchee, LEN(setting) + 2, 32)))) settingstring = -1 END IF END FUNCTION SUB shop (byval id as integer) DIM storebuf(40) as integer DIM menu(10) as string DIM menuid(10) as integer DIM sn as string DIM autopick as integer DIM w as integer DIM temp as integer DIM tog as integer DIM inn as integer DIM rsr as integer DIM h as integer DIM c as integer DIM t as integer DIM o as integer DIM page as integer DIM holdscreen as integer DIM st as MenuState FOR i as integer = 0 TO 7 menuid(i) = i NEXT i menu(0) = readglobalstring(70, "Buy", 10) menu(1) = readglobalstring(71, "Sell", 10) menu(2) = readglobalstring(73, "Hire", 10) menu(3) = readglobalstring(72, "Inn", 10) menu(4) = readglobalstring(63, "Equip", 10) menu(5) = readglobalstring(66, "Save", 10) menu(6) = readglobalstring(68, "Map", 10) menu(7) = readglobalstring(65, "Team", 10) st.size = 22 st.pt = 0 st.last = -1 '--initshop loadrecord storebuf(), game + ".sho", 40 \ 2, id sn = readbadbinstring(storebuf(), 0, 15, 0) o = 0 FOR i as integer = 0 TO 7 IF readbit(storebuf(), 17, i) THEN SWAP menu(i), menu(o) SWAP menuid(i), menuid(o) st.last = o o = o + 1 END IF NEXT i IF st.last = -1 THEN EXIT SUB IF st.last = 0 THEN autopick = 1 st.last += 1 menu(st.last) = readglobalstring(74, "Exit", 10) menuid(st.last) = 8 menusound gen(genAcceptSFX) '--Preserve background for display beneath top-level shop menu holdscreen = duplicatepage(vpage) page = compatpage show_virtual_gamepad() setkeys DO setwait speedcontrol setkeys tog = tog XOR 1 playtimer control usemenu st usemenusounds IF carray(ccMenu) > 1 THEN menusound gen(genCancelSFX) : EXIT DO IF carray(ccUse) > 1 OR autopick THEN IF menuid(st.pt) = 8 THEN '--EXIT menusound gen(genCancelSFX) EXIT DO END IF IF menuid(st.pt) = 0 THEN '--BUY buystuff id, 0, storebuf() END IF IF menuid(st.pt) = 1 THEN '--SELL sellstuff id, storebuf() END IF IF menuid(st.pt) = 2 THEN '--HIRE buystuff id, 1, storebuf() END IF IF menuid(st.pt) = 6 THEN '--MAP minimap herox(0), heroy(0) END IF IF menuid(st.pt) = 7 THEN '--TEAM hero_swap_menu 1 END IF IF menuid(st.pt) = 4 THEN '--EQUIP w = onwho(readglobalstring(108, "Equip Who?", 20), NO) IF w >= 0 THEN equip_menu w END IF END IF IF menuid(st.pt) = 5 THEN '--SAVE temp = picksave() IF temp >= 0 THEN savegame temp END IF IF menuid(st.pt) = 3 THEN '--INN IF useinn(storebuf(18), holdscreen) THEN innRestore IF storebuf(19) > 0 THEN '--Run animation for Inn trigger_script storebuf(19), 0, NO, "inn", "ID " & id, mainFibreGroup EXIT DO ELSE '--Inn has no script, do simple fade fadeout 0, 0, 80 queue_fade_in END IF END IF copypage holdscreen, vpage END IF IF autopick THEN EXIT DO 'Re-show the virtual gamepad, in case a special screen was triggered that hid it show_virtual_gamepad() END IF copypage holdscreen, vpage h = (st.last + 2) * 10 centerbox 160, 104 + (h * .5), 96, h, 1, page centerbox 160, 90, LEN(sn) * 8 + 8, 16, 1, page edgeprint sn, pCentered, 85, uilook(uiText), page FOR i as integer = 0 TO st.last c = uilook(uiMenuItem): IF st.pt = i THEN c = uilook(uiSelectedItem + tog) edgeprint menu(i), pCentered, 109 + i * 10, c, page NEXT i setvispage vpage check_for_queued_fade_in dowait LOOP FOR t = 4 TO 5: carray(t) = 0: NEXT t freepage page freepage holdscreen evalitemtags party_change_updates END SUB 'holdscreen is a copy of vpage (not a compatpage) FUNCTION useinn (byval price as integer, byval holdscreen as integer) as integer DIM menu(1) as string DIM page as integer page = compatpage DIM tog as integer DIM state as MenuState state.last = UBOUND(menu) state.size = 2 useinn = 0 'default return value menu(0) = readglobalstring(49, "Pay", 10) menu(1) = readglobalstring(50, "Cancel", 10) DIM inncost as string = readglobalstring(143, "THE INN COSTS", 20) DIM youhave as string = readglobalstring(145, "You have", 20) menusound gen(genAcceptSFX) show_virtual_gamepad() setkeys DO setwait speedcontrol setkeys tog = tog XOR 1 playtimer control IF carray(ccMenu) > 1 THEN menusound gen(genCancelSFX) EXIT DO END IF usemenusounds usemenu state 'alternatively IF carray(ccLeft) > 1 OR carray(ccRight) > 1 THEN menusound gen(genCursorSFX) state.pt = state.pt XOR 1 END IF IF carray(ccUse) > 1 THEN IF state.pt = 0 AND gold >= price THEN gold = gold - price useinn = -1 menusound gen(genAcceptSFX) EXIT DO ELSE menusound gen(genCancelSFX) END IF IF state.pt = 1 THEN EXIT DO END IF 'Draw screen copypage holdscreen, vpage edgeboxstyle 0, 3, 218, herocount() * 10 + 4, 0, page DIM y as integer = 0 FOR i as integer = 0 TO sizeActiveParty - 1 IF gam.hero(i).id >= 0 THEN DIM col as integer = uilook(uiText) edgeprint gam.hero(i).name, 128 - LEN(gam.hero(i).name) * 8, 5 + y * 10, col, page edgeprint STR(gam.hero(i).stat.cur.hp) + "/" + STR(gam.hero(i).stat.max.hp), 136, 5 + y * 10, col, page y = y + 1 END IF NEXT i centerfuz 160, 90, 200, 60, 1, page rectangle 130, 92, 60, 22, uilook(uiHighlight), page 'orig colour 20 edgeprint inncost & " " & price & " " & readglobalstring(32, "$"), 160 - LEN(inncost & price & " " & readglobalstring(32, "$")) * 4, 70, uilook(uiText), page edgeprint youhave & " " & gold & " " & readglobalstring(32, "$"), 160 - LEN(youhave & gold & " " & readglobalstring(32, "$")) * 4, 80, uilook(uiText), page FOR i as integer = 0 TO 1 DIM col as integer col = uilook(uiMenuItem) IF state.pt = i THEN col = uilook(uiSelectedItem + tog) edgeprint menu(i), 160 - LEN(menu(i)) * 4, 94 + i * 8, col, page NEXT i setvispage vpage check_for_queued_fade_in dowait LOOP freepage page party_change_updates END FUNCTION SUB tagdisplay STATIC st as MenuState DIM lineh as integer = lineheight() st.last = gen(genMaxTagName) IF gam.debug_showtags = 2 THEN st.size = small(st.last, get_resolution().h \ lineh - 1) IF st.size <= 6 THEN 'Pressing F4 had no visible effect, so quit this debug mode instead gam.debug_showtags = 0 END IF ELSE st.size = 6 END IF 'Controls IF keyval(scCtrl) > 0 THEN IF keyval(scNumpadMinus) > 1 OR keyval(scMinus) > 1 THEN settag st.pt, NO tag_updates END IF IF keyval(scNumpadPlus) > 1 OR keyval(scPlus) > 1 THEN settag st.pt, YES tag_updates END IF ELSE IF usemenu(st, scMinus, scPlus) = NO THEN 'little bit hacky... usemenu(st, scNumpadMinus, scNumpadPlus) END IF END IF IF keyval(scTab) > 1 THEN settag st.pt, NOT istag(st.pt, 0) END IF 'Draw the menu wrapprint !"To scroll:\n+/-/pgup/down\nTAB to toggle\nF4 expand/quit", pRight, 0, uilook(uiMenuItem), dpage 'This fuzzy box is exactly wide enough for the max tag name length fuzzyrect 0, 0, 200, (st.size + 1) * lineh, uilook(uiOutline), dpage FOR i as integer = st.top TO st.top + st.size DIM temp as string = i & " " SELECT CASE i CASE 0, 1 temp += "Reserved Tag" CASE IS > 1 temp += load_tag_name(i) IF tag_is_autoset(i) THEN temp &= " [AUTOSET]" END SELECT DIM col as integer IF istag(i, 0) THEN col = uiSelectedItem ELSE col = uiMenuItem DIM y as integer = (i - st.top) * lineh edgeprint temp, 8, y, uilook(col), dpage IF i = st.pt THEN edgeprint CHR(26), 0, y, uilook(uiText), dpage NEXT i END SUB SUB writejoysettings DIM fh as integer = FREEFILE OPEN exepath & SLASH & "joyset.ini" FOR OUTPUT as #fh PRINT #fh, "#Joystick/gamepad configuration" PRINT #fh, "UPTHRESH=" & joy(9) PRINT #fh, "DOWNTHRESH=" & joy(10) PRINT #fh, "LEFTTHRESH=" & joy(11) PRINT #fh, "RIGHTTHRESH=" & joy(12) PRINT #fh, "USEBUTTON=" & (joy(13) - 2) PRINT #fh, "MENUBUTTON=" & (joy(14) - 2) CLOSE #fh END SUB FUNCTION herocount (byval last as integer = sizeActiveParty - 1) as integer '--differs from liveherocount() in that it does not care if they are alive DIM count as integer = 0 FOR i as integer = 0 TO last IF gam.hero(i).id >= 0 THEN count += 1 NEXT i RETURN count END FUNCTION FUNCTION caterpillar_size () as integer 'Returns the number of heroes on the map, regardless of whether caterpillar trailing is suspended IF readbit(gen(), genBits, 1) = 1 THEN RETURN herocount RETURN 1 END FUNCTION FUNCTION default_margin() as integer IF running_on_console() THEN 'On OUYA, default to a safe 8% margin RETURN 8 END IF ' on all other platforms, default to no margin RETURN 0 END FUNCTION FUNCTION default_margin_for_game() as integer 'Only for use when a game is currently loaded DIM root as NodePtr root = get_general_reld() RETURN root."console_options"."safe_margin".default(default_margin()) END FUNCTION '========================================================================================== ' Playing time '========================================================================================== FUNCTION playtime (byval d as integer, byval h as integer, byval m as integer) as string DIM s as string = "" SELECT CASE d CASE 1 s = s & d & " " & readglobalstring(154, "day", 10) & " " CASE IS > 1 s = s & d & " " & readglobalstring(155, "days", 10) & " " END SELECT SELECT CASE h CASE 1 s = s & h & " " & readglobalstring(156, "hour", 10) & " " CASE IS > 1 s = s & h & " " & readglobalstring(157, "hours", 10) & " " END SELECT SELECT CASE m CASE 1 s = s & m & " " & readglobalstring(158, "minute", 10) & " " CASE IS > 1 s = s & m & " " & readglobalstring(159, "minutes", 10) & " " END SELECT RETURN s END FUNCTION SUB playtimer STATIC n as double IF TIMER >= n + 1 OR n - TIMER > 3600 THEN n = INT(TIMER) gen(genSeconds) = gen(genSeconds) + 1 WHILE gen(genSeconds) >= 60 gen(genSeconds) = gen(genSeconds) - 60 gen(genMinutes) = gen(genMinutes) + 1 WEND WHILE gen(genMinutes) >= 60 gen(genMinutes) = gen(genMinutes) - 60 gen(genHours) = gen(genHours) + 1 refresh_keepalive_file WEND WHILE gen(genHours) >= 24 gen(genHours) = gen(genHours) - 24 IF gen(genDays) < 32767 THEN gen(genDays) = gen(genDays) + 1 WEND END IF IF autosnap > 0 ANDALSO (get_tickcount MOD autosnap) = 0 THEN write_checkpoint END IF END SUB