2012-01-27 08:47:14 -07:00
|
|
|
/*
|
2012-01-07 08:21:07 -07:00
|
|
|
https://github.com/peterix/dfhack
|
2012-09-29 20:03:37 -06:00
|
|
|
Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com)
|
2012-01-07 08:21:07 -07:00
|
|
|
|
|
|
|
This software is provided 'as-is', without any express or implied
|
|
|
|
warranty. In no event will the authors be held liable for any
|
|
|
|
damages arising from the use of this software.
|
|
|
|
|
|
|
|
Permission is granted to anyone to use this software for any
|
|
|
|
purpose, including commercial applications, and to alter it and
|
|
|
|
redistribute it freely, subject to the following restrictions:
|
|
|
|
|
|
|
|
1. The origin of this software must not be misrepresented; you must
|
|
|
|
not claim that you wrote the original software. If you use this
|
|
|
|
software in a product, an acknowledgment in the product documentation
|
|
|
|
would be appreciated but is not required.
|
|
|
|
|
|
|
|
2. Altered source versions must be plainly marked as such, and
|
|
|
|
must not be misrepresented as being the original software.
|
|
|
|
|
|
|
|
3. This notice may not be removed or altered from any source
|
|
|
|
distribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "Internal.h"
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
#include <map>
|
|
|
|
#include <cassert>
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
#include "Core.h"
|
2012-04-05 09:55:59 -06:00
|
|
|
#include "Error.h"
|
2012-01-07 08:21:07 -07:00
|
|
|
#include "PluginManager.h"
|
2012-01-08 09:02:12 -07:00
|
|
|
#include "MiscUtils.h"
|
2012-11-12 08:17:32 -07:00
|
|
|
#include "Types.h"
|
2012-01-07 08:21:07 -07:00
|
|
|
|
|
|
|
#include "modules/Job.h"
|
|
|
|
#include "modules/Materials.h"
|
2012-01-09 05:20:17 -07:00
|
|
|
#include "modules/Items.h"
|
2012-01-07 08:21:07 -07:00
|
|
|
|
|
|
|
#include "DataDefs.h"
|
2012-01-15 13:54:14 -07:00
|
|
|
#include "df/world.h"
|
|
|
|
#include "df/ui.h"
|
|
|
|
#include "df/job.h"
|
|
|
|
#include "df/job_item.h"
|
|
|
|
#include "df/job_list_link.h"
|
2012-05-03 01:47:04 -06:00
|
|
|
#include "df/specific_ref.h"
|
2012-01-15 13:54:14 -07:00
|
|
|
#include "df/general_ref.h"
|
|
|
|
#include "df/general_ref_unit_workerst.h"
|
|
|
|
#include "df/general_ref_building_holderst.h"
|
2012-01-07 08:21:07 -07:00
|
|
|
|
|
|
|
using namespace DFHack;
|
|
|
|
using namespace df::enums;
|
|
|
|
|
2013-05-25 07:44:17 -06:00
|
|
|
df::job *DFHack::Job::cloneJobStruct(df::job *job, bool keepEverything)
|
2012-01-07 08:21:07 -07:00
|
|
|
{
|
2012-04-05 09:55:59 -06:00
|
|
|
CHECK_NULL_POINTER(job);
|
|
|
|
|
2012-01-07 08:21:07 -07:00
|
|
|
df::job *pnew = new df::job(*job);
|
2013-05-25 07:44:17 -06:00
|
|
|
|
|
|
|
if ( !keepEverything ) {
|
|
|
|
// Clean out transient fields
|
|
|
|
pnew->flags.whole = 0;
|
|
|
|
pnew->flags.bits.repeat = job->flags.bits.repeat;
|
|
|
|
pnew->flags.bits.suspend = job->flags.bits.suspend;
|
|
|
|
|
|
|
|
pnew->completion_timer = -1;
|
2012-01-07 08:21:07 -07:00
|
|
|
}
|
2013-05-25 07:44:17 -06:00
|
|
|
pnew->list_link = NULL;
|
2012-01-07 08:21:07 -07:00
|
|
|
|
2013-05-25 07:44:17 -06:00
|
|
|
//pnew->items.clear();
|
|
|
|
//pnew->specific_refs.clear();
|
|
|
|
pnew->general_refs.clear();
|
|
|
|
//pnew->job_items.clear();
|
|
|
|
|
|
|
|
if ( keepEverything ) {
|
|
|
|
for ( int a = 0; a < pnew->items.size(); a++ )
|
|
|
|
pnew->items[a] = new df::job_item_ref(*pnew->items[a]);
|
|
|
|
for ( int a = 0; a < pnew->specific_refs.size(); a++ )
|
|
|
|
pnew->specific_refs[a] = new df::specific_ref(*pnew->specific_refs[a]);
|
|
|
|
} else {
|
|
|
|
pnew->items.clear();
|
|
|
|
pnew->specific_refs.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( int a = 0; a < pnew->job_items.size(); a++ )
|
|
|
|
pnew->job_items[a] = new df::job_item(*pnew->job_items[a]);
|
|
|
|
|
|
|
|
for ( int a = 0; a < job->general_refs.size(); a++ )
|
|
|
|
if ( keepEverything || job->general_refs[a]->getType() != df::enums::general_ref_type::UNIT_WORKER )
|
|
|
|
pnew->general_refs.push_back(job->general_refs[a]->clone());
|
|
|
|
|
2012-01-07 08:21:07 -07:00
|
|
|
return pnew;
|
|
|
|
}
|
|
|
|
|
2013-05-25 07:44:17 -06:00
|
|
|
void DFHack::Job::deleteJobStruct(df::job *job, bool keptEverything)
|
2012-01-07 08:21:07 -07:00
|
|
|
{
|
|
|
|
if (!job)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Only allow free-floating job structs
|
2013-05-25 07:44:17 -06:00
|
|
|
if ( !keptEverything )
|
|
|
|
assert(!job->list_link && job->items.empty() && job->specific_refs.empty());
|
|
|
|
else
|
|
|
|
assert(!job->list_link);
|
|
|
|
|
|
|
|
if ( keptEverything ) {
|
|
|
|
for ( int a = 0; a < job->items.size(); a++ )
|
|
|
|
delete job->items[a];
|
|
|
|
for ( int a = 0; a < job->specific_refs.size(); a++ )
|
|
|
|
delete job->specific_refs[a];
|
|
|
|
}
|
|
|
|
for ( int a = 0; a < job->job_items.size(); a++ )
|
|
|
|
delete job->job_items[a];
|
|
|
|
for ( int a = 0; a < job->general_refs.size(); a++ )
|
|
|
|
delete job->general_refs[a];
|
2012-01-07 08:21:07 -07:00
|
|
|
|
|
|
|
delete job;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CMP(field) (a.field == b.field)
|
|
|
|
|
|
|
|
bool DFHack::operator== (const df::job_item &a, const df::job_item &b)
|
|
|
|
{
|
2012-04-05 09:55:59 -06:00
|
|
|
CHECK_NULL_POINTER(&a);
|
|
|
|
CHECK_NULL_POINTER(&b);
|
|
|
|
|
2012-01-07 08:21:07 -07:00
|
|
|
if (!(CMP(item_type) && CMP(item_subtype) &&
|
|
|
|
CMP(mat_type) && CMP(mat_index) &&
|
|
|
|
CMP(flags1.whole) && CMP(quantity) && CMP(vector_id) &&
|
|
|
|
CMP(flags2.whole) && CMP(flags3.whole) &&
|
|
|
|
CMP(metal_ore) && CMP(reaction_class) &&
|
|
|
|
CMP(has_material_reaction_product) &&
|
|
|
|
CMP(min_dimension) && CMP(reagent_index) &&
|
|
|
|
CMP(reaction_id) && CMP(has_tool_use) &&
|
|
|
|
CMP(contains.size())))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (int i = a.contains.size()-1; i >= 0; i--)
|
|
|
|
if (a.contains[i] != b.contains[i])
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DFHack::operator== (const df::job &a, const df::job &b)
|
|
|
|
{
|
2012-04-05 09:55:59 -06:00
|
|
|
CHECK_NULL_POINTER(&a);
|
|
|
|
CHECK_NULL_POINTER(&b);
|
|
|
|
|
2013-05-04 19:55:08 -06:00
|
|
|
if (!(CMP(job_type) && CMP(job_subtype) &&
|
2012-01-07 08:21:07 -07:00
|
|
|
CMP(mat_type) && CMP(mat_index) &&
|
|
|
|
CMP(item_subtype) && CMP(item_category.whole) &&
|
|
|
|
CMP(hist_figure_id) && CMP(material_category.whole) &&
|
|
|
|
CMP(reaction_name) && CMP(job_items.size())))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (int i = a.job_items.size()-1; i >= 0; i--)
|
|
|
|
if (!(*a.job_items[i] == *b.job_items[i]))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-04-10 01:43:36 -06:00
|
|
|
void DFHack::Job::printItemDetails(color_ostream &out, df::job_item *item, int idx)
|
2012-01-07 08:21:07 -07:00
|
|
|
{
|
2012-04-05 09:55:59 -06:00
|
|
|
CHECK_NULL_POINTER(item);
|
|
|
|
|
2012-01-09 05:20:17 -07:00
|
|
|
ItemTypeInfo info(item);
|
2012-03-10 04:55:42 -07:00
|
|
|
out << " Input Item " << (idx+1) << ": " << info.toString();
|
2012-01-09 05:20:17 -07:00
|
|
|
|
2012-01-07 08:21:07 -07:00
|
|
|
if (item->quantity != 1)
|
2012-03-10 04:55:42 -07:00
|
|
|
out << "; quantity=" << item->quantity;
|
2012-01-07 08:21:07 -07:00
|
|
|
if (item->min_dimension >= 0)
|
2012-03-10 04:55:42 -07:00
|
|
|
out << "; min_dimension=" << item->min_dimension;
|
|
|
|
out << endl;
|
2012-01-07 08:21:07 -07:00
|
|
|
|
|
|
|
MaterialInfo mat(item);
|
|
|
|
if (mat.isValid() || item->metal_ore >= 0) {
|
2012-03-10 04:55:42 -07:00
|
|
|
out << " material: " << mat.toString();
|
2012-01-07 08:21:07 -07:00
|
|
|
if (item->metal_ore >= 0)
|
2012-03-10 04:55:42 -07:00
|
|
|
out << "; ore of " << MaterialInfo(0,item->metal_ore).toString();
|
|
|
|
out << endl;
|
2012-01-07 08:21:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (item->flags1.whole)
|
2012-03-17 02:52:22 -06:00
|
|
|
out << " flags1: " << bitfield_to_string(item->flags1) << endl;
|
2012-01-07 08:21:07 -07:00
|
|
|
if (item->flags2.whole)
|
2012-03-17 02:52:22 -06:00
|
|
|
out << " flags2: " << bitfield_to_string(item->flags2) << endl;
|
2012-01-07 08:21:07 -07:00
|
|
|
if (item->flags3.whole)
|
2012-03-17 02:52:22 -06:00
|
|
|
out << " flags3: " << bitfield_to_string(item->flags3) << endl;
|
2012-01-07 08:21:07 -07:00
|
|
|
|
|
|
|
if (!item->reaction_class.empty())
|
2012-03-10 04:55:42 -07:00
|
|
|
out << " reaction class: " << item->reaction_class << endl;
|
2012-01-07 08:21:07 -07:00
|
|
|
if (!item->has_material_reaction_product.empty())
|
2012-03-10 04:55:42 -07:00
|
|
|
out << " reaction product: " << item->has_material_reaction_product << endl;
|
2012-09-10 07:19:21 -06:00
|
|
|
if (item->has_tool_use >= (df::tool_uses)0)
|
2012-03-10 04:55:42 -07:00
|
|
|
out << " tool use: " << ENUM_KEY_STR(tool_uses, item->has_tool_use) << endl;
|
2012-01-07 08:21:07 -07:00
|
|
|
}
|
|
|
|
|
2012-04-10 01:43:36 -06:00
|
|
|
void DFHack::Job::printJobDetails(color_ostream &out, df::job *job)
|
2012-01-07 08:21:07 -07:00
|
|
|
{
|
2012-04-05 09:55:59 -06:00
|
|
|
CHECK_NULL_POINTER(job);
|
|
|
|
|
2012-08-18 23:21:25 -06:00
|
|
|
out.color(job->flags.bits.suspend ? COLOR_DARKGREY : COLOR_GREY);
|
2012-03-10 04:55:42 -07:00
|
|
|
out << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_type);
|
2012-01-07 08:21:07 -07:00
|
|
|
if (job->flags.whole)
|
2012-03-17 02:52:22 -06:00
|
|
|
out << " (" << bitfield_to_string(job->flags) << ")";
|
2012-03-10 04:55:42 -07:00
|
|
|
out << endl;
|
|
|
|
out.reset_color();
|
2012-01-07 08:21:07 -07:00
|
|
|
|
2012-01-09 05:20:17 -07:00
|
|
|
df::item_type itype = ENUM_ATTR(job_type, item, job->job_type);
|
|
|
|
|
2012-01-07 08:21:07 -07:00
|
|
|
MaterialInfo mat(job);
|
2012-01-09 05:20:17 -07:00
|
|
|
if (itype == item_type::FOOD)
|
|
|
|
mat.decode(-1);
|
|
|
|
|
2012-01-07 08:21:07 -07:00
|
|
|
if (mat.isValid() || job->material_category.whole)
|
|
|
|
{
|
2012-03-10 04:55:42 -07:00
|
|
|
out << " material: " << mat.toString();
|
2012-01-07 08:21:07 -07:00
|
|
|
if (job->material_category.whole)
|
2012-03-17 02:52:22 -06:00
|
|
|
out << " (" << bitfield_to_string(job->material_category) << ")";
|
2012-03-10 04:55:42 -07:00
|
|
|
out << endl;
|
2012-01-07 08:21:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (job->item_subtype >= 0 || job->item_category.whole)
|
2012-01-09 05:20:17 -07:00
|
|
|
{
|
|
|
|
ItemTypeInfo iinfo(itype, job->item_subtype);
|
|
|
|
|
2012-03-10 04:55:42 -07:00
|
|
|
out << " item: " << iinfo.toString()
|
2012-03-17 02:52:22 -06:00
|
|
|
<< " (" << bitfield_to_string(job->item_category) << ")" << endl;
|
2012-01-09 05:20:17 -07:00
|
|
|
}
|
2012-01-07 08:21:07 -07:00
|
|
|
|
|
|
|
if (job->hist_figure_id >= 0)
|
2012-03-10 04:55:42 -07:00
|
|
|
out << " figure: " << job->hist_figure_id << endl;
|
2012-01-07 08:21:07 -07:00
|
|
|
|
|
|
|
if (!job->reaction_name.empty())
|
2012-03-10 04:55:42 -07:00
|
|
|
out << " reaction: " << job->reaction_name << endl;
|
2012-01-07 08:21:07 -07:00
|
|
|
|
2012-01-31 09:55:38 -07:00
|
|
|
for (size_t i = 0; i < job->job_items.size(); i++)
|
2012-04-10 01:43:36 -06:00
|
|
|
printItemDetails(out, job->job_items[i], i);
|
2012-01-07 08:21:07 -07:00
|
|
|
}
|
2012-01-08 09:02:12 -07:00
|
|
|
|
2012-11-12 08:17:32 -07:00
|
|
|
df::general_ref *Job::getGeneralRef(df::job *job, df::general_ref_type type)
|
|
|
|
{
|
|
|
|
CHECK_NULL_POINTER(job);
|
|
|
|
|
|
|
|
return findRef(job->general_refs, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
df::specific_ref *Job::getSpecificRef(df::job *job, df::specific_ref_type type)
|
|
|
|
{
|
|
|
|
CHECK_NULL_POINTER(job);
|
|
|
|
|
|
|
|
return findRef(job->specific_refs, type);
|
|
|
|
}
|
|
|
|
|
2012-04-10 01:43:36 -06:00
|
|
|
df::building *DFHack::Job::getHolder(df::job *job)
|
2012-01-08 09:02:12 -07:00
|
|
|
{
|
2012-04-05 09:55:59 -06:00
|
|
|
CHECK_NULL_POINTER(job);
|
|
|
|
|
2012-11-12 07:27:58 -07:00
|
|
|
for (size_t i = 0; i < job->general_refs.size(); i++)
|
2012-01-08 09:02:12 -07:00
|
|
|
{
|
2012-11-12 07:27:58 -07:00
|
|
|
VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->general_refs[i]);
|
2012-01-08 09:02:12 -07:00
|
|
|
if (ref)
|
|
|
|
return ref->getBuilding();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-04-10 01:43:36 -06:00
|
|
|
df::unit *DFHack::Job::getWorker(df::job *job)
|
|
|
|
{
|
|
|
|
CHECK_NULL_POINTER(job);
|
|
|
|
|
2012-11-12 07:27:58 -07:00
|
|
|
for (size_t i = 0; i < job->general_refs.size(); i++)
|
2012-04-10 01:43:36 -06:00
|
|
|
{
|
2012-11-12 07:27:58 -07:00
|
|
|
VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->general_refs[i]);
|
2012-04-10 01:43:36 -06:00
|
|
|
if (ref)
|
|
|
|
return ref->getUnit();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-05-12 10:12:09 -06:00
|
|
|
void DFHack::Job::checkBuildingsNow()
|
|
|
|
{
|
|
|
|
if (df::global::process_jobs)
|
|
|
|
*df::global::process_jobs = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DFHack::Job::checkDesignationsNow()
|
|
|
|
{
|
|
|
|
if (df::global::process_dig)
|
|
|
|
*df::global::process_dig = true;
|
|
|
|
}
|
|
|
|
|
2012-04-10 01:43:36 -06:00
|
|
|
bool DFHack::Job::linkIntoWorld(df::job *job, bool new_id)
|
2012-01-08 09:02:12 -07:00
|
|
|
{
|
|
|
|
using df::global::world;
|
|
|
|
using df::global::job_next_id;
|
|
|
|
|
|
|
|
assert(!job->list_link);
|
|
|
|
|
|
|
|
if (new_id) {
|
|
|
|
job->id = (*job_next_id)++;
|
|
|
|
|
|
|
|
job->list_link = new df::job_list_link();
|
|
|
|
job->list_link->item = job;
|
|
|
|
linked_list_append(&world->job_list, job->list_link);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
df::job_list_link *ins_pos = &world->job_list;
|
|
|
|
while (ins_pos->next && ins_pos->next->item->id < job->id)
|
|
|
|
ins_pos = ins_pos->next;
|
|
|
|
|
|
|
|
if (ins_pos->next && ins_pos->next->item->id == job->id)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
job->list_link = new df::job_list_link();
|
|
|
|
job->list_link->item = job;
|
|
|
|
linked_list_insert_after(ins_pos, job->list_link);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2012-04-10 01:43:36 -06:00
|
|
|
|
|
|
|
bool DFHack::Job::listNewlyCreated(std::vector<df::job*> *pvec, int *id_var)
|
|
|
|
{
|
|
|
|
using df::global::world;
|
|
|
|
using df::global::job_next_id;
|
|
|
|
|
|
|
|
pvec->clear();
|
|
|
|
|
|
|
|
if (!job_next_id || *job_next_id <= *id_var)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int old_id = *id_var;
|
|
|
|
int cur_id = *job_next_id;
|
|
|
|
|
|
|
|
*id_var = cur_id;
|
|
|
|
|
|
|
|
pvec->reserve(std::min(20,cur_id - old_id));
|
|
|
|
|
|
|
|
df::job_list_link *link = world->job_list.next;
|
|
|
|
for (; link; link = link->next)
|
|
|
|
{
|
|
|
|
int id = link->item->id;
|
|
|
|
if (id >= old_id)
|
|
|
|
pvec->push_back(link->item);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2012-04-29 11:07:39 -06:00
|
|
|
|
|
|
|
bool DFHack::Job::attachJobItem(df::job *job, df::item *item,
|
|
|
|
df::job_item_ref::T_role role,
|
|
|
|
int filter_idx, int insert_idx)
|
|
|
|
{
|
|
|
|
CHECK_NULL_POINTER(job);
|
|
|
|
CHECK_NULL_POINTER(item);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Functionality 100% reverse-engineered from DF code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (role != df::job_item_ref::TargetContainer)
|
|
|
|
{
|
|
|
|
if (item->flags.bits.in_job)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
item->flags.bits.in_job = true;
|
|
|
|
}
|
|
|
|
|
2012-05-03 01:47:04 -06:00
|
|
|
auto item_link = new df::specific_ref();
|
|
|
|
item_link->type = specific_ref_type::JOB;
|
2012-04-29 11:07:39 -06:00
|
|
|
item_link->job = job;
|
2012-05-03 01:47:04 -06:00
|
|
|
item->specific_refs.push_back(item_link);
|
2012-04-29 11:07:39 -06:00
|
|
|
|
|
|
|
auto job_link = new df::job_item_ref();
|
|
|
|
job_link->item = item;
|
|
|
|
job_link->role = role;
|
|
|
|
job_link->job_item_idx = filter_idx;
|
|
|
|
|
|
|
|
if (size_t(insert_idx) < job->items.size())
|
|
|
|
vector_insert_at(job->items, insert_idx, job_link);
|
|
|
|
else
|
|
|
|
job->items.push_back(job_link);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2012-10-17 09:16:18 -06:00
|
|
|
|
|
|
|
bool Job::isSuitableItem(df::job_item *item, df::item_type itype, int isubtype)
|
|
|
|
{
|
|
|
|
CHECK_NULL_POINTER(item);
|
|
|
|
|
|
|
|
if (itype == item_type::NONE)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
ItemTypeInfo iinfo(itype, isubtype);
|
|
|
|
MaterialInfo minfo(item);
|
|
|
|
|
|
|
|
return iinfo.isValid() && iinfo.matches(*item, &minfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Job::isSuitableMaterial(df::job_item *item, int mat_type, int mat_index)
|
|
|
|
{
|
|
|
|
CHECK_NULL_POINTER(item);
|
|
|
|
|
|
|
|
if (mat_type == -1 && mat_index == -1)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
ItemTypeInfo iinfo(item);
|
|
|
|
MaterialInfo minfo(mat_type, mat_index);
|
|
|
|
|
|
|
|
return minfo.isValid() && iinfo.matches(*item, &minfo);
|
|
|
|
}
|