view items.pas @ 0:62966dd383ed default tip

initial
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Sun, 08 Dec 2013 19:39:15 +0100
parents
children
line wrap: on
line source

{ Items handling routines. }

UNIT Items;

INTERFACE

USES Global;

FUNCTION  GetItemWeight(what: ItemSlot): INTEGER;       { Counts the weight of an item. }
FUNCTION  IsInThere(what,where: ItemSlot):BOOLEAN;      { Checks if item WHAT is somwhere inside item WHERE. }
PROCEDURE GetItemLooks(VAR a:BYTE;VAR c:CHAR;what:ItemSlot);    { Sets A and C to fit items looks. }
FUNCTION  GetItemDesc(what: ItemSlot): STRING;                  { Returns item description. }
FUNCTION  ItemName(what: ItemSlot): STRING;                     { Returns the name of an item. }
PROCEDURE ShowItem(what: ItemSlot);             { Tells what kind of item is there. }

FUNCTION  GenerateItem(id:BYTE):pItem;          { Creates a new item from definitions. }
PROCEDURE DestroyItem(what: ItemSlot);          { Destroys an item. }
PROCEDURE MoveInto(what,where: ItemSlot);       { Tries to put item from WHAT into container in slot WHERE. }
PROCEDURE MoveItem(what,where: ItemSlot);       { Tries to put item from WHAT into slot WHERE. }
PROCEDURE DropItem(what,where: ItemSlot);       { Drops item from WHAT into slot WHERE, creating a pile if needed. }
PROCEDURE PutToPack(what: ItemSlot);            { Tries to put an item into pack. }

FUNCTION  ListItems(where: ItemSlot):ItemSlot;  { Lists all items in the container and picks one. }
PROCEDURE SelectItem(what: ItemSlot);           { Shows item description,asks for action and performs it. }
PROCEDURE CheckPile(where: ItemSlot);           { Destroys a pile if itsn't needed anymore. }
PROCEDURE ExamInv;                              { Displays equipment. }

IMPLEMENTATION

USES Console;

TYPE    pStack=^StackRec;
        StackRec=RECORD
                slt: pItem;
                next: pStack;
        END;

VAR     stack: pStack;

{ ------------------------------------------- Item information ---------- }

PROCEDURE StackPush(what: pItem);       { Pushes an item entry on stack. }
VAR     tmp: pStack;
BEGIN
        New(tmp);
        tmp^.slt:=what;
        tmp^.next:=stack;
        stack:=tmp;
END;

PROCEDURE StackPop(VAR what: pItem);    { Pops an item entry from stack. }
VAR     tmp: pStack;
BEGIN
        IF stack=NIL THEN Exit;
        what:=stack^.slt;
        tmp:=stack;
        stack:=stack^.next;
        Dispose(tmp);
END;


FUNCTION GetItemWeight(what: ItemSlot): INTEGER;        { Counts the weight of an item. }
VAR     weight: INTEGER;
        p: pItem;
BEGIN
        stack:=NIL;                                     { The stack is empty. }
        weight:=what^^.def^.wgt*what^^.qnt;             { Base weight. }
        StackPush(what^^.con);                          { Now we search all the tree. }
        REPEAT
                StackPop(p);
                WHILE p<>NIL DO BEGIN
                        Inc(weight,p^.def^.wgt*p^.qnt);
                        IF p^.con<>NIL THEN StackPush(p^.con);
                        p:=p^.next;
                END;
        UNTIL p=NIL;
        GetItemWeight:=weight;
END;

FUNCTION IsInThere(what,where: ItemSlot):BOOLEAN;       { Checks if item WHAT is somwhere inside item WHERE. }
VAR     p: pItem;
BEGIN
        IsInThere:=FALSE;
        stack:=NIL;
        StackPush(where^);
        REPEAT
                StackPop(p);
                WHILE p<>NIL DO BEGIN
                        IF p=what^ THEN IsInThere:=TRUE;
                        IF p^.con<>NIL THEN StackPush(p^.con);
                        p:=p^.next;
                END;
        UNTIL p=NIL;
END;

PROCEDURE GetItemLooks(VAR a:BYTE;VAR c:CHAR;what:ItemSlot);    { Sets A and C to fit items looks. }
VAR     tmp: ItemDef;
BEGIN
        IF (SLOT_KNOWN IN what^^.flg) THEN tmp:=what^^.def
                                      ELSE tmp:=FindItem(what^^.def^.unk);
        c:=tmp^.chr;
        a:=tmp^.atr;
END;

FUNCTION GetItemDesc(what: ItemSlot): STRING;                   { Returns item description. }
BEGIN
        IF (SLOT_KNOWN IN what^^.flg)OR(what^^.def^.unk=0) THEN { If item is known -- we return its description. }
                GetItemDesc:=what^^.def^.dsc
        ELSE    GetItemDesc:=FindItem(what^^.def^.unk)^.dsc;    { But if not, we return description of item type. }
END;

FUNCTION ItemName(what: ItemSlot): STRING;                      { Returns the name of an item. }
VAR     s: STRING;
BEGIN
        ItemName:='';
        IF (what^=NIL) THEN BEGIN                               { If there is nothing, we return 'nothing'. }
                ItemName:=FindItem(0)^.nam;
                Exit;
        END;

        IF (SLOT_KNOWN IN what^^.flg) THEN s:=what^^.def^.nam   { If item is known, we return its real name. }
                                      ELSE s:=FindItem(what^^.def^.unk)^.nam;

                              { When there are many items of the same kind, we tell how many. }
        IF (ITEM_MULTIPLE IN what^^.def^.flg) AND (what^^.qnt>1) THEN
                s:=Replace(Plural(s),'@a',Num(what^^.qnt))
        ELSE BEGIN                                              { Elsewhen we add 'a' or 'an'. }
                s:=Replace(s,'@a a','an a');
                s:=Replace(s,'@a e','an e');
                s:=Replace(s,'@a i','an i');
                s:=Replace(s,'@a o','an o');
                s:=Replace(s,'@a u','an u');
                s:=Replace(s,'@a ' ,'a ');
                s:=Replace(s,'@a'  ,'a');
                s:=Replace(s,'@s','');
        END;
        ItemName:=s;
END;

PROCEDURE ShowItem(what: ItemSlot);             { Tells what kind of item is there. }
BEGIN
        IF what^=NIL THEN Exit;                 { If there is nothing -- we won't bother. }
        Msg(Replace(msgs[13],'@v',ItemName(what)));
END;

{ ------------------------------------------- Item moving and creation -- }

FUNCTION GenerateItem(id:BYTE):pItem;           { Creates a new item from definitions. }
VAR     item: ItemDef;
        tmp: pItem;
BEGIN
        New(tmp);                               { We make room for item. }
        item:=FindItem(id);                     { We find according definitions. }
        tmp^.def:=item;                         { We fill in all data necessary. }
        tmp^.qnt:=item^.qnt+Random(item^.qnt);
        tmp^.flg:=[];
        tmp^.con:=NIL;
        tmp^.next:=NIL;
        IF (ITEM_KNOWN IN item^.flg) THEN tmp^.flg:=tmp^.flg+[SLOT_KNOWN];
        GenerateItem:=tmp;                      { And we have brand new item. }
END;

PROCEDURE DestroyItem(what: ItemSlot);          { Destroys an item. }
VAR     tmp: pItem;
BEGIN
        tmp:=what^;                             { We take item from any container it may be in. }
        what^:=tmp^.next;                       { We update this container's list of items. }
        Dispose(tmp);                           { And we relase memory. }
END;

PROCEDURE MoveInto(what,where: ItemSlot);       { Tries to put item from WHAT into container in slot WHERE. }
VAR     tmp: pItem;
BEGIN
        IF IsInThere(where,what) THEN Exit;     { We dont want to put something into itself! }
        tmp:=what^;                             { Now, we take item from any container it may be in. }
        what^:=tmp^.next;                       { We update this container's list of items. }
        tmp^.next:=where^^.con;                 { We put all items from WHERE behind our item. }
        where^^.con:=tmp;                       { We put our item into item in slot WHERE. }
END;

PROCEDURE MoveItem(what,where: ItemSlot);       { Tries to put item from WHAT into slot WHERE. }
VAR     tmp: pItem;
BEGIN
        IF where^<>NIL THEN Exit;               { Is there something already? }
        tmp:=what^;                             { Now, we take item from any container it may be in. }
        what^:=tmp^.next;                       { We update this container's list of items. }
        tmp^.next:=NIL;                         { Our item is the only one here. }
        where^:=tmp;                            { We put our item into slot WHERE. }
END;

PROCEDURE DropItem(what,where: ItemSlot);       { Drops item from WHAT into slot WHERE, creating a pile if needed. }
VAR     tmp: ItemSlot;
        p: pItem;
BEGIN
        IF where^=NIL THEN BEGIN                { If WHERE is empty, we simply put our item there. }
                MoveItem(what,where);
		Exit;
        END;
	
        IF where^^.def^.id=PILEITEM THEN BEGIN  { If there is a pile already, we just add our item to the pile. }
                MoveInto(what,where)
        END ELSE BEGIN                          { If there is an item already... }
		New(tmp);                       { We need 'third hand'. }
		tmp^:=NIL;
		MoveItem(where,tmp);            { We pick the item from the floor. }
                p:=GenerateItem(PILEITEM);      { Then we create a pile... }
                MoveItem(@p,where);             { And put it on the floor. }
		MoveInto(tmp,where);		{ Then we put both items into the pile. }
		MoveInto(what,where);
                Dispose(tmp);                   { W don't need third hand anymore. }
        END;
END;

PROCEDURE PutToPack(what: ItemSlot);            { Tries to put an item into pack. }
BEGIN
        IF (pc.inv[INV_PACK]=NIL) OR            { Have we any pack to put into? }
           NOT (ITEM_EMPTY IN pc.inv[INV_PACK]^.def^.flg) THEN Exit;
                                                { We inform the player about what we are doing. }
        Msg(Replace(Replace(msgs[15],'@1',ItemName(@(pc.inv[INV_PACK]))),'@v',ItemName(what)));
        MoveInto(what,@(pc.inv[INV_PACK]));     { And we do it. }
END;

{ ------------------------------------------- Menus and handling -------- }

PROCEDURE CheckCmd(VAR a:INTEGER;c:CHAR;b:BYTE);{ Checks if key C belong to command B }
BEGIN                                           { and sets A to this command if so. }
        IF c=cmd[b] THEN a:=b;
END;

FUNCTION ListItems(where: ItemSlot):ItemSlot;   { Lists all items in the container and picks one. }
VAR     p,page: ItemSlot;
        c,k: CHAR;
BEGIN
        ListItems:=NIL;
        ClrMsg;
        p:=@(where^^.con);
        REPEAT                                  { Tell what item we are looking into. }
                Msg(Replace(msgs[29],'@v',ItemName(where)));

                page:=p;                        { Mark the top of actual page. }
                c:='a';                         { Show a-p items. }
                WHILE (p^<>NIL) AND (c<='p') DO BEGIN
                        Msg(Replace(Replace(msgs[30],'@1',c),'@v',ItemName(p)));
                        Inc(c);
                        p:=@(p^^.next);
                END;
                                                { Tell if there is more. }
                IF (p^<>NIL) THEN Msg(msgs[19]);

                k:=GetKey;                      { Get players choice. }
                ClrMsg;                         { And find this item. }
                IF (k>='a') AND (k<c) THEN BEGIN
                        p:=page;
                        c:='a';
                        WHILE (c<>k) DO BEGIN
                                Inc(c);
                                p:=@(p^^.next);
                        END;
                        ListItems:=p;
                END;
                IF k<>cmd[CMDMORE] THEN Exit;   { If the player picked something -- we finish. }
                IF p^=NIL THEN p:=@(where^^.con);
        UNTIL FALSE;                            { And if not, we display another page. }
END;

PROCEDURE SelectItem(what: ItemSlot);           { Shows item description,asks for action and performs it. }
VAR     c: CHAR;
        l: INTEGER;
        tmp: pItem;
        s: STRING;

        AllowLoot, AllowPutInto, AllowHold, AllowDrop, AllowPack: BOOLEAN;
        WhereEquip: BYTE;

BEGIN
   REPEAT
        IF (what=NIL) OR (what^=NIL) OR                 { We won't select an empty item nor pile. }
           (what^^.def^.id=PILEITEM) THEN BEGIN
                Msg(msgs[10]);
                Exit;
        END;

        ClrMsg;
        Msg(Replace(msgs[17],'@v',ItemName(what)));     { We show item's name and description. }
        {IF (ITEM_KNOWN IN what^^.flg) THEN}
        Msg(GetItemDesc(what));

        l:=GetItemWeight(what);                         { We display item's weight. }
        IF l=1 THEN s:=Replace(msgs[33],'@s','')
               ELSE s:=Plural(msgs[33]);
        Msg(Replace(s,'@v',Num(l)));

        AllowHold:=FALSE;                               { Now we determine what actions can be performed. }
        AllowPutInto:=FALSE;
        AllowLoot:=FALSE;
        AllowDrop:=FALSE;
        AllowPack:=FALSE;
        WhereEquip:=0;

        IF (pc.inv[INV_PACK]<>NIL)AND                   { If we have pack and the item we have selected isn't that pack, }
           (ITEM_EMPTY IN pc.inv[INV_PACK]^.def^.flg)AND{we can put our item into pack. }
	   NOT(IsInThere(what,@(pc.inv[INV_PACK])))AND
	   (pc.inv[INV_PACK]<>what^) THEN AllowPack:=TRUE;

        IF pc.inv[INV_HELD]=NIL THEN AllowHold:=TRUE;   { If the player character is empty handed, we can hold something. }

        IF (pc.inv[INV_HELD]<>NIL)AND			{ If the player holds something, and selected item is a container,
							{ he can put held item in. }
           NOT(IsInThere(what,@(pc.inv[INV_HELD])))AND  { But we have to make sure he won't put it into itself! }
           (ITEM_EMPTY IN what^^.def^.flg) THEN AllowPutInto:=TRUE;

        IF (what^^.con<>NIL) THEN AllowLoot:=TRUE;      { If it isn't empty, we can loot it. }

							{ Dropping only when the item isn't on floor already. }
	IF NOT(IsInThere(what,@(GetMap(playchr^.x,playchr^.y)^.slt))) THEN AllowDrop:=TRUE;

        WhereEquip:=what^^.def^.eqp;                    { And we can equip is if it can be equipped. :) }

        IF AllowHold    THEN Msg(msgs[41]);             { We display action names. }
        IF AllowPutInto THEN Msg(msgs[42]);
        IF WhereEquip>0 THEN Msg(msgs[44]);
        IF AllowPack    THEN Msg(msgs[45]);
        IF AllowLoot    THEN Msg(msgs[40]);
        IF AllowDrop    THEN Msg(msgs[43]);

        l:=CMDNOTHING;                                  { Ask for action. }
        c:=GetKey;
        ClrMsg;
        IF AllowPack    THEN CheckCmd(l,c,CMDPUTTOPACK);
        IF AllowLoot    THEN CheckCmd(l,c,CMDLOOT);
        IF AllowHold    THEN CheckCmd(l,c,CMDUPINAIR);
        IF AllowPutInto THEN CheckCmd(l,c,CMDUPINAIR);
        IF WhereEquip>0 THEN CheckCmd(l,c,CMDEQUIP);
        IF AllowDrop    THEN CheckCmd(l,c,CMDDROP);

        CASE l OF                                       { Perform the action. }
                CMDLOOT: BEGIN                          { We list items inside and wait allow to select an item. }
                                what:=ListItems(what);
                        END;
                CMDPUTTOPACK: BEGIN
                                PutToPack(what);        { Simple. }
                                what:=NIL;              { We don't want to select this item again. }
                        END;
                CMDUPINAIR: IF AllowHold THEN BEGIN
                                MoveItem(what,@(pc.inv[INV_HELD]));
                                what:=NIL;
                        END ELSE IF AllowPutInto THEN BEGIN
                                MoveInto(@(pc.inv[INV_HELD]),what);
                                Msg(Replace(Replace(msgs[15],'@1',ItemName(what)) ,'@v',ItemName(@(pc.inv[INV_HELD]))));
                                what:=NIL;
                        END;
                CMDDROP:BEGIN
                                DropItem(what,@(GetMap(playchr^.x,playchr^.y)^.slt));
                                ShowItem(@(GetMap(playchr^.x,playchr^.y)^.slt));
                                what:=NIL;
                        END;
                CMDEQUIP:BEGIN		{ If there is an item equipped and player character is empty handed, we hold equipped item. }
                                IF pc.inv[WhereEquip]<>NIL THEN
                                         IF pc.inv[INV_HELD]=NIL THEN MoveItem(@(pc.inv[WhereEquip]),@(pc.inv[INV_HELD]));

                                                        { If the slot is free, we equip selected item. }
                                IF pc.inv[WhereEquip]=NIL THEN MoveItem(what,@(pc.inv[WhereEquip]))
                                                          ELSE Msg(msgs[32]);
                                what:=NIL;
                         END;
                CMDNOTHING:BEGIN                        { If no action, we cancel selection. }
                                Msg(msgs[10]);
                                what:=NIL;
                        END;
        END;
   UNTIL what=NIL;                                      { If the player haven't selected another item, we finish. }
END;

PROCEDURE CheckPile(where: ItemSlot);                   { Destroys a pile if itsn't needed anymore. }
VAR     tmp: ItemSlot;
BEGIN
        IF where^=NIL THEN Exit;                        { If there is nothing there, we finish. }
        IF where^^.def^.id<>PILEITEM THEN Exit;         { If there is no pile, we finish too. }

        IF where^^.con=NIL THEN BEGIN                   { If the pile is empty, we simply destroy it. }
                DestroyItem(where);
                Exit;
        END;
        IF (where^^.con^.next=NIL) THEN BEGIN           { If there is only one item in the pile.. }
		New(tmp);                               { We need 'third hand'. }
		tmp^:=NIL;
		MoveItem(@(where^^.con),tmp);           { We take the item out of pile. }
                DestroyItem(where);                     { We destroy the pile. }
                MoveItem(tmp,where);                    { Then we put the item on the floor. }
                Dispose(tmp);                           { We don't need third hand. }
        END;
END;

PROCEDURE ExamInv;                                      { Displays equipment. }
VAR     c:CHAR;
BEGIN
        ClrMsg;
        Msg(Concat('@Ma@m)HELD: @M',ItemName(@(pc.inv[INV_HELD]))));
        Msg(Concat('@Mb@m)PACK: @M',ItemName(@(pc.inv[INV_PACK]))));
        Msg(Concat('@Mc@m)WEAR: @M',ItemName(@(pc.inv[INV_WEAR]))));
        c:=GetKey;
        ClrMsg;
        CASE c OF
                'a': SelectItem(@pc.inv[INV_HELD]);
                'b': SelectItem(@pc.inv[INV_PACK]);
                'c': SelectItem(@pc.inv[INV_WEAR]);
        END;
END;

END.