/*
Briefcase Inventory
Logic demo of 2d spatial inventory (vector-based).
I think. might be abusing those words.
Same kind of inventory famously used in Resident Evil 4 and Cabal Online.
The user must manage the inventory slots efficiently by rotating and moving the items.
The inventory is defined as an X-by-Y box with X*Y slots (asserting that X>1&&Y>1).
The user can pick up an item from the case, hold an item above the case, and place the held item down (which would swap it with a single item below it).
There are some assumptions for this script's use case; they are noted in comments throughout the script. (Search "assum")
*/
// rough draft & pseudocode:
/*inv manager re4
basically the item works with dots
on A press:
if each dot this item takes in this rotation == null
place item
else if all dots belong to 1 item
swap item for the 1 item under it
else (dots belong to more than 1 object)
error sound, can't place held item
board []
board[x].itemIndex
if board[ heldItem.dots[xint].x + heldItem.offsetX + ((heldItem.dots[xint].y + heldItem.offsetY) * briefcaseWidth) ].itemIndex == 0
# where itemIndex of 0 signifies a vacant slot in the briefcase
changed my mind; -1 is empty, max is 127 (s8)
*/
// we make the assumption that an item that can be held and attempted to be placed in the briefcase is actually small enough to fit into an empty briefcase (example if our game briefcase is 4x4, we don't have any 5x4 items, which would be too long to fit into the briefcase even when empty)
function item(dots){
this.rotation = 0; // current rotation. 0=upright.1=90*clockwise.2=180*(upside down).3=270*.
this.dots = []; // array of indices from coords (x,y)
this.width = 0;
this.height = 0;
this.stringBlueprint = "";
this.fromString = function(s){
this.stringBlueprint = s;
var d = [];
var line = 0; // which row (y-axis)
var width = s.indexOf('\n'); // we assume the string is properly structured
this.width = width;
var xprog = 0; // which column (x-axis)
if(width == -1) width = s.length;
for(var xint = 0;xint < s.length;xint++){
if(s.charAt(xint) == 'o'){
d.push(xprog + (line * width));
xprog++;
}else if(s.charAt(xint) == '\n'){
line++;
xprog = 0;
}
}
this.height = line + 1;
this.dots = d;
}
this.toString = function(){ // dots to string, really just returned stored string
if(this.rotation == 0) return this.stringBlueprint;
var block = [];
for(var xint = 0, len = this.width * this.height;xint < len;xint++) block[xint] = "x";
for(var xint = 0;xint < this.dots.length;xint++) block[this.dots[xint]] = "o";
var out = "";
for(var yint = 0;yint < this.height;yint++){
for(var xint = 0;xint < this.width;xint++) out += block[xint + (this.width * yint)];
out += "\n";
}
return out;
}
this.rotateClockwise = function(){
this.rotation++;
if(this.rotation >= 4) this.rotation = 0;
var oldWidth = this.width;
var oldHeight = this.height;
var newWidth = oldHeight;
var newHeight = oldWidth;
var res = [];
for(var xint = 0;xint < this.dots.length;xint++){
//console.log("roclo[" + xint + "]: " + this.dots[xint]);
var oldx = this.dots[xint] % oldWidth;
var oldy = Math.floor(this.dots[xint] / oldWidth);
var newy = oldx;
var newx = (newHeight-1) - oldy;
res.push(newx + (newy * newHeight)); // save translated dot as index
//console.log("old x/y ",oldx,",",oldy," -> new x/y ",newx,",",newy,". logged idx: " + res[res.length-1]);
}
this.dots = res;
this.width = newWidth;
this.height = newHeight;
}
this.rotateCounterClockwise = function(){
this.rotation--;
if(this.rotation < 0) this.rotation = 3;
var oldWidth = this.width;
var oldHeight = this.height;
var newWidth = oldHeight;
var newHeight = oldWidth;
var res = [];
for(var xint = 0;xint < this.dots.length;xint++){
console.log("roclo[" + xint + "]: " + this.dots[xint]);
var oldx = this.dots[xint] % oldWidth;
var oldy = Math.floor(this.dots[xint] / oldWidth);
var newx = oldy;
var newy = (newWidth-1) - oldx;
res.push(newx + (newy * newHeight)); // save translated dot as index
//console.log("old x/y ",oldx,",",oldy," -> new x/y ",newx,",",newy,". logged idx: " + res[res.length-1]);
}
this.dots = res;
this.width = newWidth;
this.height = newHeight;
}
if(typeof dots == "string"){ // construct from string
this.fromString(dots);
}
}
function makeBriefcase(width, height){
briefcase = [];
briefcaseWidth = width;
briefcaseHeight = height;
for(var xint = 0;xint < width;xint++){
for(var yint=0;yint<height;yint++) briefcase.push(-1);
}
briefcaseLen = (width * height) - 1; // assumes width & height are not both 1
}
function makeItem(name, dots){ // add an item to the shop
shopNames.push(name);
shop.push(new item(dots));
console.log("\n=====\nshop got new stock: " + name + "\n" + dots + "\n=====");
}
function ctoi(x,y){ // coordinates to index
return x + (y * briefcaseHeight);
}
function cloneItem(original){
var copy = new item();
copy.rotation = original.rotation;
copy.width = original.width;
copy.height = original.height;
copy.dots = [];
for(var xint = 0, len = original.dots.length;xint < len;xint++) copy.dots.push(original.dots[xint]);
copy.stringBlueprint = original.stringBlueprint;
return copy;
// i guess coulda also just used below instead of all above (but above is probably a little faster than additionally parsing the string):
// return new item(original.stringBlueprint);
}
var briefcase = []; // where our owned items are stored. manage this space. array of s8's. represents indices of the inventory.
var briefcaseWidth = 4;
var briefcaseHeight = 4;
var briefcaseLen = 15; // max index + 1
var shop = []; // items to select to add to inv. also acts as a database for item definitions here
var shopNames = []; // item names
var inventory = []; for(var xint=0;xint<8;xint++)inventory[xint]=-1;// a list of our owned items. indices of shop; list of item IDs. -1 = empty inventory slot. needs to be static length for briefcase values.
var holdingItem = false;
var itemInHands = null;
function tryToPlace(heldItem, x, y){
// we assume that the item will not be out of bounds over the briefcase
// for example, an item of width 3 cannot be placed at index 1 in a briefcase of width 3, because the right-most dot would be outside the suitcase, causing an exception here
var matchedAny = false;
var matchedID = -1;
for(var xint = 0, len = heldItem.dots.length;xint < len;xint++){
var slot = briefcase[ctoi(x,y) + heldItem.dots[xint]]; // an underlying slot(dot) in the briefcase
if(slot != -1){ // overlapped non-empty slot
if(matchedAny){
if(matchedID == slot) continue; // it's ok, same item we already matched
else{
// 2nd item that our held item is overlapping
// we can't place the item because we can't be holding both briefcase items - we can only hold 1 item at a time
// can play an ERRRRH sound here :)
return;
}
}else{
// first tim overlapping, note the ID of the item in the briefcase that is overlapping (we can only carry 1 item in our hands)
matchedAny = true;
matchedID = slot;
}
}
}
// check there is space in the inventory to store the item we are holding
// this may be unnecessary, as in most cases there would be a number of inventory slots equal to the 'dots'
// e.g. a 4x4 case has 4*4 slots (16), so 16 1x1 items could be fit into it
// therefore this check is a formality
var intoInvSlot = -1;
if(!matchedAny){ // only need to check if we're not swapping for an item in inv
var found = false;
for(var xint = 0;xint < inventory.length;xint++){
if(inventory[xint] == -1){
found = true;
intoInvSlot = xint;
break;
}
}
if(!found){
// can play error sound here and show message "Max number of items in briefcase reached!"
// This is the case where there is an arbitrary limit of inv items regardless of case slots
// e.g. 4x4 case size (16 slots) but player can only have 3 items in the case at any time, regardless of item size
return;
}
}
// save the inventory item from briefcase in case we need to swap it
var itemFromBriefcase;
if(matchedAny){
// is there space in the inventory to hold the briefcase item?
//itemFromBriefcase = inventory.splice(id,1)[0]; // move item from inventory (briefcase) to temporary spot
itemFromBriefcase = inventory[id];
intoInvSlot = id;
}
// let's now set the held item down
inventory[intoInvSlot] = itemInHands;
// update briefcase dots where the previously held item was set down so that they point to this inventory slot
for(var xint = 0;xint < itemInHands.dots.length;xint++){
// we have to change the dot value: y for the dot (with width of the item) is not necessarily the same as y for the briefcase, due to a difference in width.
// example without change: pistol into 4x4 briefcase. briefcase is then [0,0,0,-1,.....]
// example WITH change: pistol into 4x4 briefcase. briefcase becomes [0,0,-1,-1,0,-1,-1,-1....]
// might help to make illustration
/*
no change, using raw value from heldItem.dots[xint]
ooox
xxxx
(the width of the item is 2)
(the briefcase is 4x2)
See the issue? The item doesn't wrap because the width doesn't match the briefcase
fixed width to match briefcase
ooxx
oxxx
(width of item: 2)
(briefcase is 4x2)
Notice the difference? The handle of the pistol (bottom 'o') is now properly wrapped to the below line like it's supposed to be, because it used the offset of the briefcase's width for each line.
*/
// so here are the
var ix = heldItem.dots[xint] % heldItem.width;
var iy = Math.floor(heldItem.dots[xint] / heldItem.width);
briefcase[ctoi(x,y) + ctoi(ix,iy)] = intoInvSlot; // payload being equivalent to ctoi(x+ix,y+iy)
// briefcase[ctoi(x,y) + heldItem.dots[xint]] = intoInvSlot; // original, erroneous line that didn't respect briefcase width
}
if(matchedAny){
// we have to replace the held item with the item we overlapped
itemInhands = itemFromBriefcase; // now we're holding the item we replaced
}
else holdingItem = false; // we set it down into empty slots. we're not holding anything now. (don't need to nullify itemInHands)
}
function pickUp(x, y){ // not holding item, pick up item in briefcase at coords
var id = briefcase[ctoi(x,y)];
// -1 means empty briefcase slot
if(id >= 0){
holdingItem = true;
itemInHands = shop[inventory[id]];
// remove item from briefcase, we are now holding it
for(var xint = 0;xint < briefcaseLen;xint++){
if(briefcase[xint] == id) briefcase[xint] = -1;
}
}
}
function trashHeldItem(){
holdingItem = false;
}
function buyItem(shopIndex){ // convenient function to copy a shop item into itemInHands
if(holdingItem) trashHeldItem();
itemInHands = cloneItem(shop[shopIndex]);
holdingItem = true;
}
function pressSpace(){ // function that acts as the button to pick up / place items in briefcase.
if(holdingItem) tryToPlace(itemInHands);
else pickUp(x,y);
}
function pressX(){ // used to trash a held item
if(holdingItem) trashHeldItem();
}
makeBriefcase(4,4); // briefcase has 16 slots: width of 4, height of 4
makeItem("pistol", "oo\nox"); // L shape
makeItem("RPG", "ooooo\nxoxxx"); // vertical bar with a handle on 2nd column of 2nd row
shop[0].toString();
shop[0].rotateClockwise();
shop[0].rotateClockwise();
shop[0].rotateCounterClockwise();
console.log("now 90* clockwise");
shop[0].toString();
itemInHands = cloneItem(shop[0]);
console.log("in hands: ");
itemInHands.toString();
console.log('placing... @ 0,0 in briefcase');
tryToPlace(cloneItem(itemInHands), 0,0);
console.log("Briefcase now: ", briefcase);
Friday, April 7, 2023
Coding Challenge #44 2-Dimensional Inventory
Subscribe to:
Post Comments (Atom)
Coding Challenge #54 C++ int to std::string (no stringstream or to_string())
Gets a string from an integer (ejemplo gratis: 123 -> "123") Wanted to come up with my own function for this like 10 years ago ...
-
I NEEDS MY JAVASCRIPT PLEASE :) Open txtcode .txt Save as .gct Please click the blue cheat code title to activate it, a...
-
Naruto Personality Quiz Please enable javascript ☯ Which Naruto Character Are You?
-
Useful for extensions that want to prevent event listeners from being attached or modify prototypes or functions, generally to defeat click...
No comments:
Post a Comment