'OHRRPGCE CUSTOM - Item Editor
'(C) Copyright 1997-2025 James Paige, Ralph Versteegen, and the OHRRPGCE Developers
'Dual licensed under the GNU GPL v2+ and MIT Licenses. Read LICENSE.txt for terms and disclaimer of liability.
'
#include "config.bi"
#include "const.bi"
#include "udts.bi"
#include "custom.bi"
#include "allmodex.bi"
#include "common.bi"
#include "loading.bi"
#include "customsubs.bi"
#include "thingbrowser.bi"
#include "cglobals.bi"
#include "editorkit.bi"


'--Local SUBs
DECLARE SUB item_editor_equipbits(item as ItemDef)
DECLARE SUB item_editor_elementals(item as ItemDef)

SUB item_editor ()
 DIM itemb as ItemBrowser
 itemb.browse(-1, , @individual_item_editor)
END SUB

' an FnEditor
FUNCTION item_picker (recindex as integer = -1) as integer
 DIM itemb as ItemBrowser
 RETURN itemb.browse(recindex, , @individual_item_editor, NO)
END FUNCTION

FUNCTION item_picker_or_none (recindex as integer = -1) as integer
 DIM itemb as ItemBrowser
 RETURN itemb.browse(recindex - 1, YES , @individual_item_editor, NO) + 1
END FUNCTION

'ITEMFIXME remove this
LOCAL SUB read_item_strings(itembuf() as integer, byref item_name as string, byref info as string)
 item_name = readbadbinstring(itembuf(), 0, 8)
 info = readbadbinstring(itembuf(), 9, 36)
END SUB

'ITEMFIXME remove this
LOCAL SUB write_item_strings(itembuf() as integer, item_name as string, info as string)
 writebadbinstring item_name, itembuf(), 0, 8
 writebadbinstring info, itembuf(), 9, 36
END SUB

SUB populate_eq_slot_names(eq_slot_names() as string)
 eq_slot_names(0) = readglobalstring(38, "Weapon", 10)
 FOR i as integer = 0 TO 3
  eq_slot_names(i + 1) = readglobalstring(25 + i, "Armor" & i+1)
 NEXT i
 FOR i as integer = 0 TO UBOUND(eq_slot_names)
  IF LEN(eq_slot_names(i)) = 0 THEN eq_slot_names(i) = "Equip slot " & i
 NEXT
END SUB

FUNCTION summarize_item_equipability(item as ItemDef) as string
 DIM eq_slot_names(4) as string
 populate_eq_slot_names eq_slot_names()
 DIM summary as string
 DIM sep as string = ""
 FOR i as integer = 0 TO 4
  IF item.eqslots(i) THEN
   summary &= sep & eq_slot_names(i)
   sep = "/"
  END IF
 NEXT i
 IF summary = "" THEN summary = "NEVER EQUIPPED"
 RETURN summary
END FUNCTION

'-----------------------------------------------------------------------

TYPE ItemEditor EXTENDS EditorKit
 DECLARE CONSTRUCTOR(item_id as integer)
 DECLARE DESTRUCTOR
 DECLARE SUB define_items()
 DECLARE SUB load()
 DECLARE SUB save()
 DECLARE SUB save_new()
 DECLARE SUB draw_underlays()
 DECLARE SUB reload_sprite()
 id as integer
 item as ItemDef
 eq_slot_names(4) as string
 underlay as Slice Ptr
 wep_sl as Slice Ptr
 handle_pos_sl as Slice ptr
 preview_wep_frame as integer
 STATIC clipboard_item as ItemDef ptr  'For copy/pasting, NULL if nothing copied
 undo_item as ItemDef ptr  'Just to undo pasting. NULL if nothing
 can_copy_and_paste as bool
END TYPE
DIM ItemEditor.clipboard_item as ItemDef ptr

CONSTRUCTOR ItemEditor(item_id as integer)
 populate_eq_slot_names eq_slot_names()

 'Add a new item if an out-of-range item_id was requested
 IF item_id > gen(genMaxItem) THEN
  id = gen(genMaxItem) + 1
 ELSE
  id = item_id
 ENd IF

 exit_menu_text = "Previous Menu"
 exit_submenu_text = "Back to Item Menu"

 setup_record_switching id, 0, gen(genMaxItem), , "Item", maxMaxItems
 
 'Set up the weapon preview underlay
 underlay = NewSliceOfType(slContainer)
 underlay->Fill = YES
 wep_sl = NewSliceOfType(slSprite)
 SetSliceParent wep_sl, underlay
 ReAlignSlice wep_sl, alignRight, alignCenter, alignRight, alignCenter
 wep_sl->X = -20
 preview_wep_frame = 1
 handle_pos_sl = NewSliceOfType(slRectangle)
 SetSliceParent handle_pos_sl, wep_sl
 handle_pos_sl->x = -1
 handle_pos_sl->y = -1
 handle_pos_sl->Width = 3
 handle_pos_sl->Height = 3
 ReAlignSlice handle_pos_sl, alignLeft, alignLeft, alignCenter, alignCenter
 ChangeRectangleSlice handle_pos_sl, , , uiSelectedItem2 * -1 - 1, borderLine, transHollow
END CONSTRUCTOR

DESTRUCTOR ItemEditor()
 DeleteSlice @underlay
 IF undo_item THEN DELETE undo_item
END DESTRUCTOR

SUB ItemEditor.load()
 IF id > gen(genMaxItem) THEN save_new
 loaditemdata item, id
 reload_sprite
END SUB

SUB ItemEditor.save_new()
 DIM new_item as ItemDef = ItemDef()
 saveitemdata new_item, id
 gen(genMaxItem) = id
 'REDIMs itemtags
 load_special_tag_caches
END SUB

SUB ItemEditor.save()
 saveitemdata item, id
END SUB

SUB ItemEditor.define_items()

 '----------------------------
 IF submenu = "statbonus" THEN
 
 helpkey = "equipment_stat_bonuses"

 FOR i as integer = 0 TO statLast
  defint statnames(i) + " Bonus:", item.stat_bonuses.sta(i), -32768, 32767
  DIM cap as integer = gen(genStatCap + i)
  IF cap > 0 ANDALSO item.stat_bonuses.sta(i) > cap THEN
   set_caption item.stat_bonuses.sta(i) & " [stat capped to " & cap & "]"
  END IF
 NEXT

 '----------------------------
 ELSE '--main menu

 helpkey = "item_editor"

 can_copy_and_paste = YES

 def_record_switcher
 'Only do copy-pasting on the main menu. Not in sub-menus
 '(We don't want to create the false impression that only the contents of the sub-menu would be pasted)
 'The copy-paste is implemented at the end of the main menu definition
 IF selected THEN
  IF clipboard_item THEN
   set_tooltip "Alt-C/V to copy/paste item definition"
  ELSE
   set_tooltip "Alt-C to copy item definition"
  END IF
 END IF
 
 defstr "Name:", item.name, 8
 IF selected THEN can_copy_and_paste = NO
 defstr "Info:", item.info, 36
 IF selected THEN can_copy_and_paste = NO
 defint "Value:", item.buy_price, 0, 32767

 defint "Maximum stack size:", item.stacksize, 0, 99
 caption_default_or_int 0, "Default (99)"

 'We can split these apart after the switch from ITM to items.reld (if we wish)
 defitem "Consumability:"
 DIM consumability as integer = 0
 IF item.consumed_by_use THEN consumability = 1
 IF item.cannot_be_sold_or_dropped THEN consumability = 2
 DIM usability_captions(...) as string = {"Unlimited Use", "Consumed By Use", "Cannot be Sold/Dropped"}
 edit_int_enum consumability, usability_captions()
 item.consumed_by_use = (consumability = 1)
 item.cannot_be_sold_or_dropped = (consumability = 2)

 IF defitem_act("Equippable as...:") THEN
  editbools item.eqslots(), eq_slot_names()
  reload_sprite
 END IF
 IF refresh THEN set_caption summarize_item_equipability(item)
 
 defitem "When used as an item in battle:"
 edit_as_attack item.battle_items_menu_attack, Or_None
 IF value = -1 THEN set_caption "NOTHING"
 set_tooltip THINGGRABBER_TOOLTIP

 IF item.eqslots(0) THEN
  section "As a weapon"
  preview_wep_frame = 1
  
  defitem "When used as a Weapon:"
  edit_as_attack item.battle_weapon_attack, Or_None
  IF value = -1 THEN set_caption "NOTHING"
  set_tooltip THINGGRABBER_TOOLTIP
  
  defitem "Weapon Picture:"
  IF edit_as_spriteset(item.wep_pic, sprTypeWeapon) THEN
  END IF

  defitem "Weapon Palette:"
  IF edit_as_palette(item.wep_pal, sprTypeWeapon, item.wep_pic) THEN
  END IF
 
  IF defitem_act("Handle position (A)...") THEN
   ChangeSpriteSlice wep_sl, , , , 0
   xy_position_on_sprite_slice wep_sl, item.wep_handle(0).x, item.wep_handle(0).y, "Weapon handle position", "xy_weapon_handle"
  END IF
  IF selected THEN preview_wep_frame = 0

  IF defitem_act("Handle position (B)...") THEN
   ChangeSpriteSlice wep_sl, , , , 1
   xy_position_on_sprite_slice wep_sl, item.wep_handle(1).x, item.wep_handle(1).y, "Weapon handle position", "xy_weapon_handle"
  END IF
  
  reload_sprite
 END IF

 IF item_is_equippable(item) THEN
  IF defitem_act("Stat Bonuses...") THEN 
   enter_submenu "statbonus"
  END IF

  IF defitem_act("Elemental Resists...") THEN
   item_editor_elementals item
  END IF
  
  IF defitem_act("Who Can Equip?...") THEN
   item_editor_equipbits item
  END IF
 END IF
 
 section "When used out of battle"

 defitem "Cure Attack:"
 IF item.text_box >= 0 ORELSE item.teach_spell >= 0 THEN
  item.oob_attack = -1
  set_disabled
 ELSE
  edit_as_attack item.oob_attack, Or_None
  IF value = -1 THEN set_caption "NOTHING"
 END IF
 set_tooltip THINGGRABBER_TOOLTIP

 defitem "Text Box:"
 IF item.oob_attack >= 0 ORELSE item.teach_spell >= 0 THEN
  item.text_box = -1
  set_disabled
 ELSE
  edit_as_textbox item.text_box, Or_None
  IF value = -1 THEN set_caption "NOTHING"
  IF value = 0 THEN set_caption "(Box 0 not supported here)"
 END IF
 set_tooltip THINGGRABBER_TOOLTIP

 defitem "Teach Spell:"
 IF item.oob_attack >= 0 ORELSE item.text_box >= 0 THEN
  item.teach_spell = -1
  set_disabled
 ELSE
  edit_as_attack item.teach_spell, Or_None
  IF value = -1 THEN set_caption "NOTHING"
 END IF
 set_tooltip THINGGRABBER_TOOLTIP

 section "Automatically set tags"

 defitem "Own item:"
 edit_as_tag_id item.tags.have_tag
 IF edited THEN itemtags(id) = item.tags

 defitem "Is in inventory:"
 edit_as_tag_id item.tags.in_inventory_tag
 IF edited THEN itemtags(id) = item.tags

 IF item_is_equippable(item) THEN
  defitem "Equipped by any hero:"
  edit_as_tag_id item.tags.is_equipped_tag
  IF edited THEN itemtags(id) = item.tags
  
  defitem "Equipped by hero in active party:"
  edit_as_tag_id item.tags.is_actively_equipped_tag
  IF edited THEN itemtags(id) = item.tags
 END IF

 IF phase = processing THEN
  IF can_copy_and_paste THEN
   IF keyval(scAlt) > 0 ANDALSO keyval(scC) > 1 THEN
    IF clipboard_item THEN DELETE clipboard_item
    clipboard_item = NEW ItemDef(item)
    show_overlay_message "Copied item", 0.75
   END IF
   IF clipboard_item ANDALSO keyval(scAlt) > 0 ANDALSO keyval(scV) > 1 THEN
    IF undo_item THEN DELETE undo_item
    undo_item = NEW ItemDef(item)
    item = *clipboard_item
    state.need_update = YES
    show_overlay_message "Pasted item (Ctrl-Z to undo)", 1.1
   END IF
  END IF

  IF undo_item ANDALSO keyval(scCtrl) > 0 ANDALSO keyval(scZ) > 1 THEN
   item = *undo_item
   'DELETE undo_item
   'undo_item = NULL
   state.need_update = YES
   show_overlay_message "Undid paste", 0.75
  END IF
 END IF

 END IF '--End of main menu
 '----------------------------

END SUB

SUB ItemEditor.reload_sprite()
 ChangeSpriteSlice wep_sl, sprTypeWeapon, item.wep_pic, item.wep_pal, preview_wep_frame
 handle_pos_sl->x = item.wep_handle(preview_wep_frame).x
 handle_pos_sl->y = item.wep_handle(preview_wep_frame).y
 wep_sl->Visible = IIF(item.eqslots(0), YES, NO)
END SUB

SUB ItemEditor.draw_underlays ()
 DrawSlice underlay, vpage
END SUB

'-----------------------------------------------------------------------

SUB item_editor_elementals(item as ItemDef)
 DIM elementals(gen(genNumElements) - 1) as single
 FOR i as integer = 0 TO gen(genNumElements) - 1
  elementals(i) = item.elemental_resist(i)
  IF gen(genEquipMergeFormula) = 2 THEN  'additive merging
   elementals(i) -= 1.0
  END IF
 NEXT
 common_elementals_editor elementals(), "item_elementals", (gen(genEquipMergeFormula) = 2)
 FOR i as integer = 0 TO gen(genNumElements) - 1
  IF gen(genEquipMergeFormula) = 2 THEN  'additive merging
   elementals(i) += 1.0
  END IF
  item.elemental_resist(i) = elementals(i)
 NEXT
END SUB

' Who Can Equip? menu
SUB item_editor_equipbits(item as ItemDef)
 DIM hero_id as integer
 DIM bitnames(-1 TO maxMaxHero) as string
 FOR hero_id = 0 TO gen(genMaxHero)
  bitnames(hero_id) = getheroname(hero_id)
  IF LEN(bitnames(hero_id)) = 0 THEN bitnames(hero_id) = "Hero " & hero_id
 NEXT
 editbitset item.equip_by_bits(), 0, bitnames(), , , , item.name & " is equippable by..."
END SUB

'-----------------------------------------------------------------------


FUNCTION individual_item_editor(item_id as integer) as integer

 IF item_id > maxMaxItems THEN
  visible_debug "Can't edit item id > " & maxMaxItems
  RETURN -1
 END IF

 DIM editor as ItemEditor = ItemEditor(item_id)
 editor.run()
 
 RETURN editor.id
END FUNCTION

'-----------------------------------------------------------------------


'This elemental resistance editor is shared by the hero and item editors
SUB common_elementals_editor(elementals() as single, helpfile as string, byval showsign as integer = 0)
 DIM elementnames() as string
 getelementnames elementnames()
 DIM float_reprs(gen(genNumElements) - 1) as string
 DIM menu(1 + gen(genNumElements) - 1) as string
 DIM menu_display(UBOUND(menu)) as string
 DIM selectst as SelectTypeState
 DIM st as MenuState
 st.last = UBOUND(menu)
 st.autosize = YES
 st.need_update = YES

 FOR i as integer = 0 TO gen(genNumElements) - 1
  float_reprs(i) = format_percent(elementals(i))
  elementnames(i) = rpad(elementnames(i), " ", 15, clipRight)
 NEXT

 DO
  setwait 55
  setkeys YES
  IF keyval(ccCancel) > 1 THEN EXIT DO
  IF keyval(scF1) > 1 THEN show_help helpfile
  IF st.pt = 0 THEN
   IF enter_space_click(st) THEN EXIT DO
  ELSE
   IF percent_grabber(elementals(st.pt - 1), float_reprs(st.pt - 1), -1000, 1000) THEN st.need_update = YES
  END IF
  usemenu st

  IF st.need_update THEN
   st.need_update = NO
   menu(0) = "Previous Menu"
   FOR i as integer = 0 TO gen(genNumElements) - 1
    menu(i + 1) = "Damage from " + elementnames(i) + ": "
    IF showsign THEN
     'positive values get explicit + prefix
     IF LEFT(float_reprs(i), 1) <> "-" THEN menu(i + 1) += "+"
    END IF
    menu(i + 1) += float_reprs(i)
   NEXT
  END IF
  IF select_by_typing(selectst, NO) THEN
   select_on_word_boundary menu(), selectst, st
  END IF

  clearpage vpage
  highlight_menu_typing_selection menu(), menu_display(), selectst, st
  standardmenu menu_display(), st, , , vpage
  setvispage vpage
  dowait
 LOOP
 setkeys
END SUB

'-----------------------------------------------------------------------
