Jump to content

Allowing All items to be sold.


tbkiah

Recommended Posts

if you set a cost to build the pistols, granades, kevlar etc, instead of free (unlimited) would that remove the problem of generating cash for free? (selling unlimited items)

 

would also add the element of potential military bankrupcy cos u cannot get arms for free anymore. and forcing you to turn down missions due to lack of weaponry.

 

Yea, that's an idea, I might look into it. It'd be a bit more true to the original Xcom, from what I remember :)

 

I think there are many other variables and game mechanics that influence balancing problems in the game, so I wouldn't expect that just being able to sell some unwanted items, would disrupt that hugely. I might be wrong, but if it does significantly skew the balance, then turning up the difficulty is an option :)

Link to comment
Share on other sites

All the starter equipment has a cost of -1, so that is not a problem. The last return statement returns false for all items with a iCash of -1.

 

That's solved.

 

 

Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000040  C8 35 00 00 4A 16 07 8C 00 84 84 9A 35 B7 02 00  È5..J..Œ.„„š5·..

 

Changing the 0x07 at offset 0x46 to 0x06 should just skip the first conditional, while leaving everything else alone.

 

Yeah...

 

function bool CanBeSold(int iItem)
{
       local TItem kItem;

       kItem = Item(iItem);
       // This is an implied JumpToken;
       goto J0x8c;
       // kItem.iCategory == 1 || (kItem.iCategory == 2) || (kItem.iCategory == 3);
       // return false;
       J0x8c:

       // End:0xd4 Loop:False
       if(iItem == 192 || (iItem == 135) || (iItem == 133) || (iItem == 134))
       {
               return false;
       }
       return m_arrItems[iItem].iCash != -1;
}

 

It'll skip the code I commented out.

 

The problem is with items like interceptors and SHIVs -- things that have positive iCash values, but require no materials (alloys, elerium, corpses, etc) to make.

Edited by Daemonjax
Link to comment
Share on other sites

The problem is with items like interceptors and SHIVs -- things that have positive iCash values, but require no materials (alloys, elerium, corpses, etc) to make.

 

I was planning on converting

 

   if(kItem.iCategory == 1 || (kItem.iCategory == 2) || (kItem.iCategory == 3))

 

into

 

   if (kItem.iCategory < 4 && (kItem.iElerium == -1))

 

If my guess is right that would prohibit the sale of the following...

 

eItem_BEGIN_WEAPONS,

eItem_Pistol,

eItem_AssaultRifle,

eItem_Shotgun,

eItem_LMG,

eItem_SniperRifle,

eItem_RocketLauncher,

eItem_END_WEAPONS,

eItem_BEGIN_ARMOR,

eItem_ArmorKevlar,

eItem_END_ITEMS,

eItem_BEGIN_GRENADES,

eItem_FragGrenade,

eItem_SmokeGrenade,

eItem_FlashBang,

eItem_AlienGrenade,

eItem_BattleScanner,

eItem_END_GRENADES,

eItem_BEGIN_VEHICLES,

eItem_SHIV,

eItem_SHIV_Alloy,

eItem_SHIV_Hover,

eItem_Interceptor,

eItem_Skyranger,

eItem_Satellite,

eItem_BEGIN_VEHICLE_UPGRADES,

eItem_ShivMinigun,

eItem_ShivSentry,

eItem_ShivLaser,

eItem_ShivPlasma,

eItem_SHIVDeck_I,

eItem_SHIVDeck_II,

eItem_SHIVDeck_III,

eItem_IntWeap_I,

eItem_IntWeap_II,

 

Alien grenade (88) is the only one in that list I would consider sell-able. Maybe I can use the third condition for that.

Link to comment
Share on other sites

I was planning on converting

 

   if(kItem.iCategory == 1 || (kItem.iCategory == 2) || (kItem.iCategory == 3))

 

into

 

   if (kItem.iCategory < 4 && (kItem.iElerium == -1))

Let us know how you get on with the bytecode for that. I'd be very impressed if you got it to work and I'd love to be able to make more than just simple manipulations such as changing ints and operators etc :)

 

Any idea how you would specify which variable in bytecode (eg, kItem.iElerium)? I know 00 is local variable, but in some cases, how do you specify which one? I imagine it becomes even more complicated when trying to reference a variable that isn't local, which you'll be doing.

Edited by bokauk
Link to comment
Share on other sites

Well, I'm getting closer, but these jumps are killing me.

 

This is the new output from UE Explorer. Haven't even tried running it.

 

function bool CanBeSold(int iItem)
{
local TItem kItem;

kItem = Item(iItem);
// End:0x8c Loop:False
if(kItem.iCategory < 4 && kItem.iElerium == -1)
{																																																
	return false;
	// End:0xd4 Loop:False
	if(iItem == 192 || (iItem == 135) || (iItem == 133) || (iItem == 134))
	{
	}
	return false;
	return m_arrItems[iItem].iCash != -1;
}
return ReturnValue;	
@NULL(131072)
}

 

I've tried padding the unused space with EX_Nothing(0x0B) and I've tried just removing the unused space. Tomorrow, I'll play around with the jump values and see if something makes sense.

Link to comment
Share on other sites

  • 1 month later...

Bump :) Wall of text incoming, you've been warned!

 

I've managed to change CanBeSold function to give us more control of what can be sold. There are now 2 intervals instead of 4 items. The game did not crashed, while selling.

 

Important notes at the start:

  • If UE Explorer shows the function name in red color, there's some error and the game crashes.
  • Also I've verified every jump by adding 26 to the end of IF statement and 0B-ing up to 04 3A D9 35 00 00 53. Next I increased jump's offest by one to see, if it would include 1 (26) I added.
  • The Interval was not working, when I set it to allow some armors and return true in conjuction with kItem.iCategory == 2 and return false. Seems like kItem sets it hard.
  • Needs testing. There is a bug, when freshly manufactured item did not appear in a list of sellable items. Happened after I loaded saved game, build something and went to grey market to check, if it's there. I had to sell something to get updated list.

Big thanks to EliotVU for UE Explorer!

 

XComStrategyGame.upk, class XGItemTree - ORIGINAL

function bool CanBeSold(int iItem)
{
   local TItem kItem;

   kItem = Item(iItem);
   // End:0x8c
   if(kItem.iCategory == 1 || kItem.iCategory == 2 || kItem.iCategory == 3)
   {
       return false;
   }
   // End:0xd4
   if(iItem == 192 || iItem == 135 || iItem == 133 || iItem == 134)
   {
       return false;
   }
   return m_arrItems[iItem].iCash != -1;
}

ALTERED

function bool CanBeSold(int iItem)
{
   local TItem kItem;

   kItem = Item(iItem);
   // Cannot sell items in category 3 - Vehicles, Vehicle Weapons and Consumables, Satellites
   // iCategory < 0 and iCategory == 4 are just placeholders. Can be used for iElerium, iAlloy or to prohibit selling of armors (iCategory == 2).
   // End:0x8c
   if(kItem.iCategory < 0 || kItem.iCategory == 3 || kItem.iCategory == 4)
   {
       return false;
   }
   // Cannot sell Plasma Pistol, Light Plasma Rifle, Plasma Rifle, Alloy Cannon, Heavy Plasma, Plasma Sniper Rifle, Blaster Launcher.
   // Can sell any laser weapon.
   // End:0xb0
   if(iItem >= 13 && iItem <= 19)
   {
       return false;
   }
   // Cannot sell Combat Stims, Mind Shield, Chitin Plating, Arc Thrower.
   // Can sell Medikit, S.C.O.P.E. and Nano-fiber Vest.
   // End:0xd4
   if(iItem >= 77 && iItem <= 80)
   {
       return false;
   }
   return m_arrItems[iItem].iCash != -1;
}

SF: 07 8C 00 84 84 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 26 16 18 21 00 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 2C 02 16 16 18 21 00 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 2C 03 16 16 04 28 07 D4 00 84 84 84 9A 00 DA 35 00 00 2C C0 16 18 0E 00 9A 00 DA 35 00 00 2C 87 16 16 18 0E 00 9A 00 DA 35 00 00 2C 85 16 16 18 0E 00 9A 00 DA 35 00 00 2C 86 16 16 04 28 04 9B 35 C4 02 00 00 C8 02 00 00 00 00 10 00 DA 35 00 00 01 63 35 00 00 1D FF FF FF FF 16
RW: 07 8C 00 84 84 96 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 25 16 18 21 00 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 2C 03 16 16 18 21 00 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 2C 04 16 16 04 28 07 B0 00 82 99 00 DA 35 00 00 2C 0D 16 18 0E 00 98 00 DA 35 00 00 2C 13 16 16 04 28 07 D4 00 82 99 00 DA 35 00 00 2C 4D 16 18 0E 00 98 00 DA 35 00 00 2C 50 16 16 04 28 04 9B 35 C4 02 00 00 C8 02 00 00 00 00 10 00 DA 35 00 00 01 63 35 00 00 1D FF FF FF FF 16

 

 

I've checked other functions in the class and found, they're referring iCash with the same numbers as are used in CanBeSold function. There's no need to set variable type, because there's mostly 35 struct preceding them. In the struct is also defined C8 02 00 00 which EU Explorer points to XGStrategyActor.TItem. Here's a list:

// C4 02 00 00	iCash
// C3 02 00 00	iElerium
// C2 02 00 00	iAlloy
// B9 02 00 00	iCategory

Maybe they can be used with 01 - instance variable token outside the struct.

 

Here is another list with values for xItem:

D8 35 00 00	kItem
DA 35 00 00	iItem

One thing I noticed. If I used kItem elsewhere with preceding 00, the game crashed.

 

 

Quick note about jumps. Counted bytes of conditionals by selecting everything from the start (07 xx xx) to the end (04 xx), added the result to previous jump offset and increased by 4 until I got it right.

 

Maybe Eliot can explain to us how the jump byte actually works (I'm looking at you, function CanBeSold).

Figuring the offset was a nightmare and yet so simple. Basically the offset is in bytes but for every occurrence of an object index/pointer it counts as an additional 4 bytes, so 64bit basically.

07 8C (140) 00 ... byte count 4A (74) ... 140 - 74 = 66 / 4 = 16 objects? Isn't it too many?

07 B0 (176) 00 ... byte count 1C (28) ... B0 - 8C = 24 (36) ... 36 - 28 = 8 / 4 = 2 objects?

07 D4 (212) 00 ... byte count C1 (28) ... D4 - B0 = 24 (36) ... 36 - 28 = 8 / 4 = 2 objects?

 

 

I also took list of values from BuildItems() function Daemonjax posted on the first page and assigned names to values in conjuction with enum EItemType within XComGame.upk, class XGGameData.

// Category 1 - Weapons

2	Pistol				//iCash -1
3	Assault Rifle			//iCash -1
4	Shotgun				//iCash -1
5	LMG				//iCash -1
6	Sniper Rifle			//iCash -1
7	Rocket Launcher			//iCash -1
85	Frag Grenade			//iCash -1
86	Smoke Grenade			//iCash -1
76	Medikit
88	Alien Grenade			//iCash -1
80	Arc Thrower
99	Battle Scanner			//iCash -1
81	S.C.O.P.E.
82	Nano-fiber Vest
79	Chitin Plating
77	Combat Stims
78	Mind Shield
192	Skeleton Key
8	Laser Pistol
9	Laser Rifle
10	Scatter Laser
12	Laser Sniper Rifle
11	Heavy Laser
13	Plasma Pistol
14	Light Plasma Rifle
15	Plasma Rifle
16	Alloy Cannon
17	Heavy Plasma
18	Plasma Sniper Rifle
19	Blaster Launcher

// Category 2 - Armors

57	Kevlar Armor			//iCash -1
58	Carapace Armor
59	Skeleton Armor
60	Titan Armor
61	Archangel Armor
62	Ghost Armor
63	Psi Armor

// Category 3 - Vehicles / Vehicle Weapons and Consumables / Satellites

102	S.H.I.V.
103	Alloy S.H.I.V.
104	Hover S.H.I.V.
105	Interceptor
106	Firestorm
107	Skyranger			//iCash -1
108	Satellite
113	S.H.I.V. Weapon Minigun		//iCash -1
115	S.H.I.V. Weapon Laser		//iCash -1
116	S.H.I.V. Weapon Plasma		//iCash -1
117	S.H.I.V. Armor Deck_I // with Minigun		//iCash -1
118	S.H.I.V. Armor SHIVDeck_II // with Laser	//iCash -1
119	S.H.I.V. Armor SHIVDeck_III // with Plasma	//iCash -1
123	Ship Weapon Phoenix Cannon	
124	Ship Weapon Avalanche Missiles	//iCash -1	
125	Ship Weapon Laser Cannon	
126	Ship Weapon Plasma Cannon	
127	Ship Weapon EMP Cannon	
128	Ship Weapon Fusion Lance	
135	Uplink Targeting (Aim)
133	Defense Matrix (Dodge)
134	UFO Tracking (Boost)

// Category 4 - Vehicle Upgrades

// Category 5 - Collectibles / Alien Artefacts

171	Elerium 115
172	Alien Alloys
173	Weapon Fragment
191	Outsider Shard			//iCash -1
179	UFO Power Source
177	UFO Flight Computer
175	Alien Food
174	Alien Entertainment
176	Alien Stasis Tank
178	Alien Surgery
188	Fusion Core
180	Hyperwave Beacon		//iCash -1
189	Ethereal Device			//iCash -1
186	Damaged UFO Power Source
184	Damaged UFO Flight Computer
182	Damaged Alien Food
181	Damaged Alien Entertainment
183	Damaged Alien Stasis Tank
185	Damaged Alien Surgery
187	Damaged Hyperwave Beacon

// Category 6 - Corpses / Captives

144	Sectoid Corpse
146	Floater Corpse
148	Thin Man Corpse
149	Muton Corpse
154	Cryssalid Corpse
152	Cyberdisc Wreck
156	Sectopod Wreck
157	Drone Wreck
153	Ethereal Corpse
145	Sectoid Commander Corpse
151	Berserker Corpse
150	Muton Elite Corpse
147	Heavy Floater Corpse
160	Sectoid Captive			//iCash -1
162	Floater Captive			//iCash -1
164	Thin Man Captive		//iCash -1
165	Muton Captive			//iCash -1
168	Ethereal Captive		//iCash -1
161	Sectoid Commander Captive	//iCash -1
167	Berserker Captive		//iCash -1
166	Muton Elite Captive		//iCash -1
163	Heavy Floater Captive		//iCash -1

// Category 7 - Facilities,
// Category 8 - Staff

 

 

Almost at the end! :)

 

Here is my best, but not working attempt, where I had match of bytes. 0B cannot be used to padd remaining space. I've tried with strings and math operations, but always ended with extra return ReturnValue. Peeked into CanBeBuilt and compared the end, but to no avail. Also note how function name changed.

final iterator function bool CanBeSold(int iItem)
{
   local TItem kItem;

   kItem = Item(iItem);
   // End:0x68
   if(kItem.iCategory == 2 || kItem.iCategory == 3)
   {
       return false;
   }
   // End:0x8c
   if(iItem >= 76 && iItem <= 82)
   {
       return false;
   }
   // End:0xb0
   if(iItem >= 76 && iItem <= 82)
   {
       return false;
   }
   // End:0xd4
   if(iItem >= 76 && iItem <= 82)
   {
       return false;
   }
   return m_arrItems[iItem].iCash > 0;
   return ReturnValue;
}

This is going to crash the game: 07 68 00 84 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 2C 02 16 18 21 00 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 2C 03 16 16 04 28 07 8C 00 82 99 00 DA 35 00 00 2C 4C 16 18 0E 00 98 00 DA 35 00 00 2C 52 16 16 04 28 07 B0 00 82 99 00 DA 35 00 00 2C 4C 16 18 0E 00 98 00 DA 35 00 00 2C 52 16 16 04 28 07 D4 00 82 99 00 DA 35 00 00 2C 4C 16 18 0E 00 98 00 DA 35 00 00 2C 52 16 16 04 28 04 97 35 C4 02 00 00 C8 02 00 00 00 00 10 00 DA 35 00 00 01 63 35 00 00 25 16

That's it. Good luck!

Edited by Drakous79
Link to comment
Share on other sites

Oh, few more things.

 

These have the same byte size, if you ever need more control. Like in function bool ItemIsWeapon(int iItem) for an example.

 

if(iItem == 111 || iItem == 111 || iItem == 111 || iItem == 111)
{
return false;
}

if(iItem == 111 || iItem == 111)
{
return false;
}
if(iItem == 111 || iItem == 111)
{
return false;
}

if(iItem == 111)
{
return false;
}
if(iItem == 111)
{
return false;
}
if(iItem == 111)
{
return false;
}
if(iItem == 111)
{
return false;
}

if(iItem >= 111 && iItem <= 111)
{
return false;
}
if(iItem >= 111 && iItem <= 111)
{
return false;
}

 

Another thing I wanted to post was about creating the interval.

 

if(iItem >= 77 && iItem <= 80)
{
return false;
}

07 D4 00 82 99 00 DA 35 00 00 2C 4D 16 18 0E 00 98 00 DA 35 00 00 2C 50 16 16 04 28

// 07 If
// D4 00 Where if ends
// 82 && Logical and sign
// 99 >= Is greater than or equal sign
// 00 Local variable follows
// DA 35 00 00 iItem
// 2C Integer constant of one byte follows
// 4D Number 77
// 16 End ... first condition has one 16, following conditions have two 16s
// 18 0E 00 ... this is between two conditions, is 18 21 00 in struct kItem.iCategory, so I just used the one from iItem conditionals
// 98 <= Is less than or equal sign
// 00 Local variable follows
// DA 35 00 00 iItem
// 2C Integer constant of one byte follows
// 50 Number 80
// 16 16 End
// 04 Return
// 28 False

Edited by Drakous79
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...