Rename osx directory to src since it's now the official source base

This commit is contained in:
2014-12-28 12:12:38 -05:00
parent ca15b2781e
commit af0ee110c6
228 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>.exg</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>boesave</string>
<key>CFBundleTypeName</key>
<string>SavedGame</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>beSV</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<false/>
<key>NSPersistentStoreTypeKey</key>
<string>Binary</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>BoECharEd</string>
<key>CFBundleIdentifier</key>
<string>com.spidweb.boepceditor</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>blxe</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>

BIN
src/pcedit/BoECharEd.icns Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/pcedit/bladespced.rsrc Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

286
src/pcedit/pc.action.cpp Normal file
View File

@@ -0,0 +1,286 @@
#include <cstdio>
#include "pc.graphics.h"
#include "pc.global.h"
#include "classes.h"
#include "pc.editors.h"
#include "pc.fileio.h"
#include "pc.action.h"
#include "graphtool.h"
#include "soundtool.h"
#include "mathutil.h"
#include "dlogutil.hpp"
#include <boost/lexical_cast.hpp>
/* Adventure globals */
//extern party_record_type party;
//extern outdoor_record_type outdoors[2][2];
//extern current_town_type c_town;
//extern big_tr_type t_d;
//extern town_item_list t_i;
//extern unsigned char out[96][96],out_e[96][96];
//extern setup_save_type setup_save;
//extern stored_items_list_type stored_items[3];
//extern stored_town_maps_type town_maps;
//extern stored_outdoor_maps_type o_maps;
extern cUniverse univ;
//extern bool ed_reg;
//extern long ed_flag,ed_key;
extern sf::RenderWindow mainPtr;
extern bool file_in_mem;
//extern long register_flag;
extern sf::Texture pc_gworld;
extern bool diff_depth_ok;
short which_pc_displayed,store_pc_trait_mode,store_which_to_edit;
extern short current_active_pc;
char empty_string[256] = " ";
extern rectangle pc_area_buttons[6][4] ; // 0 - whole 1 - pic 2 - name 3 - stat strs 4,5 - later
extern rectangle item_string_rects[24][4]; // 0 - name 1 - drop 2 - id 3 -
extern rectangle pc_info_rect;
extern rectangle name_rect;
extern rectangle pc_race_rect;
extern rectangle edit_rect[5][2];
//extern rectangle pc_area_buttons[6][6] ; // 0 - whole 1 - pic 2 - name 3 - stat strs 4,5 - later
//extern rectangle item_string_rects[24][4]; // 0 - name 1 - drop 2 - id 3 -
//short mode; // ignore,
bool handle_action(sf::Event event) {
short i;
location the_point;
bool to_return = false;
the_point = {event.mouseButton.x, event.mouseButton.y};
if(!file_in_mem)
return false;
for(i = 0; i < 6; i++)
if((the_point.in(pc_area_buttons[i][0])) &&
(univ.party[i].main_status != eMainStatus::ABSENT)) {
do_button_action(0,i);
current_active_pc = i;
redraw_screen();
}
for(i = 0; i < 5; i++)
if((the_point.in(edit_rect[i][0])) &&
(univ.party[current_active_pc].main_status != eMainStatus::ABSENT)) {
do_button_action(0,i + 10);
switch(i) {
case 0:
display_pc(current_active_pc,0,NULL);
break;
case 1:
display_pc(current_active_pc,1,NULL);
break;
case 2:
pick_race_abil(&univ.party[current_active_pc],0);
break;
case 3:
spend_xp(current_active_pc,1,NULL);
break;
case 4:
edit_xp(&univ.party[current_active_pc]);
break;
}
}
for(i = 0; i < 24; i++)
if((the_point.in(item_string_rects[i][1])) && // drop item
univ.party[current_active_pc].items[i].variety != eItemType::NO_ITEM) {
flash_rect(item_string_rects[i][1]);
take_item(current_active_pc,i);
}
for(i = 0; i < 24; i++)
if((the_point.in(item_string_rects[i][2])) && // identify item
univ.party[current_active_pc].items[i].variety != eItemType::NO_ITEM) {
flash_rect(item_string_rects[i][2]);
univ.party[current_active_pc].items[i].ident = true;
}
return to_return;
}
void flash_rect(rectangle /*to_flash*/) {
// TODO: Think of a good way to do this
//InvertRect (&to_flash);
play_sound(37);
sf::sleep(time_in_ticks(5));
}
static bool get_num_event_filter(cDialog& me, std::string id, eKeyMod) {
me.toast(id == "okay");
me.setResult<long long>(me["number"].getTextAsNum());
return true;
}
//0 - gold 1 - food
void edit_gold_or_food(short which_to_edit) {
location view_loc;
store_which_to_edit = which_to_edit;
make_cursor_sword();
cDialog dlog("get-num");
dlog["okay"].attachClickHandler(get_num_event_filter);
if(which_to_edit == 0)
dlog["prompt"].setText("How much gold do you want?");
else dlog["prompt"].setText("How much food do you want?");
dlog["number"].setTextToNum((which_to_edit == 0) ? univ.party.gold : univ.party.food);
dlog.run();
int dialog_answer = minmax(0,25000,dlog.getResult<long long>());
if(which_to_edit == 0)
univ.party.gold = dialog_answer;
else
univ.party.food = dialog_answer;
}
void edit_day() {
location view_loc;
make_cursor_sword();
cDialog dlog("edit-day");
dlog["okay"].attachClickHandler(get_num_event_filter);
dlog["number"].setTextToNum(((univ.party.age) / 3700) + 1);
dlog.run();
long long dialog_answer = minmax(0,500,dlog.getResult<long long>());
univ.party.age = (long long) (3700) * (long long) (dialog_answer);
}
void combine_things(short pc_num) {
short i,j,test;
for(i = 0; i < 24; i++) {
if(univ.party[pc_num].items[i].variety != eItemType::NO_ITEM &&
(univ.party[pc_num].items[i].type_flag > 0) && (univ.party[pc_num].items[i].ident)) {
for(j = i + 1; j < 24; j++)
if(univ.party[pc_num].items[j].variety != eItemType::NO_ITEM &&
(univ.party[pc_num].items[j].type_flag == univ.party[pc_num].items[i].type_flag)
&& (univ.party[pc_num].items[j].ident)) {
// add_string_to_buf("(items combined)");
test = (short) (univ.party[pc_num].items[i].charges) + (short) (univ.party[pc_num].items[j].charges);
if(test > 125) {
univ.party[pc_num].items[i].charges = 125;
// ASB("Can have at most 125 of any item.");
}
else univ.party[pc_num].items[i].charges += univ.party[pc_num].items[j].charges;
if(univ.party[pc_num].equip[j]) {
univ.party[pc_num].equip[i] = true;
univ.party[pc_num].equip[j] = false;
}
take_item(pc_num,j);
}
}
if(univ.party[pc_num].items[i].variety != eItemType::NO_ITEM && univ.party[pc_num].items[i].charges < 0)
univ.party[pc_num].items[i].charges = 1;
}
}
bool give_to_pc(short pc_num,cItemRec item, short /*print_result*/) {
short free_space;
if(item.variety == eItemType::NO_ITEM)
return true;
if((free_space = pc_has_space(pc_num)) == 24 || univ.party[pc_num].main_status != eMainStatus::ALIVE)
return false;
else {
univ.party[pc_num].items[free_space] = item;
combine_things(pc_num);
return true;
}
return false;
}
bool give_to_party(cItemRec item,short print_result) {
short i = 0;
while(i < 6) {
if(give_to_pc(i,item,print_result))
return true;
i++;
}
return false;
}
void give_gold(short amount,bool /*print_result*/) {
univ.party.gold = univ.party.gold + amount;
}
bool take_gold(short amount,bool /*print_result*/) {
if(univ.party.gold < amount)
return false;
univ.party.gold = univ.party.gold - amount;
return true;
}
short pc_has_space(short pc_num) {
short i = 0;
while(i < 24) {
if(univ.party[pc_num].items[i].variety == eItemType::NO_ITEM)
return i;
i++;
}
return 24;
}
//short pc_num,which_item; // if which_item > 20, don't update stat win, item is which_item - 20
void take_item(short pc_num,short which_item) {
short i;
if(univ.party[pc_num].weap_poisoned == which_item && univ.party[pc_num].status[eStatus::POISONED_WEAPON] > 0) {
// add_string_to_buf(" Poison lost. ");
univ.party[pc_num].status[eStatus::POISONED_WEAPON] = 0;
}
if(univ.party[pc_num].weap_poisoned > which_item && univ.party[pc_num].status[eStatus::POISONED_WEAPON] > 0)
univ.party[pc_num].weap_poisoned--;
for(i = which_item; i < 23; i++) {
univ.party[pc_num].items[i] = univ.party[pc_num].items[i + 1];
univ.party[pc_num].equip[i] = univ.party[pc_num].equip[i + 1];
}
univ.party[pc_num].items[23].variety = eItemType::NO_ITEM;
univ.party[pc_num].equip[23] = false;
}
void edit_xp(cPlayer *pc) {
location view_loc;
make_cursor_sword();
cDialog dlog("edit-xp");
dlog["okay"].attachClickHandler(get_num_event_filter);
dlog["number"].setTextToNum(pc->experience);
dlog["perlevel"].setTextToNum(pc->get_tnl());
dlog.run();
int dialog_answer = minmax(0,10000,abs(dlog.getResult<long long>()));
pc->experience = dialog_answer;
}

12
src/pcedit/pc.action.h Normal file
View File

@@ -0,0 +1,12 @@
#include <SFML/Window/Event.hpp>
#include "dialog.hpp"
bool handle_action(sf::Event event);
void flash_rect(rectangle to_flash);
void edit_gold_or_food(short which_to_edit);
void display_pc(short pc_num,short mode,cDialog* parent);
void display_alchemy(bool allowEdit);
bool spend_xp(short pc_num, short mode, cDialog* parent);
void edit_day();
void edit_xp(cPlayer *pc);

View File

@@ -0,0 +1,43 @@
//
// pc.appleevents.mm
// BoE
//
// Created by Celtic Minstrel on 14-04-16.
//
//
#include <Cocoa/Cocoa.h>
extern bool verify_restore_quit(bool mode);
extern bool All_Done;
typedef NSAppleEventDescriptor AEDescr;
@interface AppleEventHandler : NSObject
-(void)handleOpenDoc:(AEDescr*)theAppleEvent withReply: (AEDescr*)reply;
-(void)handleQuit:(AEDescr*)theAppleEvent withReply: (AEDescr*)reply;
@end
void set_up_apple_events(); // Suppress "no prototype" warning
void set_up_apple_events() {
AppleEventHandler* aeHandler = [[AppleEventHandler alloc] init];
NSAppleEventManager* AEmgr = [NSAppleEventManager sharedAppleEventManager];
[AEmgr setEventHandler: aeHandler andSelector: @selector(handleOpenDoc:withReply:)
forEventClass: kCoreEventClass andEventID: kAEOpenDocuments];
[AEmgr setEventHandler: aeHandler andSelector: @selector(handleQuit:withReply:)
forEventClass: kCoreEventClass andEventID: kAEQuitApplication];
}
@implementation AppleEventHandler
-(void)handleOpenDoc:(AEDescr*)theAppleEvent withReply: (AEDescr*)reply {
(void) theAppleEvent; // Suppress "unused parameter" warning
(void) reply;
// TODO: Handle this
}
-(void)handleQuit:(AEDescr*)theAppleEvent withReply: (AEDescr*)reply {
(void) theAppleEvent; // Suppress "unused parameter" warning
(void) reply;
All_Done = verify_restore_quit(0);
}
@end

631
src/pcedit/pc.editors.cpp Normal file
View File

@@ -0,0 +1,631 @@
#include "pc.graphics.h"
#include "pc.global.h"
#include "classes.h"
#include "pc.editors.h"
#include "graphtool.h"
#include "dialog.hpp"
#include "control.hpp"
#include "button.hpp"
#include "dlogutil.hpp"
#include "winutil.h"
#include <boost/lexical_cast.hpp>
/*
* These three are not declared in any included header.
* Instead they are declared in pc.actions.h, which is not
* included here because it contains additional functions that
* should not be available to the game (which also includes
* this file).
*
* For the game's purposes, these are declared in
* boe.infodlg.h and boe.party.h.
*/
void display_pc(short pc_num,short mode,cDialog* parent);
void display_alchemy(bool allowEdit);
bool spend_xp(short pc_num, short mode, cDialog* parent);
// TODO: There's probably a more logical way of arranging this
/* Adventure globals */
//extern party_record_type party;
//extern outdoor_record_type outdoors[2][2];
//extern current_town_type c_town;
//extern big_tr_type t_d;
//extern town_item_list t_i;
//extern unsigned char out[96][96],out_e[96][96];
//extern setup_save_type setup_save;
//extern stored_items_list_type stored_items[3];
extern cUniverse univ;
extern short store_flags[3];
extern short current_active_pc;
extern sf::RenderWindow mainPtr;
extern rectangle d_rects[80];
extern short d_rect_index[80];
extern bool diff_depth_ok,current_file_has_maps;
bool choice_active[6];
extern short which_pc_displayed;
cPlayer *store_pc;
sf::Texture button_num_gworld;
std::map<eSkill,short> skill_cost = {
{eSkill::STRENGTH,3}, {eSkill::DEXTERITY,3}, {eSkill::INTELLIGENCE,3},
{eSkill::EDGED_WEAPONS,2}, {eSkill::BASHING_WEAPONS,2}, {eSkill::POLE_WEAPONS,2},
{eSkill::THROWN_MISSILES,1}, {eSkill::ARCHERY,2}, {eSkill::DEFENSE,2},
{eSkill::MAGE_SPELLS,6}, {eSkill::PRIEST_SPELLS,5}, {eSkill::MAGE_LORE,1},
{eSkill::ALCHEMY,2}, {eSkill::ITEM_LORE,4}, {eSkill::DISARM_TRAPS,2},
{eSkill::LOCKPICKING,1}, {eSkill::ASSASSINATION,4}, {eSkill::POISON,2},
{eSkill::LUCK,5},
};
std::map<eSkill,short> skill_max = {
{eSkill::STRENGTH,20}, {eSkill::DEXTERITY,20}, {eSkill::INTELLIGENCE,20},
{eSkill::EDGED_WEAPONS,20}, {eSkill::BASHING_WEAPONS,20}, {eSkill::POLE_WEAPONS,20},
{eSkill::THROWN_MISSILES,20}, {eSkill::ARCHERY,20}, {eSkill::DEFENSE,20},
{eSkill::MAGE_SPELLS,7}, {eSkill::PRIEST_SPELLS,7}, {eSkill::MAGE_LORE,20},
{eSkill::ALCHEMY,20}, {eSkill::ITEM_LORE,10}, {eSkill::DISARM_TRAPS,20},
{eSkill::LOCKPICKING,20}, {eSkill::ASSASSINATION,20}, {eSkill::POISON,20},
{eSkill::LUCK,20},
};
std::map<eSkill,short> skill_g_cost = {
{eSkill::STRENGTH,50}, {eSkill::DEXTERITY,503}, {eSkill::INTELLIGENCE,50},
{eSkill::EDGED_WEAPONS,40}, {eSkill::BASHING_WEAPONS,40}, {eSkill::POLE_WEAPONS,40},
{eSkill::THROWN_MISSILES,30}, {eSkill::ARCHERY,50}, {eSkill::DEFENSE,40},
{eSkill::MAGE_SPELLS,250}, {eSkill::PRIEST_SPELLS,250}, {eSkill::MAGE_LORE,25},
{eSkill::ALCHEMY,100}, {eSkill::ITEM_LORE,200}, {eSkill::DISARM_TRAPS,30},
{eSkill::LOCKPICKING,20}, {eSkill::ASSASSINATION,100}, {eSkill::POISON,80},
{eSkill::LUCK,0},
};
// The index here is the skill's level, not the skill itself; thus 20 is the max index since no spell can go above 20.
short skill_bonus[21] = {
-3,-3,-2,-1,0,0,1,1,1,2,
2,2,3,3,3,3,4,4,4,5,5};
// These are the IDs used to refer to the skills in the dialog
const char* skill_ids[19] = {
"str","dex","int",
"edge","bash","pole","thrown","bow","def",
"mage","priest","lore","alch","item",
"trap","lock","assassin","poison","luck"
};
// Variables for spending xp
bool talk_done = false;
long val_for_text;
bool keep_change = false;
short store_h,store_sp,i,store_skp;
long store_g;
short store_train_mode,store_train_pc;
static bool select_pc_event_filter (cDialog& me, std::string item_hit, eKeyMod) {
me.toast(true);
if(item_hit != "cancel") {
short which_pc = item_hit[item_hit.length() - 1] - '1';
me.setResult<short>(which_pc);
} else me.setResult<short>(6);
return true;
}
//active_only; // 0 - no 1 - yes 2 - disarm trap
short char_select_pc(short active_only,short free_inv_only,const char *title) {
short item_hit,i;
make_cursor_sword();
cDialog selectPc("select-pc");
selectPc.attachClickHandlers(select_pc_event_filter, {"cancel", "pick1", "pick2", "pick3", "pick4", "pick5", "pick6"});
selectPc["title"].setText(title);
for(i = 0; i < 6; i++) {
std::string n = boost::lexical_cast<std::string>(i + 1);
if(univ.party[i].main_status == eMainStatus::ABSENT ||
(active_only && univ.party[i].main_status > eMainStatus::ALIVE) ||
(free_inv_only == 1 && pc_has_space(i) == 24) || univ.party[i].main_status == eMainStatus::FLED) {
selectPc["pick" + n].hide();
}
// TODO: Wouldn't this lead to blank name fields for non-active characters if those characters are allowed?
if(univ.party[i].main_status != eMainStatus::ABSENT) {
selectPc["pc" + n].setText(univ.party[i].name);
}
else selectPc["pc" + n].hide();
}
selectPc.run();
item_hit = selectPc.getResult<short>();
return item_hit;
}
//active_only; // 0 - no 1 - yes 2 - disarm trap
short select_pc(short active_only,short free_inv_only) {
if(active_only == 2)
return char_select_pc(active_only,free_inv_only,"Trap! Who will disarm?");
else return char_select_pc(active_only,free_inv_only,"Select a character:");
}
static short party_total_level() {
short i,j = 0;
for(i = 0; i < 6; i++)
if(univ.party[i].main_status == eMainStatus::ALIVE)
j += univ.party[i].level;
return j;
}
static void put_pc_spells(cDialog& me, const short store_trait_mode) {
short i;
for(i = 0; i < 62; i++) {
std::string id = "spell" + boost::lexical_cast<std::string>(i + 1);
cLed& cur = dynamic_cast<cLed&>(me[id]);
if(((store_trait_mode == 0) && univ.party[which_pc_displayed].mage_spells[i]) ||
((store_trait_mode == 1) && univ.party[which_pc_displayed].priest_spells[i]))
cur.setState(led_red);
else cur.setState(led_off);
}
me["who"].setText(univ.party[which_pc_displayed].name.c_str());
}
static bool display_pc_event_filter(cDialog& me, std::string item_hit, const short trait_mode) {
short pc_num;
pc_num = which_pc_displayed;
if(item_hit == "done") {
me.toast(true);
} else if(item_hit == "left") {
do {
pc_num = (pc_num == 0) ? 5 : pc_num - 1;
} while(univ.party[pc_num].main_status == eMainStatus::ABSENT);
which_pc_displayed = pc_num;
put_pc_spells(me, trait_mode);
} else if(item_hit == "right") {
do {
pc_num = (pc_num == 5) ? 0 : pc_num + 1;
} while(univ.party[pc_num].main_status == eMainStatus::ABSENT);
which_pc_displayed = pc_num;
put_pc_spells(me, trait_mode);
}
return true;
}
void display_pc(short pc_num,short mode, cDialog* parent) {
using namespace std::placeholders;
short i;
std::string label_str;
if(univ.party[pc_num].main_status == eMainStatus::ABSENT) {
for(pc_num = 0; pc_num < 6; pc_num++)
if(univ.party[pc_num].main_status == eMainStatus::ALIVE)
break;
}
which_pc_displayed = pc_num;
make_cursor_sword();
cDialog pcInfo("pc-spell-info", parent);
pcInfo.attachClickHandlers(std::bind(display_pc_event_filter, _1, _2, mode),{"done","left","right"});
for(i = 0; i < 62; i++) {
std::string id = "spell" + boost::lexical_cast<std::string>(i + 1);
label_str = get_str("magic-names", i + (mode == 0 ? 1 : 101));
pcInfo[id].setText(label_str);
}
put_pc_spells(pcInfo, mode);
dynamic_cast<cPict&>(pcInfo["pic"]).setPict(14 + mode,PIC_DLOG);
pcInfo.run();
}
static void display_traits_graphics(cDialog& me) {
short i,store;
std::string race = "race" + boost::lexical_cast<std::string>(int(store_pc->race) + 1);
dynamic_cast<cLedGroup&>(me["race"]).setSelected(race);
for(i = 0; i < 10; i++) {
std::string id = "good" + boost::lexical_cast<std::string>(i + 1);
eTrait trait = eTrait(i);
dynamic_cast<cLed&>(me[id]).setState((store_pc->traits[trait] > 0) ? led_red : led_off);
}
for(i = 0; i < 5; i++) {
// TODO: Pacifist
std::string id = "bad" + boost::lexical_cast<std::string>(i + 1);
eTrait trait = eTrait(i + 10);
dynamic_cast<cLed&>(me[id]).setState((store_pc->traits[trait] > 0) ? led_red : led_off);
}
store = store_pc->get_tnl();
me["xp"].setTextToNum(store);
}
static bool pick_race_select_led(cDialog& me, std::string item_hit, bool losing, const short store_trait_mode) {
int abil_str = 0;
cPlayer *pc;
pc = store_pc;
if(item_hit == "race") {
item_hit = dynamic_cast<cLedGroup&>(me["race"]).getSelected();
eRace race;
switch(item_hit[4] - '1') {
case 0: race = eRace::HUMAN; break;
case 1: race = eRace::NEPHIL; break;
case 2: race = eRace::SLITH; break;
case 3: race = eRace::VAHNATAI; break;
}
if(store_trait_mode == 0)
pc->race = race;
display_traits_graphics(me);
abil_str = 34 + int(race) * 2;
} else if(item_hit.substr(0,3) == "bad") {
int hit = item_hit[3] - '1';
eTrait trait = eTrait(hit + 10);
if(store_trait_mode != 1)
pc->traits[trait] = !pc->traits[trait];
display_traits_graphics(me);
abil_str = 22 + hit * 2;
} else if(item_hit.substr(0,4) == "good") {
int hit = item_hit[4] - '1';
eTrait trait = eTrait(hit);
if(store_trait_mode != 1)
pc->traits[trait] = !pc->traits[trait];
display_traits_graphics(me);
abil_str = 2 + hit * 2;
}
if(abil_str > 0)
me["info"].setText(get_str("traits", abil_str));
return store_trait_mode == 0;
}
//mode; // 0 - edit 1 - just display 2 - can't change race
void pick_race_abil(cPlayer *pc,short mode) {
using namespace std::placeholders;
static const char*const start_str1 = "Click on button by name for description.";
static const char*const start_str2 = "Click on advantage button to add/remove.";
store_pc = pc;
make_cursor_sword();
cDialog pickAbil("pick-race-abil");
pickAbil["done"].attachClickHandler(std::bind(&cDialog::toast, &pickAbil, true));
auto led_selector = std::bind(pick_race_select_led, _1, _2, _3, mode);
pickAbil.attachFocusHandlers(led_selector, {"race", "bad1", "bad2", "bad3", "bad4", "bad5"});
pickAbil.attachFocusHandlers(led_selector, {"good1", "good2", "good3", "good4", "good5"});
pickAbil.attachFocusHandlers(led_selector, {"good6", "good7", "good8", "good9", "good10"});
display_traits_graphics(pickAbil);
if(mode == 1)
pickAbil["info"].setText(start_str1);
else pickAbil["info"].setText(start_str2);
pickAbil.run();
}
short alch_difficulty[20] = {
1,1,1,3,3,
4,5,5,7,9,
9,10,12,12,9,
14,19,10,16,20
};
void display_alchemy(bool allowEdit) {
short i;
make_cursor_sword();
cChoiceDlog showAlch("pc-alchemy-info", {"done"});
for(i = 0; i < 20; i++) {
std::string id = "potion" + boost::lexical_cast<std::string>(i + 1);
std::string name = get_str("magic-names", i + 200) + " (";
name += std::to_string(alch_difficulty[i]);
name += ')';
showAlch->addLabelFor(id, name, LABEL_LEFT, 83, true);
if(!allowEdit)
showAlch->getControl(id).attachClickHandler(&cLed::noAction);
cLed& led = dynamic_cast<cLed&>(showAlch->getControl(id));
if(univ.party.alchemy[i] > 0)
led.setState(led_red);
else led.setState(led_off);
}
showAlch.show();
if(!allowEdit) return;
for(i = 0; i < 20; i++) {
std::string id = "potion" + boost::lexical_cast<std::string>(i + 1);
cLed& led = dynamic_cast<cLed&>(showAlch->getControl(id));
if(led.getState() == led_red) univ.party.alchemy[i] = true;
else univ.party.alchemy[i] = false;
}
}
// TODO: This dialog needs some kind of context system really badly to avoid the rampant globals
// MARK: Start spend XP dialog
static void do_xp_keep(short pc_num,short mode,std::map<eSkill,short>& store_skills) {
for(i = 0; i < 19; i++) {
eSkill skill = eSkill(i);
univ.party[pc_num].skills[skill] = store_skills[skill];
}
univ.party[pc_num].cur_health += store_h - univ.party[pc_num].max_health;
univ.party[pc_num].max_health = store_h;
univ.party[pc_num].cur_sp += store_sp - univ.party[pc_num].max_sp;
univ.party[pc_num].max_sp = store_sp;
if(mode == 1)
univ.party.gold = store_g;
univ.party[pc_num].skill_pts = store_skp;
}
static void draw_xp_skills(cDialog& me,std::map<eSkill,short>& store_skills) {
short i;
// TODO: Wouldn't it make more sense for it to be red when you can't buy the skill rather than red when you can?
for(i = 0; i < 19; i++) {
cControl& cur = me[skill_ids[i]];
eSkill skill = eSkill(i);
if((store_skp >= skill_cost[skill]) && (store_g >= skill_g_cost[skill]))
cur.setColour(sf::Color::Red);
else cur.setColour(me.getDefTextClr());
cur.setTextToNum(store_skills[skill]);
}
cControl& sp = me["sp"];
cControl& hp = me["hp"];
if((store_skp >= 1) && (store_g >= 10))
hp.setColour(sf::Color::Red);
else hp.setColour(me.getDefTextClr());
hp.setTextToNum(store_h);
if((store_skp >= 1) && (store_g >= 15))
sp.setColour(sf::Color::Red);
else sp.setColour(me.getDefTextClr());
sp.setTextToNum(store_sp);
}
static void update_gold_skills(cDialog& me) {
me["gold"].setTextToNum(((store_train_mode == 0) ? 0 : store_g));
me["skp"].setTextToNum(store_skp);
}
static void do_xp_draw(cDialog& me,std::map<eSkill,short>& store_skills) {
char get_text[256];
short mode,pc_num;
mode = store_train_mode;
pc_num = store_train_pc;
if(mode == 0) {
if(univ.party[pc_num].main_status == eMainStatus::ALIVE)
sprintf((char *) get_text, "%s",(char *) univ.party[pc_num].name.c_str());
else sprintf((char *) get_text, "New PC");
}
else sprintf((char *) get_text, "%s",(char *) univ.party[pc_num].name.c_str());
me["recipient"].setText(get_text);
for(i = 0; i < 19; i++) {
eSkill skill = eSkill(i);
store_skills[skill] = univ.party[pc_num].skills[skill];
}
store_h = univ.party[pc_num].max_health;
store_sp = univ.party[pc_num].max_sp;
store_g = (mode == 0) ? 20000 : univ.party.gold;
store_skp = univ.party[pc_num].skill_pts;
draw_xp_skills(me,store_skills);
update_gold_skills(me);
}
static bool spend_xp_navigate_filter(cDialog& me, std::string item_hit,std::map<eSkill,short>& store_skills) {
short mode,pc_num;
bool talk_done = false;
mode = store_train_mode;
pc_num = store_train_pc;
if(item_hit == "cancel") {
// TODO: Um, I'm pretty sure this can never happen.
if(mode == 0 && univ.party[pc_num].main_status < eMainStatus::ABSENT)
univ.party[pc_num].main_status = eMainStatus::ABSENT;
me.setResult(false);
talk_done = true;
} else if(item_hit == "help") {
univ.party.help_received[10] = 0;
// give_help(210,11,me);
} else if(item_hit == "keep") {
do_xp_keep(pc_num,mode,store_skills);
me.setResult(true);
talk_done = true;
} else if(item_hit == "left") {
// TODO: Try not forcing a commit when using the arrows?
if(mode != 0) {
do_xp_keep(pc_num,mode,store_skills);
do {
pc_num = (pc_num == 0) ? 5 : pc_num - 1;
} while(univ.party[pc_num].main_status != eMainStatus::ALIVE);
store_train_pc = pc_num;
do_xp_draw(me,store_skills);
} else
beep(); // TODO: This is a game event, so it should have a game sound, not a system alert.
} else if(item_hit == "right") {
// TODO: If they don't work in mode 0, why are they visible?
if(mode != 0) {
do_xp_keep(pc_num,mode,store_skills);
do {
pc_num = (pc_num == 5) ? 0 : pc_num + 1;
} while(univ.party[pc_num].main_status != eMainStatus::ALIVE);
store_train_pc = pc_num;
do_xp_draw(me,store_skills);
} else
beep(); // TODO: This is a game event, so it should have a game sound, not a system alert.
}
store_train_pc = pc_num;
if(talk_done) {
me.toast(item_hit == "keep");
}
return true;
}
static bool spend_xp_event_filter(cDialog& me, std::string item_hit, eKeyMod mods,std::map<eSkill,short>& store_skills) {
short mode = store_train_mode, pc_num = store_train_pc;
if(item_hit.substr(0,2) == "hp") {
if(mod_contains(mods, mod_alt)) {
cStrDlog aboutHP(get_str("help",63),"","About Health",24,PIC_DLOG,&me);
aboutHP.setSound(57);
aboutHP.show();
} else if(((store_h >= 250) && (item_hit[3] == 'p')) ||
((store_h == univ.party[pc_num].max_health) && (item_hit[3] == 'm') && (mode == 1)) ||
((store_h == 6) && (item_hit[3] == 'm') && (mode == 0)))
beep(); // TODO: This is a game event, so it should have a game sound, not a system alert.
else if(item_hit == "hp-m") {
store_g += 10;
store_h -= 2;
store_skp += 1;
}
else {
if((store_g < 10) || (store_skp < 1)) {
// if(store_g < 10)
// give_help(24,0,me);
// else give_help(25,0,me);
}
else {
store_g -= 10;
store_h += 2;
store_skp -= 1;
}
}
update_gold_skills(me);
me["hp"].setTextToNum(store_h);
draw_xp_skills(me,store_skills);
} else if(item_hit.substr(0,2) == "sp") {
if(mod_contains(mods, mod_alt)) {
cStrDlog aboutSP(get_str("help",64),"","About Spell Points",24,PIC_DLOG,&me);
aboutSP.setSound(57);
aboutSP.show();
} else if(((store_sp >= 150) && (item_hit[3] == 'p')) ||
((store_sp == univ.party[pc_num].max_sp) && (item_hit[3] == 'm') && (mode == 1)) ||
((store_sp == 0) && (item_hit[3] == 'm') && (mode == 0)))
beep(); // TODO: This is a game event, so it should have a game sound, not a system alert.
else if(item_hit == "sp-m") {
store_g += 15;
store_sp -= 1;
store_skp += 1;
}
else {
if((store_g < 15) || (store_skp < 1)) {
// if(store_g < 15)
// give_help(24,0,me);
// else give_help(25,0,me);
}
else {
store_sp += 1;
store_g -= 15;
store_skp -= 1;
}
}
update_gold_skills(me);
me["sp"].setTextToNum(store_sp);
draw_xp_skills(me,store_skills);
} else {
eSkill which_skill = eSkill::INVALID;
for(int i = 0; i < 19; i++) {
size_t n = strlen(skill_ids[i]);
if(item_hit.length() < n + 2) continue;
if(item_hit.substr(0, item_hit.length() - 2) == skill_ids[i]) {
which_skill = eSkill(i);
break;
}
}
if(which_skill == eSkill::INVALID) return true;
/* if(mod_contains(mods, mod_alt)) display_skills(which_skill,&me);
else */{
char dir = item_hit[item_hit.length() - 1];
// TODO: This is a game event, so it should have a game sound, not a system alert.
if(store_skills[which_skill] >= skill_max[which_skill] && dir == 'p')
beep();
else if(store_skills[which_skill] == univ.party[pc_num].skills[which_skill] && dir == 'm' && mode == 1)
beep();
else if(store_skills[which_skill] == 0 && dir == 'm' && mode == 0 &&
which_skill != eSkill::STRENGTH && which_skill != eSkill::DEXTERITY && which_skill != eSkill::INTELLIGENCE)
beep();
else if(store_skills[which_skill] == 1 && dir == 'm' && mode == 0 &&
(which_skill == eSkill::STRENGTH || which_skill == eSkill::DEXTERITY || which_skill == eSkill::INTELLIGENCE))
beep();
else {
if(dir == 'm') {
store_g += skill_g_cost[which_skill];
store_skills[which_skill] -= 1;
store_skp += skill_cost[which_skill];
}
else {
if((store_g < skill_g_cost[which_skill]) || (store_skp < skill_cost[which_skill])) {
// if(store_g < skill_g_cost[which_skill])
// give_help(24,0,me);
// else give_help(25,0,me);
}
else {
store_skills[which_skill] += 1;
store_g -= skill_g_cost[which_skill];
store_skp -= skill_cost[which_skill];
}
}
update_gold_skills(me);
me[skill_ids[int(which_skill)]].setTextToNum(store_skills[which_skill]);
draw_xp_skills(me,store_skills);
}
}
}
return true;
}
//short mode; // 0 - create 1 - train
// returns 1 if cancelled
bool spend_xp(short pc_num, short mode, cDialog* parent) {
using namespace std::placeholders;
store_train_pc = pc_num;
store_train_mode = mode;
make_cursor_sword();
std::map<eSkill,short> skills = univ.party[pc_num].skills;
cDialog xpDlog("spend-xp",parent);
xpDlog.addLabelFor("hp","Health (1/10)",LABEL_LEFT,75,true);
xpDlog.addLabelFor("sp","Spell Pts. (1/15)",LABEL_LEFT,75,true);
auto spend_xp_filter = std::bind(spend_xp_event_filter,_1,_2,_3,std::ref(skills));
std::string minus = "-m", plus = "-p";
for(i = 54; i < 73; i++) {
std::ostringstream sout;
eSkill skill = eSkill(i - 54);
sout << get_str("skills",1 + 2 * (i - 54)) << ' ' << '(';
sout << skill_cost[skill] << '/' << skill_g_cost[skill] << ')';
xpDlog.addLabelFor(skill_ids[i - 54],sout.str(),LABEL_LEFT,(i < 63) ? 75 : 69,true);
xpDlog[skill_ids[i - 54] + minus].attachClickHandler(spend_xp_filter);
xpDlog[skill_ids[i - 54] + plus].attachClickHandler(spend_xp_filter);
}
do_xp_draw(xpDlog,skills);
xpDlog.attachClickHandlers(std::bind(spend_xp_navigate_filter,_1,_2,std::ref(skills)),{"keep","cancel","left","right","help"});
xpDlog.attachClickHandlers(spend_xp_filter,{"sp-m","sp-p","hp-m","hp-p"});
if(univ.party.help_received[10] == 0) {
// TODO: Is an initial draw even needed?
// cd_initial_draw(1010);
// give_help(10,11,xpDlog);
}
xpDlog.run();
return xpDlog.getResult<bool>();
}

14
src/pcedit/pc.editors.h Normal file
View File

@@ -0,0 +1,14 @@
bool give_to_pc(short pc_num,cItemRec item, short print_result);
bool give_to_party(cItemRec item,short print_result);
void give_gold(short amount,bool print_result);
bool take_gold(short amount,bool print_result);
short pc_has_space(short pc_num);
void take_item(short pc_num,short which_item);
short char_select_pc(short active_only,short free_inv_only,const char *title);
short select_pc(short active_only,short free_inv_only);
void give_spec_items();
void pick_race_abil(cPlayer *pc,short mode);
void reset_boats();
void combine_things(short pc_num);

88
src/pcedit/pc.fileio.cpp Normal file
View File

@@ -0,0 +1,88 @@
#include <cstring>
#include <cstdio>
#include "pc.global.h"
#include "classes.h"
#include "pc.fileio.h"
#include "pc.graphics.h"
#include "graphtool.h"
#include "soundtool.h"
#include "pc.editors.h"
#include "mathutil.h"
#include "dlogutil.hpp"
#include "restypes.hpp"
#include "fileio.h"
#define DONE_BUTTON_ITEM 1
extern bool play_sounds;
extern short current_active_pc;
extern long stored_key;
extern sf::RenderWindow mainPtr;
extern cItemRec item_list[400];
extern cUniverse univ;
extern bool file_in_mem,party_in_scen,scen_items_loaded;
bool ae_loading = false;
typedef struct {
char expl[96][96];
} out_info_type;
char *party_encryptor;
std::string last_load_file = "Blades of Exile Save";
extern void update_item_menu();
extern short store_flags[3];
fs::path store_file_reply;
short give_intro_hint,display_mode;
short jl;
fs::path file_to_load;
void load_base_item_defs();
bool load_scen_item_defs(char scen_name[256]);
extern fs::path progDir;
void leave_town() {
store_flags[0] = 5790;
}
void remove_party_from_scen() {
store_flags[1] = 200;
party_in_scen = false;
load_base_item_defs();
}
/*
* XXX This was referenced but not defined, so I copied the implementation
* from blxfileio.c. Need to check that it's OK.
*/
short init_data(short flag) {
long k = 0;
k = (long) flag;
k = k * k;
jl = jl * jl + 84 + k;
k = k + 51;
jl = jl * 2 + 1234 + k;
k = k % 3000;
jl = jl * 54;
jl = jl * 2 + 1234 + k;
k = k * 82;
k = k % 10000;
k = k + 10000;
return (short) k;
}
void load_base_item_defs(){
fs::path basePath = progDir/"Scenario Editor"/"Blades of Exile Base"/"bladbase.exs";
scen_items_loaded = load_scenario(basePath, univ.scenario);
}

11
src/pcedit/pc.fileio.h Normal file
View File

@@ -0,0 +1,11 @@
//bool select_save_location(FSSpec* to_save_ptr);
//void save_file(FSSpec to_save);
//void load_file();
void leave_town();
//long do_waterfall(long flag);
short init_data(short flag);
//long open_pref_file();
//void make_pref_file(FSSpec pref);
//void save_prefs();
void remove_party_from_scen();
void load_base_item_defs();

85
src/pcedit/pc.global.cpp Normal file
View File

@@ -0,0 +1,85 @@
//#include <OSUtils.h>
#include <Carbon/Carbon.h>
#include "ed.global.h"
#include "math.h"
extern short give_delays;
short get_ran (short times,short min,short max)
{
long int store;
short i, to_ret = 0;
for (i = 1; i < times + 1; i++) {
store = Random();
to_ret = to_ret + min + (((store + 32767) * (max - min + 1)) / 65536);
}
return to_ret;
}
Boolean same_point(location p1,location p2)
{
if ((p1.x == p2.x) & (p1.y == p2.y))
return TRUE;
else return FALSE;
}
short move_to_zero(short val)
{
if (val < 0)
return val + 1;
if (val > 0)
return val - 1;
return val;
}
short max(short a,short b)
{
if (a > b)
return a;
else return b;
}
short min(short a,short b)
{
if (a < b)
return a;
else return b;
}
short minmax(short min,short max,short k)
{
if (k < min)
return min;
if (k > max)
return max;
return k;
}
short s_pow(short x,short y)
{
return (short) pow((double) x, (double) y);
}
short a_v(short x)
{
if (x < 0)
return (-1 * x);
else return x;
}
short ex_abs(short x)
{
if (x < 0)
return (-1 * x);
else return x;
}
void BoEpause(short length)
{
unsigned long dummy,len;
len = (unsigned long)length;
if (give_delays == 0)
Delay(len, &dummy);
}

427
src/pcedit/pc.global.h Normal file
View File

@@ -0,0 +1,427 @@
//#define EXILE_BIG_GUNS 1
#define DRAG_EDGE 15
#define T_M 60
#define NUM_TOWN_ITEMS 115
#define DRAG_EDGE 15
#define DISPLAY_LEFT 23
#define DISPLAY_TOP 23
#define BITMAP_WIDTH 28
#define BITMAP_HEIGHT 36
//#define STORED_GRAPHICS 240
#define PC_WIN_UL_X 291
#define PC_WIN_UL_Y 5
#define ITEM_WIN_UL_X 291
#define ITEM_WIN_UL_Y 130
#define TEXT_WIN_UL_X 291
#define TEXT_WIN_UL_Y 283
#define NUM_BUTTONS 15
#define ASB add_string_to_buf
#define PSD univ.party.stuff_done
#define DES display_enc_string
#define D2ES display_2_enc_string
#define DOOR_LIGHT can_enter = run_trap(7,&PSD[c_town.town_num][which],4220,0); break;
#define DOOR_HEAVY can_enter = run_trap(7,&PSD[c_town.town_num][which],4220,20); break;
#define DOOR_ALARM can_enter = run_trap(7,&PSD[c_town.town_num][which],4220,11); break;
#define DRESSER_LIGHT can_enter = run_trap(7,&PSD[c_town.town_num][which],4221,0); break;
#define DRESSER_HEAVY can_enter = run_trap(7,&PSD[c_town.town_num][which],4221,20); break;
#define DRESSER_ALARM can_enter = run_trap(7,&PSD[c_town.town_num][which],4221,11); break;
#define FLOOR_LIGHT can_enter = run_trap(7,&PSD[c_town.town_num][which],4222,0); break;
#define FLOOR_HEAVY can_enter = run_trap(7,&PSD[c_town.town_num][which],4222,20); break;
#define FLOOR_ALARM can_enter = run_trap(7,&PSD[c_town.town_num][which],4222,11); break;
#define CHEST_LIGHT can_enter = run_trap(7,&PSD[c_town.town_num][which],3450,0); break;
#define CHEST_HEAVY can_enter = run_trap(7,&PSD[c_town.town_num][which],3450,20); break;
#define CHEST_ALARM can_enter = run_trap(7,&PSD[c_town.town_num][which],3450,11); break;
#define printFlag() printf("%s %i\n",__FILE__,__LINE__)
//typedef struct {
// char x,y;
// } location;
//typedef struct {
// short x,y;} shortloc;
//typedef struct {
// short type,sd1,sd2,pic,m1,m2,ex1a,ex1b,ex2a,ex2b,jumpto;
// } special_node_type;
//typedef struct {
// short personality,type;
// char link1[4],link2[4];
// short extras[4];
// } talking_node_type;
//typedef struct {
// unsigned char strlens[200];
// talking_node_type talk_nodes[60];
// } talking_record_type;
//typedef struct {
// short picture;
// unsigned char blockage,flag1,flag2,special,trans_to_what,fly_over,boat_over;
// unsigned char block_horse,light_radius,step_sound,shortcut_key,res1,res2,res3;
// } terrain_type_type;
//typedef struct {
// unsigned char monst[4];
// } wandering_type;
//typedef struct {
// unsigned char monst[7];
// unsigned char friendly[3];
// short spec_on_meet,spec_on_win,spec_on_flee,cant_flee;
// short end_spec1,end_spec2;
//} out_wandering_type;
//typedef struct {
// unsigned char terrain[48][48];
// location special_locs[18];
// unsigned char special_id[18];
// location exit_locs[8];
// char exit_dests[8];
// location sign_locs[8];
// out_wandering_type wandering[4],special_enc[4];
// location wandering_locs[4];
// RECT info_rect[8];
// unsigned char strlens[180];
// special_node_type specials[60];
// } outdoor_record_type;
//typedef struct {
// unsigned char number;
// unsigned char start_attitude;
// location start_loc;
// unsigned char mobile;
// unsigned char time_flag;
// unsigned char extra1,extra2;
// short spec1, spec2;
// char spec_enc_code,time_code;
// short monster_time,personality;
// short special_on_kill,facial_pic;
//
// } creature_start_type;
//typedef struct {
// short variety, item_level;
// char awkward, bonus, protection, charges, type;
// unsigned char graphic_num,ability, type_flag, is_special;
// short value;
// bool identified, magic;
// unsigned char weight, description_flag;
// char full_name[25], name[15];
// unsigned char reserved1,reserved2;
// unsigned char magic_use_type, ability_strength, treas_class, real_abil;
//
//} short_item_record_type;
//typedef struct {
// short variety, item_level;
// char awkward, bonus, protection, charges, type, magic_use_type;
// unsigned char graphic_num,ability, ability_strength,type_flag, is_special;
// short value;
// unsigned char weight, special_class;
// location item_loc;
// char full_name[25], name[15];
// unsigned char treas_class,item_properties,reserved1,reserved2;
//} item_record_type;
//typedef struct {
// location item_loc;
// short item_code,ability;
// unsigned char charges,always_there,property,contained;
// } preset_item_type;
//typedef struct {
// location field_loc;
// short field_type;
// } preset_field_type;
//typedef struct {
// short town_chop_time,town_chop_key;
// wandering_type wandering[4];
// location wandering_locs[4];
// location special_locs[50];
// unsigned char spec_id[50];
// location sign_locs[15];
// short lighting;
// location start_locs[4];
// location exit_locs[4];
// short exit_specs[4];
// RECT in_town_rect;
// preset_item_type preset_items[64];
// short max_num_monst;
// preset_field_type preset_fields[50];
// short spec_on_entry,spec_on_entry_if_dead;
// short timer_spec_times[8];
// short timer_specs[8];
// unsigned char strlens[180];
// special_node_type specials[100];
// unsigned char specials1,specials2,res1,res2;
// short difficulty;
// } town_record_type;
//typedef struct {
// unsigned char terrain[64][64];
// RECT room_rect[16];
// creature_start_type creatures[60];
// unsigned char lighting[8][64];
// } big_tr_type;
//typedef struct {
// unsigned char terrain[48][48];
// RECT room_rect[16];
// creature_start_type creatures[40];
// unsigned char lighting[6][48];
// } ave_tr_type;
//typedef struct {
// unsigned char terrain[32][32];
// RECT room_rect[16];
// creature_start_type creatures[30];
// unsigned char lighting[4][32];
// } tiny_tr_type;
//typedef struct {
// short block_type;
// short block_destroy_time;
// char block_alignment;
// char block_key_time;
// location block_loc;
// } city_block_type;
//typedef struct {
// RECT what_rect;
// unsigned char ter_type;
// unsigned char hollow;
// } city_ter_rect_type;
//typedef struct {
// creature_start_type creatures[30];
// city_block_type city_block[15];
// city_ter_rect_type city_ter_rect[10];
// } template_town_type;
//typedef struct {
// item_record_type scen_items[400];
// char monst_names[256][20];
// char ter_names[256][30];
// } scen_item_data_type;
//typedef struct {
// short ter_type,item_num[10],item_odds[10],property;
// } item_storage_shortcut_type;
//typedef struct {
// unsigned char m_num,level,m_name[26];
// short health,m_health,mp,max_mp;
// unsigned char armor,skill;
// short a[3];
// unsigned char a1_type,a23_type,m_type,speed,ap,mu,cl,breath,breath_type,treasure,spec_skill,poison;
// short morale,m_morale;
// short corpse_item,corpse_item_chance;
// short status[15];
// unsigned char direction,immunities,x_width,y_width,radiate_1,radiate_2;
// unsigned char default_attitude,summon_type,default_facial_pic,res1,res2,res3;
// short picture_num;
//
// } monster_record_type;
//typedef struct {
// short active,attitude;
// unsigned char number;
// location m_loc;
// monster_record_type m_d;
// bool mobile;
// short summoned;
// creature_start_type monst_start;
// } creature_data_type;
//typedef struct {
// location horse_loc,horse_loc_in_sec,horse_sector;
// short which_town;
// bool exists,property;
// } horse_record_type;
//typedef struct {
// location boat_loc,boat_loc_in_sec,boat_sector;
// short which_town;
// bool exists,property;
//} boat_record_type;
//typedef struct {
// unsigned char flag1, flag2, flag3, flag4;
// unsigned char ver[3],min_run_ver,prog_make_ver[3],num_towns;
// unsigned char out_width,out_height,difficulty,intro_pic,default_ground;
// } scen_header_type;
// typedef struct {
// unsigned char flag1, flag2, flag3, flag4;
// unsigned char ver[3],min_run_ver,prog_make_ver[3],num_towns;
// unsigned char out_width,out_height,difficulty,intro_pic,default_ground;
// unsigned char town_size[200];
// unsigned char town_hidden[200];
// short flag_a;
// short intro_mess_pic,intro_mess_len;
// location where_start,out_sec_start,out_start;
// short which_town_start;
// short flag_b;
// short town_data_size[200][5];
// short town_to_add_to[10];
// short flag_to_add_to_town[10][2];
// short flag_c;
// short out_data_size[100][2];
// RECT store_item_rects[3];
// short store_item_towns[3];
// short flag_e;
// short special_items[50];
// short special_item_special[50];
// short rating,uses_custom_graphics;
// short flag_f;
// monster_record_type scen_monsters[256];
// boat_record_type scen_boats[30];
// horse_record_type scen_horses[30];
// short flag_g;
// terrain_type_type ter_types[256];
// short scenario_timer_times[20];
// short scenario_timer_specs[20];
// short flag_h;
// special_node_type scen_specials[256];
// item_storage_shortcut_type storage_shortcuts[10];
// short flag_d;
// unsigned char scen_str_len[300];
// short flag_i;
// location last_out_edited;
// short last_town_edited;
//
// } scenario_data_type;
// for game
//typedef struct {
// short personality;
// short town_num;
// short str1,str2;
// } talk_save_type;
//typedef struct {
// creature_data_type dudes[60];
// short which_town;
// short friendly;
// } creature_list_type;
//typedef struct {
// short town_num, difficulty;
// town_record_type town;
// char explored[64][64];
// bool hostile;
// creature_list_type monst;
// bool in_boat;
// location p_loc;
// } current_town_type;
//typedef struct {
// bool exists;
// short direction;
// out_wandering_type what_monst;
// location which_sector,m_loc;
// } outdoor_creature_type;
//typedef struct {
// long age;
// short gold,food;
// unsigned char stuff_done[310][10],item_taken[200][8];
// short light_level;
// location outdoor_corner,i_w_c,p_loc,loc_in_sec;
// boat_record_type boats[30];
// horse_record_type horses[30];
// creature_list_type creature_save[4];
// short in_boat,in_horse;
// outdoor_creature_type out_c[10];
// item_record_type magic_store_items[5][10];
// short imprisoned_monst[4];
// char m_seen[256];
// char journal_str[50];
// short journal_day[50];
// short special_notes_str[140][2];
// talk_save_type talk_save[120];
// short direction,at_which_save_slot;
// char alchemy[20];
// bool can_find_town[200];
// short key_times[100];
// short party_event_timers[30];
// short global_or_town[30];
// short node_to_call[30];
// char spec_items[50],help_received[120];
// short m_killed[200];
// long total_m_killed,total_dam_done,total_xp_gained,total_dam_taken;
// char scen_name[256];
// } party_record_type;
//typedef struct {
// char town_maps[200][8][64];
// } stored_town_maps_type;
//typedef struct {
// char town_strs[180][256];
// //char out_strs[120][256];
// char scen_strs[270][256];
// char talk_strs[170][256];
// char scen_header_strs[25][3][80];
// Str255 scen_names[25];
// scen_item_data_type scen_item_list;
// stored_town_maps_type town_maps;
// } piles_of_stuff_dumping_type;
//typedef struct {
// char out_strs[9][256];
// } outdoor_strs_type;
//typedef struct {
// short main_status;
// char name[20];
// short skills[30];
// short max_health,cur_health,max_sp,cur_sp,experience,skill_pts,level;
// short status[15];
// item_record_type items[24];
// bool equip[24];
// bool priest_spells[62],mage_spells[62];
// short which_graphic,weap_poisoned;
// bool advan[15],traits[15];
// short race,exp_adj,direction;
// } pc_record_type;
//typedef struct {
// unsigned char setup[4][64][64];
// } setup_save_type;
//typedef struct {
// item_record_type items[NUM_TOWN_ITEMS];
// } town_item_list;
//typedef struct {short i;} flag_type;
typedef struct {unsigned char pattern[9][9];} effect_pat_type;
//typedef struct {
// item_record_type items[NUM_TOWN_ITEMS];
// } stored_items_list_type;
//typedef struct {
// char outdoor_maps[100][6][48];
// } stored_outdoor_maps_type;
typedef struct {
long l[10];
} PrefRecord,*PrefPtr,**PrefHandle;

1044
src/pcedit/pc.graphics.cpp Normal file

File diff suppressed because it is too large Load Diff

6
src/pcedit/pc.graphics.h Normal file
View File

@@ -0,0 +1,6 @@
void init_main_buttons();
void Set_up_win ();
void redraw_screen();
void do_button_action(short which_pc,short which_button);
void make_cursor_sword();

553
src/pcedit/pc.main.cpp Normal file
View File

@@ -0,0 +1,553 @@
//#include <cMemory>
#include <iostream>
#include <cstring>
#include "pc.global.h"
#include "classes.h"
#include "pc.graphics.h"
#include "pc.editors.h"
#include "pc.action.h"
#include "pc.fileio.h"
#include "soundtool.h"
#include "graphtool.h"
#include "boe.consts.h"
#include "dlogutil.hpp"
#include "fileio.h"
#include "pc.menus.h"
#include "winutil.h"
#include "cursors.h"
cUniverse univ;
rectangle pc_area_buttons[6][4] ; // 0 - whole 1 - pic 2 - name 3 - stat strs 4,5 - later
rectangle item_string_rects[24][4]; // 0 - name 1 - drop 2 - id 3 -
rectangle pc_info_rect; // Frame that holds a pc's basic info and items
rectangle name_rect; //Holds pc name inside pc_info_rect
rectangle info_area_rect;
rectangle hp_sp_rect; // Holds hit points and spells points for pc
rectangle skill_rect; // Holds "Skills:" string
rectangle pc_skills_rect[19]; //Holds current pc's skill levels
rectangle status_rect; //Holds the string "Status:"
rectangle pc_status_rect[10]; //Holds first 8 effects on pc
rectangle traits_rect; //Holds the string "Traits:"
rectangle pc_traits_rect[16]; //Holds pc traits
rectangle pc_race_rect; //Holds current pc's race
rectangle edit_rect[5][2]; //Buttons that bring up pc edit dialog boxs
short current_active_pc = 0;
//short dialog_answer;
/* Mac stuff globals */
bool All_Done = false,diff_depth_ok = false;
sf::Event event;
sf::RenderWindow mainPtr;
bool gInBackground = false;
fs::path file_in_mem;
bool party_in_scen = false;
bool scen_items_loaded = false;
/* Adventure globals */
//party_record_type party;
//outdoor_record_type outdoors[2][2];
//current_town_type c_town;
//big_tr_type t_d;
//town_item_list t_i;
//unsigned char out[96][96],out_e[96][96];
//setup_save_type setup_save;
//unsigned char misc_i[64][64],sfx[64][64];
//unsigned char template_terrain[64][64];
short store_flags[3];
//town_record_type anim_town;
//tiny_tr_type anim_t_d;
//stored_items_list_type stored_items[3];
//stored_town_maps_type maps;
//stored_town_maps_type town_maps;
//stored_outdoor_maps_type o_maps;
/* Display globals */
short give_delays = 0; /* XXX this wasn't defined anywhere, is this right? -jmr */
/* Prototypes */
int main(int argc, char* argv[]);
void Initialize(void);
void Handle_One_Event();
void Handle_Activate();
void Handle_Update();
void Mouse_Pressed();
void handle_apple_menu(int item_hit);
void handle_file_menu(int item_hit);
void handle_reg_menu(int item_hit);
void handle_extra_menu(int item_hit);
void handle_edit_menu(int item_hit);
void update_item_menu();
bool verify_restore_quit(bool mode);
void set_up_apple_events();
void handle_item_menu(int item_hit);
//item_record_type convert_item (short_item_record_type s_item);
extern bool cur_scen_is_mac;
extern fs::path progDir;
// File io
short specials_res_id;
char start_name[256];
//
// Main body of program Exile
//
//MW specified return type was 'void', changed to ISO C style for Carbonisation -jmr
int main(int /*argc*/, char* argv[]) {
try {
init_menubar();
init_directories(argv[0]);
Initialize();
init_fileio();
init_main_buttons();
Set_up_win();
init_graph_tool();
init_snd_tool();
set_up_apple_events();
cDialog::init();
redraw_screen();
while(!All_Done)
Handle_One_Event();
return 0;
} catch(std::exception& x) {
giveError(x.what());
throw;
} catch(std::string& x) {
giveError(x);
throw;
} catch(...) {
giveError("An unknown error occurred!");
throw;
}
}
//
// Initialize everything for the program, make sure we can run
//
//MW specified argument and return type.
void Initialize(void) {
check_for_intel();
//
// To make the Random sequences truly random, we need to make the seed start
// at a different number. An easy way to do this is to put the current time
// and date into the seed. Since it is always incrementing the starting seed
// will always be different. Dont for each call of Random, or the sequence
// will no longer be random. Only needed once, here in the init.
//
//GetDateTime(&randSeed);
// SetQDGlobalsRandomSeed((long)randSeed);
srand(time(NULL));
//
// Make a new window for drawing in, and it must be a color window.
// The window is full screen size, made smaller to make it more visible.
//
// Size and style obtained from WIND resource #128
mainPtr.create(sf::VideoMode(590, 440), "Blades of Exile Character Editor", sf::Style::Titlebar | sf::Style::Close);
}
void Handle_One_Event() {
if(!mainPtr.pollEvent(event)) return;
init_main_buttons();
redraw_screen();
switch(event.type){
case sf::Event::KeyPressed:
break;
case sf::Event::MouseButtonPressed:
Mouse_Pressed();
break;
case sf::Event::GainedFocus:
set_cursor(sword_curs);
break;
case sf::Event::Closed:
All_Done = verify_restore_quit(false);
break;
default:
break;
}
}
void Mouse_Pressed() {
bool try_to_end;
try_to_end = handle_action(event);
if(try_to_end)
All_Done = verify_restore_quit(false);
}
void handle_apple_menu(int item_hit) {
//char desk_acc_name[256];
//short desk_acc_num;
switch(item_hit) {
case 1:
cChoiceDlog("about-pced").show();
break;
default:
//GetItem (apple_menu,item_hit,desk_acc_name);
//desk_acc_num = OpenDeskAcc(desk_acc_name);
break;
}
}
void handle_file_menu(int item_hit) {
fs::path file;
switch(item_hit) {
case 1://save
save_party(file_in_mem, univ);
break;
case 2://save as
file = nav_put_party();
if(!file.empty()) save_party(file, univ);
break;
case 3://open
if(verify_restore_quit(true)){
file = nav_get_party();
if(!file.empty()) {
if(load_party(file, univ)) {
file_in_mem = file;
party_in_scen = !univ.party.scen_name.empty();
if(!party_in_scen) load_base_item_defs();
update_item_menu();
}
}
menu_activate();
}
break;
case 7://quit
All_Done = verify_restore_quit(false);
break;
}
}
static void display_strings(short nstr, pic_num_t pic) {
cStrDlog display_strings(get_str("pcedit", nstr), "", "Editing party", pic, PIC_DLOG);
display_strings.setSound(57);
display_strings.show();
}
void handle_extra_menu(int item_hit) {
short i;
//cVehicle v_boat = {{12,17},{0,0},{0,0},80,true,false};
if(file_in_mem.empty()) {
display_strings(5, 7);
return;
}
switch(item_hit) {
case 1:
edit_gold_or_food(0);
break;
case 2:
edit_gold_or_food(1);
break;
case 4:
if(univ.party.is_split() > 0) {
cChoiceDlog("reunite-first").show();
break;
}
cChoiceDlog("leave-town").show();
leave_town();
break;
case 5:
if(univ.party.is_split() == 0) {
cChoiceDlog("not-split").show();
break;
}
cChoiceDlog("reunited").show();
univ.town.p_loc = univ.party.left_at();
for(i = 0; i < 6; i++)
if(univ.party[i].main_status >= eMainStatus::SPLIT)
univ.party[i].main_status -= eMainStatus::SPLIT;
break;
case 6:
display_strings(20,7);
for(i = 0; i < 4; i++)
univ.party.creature_save[i].which_town = 200;
break;
case 8: // damage
display_strings(1,15);
for(i = 0; i < 6; i++)
univ.party[i].cur_health = univ.party[i].max_health;
break;
case 9: // spell pts
display_strings(2,15);
for(i = 0; i < 6; i++)
univ.party[i].cur_sp = univ.party[i].max_sp;
break;
case 10: // raise dead
display_strings(3,15);
for(i = 0; i < 6; i++)
if(univ.party[i].main_status == eMainStatus::DEAD || univ.party[i].main_status == eMainStatus::DUST ||
univ.party[i].main_status == eMainStatus::STONE)
univ.party[i].main_status = eMainStatus::ALIVE;
break;
case 11: // conditions
display_strings(4,15);
for(i = 0; i < 6; i++) {
univ.party[i].status[eStatus::POISON] = 0;
if(univ.party[i].status[eStatus::HASTE_SLOW] < 0)
univ.party[i].status[eStatus::HASTE_SLOW] = 0;
univ.party[i].status[eStatus::WEBS] = 0;
univ.party[i].status[eStatus::DISEASE] = 0;
univ.party[i].status[eStatus::DUMB] = 0;
univ.party[i].status[eStatus::ASLEEP] = 0;
univ.party[i].status[eStatus::PARALYZED] = 0;
univ.party[i].status[eStatus::ACID] = 0;
}
break;
case 13:
if(!party_in_scen) {
display_strings(25,15);
break;
}
if(cChoiceDlog("leave-scenario",{"okay","cancel"}).show() != "okay")
break;
remove_party_from_scen();
break;
}
redraw_screen();
}
void handle_edit_menu(int item_hit) {
short i,j,k;
if(file_in_mem.empty()) {
display_strings(5,7);
return;
}
switch(item_hit) {
case 1:
display_alchemy(true);
break;
case 2: // all property
display_strings(6,7);
for(i = 0; i < 30; i++) {
univ.party.boats[i].property = false;
univ.party.horses[i].property = false;
}
break;
case 4: // edit day
edit_day();
break;
case 6: // ouit maps
if(!party_in_scen) {
display_strings(25,15);
break;
}
display_strings(13,15);
for(i = 0; i < 100; i++)
for(j = 0; j < 6; j++)
for(k = 0; k < 48; k++)
univ.out_maps[i][j][k] = 255;
break;
case 7: // town maps
if(!party_in_scen) {
display_strings(25,15);
break;
}
display_strings(14,15);
for(i = 0; i < 200; i++)
for(j = 0; j < 8; j++)
for(k = 0; k < 64; k++)
univ.town_maps[i][j][k] = 255;
break;
case 9:
display_pc(current_active_pc,0,0);
break;
case 10:
display_pc(current_active_pc,1,0);
break;
case 11:
pick_race_abil(&univ.party[current_active_pc],0);
break;
case 12:
spend_xp(current_active_pc,1,0);
break;
case 13:
edit_xp(&univ.party[current_active_pc]);
break;
}
}
//item_record_type convert_item (short_item_record_type s_item) {
// item_record_type i;
// location l = {0,0};
// short temp_val;
//
// i.variety = (short) s_item.variety;
// i.item_level = (short) s_item.item_level;
// i.awkward = (short) s_item.awkward;
// i.bonus = (short) s_item.bonus;
// i.protection = (short) s_item.protection;
// i.charges = (short) s_item.charges;
// i.type = (short) s_item.type;
// i.graphic_num = (short) s_item.graphic_num;
// if(i.graphic_num >= 25)
// i.graphic_num += 20;
// i.ability = (short) s_item.real_abil;
// i.type_flag = (short) s_item.type_flag;
// i.is_special = (short) s_item.is_special;
// i.value = (short) s_item.value;
// i.weight = s_item.weight;
// i.special_class = 0;
// i.item_loc = l;
// strcpy((char *)i.full_name,(char *)s_item.full_name);
// strcpy((char *)i.name,(char *)s_item.name);
//
// if(i.charges > 0)
// temp_val = i.value * i.charges;
// else temp_val = i.value;
// if(temp_val >= 15)
// i.treas_class = 1;
// if(temp_val >= 100)
// i.treas_class = 2;
// if(temp_val >= 900)
// i.treas_class = 3;
// if(temp_val >= 2400)
// i.treas_class = 4;
//
// i.magic_use_type = s_item.magic_use_type;
// i.ability_strength = s_item.ability_strength;
// i.reserved1 = 0;
// i.reserved2 = 0;
// i.item_properties = 0;
// if(s_item.identified)
// i.item_properties = i.item_properties | 1;
// if((s_item.ability == 14) || (s_item.ability == 129) || (s_item.ability == 95))
// i.item_properties = i.item_properties | 16;
// if(s_item.magic)
// i.item_properties = i.item_properties | 4;
//
// return i;
//}
// TODO: Let this take the item directly instead of the index
void handle_item_menu(int item_hit) {
cItemRec store_i;
if(file_in_mem.empty()) {
display_strings(5,7);
return;
}
store_i = univ.scenario.scen_items[item_hit];
store_i.ident = true;
give_to_pc(current_active_pc,store_i,false);
}
//void set_cursor(CursHandle which_curs) {
// HLock ((Handle) which_curs);
// SetCursor (*which_curs);
// HUnlock((Handle) which_curs);
//}
//short mode; // 0 - quit 1- restore
bool verify_restore_quit(bool mode) {
std::string choice;
if(file_in_mem.empty())
return true;
cChoiceDlog verify(mode ? "save-open" : "save-quit", {"save", "quit", "cancel"});
choice = verify.show();
if(choice == "cancel")
return false;
if(choice == "quit")
return true;
save_party(file_in_mem, univ);
return true;
}
//pascal bool cd_event_filter (DialogPtr hDlg, EventRecord *event, short *dummy_item_hit) {
// char chr,chr2;
// short the_type,wind_hit,item_hit;
// Handle the_handle = NULL;
// rectangle the_rect,button_rect;
// location the_point;
// CWindowPtr w;
// RgnHandle updateRgn;
//
// dummy_item_hit = 0;
//
// switch(event->what) {
// case updateEvt:
// w = GetDialogWindow(hDlg);
// updateRgn = NewRgn();
// GetWindowRegion(w, kWindowUpdateRgn, updateRgn);
// if(EmptyRgn(updateRgn)) {
// return true;
// }
// BeginUpdate(GetDialogWindow(hDlg));
// cd_redraw(hDlg);
// EndUpdate(GetDialogWindow(hDlg));
// DrawDialog(hDlg);
// return true;
// break;
//
// case keyDown:
// chr = event->message & charCodeMask;
// chr2 = (char) ((event->message & keyCodeMask) >> 8);
// switch(chr2) {
// case 126: chr = 22; break;
// case 124: chr = 21; break;
// case 123: chr = 20; break;
// case 125: chr = 23; break;
// case 53: chr = 24; break;
// case 36: chr = 31; break;
// case 76: chr = 31; break;
// }
// // specials ... 20 - <- 21 - -> 22 up 23 down 24 esc
// // 25-30 ctrl 1-6 31 - return
//
// wind_hit = cd_process_keystroke(hDlg,chr,&item_hit);
// break;
//
// case mouseDown:
// the_point = event->where;
// GlobalToLocal(&the_point);
// wind_hit = cd_process_click(hDlg,the_point, event->modifiers,&item_hit);
// break;
//
// default: wind_hit = -1; break;
// }
// switch(wind_hit) {
// case -1: break;
//
// case 917: edit_day_event_filter(item_hit); break;
// case 970: case 971: case 972: case 973: display_strings_event_filter(item_hit); break;
// case 991: display_pc_event_filter(item_hit); break;
// case 996: display_alchemy_event_filter(item_hit); break;
// case 1010: spend_xp_event_filter (item_hit); break;
// case 1012: case 947: edit_gold_or_food_event_filter (item_hit); break;
// case 1013: pick_race_abil_event_filter (item_hit); break;
// case 1018: select_pc_event_filter (item_hit); break;
// case 1024: edit_xp_event_filter (item_hit); break;
// case 1073: give_reg_info_event_filter (item_hit); break;
// default: fancy_choice_dialog_event_filter (item_hit); break;
// }
// return(wind_hit != -1);
//}

1177
src/pcedit/pc.menu.xib Normal file

File diff suppressed because it is too large Load Diff

16
src/pcedit/pc.menus.h Normal file
View File

@@ -0,0 +1,16 @@
//
// pc.menus.h
// BoE
//
// Created by Celtic Minstrel on 14-04-15.
//
//
#ifndef BoE_pc_menus_h
#define BoE_pc_menus_h
void init_menubar();
void update_item_menu();
void menu_activate();
#endif

160
src/pcedit/pc.menus.mac.mm Normal file
View File

@@ -0,0 +1,160 @@
//
// pc.menus.mac.mm
// BoE
//
// Created by Celtic Minstrel on 14-04-15.
//
//
#include "pc.menus.h"
#include <Cocoa/Cocoa.h>
#include "item.h"
#include "universe.h"
#ifndef __APPLE__
#error pc.menus.mm is Mac-specific code; try compiling pc.menus.win.cpp instead
#endif
using MenuHandle = NSMenu*;
extern void handle_apple_menu(int item_hit);
extern void handle_file_menu(int item_hit);
extern void handle_extra_menu(int item_hit);
extern void handle_edit_menu(int item_hit);
extern void handle_item_menu(int item_hit);
extern cUniverse univ;
extern fs::path file_in_mem;
extern bool scen_items_loaded;
MenuHandle menu_bar_handle;
MenuHandle apple_menu, file_menu, reg_menu, extra_menu, items_menu[4];
@interface MenuHandler : NSObject
-(void) fileMenu:(id) sender;
-(void) freeMenu:(id) sender;
-(void) specMenu:(id) sender;
-(void) itemMenu:(id) sender;
-(void) helpMenu:(id) sender;
@end
@interface ItemWrapper : NSObject
+(id) withItem:(int) theItem;
-(cItemRec&) item;
-(void) setItem:(int) theItem;
@end
static void setMenuCallback(NSMenuItem* item, id targ, SEL selector, int num) {
[item setTarget: targ];
[item setAction: selector];
[item setRepresentedObject: [[NSNumber numberWithInt: num] retain]];
}
void init_menubar() {
NSApplication* app = [NSApplication sharedApplication];
[NSBundle loadNibNamed: @"pc.menu" owner: app];
menu_bar_handle = [app mainMenu];
apple_menu = [[menu_bar_handle itemWithTitle: @"BoE Character Editor"] submenu];
file_menu = [[menu_bar_handle itemWithTitle: @"File"] submenu];
reg_menu = [[menu_bar_handle itemWithTitle: @"Free Extras"] submenu];
extra_menu = [[menu_bar_handle itemWithTitle: @"Special Edit"] submenu];
items_menu[0] = [[menu_bar_handle itemWithTitle: @"Items 1"] submenu];
items_menu[1] = [[menu_bar_handle itemWithTitle: @"Items 2"] submenu];
items_menu[2] = [[menu_bar_handle itemWithTitle: @"Items 3"] submenu];
items_menu[3] = [[menu_bar_handle itemWithTitle: @"Items 4"] submenu];
MenuHandler* handler = [[[MenuHandler alloc] init] retain];
setMenuCallback([apple_menu itemWithTitle: @"About BoE Character Editor"], handler, @selector(helpMenu:), 1);
setMenuCallback([apple_menu itemWithTitle: @"Quit BoE Character Editor"], handler, @selector(fileMenu:), 7);
// TODO: Organize the file menu handling function
setMenuCallback([file_menu itemWithTitle: @"Save"], handler, @selector(fileMenu:), 1);
setMenuCallback([file_menu itemWithTitle: @"Save As…"], handler, @selector(fileMenu:), 2);
setMenuCallback([file_menu itemWithTitle: @"Open…"], handler, @selector(fileMenu:), 3);
for(int i = 0; i < [reg_menu numberOfItems]; i++)
setMenuCallback([reg_menu itemAtIndex: i], handler, @selector(freeMenu:), i + 1);
for(int i = 0; i < [extra_menu numberOfItems]; i++)
setMenuCallback([extra_menu itemAtIndex: i], handler, @selector(specMenu:), i + 1);
update_item_menu();
menu_activate();
}
void menu_activate() {
if(file_in_mem.empty())
for(int i = 3; i < [file_menu numberOfItems]; i++)
[[file_menu itemAtIndex: i] setEnabled: NO];
else for(int i = 3; i < [file_menu numberOfItems]; i++)
[[file_menu itemAtIndex: i] setEnabled: YES];
}
void update_item_menu() {
id targ = [[file_menu itemAtIndex: 0] target];
cItemRec(& item_list)[400] = univ.scenario.scen_items;
for(int j = 0; j < 4; j++){
[items_menu[j] removeAllItems];
if(!scen_items_loaded) {
[[items_menu[j] addItemWithTitle: @"Items Not Loaded" action: @selector(itemMenu:) keyEquivalent: @""] setEnabled: NO];
} else for(int i = 0; i < 100; i++) {
ItemWrapper* item = [ItemWrapper withItem: i + 100 * j];
NSString* item_name = [NSString stringWithCString: item_list[i + j * 100].full_name.c_str() encoding: NSASCIIStringEncoding];
NSMenuItem* choice = [items_menu[j] addItemWithTitle: item_name action: @selector(itemMenu:) keyEquivalent: @""];
[choice setTarget: targ];
// TODO: Also disable gold or food
[choice setEnabled: [item item].variety != eItemType::NO_ITEM];
[choice setRepresentedObject: item];
}
}
}
@implementation MenuHandler
-(void) fileMenu:(id) sender {
handle_file_menu([[sender representedObject] shortValue]);
}
-(void) freeMenu:(id) sender {
handle_extra_menu([[sender representedObject] shortValue]);
}
-(void) specMenu:(id) sender {
handle_edit_menu([[sender representedObject] shortValue]);
}
-(void) itemMenu:(id) sender {
ItemWrapper* item = [sender representedObject];
cItemRec& theItem = [item item];
(void) theItem; // Suppress "unused parameter" warning
for(int i = 0; i < 4; i++) {
int whichItem = [items_menu[i] indexOfItem: sender];
if(whichItem >= 0)
handle_item_menu(whichItem + 100 * i);
}
}
-(void) helpMenu:(id) sender {
int i = [[sender representedObject] intValue];
if(i == 0); // TODO: "BoE Character Editor Help"
else if(i == 1) handle_apple_menu(i);
}
@end
@implementation ItemWrapper {
int itemID;
}
+(id) withItem:(int) theItem {
ItemWrapper* item = [[ItemWrapper alloc] init];
[item setItem: theItem];
return item;
}
-(void) setItem:(int) theItem {
self->itemID = theItem;
}
-(cItemRec&) item {
return univ.scenario.scen_items[self->itemID];
}
@end