"Girl at Gulg Volcano" - Final Fantasy Tactics Proposition Calculator - johannhowitzer - 03-08-2024
This program will require a little explanation. It may look like a game when you run it, but it's just a way to plug in your stats and see what mission results you'll get. Controls are just arrow keys, enter and escape, along with the plus/minus keys to the left of backspace, as an additional convenience when tweaking stats. To compile, you will also need resource.mfi and icon32.ico, which are in the attached zip file:
Girl at Gulg Volcano.zip (Size: 210.3 KB / Downloads: 33)
Final Fantasy Tactics has little missions you can do, called "Propositions." You pay a little money, send characters, they spend some days being unavailable while doing the mission, then you can return and pick them up when done, for some money, ability points, and also extra lore in many cases.
But success on a mission is not guaranteed, and Final Fantasy Tactics does not tell you anything about what influences the outcome. Finally, years later via decompilation, the details were uncovered. And they are ridiculously complicated for such a small part of the game. Your characters' jobs, levels, and brave and faith values all play a part.
So in this program, you can select a proposition, set up the three characters to match your party, and you can see what the outcome will be at the bottom. As the game allows you to send less than three characters, you can toggle each unit on or off. And there is also a toggle between the original PS1 release's translation, and the PSP "War of the Lions" remake's translation.
This project represents FAR too much work, for how few people will probably ever use it for its intended purpose, but I thought I'd post it here anyway, both as a demonstration of the graphics, and just in case anyone happens to like Final Fantasy Tactics. You do not want to know how much time I spent making sure the graphics were as faithful to the game as possible. Was fun, though, and I learned a lot!
Code: (Select All)
$noprefix
$exeicon:'icon32.ico'
title "Girl at Gulg Volcano"
const true = -1
const false = 0
type coordinate_int
x as integer
y as integer
end type
' ===== Screen =====
const screenw = 256
const screenh = 240
dim shared full_screen as unsigned long ' Main screen before scaling
full_screen = newimage(screenw, screenh, 32)
dim shared option_window_size as byte
option_window_size = 2
dim shared scaled_screen(3) as unsigned long
scaled_screen(1) = newimage(280, 240, 32)
scaled_screen(2) = newimage(512, 480, 32)
scaled_screen(3) = newimage(768, 720, 32)
screen scaled_screen(option_window_size)
display_screen
' ===== Source images =====
dim shared bar_background as unsigned long
dim shared job_background as unsigned long
dim shared job_image as unsigned long
dim shared window_image as unsigned long
dim shared city_image as unsigned long
dim shared data_image as unsigned long
dim shared cursor_layer as unsigned long
dim shared frame_assembly as unsigned long
dim shared store_screen as unsigned long
dim shared final_screen as unsigned long
cursor_layer = newimage(screenw, screenh, 32)
frame_assembly = newimage(screenw, screenh, 32)
store_screen = newimage(screenw, screenh, 32)
final_screen = newimage(screenw, screenh, 32)
' ===== Fonts =====
const fonts = 9
' Glass Fonts - custom pixel font processing and drawing
type font_structure
image as unsigned long
pos as coordinate_int
h as integer
w as integer
end type
' Alignment in font calls
const left_align = 0
const right_align = 1
const center_align = 2
dim shared g_font(fonts, 255) as font_structure ' Number of fonts comes from main program header
' Current font options in use
dim shared font_using as byte ' Index of current font being used
dim shared font_align as byte ' Alignment
dim shared font_dest as unsigned long ' Destination image surface
dim shared font_x as integer ' Rolling font position, set by each glass_fonts call
dim shared font_y as integer
const f_text_black = 1
const f_text_red = 2
const f_text_blue = 3
const f_text_dkblue = 4
const f_gil_white = 5
const f_map_white = 6
const f_map_grey = 7
const f_map_black = 8
const f_map_dkblue = 9
' --- Resource file ---
dim shared mfi_s(255) as long ' Size
dim shared mfi_o(255) as long ' Offset
dim shared mfi_count as unsigned byte
dim shared mfi_index as unsigned byte
mfi_loader "resource.mfi"
for n = 1 to fonts: initialize_font n: next
const f_row_h = 16
' ===== Window data =====
const win_normal = 1 ' Normal window with drop shadow
const win_normal_dark = 2 ' Darkened version, when inactive
const win_list = 3 ' Top and bottom bars added
const win_list_dark = 4
const window_count = 4
dim shared window_piece(window_count, 3) as coordinate_int
' window origin left top mid mid right bot
set_window win_normal, 1, 1, 12, 8, 15, 15, 4, 6
set_window win_list, 44, 1, 3, 9, 15, 15, 5, 8
set_window win_normal_dark, 79, 1, 12, 8, 15, 15, 4, 6
set_window win_list_dark, 122, 1, 3, 9, 15, 15, 5, 8
' ===== Drop shadow data =====
source data_image
dim shared finger_shadow(-1 to 0, 16, 12) as integer
for d = -1 to 0: for y = 0 to 12: for x = 0 to 16
dx = 1 + x + (18 * abs(d)): dy = 32 + y
if alpha(point(dx, dy)) = 0 then continue
finger_shadow(d, x, y) = red(point(dx, dy))
next x: next y: next d
dest full_screen
dim shared blob_shadow(-1 to 0, 19, 9) as integer
for d = -1 to 0: for y = 0 to 9: for x = 0 to 19
dx = 1 + x + (21 * abs(d)): dy = 46 + y
if alpha(point(dx, dy)) = 0 then continue
blob_shadow(d, x, y) = red(point(dx, dy))
next x: next y: next d
' ===== Proposition system data =====
const psx = 1
const psp = 2
const failure = 0
const success = 59
const great_success = 99
const job_count = 20
const prop_count = 96
dim shared job_name$(2, job_count)
const j_sq = 1: set_jn j_sq, "Squire", "Squire"
const j_ch = 2: set_jn j_ch, "Chemist", "Chemist"
const j_kn = 3: set_jn j_kn, "Knight", "Knight"
const j_ar = 4: set_jn j_ar, "Archer", "Archer"
const j_mk = 5: set_jn j_mk, "Monk", "Monk"
const j_pr = 6: set_jn j_pr, "Priest", "White Mage"
const j_wz = 7: set_jn j_wz, "Wizard", "Black Mage"
const j_tm = 8: set_jn j_tm, "Time Mage", "Time Mage"
const j_su = 9: set_jn j_su, "Summoner", "Summoner"
const j_th = 10: set_jn j_th, "Thief", "Thief"
const j_me = 11: set_jn j_me, "Mediator", "Orator"
const j_or = 12: set_jn j_or, "Oracle", "Mystic"
const j_ge = 13: set_jn j_ge, "Geomancer", "Geomancer"
const j_ln = 14: set_jn j_ln, "Lancer", "Dragoon"
const j_sm = 15: set_jn j_sm, "Samurai", "Samurai"
const j_nj = 16: set_jn j_nj, "Ninja", "Ninja"
const j_cl = 17: set_jn j_cl, "Calculator", "Arithmetician"
const j_bd = 18: set_jn j_bd, "Bard", "Bard"
const j_dc = 19: set_jn j_dc, "Dancer", "Dancer"
const j_mi = 20: set_jn j_mi, "Mime", "Mime"
dim shared type_name$(8)
const salvage = 1: type_name$(salvage) = "Salvage"
const mining = 2: type_name$(mining) = "Mining"
const explore = 3: type_name$(explore) = "Exploration"
const combat = 4: type_name$(combat) = "Combat"
const invest = 5: type_name$(invest) = "Investigation"
const invest2 = 6: type_name$(invest2) = "Investigation 2"
const oddjob = 7: type_name$(oddjob) = "Odd Job"
const contest = 8: type_name$(contest) = "Contest"
dim shared type_job_wp(8, job_count) as byte ' WP per proptype-job pair
dim shared stat_name$(3)
const neutral = 0: stat_name$(neutral) = "Neutral"
const brave = 1: stat_name$(brave) = "Brave"
const faith = 2: stat_name$(faith) = "Faith"
dim shared stat_job_wp(3, job_count) as byte ' WP per propstat-job pair
dim shared stat_br_wp(3, 5) as byte ' bonus WP from Brave stat
dim shared stat_fa_wp(3, 5) as byte ' bonus WP from Faith stat
dim shared stat_lv_wp(3, 10) as byte ' bonus WP from level
set_prop_wp
dim shared stat_range$(5)
stat_range$(1) = "3-20"
stat_range$(2) = "21-40"
stat_range$(3) = "41-60"
stat_range$(4) = "61-80"
stat_range$(5) = "81-97"
dim shared level_range$(10)
level_range$(1) = "1-10"
level_range$(2) = "11-20"
level_range$(3) = "21-30"
level_range$(4) = "31-40"
level_range$(5) = "41-50"
level_range$(6) = "51-60"
level_range$(7) = "61-70"
level_range$(8) = "71-80"
level_range$(9) = "81-90"
level_range$(10) = "91-99"
dim shared prop_name$(2, prop_count)
dim shared prop_city(prop_count) as byte
dim shared prop_type(prop_count) as byte
dim shared prop_stat(prop_count) as byte
dim shared city_name$(2, 15)
const igros = 1: city_name$(psx, igros) = "Igros Castle": city_name$(psp, igros) = "Eagrose Castle"
const gariland = 2: city_name$(psx, gariland) = "Gariland Magic City": city_name$(psp, gariland) = "Magick City of Gariland"
const dorter = 3: city_name$(psx, dorter) = "Dorter Trade City": city_name$(psp, dorter) = "Merchant City of Dorter"
const zaland = 4: city_name$(psx, zaland) = "Zaland Fort City": city_name$(psp, zaland) = "Castled City of Zaland"
const lionel = 5: city_name$(psx, lionel) = "Lionel Castle": city_name$(psp, lionel) = "Lionel Castle"
const goug = 6: city_name$(psx, goug) = "Goug Machine City": city_name$(psp, goug) = "Clockwork City of Goug"
const warjilis = 7: city_name$(psx, warjilis) = "Warjilis Trade City": city_name$(psp, warjilis) = "Port City of Warjilis"
const goland = 8: city_name$(psx, goland) = "Goland Coal City": city_name$(psp, goland) = "Mining Town of Gollund"
const lesalia = 9: city_name$(psx, lesalia) = "Lesalia Imperial Capital": city_name$(psp, lesalia) = "Royal City of Lesalia"
const yardow = 10: city_name$(psx, yardow) = "Yardow Fort City": city_name$(psp, yardow) = "Walled City of Yardrow"
const bervenia = 11: city_name$(psx, bervenia) = "Bervenia Free City": city_name$(psp, bervenia) = "Free City of Bervenia"
const riovanes = 12: city_name$(psx, riovanes) = "Riovanes Castle": city_name$(psp, riovanes) = "Riovanes Castle"
const zeltennia = 13: city_name$(psx, zeltennia) = "Zeltennia Castle": city_name$(psp, zeltennia) = "Zeltennia Castle"
const zarghidas = 14: city_name$(psx, zarghidas) = "Zarghidas Trade City": city_name$(psp, zarghidas) = "Trade City of Sal Ghidos"
const limberry = 15: city_name$(psx, limberry) = "Limberry Castle": city_name$(psp, limberry) = "Limberry Castle"
dim shared chapter_name$(2, 9)
const ch2 = 1: chapter_name$(psx, ch2) = "Chapter 2": chapter_name$(psp, ch2) = "Chapter 2"
const ch3 = 2: chapter_name$(psx, ch3) = "Chapter 3": chapter_name$(psp, ch3) = "Chapter 3"
const ch3b = 3: chapter_name$(psx, ch3b) = "After Book Storage": chapter_name$(psp, ch3b) = "After Book Storage"
const ch4 = 4: chapter_name$(psx, ch4) = "Chapter 4": chapter_name$(psp, ch4) = "Chapter 4"
const ch4b = 5: chapter_name$(psx, ch4b) = "After Limberry": chapter_name$(psp, ch4b) = "After Limberry"
const virgo = 6: chapter_name$(psx, virgo) = "Aug 23 to Sep 22": chapter_name$(psp, virgo) = "During Virgo"
const aries = 7: chapter_name$(psx, aries) = "Mar 21 to Apr 19": chapter_name$(psp, aries) = "During Aries"
const sagit = 8: chapter_name$(psx, sagit) = "Nov 23 to Dec 22": chapter_name$(psp, sagit) = "During Sagittarius"
const cancer = 9: chapter_name$(psx, cancer) = "Jun 22 to Jul 22": chapter_name$(psp, cancer) = "During Cancer"
dim shared prop_chapter(prop_count) as byte
const item = 1
const land = 2
dim shared prop_reward(prop_count) as byte
dim shared reward$(2, 2)
reward$(psx, item) = "Treasure"
reward$(psx, land) = "Unexplored Land"
reward$(psp, item) = "Artefact"
reward$(psp, land) = "Wonder"
' For setting up proposition data
dim shared prop_i as byte
dim shared prop_c as byte
set_prop_data
dim shared subtotal_wp as integer ' Total before job is factored in
dim shared total_wp as integer
' ===== Input handling =====
' References for press function and hold array
const up_key = 1
const down_key = 2
const left_key = 3
const right_key = 4
const minus_key = 5
const plus_key = 6
const f1_key = 7 ' Toggle console
const enter_key = 8
const esc_key = 9
' Input reference and binding data
const keybind_count = 9 ' Number of functions
' Input handling routine library - keyboard, gamepad, mouse
' Must be set per program: action constants, keybind_name$(), keybind_default(), keybind_overlap()
' Example of action constants:
' const up_key = 1
' const down_key = 2
' const left_key = 3
' const right_key = 4
' const weapon_key = 5
' const shield_key = 6
dim shared dev_keyboard as byte ' Store device index, to be re-checked whenever inputs are involved
dim shared dev_gamepad as byte
dim shared dev_mouse as byte
const keyboard = 1
const gamepad = 2
const mouse = 3
kc = keybind_count
dim shared keybind_name$(kc) ' Name of keybind slots - "WEAPON, UP" etc
dim shared key_name$(512, 2) ' Names of keyboard keys and gamepad buttons
dim shared keybind(kc, 2) as integer ' Contains key code assigned by player
dim shared keybind_default(kc, 2) as integer ' Defaults in case player wants to reset
dim shared axis_threshold as single ' Amount a stick needs to be tilted before input is registered
axis_threshold = 0.7
set_key_data
' Input tracking flags
dim shared press(kc) as byte ' What was pressed this frame
dim shared hold(kc) as byte ' What was pressed last frame
' Mouse data
dim shared mouse_press(3) as byte
dim shared mouse_hold(3) as byte
dim shared mouse_press_x(3) as integer ' Records where mouse button press started
dim shared mouse_press_y(3) as integer
dim shared mouse_release_x(3) as integer ' Records where mouse button was released
dim shared mouse_release_y(3) as integer
dim shared mouse_pos_x as integer ' Current position
dim shared mouse_pos_y as integer
set_keybinds
dim shared key_held(keybind_count) as byte
const repeat_key = 10 ' Frames from starting to hold key when key begins repeating
const repeat_speed = 2 ' Once begun, repeat once per this many frames
' Cursor visual position
dim shared c_posx(3) as integer
dim shared c_posy(8) as integer
c_posx(1) = 5
c_posx(2) = 30
c_posx(3) = 55
c_posy(1) = 2
c_posy(2) = 4
c_posy(3) = 7
c_posy(4) = 17
c_posy(5) = 19
c_posy(6) = 20
c_posy(7) = 21
c_posy(8) = 22
const cmovef = 5 ' Frames it takes for cursor to move to new position
dim shared city_menu_scroll as byte
dim shared city_menu_cursor as byte
dim shared prop_menu_cursor as byte
city_menu_scroll = 0
city_menu_cursor = 1
prop_menu_cursor = 1
' ===== Ring job selection =====
const job_width = 22
const job_height = 37
const interp_frames = 13
dim shared ring_pos(20, interp_frames) as coordinate_int ' Visual position of job icons on ring
ring_pos( 0, 0).x = 117: ring_pos( 0, 0).y = 168
ring_pos( 1, 0).x = 150: ring_pos( 1, 0).y = 164
ring_pos( 2, 0).x = 179: ring_pos( 2, 0).y = 154
ring_pos( 3, 0).x = 201: ring_pos( 3, 0).y = 140
ring_pos( 4, 0).x = 214: ring_pos( 4, 0).y = 121
ring_pos( 5, 0).x = 216: ring_pos( 5, 0).y = 102
ring_pos( 6, 0).x = 208: ring_pos( 6, 0).y = 83
ring_pos( 7, 0).x = 189: ring_pos( 7, 0).y = 66
ring_pos( 8, 0).x = 163: ring_pos( 8, 0).y = 54
ring_pos( 9, 0).x = 132: ring_pos( 9, 0).y = 48
ring_pos(10, 0).x = 99: ring_pos(10, 0).y = 48
ring_pos(11, 0).x = 68: ring_pos(11, 0).y = 55
ring_pos(12, 0).x = 43: ring_pos(12, 0).y = 67
ring_pos(13, 0).x = 25: ring_pos(13, 0).y = 84
ring_pos(14, 0).x = 17: ring_pos(14, 0).y = 103
ring_pos(15, 0).x = 20: ring_pos(15, 0).y = 122
ring_pos(16, 0).x = 33: ring_pos(16, 0).y = 140
ring_pos(17, 0).x = 55: ring_pos(17, 0).y = 155
ring_pos(18, 0).x = 84: ring_pos(18, 0).y = 164
ring_pos(19, 0).x = 0: ring_pos(19, 0).y = 0 ' These are to prevent invalid subscript, but still
ring_pos(20, 0).x = 233: ring_pos(20, 0).y = 0 ' position at extremes to show error visually
const ring_center_x = 116 ' Unit's current job in center of ring
const ring_center_y = 104
' Produce interpolation values for animation
i = 1 / interp_frames
for j1 = 0 to 18
j2 = wrap(j1 + 1, 0, 18)
dx = ring_pos(j2, 0).x - ring_pos(j1, 0).x
dy = ring_pos(j2, 0).y - ring_pos(j1, 0).y
for f = 1 to interp_frames
ring_pos(j1, f).x = rounding(ring_pos(j1, 0).x + (dx * i * f))
ring_pos(j1, f).y = rounding(ring_pos(j1, 0).y + (dy * i * f))
next f
next j1
' ===== Cursor animation =====
dim shared cursor_anim(46) as byte
for n = 1 to 2: cursor_anim(n) = 0: next n
for n = 3 to 6: cursor_anim(n) = 1: next n
for n = 7 to 12: cursor_anim(n) = 2: next n
for n = 13 to 20: cursor_anim(n) = 1: next n
for n = 21 to 30: cursor_anim(n) = 0: next n
for n = 31 to 46: cursor_anim(n) = -1: next n
dim shared cursor_frame as byte
cursor_frame = 1
' ===== User settings =====
dim shared light_mode as byte
light_mode = 1
dim shared hue(2, 4)
const hue_back = 0: hue(1, hue_back) = 15: hue(2, hue_back) = 0
const hue_full = 1: hue(1, hue_full) = 0: hue(2, hue_full) = 15
const hue_faded = 2: hue(1, hue_faded) = 7: hue(2, hue_faded) = 8
const hue_cursor = 3: hue(1, hue_cursor) = 1: hue(2, hue_cursor) = 9
const hue_red = 4: hue(1, hue_red) = 4: hue(2, hue_red) = 12
dim shared console_type as byte
console_type = psx
dim shared proposition as integer ' Proposition currently being used
proposition = 1
type wp_structure
job as byte
stat as byte
lv as byte
br as byte
fa as byte
subtotal as byte ' Total before job is factored in
total as byte
end type
type unit_structure
disabled as byte ' True means unit is ignored in total
job as byte
gender as byte ' Only affects displayed sprite in ring, and availability of bard/dancer
lv as byte ' Values range 1 to 10
br as byte ' Values range 1 to 5
fa as byte ' Values range 1 to 5
wp as wp_structure ' Unit's current WP are processed into this
end type
dim shared unit(3) as unit_structure
const g_male = 1
const g_female = 2
for u = 1 to 3
unit(u).disabled = false
unit(u).job = j_sq ' All jobs start as Squire
unit(u).gender = g_male
unit(u).lv = 1 ' All levels start as 1-10
unit(u).br = 3 ' All brave/faith starts at 41-60
unit(u).fa = 3
next u
calculate_wp
cx = 1
cy = 1
dim shared cursor_pos(3, 6) as coordinate_int
do
limit 60
putimage(0, 0)-(screenw - 1, screenh - 1), bar_background, full_screen, (0, 0)-(screenw - 1, screenh - 1)
draw_prop_window false
set_font f_text_red, left_align, full_screen
ox = 165: oy = 10
draw_frame win_normal, ox, oy, 80, 42, full_screen
x1 = ox + 10: y1 = oy + 8
cursor_pos(2, 1).x = x1 - 4: cursor_pos(2, 1).y = y1
glass_fonts "Console", x1, y1
if console_type = psx then t$ = "PSX" else t$ = "PSP"
font_using = f_text_black: glass_fonts t$, x1 + 5, y1 + 16
x1 = x1 + 44
cursor_pos(3, 1).x = x1 - 4: cursor_pos(3, 1).y = y1
font_using = f_text_red: glass_fonts "Size", x1, y1
font_using = f_text_black: glass_fonts "x" + trim$(str$(option_window_size)), x1 + 3, y1 + 16
for u = 1 to 3
big_font = f_text_black: small_font = f_map_black: w = win_normal
if unit(u).disabled = true then big_font = f_text_dkblue: small_font = f_map_dkblue: w = win_normal_dark
set_font big_font, left_align, full_screen
ox = 15 + ((u - 1) * 78): oy = 110
for n = 3 to 6: cursor_pos(u, n).x = ox + 2: next n
draw_frame w, ox, oy, 74, 90, full_screen
x1 = ox + 6: x2 = x1 + 40: y1 = oy + 8
glass_fonts "Job", x1, y1: cursor_pos(u, 3).y = y1: y1 = y1 + f_row_h
set_font small_font, right_align, full_screen
glass_fonts_backtrack level_range$(unit(u).lv), x2, y1 + 1, 1
set_font big_font, left_align, full_screen
glass_fonts "Lv", x1, y1: cursor_pos(u, 4).y = y1: y1 = y1 + f_row_h
set_font small_font, right_align, full_screen
glass_fonts_backtrack stat_range$(unit(u).br), x2, y1 + 1, 1
set_font big_font, left_align, full_screen
glass_fonts "Br", x1, y1: cursor_pos(u, 5).y = y1: y1 = y1 + f_row_h
set_font small_font, right_align, full_screen
glass_fonts_backtrack stat_range$(unit(u).fa), x2, y1 + 1, 1
set_font big_font, left_align, full_screen
glass_fonts "Fa", x1, y1: cursor_pos(u, 6).y = y1: y1 = y1 + f_row_h
glass_fonts "Total", x1, y1: y1 = y1 + f_row_h
font_align = right_align
x1 = ox + 65: y1 = oy + 8
glass_fonts str$(unit(u).wp.job + unit(u).wp.stat), x1, y1: y1 = y1 + f_row_h
glass_fonts str$(unit(u).wp.lv), x1, y1: y1 = y1 + f_row_h
glass_fonts str$(unit(u).wp.br), x1, y1: y1 = y1 + f_row_h
glass_fonts str$(unit(u).wp.fa), x1, y1: y1 = y1 + f_row_h
if unit(u).disabled = false then font_using = f_text_blue
glass_fonts str$(unit(u).wp.total), x1, y1: y1 = y1 + f_row_h
draw_job_icon unit(u).job, unit(u).gender, unit(u).disabled, ox + 26, oy - 18, full_screen
cursor_pos(u, 2).x = ox + 23
cursor_pos(u, 2).y = oy - 7
next u
x1 = 154: y1 = 214
set_font f_map_white, left_align, full_screen
glass_fonts_backtrack "Total Work Points", 29, y1, 1
if total_wp < 59 then font_using = f_map_white else font_using = f_map_grey
glass_fonts_backtrack "0-58 Failure", x1 + 5, y1 - 10, 1
if total_wp => 59 and total_wp < 99 then font_using = f_map_white else font_using = f_map_grey
glass_fonts_backtrack "59-98 Success", x1, y1, 1
if total_wp => 99 then font_using = f_map_white else font_using = f_map_grey
glass_fonts_backtrack "99+ Great Success", x1 + 9, y1 + 10, 1
set_font f_gil_white, center_align, full_screen
glass_fonts trim$(str$(total_wp)), inthalf(screenw) - 1, y1 - 2
draw_cursor cursor_pos(cx, cy).x, cursor_pos(cx, cy).y - 1, false
display_screen
update_inputs_repeat
if new_press_repeat(left_key) = true then cx = wrap(cx - 1, 1, 3)
if new_press_repeat(right_key) = true then cx = wrap(cx + 1, 1, 3)
if new_press_repeat(up_key) = true then
cy = wrap(cy - 1, 1, 6)
if cy = 1 or cy = 6 then cx = 1
end if
if new_press_repeat(down_key) = true then
cy = wrap(cy + 1, 1, 6)
if cy = 1 or cy = 2 then cx = 1
end if
d = 0
if new_press_repeat(minus_key) = true then d = -1
if new_press_repeat(plus_key) = true then d = 1
if new_press_repeat(enter_key) = true and cy <> 3 and cx + cy > 2 then d = 1
select case cy
case 1
if cx = 2 then console_type = wrap(console_type + d, psx, psp)
if cx = 3 then option_window_size = wrap(option_window_size + d, 1, 3): screen scaled_screen(option_window_size)
case 2: unit(cx).disabled = wrap(unit(cx).disabled + d, true, false)
case 3: unit(cx).job = wrap(unit(cx).job + d, 1, 20)
case 4: unit(cx).lv = wrap(unit(cx).lv + d, 1, 10)
case 5: unit(cx).br = wrap(unit(cx).br + d, 1, 5)
case 6: unit(cx).fa = wrap(unit(cx).fa + d, 1, 5)
end select
if d <> 0 then calculate_wp
if new_press(enter_key) = true and cx + cy = 2 then
draw_prop_window true
draw_cursor cursor_pos(cx, cy).x, cursor_pos(cx, cy).y - 1, true
capture_screen
prop_menu
calculate_wp
end if
if new_press(enter_key) = true and cy = 3 then
job_menu cx
calculate_wp
end if
loop
system
sub draw_prop_window(dark)
ox = 15: oy = 10
if dark = false then w = win_normal: set_font f_text_black, left_align, full_screen
if dark = true then w = win_normal_dark: set_font f_text_dkblue, left_align, full_screen
draw_frame w, ox, oy, 130, 90, full_screen
x1 = ox + 6: y1 = oy + 8
cursor_pos(1, 1).x = ox + 2: cursor_pos(1, 1).y = y1
glass_fonts prop_name$(console_type, proposition), x1, y1: y1 = y1 + f_row_h
if dark = false then font_using = f_text_red
glass_fonts "When", x1, y1: y1 = y1 + f_row_h
glass_fonts "Type", x1, y1: y1 = y1 + f_row_h
glass_fonts "Stat", x1, y1: y1 = y1 + f_row_h
glass_fonts "Reward", x1, y1
if dark = false then font_using = f_text_black
x1 = ox + 44: y1 = oy + 8
y1 = y1 + f_row_h
glass_fonts chapter_name$(console_type, prop_chapter(proposition)), x1, y1: y1 = y1 + f_row_h
glass_fonts type_name$(prop_type(proposition)), x1, y1: y1 = y1 + f_row_h
glass_fonts stat_name$(prop_stat(proposition)), x1, y1: y1 = y1 + f_row_h
if dark = false then font_using = f_text_blue
glass_fonts reward$(console_type, prop_reward(proposition)), x1, y1
w = 95: h = 9
x1 = 1 + ((w + 2) * 2 * abs(dark)) + ((w + 2) * (console_type - 1))
y1 = 1 + ((h + 2) * (prop_city(proposition) - 1))
putimage(ox + 2, oy - 4)-step(w, h), city_image, full_screen, (x1, y1)-step(w, h)
end sub
sub draw_city_menu(dark, ox, oy)
if dark = false then w = win_list else w = win_list_dark
draw_frame w, ox, oy, 118, 130, full_screen
x1 = ox + 6: y1 = oy + 13
for n = 1 to 7
set_font f_text_dkblue, left_align, full_screen
if dark = false then font_using = f_text_black
glass_fonts city_name$(console_type, n + city_menu_scroll), x1, y1
if n = city_menu_cursor - city_menu_scroll and dark = false then draw_cursor ox + 2, y1 - 1, dark
y1 = y1 + f_row_h
next n
set_font f_map_white, left_align, full_screen
if dark = true then font_using = f_map_grey
glass_fonts_backtrack "City", ox + 2, oy - 4, 1
if dark = true then exit sub
' Scroll arrows
w = 7: h = 15
if city_menu_scroll > 0 then putimage(ox + 111, oy + 13)-step(w, h), data_image, full_screen, (1, 1)-step(w, h)
if city_menu_scroll < 8 then putimage(ox + 111, oy + 104)-step(w, h), data_image, full_screen, (1 + w + 2, 1)-step(w, h)
w = 7: h = 6
putimage(ox + 111, oy + 21 + (city_menu_cursor * 5))-step(w, h), data_image, full_screen, (19, 1)-step(w, h)
end sub
sub prop_menu
city_menu_cursor = prop_city(proposition)
city_ox = 35
city_oy = 30
dim prop_list(8) as byte
dim prop_list_count
update_inputs_repeat
' Select city
do
limit 60
city_menu_scroll = min(max(city_menu_scroll, city_menu_cursor - 7), city_menu_cursor - 1)
restore_screen
draw_city_menu false, city_ox, city_oy
display_screen
update_inputs_repeat
if new_press_repeat(up_key) = true then city_menu_cursor = max(city_menu_cursor - 1, 1)
if new_press_repeat(down_key) = true then city_menu_cursor = min(city_menu_cursor + 1, 15)
if new_press(esc_key) = true then exit sub
if new_press(enter_key) = false then continue
' Compile proposition list
prop_list_count = 0
for p = 1 to prop_count
if prop_city(p) <> city_menu_cursor then continue
prop_list_count = prop_list_count + 1
prop_list(prop_list_count) = p
next p
' Get dynamic window width from widest text line
max_width = 0
for p = 1 to prop_list_count
max_width = max(max_width, text_width(prop_name$(console_type, prop_list(p)), f_text_black))
next p
restore_screen
draw_city_menu true, city_ox, city_oy
capture_screen
' Select proposition
prop_menu_cursor = 1
update_inputs_repeat
do
limit 60
restore_screen
draw_cursor city_ox + 2, city_oy + 13 + (((city_menu_cursor - city_menu_scroll) - 1) * f_row_h) - 1, true
ox = 55: oy = 50
draw_frame win_list, ox, oy, max_width + 15, (prop_list_count * f_row_h) + 18, full_screen
x1 = ox + 6: y1 = oy + 13
for n = 1 to prop_list_count
set_font f_text_black, left_align, full_screen
glass_fonts prop_name$(console_type, prop_list(n)), x1, y1
if n = prop_menu_cursor then draw_cursor ox + 2, y1 - 1, false
y1 = y1 + f_row_h
next n
set_font f_map_white, left_align, full_screen
if console_type = psx then t$ = "Proposition" else t$ = "Errand"
glass_fonts_backtrack t$, ox + 2, oy - 4, 1
display_screen
update_inputs_repeat
if new_press_repeat(up_key) = true then prop_menu_cursor = wrap(prop_menu_cursor - 1, 1, prop_list_count)
if new_press_repeat(down_key) = true then prop_menu_cursor = wrap(prop_menu_cursor + 1, 1, prop_list_count)
if new_press(esc_key) = true then exit do
if new_press(enter_key) = true then proposition = prop_list(prop_menu_cursor): exit sub
loop
loop
end sub
sub job_menu(u)
' Set of references to jobs on ring, since bard and dancer must occupy the same position
temp_g = unit(u).gender
dim ring_ref(19) as byte
dim wp_preview$(19)
for n = 1 to 19
n1 = n
if n = 19 then n1 = 20
if n = 18 and temp_g = g_female then n1 = j_dc
ring_ref(n) = n1
wp_preview$(n) = trim$(str$(type_job_wp(prop_type(proposition), n1) + stat_job_wp(prop_stat(proposition), n1)))
next n
' Using current job, find initial cursor position
for n = 1 to 19
if unit(u).job = ring_ref(n) then j = n
next n
if j = false then j = j_sq ' Prevent somehow failing, such as having male dancer or female bard
update_inputs_repeat
wp_dx = 10 ' Position of work point text above each job
wp_dy = -13
do
limit 60
putimage(0, 0)-(screenw - 1, screenh - 1), job_background, full_screen, (0, 0)-(screenw - 1, screenh - 1)
set_font f_map_white, center_align, full_screen
for n = 9 to 0 step -1
p = wrap(-n, 0, 18)
draw_job_icon ring_ref(wrap(j - n, 1, 19)), temp_g, false, ring_pos(p, 0).x, ring_pos(p, 0).y, full_screen
glass_fonts_backtrack wp_preview$(wrap(j - n, 1, 19)), ring_pos(p, 0).x + wp_dx, ring_pos(p, 0).y + wp_dy, 1
if n = 0 then continue
draw_job_icon ring_ref(wrap(j + n, 1, 19)), temp_g, false, ring_pos(n, 0).x, ring_pos(n, 0).y, full_screen
glass_fonts_backtrack wp_preview$(wrap(j + n, 1, 19)), ring_pos(n, 0).x + wp_dx, ring_pos(n, 0).y + wp_dy, 1
next n
draw_job_icon unit(u).job, unit(u).gender, false, ring_center_x, ring_center_y, full_screen
glass_fonts_backtrack wp_preview$(unit(u).job), ring_center_x + wp_dx, ring_center_y + wp_dy, 1
draw_frame win_normal, 83, 207, 90, 27, full_screen
set_font f_text_black, center_align, full_screen
glass_fonts job_name$(console_type, ring_ref(j)), inthalf(screenw), 217
putimage(3, 224)-(80, 236), data_image, full_screen, (1, 18)-(78, 30) ' Gender UI
display_screen
update_inputs_repeat
if press(left_key) = true then
for f = 1 to interp_frames
limit 60
putimage(0, 0)-(screenw - 1, screenh - 1), job_background, full_screen, (0, 0)-(screenw - 1, screenh - 1)
set_font f_map_white, center_align, full_screen
for n = 9 to 0 step -1
p = wrap(-n, 0, 18)
draw_job_icon ring_ref(wrap(j - n, 1, 19)), temp_g, false, ring_pos(p, f).x, ring_pos(p, f).y, full_screen
glass_fonts_backtrack wp_preview$(wrap(j - n, 1, 19)), ring_pos(p, f).x + wp_dx, ring_pos(p, f).y + wp_dy, 1
if n = 0 then continue
draw_job_icon ring_ref(wrap(j + n, 1, 19)), temp_g, false, ring_pos(n, f).x, ring_pos(n, f).y, full_screen
glass_fonts_backtrack wp_preview$(wrap(j + n, 1, 19)), ring_pos(n, f).x + wp_dx, ring_pos(n, f).y + wp_dy, 1
next n
draw_job_icon unit(u).job, unit(u).gender, false, ring_center_x, ring_center_y, full_screen
glass_fonts_backtrack wp_preview$(unit(u).job), ring_center_x + wp_dx, ring_center_y + wp_dy, 1
draw_frame win_normal, 83, 207, 90, 27, full_screen
set_font f_text_black, center_align, full_screen
glass_fonts job_name$(console_type, ring_ref(j)), inthalf(screenw), 217
putimage(3, 224)-(80, 236), data_image, full_screen, (1, 18)-(78, 30) ' Gender UI
display_screen
next f
j = wrap(j - 1, 1, job_count - 1)
end if
if press(right_key) = true then
j = wrap(j + 1, 1, job_count - 1)
for f = interp_frames to 1 step -1
limit 60
putimage(0, 0)-(screenw - 1, screenh - 1), job_background, full_screen, (0, 0)-(screenw - 1, screenh - 1)
set_font f_map_white, center_align, full_screen
for n = 9 to 0 step -1
p = wrap(-n, 0, 18)
draw_job_icon ring_ref(wrap(j - n, 1, 19)), temp_g, false, ring_pos(p, f).x, ring_pos(p, f).y, full_screen
glass_fonts_backtrack wp_preview$(wrap(j - n, 1, 19)), ring_pos(p, f).x + wp_dx, ring_pos(p, f).y + wp_dy, 1
if n = 0 then continue
draw_job_icon ring_ref(wrap(j + n, 1, 19)), temp_g, false, ring_pos(n, f).x, ring_pos(n, f).y, full_screen
glass_fonts_backtrack wp_preview$(wrap(j + n, 1, 19)), ring_pos(n, f).x + wp_dx, ring_pos(n, f).y + wp_dy, 1
next n
draw_job_icon unit(u).job, unit(u).gender, false, ring_center_x, ring_center_y, full_screen
glass_fonts_backtrack wp_preview$(unit(u).job), ring_center_x + wp_dx, ring_center_y + wp_dy, 1
draw_frame win_normal, 83, 207, 90, 27, full_screen
set_font f_text_black, center_align, full_screen
glass_fonts job_name$(console_type, ring_ref(wrap(j - 1, 1, job_count - 1))), inthalf(screenw), 217
putimage(3, 224)-(80, 236), data_image, full_screen, (1, 18)-(78, 30) ' Gender UI
display_screen
next f
end if
if new_press(up_key) = true or new_press(down_key) = true then
temp_g = toggle(temp_g, g_male, g_female)
if temp_g = g_male then ring_ref(18) = j_bd else ring_ref(18) = j_dc
wp_preview$(18) = trim$(str$(type_job_wp(prop_type(proposition), ring_ref(18)) + stat_job_wp(prop_stat(proposition), ring_ref(18))))
end if
if new_press(esc_key) = true then exit sub
if new_press(enter_key) = true then exit do
loop
unit(u).job = ring_ref(j)
unit(u).gender = temp_g
end sub
sub draw_job_icon(j, gender, dark, x, y, d~&)
' j is job, dark is true if dark version should be used
' Blob shadow
preserve& = dest
dest full_screen
source full_screen
for y1 = 0 to 9: for x1 = 0 to 19
v = blob_shadow(dark, x1, y1)
if v = 0 then continue
shx = x + x1 - 1: shy = y + y1 + 28
pset(shx, shy), rgba32(max(0, red(point(shx, shy)) - v), max(0, green(point(shx, shy)) - v), max(0, blue(point(shx, shy)) - v), 255)
next x1: next y1
dest preserve&
x1 = 1 + ((j - 1) * (job_width + 2))
y1 = 1 + ((gender - 1) * (job_height + 2)) + ((2 * (job_height + 2)) * abs(dark))
putimage(x, y)-step(job_width, job_height), job_image, d~&, (x1, y1)-step(job_width, job_height)
end sub
sub draw_cursor(x, y, dark)
if dark = false then set_font f_text_black, right_align, cursor_layer: dx = cursor_anim((timer * 60) mod 46)
if dark = true then set_font f_text_dkblue, right_align, full_screen: dx = -1
glass_fonts "@", x + dx, y
preserve& = dest
dest full_screen
source full_screen
for y1 = 0 to 12: for x1 = 0 to 16
v = finger_shadow(dark, x1, y1)
if v = 0 then continue
shx = x + dx + x1 - 14: shy = y + y1
pset(shx, shy), rgba32(max(0, red(point(shx, shy)) - v), max(0, green(point(shx, shy)) - v), max(0, blue(point(shx, shy)) - v), 255)
next x1: next y1
dest preserve&
end sub
sub calculate_wp
subtotal_wp = 0
total_wp = 0
for u = 1 to 3
unit(u).wp.job = 0
unit(u).wp.stat = 0
unit(u).wp.lv = 0
unit(u).wp.br = 0
unit(u).wp.fa = 0
unit(u).wp.subtotal = 0
unit(u).wp.total = 0
j = unit(u).job
if j <> false then
s = prop_stat(proposition)
unit(u).wp.job = type_job_wp(prop_type(proposition), j)
unit(u).wp.stat = stat_job_wp(s, j)
unit(u).wp.lv = stat_lv_wp(s, unit(u).lv)
unit(u).wp.br = stat_br_wp(s, unit(u).br)
unit(u).wp.fa = stat_fa_wp(s, unit(u).fa)
unit(u).wp.subtotal = unit(u).wp.lv + unit(u).wp.br + unit(u).wp.fa
unit(u).wp.total = unit(u).wp.subtotal + unit(u).wp.job + unit(u).wp.stat
end if
if unit(u).disabled = false then
subtotal_wp = subtotal_wp + unit(u).wp.subtotal
total_wp = total_wp + unit(u).wp.total
end if
next u
end sub
function right_num$(v, w)
t$ = trim$(str$(v))
right_num$ = space$(max(w - len(t$), 0)) + t$
end function
sub draw_frame(f, ox, oy, win_w, win_h, d~&)
preserve& = dest
dest frame_assembly
dontblend
' Piece dimensions
w1 = window_piece(f, 1).x: h1 = window_piece(f, 1).y
w2 = window_piece(f, 2).x: h2 = window_piece(f, 2).y
w3 = window_piece(f, 3).x: h3 = window_piece(f, 3).y
' Required size of center bands based on size of edges
center_w = win_w - w1 - w3 - 2
center_h = win_h - h1 - h3 - 2
center_cols = int(center_w / max(1, w2 + 1)) + 1 ' Overdrawing by one is fine
' Top corners and edge
draw_winp f, 0, 0, 1, 1
x = w1 + 1
for n = 1 to center_cols
draw_winp f, x, 0, 2, 1
x = x + w2 + 1
next n
draw_winp f, win_w - 1 - w3, 0, 3, 1
' Middle row
y = h1 + 1
for row = 1 to int(center_h / max(1, h2 + 1)) + 1
draw_winp f, 0, y, 1, 2
x = w1 + 1
for n = 1 to center_cols
draw_winp f, x, y, 2, 2
x = x + w2 + 1
next n
draw_winp f, win_w - 1 - w3, y, 3, 2
y = y + h2 + 1
next row
' Bottom row
y = win_h - 1 - h3
draw_winp f, 0, y, 1, 3
x = w1 + 1
for n = 1 to center_cols
draw_winp f, x, y, 2, 3
x = x + w2 + 1
next n
draw_winp f, win_w - 1 - w3, y, 3, 3
blend
dest preserve&
' Copy finished window to screen
putimage(ox, oy)-step(win_w - 1, win_h - 1), frame_assembly, d~&, (0, 0)-step(win_w - 1, win_h - 1)
end sub
sub draw_winp(f, x, y, x2, y2)
dim x(3): dim y(3)
' Source image coordinates
x(1) = window_piece(f, 0).x: y(1) = window_piece(f, 0).y
x(2) = x(1) + window_piece(f, 1).x + 2: y(2) = y(1) + window_piece(f, 1).y + 2
x(3) = x(2) + window_piece(f, 2).x + 2: y(3) = y(2) + window_piece(f, 2).y + 2
w = window_piece(f, x2).x: h = window_piece(f, y2).y
line(x, y)-step(w, h), rgba32(0, 0, 0, 0), bf
putimage(x, y)-step(w, h), window_image, frame_assembly, (x(x2), y(y2))-step(w, h)
end sub
sub set_window(w, x0, y0, w1, h1, w2, h2, w3, h3)
window_piece(w, 0).x = x0
window_piece(w, 0).y = y0
window_piece(w, 1).x = w1
window_piece(w, 1).y = h1
window_piece(w, 2).x = w2
window_piece(w, 2).y = h2
window_piece(w, 3).x = w3
window_piece(w, 3).y = h3
end sub
sub display_screen
putimage(0, 0)-(screenw - 1, screenh - 1), full_screen, final_screen, (0, 0)-(screenw - 1, screenh - 1)
putimage(0, 0)-(screenw - 1, screenh - 1), cursor_layer, final_screen, (0, 0)-(screenw - 1, screenh - 1)
clear_image cursor_layer
hardware_image = copyimage(final_screen, 33)
putimage(0, 0)-((screenw * option_window_size) - 1, (screenh * option_window_size) - 1), hardware_image
display
freeimage hardware_image
end sub
sub clear_image(d&)
preserve& = dest
dest d&
cls , rgba32(0, 0, 0, 0)
dest preserve&
end sub
sub capture_screen
clear_image store_screen
putimage(0, 0)-(screenw - 1, screenh - 1), full_screen, store_screen, (0, 0)-(screenw - 1, screenh - 1)
end sub
sub restore_screen
clear_image full_screen
putimage(0, 0)-(screenw - 1, screenh - 1), store_screen, full_screen, (0, 0)-(screenw - 1, screenh - 1)
end sub
sub glass_fonts_backtrack(t1$, x1, y1, backtrack)
' Special version, last parameter is number of pixels to backtrack per character
t$ = t1$
carriage = true
if right$(t$, 1) = ";" then
carriage = false
t$ = left$(t$, len(t$) - 1)
end if
x = x1: y = y1
f = font_using
if font_align <> left_align then
' Adjust starting point based on line width, for center or right align
w = text_width(t$, f) - (len(t$) * backtrack)
if font_align = center_align then w = int(w * 0.5)
x = x - w
end if
for n = 1 to len(t$)
c = asc(mid$(t$, n, 1))
w = g_font(f, c).w
putimage(x, y)-step(w, g_font(f, 0).h), g_font(f, 0).image, font_dest, (g_font(f, c).pos.x, g_font(f, c).pos.y)-step(w, g_font(f, 0).h)
x = x + w + 1 - backtrack
next n
font_x = x1
font_y = y1
if carriage = false then font_x = x
if carriage = true then font_y = y1 + g_font(f, 0).h
end sub
sub update_inputs_repeat
update_inputs
for b = 1 to keybind_count
key_held(b) = key_held(b) + 1
if hold(b) = false then key_held(b) = 0
if key_held(b) > repeat_key then key_held(b) = repeat_key - repeat_speed
next b
end sub
function new_press_repeat(b)
new_press_repeat = false
if new_press(b) = true or key_held(b) => repeat_key then new_press_repeat = true
end function
sub mfi_loader(f$)
mfi = freefile
open f$ for binary as #mfi
get #mfi, , mfi_count
for i = 1 to mfi_count
get #mfi, , mfi_o(i)
get #mfi, , mfi_s(i)
mfi_o(i) = mfi_o(i) + 1
next i
mfi_index = 1
' ----- Images -----
g_font(f_text_black, 0).image = load_gfx(mfi)
g_font(f_text_red, 0).image = load_gfx(mfi)
g_font(f_text_blue, 0).image = load_gfx(mfi)
g_font(f_text_dkblue, 0).image = load_gfx(mfi)
g_font(f_gil_white, 0).image = load_gfx(mfi)
g_font(f_map_white, 0).image = load_gfx(mfi)
g_font(f_map_grey, 0).image = load_gfx(mfi)
g_font(f_map_black, 0).image = load_gfx(mfi)
g_font(f_map_dkblue, 0).image = load_gfx(mfi)
bar_background = load_gfx(mfi)
job_background = load_gfx(mfi)
job_image = load_gfx(mfi)
window_image = load_gfx(mfi)
city_image = load_gfx(mfi)
data_image = load_gfx(mfi)
if fileexists("mfi_temp.dat") then kill "mfi_temp.dat"
end sub
function load_gfx&(mfi)
if fileexists("mfi_temp.dat") then kill "mfi_temp.dat"
mfidata = freefile
open "mfi_temp.dat" for binary as #mfidata
dat$ = space$(mfi_s(mfi_index))
get #mfi, mfi_o(mfi_index), dat$
put #mfidata, , dat$
close #mfidata
load_gfx& = loadimage("mfi_temp.dat", 32)
mfi_index = mfi_index + 1
end function
function load_sfx&(mfi)
if fileexists("mfi_temp.dat") then kill "mfi_temp.dat"
mfidata = freefile
open "mfi_temp.dat" for binary as #mfidata
dat$ = space$(mfi_s(mfi_index))
get #mfi, mfi_o(mfi_index), dat$
put #mfidata, , dat$
close #mfidata
load_sfx& = sndopen("mfi_temp.dat")
mfi_index = mfi_index + 1
end function
sub set_keybinds
' Default keybinds
d = keyboard
keybind_name$(up_key) = "UP": keybind_default(up_key, d) = 329
keybind_name$(down_key) = "DOWN": keybind_default(down_key, d) = 337
keybind_name$(left_key) = "LEFT": keybind_default(left_key, d) = 332
keybind_name$(right_key) = "RIGHT": keybind_default(right_key, d) = 334
keybind_name$(minus_key) = "MINUS": keybind_default(minus_key, d) = 13 ' Near backspace, not on numpad
keybind_name$(plus_key) = "PLUS": keybind_default(plus_key, d) = 14
keybind_name$(f1_key) = "F1": keybind_default(f1_key, d) = 60
keybind_name$(enter_key) = "ENTER": keybind_default(enter_key, d) = 29
keybind_name$(esc_key) = "ESC": keybind_default(esc_key, d) = 2
' On launch, start with defaults
set_default_keybinds
end sub
sub set_type_job_wp(t, jsq, jch, jkn, jar, jmk, jpr, jwz, jtm, jsu, jth, jme, jor, jge, jln, jsm, jnj, jcl, jbd, jdc, jmi)
type_job_wp(t, j_sq) = jsq
type_job_wp(t, j_ch) = jch
type_job_wp(t, j_kn) = jkn
type_job_wp(t, j_ar) = jar
type_job_wp(t, j_mk) = jmk
type_job_wp(t, j_pr) = jpr
type_job_wp(t, j_wz) = jwz
type_job_wp(t, j_tm) = jtm
type_job_wp(t, j_su) = jsu
type_job_wp(t, j_th) = jth
type_job_wp(t, j_me) = jme
type_job_wp(t, j_or) = jor
type_job_wp(t, j_ge) = jge
type_job_wp(t, j_ln) = jln
type_job_wp(t, j_sm) = jsm
type_job_wp(t, j_nj) = jnj
type_job_wp(t, j_cl) = jcl
type_job_wp(t, j_bd) = jbd
type_job_wp(t, j_dc) = jdc
type_job_wp(t, j_mi) = jmi
end sub
sub set_prop_wp
' ----- Proposition types -----
' Sq Ch Kn Ar Mk Pr Wz TM Su Th Me Or Ge Ln Sm Nj Cl Bd Dc Mi
set_type_job_wp salvage, 8, 10, 10, 2, 10, 5, 5, 15, 5, 8, 0, 5, 15, 2, 5, 5, 15, 0, 0, 10
set_type_job_wp mining, 8, 10, 5, 2, 10, 10, 5, 5, 5, 8, 0, 10, 15, 2, 10, 5, 5, 0, 0, 10
set_type_job_wp explore, 8, 5, 10, 15, 5, 5, 15, 5, 5, 5, 0, 10, 15, 10, 2, 5, 10, 0, 5, 0
set_type_job_wp combat, 8, 5, 15, 5, 15, 5, 5, 5, 10, 8, 0, 2, 0, 15, 15, 20, 5, 5, 5, 0
set_type_job_wp invest, 8, 5, 2, 5, 2, 5, 2, 2, 5, 10, 15, 10, 0, 5, 5, 5, 5, 10, 10, 0
set_type_job_wp invest2, 8, 5, 2, 5, 2, 15, 2, 8, 5, 5, 15, 5, 10, 5, 5, 5, 10, 5, 5, 0
set_type_job_wp oddjob, 8, 15, 2, 5, 2, 5, 5, 5, 5, 15, 15, 2, 0, 2, 2, 5, 5, 10, 10, 10
set_type_job_wp contest, 8, 5, 15, 5, 15, 5, 5, 10, 5, 5, 5, 5, 0, 15, 15, 15, 5, 5, 0, 10
' ----- Preferred stats with job and stat bonuses -----
s = neutral
stat_job_wp(s, j_sq) = 10: stat_br_wp(s, 1) = 10
stat_job_wp(s, j_ch) = 5: stat_br_wp(s, 2) = 10
stat_job_wp(s, j_kn) = 5: stat_br_wp(s, 3) = 10
stat_job_wp(s, j_ar) = 10: stat_br_wp(s, 4) = 10
stat_job_wp(s, j_mk) = 5: stat_br_wp(s, 5) = 10
stat_job_wp(s, j_pr) = 5: stat_fa_wp(s, 1) = 10
stat_job_wp(s, j_wz) = 5: stat_fa_wp(s, 2) = 10
stat_job_wp(s, j_tm) = 5: stat_fa_wp(s, 3) = 10
stat_job_wp(s, j_su) = 5: stat_fa_wp(s, 4) = 10
stat_job_wp(s, j_th) = 10: stat_fa_wp(s, 5) = 10
stat_job_wp(s, j_me) = 10: stat_lv_wp(s, 1) = 5
stat_job_wp(s, j_or) = 5: stat_lv_wp(s, 2) = 5
stat_job_wp(s, j_ge) = 5: stat_lv_wp(s, 3) = 8
stat_job_wp(s, j_ln) = 5: stat_lv_wp(s, 4) = 10
stat_job_wp(s, j_sm) = 0: stat_lv_wp(s, 5) = 15
stat_job_wp(s, j_nj) = 5: stat_lv_wp(s, 6) = 20
stat_job_wp(s, j_cl) = 5: stat_lv_wp(s, 7) = 10
stat_job_wp(s, j_bd) = 10: stat_lv_wp(s, 8) = 10
stat_job_wp(s, j_dc) = 10: stat_lv_wp(s, 9) = 10
stat_job_wp(s, j_mi) = 20: stat_lv_wp(s, 10) = 10
s = brave
stat_job_wp(s, j_sq) = 10: stat_br_wp(s, 1) = 5
stat_job_wp(s, j_ch) = 0: stat_br_wp(s, 2) = 8
stat_job_wp(s, j_kn) = 10: stat_br_wp(s, 3) = 10
stat_job_wp(s, j_ar) = 5: stat_br_wp(s, 4) = 15
stat_job_wp(s, j_mk) = 10: stat_br_wp(s, 5) = 20
stat_job_wp(s, j_pr) = 0: stat_fa_wp(s, 1) = 20
stat_job_wp(s, j_wz) = 0: stat_fa_wp(s, 2) = 15
stat_job_wp(s, j_tm) = 0: stat_fa_wp(s, 3) = 10
stat_job_wp(s, j_su) = 0: stat_fa_wp(s, 4) = 8
stat_job_wp(s, j_th) = 5: stat_fa_wp(s, 5) = 5
stat_job_wp(s, j_me) = 0: stat_lv_wp(s, 1) = 5
stat_job_wp(s, j_or) = 0: stat_lv_wp(s, 2) = 8
stat_job_wp(s, j_ge) = 0: stat_lv_wp(s, 3) = 10
stat_job_wp(s, j_ln) = 10: stat_lv_wp(s, 4) = 15
stat_job_wp(s, j_sm) = 5: stat_lv_wp(s, 5) = 20
stat_job_wp(s, j_nj) = 10: stat_lv_wp(s, 6) = 20
stat_job_wp(s, j_cl) = 0: stat_lv_wp(s, 7) = 15
stat_job_wp(s, j_bd) = 5: stat_lv_wp(s, 8) = 10
stat_job_wp(s, j_dc) = 5: stat_lv_wp(s, 9) = 8
stat_job_wp(s, j_mi) = 0: stat_lv_wp(s, 10) = 5
s = faith
stat_job_wp(s, j_sq) = 10: stat_br_wp(s, 1) = 20
stat_job_wp(s, j_ch) = 10: stat_br_wp(s, 2) = 15
stat_job_wp(s, j_kn) = 0: stat_br_wp(s, 3) = 10
stat_job_wp(s, j_ar) = 0: stat_br_wp(s, 4) = 8
stat_job_wp(s, j_mk) = 0: stat_br_wp(s, 5) = 5
stat_job_wp(s, j_pr) = 10: stat_fa_wp(s, 1) = 5
stat_job_wp(s, j_wz) = 10: stat_fa_wp(s, 2) = 8
stat_job_wp(s, j_tm) = 10: stat_fa_wp(s, 3) = 10
stat_job_wp(s, j_su) = 10: stat_fa_wp(s, 4) = 15
stat_job_wp(s, j_th) = 0: stat_fa_wp(s, 5) = 20
stat_job_wp(s, j_me) = 5: stat_lv_wp(s, 1) = 10
stat_job_wp(s, j_or) = 10: stat_lv_wp(s, 2) = 10
stat_job_wp(s, j_ge) = 10: stat_lv_wp(s, 3) = 10
stat_job_wp(s, j_ln) = 0: stat_lv_wp(s, 4) = 10
stat_job_wp(s, j_sm) = 10: stat_lv_wp(s, 5) = 10
stat_job_wp(s, j_nj) = 5: stat_lv_wp(s, 6) = 0
stat_job_wp(s, j_cl) = 10: stat_lv_wp(s, 7) = 20
stat_job_wp(s, j_bd) = 5: stat_lv_wp(s, 8) = 15
stat_job_wp(s, j_dc) = 5: stat_lv_wp(s, 9) = 10
stat_job_wp(s, j_mi) = 0: stat_lv_wp(s, 10) = 8
end sub
sub set_jn(j, nx$, np$)
job_name$(psx, j) = nx$
job_name$(psp, j) = np$
end sub
sub add_prop(nx$, np$, chapter, ptype, pstat, preward)
prop_i = prop_i + 1
prop_name$(psx, prop_i) = nx$
prop_name$(psp, prop_i) = np$
prop_chapter(prop_i) = chapter
prop_type(prop_i) = ptype
prop_stat(prop_i) = pstat
prop_reward(prop_i) = preward
prop_city(prop_i) = prop_c
end sub
sub set_prop_data
prop_i = 0
prop_c = igros
add_prop "Girl at Gulg Volcano", "Mount Gulg Mother Lode", ch2, mining, brave, item
add_prop "Sad Traveling Artist", "Minstrel in Distress", ch3, oddjob, faith, 0
add_prop "Traveling Artist Mameko", "Mameco the Minstrel", ch3, oddjob, neutral, 0
add_prop "Ringing of the Bell", "Guard Duty", ch3b, oddjob, faith, item
add_prop "Legendary Monster", "Hellspawned Beast", ch4, combat, faith, 0
add_prop "Sullen Experiment", "Metamorphosed Misery", ch4, combat, neutral, 0
add_prop "Thief Zero Reborn!", "Zerro Strikes Again", ch4, combat, brave, item
add_prop "Legendary Traces", "Ancient Wonder", ch4b, explore, neutral, land
prop_c = gariland
add_prop "Orders of the Coast Guard", "Shoreline Defense", ch2, invest2, neutral, 0
add_prop "Testimony of an Ex-miner", "Miner's Tale", ch2, mining, neutral, 0
add_prop "Stolen Ancient Writings", "Stolen Tomes", ch3, salvage, neutral, 0
add_prop "Master Math!", "Arithmeticks Tutor Wanted", ch4, oddjob, faith, 0
add_prop "Win the Magic Contest!", "Magick Melee", virgo, contest, faith, item
prop_c = dorter
add_prop "Trap of the Bandits", "Bandits", ch2, combat, faith, 0
add_prop "Discovery Race", "Frontier Marathon", ch3, explore, neutral, land
add_prop "Discovery Race 2", "Second Frontier Marathon", ch3, explore, neutral, land
add_prop "Discovery Race 3", "Third Frontier Marathon", ch3, explore, neutral, land
add_prop "Minimum's Melancholy", "Count Minimas", ch4, oddjob, brave, item
add_prop "Minimum's Melancholy", "Count Minimas", ch4, oddjob, brave, item
add_prop "Minimum's Melancholy", "Count Minimas", ch4, combat, neutral, 0
prop_c = zaland
add_prop "Salvage the Trade Ship!", "The Hindenburg", ch2, salvage, faith, 0
add_prop "Zaland Embassy", "Zaland Embassy Antiques", ch2, salvage, neutral, item
add_prop "Rolade Ore Company", "Lorraide Mine", ch3, mining, brave, 0
add_prop "Deep in Sweegy Woods", "The Siedge Weald", ch3b, explore, neutral, land
add_prop "Shy Katedona", "Cattedona", ch4b, oddjob, faith, 0
add_prop "Win the Zaland Fight!", "The Zaland Melee", aries, contest, brave, 0
prop_c = lionel
add_prop "My Little Carrot", "My Little Carrot", ch2, invest, neutral, 0
add_prop "Trade Ship Douing", "The Dawn Queen", ch3, salvage, neutral, 0
add_prop "Challenge of Zero", "Zerro's Challenge", ch3, combat, brave, item
add_prop "I saw it.", "The Trick of Light", ch3b, explore, neutral, land
add_prop "Storm of Zigolis!", "Fenland Mystery", ch3b, explore, faith, land
add_prop "Protect the Little Life", "Father's Nightmare", ch4, invest, neutral, item
add_prop "Emissary of Lionel", "Lionel Emissary", ch4b, salvage, neutral, item
prop_c = goug
add_prop "Vacancy!", "Miner Shortage", ch2, mining, neutral, 0
add_prop "Heir of Mesa", "Masa's Legacy", ch3, salvage, neutral, 0
add_prop "Machinist Contest", "Clockwork Faire", ch3, oddjob, neutral, item
add_prop "Salvage the Trade Ship!", "The Durga", ch4, salvage, brave, item
add_prop "Devil in the Dark", "Devil in the Dark", ch4, invest2, neutral, 0
add_prop "Meister Contest", "Arteficer's Contest", sagit, contest, neutral, 0
prop_c = warjilis
add_prop "Destiny of the Company", "The Highwind", ch2, salvage, faith, 0
add_prop "Concerns of a Merchant", "Merchant's Regret", ch3, explore, neutral, land
add_prop "Mountain of Rain", "Rain-Swept Slopes", ch4, explore, brave, land
add_prop "Within the Darkness", "In the Darkness", ch4, combat, faith, 0
add_prop "True Romance", "True Romance", ch4, oddjob, neutral, item
add_prop "Wandering Gambler", "Wandering Gambler", ch4, oddjob, neutral, 0
prop_c = goland
add_prop "Will of Elder Topa", "Old Tappa's Will", ch3, mining, brave, 0
add_prop "Miners Wanted!", "Coal Miners Wanted", ch4, mining, brave, 0
add_prop "Miners Wanted! 2", "More Coal Miners Wanted", ch4, mining, brave, item
add_prop "Adventurer Ramzen", "Lamzen the Adventurer", ch4, explore, neutral, land
add_prop "Defeat Golden Gotsko!", "Twilight Gustkov", ch4b, combat, neutral, 0
add_prop "Terror of Assault Cave", "Terror's Maw", ch4b, combat, faith, 0
add_prop "Dream of a Miner", "Miner's Dream", ch4b, mining, neutral, 0
prop_c = lesalia
add_prop "Sunken Salvage Tour", "Salvage Expedition", ch3, salvage, neutral, item
add_prop "Mine Excavation Tour", "Abandoned Mine", ch3, mining, neutral, item
add_prop "Discovery Tour", "Frontier Expedition", ch4, explore, neutral, land
add_prop "Thief Zero Returns!", "Zerro's Return", ch4, combat, brave, item
add_prop "If wishes come true", "Ducal Disaster", ch4b, invest, neutral, 0
add_prop "Son, Pappal!", "Young Lord Pappal", ch4b, combat, brave, 0
add_prop "Secret Door", "Cries in the Dark", ch4b, invest2, neutral, 0
prop_c = yardow
add_prop "Sailor Tour", "Diving Expedition", ch3b, salvage, brave, 0
add_prop "Envoy ship, Falcon", "The Falcon", ch4, salvage, brave, item
add_prop "Good Workplace and Job!", "Salvage Work", ch4b, salvage, neutral, 0
add_prop "Miner's Tour", "Coal Mining Expedition", ch4b, mining, neutral, 0
add_prop "Miner's Tour 2", "Second Coal Mining Expedition", ch4b, mining, neutral, item
add_prop "Win the Yardow Fight!", "The Yardrow Melee", cancer, contest, brave, item
prop_c = bervenia
add_prop "Hidden Trap at the Maze", "Endless Caverns", ch4, mining, neutral, item
add_prop "One Activity", "Past Glory", ch4, mining, brave, item
add_prop "Ruins at Bed Desert", "Beddha Sandwaste", ch4, explore, neutral, land
add_prop "Adventurer Wanted!", "Adventurers Wanted", ch4, explore, neutral, land
add_prop "I saw it! I swear!", "Shadows from the Past", ch4, explore, neutral, land
add_prop "Defeat Behemoth!", "The Behemoth", ch4b, combat, brave, 0
prop_c = riovanes
add_prop "Sea of Gredia Island", "Gleddia Isle", ch4, salvage, faith, item
add_prop "Stranded Trade Ship", "Foundered Vessel", ch4, salvage, faith, 0
add_prop "Fiar's Request", "Fia's Wish", ch4, combat, neutral, 0
add_prop "Secret Society", "Secret Society", ch4, invest2, brave, 0
add_prop "Letter to My Love", "Lettre d'amour", ch4, oddjob, neutral, item
add_prop "The Greatest Plan", "Historic Revolt", ch4b, invest2, neutral, item
add_prop "Hard Lecture", "Tutoring", ch4b, oddjob, faith, 0
prop_c = zeltennia
add_prop "Larner Channel Waves", "Rhana Straight", ch4, salvage, faith, item
add_prop "Mother", "Nightwalker", ch4, invest2, neutral, 0
add_prop "Phantom Thief Zero!", "Zerro Strikes", ch4, combat, brave, item
add_prop "Attractive Workplace", "Dredge Work", ch4b, salvage, brave, 0
add_prop "Dream child", "Missing Boy", ch4b, invest, neutral, 0
add_prop "How much is Life worth?", "Appraisal", ch4b, invest2, neutral, item
prop_c = zarghidas
add_prop "Himka Cliffs", "Himca Cliffs", ch4, mining, neutral, item
add_prop "The Lord's Ore", "Ore of the Gods", ch4, mining, neutral, item
add_prop "Death Canyon", "Death's Gorge", ch4b, mining, neutral, 0
add_prop "Defeat Whirlwind Karz!", "The Typhoon", ch4b, combat, brave, 0
add_prop "Road of Beasts", "Beastly Trail", ch4b, oddjob, faith, 0
add_prop "Memories", "Memories", ch4b, oddjob, neutral, 0
prop_c = limberry
add_prop "Poeskas Lake Bottom", "Lake Poescas Depths", ch4b, explore, brave, land
add_prop "Ominous Dungeon", "Cellar Dungeon", ch4b, explore, neutral, land
add_prop "My treasure", "Uninvited Guests", ch4b, invest2, neutral, 0
add_prop "Chocobo Restaurant", "Gysahl Greens", ch4b, oddjob, brave, item
add_prop "Wandering Gambler", "Wandering Gambler", ch4b, oddjob, neutral, 0
add_prop "Thief Zero's Last Stand", "Zerro's Final Heist", ch4b, combat, brave, 0
end sub
' Input handling routine library - keyboard, gamepad, mouse
' NOTE: Mouse not yet implemented.
' NOTE: Keybinding menu must be made separately.
' No dependencies
function new_press(b)
new_press = false
if press(b) = true and hold(b) = false then new_press = true
end function
sub update_inputs
call detect_devices
for b = 1 to keybind_count
hold(b) = press(b)
press(b) = false
d = keyboard
if dev_keyboard <> false then
z = deviceinput(dev_keyboard)
if button(keybind(b, d)) = true then press(b) = true
end if
d = gamepad
if dev_gamepad <> false and keybind(b, d) <> false then
z = deviceinput(dev_gamepad)
if keybind(b, d) < 100 then ' Button
if button(keybind(b, d)) = true then press(b) = true
' Stick handling:
' keybind() set to 101, 102 etc. is an assignment of stick 1, 2 etc. in the negative direction
' keybind() set to 201, 202 etc. is an assignment of stick 1, 2 etc. in the positive direction
elseif keybind(b, d) > 200 then ' Stick positive
if axis(keybind(b, d) - 200) > option_sensitivity * 0.1 then press(b) = true
else ' Stick negative
if axis(keybind(b, d) - 100) < -option_sensitivity * 0.1 then press(b) = true
end if
end if
next b
if dev_mouse = false then exit sub
dim mb(3)
for b = 1 to 3
mb(b) = mouse_press(b)
next b
mouse_change = false
do while mouseinput <> false and mouse_change = false
mouse_pos_x = mousex
mouse_pos_y = mousey
for b = 1 to 3
mb(b) = mousebutton(b)
if mb(b) <> mouse_press(b) then mouse_change = true
next b
loop
for b = 1 to 3
mouse_hold(b) = mouse_press(b)
mouse_press(b) = mb(b)
if mouse_press(b) = true and mouse_hold(b) = false then
mouse_press_x(b) = mouse_pos_x ' Store position mouse button was pressed
mouse_press_y(b) = mouse_pos_y
elseif mouse_press(b) = false and mouse_hold(b) = true then
mouse_release_x(b) = mouse_pos_x ' Store position mouse button was released
mouse_release_y(b) = mouse_pos_y
end if
next b
end sub
sub detect_devices
dev_keyboard = false
dev_gamepad = false
dev_mouse = false
d = devices
for n = d to 1 step -1
if left$(device$(n), 10) = "[KEYBOARD]" then dev_keyboard = n
if left$(device$(n), 12) = "[CONTROLLER]" then dev_gamepad = n
if left$(device$(n), 7) = "[MOUSE]" then dev_mouse = n
next n
end sub
sub set_press(p)
for b = 1 to keybind_count
press(b) = p
next b
end sub
sub set_hold(p)
for b = 1 to keybind_count
hold(b) = p
next b
end sub
sub press_any_key
do
limit 60
display
for b = 1 to keybind_count
if new_press(b) = true then exit sub
next b
call update_inputs
loop
end sub
sub set_default_keybinds
call detect_devices
for b = 1 to keybind_count
keybind(b, keyboard) = keybind_default(b, keyboard)
keybind(b, gamepad) = keybind_default(b, gamepad)
next b
if dev_gamepad = false then exit sub
' Eliminate any defaults that go beyond a gamepad's features
d = gamepad
for b = 1 to keybind_count
if keybind(b, d) < 100 and keybind(b, d) > lastbutton(dev_gamepad) then keybind(b, d) = false
next b
if lastaxis(dev_gamepad) < 2 then
keybind(up_key, d) = false
keybind(down_key, d) = false
keybind(left_key, d) = false
keybind(right_key, d) = false
end if
end sub
sub set_key_data
key_name$(false, keyboard) = "NOT SET"
key_name$(false, gamepad) = "NOT SET"
d = keyboard
for n = 1 to 512
key_name$(n, d) = "UNKNOWN"
next n
key_name$(2, d) = "ESC"
key_name$(60, d) = "F1"
key_name$(61, d) = "F2"
key_name$(62, d) = "F3"
key_name$(63, d) = "F4"
key_name$(64, d) = "F5"
key_name$(65, d) = "F6"
key_name$(66, d) = "F7"
key_name$(67, d) = "F8"
key_name$(68, d) = "F9"
key_name$(88, d) = "F11"
key_name$(89, d) = "F12"
key_name$(42, d) = "~"
key_name$(3, d) = "1"
key_name$(4, d) = "2"
key_name$(5, d) = "3"
key_name$(6, d) = "4"
key_name$(7, d) = "5"
key_name$(8, d) = "6"
key_name$(9, d) = "7"
key_name$(10, d) = "8"
key_name$(11, d) = "9"
key_name$(12, d) = "0"
key_name$(13, d) = "-"
key_name$(14, d) = "="
key_name$(15, d) = "BKSP"
key_name$(16, d) = "TAB"
key_name$(17, d) = "Q"
key_name$(18, d) = "W"
key_name$(19, d) = "E"
key_name$(20, d) = "R"
key_name$(21, d) = "T"
key_name$(22, d) = "Y"
key_name$(23, d) = "U"
key_name$(24, d) = "I"
key_name$(25, d) = "O"
key_name$(26, d) = "P"
key_name$(27, d) = "["
key_name$(28, d) = "]"
key_name$(44, d) = "\"
key_name$(31, d) = "A"
key_name$(32, d) = "S"
key_name$(33, d) = "D"
key_name$(34, d) = "F"
key_name$(35, d) = "G"
key_name$(36, d) = "H"
key_name$(37, d) = "J"
key_name$(38, d) = "K"
key_name$(39, d) = "L"
key_name$(40, d) = ";"
key_name$(41, d) = "'"
key_name$(29, d) = "ENTER"
key_name$(43, d) = "L SHIFT"
key_name$(45, d) = "Z"
key_name$(46, d) = "X"
key_name$(47, d) = "C"
key_name$(48, d) = "V"
key_name$(49, d) = "B"
key_name$(50, d) = "N"
key_name$(51, d) = "M"
key_name$(52, d) = ","
key_name$(53, d) = "."
key_name$(54, d) = "/"
key_name$(55, d) = "R SHIFT"
key_name$(30, d) = "L CTRL"
key_name$(58, d) = "SPACE"
key_name$(286, d) = "R CTRL"
key_name$(339, d) = "INS"
key_name$(340, d) = "DEL"
key_name$(328, d) = "HOME"
key_name$(336, d) = "END"
key_name$(330, d) = "PG UP"
key_name$(338, d) = "PG DN"
key_name$(329, d) = "UP"
key_name$(337, d) = "DOWN"
key_name$(332, d) = "LEFT"
key_name$(334, d) = "RIGHT"
key_name$(310, d) = "NUM /"
key_name$(56, d) = "NUM *"
key_name$(75, d) = "NUM -"
key_name$(79, d) = "NUM +"
key_name$(285, d) = "NUM ENTER"
key_name$(72, d) = "NUM 7"
key_name$(73, d) = "NUM 8"
key_name$(74, d) = "NUM 9"
key_name$(76, d) = "NUM 4"
key_name$(77, d) = "NUM 5"
key_name$(78, d) = "NUM 6"
key_name$(80, d) = "NUM 1"
key_name$(81, d) = "NUM 2"
key_name$(82, d) = "NUM 3"
key_name$(83, d) = "NUM 0"
key_name$(84, d) = "NUM ."
' Troublesome keyboard codes:
' 71 - Scroll Lock
' 70 - Pause
' 59 - Caps Lock
' 348 - Windows Left
' 349 - Windows Right?
' 350 - Menu
' 326 - Num Lock
d = gamepad
for n = 1 to 20
key_name$(n, d) = "BUTTON" + str$(n)
next n
for n = 1 to 8
key_name$(n + 100, d) = "AXIS" + str$(n) + "-" ' *** Check that first axis is horizontal
key_name$(n + 200, d) = "AXIS" + str$(n) + "+"
next n
end sub
sub glass_fonts(t1$, x1, y1)
' Text, font, destination image surface, position, alignment
t$ = t1$
carriage = true
if right$(t$, 1) = ";" then
carriage = false
t$ = left$(t$, len(t$) - 1)
end if
x = x1: y = y1
f = font_using
if font_align <> left_align then
' Adjust starting point based on line width, for center or right align
w = text_width(t$, f)
if font_align = center_align then w = int(w * 0.5)
x = x - w
end if
for n = 1 to len(t$)
c = asc(mid$(t$, n, 1))
w = g_font(f, c).w
putimage(x, y)-step(w, g_font(f, 0).h), g_font(f, 0).image, font_dest, (g_font(f, c).pos.x, g_font(f, c).pos.y)-step(w, g_font(f, 0).h)
x = x + w + 1
next n
font_x = x1
font_y = y1
if carriage = false then font_x = x
if carriage = true then font_y = y1 + g_font(f, 0).h
end sub
sub glass_fonts_at(t$)
glass_fonts t$, font_x, font_y
end sub
sub set_font(f, a, d&)
font_using = f
font_align = a
font_dest = d&
end sub
sub font_pos(x, y)
font_x = x
font_y = y
end sub
sub initialize_font(f)
preserve& = source
source g_font(f, 0).image
clearcolor point(0, 0), g_font(f, 0).image
i& = g_font(f, 0).image
d~& = point(1, 0) ' Detection color
' Height
g_font(f, 0).h = scan_down(1, 2, i&, d~&) - 3
y = 0
for cy = 0 to 15
y = scan_down(1, y, i&, d~&) + 1
x = 1
for cx = 0 to 15
n = (cy * 16) + cx
g_font(f, n).pos.x = x ' Source position
g_font(f, n).pos.y = y
x = scan_right(x, y, i&, d~&) + 1
g_font(f, n).w = x - g_font(f, n).pos.x - 2 ' Variable width
next cx
next cy
source preserve&
end sub
function font_height
font_height = g_font(font_using, 0).h
end function
function text_width(t$, f)
w = 0
for n = 1 to len(t$)
w = w + g_font(f, asc(mid$(t$, n, 1))).w + 1
next n
text_width = w - 1
end function
function scan_right(x1, y, i&, d~&) ' Starting position (noninclusive), image, detection color
x = x1
preserve& = source
source i&
w = width(i&)
do
x = x + 1
if x > w then call scan_error(x, y, "right")
loop until point(x, y) = d~& or x > w
scan_right = x
source preserve&
end function
function scan_down(x, y1, i&, d~&)
y = y1
preserve& = source
source i&
h = height(i&)
do
y = y + 1
if y > h then call scan_error(x, y, "down")
loop until point(x, y) = d~& or y > h
scan_down = y
source preserve&
end function
sub scan_error(x, y, t$)
t1$ = "Moved " + t$ + " beyond image at" + str$(x) + "," + str$(y)
set_font f_kharon, left_align, full_screen
glass_fonts t1$, 0, 0
display: sleep
end sub
' Math and logic routines
' No data structure or dependencies
function plus_limit(n, p, l) ' p is added to n, but can't go past l in the direction of travel
q = n + p
if sgn(q - l) = sgn(p) then q = l
plus_limit = q
end function
function half(n) ' less expensive than n / 2, less parentheses than n * 0.5
half = n * 0.5
end function
function inthalf(n) ' same, but with int() around it
inthalf = int(n * 0.5)
end function
function sq(n) ' less expensive and less parentheses than n ^ 2
' For code clarity
sq = n * n
end function
function atn1(n) ' shortcut for getting radians of eight cardinals and diagonals
' For code clarity - n represents multiple of 45 degrees, or quarter-pi radians
atn1 = n * atn(1)
end function
function degrees(d) ' pass in degrees, returns radians
degrees = atn1(d / 45)
end function
function hypo(a, b) ' pass in triangle legs, returns hypotenuse (Pythagoras)
hypo = sqr(sq(a) + sq(b)) ' squares are always positive, so no danger of imaginary component
end function
function arctan(y, x) ' atn() with safety checks, and sensitive to negative axes
arctan = 0
if x = 0 and y = 0 then exit function
a = atn1(2)
if x <> 0 then ' prevent division by zero
a = abs(atn(y / x))
if x < 0 then a = atn1(4) - a
end if
if y < 0 then a = flip_y(a)
arctan = a
end function
function flip_x(a) ' flips angle left/right
flip_x = wrap_a( (atn1(8) - wrap_a(a + atn1(2))) - atn1(2) )
end function
function flip_y(a) ' flips angle up/down
flip_y = atn1(8) - a
end function
function frames(s) ' pass a decimal as seconds.frames, returns integer frames
f = int(s) * 60
frames = int(f + ((s - int(s)) * 100))
end function
function frames_dec(s) ' like frames(), but takes seconds.decimal instead of seconds.frames,
f = int(s) * 60 ' so 0.50 will return 30 frames, not 50 frames
frames_dec = int(f + ((s - int(s)) * 60))
end function
function wrap(n, l1, h1) ' n is adjusted back within lower(l) and upper(h) bounds similar to mod operator
l = l1: h = h1 ' make sure h is never less than l, this also prevents division by zero
if h1 < l1 then
l = h1: h = l1
end if
x = (l - n) / ((h - l) + 1)
if x <> int(x) then x = x + 1
wrap = n + (int(x) * ((h - l) + 1))
end function
function wrap_a(a) ' angle a is adjusted back within 0 and 2pi, noninclusive of 2pi
x = -a / atn1(8)
if x <> int(x) then x = x + 1
wrap_a = a + (int(x) * atn1(8))
end function
function toggle(v, p, q)
if v = p then toggle = q
if v = q then toggle = p
end function
function rounding(n) ' rounds to closer integer
p = int(n)
if mod_dec(n, 1) => 0.5 then p = p + 1
rounding = p
end function
function min(n1, n2)
if n2 < n1 then min = n2 else min = n1
end function
function max(n1, n2)
if n2 > n1 then max = n2 else max = n1
end function
function pyr(n) ' produce pyramid number on n (1 + 2 + 3 ... n)
pyr = n * (n + 1) * 0.5
end function
function rand(n) ' produce random whole number from 1 to n
rand = int(rnd * n) + 1
end function
function mod_dec(n, d) ' mod operator that preserves decimal
mod_dec = n
if d = 0 then exit function ' Division by zero protection
mod_dec = ((n / d) - int(n / d)) * d
end function
function hexcolor~&(h$)
hexcolor~& = rgba32(0, 0, 0, 255)
if len(h$) <> 6 then exit function
hexcolor~& = rgba32(val("&H" + mid$(h$, 1, 2)), val("&H" + mid$(h$, 3, 2)), val("&H" + mid$(h$, 5, 2)), 255)
end function
function before$(t$, c$)
p = instr(t$, c$)
if p = false then p = len(t$) + 1
before$ = left$(t$, p - 1)
end function
function after$(t$, c$)
after$ = right$(t$, len(t$) - instr(t$, c$) - (len(c$) - 1))
end function
function between$(t$, c1$, c2$)
between$ = before$(after$(t$, c1$), c2$)
end function
function vector_x(a, v) ' convert polar vector to x component
vector_x = 0
if a = aim_n or a = aim_s then exit function ' Protect against undefined cos()
vector_x = v * cos(a)
end function
function vector_y(a, v) ' convert polar vector to y component
vector_y = 0
if a = aim_w or a = aim_e then exit function ' Protect against undefined sin()
vector_y = v * sin(a)
end function
function ellipse_focus_x(axis_x, axis_y)
ellipse_focus_x = 0
if axis_x > axis_y then ellipse_focus_x = sqr(sq(axis_x) - sq(axis_y))
end function
function ellipse_focus_y(axis_x, axis_y)
ellipse_focus_y = 0
if axis_x < axis_y then ellipse_focus_y = sqr(sq(axis_y) - sq(axis_x))
end function
function x_on_ellipse(ax, ay, angle)
select case angle
case atn1(0): ex = ax
case atn1(4): ex = ax
case atn1(2): ex = 0
case atn1(6): ex = 0
case else: ex = (ax * ay) / sqr(sq(ay) + sq(ax * tan(angle)))
end select
if angle > atn1(2) and angle < atn1(6) then ex = -ex
x_on_ellipse = ex
end function
function y_on_ellipse(ax, ay, angle)
select case angle
case atn1(0): ey = 0
case atn1(4): ey = 0
case atn1(2): ey = ay
case atn1(6): ey = ay
case else: ey = (ax * ay) / sqr(sq(ax) + sq(ay / tan(angle)))
end select
if angle > atn1(4) then ey = -ey
y_on_ellipse = ey
end function
function ellipse_tangent(ax, ay, angle)
' ax and ay are axis lengths from center of ellipse
' angle is from center of ellipse
' Returns tangent angle, facing in clockwise direction
' Point angle intersects ellipse
ix = x_on_ellipse(ax, ay, angle)
iy = y_on_ellipse(ax, ay, angle)
' Focus distance from center
fx = ellipse_focus_x(ax, ay)
fy = ellipse_focus_y(ax, ay)
' Angles from foci to intersection point
a1 = arctan(iy + fy, ix + fx)
a2 = arctan(iy - fy, ix - fx)
' Average, then right angle to get tangent angle
ellipse_tangent = wrap_a(half(a1 + a2) + atn1(2))
end function
function line_and_ellipse(x1, y1, x2, y2, axis_x, axis_y, ix, iy)
' Given a line between points (x1, y1) and (x2, y2) relative to an ellipse's center,
' find the x coordinate of the intersection between the line and the ellipse,
' closer to (x1, y1), and put output in (ix, iy).
ix = 0 ' Default to center of ellipse
iy = 0
line_and_ellipse = true ' Becomes false later if the quadratic's radical is negative
if axis_x = 0 and axis_y = 0 then exit function
fx = ellipse_focus_x(axis_x, axis_y)
fy = ellipse_focus_y(axis_x, axis_y)
dx = sgn(x2 - x1)
dy = sgn(y2 - y1)
' Handle pure vertical and horizontal
if dx = 0 or dy = 0 then
ix = x1
iy = y1
if dx = 0 and dy <> 0 and axis_x > 0 then
iy = -dy * sqr((sq(axis_y) * abs(sq(axis_x) - sq(x1))) / sq(axis_x))
end if
if dy = 0 and dx <> 0 and axis_y > 0 then
ix = -dx * sqr((sq(axis_x) * abs(sq(axis_y) - sq(y1))) / sq(axis_y))
end if
' Otherwise, run quadratic solution of line and ellipse
else
slope = (y2 - y1) / (x2 - x1)
elevation = y1 - (slope * x1)
' Quadratic coefficients
a = sq(axis_x * slope) + sq(axis_y)
b = 2 * sq(axis_x) * slope * elevation
c = sq(axis_x) * (sq(elevation) - sq(axis_y))
if sq(b) - (4 * a * c) < 0 then
line_and_ellipse = false ' Negative will fail the quadratic radical,
exit function ' calling routine must be alerted
end if
' Use x coordinate closer to (x1, y1)
ix1 = quadratic(a, b, c, 1)
ix2 = quadratic(a, b, c, -1)
if abs(x1 - ix1) < abs(x1 - ix2) then ix = ix1 else ix = ix2
iy = (slope * ix) + elevation
end if
end function
function quadratic(a, b, c, pm)
' pm is 1 or -1, to represent the +/- in the quadratic formula
if a = 0 then
print "Quadratic denominator was zero!"
display: sleep
exit function
end if
quadratic = (-b + (pm * sqr(sq(b) - (4 * a * c)))) / (2 * a)
end function
RE: "Girl at Gulg Volcano" - Final Fantasy Tactics Proposition Calculator - Pete - 03-08-2024
So when you finished coding it, what did you do with the rest of your day?
No, that's impressive. I looked through the code a couple of times, it certainly is a lot of work in several aspects of planning, creation and organizing.
+2
Pete
RE: "Girl at Gulg Volcano" - Final Fantasy Tactics Proposition Calculator - johannhowitzer - 03-09-2024
Heh, the practical side of this program, the calculating of mission success, that has existed for a couple years I think. The UI was ASCII until I decided to overhaul it and see just how close I could get to the game's own graphics and menu movement feel.
Most of the time was spent trying to get accurate, complete captures of the game's assets... right down to the drop shadows, which Final Fantasy Tactics does not blend with current alpha method. It took some testing to reveal that it just does a straight subtraction on the RGB values. So the blending had to be done by hand. It intentionally does not have any sounds, because the idea would be to run this program while playing the game, and then you'd have two sets of sounds going on at the same time.
|