Welcome, Guest
You have to register before you can post on our site.

Username/Email:
  

Password
  





Search Forums

(Advanced Search)

Forum Statistics
» Members: 483
» Latest member: aplus
» Forum threads: 2,796
» Forum posts: 26,383

Full Statistics

Latest Threads
Mean user base makes Stev...
Forum: General Discussion
Last Post: PhilOfPerth
56 minutes ago
» Replies: 17
» Views: 290
GNU C++ Compiler error
Forum: Help Me!
Last Post: Pete
3 hours ago
» Replies: 44
» Views: 503
_IIF limits two question...
Forum: General Discussion
Last Post: madscijr
5 hours ago
» Replies: 9
» Views: 155
A question on using Infor...
Forum: Help Me!
Last Post: bplus
7 hours ago
» Replies: 2
» Views: 34
What do you guys like to ...
Forum: General Discussion
Last Post: Pete
Yesterday, 05:16 PM
» Replies: 11
» Views: 176
Fast QB64 base64 encoder ...
Forum: a740g
Last Post: a740g
12-21-2024, 04:43 AM
» Replies: 3
» Views: 464
DeflatePro
Forum: a740g
Last Post: a740g
12-21-2024, 02:11 AM
» Replies: 2
» Views: 77
New QBJS Samples Site
Forum: QBJS, BAM, and Other BASICs
Last Post: dbox
12-20-2024, 06:16 PM
» Replies: 25
» Views: 908
Raspberry OS
Forum: Help Me!
Last Post: Jack
12-20-2024, 05:42 PM
» Replies: 7
» Views: 171
InForm-PE
Forum: a740g
Last Post: Kernelpanic
12-20-2024, 05:22 PM
» Replies: 80
» Views: 6,201

 
  "Girl at Gulg Volcano" - Final Fantasy Tactics Proposition Calculator
Posted by: johannhowitzer - 03-08-2024, 08:29 AM - Forum: Programs - Replies (2)

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:


.zip   Girl at Gulg Volcano.zip (Size: 210.3 KB / Downloads: 34)

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!

[Image: screenshot.png]

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

Print this item

  I can't get over this...
Posted by: Pete - 03-07-2024, 10:57 PM - Forum: Help Me! - Replies (7)

What I'm shooting for is a way to get the hardware image UNDER the text. So far, even with the _DISPLAY order statement, no luck.

Code: (Select All)
Screen 0, 0, 0, 0
Width 80, 42
Palette 5, 63
Cls
_Delay .1
_ScreenMove 0, 0
_DisplayOrder  _Software, _Hardware ' Reversing this order just eliminates the hardware image.
Overlay = _NewImage(_Width * _FontWidth, _Height * _FontHeight, 32)
x% = 7 * 16: y% = 3 * 8
_Dest Overlay
_Display

img& = _LoadImage("activate-static.png", 32)
_PutImage (x%, y%), img&
Overlay_Hardware = _CopyImage(Overlay, 33)
_PutImage (0, 0), Overlay_Hardware
_Dest 0
Color 0, 5
Locate 3, 10, 1, 7, 30
Print "sakf sfkj safjsf sfjsf";
_Display
Sleep

[Image: activate-static.png]  Right click image and "Save As" to your local QB64PE folder to test.

Pete

Print this item

  What do you graphics gurus think?..
Posted by: Pete - 03-07-2024, 07:03 PM - Forum: General Discussion - Replies (6)

If you just have a few small images, approx. 10, would it be better to load each at the top of the program and keep them in memory, or use the _LOAD statement each time they are needed? In my routine, these images overlay each other, so they get exchanged a lot; so the way I'm doing this now is to load them only when needed, and free them once loaded and displayed.

Here is the routine I'm working on: https://qb64phoenix.com/forum/showthread...8#pid23598

Thanks,

Pete

Print this item

  9tka Board Game
Posted by: Donald Foster - 03-07-2024, 04:33 AM - Forum: Donald Foster - No Replies

Hello All,

This is an older game that I posted om QB64 forum years ago

9tka is a 2 to 4 player board game which has 9 cells. Each cell has 9 spaces in it. The object of the game is to be the player who dominates the most cells. At the start of the game each player in turn places red blocker piece in each cell until each cell is filled with one red blocker in each cell. Blocker pieces can not be placed directly on the outer spaces inside the playing area. Next each player alternates placing one of their pieces on the outside border area until all spaces are filled, except for the corners. Next players takes turns sliding one of their pieces from outside the board, the border area, and slides it into the playing area until it is stopped by another piece or red blocker until all pieces are slid into the board that can be moved. The player who has the most of their pieces in a cell wins that cell. There may be a tie for cell which there is no winner of that cell. The player who wins the most number of cells is the winner.

This game uses keyboard inputs.

[Image: 9tka-Screenshot.png]

[Image: 9-TKA-Board-Game.jpg]

Code: (Select All)
_TITLE "9 by Donald L. Foster Jr."

SCREEN _NEWIMAGE(1010, 735, 256)

_PALETTECOLOR 7, _RGB32(255, 215, 0) '        Yellow
_PALETTECOLOR 9, _RGB32(0, 80, 180) '          Blue
_PALETTECOLOR 14, _RGB32(255, 140, 0) '       Orange
_PALETTECOLOR 2, _RGB32(0, 180, 0) '          Green
_PALETTECOLOR 12, _RGB32(255, 0, 0) '         Red
_PALETTECOLOR 3, _RGB32(144, 238, 144)
_PALETTECOLOR 13, _RGB32(255, 192, 203)

DIM a1(36), b1(36), d1(36), e1(36), e2(36), e3(36), e4(36), l1(36), m1(36)

a = 1: c = 5: d = 5: e = 5: f = 5: endgame = 1: FullScreen = 0
c(1) = 7: c(2) = 9: c(3) = 2: c(4) = 14

LINE (0, 0)-(1009, 734), 15, BF
LINE (76, 76)-(658, 658), 0, BF
LINE (79, 79)-(654, 654), 15, BF
x = 0
FOR z = 1 TO 9
   w = 0
   FOR y = 1 TO 9
      IF (z < 4) * (y < 4) THEN
         cell(z, y) = 1: cellcolor(z, y) = 3
      ELSEIF (z < 4) * (y > 6) THEN cell(z, y) = 3: cellcolor(z, y) = 3
      ELSEIF z < 4 THEN cell(z, y) = 2: cellcolor(z, y) = 13
      ELSEIF (z > 6) * (y < 4) THEN cell(z, y) = 7: cellcolor(z, y) = 3
      ELSEIF (z > 6) * (y > 6) THEN cell(z, y) = 9: cellcolor(z, y) = 3
      ELSEIF z > 6 THEN cell(z, y) = 8: cellcolor(z, y) = 13
      ELSEIF y < 4 THEN cell(z, y) = 4: cellcolor(z, y) = 13
      ELSEIF y > 6 THEN cell(z, y) = 6: cellcolor(z, y) = 13
      ELSE cell(z, y) = 5: cellcolor(z, y) = 3
      END IF
      LINE (79 + w, 79 + x)-(143 + w, 143 + x), cellcolor(z, y), BF
      a(z, y) = 111 + w: b(z, y) = 111 + x
      w = w + 64
   NEXT
   a1(z) = 111 + x: b1(z) = 44: a1(9 + z) = 690: b1(9 + z) = 111 + x: a1(18 + z) = 623 - x: b1(18 + z) = 690: a1(27 + z) = 44: b1(27 + z) = 623 - x
   x = x + 64
NEXT

x = 0
FOR z = 1 TO 10
   LINE (79 + x, 14)-(79 + x, 719), 0
   LINE (14, 79 + x)-(719, 79 + x), 0
   x = x + 64
NEXT

LINE (734, 0)-(1010, 735), 0, BF

COLOR 15, 0
LOCATE 44, 95: PRINT "Number of players (2 to 4). [_]";

CURSOR1:
a$ = INKEY$: IF a$ = "" THEN GOTO CURSOR1
IF ASC(a$) = 27 AND FullScreen = 0 THEN FullScreen = -1: _FULLSCREEN _SQUAREPIXELS , _SMOOTH ELSE IF ASC(a$) = 27 THEN FullScreen = 0: _FULLSCREEN _OFF
IF (VAL(a$) > 1) * (VAL(a$) < 5) THEN b = VAL(a$): LOCATE 44, 124: PRINT a$;: GOTO CURSOR1
IF ASC(a$) = 8 THEN LOCATE 44, 124: PRINT "_";: GOTO CURSOR1
IF ASC(a$) <> 13 THEN GOTO CURSOR1
IF (b < 2) + (b > 4) THEN GOTO CURSOR1

LOCATE 44, 95: PRINT "                               ";

PLAYERINDICATOR:
CIRCLE (872, 107), 28, 8: PAINT (872, 107), c(a), 8
LOCATE 10, 106: PRINT "Player:"; a;

CIRCLE (872, 300), 28, 8: PAINT (872, 300), 12, 8
LOCATE 15, 103: PRINT "Place a marker";: LOCATE 16, 97: PRINT "in one of the empty cells.";

CURSOR2:
LINE (a(c, d) - 31, b(c, d) - 31)-(a(c, d) + 31, b(c, d) + 31), cellcolor(c, d), B
LINE (a(e, f) - 31, b(e, f) - 31)-(a(e, f) + 31, b(e, f) + 31), 0, B

GETINPUT1:
a$ = INKEY$
IF a$ = "" THEN GOTO GETINPUT1
IF ASC(a$) = 27 AND FullScreen = 0 THEN FullScreen = -1: _FULLSCREEN _SQUAREPIXELS , _SMOOTH ELSE IF ASC(a$) = 27 THEN FullScreen = 0: _FULLSCREEN _OFF
IF (a$ = CHR$(0) + CHR$(72)) * (e = 2) THEN c = e: d = f: e = 8: GOTO CURSOR2
IF a$ = CHR$(0) + CHR$(72) THEN c = e: d = f: e = e - 1: GOTO CURSOR2
IF (a$ = CHR$(0) + CHR$(77)) * (f = 8) THEN c = e: d = f: f = 2: GOTO CURSOR2
IF a$ = CHR$(0) + CHR$(77) THEN c = e: d = f: f = f + 1: GOTO CURSOR2
IF (a$ = CHR$(0) + CHR$(80)) * (e = 8) THEN c = e: d = f: e = 2: GOTO CURSOR2
IF a$ = CHR$(0) + CHR$(80) THEN c = e: d = f: e = e + 1: GOTO CURSOR2
IF (a$ = CHR$(0) + CHR$(75)) * (f = 2) THEN c = e: d = f: f = 8: GOTO CURSOR2
IF a$ = CHR$(0) + CHR$(75) THEN c = e: d = f: f = f - 1: GOTO CURSOR2
IF ASC(a$) <> 13 THEN GOTO GETINPUT1

IF (d(e, f) = 0) * (marker(cell(e, f)) = 0) THEN
   d(e, f) = 5: counter = counter + 1: marker(cell(e, f)) = 1: CIRCLE (a(e, f), b(e, f)), 28, 8: PAINT (a(e, f), b(e, f)), 12, 8
ELSE GOTO GETINPUT1
END IF

IF a = b THEN a = 1 ELSE a = a + 1

IF counter < 9 THEN GOTO PLAYERINDICATOR

a = 1: c = e: d = f: g = 1: h = 1: counter = 0
LINE (a(c, d) - 31, b(c, d) - 31)-(a(c, d) + 31, b(c, d) + 31), cellcolor(c, d), B

PLAYERINDICATOR1:
CIRCLE (872, 107), 28, 0: PAINT (872, 107), c(a), 0
LOCATE 10, 106: PRINT "Player:"; a;

IF counter < 36 THEN
   LOCATE 15, 99: PRINT "    Place your piece    ";: LOCATE 16, 96: PRINT "outside of the playing board.";
ELSE LOCATE 15, 99: PRINT "     Choose a piece     ";: LOCATE 16, 96: PRINT "   to move onto the board.   ";
END IF

LINE (842, 270)-(902, 330), 0, BF

IF counter < 36 THEN GOTO cursor3

endplayer = 1

FOR z = 1 TO 36: e1(z) = 0: NEXT
FOR z = 1 TO 36
   IF d1(z) = 0 THEN GOTO ENDLOOP
   IF z < 10 THEN i = 1: j = z: k = 0: GOTO DIR1
   IF (z > 9) * (z < 19) THEN i = z - 9: j = 9: k = 0: GOTO DIR2
   IF (z > 18) * (z < 28) THEN i = 9: j = 28 - z: k = 0: GOTO DIR3
   IF z > 27 THEN i = 37 - z: j = 1: k = 0: GOTO DIR4

   DIR1:
   IF i + k < 10 THEN
      IF d(i + k, j) = 0 THEN
         IF (d1(z) = a) * (i + k = 9) THEN e1(z) = 1: l1(z) = 9: m1(z) = j: endplayer = 0: GOTO ENDLOOP
         k = k + 1: GOTO DIR1
      ELSE k = k - 1
      END IF
   END IF
   IF k >= 0 THEN
      endgame = 0
      IF (d1(z) = a) + (i + k = 9) THEN
         e1(z) = 1: l1(z) = i + k: m1(z) = z: endplayer = 0
      END IF
   END IF
   GOTO ENDLOOP

   DIR2:
   IF j - k >= 0 THEN
      IF d(i, j - k) = 0 THEN
         IF (d1(z) = a) * (j - k = 1) THEN e1(z) = 1: l1(z) = i: m1(z) = 1: endplayer = 0: GOTO ENDLOOP
         k = k + 1: GOTO DIR2
      ELSE k = k - 1
      END IF
   END IF
   IF k >= 0 THEN
      endgame = 0
      IF (d1(z) = a) + (j - k = 1) THEN
         e1(z) = 1: l1(z) = i: m1(z) = j - k: endplayer = 0
      END IF
   END IF
   GOTO ENDLOOP

   DIR3:
   IF i - k >= 0 THEN
      IF d(i - k, j) = 0 THEN
         IF (d1(z) = a) * (i - k = 1) THEN e1(z) = 1: l1(z) = 1: m1(z) = j: endplayer = 0: GOTO ENDLOOP
         k = k + 1: GOTO DIR3
      ELSE k = k - 1
      END IF
   END IF
   IF k >= 0 THEN
      endgame = 0
      IF (d1(z) = a) + (i - k = 1) THEN
         e1(z) = 1: l1(z) = i - k: m1(z) = j: endplayer = 0
      END IF
   END IF
   GOTO ENDLOOP

   DIR4:
   IF j + k < 10 THEN
      IF d(i, j + k) = 0 THEN
         IF (d1(z) = a) * (j + k = 9) THEN e1(z) = 1: l1(z) = i: m1(z) = 9: endplayer = 0: GOTO ENDLOOP
         k = k + 1: GOTO DIR4
      ELSE k = k - 1
      END IF
   END IF
   IF k >= 0 THEN
      endgame = 0
      IF (d1(z) = a) + (j + k = 9) THEN
         e1(z) = 1: l1(z) = i: m1(z) = j + k: endplayer = 0
      END IF
   END IF

   ENDLOOP:
NEXT

IF endgame = 1 THEN
   LINE (734, 0)-(1010, 735), 0, BF
   LOCATE 23, 104: PRINT "Cells Won by:";

   v = 0: w = 0
   FOR y = 1 TO b
      CIRCLE (872, 48 + w), 28, 8: PAINT (872, 48 + w), c(y), 8
      LOCATE 6 + v, 106: PRINT "Player:"; y;
      v = v + 5: w = w + 80
   NEXT

   x = 0
   FOR z = 1 TO 9
      celltotal = 0
      FOR y = 1 TO b
         IF playercell(y, z) > celltotal THEN celltotal = playercell(y, z): cellwinner(z) = y ELSE IF playercell(y, z) = celltotal THEN cellwinner(z) = 0
      NEXT
      LOCATE 25 + x, 100: PRINT "Cell"; z; ":    ";: IF cellwinner(z) = 0 THEN PRINT "No Winner"; ELSE PRINT "Player "; cellwinner(z);
      x = x + 2
   NEXT

   FOR z = 1 TO b: playerwinner(z) = 0: NEXT
   FOR z = 1 TO 9: playerwinner(cellwinner(z)) = playerwinner(cellwinner(z)) + 1: NEXT

   cells = 0
   FOR z = 1 TO b
      IF playerwinner(z) > cells THEN cells = playerwinner(z): winner = z ELSE IF playerwinner(z) = cells THEN winner = 0
   NEXT

   LOCATE 43, 97: IF winner = 0 THEN PRINT "The game ended in a Draw!!!"; ELSE PRINT " Player"; winner; "is the winner!!! ";

   LOCATE 45, 96: PRINT "Play Another Game?  ( Y / N )";

   ENDGAME:
   a$ = UCASE$(INKEY$)
   IF a$ = "" THEN GOTO ENDGAME
   IF ASC(a$) = 27 AND FullScreen = 0 THEN FullScreen = -1: _FULLSCREEN _SQUAREPIXELS , _SMOOTH ELSE IF ASC(a$) = 27 THEN FullScreen = 0: _FULLSCREEN _OFF
   IF a$ = "Y" THEN RUN
   IF a$ = "N" THEN SYSTEM
   GOTO ENDGAME

END IF

IF endplayer = 1 THEN
   LOCATE 15, 99: PRINT "You have no more moves. ";
   LOCATE 16, 96: PRINT " Press <ENTER> to continue. ";
   PRESSENTER:
   a$ = INKEY$: IF a$ = "" THEN GOTO PRESSENTER
   IF ASC(a$) = 27 AND FullScreen = 0 THEN FullScreen = -1: _FULLSCREEN _SQUAREPIXELS , _SMOOTH ELSE IF ASC(a$) = 27 THEN FullScreen = 0: _FULLSCREEN _OFF
   IF ASC(a$) <> 13 THEN GOTO PRESSENTER
   GOTO ENDTURN
END IF

cursor3:
LINE (a1(g) - 30, b1(g) - 30)-(a1(g) + 30, b1(g) + 30), 15, B
LINE (a1(h) - 30, b1(h) - 30)-(a1(h) + 30, b1(h) + 30), 0, B

GETINPUT2:
a$ = INKEY$
IF a$ = "" THEN GOTO GETINPUT2
IF ASC(a$) = 27 AND FullScreen = 0 THEN FullScreen = -1: _FULLSCREEN _SQUAREPIXELS , _SMOOTH ELSE IF ASC(a$) = 27 THEN FullScreen = 0: _FULLSCREEN _OFF
IF (a$ = CHR$(0) + CHR$(77)) * (h = 36) THEN g = h: h = 1: GOTO cursor3
IF a$ = CHR$(0) + CHR$(77) THEN g = h: h = h + 1: GOTO cursor3
IF (a$ = CHR$(0) + CHR$(75)) * (h = 1) THEN g = h: h = 36: GOTO cursor3
IF a$ = CHR$(0) + CHR$(75) THEN g = h: h = h - 1: GOTO cursor3
IF ASC(a$) <> 13 THEN GOTO GETINPUT2

IF (d1(h) = 0) * (counter < 36) THEN
   d1(h) = a
   CIRCLE (a1(h), b1(h)), 28, 0: PAINT (a1(h), b1(h)), c(a), 0
   counter = counter + 1

ELSEIF (d1(h) = a) * (e1(h) = 1) * (counter = 36) THEN
   d1(h) = 0
   e1(h) = 0
   d(l1(h), m1(h)) = a
   playercell(a, cell(l1(h), m1(h))) = playercell(a, cell(l1(h), m1(h))) + 1
   LINE (a1(h) - 30, b1(h) - 30)-(a1(h) + 30, b1(h) + 30), 15, BF
   CIRCLE (a(l1(h), m1(h)), b(l1(h), m1(h))), 28, 0
   PAINT (a(l1(h), m1(h)), b(l1(h), m1(h))), c(a), 0

ELSE GOTO GETINPUT2
END IF

ENDTURN:
IF a = b THEN a = 1 ELSE a = a + 1

endgame = 1

GOTO PLAYERINDICATOR1

.pdf   9 TKA Board Game Rules.pdf (Size: 131.44 KB / Downloads: 70)

Print this item

  Qb64 is inherently broken (simple example)
Posted by: JamesAlexander - 03-06-2024, 10:09 PM - Forum: General Discussion - Replies (12)

Just doing a loop, I should expect it to display 2 variables to display in sequence. But no...

-----------------------
For x1 = 0 To 255
For x2 = 0 To 255
        Print x2, x1
        Sleep
    Next x2
Next x1
--------------------

I cannot even get it to do a simple loop. It kicks out the compile log and gives me this overly verbose error:

"
g++  -no-pie -w -std=gnu++11 -DFREEGLUT_STATIC -I./internal/c/libqb/include -I./internal/c/parts/core/src/ -I./internal/c/parts/core/glew/include/ -DDEPENDENCY_NO_SOCKETS -DDEPENDENCY_NO_PRINTER -DDEPENDENCY_NO_ICON -DDEPENDENCY_NO_SCREENIMAGE internal/c/qbx2.cpp -c -o internal/c/qbx2.o
g++  -no-pie -w -std=gnu++11 -DFREEGLUT_STATIC -I./internal/c/libqb/include -I./internal/c/parts/core/src/ -I./internal/c/parts/core/glew/include/ -DDEPENDENCY_NO_SOCKETS -DDEPENDENCY_NO_PRINTER -DDEPENDENCY_NO_ICON -DDEPENDENCY_NO_SCREENIMAGE ./internal/c/libqb_make_000000000000.o  ./internal/c/qbx2.o -o "untitled"  ./internal/c/libqb/src/threading.o ./internal/c/libqb/src/buffer.o ./internal/c/libqb/src/filepath.o ./internal/c/libqb/src/datetime.o ./internal/c/libqb/src/rounding.o ./internal/c/libqb/src/http-stub.o ./internal/c/libqb/src/threading-posix.o ./internal/c/libqb/src/glut-main-thread.o ./internal/c/libqb/src/glut-message.o ./internal/c/libqb/src/glut-msg-queue.o ./internal/c/parts/gui/tinyfiledialogs.o ./internal/c/parts/gui/gui.o ./internal/c/parts/video/font/stub_font.o ./internal/c/parts/core/src.a  -lGL -lGLU -lX11 -lpthread -ldl -lrt
objcopy --only-keep-debug "untitled" "./internal/temp2/untitled.sym"
objcopy --strip-unneeded "untitled"
"

In Quickbasic 4.5 for Dosbox, it works fine. This is BS.

Making me really rethink just dropping this and going back to VB6 or hell, even Dos and qb4.5. At least it will work!

Print this item

  Random Access word-list maker
Posted by: PhilOfPerth - 03-06-2024, 03:59 AM - Forum: Programs - No Replies

This is the prog I use now to generate Random Access word-lists for my games.
Not very elegant, but effective.  Maybe someone else will find it useful.

Thanks to bplus and others for help with RA.

Code: (Select All)
' RA Creator creates a Random Access file from the Collins Scrabble Dictionary, up to about 280000 words of 2 to 15 letters.
' Time taken should not exceed 10 seconds.
ScreenSetup:
Screen _NewImage(1120, 820, 32)
SetFont: f& = _LoadFont("C:\WINDOWS\fonts\courbd.ttf", 24, "monospace"): _Font f&
lhs = (_DesktopWidth - 1120) / 2
_ScreenMove lhs, 86 '                                                               centre display on screen
Common Shared CPL, CTR
CPL = 1120 / _PrintWidth("X")
DefLng A '                                                                          4 bytes allows for all word lengths

GEtANumber:
Input "Max Length (2 to 15)"; ML$ '                                                 select a max word length for RA file
ML = Val(ML$)
Cls
If ML < 2 Or ML > 15 Or ML <> Int(ML) Then GoTo GEtANumber '                        ML must be integer 2 - 15
FileName$ = "RA" + ML$: RecSize = ML + 4
Print "Creating RA file "; FileName$
Print
Open "words15.txt" For Input As #1 '                                                condensed dictionary with words up to 15 letters
Open FileName$ For Random As #2 Len = RecSize
While Not EOF(1)
    Input #1, wd$
    If Len(wd$) <= ML Then
        a = a + 1: Put #2, a, wd$
    End If
Wend
Print FileName$; " has"; a; "words, to"; ML; "letters max"

For b = 1 To 10
    Get #2, b, wd$: Print wd$
Next
Print
Print "Last word is ";
Get #2, a, wd$: Print wd$
Close
Sleep: Clear: Run



Attached Files
.txt   words15.txt (Size: 2.96 MB / Downloads: 13666)
Print this item

Question Just a heads up about the NSA list of memory-safe programming languages
Posted by: madscijr - 03-05-2024, 09:40 PM - Forum: General Discussion - Replies (5)

Basically they're warning the public to stay away from C and C++ for security reasons. I know QB64PE ain't C, but because it uses a C compiler as part of its tool chain, people who use QB64PE at work at a bigger security-conscious company might find the IT department locking things down (like they always do!) so just a heads up! You might want to proactively contact your IT group and let them know why they might find minigw on your system, and provide them the info they need to allow you to keep it on your machine. The QB64PE devs might be able to tell us if QB64PE using minigw opens it up to the kinds of memory exploits that programming directly in C/C++ might, and if not, explain why, so anyone affected knows what to tell their IT dept. They've been limiting our tools and options forever, and each year it's more and more, thanks to those pesky hackers, and it's damn annoying! (But maybe better annoying than being the guy who gets blamed for the next big security breach, right?) Anyway I hope this helps!

The NSA list of memory-safe programming languages has been updated

Print this item

  Handle letters dynamically
Posted by: MasterGy - 03-05-2024, 07:59 PM - Forum: MasterGy - Replies (6)

   

You often need to place text in games.
Unfortunately, _printstring and _printwidth are significantly slow.

I have always used printing by either using the letters based on a font set or transforming an existing sentence into a hardware image.
Since I always had ideas when displaying the text, I always had to adapt the long-used solutions.

Now I tried to create a universal solution so that I no longer have to deal with this.

First you need a newimage,,32 image that uses the display. So that the screen is not SCREEN 0. After.

Let's define the most important parameters of font management!
'm_fontsettings'
with this, we determine which characters will be used to mark the beginning and end of the commands in the print string in the future, as well as determine whether the hardware letter-image creation engine generates software (32) or hardware (33) images.


Let's create fonts!
There are two options. When defining a letter with a color, or when with an image-texture.

-------------------------------------------------- -------------------------------------------------- ------------------------------
We create a font with 'm_fontadd' - color
for example
m_fontadd 0, "youngfrankexpand.ttf", _RGBA32(238, 194, 194, 255), _RGBA32(255, 0, 0, 100), 10, 150, 1, 1

parameters in order:
1.index
2.ttf file location
3. letter color (feel free to use the alpha value if you want a transparent letter)
The color of the shadow of letter 4 (if you don't want a shadow, then alpha should be 0, or simply write 0 here)
Shifting the shadow of the 5th letter. moves the shadow to the right and down relative to the letter by that many pixels
6. letter size/quality. here we define the quality of the hardware creation of the letter. The height of the image where the letter image is created.
7. is the multiplier of the horizontal size of the letter. by default 1, then we get the look of the original font. The letter can be stretched (eg 1.1 or more) or compressed (0.9 or less).
8. letter spacing. horizontal spread of letter spacing. 1 if we want the natural appearance. You can stretch the letter spacing with gaps (for example, 1.1 or more) or compress them (0.9 or less).

-------------------------------------------------- -------------------------------------------------- ------------------------------------------
'm_fontaddpic' - we create a font with an image texture
for example

m_fontaddpic 2, "youngfrankexpand.ttf", qb64logo, 255, 5, _RGBA32(0, 0, 0, 180), 10, 200, 2, 1

parameters:
1.index
2.ttf file location
3.'handle', a software (32) existing image, for example 'qb64logo = _LoadImage("qb64logo.jpg", 32)'
4. the alpha value of the image
5. the image size multiplier. Inserts a mosaic image from the image into the letter. The higher the number, the smaller the texture will be.
6. The color of the shadow  (if we don't want a shadow, then alpha should be 0, or simply write 0 here)
7. Shifting the shadow of letter 7. moves the shadow to the right and down relative to the letter by that many pixels
8. letter size/quality. here we define the quality of the hardware creation of the letter. The height of the image where the letter image is created.
9. is the multiplier of the horizontal size of the letter. by default 1, then we get the look of the original font. The letter can be stretched (eg 1.1 or more) or compressed (0.9 or less).
10. letter spacing. horizontal spread of letter spacing. 1 if we want the natural appearance. You can stretch the letter spacing with gaps (for example, 1.1 or more) or compress them (0.9 or less).


With this system, we can define any number of fonts.




Use, display

-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -----
Any placement

m_printstring 1, 200, "<fh80><fi0>mid<fh30><fi1>mini<fh200>BIG", 1, 0

parameters:
1.X position
2.Y position
3.TEXT-STRING-COMMAND
4. rotation 0=from the left edge of the image 1=from the center of the image 2=from the right edge of the image
5. magnitude of rotation. A value of 0 means no rotation

-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ----

Placement in the horizontal center of the screen

m_printstring_center 300, "<fi1><fh80>welcome<fh50> here (Center)", 0, 0

parameters:
1.Y position
2.TEXT-STRING-COMMAND
3. rotation from where? 0/1/2 (see above)
4. amount of rotation (see above)

-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---
Aligns to the right side of the screen

m_printstring_right 700, "<fi2><fh100>QB64 logo", 20, 1, .1

parameters:
1. Y position
2. TEXT-STRING-COMMAND
3. margin - leaves this many pixels near the right edge of the screen
4. rotation from where? 0/1/2 (see above)
5. amount of rotation (see above)


-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ------
TEXT-STRING-COMMAND

In this string, we can pass the text to be displayed and the commands.

Commands: if possible, indicate at the beginning of the string which font and which size we want to print!
<fh80> - Switch to font 80! (fh = font height)
<fi1> - Switch to the font with index 1! (fi=pound index)

The texts to be displayed are placed in the section between the commands.

Example:
m_printstring 1, 200, "<fh80><fi0>mid<fh30><fi1>mini<fh200>BIG", 1, .2

What this command does is display this in column 1, line 200 of the screen:
Change to pixel size 80, change to font index 0 and write 'mid', then change to pixel size 30 and change to font index 1 and write 'mini', then change 200- as pixel size and write 'BIG', and rotate all of this from the center of the text to 0.2 radian degrees!

-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -----

When we have defined a TEXT-STRING-COMMAND, we have the option of querying its size.

x$ = "<fh80><fi0>mid<fh30><fi1>mini<fh200>BIG"
pixelsize length = m_printwhidht (x$)
pixelSize height = m_printheight (x$)

The height naturally returns the size of the largest font size used.


-------------------------------------------------- -------------------------------------------------- ------------------------------
some thoughts about the system:

-when you create a font, the program stores its characteristics. Later, when we need to display a character, it checks to see if you have already taken a hardware image of it. If you don't have it yet, make it. This is good because it saves memory. It only creates a character when we use it. For example, when displaying the text 'Hello', it takes a hardware image only 4 times. The letter 'l' is only read out for the second time. This solution is very fast and saves memory.

-When we use a TEXT-STRING-COMMAND, any display subroutine or size query function (m_printwidth,m_printheight) is forced to assemble, prepare, build for the location of the characters. If the same TEXT-STRING-COMMAND is used several times, the program will not calculate it again. Lots of time saved.

- Important! when we enter a coordinate, it does not indicate the upper left corner of the text (like _printstring) !!! Each coordinate point starts from the lower left corner of the text!



Attached Files
.zip   m_fontst.zip (Size: 62.04 KB / Downloads: 57)
Print this item

  Coms port and RAM disk
Posted by: Willi Grundmann - 03-05-2024, 03:01 AM - Forum: General Discussion - Replies (3)

Hi to everyone,

I am new to QB64PE but have used a previous version of QB64 and Inform before. Over the decades I also have used QB45 for many projects.

I am retired now and am investigating the suitability of QB64PE and Inform-PE for a hobby project. I would like to find out the following.

Coms Port: Is it possible to communicate via a serial coms port? The old QB45 required a coms port on the mother boards. New computers do not have serial coms ports anymore. Would a USB based coms port adapter work with QB64PE? Code examples are very appreciated.

The version of Inform, which I used before only allowed to set up one window. For my hobby project, I would need several windows. Can the latest version of Inform do more than one window? If not then I would like to exchange data between the programs of several windows  either via files on the HDD or a RAM disk. Any reply and advice on this topic is greatly appreciated.

Best Regards

Willi

Print this item

  So what's that damn Pete up to now?
Posted by: Pete - 03-05-2024, 12:34 AM - Forum: Works in Progress - Replies (18)

Glad you asked.

Well, since March is QB Coding month, I decided to at least take a shot at "pruddying" up some of my older SCREEN 0 programs. One area that I'd like to improve upon is buttons. Now I have cheated a bit in the past, by using hardware acceleration to display graphics buttons in SCREEN 0. I'm sure I'll be going to Hell for that... but how bad can Text Hell be?

There are times I have wished we could reassign bit values to ASCII characters. I'd make the border characters have the lines displayed on the outside margins of the 8 x 8 pixel blocks. Atari allowed for that feature. That would make it possible to color inside the border. The next best thing is probably this...

[Latest Update Here: https://qb64phoenix.com/forum/showthread...8#pid23598] Includes 3 different types of buttons: Text-Hybrid, Graphics, and HTML.

Code: (Select All)
Palette 5, 63
Palette 6, 56
Color 7, 5: Cls

Const FALSE = 0, TRUE = Not FALSE

Type MouseType
    EndX As Integer
    EndY As Integer
    StartX As Integer
    StartY As Integer
    LButDown As Integer
    LtClick As Integer
End Type

Dim As MouseType Mouse
Dim As _Bit Active

Active = FALSE

Dim b(13) As String * 1
b(0) = " "
b(1) = "Ú"
b(2) = "Ä"
b(3) = "¿"
b(4) = "³"
b(5) = "À"
b(6) = "Ù"
b(7) = "Ã"
b(8) = "´"
b(9) = "Ä"
b(10) = "Â"
b(11) = "Á"
b(12) = "Þ"
b(13) = "Ý"

y = 10: x = 35

Do
    Locate y, x: Color 5, 6: Print b(13);: Color 0, 6: Print b(1); String$(10, b(2)); b(3);: Color 5, 6: Print b(12)
    Locate , x: Color 5, 6: Print b(13);: Color 0, 6: Print b(4); String$(10, b(0)); b(4);: Color 5, 6: Print b(12)
    Locate , x: Color 5, 6: Print b(13);: Color 0, 6: Print b(5); String$(10, b(2)); b(6);: Color 5, 6: Print b(12);
    Locate y + 1, x + 2: Color 7, 6: Print b(12);: Color 6, 7: Print "Activate";: Color 7, 6: Print b(13);
    'Locate y + 1, x + 2: Color 0, 6: Print b(12);: Color 5, 0: Print "Activate";: Color 0, 6: Print b(13);

    Do
        _Limit 30

        While _MouseInput: Wend

        Mouse.StartX = _MouseX
        Mouse.StartY = _MouseY
        Mouse.LButDown = _MouseButton(1)

        If Not Mouse.LButDown And Mouse.LtClick Then Mouse.LtClick = 0

        Rem Locate 5, 1: Print Mouse.StartX; Mouse.StartY; Active

        If Mouse.StartX >= x And Mouse.StartX <= x + 13 And Mouse.StartY >= 10 And Mouse.StartY <= 12 And Not Active Then
            If Not Active Then _MouseShow "LINK"
            Active = TRUE
        ElseIf Active Eqv Mouse.StartX < x Or Mouse.StartX > x + 13 Or Mouse.StartY < 10 Or Mouse.StartY > 12 Then
            If Active Then _MouseShow "DEFAULT"
            Active = FALSE
        End If

        b$ = InKey$
        If Len(b$) Or Active And Mouse.LButDown And Mouse.LtClick = 0 Then
            Mouse.LtClick = -1
            If b$ = Chr$(27) Then System
            Color 0, 7
            Locate y, x: Color 5, 7: Print b(13);: Color 0, 7: Print b(1); String$(10, b(2)); b(3);: Color 5, 7: Print b(12)
            Locate , x: Color 5, 7: Print b(13);: Color 0, 7: Print b(4); String$(10, b(0)); b(4);: Color 5, 7: Print b(12)
            Locate , x: Color 5, 7: Print b(13);: Color 0, 7: Print b(5); String$(10, b(2)); b(6);: Color 5, 7: Print b(12);
            ' Locate y + 1, x + 2: Color 7, 7: Print b(12);: Color 0, 7: Print "Activate";: Color 7, 7: Print b(13);
            _Delay .1
            Exit Do
        End If
    Loop
Loop

If I tweak it more it will be by using the QB64 RGB color statements to improve on the shades of grey.

Use the mouse to hover and left click.

If anyone else has some nifty button stuff to share, please post 'em.

Happy QB Coding Month!

Pete

Print this item