瀏覽代碼

[SugarCube] adds filter

Stephan Fuchs 2 月之前
父節點
當前提交
cb6ffbcd5c

+ 1 - 1
locations/_attributes_gm_panties.qsrc

@@ -1,5 +1,5 @@
 # $attributes_gm_panties
-!! QSRC2TW_module wardrobeItems {"shop":"gm"}
+!! QSRC2TW_module wardrobeItems {"shop":"gm","slots":["panties"]}
 
 if ARGS[1] = 1:
 	PanType = 1

File diff suppressed because it is too large
+ 0 - 0
qsrc2tw/tools/QSRC2TW/task_processor.js


File diff suppressed because it is too large
+ 0 - 0
qsrc2tw/twine-code/-generatedFromTS/typescript-min.js


File diff suppressed because it is too large
+ 0 - 0
qsrc2tw/twine-code/-generatedFromTW/typescript-min.js


+ 97 - 0
qsrc2tw/twine-code/core/common/Filter.ts

@@ -0,0 +1,97 @@
+type FilterData = {[fieldId:string]:number|string|boolean|FilterObj}
+
+type FilterObj = {
+	includesAny?: (number|string)[];
+	max?: number;
+	min?: number;
+};
+
+class Filter extends GameObject{
+	private _filterData = [];
+	public get filterData():FilterData{
+		return Object.fromEntries(this._filterData);
+	}
+	private set filterData(v:FilterData){
+		function easeOfChecking(val:any):number{
+			switch(typeof val){
+				case 'boolean': return 0;
+				case 'number': return 1;
+				case 'string': return 3;
+				case 'object': return 10;
+				default:
+					return 100;
+			}
+		}
+		this._filterData = Object.entries(v).toSorted((a,b)=>easeOfChecking(a) - easeOfChecking(b));
+	}
+	constructor(filterData: FilterData={}){
+        super();
+		this.filterData = filterData;
+	}
+
+	//#region Filter Data Manipulation
+		public empty(){
+			this.filterData = {};
+		}
+
+		public update(update:FilterData){
+			const newFilterData = Object.assign({},this.filterData,update);
+			this.filterData = newFilterData;
+		}
+	//#endregion
+
+	//#region Passing
+		public passes(val:any){
+			if(typeof val != "object")
+				return false;
+			for(const [fieldId, checkValue] of this._filterData){
+				const valueToCheckAgainst = val[fieldId];
+				if(valueToCheckAgainst === undefined)
+					return false;
+				switch(typeof checkValue){
+					case "string":
+					case "number":
+					case "boolean":
+						if(valueToCheckAgainst !== checkValue)
+							return false;
+						break;
+					case "object":
+						if(!this.passesObj(valueToCheckAgainst, checkValue))
+							return false;
+				}
+			}
+			return true;
+		}
+
+		private passesObj(checkedValue:any, filterObj:FilterObj):boolean{
+			if(filterObj.includesAny){
+				if(Array.isArray(checkedValue))
+					return checkedValue.some(r=> filterObj.includesAny.includes(r));
+				return filterObj.includesAny.includes(checkedValue);
+			}
+
+			if(typeof filterObj.max == "number"){
+				if(typeof checkedValue == "number" && checkedValue <= filterObj.max)
+					return true;
+				return false;
+			}
+			if(typeof filterObj.min == "number"){
+				if(typeof checkedValue == "number" && checkedValue >= filterObj.min)
+					return true;
+				return false;
+			}
+		}
+	//#endregion
+
+    //#region SYSTEM
+		clone = function () {
+			return (new setup.Filter())._init(this.clonableFields);
+		};
+
+		toJSON = function () {
+			return JSON.reviveWrapper('(new setup.Filter())._init($ReviveData$)', this.ownData);
+		};
+	//#endregion
+}
+
+setup.Filter = Filter;

+ 125 - 2
qsrc2tw/twine-code/core/wardrobe/OutfitItem.ts

@@ -21,20 +21,143 @@ class OutfitItem extends GameObject{
 		const imageFromStaticData = this.staticData.image as string;
 		if(imageFromStaticData)
 			return imageFromStaticData;
-		const shopData = setup.outfitShop[this.staticData.shop] ?? {};
+		const shopData = setup.outfitShop[this.shop] ?? {};
 		if(shopData.outfitItems?.image)
 			return shopData.outfitItems.image(this.id);
 		return undefined;
 	}
 
+	get label():string{
+		return (this.staticData.label as string) ?? this._id;
+	}
+
+	get quality():number{
+		return this.staticData['Quality'] as number;
+	}
+
+	get shop():string{
+		return this.staticData['shop'] as string;
+	}
+
 	get slots():string[]{
-		return ['clothes'];
+		const type = this.type;
+		switch(type){
+			case 'bra':
+			case 'coat':
+			case 'panties':
+			case 'purse':
+			case 'shoes':
+				return [type];
+			case 'clothes':
+				const s = ['clothes'];
+				if(this.includesBra)
+					s.push('bra');
+				if(this.includesPanties)
+					s.push('panties');
+				return s;
+		}
+		return [];
 	}
 
 	get type(){
 		return this.staticData['type'];
 	}
 
+	//#region Traits
+		//#region Clothes
+			get includesBra():boolean{
+				return this.staticData['Bra'] == 1;
+			}
+
+			get includesPanties():boolean{
+				return !(!this.staticData['Panties']);
+			}
+
+			get inhibition(){
+				return this.staticData['Inhibit'] as number ?? 0;
+			}
+
+			get isBimbo(){
+				return !(!this.staticData['Bimbo']);
+			}
+
+			get isDress(){
+				return !(!this.staticData['Dress']);
+			}
+
+			get isOnePiece():boolean{
+				return !(!this.staticData['OnePiece']);
+			}
+
+			get isTopless(){
+				return this.staticData['Bra'] == 2;
+			}
+
+			get pantsShortness():number{
+				return this.staticData['PantsShortness'] as number ?? 0;
+			}
+
+			get skirtShortness():number{
+				return this.staticData['SkirtShortness'] as number ?? 0;
+			}
+
+			get style1(){
+				return this.staticData['Style'] as number ?? 0;
+			}
+
+			get style2(){
+				return this.staticData['Style2'] as number ?? 0;
+			}
+
+			get style3(){
+				return this.staticData['Style3'] as number ?? 0;
+			}
+
+			get thinness():number{
+				return this.staticData['Thinness'] as number ?? 0;
+			}
+
+			get thinnessExposure():number{
+				switch(this.thinness){
+					case 1:	return 150;
+					case 2:	return 200;
+					case 3:	return 250;
+					case 4:	return 300;
+					case 5:	return 350;
+					case 6:	return 400;
+					case 0:
+					default:
+						return 0;
+				}
+			}
+
+			get topCut():number{
+				return this.staticData['TopCut'] as number ?? 0;
+			}
+
+			get topExposure():number{
+				if(this.includesBra)
+					return 400;
+				if(this.isTopless)
+					return 500;
+				switch(this.topCut){
+					case 0:	return 0;
+					case 1:	return 100;
+					case 2:	return 150;
+					case 3:	return 200;
+					case 4:	return 250;
+					case 5:	return 300;
+					case 6:	return 350;
+					case 7:
+					default:
+						return 400;
+
+				}
+			}
+		//#endregion
+
+	//#endregion
+
 	//#region SYSTEM
 		constructor(id:string=undefined){
 			super();

+ 21 - 4
qsrc2tw/twine-code/core/wardrobe/OutfitShops.ts

@@ -1,8 +1,12 @@
 let defaultOutfitImageFunction = (id:string) =>{
 	const idParts = id.split('_');
-	const shop = idParts[0];
-	const type = idParts[1];
-	const index= idParts[2];
+	let shop = idParts[0];
+	let type = idParts[1];
+	let index= idParts[2];
+
+	switch(type){
+		case 'bra':		type = 'bras'; break;
+	}
 
 	return `pc/items/${shop}/${type}/${index}.jpg`;
 };
@@ -25,7 +29,20 @@ setup.outfitShop['allure'] = {
 	}
 };
 
-for(const shopId of ['danilovich','gm']){
+setup.outfitShop['gm'] = {
+	outfitItems:{
+		image: (id:string)=>{
+			const idParts = id.split('_');
+			if(idParts[0] == "school")
+				return `pc/items/gm/school/${idParts[1]}.jpg`;
+			else
+				return defaultOutfitImageFunction(id);
+
+		}
+	}
+};
+
+for(const shopId of ['cats','danilovich']){
 	setup.outfitShop[shopId] = {
 		outfitItems:{
 			image: defaultOutfitImageFunction

+ 289 - 0
qsrc2tw/twine-code/core/wardrobe/Overwrites.ts

@@ -0,0 +1,289 @@
+// This code ensures that outfititems will be added to the wardrobe and removed from there.
+for(const[type, variableArray] of Object.entries(setup.wardrobeItemVars)){
+    for(const variable of variableArray){
+        let outfitIdVariable = variable;
+        if(outfitIdVariable.endsWith('shoe'))
+            outfitIdVariable += 's';
+        else if(outfitIdVariable.endsWith('bras'))
+            outfitIdVariable = variable.slice(0,-1);
+        else if(outfitIdVariable == "gm_school")
+            outfitIdVariable = 'school';
+        setup.Overwrite.varRegister(
+            variable,
+            (index:number)=>State.variables.wardrobe.ownsItem(`${outfitIdVariable}_${index}`) ? 1 : 0,
+            (index:number, v: number)=>{
+                const itemId = `${outfitIdVariable}_${index}`;
+                if(v)
+                    State.variables.wardrobe.add(itemId);
+                else
+                    State.variables.wardrobe.delete(itemId);
+            },
+        );
+    }
+}
+
+for(const passage of ['bras','coats','clothing','panties','shoes']){
+    setup.Overwrite.gsRegister(passage,(args)=>{
+        if(args[0] == 'wear'){
+            const type:string = args[1];
+            const index:number= args[2];
+            const outfitItemId = `${type}_${index}`;
+            try{
+                variables().wardrobe.wear(outfitItemId);
+            }
+            catch(e){
+                console.error(e);
+            }
+
+            return 'empty';
+        }
+        return undefined;
+    })
+}
+
+// All wardrobes will be ignore except for opening it.
+setup.Overwrite.gsRegister('wardrobe',(args)=>{
+    if(!args[0] || args[0] == 'start')
+        return undefined;
+    return 'empty';
+})
+
+
+// Clothing overwrites
+
+setup.Overwrite.varRegister('$clothingworntype',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return '';
+        return (item.id.split('_').slice(0,-1).join('_'));
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('clothingwornnumber',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return (Number(item.id.split('_').last()));
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloQuality',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.quality;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloThinness',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.thinness;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PXCloThinness',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.thinnessExposure;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloTopCut',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.topCut;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloBra',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.includesBra ? 1 : 0;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloOnePiece',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.isOnePiece ? 1 : 0;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloPants',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.pantsShortness;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloSkirt',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.skirtShortness;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloPanties',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.includesPanties ? 1 : 0;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloDress',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.isDress ? 1 : 0;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloStyle',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.style1;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloStyle2',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.style2;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloStyle3',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.style3;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloInhibit',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.inhibition;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PCloBimbo',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.isBimbo ? 1 : 0;
+    },
+    undefined
+);
+
+setup.Overwrite.varRegister('PXCloTopCut',
+    ()=>{
+        const item = variables().wardrobe.wornAtSlot('clothes');
+        if(!item)
+            return 0;
+        return item.topExposure;
+    },
+    undefined
+);
+
+/*
+	<<run setup.qsp_dynamic(QSP.$ARGS[1] + '_w['+(QSP.ARGS[2])+'] = 1')>>
+	<<gs `['clothing_attributes' , QSP.$ARGS[1] , QSP.ARGS[2]]`>>
+	<<run setup.qsp_dynamic(''+(QSP.$ARGS[1])+'_s['+(QSP.ARGS[2])+'] = 0')>>
+
+	<<set QSP.PCloGoth[0] = QSP.CloGoth[0]>>
+	<<set QSP.PCloPunk[0] = QSP.CloPunk[0]>>
+	<<set QSP.PCloCoverTop[0] = QSP.CloCoverTop[0]>>
+	<<set QSP.PCloCoverBack[0] = QSP.CloCoverBack[0]>>
+	<<set QSP.PCloCoverFront[0] = QSP.CloCoverFront[0]>>
+	<<gs `['outfit' , 'set_tags']`>>
+	<<if setup.qsp_strpos(QSP.$clothingworntype[0] , 'swimsuit') > 0 || setup.qsp_strpos(QSP.$clothingworntype[0] , 'bikinis') > 0>><<set QSP.PCloSwimwear[0] = 1>><</if>>
+	<<gs `['outfit' , 'exposure_calc']`>>
+	<!-- !Calculationsforclothingbeauty,multipliedbynaturalbeauty -->
+
+
+	<<if QSP.PCloSkirt[0] > 0>>
+		<<if QSP.PCloSkirt[0] == 1>>
+			<<set QSP.PXCloBottomShortness[0] = 100>>
+		<<elseif (QSP.PCloSkirt[0] == 2) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 150>>
+		<<elseif (QSP.PCloSkirt[0] == 3) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 200>>
+		<<elseif (QSP.PCloSkirt[0] == 4) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 250>>
+		<<elseif (QSP.PCloSkirt[0] == 5) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 300>>
+		<<elseif (QSP.PCloSkirt[0] == 6) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 350>>
+		<<elseif (QSP.PCloSkirt[0] >= 7) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 400>>
+		<</if>>
+
+	<</if>>
+
+	<<if QSP.PCloPants[0] > 0>>
+		<<if QSP.PCloPants[0] == 1>>
+			<<set QSP.PXCloBottomShortness[0] = 100>>
+		<<elseif (QSP.PCloPants[0] == 2) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 150>>
+		<<elseif (QSP.PCloPants[0] == 3) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 200>>
+		<<elseif (QSP.PCloPants[0] == 4) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 250>>
+		<<elseif (QSP.PCloPants[0] == 5) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 300>>
+		<<elseif (QSP.PCloPants[0] == 6) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 350>>
+		<<elseif (QSP.PCloPants[0] >= 7) != 0>>
+			<<set QSP.PXCloBottomShortness[0] = 400>>
+		<</if>>
+
+	<</if>>
+
+	<<if QSP.PCloPanties[0] == 1>><<set QSP.PXCloBottomShortness[0] = 400>><</if>>
+    */

+ 18 - 0
qsrc2tw/twine-code/core/wardrobe/OwnedOutfitItem.ts

@@ -1,5 +1,23 @@
 class OwnedOutfitItem extends OutfitItem{
 
+	private _condition:number = -1;	//-1: Always perfect
+	get condition(){return this._condition;}
+	set condition(v){this._condition = v;}
+	get conditionEffective(){
+		if(this._condition == -1)
+			return 100;
+		return Math.clamp(this._condition,0,100);
+	}
+
+	private _size:number = -1;		//-1: Always fits
+	get size(){return this._size;}
+	set size(v){this._size = v;}
+
+
+	show(dom: HTMLElement|JQuery<HTMLElement>){
+		State.temporary.tempOutfitItem = this;
+		$(dom).wiki('<<OwnedOutfitItem _tempOutfitItem>>');
+	}
 
     //#region SYSTEM
         constructor(id:string=undefined){

+ 15 - 46
qsrc2tw/twine-code/core/wardrobe/Wardrobe.ts

@@ -9,53 +9,7 @@ function outfitItemId(item:ItemReference){
 	throw new Error("Invalid Argument");
 }
 
-type FilterData = {[fieldId:string]:number|string|boolean|{
-	max?: number;
-	min?: number;
-}}
-
-class Filter{
-	private _filterData = [];
-	public get filterData():FilterData{
-		return Object.fromEntries(this._filterData);
-	}
-	private set filterData(v:FilterData){
-		function easeOfChecking(val:any):number{
-			switch(typeof val){
-				case 'boolean': return 0;
-				case 'number': return 1;
-				case 'string': return 3;
-				case 'object': return 10;
-				default:
-					return 100;
-			}
-		}
-		this._filterData = Object.entries(v).toSorted((a,b)=>easeOfChecking(a) - easeOfChecking(b));
-	}
-	constructor(filterData: FilterData){
-		this.filterData = filterData;
-	}
 
-	public passes(val:any){
-		if(typeof val != "object")
-			return false;
-		for(const [fieldId, checkValue] of this._filterData){
-			if(val[fieldId] === undefined)
-				return false;
-			switch(typeof checkValue){
-				case "string":
-				case "number":
-				case "boolean":
-					if(val[fieldId] !== checkValue)
-						return false;
-					break;
-				case "object":
-					throw new Error("NOT IMPLEMENTED");
-			}
-		}
-		return true;
-	}
-}
 
 class Wardrobe extends GameObject{
 
@@ -214,6 +168,11 @@ class Wardrobe extends GameObject{
 			console.log("Wardrobe Deleted",itemId);
 		}
 
+		get(item: ItemReference){
+			const itemId = outfitItemId(item);
+			return this._ownedItems[itemId];
+		}
+
 		public ownsItem(item: ItemReference){
 			const itemId = outfitItemId(item);
 			return Object.keys(this._ownedItems).includes(itemId);
@@ -234,6 +193,14 @@ class Wardrobe extends GameObject{
 			);
 		}
 
+		public isWorn(item: ItemReference){
+			const itemId = outfitItemId(item);
+			return Object.values(this._wornItemIds)
+						.some(
+							(wornItemId) => wornItemId == itemId
+						);
+		}
+
 		public wornAtSlot(slotId:string):OutfitItem|null{
 			const itemId = this._wornItemIds[slotId];
 			if(!itemId)
@@ -257,7 +224,9 @@ class Wardrobe extends GameObject{
 				const itemAtSlot = this.wornAtSlot(slot);
 				if(itemAtSlot)
 					this.unwear(itemAtSlot);
+				this._wornItemIds[slot] = itemId;
 			}
+
 		}
 	//#endregion
 

+ 13 - 0
qsrc2tw/twine-code/core/wardrobe/ui/OwnedOutfitItem.tw

@@ -0,0 +1,13 @@
+:: OwnedOutfitItem[widget]
+<<widget 'OwnedOutfitItem'>>
+	<<set _outfitItem = _args[0]>>
+	<<image _outfitItem.image>>
+	<h3>_outfitItem.label</h3>
+	<div class="detailsTable">
+		<<if _outfitItem.condition >= 0>>
+			<label>Condition:</label><<meter _outfitItem.condition 0 100>>
+		<</if>>
+		<label>Quality:</label><<meter _outfitItem.quality 0 7>>
+		<!--<label>Revealing:</label><<meter 20 0 100 `{colors:[{start:0,color:"grey"},{start:0.5,color:"indianred"},{start:1,color:"darkred"}]}`>>-->
+	</div>
+<</widget>>

File diff suppressed because it is too large
+ 28 - 0
qsrc2tw/twine-code/core/wardrobe/ui/wardrobe.css


+ 37 - 0
qsrc2tw/twine-code/core/wardrobe/ui/wardrobe.tw

@@ -0,0 +1,37 @@
+:: wardrobe
+
+<<set _filter = new setup.Filter()>>
+
+<div id="wearing">
+	<<wardrobeWorn>>
+</div>
+
+<div id="owned">
+
+	<div id="filter">
+		<<wardrobeFilter>>
+	</div>
+	<div class="itemsOverview">
+		<<for _itemId, _item range $wardrobe.ownedItems>>
+				<div @id="'owned_'+_itemId" class="item filterMatch" @data-itemid="_itemId">
+					<<image _item.image>>
+				</div>
+		<</for>>
+	</div>
+</div>
+
+<div id="details">
+	<<wardrobeDetails>>
+</div>
+
+<<id 'closeWardrobeButton'>>
+	<<class 'action'>>
+		<<link 'Close Wardrobe'>>
+			<<gt `QSP.$loc[0]` `QSP.$loc_arg[0]`>>
+		<</link>>
+	<</class>>
+<</id>>
+
+<<done>>
+	<<run setup.wardrobeDisplay_setup()>>
+<</done>>

+ 23 - 0
qsrc2tw/twine-code/core/wardrobe/ui/wardrobeDetails.tw

@@ -0,0 +1,23 @@
+:: wardrobeDetails[widget]
+<<widget 'wardrobeDetails'>>
+	<<set _itemId = _args[0]>>
+
+	<<if _itemId && $wardrobe.ownsItem(_itemId)>>
+		<<set _item = $wardrobe.get(_itemId)>>
+		<h2>Details</h2>
+
+		<div id="detailOperations">
+			<<if !$wardrobe.isWorn(_item)>>
+				<<button 'Wear'>>
+					<<run $wardrobe.wear(_item)>>
+					<<run setup.wardrobeDisplay_showItemDetails(_itemId)>>
+					<<run setup.wardrobeDisplay_updateWorn()>>
+				<</button>>
+			<</if>>
+		</div>
+
+		<div id="detailsBody">
+			<<OwnedOutfitItem _item>>
+		</div>
+	<</if>>
+<</widget>>

+ 21 - 0
qsrc2tw/twine-code/core/wardrobe/ui/wardrobeFilter.tw

@@ -0,0 +1,21 @@
+:: wardrobeFilter[widget]
+<<widget 'wardrobeFilter'>>
+    <<set _wardrobeFilter = _args[0] ?? _filter>>
+    <<for _index, _val range _wardrobeFilter.filterData>>
+        <<capture _index>>
+            <<set _label = _index.toLocaleUpperFirst() + ': '>>
+            <<if typeof _val == "object">>
+                <<if _val.includesAny>>
+                    <<set _label += _val.includesAny.join("|")>>
+                <</if>>
+            <<else>>
+                <<set _label += _val>>
+            <</if>>
+            <<button _label>>
+                <<set _newFilterData = _filter.filterData>>
+                <<unset _newFilterData[_index]>>
+                <<run setup.wardrobeDisplay_setFilter(_newFilterData)>>
+            <</button>>
+        <</capture>>
+    <</for>>
+<</widget>>

+ 15 - 0
qsrc2tw/twine-code/core/wardrobe/ui/wardrobeWorn.tw

@@ -0,0 +1,15 @@
+:: wardrobeWorn[widget]
+<<widget 'wardrobeWorn'>>
+	<<for _slot range ['coat','bra','panties','socks','shoes','hat','clothes','gloves','jewelry','purse']>>
+		<<set _item = $wardrobe.wornAtSlot(_slot)>>
+		<<if _item>>
+			<div @id="_slot" class="slot item" @data-itemid="_item.id" @data-slot="_slot">
+
+				<<image _item.image>>
+			</div>
+		<<else>>
+			<div @id="_slot" class="slot" @data-slot="_slot"></div>
+		<</if>>
+
+	<</for>>
+<</widget>>

+ 84 - 0
qsrc2tw/twine-code/core/wardrobe/ui/wardrobe_display.ts

@@ -0,0 +1,84 @@
+function wardrobeAttachOutfitEvents(selector: string){
+	$(selector)
+		.on("click", function(){
+			const itemId = $(this).data("itemid");
+			State.temporary.presentedItemId = itemId;
+		})
+		.on("mouseover", function(){
+			const itemId = $(this).data("itemid");
+			setup.wardrobeDisplay_showItemDetails(itemId);
+		})
+		.on("mouseout", function(){
+			const presentedItemId = State.temporary.presentedItemId;
+			if(presentedItemId)
+				setup.wardrobeDisplay_showItemDetails(presentedItemId);
+		});
+}
+
+setup.wardrobeDisplay_setup = function(){
+	setup.wardrobeDisplay_updateAvailable();
+	setup.wardrobeDisplay_updateWorn();
+}
+
+setup.wardrobeDisplay_setFilter = function(update:FilterData){
+	const filter = State.temporary.filter;
+
+	if(!update)
+		filter.empty();
+	else
+		filter.update(update);
+
+	for(const [itemId,item] of Object.entries(State.variables.wardrobe.ownedItems)){
+		if(filter.passes(item))
+			$(`#owned_${itemId}`).addClass('filterMatch');
+		else
+			$(`#owned_${itemId}`).removeClass('filterMatch');
+	}
+
+	const filterDomId = 'filter';
+	const filterDom = $(`#${filterDomId}`);
+	filterDom.empty();
+	filterDom.wiki('<<wardrobeFilter _filter>>');
+
+}
+
+setup.wardrobeDisplay_showItemDetails = function(itemId:string){
+	const detailsContainer = $("#details");
+	detailsContainer.empty();
+	detailsContainer.wiki(`<<wardrobeDetails '${itemId}'>>`);
+}
+
+setup.wardrobeDisplay_updateAvailable = function(){
+	wardrobeAttachOutfitEvents(".item[data-itemid]");
+
+	$(".item[data-itemid]").on('click', function(){
+		if($(this).hasClass("selected")){
+			const itemId = $(this).data("itemid");
+			State.variables.wardrobe.wear(itemId);
+			setup.wardrobeDisplay_showItemDetails(itemId);
+			setup.wardrobeDisplay_updateWorn();
+		}else{
+			$(".item.selected").removeClass("selected");
+			$(this).addClass("selected");
+		}
+	})
+}
+
+
+setup.wardrobeDisplay_updateWorn = function(){
+	const wornContainerId = "#wearing";
+	const wornItemsSelector = `${wornContainerId} .item[data-itemid]`;
+	const wornSlotsSelector = `${wornContainerId} .slot[data-slot]`;
+
+	const wornContainer = $(wornContainerId);
+	wornContainer.empty();
+	wornContainer.wiki(`<<wardrobeWorn>>`);
+
+	wardrobeAttachOutfitEvents(wornItemsSelector);
+
+	$(wornSlotsSelector).on("click", function(){
+		const slot:string = $(this).data("slot");
+		setup.wardrobeDisplay_setFilter({slots:{includesAny:[slot]}});
+
+	});
+}

+ 0 - 0
qsrc2tw/twine-code/core/wardrobe/wardrobe.md


+ 28 - 0
qsrc2tw/twine-code/core/wardrobe/wearOverwrites.ts

@@ -0,0 +1,28 @@
+/*
+let purseNumberAssigment:number;
+let purseShopAssigment:string;
+
+setup.Overwrite.varRegister('currentpursenumber',
+    ()=>Number(State.variables.wardrobe.wornAtSlot('purse').id.split('_').last()),
+    (index,val:number)=>{
+        purseNumberAssigment = val;
+        if(purseShopAssigment && typeof purseNumberAssigment == "number"){
+            State.variables.wardrobe.wear(`${purseShopAssigment}_purse_${purseNumberAssigment}`);
+            purseNumberAssigment = undefined;
+            purseShopAssigment = undefined;
+            console.warn('Purse Assigned');
+        }
+    });
+
+setup.Overwrite.varRegister('currentpursetype',
+    ()=>Number(State.variables.wardrobe.wornAtSlot('purse').id.split('_').first()),
+    (index,val:string)=>{
+        purseShopAssigment = val;
+        if(purseShopAssigment && typeof purseNumberAssigment == "number"){
+            State.variables.wardrobe.wear(`${purseShopAssigment}_purse_${purseNumberAssigment}`);
+            purseNumberAssigment = undefined;
+            purseShopAssigment = undefined;
+            console.warn('Purse Assigned');
+        }
+    });
+    */

+ 25 - 3
qsrc2tw/twine-code/editor/Editor.css

@@ -28,7 +28,8 @@
 }
 
 
-.collapsable>h2{
+.collapsable>h2,
+.collapsableV>h2{
 	cursor: pointer;
 }
 
@@ -38,13 +39,23 @@
 	min-width: 6vw;
 }
 
-.collapsable{
+.collapsable,
+.collapsableV{
 	--transitionSpeed: 0.3s;
 	transition: var(--transitionSpeed);
+	overflow: auto;
+}
+
+.collapsable {
 	width: 30vw;
 	width: calc(30vw - 1em);
 	padding: 0 1em;
-	overflow: auto;
+
+}
+
+.collapsableV {
+	--expandedHeight:30vh;
+	height: var(--expandedHeight);
 }
 
 .collapsable.collapsed{
@@ -52,6 +63,11 @@
 	padding: 0;
 }
 
+.collapsableV.collapsed {
+	height: 1.5em;
+	overflow: clip;
+}
+
 .collapsable.collapsed>*{
 	display: none;
 }
@@ -64,6 +80,12 @@
 	display: block;
 }
 
+.collapsableV.collapsed>h2:first-of-type {
+	font-size: 1em;
+	transition: var(--transitionSpeed);
+	display: block;
+}
+
 .diff{
 	font-size: 0.7em;
 }

+ 15 - 1
qsrc2tw/twine-code/twine-code.d.ts

@@ -2,6 +2,8 @@ declare module "twine-sugarcube" {
     export interface SugarCubeSetupObject {
         Example: { new(): Example };
 
+        Filter: { new(): Filter };
+
         //#region Player Character
             attributes: string[];
             proficiencies: {[proficiencyId:string]: ProficiencyDefinition};
@@ -25,13 +27,21 @@ declare module "twine-sugarcube" {
             }};
 
             Wardrobe: {new(): Wardrobe};
-            wardrobeStatic: {[wardrobeItemId:string]: {[propertyKey:string]:(string|number)}};
+            wardrobeStatic: {[wardrobeItemId:string]: {[propertyKey:string]:(string|number|string[])}};
         //#endregion
 
         //#region Wardrobe Overwrites
             wardrobeItemVars: {[type:string]:string[]};
         //#endregion
 
+        //#region Wardrobe Display
+            wardrobeDisplay_setup:()=>void;
+            wardrobeDisplay_setFilter:(filter:FilterData)=>void;
+            wardrobeDisplay_showItemDetails:(itemId:string)=>void;
+            wardrobeDisplay_updateAvailable:()=>void;
+            wardrobeDisplay_updateWorn:()=>void;
+        //#endregion
+
         /**
          * Provides functions to overwrite behavior of functions and variables
          */
@@ -107,6 +117,10 @@ declare module "twine-sugarcube" {
 
         wardrobe: Wardrobe;
     }
+
+    export interface SugarCubeTemporaryVariables{
+        filter: Filter;
+    }
 }
 
 declare global {

Some files were not shown because too many files changed in this diff