Merge branch 'duke' into dev
This commit is contained in:
@@ -272,26 +272,9 @@ libbitcoin_server_a_SOURCES = \
|
||||
asyncrpcqueue.cpp \
|
||||
bloom.cpp \
|
||||
cc/eval.cpp \
|
||||
cc/import.cpp \
|
||||
cc/importgateway.cpp \
|
||||
cc/CCassetsCore.cpp \
|
||||
cc/CCcustom.cpp \
|
||||
cc/CCtx.cpp \
|
||||
cc/CCutils.cpp \
|
||||
cc/CCtokens.cpp \
|
||||
cc/assets.cpp \
|
||||
cc/faucet.cpp \
|
||||
cc/rewards.cpp \
|
||||
cc/dice.cpp \
|
||||
cc/lotto.cpp \
|
||||
cc/fsm.cpp \
|
||||
cc/heir.cpp \
|
||||
cc/oracles.cpp \
|
||||
cc/payments.cpp \
|
||||
cc/gateways.cpp \
|
||||
cc/channels.cpp \
|
||||
cc/auction.cpp \
|
||||
cc/betprotocol.cpp \
|
||||
chain.cpp \
|
||||
checkpoints.cpp \
|
||||
fs.cpp \
|
||||
@@ -345,9 +328,6 @@ libbitcoin_wallet_a_SOURCES = \
|
||||
zcash/Note.cpp \
|
||||
transaction_builder.cpp \
|
||||
wallet/rpcdump.cpp \
|
||||
cc/CCtokens.cpp \
|
||||
cc/CCassetsCore.cpp \
|
||||
cc/CCassetstx.cpp \
|
||||
cc/CCtx.cpp \
|
||||
wallet/rpcwallet.cpp \
|
||||
wallet/rpchushwallet.cpp \
|
||||
@@ -412,7 +392,6 @@ libbitcoin_common_a_SOURCES = \
|
||||
core_read.cpp \
|
||||
core_write.cpp \
|
||||
hash.cpp \
|
||||
importcoin.cpp \
|
||||
key.cpp \
|
||||
key_io.cpp \
|
||||
keystore.cpp \
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef CC_GATEWAYS_H
|
||||
#define CC_GATEWAYS_H
|
||||
|
||||
#include "CCinclude.h"
|
||||
|
||||
bool GatewaysValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
|
||||
UniValue GatewaysBind(const CPubKey& pk, uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector<CPubKey> pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4);
|
||||
UniValue GatewaysDeposit(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vector<uint8_t>proof,CPubKey destpub,int64_t amount);
|
||||
UniValue GatewaysClaim(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount);
|
||||
UniValue GatewaysWithdraw(const CPubKey& pk, uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount);
|
||||
UniValue GatewaysPartialSign(const CPubKey& pk, uint64_t txfee,uint256 txidaddr,std::string refcoin,std::string hex);
|
||||
UniValue GatewaysCompleteSigning(const CPubKey& pk, uint64_t txfee,uint256 txidaddr,std::string refcoin,std::string hex);
|
||||
UniValue GatewaysMarkDone(const CPubKey& pk, uint64_t txfee,uint256 withdrawtxid,std::string refcoin);
|
||||
UniValue GatewaysPendingDeposits(const CPubKey& pk, uint256 bindtxid,std::string refcoin);
|
||||
UniValue GatewaysPendingWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin);
|
||||
UniValue GatewaysProcessedWithdraws(const CPubKey& pk, uint256 bindtxid,std::string refcoin);
|
||||
|
||||
// CCcustom
|
||||
UniValue GatewaysInfo(uint256 bindtxid);
|
||||
UniValue GatewaysExternalAddress(uint256 bindtxid,CPubKey pubkey);
|
||||
UniValue GatewaysDumpPrivKey(uint256 bindtxid,CKey privkey);
|
||||
UniValue GatewaysList();
|
||||
|
||||
#endif
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
#ifndef CC_PAYMENTS_H
|
||||
#define CC_PAYMENTS_H
|
||||
|
||||
#include "CCinclude.h"
|
||||
#include <gmp.h>
|
||||
#include <key_io.h>
|
||||
|
||||
#define PAYMENTS_TXFEE 10000
|
||||
#define PAYMENTS_MERGEOFSET 60 // 1H extra.
|
||||
extern std::vector <std::pair<CAmount, CTxDestination>> vAddressSnapshot;
|
||||
extern int32_t lastSnapShotHeight;
|
||||
|
||||
bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
|
||||
|
||||
// CCcustom
|
||||
UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr);
|
||||
UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr);
|
||||
UniValue PaymentsMerge(struct CCcontract_info *cp,char *jsonstr);
|
||||
UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr);
|
||||
UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr);
|
||||
UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr);
|
||||
UniValue PaymentsAirdropTokens(struct CCcontract_info *cp,char *jsonstr);
|
||||
UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr);
|
||||
UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -46,23 +46,5 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr
|
||||
int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid);
|
||||
bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid);
|
||||
|
||||
// CCassetstx
|
||||
//int64_t GetAssetBalance(CPubKey pk,uint256 tokenid); // --> GetTokenBalance()
|
||||
int64_t AddAssetInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 assetid, int64_t total, int32_t maxinputs);
|
||||
|
||||
UniValue AssetOrders(uint256 tokenid, CPubKey pubkey, uint8_t additionalEvalCode);
|
||||
//UniValue AssetInfo(uint256 tokenid);
|
||||
//UniValue AssetList();
|
||||
//std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description);
|
||||
//std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total);
|
||||
//std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode);
|
||||
|
||||
std::string CreateBuyOffer(int64_t txfee,int64_t bidamount,uint256 assetid,int64_t pricetotal);
|
||||
std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid);
|
||||
std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount);
|
||||
std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t pricetotal);
|
||||
std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 assetid2,int64_t pricetotal);
|
||||
std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid);
|
||||
std::string FillSell(int64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,int64_t fillamount);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,653 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2018 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCassets.h"
|
||||
|
||||
/*
|
||||
The SetAssetFillamounts() and ValidateAssetRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively.
|
||||
|
||||
This pair of functions are critical to make sure the trading is correct and is the trickiest part of the assets contract.
|
||||
|
||||
//vin.0: normal input
|
||||
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
|
||||
//vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
|
||||
//vout.0: remaining amount of bid to unspendable
|
||||
//vout.1: vin.1 value to signer of vin.2
|
||||
//vout.2: vin.2 assetoshis to original pubkey
|
||||
//vout.3: CC output for assetoshis change (if any)
|
||||
//vout.4: normal output for change (if any)
|
||||
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
|
||||
ValidateAssetRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits);
|
||||
|
||||
Yes, this is quite confusing...
|
||||
|
||||
In ValidateAssetRemainder the naming convention is nValue is the coin/asset with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or assets, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits.
|
||||
|
||||
We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it.
|
||||
*/
|
||||
|
||||
bool ValidateBidRemainder(int64_t remaining_units,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidunits,int64_t totalunits)
|
||||
{
|
||||
int64_t unitprice,recvunitprice,newunitprice=0;
|
||||
if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits);
|
||||
return(false);
|
||||
}
|
||||
else if ( totalunits != (remaining_units + paidunits) )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_units %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_units + paidunits),(long long)remaining_units,(long long)paidunits);
|
||||
return(false);
|
||||
}
|
||||
else if ( orig_nValue != (remaining_nValue + received_nValue) )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue);
|
||||
return(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
//unitprice = (orig_nValue * COIN) / totalunits;
|
||||
//recvunitprice = (received_nValue * COIN) / paidunits;
|
||||
//if ( remaining_units != 0 )
|
||||
// newunitprice = (remaining_nValue * COIN) / remaining_units;
|
||||
unitprice = (orig_nValue / totalunits);
|
||||
recvunitprice = (received_nValue / paidunits);
|
||||
if ( remaining_units != 0 )
|
||||
newunitprice = (remaining_nValue / remaining_units);
|
||||
if ( recvunitprice < unitprice )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN));
|
||||
return(false);
|
||||
}
|
||||
fprintf(stderr,"ValidateAssetRemainder() orig %llu total %llu, recv %llu paid %llu,recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(long long)orig_nValue,(long long)totalunits,(long long)received_nValue,(long long)paidunits,(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN));
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool SetBidFillamounts(int64_t &received_nValue,int64_t &remaining_units,int64_t orig_nValue,int64_t &paidunits,int64_t totalunits)
|
||||
{
|
||||
int64_t remaining_nValue,unitprice; double dprice;
|
||||
if ( totalunits == 0 )
|
||||
{
|
||||
received_nValue = remaining_units = paidunits = 0;
|
||||
return(false);
|
||||
}
|
||||
if ( paidunits >= totalunits )
|
||||
{
|
||||
paidunits = totalunits;
|
||||
received_nValue = orig_nValue;
|
||||
remaining_units = 0;
|
||||
fprintf(stderr,"SetBidFillamounts() bid order totally filled!\n");
|
||||
return(true);
|
||||
}
|
||||
remaining_units = (totalunits - paidunits);
|
||||
//unitprice = (orig_nValue * COIN) / totalunits;
|
||||
//received_nValue = (paidunits * unitprice) / COIN;
|
||||
unitprice = (orig_nValue / totalunits);
|
||||
received_nValue = (paidunits * unitprice);
|
||||
if ( unitprice > 0 && received_nValue > 0 && received_nValue <= orig_nValue )
|
||||
{
|
||||
remaining_nValue = (orig_nValue - received_nValue);
|
||||
printf("SetBidFillamounts() total.%llu - paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue);
|
||||
return(ValidateBidRemainder(remaining_units,remaining_nValue,orig_nValue,received_nValue,paidunits,totalunits));
|
||||
} else return(false);
|
||||
}
|
||||
|
||||
bool SetAskFillamounts(int64_t &received_assetoshis,int64_t &remaining_nValue,int64_t orig_assetoshis,int64_t &paid_nValue,int64_t total_nValue)
|
||||
{
|
||||
int64_t remaining_assetoshis; double dunitprice;
|
||||
if ( total_nValue == 0 )
|
||||
{
|
||||
received_assetoshis = remaining_nValue = paid_nValue = 0;
|
||||
return(false);
|
||||
}
|
||||
if ( paid_nValue >= total_nValue )
|
||||
{
|
||||
paid_nValue = total_nValue;
|
||||
received_assetoshis = orig_assetoshis;
|
||||
remaining_nValue = 0;
|
||||
fprintf(stderr,"SetAskFillamounts() ask order totally filled!\n");
|
||||
return(true);
|
||||
}
|
||||
remaining_nValue = (total_nValue - paid_nValue);
|
||||
dunitprice = ((double)total_nValue / orig_assetoshis);
|
||||
received_assetoshis = (paid_nValue / dunitprice);
|
||||
fprintf(stderr,"SetAskFillamounts() remaining_nValue %.8f (%.8f - %.8f)\n",(double)remaining_nValue/COIN,(double)total_nValue/COIN,(double)paid_nValue/COIN);
|
||||
fprintf(stderr,"SetAskFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis);
|
||||
if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis )
|
||||
{
|
||||
remaining_assetoshis = (orig_assetoshis - received_assetoshis);
|
||||
return(ValidateAskRemainder(remaining_nValue,remaining_assetoshis,orig_assetoshis,received_assetoshis,paid_nValue,total_nValue));
|
||||
} else return(false);
|
||||
}
|
||||
|
||||
bool ValidateAskRemainder(int64_t remaining_nValue,int64_t remaining_assetoshis,int64_t orig_assetoshis,int64_t received_assetoshis,int64_t paid_nValue,int64_t total_nValue)
|
||||
{
|
||||
int64_t unitprice,recvunitprice,newunitprice=0;
|
||||
if ( orig_assetoshis == 0 || received_assetoshis == 0 || paid_nValue == 0 || total_nValue == 0 )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis == %llu || received_assetoshis == %llu || paid_nValue == %llu || total_nValue == %llu\n",(long long)orig_assetoshis,(long long)received_assetoshis,(long long)paid_nValue,(long long)total_nValue);
|
||||
return(false);
|
||||
}
|
||||
else if ( total_nValue != (remaining_nValue + paid_nValue) )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() total_nValue %llu != %llu (remaining_nValue %llu + %llu paid_nValue)\n",(long long)total_nValue,(long long)(remaining_nValue + paid_nValue),(long long)remaining_nValue,(long long)paid_nValue);
|
||||
return(false);
|
||||
}
|
||||
else if ( orig_assetoshis != (remaining_assetoshis + received_assetoshis) )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_assetoshis,(long long)(remaining_assetoshis - received_assetoshis),(long long)remaining_assetoshis,(long long)received_assetoshis);
|
||||
return(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
unitprice = (total_nValue / orig_assetoshis);
|
||||
recvunitprice = (paid_nValue / received_assetoshis);
|
||||
if ( remaining_nValue != 0 )
|
||||
newunitprice = (remaining_nValue / remaining_assetoshis);
|
||||
if ( recvunitprice < unitprice )
|
||||
{
|
||||
fprintf(stderr,"ValidateAskRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN);
|
||||
return(false);
|
||||
}
|
||||
fprintf(stderr,"ValidateAskRemainder() got recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool SetSwapFillamounts(int64_t &received_assetoshis,int64_t &remaining_assetoshis2,int64_t orig_assetoshis,int64_t &paid_assetoshis2,int64_t total_assetoshis2)
|
||||
{
|
||||
int64_t remaining_assetoshis; double dunitprice;
|
||||
if ( total_assetoshis2 == 0 )
|
||||
{
|
||||
fprintf(stderr,"SetSwapFillamounts() total_assetoshis2.0 origsatoshis.%llu paid_assetoshis2.%llu\n",(long long)orig_assetoshis,(long long)paid_assetoshis2);
|
||||
received_assetoshis = remaining_assetoshis2 = paid_assetoshis2 = 0;
|
||||
return(false);
|
||||
}
|
||||
if ( paid_assetoshis2 >= total_assetoshis2 )
|
||||
{
|
||||
paid_assetoshis2 = total_assetoshis2;
|
||||
received_assetoshis = orig_assetoshis;
|
||||
remaining_assetoshis2 = 0;
|
||||
fprintf(stderr,"SetSwapFillamounts() swap order totally filled!\n");
|
||||
return(true);
|
||||
}
|
||||
remaining_assetoshis2 = (total_assetoshis2 - paid_assetoshis2);
|
||||
dunitprice = ((double)total_assetoshis2 / orig_assetoshis);
|
||||
received_assetoshis = (paid_assetoshis2 / dunitprice);
|
||||
fprintf(stderr,"SetSwapFillamounts() remaining_assetoshis2 %llu (%llu - %llu)\n",(long long)remaining_assetoshis2/COIN,(long long)total_assetoshis2/COIN,(long long)paid_assetoshis2/COIN);
|
||||
fprintf(stderr,"SetSwapFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis);
|
||||
if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis )
|
||||
{
|
||||
remaining_assetoshis = (orig_assetoshis - received_assetoshis);
|
||||
return(ValidateAskRemainder(remaining_assetoshis2,remaining_assetoshis,orig_assetoshis,received_assetoshis,paid_assetoshis2,total_assetoshis2));
|
||||
} else return(false);
|
||||
}
|
||||
|
||||
bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidunits,int64_t totalunits)
|
||||
{
|
||||
int64_t unitprice,recvunitprice,newunitprice=0;
|
||||
if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits);
|
||||
return(false);
|
||||
}
|
||||
else if ( totalunits != (remaining_price + paidunits) )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits);
|
||||
return(false);
|
||||
}
|
||||
else if ( orig_nValue != (remaining_nValue + received_nValue) )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue);
|
||||
return(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
unitprice = (orig_nValue * COIN) / totalunits;
|
||||
recvunitprice = (received_nValue * COIN) / paidunits;
|
||||
if ( remaining_price != 0 )
|
||||
newunitprice = (remaining_nValue * COIN) / remaining_price;
|
||||
if ( recvunitprice < unitprice )
|
||||
{
|
||||
fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN));
|
||||
return(false);
|
||||
}
|
||||
fprintf(stderr,"ValidateAssetRemainder() recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN));
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
/* use EncodeTokenCreateOpRet instead:
|
||||
CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description)
|
||||
{
|
||||
CScript opret; uint8_t evalcode = EVAL_ASSETS;
|
||||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description);
|
||||
return(opret);
|
||||
}
|
||||
*/
|
||||
|
||||
vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey)
|
||||
{
|
||||
vscript_t vopret;
|
||||
uint8_t evalcode = EVAL_ASSETS;
|
||||
|
||||
switch ( assetFuncId )
|
||||
{
|
||||
//case 't': this cannot be here
|
||||
case 'x': case 'o':
|
||||
vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId);
|
||||
break;
|
||||
case 's': case 'b': case 'S': case 'B':
|
||||
vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId << price << origpubkey);
|
||||
break;
|
||||
case 'E': case 'e':
|
||||
assetid2 = revuint256(assetid2);
|
||||
vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId << assetid2 << price << origpubkey);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"EncodeAssetOpRet: illegal funcid.%02x\n", assetFuncId);
|
||||
//opret << OP_RETURN;
|
||||
break;
|
||||
}
|
||||
return(vopret);
|
||||
}
|
||||
|
||||
/* it is for compatibility, do not use this for new contracts (use DecodeTokenCreateOpRet)
|
||||
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description)
|
||||
{
|
||||
std::vector<uint8_t> vopret; uint8_t evalcode,funcid,*script;
|
||||
GetOpReturnData(scriptPubKey, vopret);
|
||||
script = (uint8_t *)vopret.data();
|
||||
if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_ASSETS && script[1] == 'c' )
|
||||
{
|
||||
if ( E_UNMARSHAL(vopret,ss >> evalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description) != 0 )
|
||||
return(true);
|
||||
}
|
||||
return(0);
|
||||
} */
|
||||
|
||||
uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey)
|
||||
{
|
||||
vscript_t vopretAssets; //, vopretAssetsStripped;
|
||||
uint8_t *script, funcId = 0, assetsFuncId = 0, dummyEvalCode, dummyAssetFuncId;
|
||||
uint256 dummyTokenid;
|
||||
std::vector<CPubKey> voutPubkeysDummy;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
tokenid = zeroid;
|
||||
assetid2 = zeroid;
|
||||
price = 0;
|
||||
assetsEvalCode = 0;
|
||||
assetsFuncId = 0;
|
||||
|
||||
// First - decode token opret:
|
||||
funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, oprets);
|
||||
GetOpretBlob(oprets, OPRETID_ASSETSDATA, vopretAssets);
|
||||
|
||||
LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() from DecodeTokenOpRet returned funcId=" << (int)funcId << std::endl);
|
||||
|
||||
if (funcId == 0 || vopretAssets.size() < 2) {
|
||||
LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << std::endl);
|
||||
return (uint8_t)0;
|
||||
}
|
||||
|
||||
//if (!E_UNMARSHAL(vopretAssets, { ss >> vopretAssetsStripped; })) { //strip string size
|
||||
// std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretAssetsStripped" << std::endl;
|
||||
// return (uint8_t)0;
|
||||
//}
|
||||
|
||||
// additional check to prevent crash
|
||||
if (vopretAssets.size() >= 2) {
|
||||
|
||||
assetsEvalCode = vopretAssets.begin()[0];
|
||||
assetsFuncId = vopretAssets.begin()[1];
|
||||
|
||||
LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() assetsEvalCode=" << (int)assetsEvalCode << " funcId=" << (char)(funcId ? funcId : ' ') << " assetsFuncId=" << (char)(assetsFuncId ? assetsFuncId : ' ') << std::endl);
|
||||
|
||||
if (assetsEvalCode == EVAL_ASSETS)
|
||||
{
|
||||
//fprintf(stderr,"DecodeAssetTokenOpRet() decode.[%c] assetFuncId.[%c]\n", funcId, assetFuncId);
|
||||
switch (assetsFuncId)
|
||||
{
|
||||
case 'x': case 'o':
|
||||
if (vopretAssets.size() == 2) // no data after 'evalcode assetFuncId' allowed
|
||||
{
|
||||
return(assetsFuncId);
|
||||
}
|
||||
break;
|
||||
case 's': case 'b': case 'S': case 'B':
|
||||
if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0)
|
||||
{
|
||||
//fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price);
|
||||
return(assetsFuncId);
|
||||
}
|
||||
break;
|
||||
case 'E': case 'e':
|
||||
if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0)
|
||||
{
|
||||
//fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price);
|
||||
assetid2 = revuint256(assetid2);
|
||||
return(assetsFuncId);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() no asset's payload or incorrect assets funcId or evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl);
|
||||
return (uint8_t)0;
|
||||
}
|
||||
|
||||
// extract sell/buy owner's pubkey from the opret
|
||||
bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx)
|
||||
{
|
||||
uint256 assetid,assetid2;
|
||||
uint8_t evalCode;
|
||||
|
||||
if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey) != 0 )
|
||||
return(true);
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
|
||||
// Calculate seller/buyer's dest cc address from ask/bid tx funcid
|
||||
bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origNormalAddr, const CTransaction& vintx)
|
||||
{
|
||||
uint256 assetid, assetid2;
|
||||
int64_t price,nValue=0;
|
||||
int32_t n;
|
||||
uint8_t vintxFuncId;
|
||||
std::vector<uint8_t> origpubkey;
|
||||
CScript script;
|
||||
uint8_t evalCode;
|
||||
|
||||
n = vintx.vout.size();
|
||||
if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 )
|
||||
return(false);
|
||||
|
||||
bool bGetCCaddr = false;
|
||||
struct CCcontract_info *cpTokens, tokensC;
|
||||
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
|
||||
|
||||
if (vintxFuncId == 's' || vintxFuncId == 'S') {
|
||||
// bGetCCaddr = GetCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey));
|
||||
cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present
|
||||
bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible
|
||||
}
|
||||
else if (vintxFuncId == 'b' || vintxFuncId == 'B') {
|
||||
cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present
|
||||
bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible
|
||||
}
|
||||
else {
|
||||
std::cerr << "GetAssetorigaddrs incorrect vintx funcid=" << (char)(vintxFuncId?vintxFuncId:' ') << std::endl;
|
||||
return false;
|
||||
}
|
||||
if( bGetCCaddr && Getscriptaddress(origNormalAddr, CScript() << origpubkey << OP_CHECKSIG))
|
||||
return(true);
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx)
|
||||
{
|
||||
uint256 hashBlock;
|
||||
uint256 assetid, assetid2;
|
||||
int64_t tmpprice;
|
||||
std::vector<uint8_t> tmporigpubkey;
|
||||
uint8_t evalCode;
|
||||
|
||||
char destaddr[64], unspendableAddr[64];
|
||||
|
||||
origaddr[0] = destaddr[0] = origCCaddr[0] = 0;
|
||||
|
||||
uint8_t funcid = 0;
|
||||
if (tx.vout.size() > 0) {
|
||||
uint256 assetid, assetid2;
|
||||
int64_t tmpprice;
|
||||
std::vector<uint8_t> tmporigpubkey;
|
||||
uint8_t evalCode;
|
||||
|
||||
funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey);
|
||||
}
|
||||
|
||||
if( tx.vin.size() < 2 )
|
||||
return eval->Invalid("not enough for CC vins");
|
||||
else if( tx.vin[vini].prevout.n != 0 )
|
||||
return eval->Invalid("vin1 needs to be buyvin.vout[0]");
|
||||
else if( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash, vinTx,hashBlock) == 0 )
|
||||
{
|
||||
std::cerr << "AssetValidateCCvin() cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl;
|
||||
return eval->Invalid("always should find CCvin, but didnt");
|
||||
}
|
||||
// check source cc unspendable cc address:
|
||||
// if fillSell or cancelSell --> should spend tokens from dual-eval token-assets unspendable addr
|
||||
else if( (funcid == 'S' || funcid == 'x') &&
|
||||
(Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ||
|
||||
!GetTokensCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) ||
|
||||
strcmp(destaddr, unspendableAddr) != 0))
|
||||
{
|
||||
fprintf(stderr,"AssetValidateCCvin() cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr);
|
||||
return eval->Invalid("invalid vin assets CCaddr");
|
||||
}
|
||||
// if fillBuy or cancelBuy --> should spend coins from asset unspendable addr
|
||||
else if ((funcid == 'B' || funcid == 'o') &&
|
||||
(Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ||
|
||||
!GetCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) ||
|
||||
strcmp(destaddr, unspendableAddr) != 0))
|
||||
{
|
||||
fprintf(stderr, "AssetValidateCCvin() cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr);
|
||||
return eval->Invalid("invalid vin assets CCaddr");
|
||||
}
|
||||
// end of check source unspendable cc address
|
||||
//else if ( vinTx.vout[0].nValue < 10000 )
|
||||
// return eval->Invalid("invalid dust for buyvin");
|
||||
// get user dest cc and normal addresses:
|
||||
else if( GetAssetorigaddrs(cp, origCCaddr, origaddr, vinTx) == 0 )
|
||||
return eval->Invalid("couldnt get origaddr for buyvin");
|
||||
|
||||
//fprintf(stderr,"AssetValidateCCvin() got %.8f to origaddr.(%s)\n", (double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr);
|
||||
|
||||
if ( vinTx.vout[0].nValue == 0 )
|
||||
return eval->Invalid("null value CCvin");
|
||||
|
||||
return(vinTx.vout[0].nValue);
|
||||
}
|
||||
|
||||
int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid)
|
||||
{
|
||||
CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode;
|
||||
|
||||
CCaddr[0] = origaddr[0] = 0;
|
||||
|
||||
// validate locked coins on Assets vin[1]
|
||||
if ( (nValue= AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 )
|
||||
return(0);
|
||||
else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
return eval->Invalid("invalid normal vout0 for buyvin");
|
||||
else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey)) == 'b' &&
|
||||
vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'?
|
||||
return eval->Invalid("invalid normal vout1 for buyvin");
|
||||
else
|
||||
{
|
||||
//fprintf(stderr,"have %.8f checking assetid origaddr.(%s)\n",(double)nValue/COIN,origaddr);
|
||||
if ( vinTx.vout.size() > 0 && funcid != 'b' && funcid != 'B' )
|
||||
return eval->Invalid("invalid opreturn for buyvin");
|
||||
else if ( refassetid != assetid )
|
||||
return eval->Invalid("invalid assetid for buyvin");
|
||||
//int32_t i; for (i=31; i>=0; i--)
|
||||
// fprintf(stderr,"%02x",((uint8_t *)&assetid)[i]);
|
||||
//fprintf(stderr," AssetValidateBuyvin assetid for %s\n",origaddr);
|
||||
}
|
||||
return(nValue);
|
||||
}
|
||||
|
||||
int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid)
|
||||
{
|
||||
CTransaction vinTx; int64_t nValue,assetoshis;
|
||||
//fprintf(stderr,"AssetValidateSellvin()\n");
|
||||
if ( (nValue = AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 )
|
||||
return(0);
|
||||
if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey, vinTx, 0, assetid)) == 0 )
|
||||
return eval->Invalid("invalid missing CC vout0 for sellvin");
|
||||
else
|
||||
return(assetoshis);
|
||||
}
|
||||
|
||||
|
||||
// validates opret for asset tx:
|
||||
bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &price, std::vector<uint8_t> &origpubkey) {
|
||||
|
||||
uint256 assetidOpret, assetidOpret2;
|
||||
uint8_t funcid, evalCode;
|
||||
|
||||
// this is just for log messages indentation fur debugging recursive calls:
|
||||
int32_t n = tx.vout.size();
|
||||
|
||||
if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey)) == 0)
|
||||
{
|
||||
std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl;
|
||||
return(false);
|
||||
}
|
||||
/* it is now on token level:
|
||||
else if (funcid == 'c')
|
||||
{
|
||||
if (assetid != zeroid && assetid == tx.GetHash() && v == 0) {
|
||||
//std::cerr << "ValidateAssetOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
else if (funcid == 't') // TODO: check if this new block does not influence IsAssetVout
|
||||
{
|
||||
//std::cerr << "ValidateAssetOpret() assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
|
||||
if (assetid != zeroid && assetid == assetidOpret) {
|
||||
//std::cerr << "ValidateAssetOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
|
||||
return(true);
|
||||
}
|
||||
} */
|
||||
//else if ((funcid == 'b' || funcid == 'B') && v == 0) // critical! 'b'/'B' vout0 is NOT asset
|
||||
// return(false);
|
||||
else if (funcid != 'E')
|
||||
{
|
||||
if (assetid != zeroid && assetidOpret == assetid)
|
||||
{
|
||||
//std::cerr << "ValidateAssetOpret() returns true for not 'E', funcid=" << (char)funcid << std::endl;
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
else if (funcid == 'E') // NOTE: not implemented yet!
|
||||
{
|
||||
if (v < 2 && assetid != zeroid && assetidOpret == assetid)
|
||||
return(true);
|
||||
else if (v == 2 && assetid != zeroid && assetidOpret2 == assetid)
|
||||
return(true);
|
||||
}
|
||||
|
||||
//std::cerr << "ValidateAssetOpret() return false funcid=" << (char)funcid << " assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks if the vout is a really Asset CC vout
|
||||
int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid)
|
||||
{
|
||||
|
||||
//std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl;
|
||||
|
||||
int32_t n = tx.vout.size();
|
||||
// just check boundaries:
|
||||
if (v >= n - 1) { // just moved this up (dimxy)
|
||||
std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl;
|
||||
return(0);
|
||||
}
|
||||
|
||||
if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here
|
||||
{
|
||||
// moved opret checking to this new reusable func (dimxy):
|
||||
const bool valOpret = ValidateAssetOpret(tx, v, refassetid, price, origpubkey);
|
||||
//std::cerr << "IsAssetvout() ValidateAssetOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl;
|
||||
if (valOpret) {
|
||||
//std::cerr << "IsAssetvout() ValidateAssetOpret returned true, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl;
|
||||
return tx.vout[v].nValue;
|
||||
}
|
||||
|
||||
//fprintf(stderr,"IsAssetvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str());
|
||||
}
|
||||
//fprintf(stderr,"IsAssetvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
|
||||
return(0);
|
||||
}
|
||||
|
||||
// sets cc inputs vs cc outputs and ensures they are equal:
|
||||
bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid)
|
||||
{
|
||||
CTransaction vinTx; uint256 hashBlock, id, id2; int32_t flag; int64_t assetoshis; std::vector<uint8_t> tmporigpubkey; int64_t tmpprice;
|
||||
int32_t numvins = tx.vin.size();
|
||||
int32_t numvouts = tx.vout.size();
|
||||
inputs = outputs = 0;
|
||||
|
||||
struct CCcontract_info *cpTokens, C;
|
||||
|
||||
cpTokens = CCinit(&C, EVAL_TOKENS);
|
||||
|
||||
for (int32_t i = 0; i<numvins; i++)
|
||||
{ // only tokens are relevant!!
|
||||
if (/*(*cpAssets->ismyvin)(tx.vin[i].scriptSig)*/ (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) // || IsVinAllowed(tx.vin[i].scriptSig) != 0)
|
||||
{
|
||||
//std::cerr << indentStr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
|
||||
// we are not inside the validation code -- dimxy
|
||||
if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)))
|
||||
{
|
||||
std::cerr << "AssetCalcAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl;
|
||||
return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt");
|
||||
}
|
||||
else {
|
||||
// validate vouts of vintx
|
||||
//std::cerr << indentStr << "AssetExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl;
|
||||
//assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, vinTx, tx.vin[i].prevout.n, assetid);
|
||||
std::vector<uint8_t> vopretExtra;
|
||||
std::vector<CPubKey> vinPubkeysEmpty;
|
||||
|
||||
// TODO: maybe we do not need call to IsTokensVout here, cause we've already selected token vins
|
||||
assetoshis = IsTokensvout(false, false, cpTokens, NULL, vinTx, tx.vin[i].prevout.n, assetid);
|
||||
if (assetoshis != 0)
|
||||
{
|
||||
//std::cerr << "AssetCalcAmounts() vin i=" << i << " assetoshis=" << assetoshis << std::endl;
|
||||
inputs += assetoshis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < numvouts-1; i++)
|
||||
{
|
||||
assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, tx, i, assetid);
|
||||
if (assetoshis != 0)
|
||||
{
|
||||
//std::cerr << "AssetCalcAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl;
|
||||
outputs += assetoshis;
|
||||
}
|
||||
}
|
||||
|
||||
//std::cerr << "AssetCalcAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
|
||||
|
||||
/* we do not verify inputs == outputs here,
|
||||
it's now done in Tokens */
|
||||
return(true);
|
||||
}
|
||||
@@ -1,783 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCassets.h"
|
||||
#include "CCtokens.h"
|
||||
|
||||
|
||||
UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t additionalEvalCode)
|
||||
{
|
||||
UniValue result(UniValue::VARR);
|
||||
|
||||
struct CCcontract_info *cpAssets, assetsC;
|
||||
struct CCcontract_info *cpTokens, tokensC;
|
||||
|
||||
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
|
||||
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
|
||||
|
||||
auto addOrders = [&](struct CCcontract_info *cp, std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it)
|
||||
{
|
||||
uint256 txid, hashBlock, assetid, assetid2;
|
||||
int64_t price;
|
||||
std::vector<uint8_t> origpubkey;
|
||||
CTransaction ordertx;
|
||||
uint8_t funcid, evalCode;
|
||||
char numstr[32], funcidstr[16], origaddr[64], origtokenaddr[64];
|
||||
|
||||
txid = it->first.txhash;
|
||||
LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() checking txid=" << txid.GetHex() << std::endl);
|
||||
if ( myGetTransaction(txid, ordertx, hashBlock) != 0 )
|
||||
{
|
||||
// for logging: funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey);
|
||||
if (ordertx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(ordertx.vout[ordertx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0)
|
||||
{
|
||||
LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() checking ordertx.vout.size()=" << ordertx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << " assetid=" << assetid.GetHex() << std::endl);
|
||||
|
||||
if (pk == CPubKey() && (refassetid == zeroid || assetid == refassetid) // tokenorders
|
||||
|| pk != CPubKey() && pk == pubkey2pk(origpubkey) && (funcid == 'S' || funcid == 's')) // mytokenorders, returns only asks (is this correct?)
|
||||
{
|
||||
|
||||
LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << std::endl);
|
||||
if (ordertx.vout[it->first.index].nValue == 0) {
|
||||
LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() order with value=0 skipped" << std::endl);
|
||||
return;
|
||||
}
|
||||
|
||||
UniValue item(UniValue::VOBJ);
|
||||
|
||||
funcidstr[0] = funcid;
|
||||
funcidstr[1] = 0;
|
||||
item.push_back(Pair("funcid", funcidstr));
|
||||
item.push_back(Pair("txid", txid.GetHex()));
|
||||
item.push_back(Pair("vout", (int64_t)it->first.index));
|
||||
if (funcid == 'b' || funcid == 'B')
|
||||
{
|
||||
sprintf(numstr, "%.8f", (double)ordertx.vout[it->first.index].nValue / COIN);
|
||||
item.push_back(Pair("amount", numstr));
|
||||
sprintf(numstr, "%.8f", (double)ordertx.vout[0].nValue / COIN);
|
||||
item.push_back(Pair("bidamount", numstr));
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(numstr, "%llu", (long long)ordertx.vout[it->first.index].nValue);
|
||||
item.push_back(Pair("amount", numstr));
|
||||
sprintf(numstr, "%llu", (long long)ordertx.vout[0].nValue);
|
||||
item.push_back(Pair("askamount", numstr));
|
||||
}
|
||||
if (origpubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE)
|
||||
{
|
||||
GetCCaddress(cp, origaddr, pubkey2pk(origpubkey));
|
||||
item.push_back(Pair("origaddress", origaddr));
|
||||
GetTokensCCaddress(cpTokens, origtokenaddr, pubkey2pk(origpubkey));
|
||||
item.push_back(Pair("origtokenaddress", origtokenaddr));
|
||||
}
|
||||
if (assetid != zeroid)
|
||||
item.push_back(Pair("tokenid", assetid.GetHex()));
|
||||
if (assetid2 != zeroid)
|
||||
item.push_back(Pair("otherid", assetid2.GetHex()));
|
||||
if (price > 0)
|
||||
{
|
||||
if (funcid == 's' || funcid == 'S' || funcid == 'e' || funcid == 'e')
|
||||
{
|
||||
sprintf(numstr, "%.8f", (double)price / COIN);
|
||||
item.push_back(Pair("totalrequired", numstr));
|
||||
sprintf(numstr, "%.8f", (double)price / (COIN * ordertx.vout[0].nValue));
|
||||
item.push_back(Pair("price", numstr));
|
||||
}
|
||||
else
|
||||
{
|
||||
item.push_back(Pair("totalrequired", (int64_t)price));
|
||||
sprintf(numstr, "%.8f", (double)ordertx.vout[0].nValue / (price * COIN));
|
||||
item.push_back(Pair("price", numstr));
|
||||
}
|
||||
}
|
||||
result.push_back(item);
|
||||
LOGSTREAM("ccassets", CCLOG_DEBUG1, stream << "addOrders() added order funcId=" << (char)(funcid ? funcid : ' ') << " it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << " tokenid=" << assetid.GetHex() << std::endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputsTokens, unspentOutputsDualEvalTokens, unspentOutputsCoins;
|
||||
|
||||
char assetsUnspendableAddr[64];
|
||||
GetCCaddress(cpAssets, assetsUnspendableAddr, GetUnspendable(cpAssets, NULL));
|
||||
SetCCunspents(unspentOutputsCoins, assetsUnspendableAddr,true);
|
||||
|
||||
char assetsTokensUnspendableAddr[64];
|
||||
std::vector<uint8_t> vopretNonfungible;
|
||||
if (refassetid != zeroid) {
|
||||
GetNonfungibleData(refassetid, vopretNonfungible);
|
||||
if (vopretNonfungible.size() > 0)
|
||||
cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
|
||||
}
|
||||
GetTokensCCaddress(cpAssets, assetsTokensUnspendableAddr, GetUnspendable(cpAssets, NULL));
|
||||
SetCCunspents(unspentOutputsTokens, assetsTokensUnspendableAddr,true);
|
||||
|
||||
// tokenbids:
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itCoins = unspentOutputsCoins.begin();
|
||||
itCoins != unspentOutputsCoins.end();
|
||||
itCoins++)
|
||||
addOrders(cpAssets, itCoins);
|
||||
|
||||
// tokenasks:
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itTokens = unspentOutputsTokens.begin();
|
||||
itTokens != unspentOutputsTokens.end();
|
||||
itTokens++)
|
||||
addOrders(cpAssets, itTokens);
|
||||
|
||||
if (additionalEvalCode != 0) { //this would be mytokenorders
|
||||
char assetsDualEvalTokensUnspendableAddr[64];
|
||||
|
||||
// try also dual eval tokenasks (and we do not need bids):
|
||||
cpAssets->additionalTokensEvalcode2 = additionalEvalCode;
|
||||
GetTokensCCaddress(cpAssets, assetsDualEvalTokensUnspendableAddr, GetUnspendable(cpAssets, NULL));
|
||||
SetCCunspents(unspentOutputsDualEvalTokens, assetsDualEvalTokensUnspendableAddr,true);
|
||||
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itDualEvalTokens = unspentOutputsDualEvalTokens.begin();
|
||||
itDualEvalTokens != unspentOutputsDualEvalTokens.end();
|
||||
itDualEvalTokens++)
|
||||
addOrders(cpAssets, itDualEvalTokens);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
// not used (use TokenCreate instead)
|
||||
/* std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk; struct CCcontract_info *cp,C;
|
||||
if ( assetsupply < 0 )
|
||||
{
|
||||
fprintf(stderr,"negative assetsupply %lld\n",(long long)assetsupply);
|
||||
return("");
|
||||
}
|
||||
cp = CCinit(&C,EVAL_ASSETS);
|
||||
if ( name.size() > 32 || description.size() > 4096 )
|
||||
{
|
||||
fprintf(stderr,"name.%d or description.%d is too big\n",(int32_t)name.size(),(int32_t)description.size());
|
||||
return("");
|
||||
}
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
if ( AddNormalinputs(mtx,mypk,assetsupply+2*txfee,64) > 0 )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,assetsupply,mypk));
|
||||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetCreateOpRet('c',Mypubkey(),name,description)));
|
||||
}
|
||||
return("");
|
||||
} */
|
||||
|
||||
// not used (use TokenTransfer instead)
|
||||
/* std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk; uint64_t mask; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C;
|
||||
if ( total < 0 )
|
||||
{
|
||||
fprintf(stderr,"negative total %lld\n",(long long)total);
|
||||
return("");
|
||||
}
|
||||
cp = CCinit(&C,EVAL_ASSETS);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
|
||||
{
|
||||
//n = outputs.size();
|
||||
//if ( n == amounts.size() )
|
||||
//{
|
||||
// for (i=0; i<n; i++)
|
||||
// total += amounts[i];
|
||||
mask = ~((1LL << mtx.vin.size()) - 1);
|
||||
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,total,60)) > 0 )
|
||||
{
|
||||
|
||||
if (inputs < total) { //added dimxy
|
||||
std::cerr << "AssetTransfer(): insufficient funds" << std::endl;
|
||||
return ("");
|
||||
}
|
||||
if ( inputs > total )
|
||||
CCchange = (inputs - total);
|
||||
//for (i=0; i<n; i++)
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,total,pubkey2pk(destpubkey)));
|
||||
if ( CCchange != 0 )
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
|
||||
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey())));
|
||||
} else fprintf(stderr,"not enough CC asset inputs for %.8f\n",(double)total/COIN);
|
||||
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
|
||||
}
|
||||
return("");
|
||||
} */
|
||||
|
||||
// deprecated
|
||||
/* std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C;
|
||||
if ( total < 0 )
|
||||
{
|
||||
fprintf(stderr,"negative total %lld\n",(long long)total);
|
||||
return("");
|
||||
}
|
||||
cp = CCinit(&C,EVAL_ASSETS);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
|
||||
{
|
||||
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,total,60)) > 0 )
|
||||
{
|
||||
if ( inputs > total )
|
||||
CCchange = (inputs - total);
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
|
||||
mtx.vout.push_back(MakeCC1vout(evalcode,total,pubkey2pk(destpubkey)));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey())));
|
||||
} else fprintf(stderr,"not enough CC asset inputs for %.8f\n",(double)total/COIN);
|
||||
}
|
||||
return("");
|
||||
} */
|
||||
|
||||
// rpc tokenbid implementation, locks 'bidamount' coins for the 'pricetotal' of tokens
|
||||
std::string CreateBuyOffer(int64_t txfee, int64_t bidamount, uint256 assetid, int64_t pricetotal)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk;
|
||||
struct CCcontract_info *cpAssets, C;
|
||||
uint256 hashBlock;
|
||||
CTransaction vintx;
|
||||
std::vector<uint8_t> origpubkey;
|
||||
std::string name,description;
|
||||
int64_t inputs;
|
||||
|
||||
std::cerr << "CreateBuyOffer() bidamount=" << bidamount << " numtokens(pricetotal)=" << pricetotal << std::endl;
|
||||
|
||||
if (bidamount < 0 || pricetotal < 0)
|
||||
{
|
||||
fprintf(stderr,"negative bidamount %lld, pricetotal %lld\n", (long long)bidamount, (long long)pricetotal);
|
||||
return("");
|
||||
}
|
||||
if (myGetTransaction(assetid, vintx, hashBlock) == 0)
|
||||
{
|
||||
fprintf(stderr,"cant find assetid\n");
|
||||
return("");
|
||||
}
|
||||
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, origpubkey, name, description) == 0)
|
||||
{
|
||||
fprintf(stderr,"assetid isnt assetcreation txid\n");
|
||||
return("");
|
||||
}
|
||||
|
||||
cpAssets = CCinit(&C,EVAL_ASSETS); // NOTE: assets here!
|
||||
if (txfee == 0)
|
||||
txfee = 10000;
|
||||
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
|
||||
if ((inputs = AddNormalinputs(mtx, mypk, bidamount+(2*txfee), 64)) > 0)
|
||||
{
|
||||
std::cerr << "CreateBuyOffer() inputs=" << inputs << std::endl;
|
||||
if (inputs < bidamount+txfee) {
|
||||
std::cerr << "CreateBuyOffer(): insufficient coins to make buy offer" << std::endl;
|
||||
CCerror = strprintf("insufficient coins to make buy offer");
|
||||
return ("");
|
||||
}
|
||||
|
||||
CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0);
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendableAssetsPubkey));
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk));
|
||||
std::vector<CPubKey> voutTokenPubkeys; // should be empty - no token vouts
|
||||
|
||||
return FinalizeCCTx(0, cpAssets, mtx, mypk, txfee,
|
||||
EncodeTokenOpRet(assetid, voutTokenPubkeys, // TODO: actually this tx is not 'tokens', maybe it is better not to have token opret here but only asset opret.
|
||||
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('b', zeroid, pricetotal, Mypubkey())))); // But still such token opret should not make problems because no token eval in these vouts
|
||||
}
|
||||
CCerror = strprintf("no coins found to make buy offer");
|
||||
return("");
|
||||
}
|
||||
|
||||
// rpc tokenask implementation, locks 'askamount' tokens for the 'pricetotal'
|
||||
std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t pricetotal)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk;
|
||||
uint64_t mask;
|
||||
int64_t inputs, CCchange;
|
||||
struct CCcontract_info *cpAssets, assetsC;
|
||||
struct CCcontract_info *cpTokens, tokensC;
|
||||
|
||||
//std::cerr << "CreateSell() askamount=" << askamount << " pricetotal=" << pricetotal << std::endl;
|
||||
|
||||
if (askamount < 0 || pricetotal < 0) {
|
||||
fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount);
|
||||
return("");
|
||||
}
|
||||
|
||||
cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: for signing
|
||||
|
||||
|
||||
if (txfee == 0)
|
||||
txfee = 10000;
|
||||
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0)
|
||||
{
|
||||
std::vector<uint8_t> vopretNonfungible;
|
||||
mask = ~((1LL << mtx.vin.size()) - 1);
|
||||
// add single-eval tokens (or non-fungible tokens):
|
||||
cpTokens = CCinit(&tokensC, EVAL_TOKENS); // NOTE: adding inputs only from EVAL_TOKENS cc
|
||||
if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, askamount, 60, vopretNonfungible)) > 0)
|
||||
{
|
||||
if (inputs < askamount) {
|
||||
//was: askamount = inputs;
|
||||
std::cerr << "CreateSell(): insufficient tokens for ask" << std::endl;
|
||||
CCerror = strprintf("insufficient tokens for ask");
|
||||
return ("");
|
||||
}
|
||||
|
||||
// if this is non-fungible tokens:
|
||||
if( !vopretNonfungible.empty() )
|
||||
// set its evalcode
|
||||
cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
|
||||
|
||||
CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, NULL);
|
||||
mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, cpAssets->additionalTokensEvalcode2, askamount, unspendableAssetsPubkey));
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); //marker (seems, it is not for tokenorders)
|
||||
if (inputs > askamount)
|
||||
CCchange = (inputs - askamount);
|
||||
if (CCchange != 0)
|
||||
// change to single-eval or non-fungible token vout (although for non-fungible token change currently is not possible)
|
||||
mtx.vout.push_back(MakeTokensCC1vout((cpAssets->additionalTokensEvalcode2) ? cpAssets->additionalTokensEvalcode2 : EVAL_TOKENS, CCchange, mypk));
|
||||
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
voutTokenPubkeys.push_back(unspendableAssetsPubkey);
|
||||
|
||||
return FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee,
|
||||
EncodeTokenOpRet(assetid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey()))));
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "need some tokens to place ask\n");
|
||||
}
|
||||
}
|
||||
else { // dimxy added 'else', because it was misleading message before
|
||||
fprintf(stderr, "need some native coins to place ask\n");
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
|
||||
std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 assetid2,int64_t pricetotal)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk; uint64_t mask; int64_t inputs,CCchange; CScript opret; struct CCcontract_info *cp,C;
|
||||
|
||||
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
|
||||
fprintf(stderr,"asset swaps disabled\n");
|
||||
return("");
|
||||
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
|
||||
|
||||
if ( askamount < 0 || pricetotal < 0 )
|
||||
{
|
||||
fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount);
|
||||
return("");
|
||||
}
|
||||
cp = CCinit(&C, EVAL_ASSETS);
|
||||
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
|
||||
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
|
||||
{
|
||||
mask = ~((1LL << mtx.vin.size()) - 1);
|
||||
/*if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0)
|
||||
{
|
||||
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
|
||||
if (inputs < askamount) {
|
||||
//was: askamount = inputs;
|
||||
std::cerr << "CreateSwap(): insufficient tokens for ask" << std::endl;
|
||||
CCerror = strprintf("insufficient tokens for ask");
|
||||
return ("");
|
||||
}
|
||||
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
|
||||
CPubKey unspendablePubkey = GetUnspendable(cp, 0);
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, askamount, unspendablePubkey));
|
||||
|
||||
if (inputs > askamount)
|
||||
CCchange = (inputs - askamount);
|
||||
if (CCchange != 0)
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk));
|
||||
|
||||
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
|
||||
std::vector<CPubKey> voutTokenPubkeys; // should be empty - no token vouts
|
||||
|
||||
if (assetid2 == zeroid) {
|
||||
opret = EncodeTokenOpRet(assetid, voutTokenPubkeys,
|
||||
EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey()));
|
||||
}
|
||||
else {
|
||||
opret = EncodeTokenOpRet(assetid, voutTokenPubkeys,
|
||||
EncodeAssetOpRet('e', assetid2, pricetotal, Mypubkey()));
|
||||
}
|
||||
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
|
||||
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret));
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "need some assets to place ask\n");
|
||||
} */
|
||||
}
|
||||
else { // dimxy added 'else', because it was misleading message before
|
||||
fprintf(stderr,"need some native coins to place ask\n");
|
||||
}
|
||||
|
||||
return("");
|
||||
} ////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
|
||||
|
||||
// unlocks coins
|
||||
std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CTransaction vintx; uint64_t mask;
|
||||
uint256 hashBlock; int64_t bidamount;
|
||||
CPubKey mypk; struct CCcontract_info *cpAssets, C;
|
||||
uint8_t funcid,dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector<uint8_t> dummyOrigpubkey;
|
||||
|
||||
cpAssets = CCinit(&C, EVAL_ASSETS);
|
||||
|
||||
if (txfee == 0)
|
||||
txfee = 10000;
|
||||
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
|
||||
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
|
||||
{
|
||||
mask = ~((1LL << mtx.vin.size()) - 1);
|
||||
if (myGetTransaction(bidtxid, vintx, hashBlock) != 0)
|
||||
{
|
||||
std::vector<uint8_t> vopretNonfungible;
|
||||
GetNonfungibleData(assetid, vopretNonfungible);
|
||||
|
||||
bidamount = vintx.vout[0].nValue;
|
||||
mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets
|
||||
|
||||
if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0)
|
||||
{
|
||||
if (funcid == 's') mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b'
|
||||
else if (funcid=='S') mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B'
|
||||
}
|
||||
|
||||
mtx.vout.push_back(CTxOut(bidamount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
|
||||
std::vector<CPubKey> voutTokenPubkeys; // should be empty, no token vouts
|
||||
|
||||
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
|
||||
EncodeTokenOpRet(assetid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('o', zeroid, 0, Mypubkey())))));
|
||||
}
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
//unlocks tokens
|
||||
std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CTransaction vintx; uint64_t mask;
|
||||
uint256 hashBlock; int64_t askamount;
|
||||
CPubKey mypk;
|
||||
struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC;
|
||||
uint8_t funcid, dummyEvalCode;
|
||||
uint256 dummyAssetid, dummyAssetid2;
|
||||
int64_t dummyPrice;
|
||||
std::vector<uint8_t> dummyOrigpubkey;
|
||||
|
||||
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
|
||||
|
||||
if (txfee == 0)
|
||||
txfee = 10000;
|
||||
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
|
||||
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
|
||||
{
|
||||
mask = ~((1LL << mtx.vin.size()) - 1);
|
||||
if (myGetTransaction(asktxid, vintx, hashBlock) != 0)
|
||||
{
|
||||
std::vector<uint8_t> vopretNonfungible;
|
||||
GetNonfungibleData(assetid, vopretNonfungible);
|
||||
|
||||
askamount = vintx.vout[0].nValue;
|
||||
mtx.vin.push_back(CTxIn(asktxid, 0, CScript()));
|
||||
|
||||
if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0)
|
||||
{
|
||||
if (funcid == 's')
|
||||
mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s'
|
||||
else if (funcid=='S')
|
||||
mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S'
|
||||
}
|
||||
|
||||
if (vopretNonfungible.size() > 0)
|
||||
cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
|
||||
|
||||
mtx.vout.push_back(MakeTokensCC1vout(cpAssets->additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : cpAssets->additionalTokensEvalcode2, askamount, mypk)); // one-eval token vout
|
||||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
voutTokenPubkeys.push_back(mypk);
|
||||
|
||||
// this is only for unspendable addresses:
|
||||
//CCaddr2set(cpTokens, EVAL_ASSETS, mypk, myPrivkey, myCCaddr); //do we need this? Seems FinalizeCCTx can attach to any evalcode cc addr by calling Getscriptaddress
|
||||
|
||||
uint8_t unspendableAssetsPrivkey[32];
|
||||
char unspendableAssetsAddr[64];
|
||||
// init assets 'unspendable' privkey and pubkey
|
||||
CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey);
|
||||
GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk);
|
||||
|
||||
// add additional eval-tokens unspendable assets privkey:
|
||||
CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
|
||||
|
||||
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
|
||||
EncodeTokenOpRet(assetid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('x', zeroid, 0, Mypubkey())))));
|
||||
}
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
//send tokens, receive coins:
|
||||
std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CTransaction vintx;
|
||||
uint256 hashBlock;
|
||||
CPubKey mypk;
|
||||
std::vector<uint8_t> origpubkey;
|
||||
int32_t bidvout=0;
|
||||
uint64_t mask;
|
||||
int64_t origprice, bidamount, paid_amount, remaining_required, inputs, CCchange=0;
|
||||
struct CCcontract_info *cpTokens, tokensC;
|
||||
struct CCcontract_info *cpAssets, assetsC;
|
||||
|
||||
if (fillamount < 0)
|
||||
{
|
||||
fprintf(stderr,"negative fillamount %lld\n", (long long)fillamount);
|
||||
return("");
|
||||
}
|
||||
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
|
||||
|
||||
if (txfee == 0)
|
||||
txfee = 10000;
|
||||
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
|
||||
if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0)
|
||||
{
|
||||
mask = ~((1LL << mtx.vin.size()) - 1);
|
||||
if (myGetTransaction(bidtxid, vintx, hashBlock) != 0)
|
||||
{
|
||||
bidamount = vintx.vout[bidvout].nValue;
|
||||
SetAssetOrigpubkey(origpubkey, origprice, vintx);
|
||||
|
||||
mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable
|
||||
|
||||
std::vector<uint8_t> vopretNonfungible;
|
||||
if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60, vopretNonfungible)) > 0)
|
||||
{
|
||||
if (inputs < fillamount) {
|
||||
std::cerr << "FillBuyOffer(): insufficient tokens to fill buy offer" << std::endl;
|
||||
CCerror = strprintf("insufficient tokens to fill buy offer");
|
||||
return ("");
|
||||
}
|
||||
|
||||
SetBidFillamounts(paid_amount, remaining_required, bidamount, fillamount, origprice);
|
||||
|
||||
uint8_t additionalTokensEvalcode2 = 0;
|
||||
if (vopretNonfungible.size() > 0)
|
||||
additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
|
||||
|
||||
if (inputs > fillamount)
|
||||
CCchange = (inputs - fillamount);
|
||||
|
||||
uint8_t unspendableAssetsPrivkey[32];
|
||||
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
|
||||
CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey);
|
||||
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder
|
||||
mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // vout1 coins to normal
|
||||
mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, fillamount, pubkey2pk(origpubkey))); // vout2 single-eval tokens sent to the originator
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, origpubkey)); // vout3 marker to origpubkey
|
||||
|
||||
if (CCchange != 0)
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // vout4 change in single-eval tokens
|
||||
|
||||
fprintf(stderr,"FillBuyOffer() remaining %llu -> origpubkey\n", (long long)remaining_required);
|
||||
|
||||
char unspendableAssetsAddr[64];
|
||||
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
|
||||
GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk);
|
||||
|
||||
// add additional unspendable addr from Assets:
|
||||
CCaddr2set(cpTokens, EVAL_ASSETS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
|
||||
|
||||
// token vout verification pubkeys:
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
voutTokenPubkeys.push_back(pubkey2pk(origpubkey));
|
||||
|
||||
return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee,
|
||||
EncodeTokenOpRet(assetid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey)))));
|
||||
} else return("dont have any assets to fill bid");
|
||||
}
|
||||
}
|
||||
return("no normal coins left");
|
||||
}
|
||||
|
||||
|
||||
// send coins, receive tokens
|
||||
std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 asktxid, int64_t fillunits)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CTransaction vintx,filltx;
|
||||
uint256 hashBlock;
|
||||
CPubKey mypk;
|
||||
std::vector<uint8_t> origpubkey;
|
||||
double dprice;
|
||||
uint64_t mask = 0;
|
||||
int32_t askvout = 0;
|
||||
int64_t received_assetoshis, total_nValue, orig_assetoshis, paid_nValue, remaining_nValue, inputs, CCchange=0;
|
||||
//struct CCcontract_info *cpTokens, tokensC;
|
||||
struct CCcontract_info *cpAssets, assetsC;
|
||||
|
||||
if (fillunits < 0)
|
||||
{
|
||||
CCerror = strprintf("negative fillunits %lld\n",(long long)fillunits);
|
||||
fprintf(stderr,"%s\n",CCerror.c_str());
|
||||
return("");
|
||||
}
|
||||
if (assetid2 != zeroid)
|
||||
{
|
||||
CCerror = "asset swaps disabled";
|
||||
fprintf(stderr,"%s\n",CCerror.c_str());
|
||||
return("");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> vopretNonfungible;
|
||||
uint8_t additionalTokensEvalcode2 = 0;
|
||||
GetNonfungibleData(assetid, vopretNonfungible);
|
||||
if (vopretNonfungible.size() > 0)
|
||||
additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
|
||||
|
||||
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
|
||||
|
||||
if (txfee == 0)
|
||||
txfee = 10000;
|
||||
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
//if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0)
|
||||
//{
|
||||
//mask = ~((1LL << mtx.vin.size()) - 1);
|
||||
if (myGetTransaction(asktxid, vintx, hashBlock) != 0)
|
||||
{
|
||||
orig_assetoshis = vintx.vout[askvout].nValue;
|
||||
SetAssetOrigpubkey(origpubkey, total_nValue, vintx);
|
||||
dprice = (double)total_nValue / orig_assetoshis;
|
||||
paid_nValue = dprice * fillunits;
|
||||
|
||||
if (assetid2 != zeroid) {
|
||||
inputs = 0; // = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet
|
||||
}
|
||||
else
|
||||
{
|
||||
inputs = AddNormalinputs(mtx, mypk, 2 * txfee + paid_nValue, 60); // Better to use single AddNormalinputs() to allow payment if user has only single utxo with normal funds
|
||||
mask = ~((1LL << mtx.vin.size()) - 1);
|
||||
}
|
||||
if (inputs > 0)
|
||||
{
|
||||
if (inputs < paid_nValue) {
|
||||
std::cerr << "FillSell(): insufficient coins to fill sell" << std::endl;
|
||||
CCerror = strprintf("insufficient coins to fill sell");
|
||||
return ("");
|
||||
}
|
||||
|
||||
// cc vin should be after normal vin
|
||||
mtx.vin.push_back(CTxIn(asktxid, askvout, CScript()));
|
||||
|
||||
if (assetid2 != zeroid)
|
||||
SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); //not implemented correctly yet
|
||||
else
|
||||
SetAskFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue);
|
||||
|
||||
if (assetid2 != zeroid && inputs > paid_nValue)
|
||||
CCchange = (inputs - paid_nValue);
|
||||
|
||||
// vout.0 tokens remainder to unspendable cc addr:
|
||||
mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, additionalTokensEvalcode2, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL)));
|
||||
//vout.1 purchased tokens to self token single-eval or dual-eval token+nonfungible cc addr:
|
||||
mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, received_assetoshis, mypk));
|
||||
|
||||
if (assetid2 != zeroid) {
|
||||
std::cerr << "FillSell() WARNING: asset swap not implemented yet! (paid_nValue)" << std::endl;
|
||||
// TODO: change MakeCC1vout appropriately when implementing:
|
||||
//mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, paid_nValue, origpubkey)); //vout.2 tokens... (swap is not implemented yet)
|
||||
}
|
||||
else {
|
||||
//std::cerr << "FillSell() paid_value=" << paid_nValue << " origpubkey=" << HexStr(pubkey2pk(origpubkey)) << std::endl;
|
||||
mtx.vout.push_back(CTxOut(paid_nValue, CScript() << origpubkey << OP_CHECKSIG)); //vout.2 coins to tokens seller's normal addr
|
||||
}
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,txfee,origpubkey)); //vout.3 marker to origpubkey
|
||||
|
||||
// not implemented
|
||||
if (CCchange != 0) {
|
||||
std::cerr << "FillSell() WARNING: asset swap not implemented yet! (CCchange)" << std::endl;
|
||||
// TODO: change MakeCC1vout appropriately when implementing:
|
||||
//mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); //vout.3 coins in Assets cc addr (swap not implemented)
|
||||
}
|
||||
|
||||
uint8_t unspendableAssetsPrivkey[32];
|
||||
char unspendableAssetsAddr[64];
|
||||
// init assets 'unspendable' privkey and pubkey
|
||||
CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey);
|
||||
GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk);
|
||||
|
||||
// add additional eval-tokens unspendable assets privkey:
|
||||
CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
|
||||
|
||||
// vout verification pubkeys:
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
voutTokenPubkeys.push_back(mypk);
|
||||
|
||||
cpAssets->additionalTokensEvalcode2 = additionalTokensEvalcode2;
|
||||
|
||||
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
|
||||
EncodeTokenOpRet(assetid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey)))));
|
||||
} else {
|
||||
CCerror = strprintf("filltx not enough utxos");
|
||||
fprintf(stderr,"%s\n", CCerror.c_str());
|
||||
}
|
||||
}
|
||||
//}
|
||||
return("");
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
#ifndef CC_CHANNELS_H
|
||||
#define CC_CHANNELS_H
|
||||
|
||||
#include "CCinclude.h"
|
||||
#define CHANNELS_MAXPAYMENTS 1000
|
||||
|
||||
bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
|
||||
UniValue ChannelOpen(const CPubKey& pk,uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 tokenid);
|
||||
UniValue ChannelPayment(const CPubKey& pk,uint64_t txfee,uint256 opentxid,int64_t amount, uint256 secret);
|
||||
UniValue ChannelClose(const CPubKey& pk,uint64_t txfee,uint256 opentxid);
|
||||
UniValue ChannelRefund(const CPubKey& pk,uint64_t txfee,uint256 opentxid,uint256 closetxid);
|
||||
UniValue ChannelsList(const CPubKey& pk);
|
||||
// CCcustom
|
||||
UniValue ChannelsInfo(const CPubKey& pk,uint256 opentxid);
|
||||
|
||||
#endif
|
||||
@@ -20,20 +20,13 @@
|
||||
#include "CCinclude.h"
|
||||
#include "CCassets.h"
|
||||
#include "CCfaucet.h"
|
||||
#include "CCrewards.h"
|
||||
#include "CCdice.h"
|
||||
#include "CCauction.h"
|
||||
#include "CClotto.h"
|
||||
#include "CCfsm.h"
|
||||
#include "CCHeir.h"
|
||||
#include "CCchannels.h"
|
||||
#include "CCOracles.h"
|
||||
#include "CCPrices.h"
|
||||
#include "CCPegs.h"
|
||||
#include "CCPayments.h"
|
||||
#include "CCGateways.h"
|
||||
#include "CCtokens.h"
|
||||
#include "CCImportGateway.h"
|
||||
|
||||
/*
|
||||
CCcustom has most of the functions that need to be extended to create a new CC contract.
|
||||
@@ -302,80 +295,80 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
|
||||
strcpy(cp->normaladdr,AssetsNormaladdr);
|
||||
strcpy(cp->CChexstr,AssetsCChexstr);
|
||||
memcpy(cp->CCpriv,AssetsCCpriv,32);
|
||||
cp->validate = AssetsValidate;
|
||||
cp->ismyvin = IsAssetsInput;
|
||||
//cp->validate = AssetsValidate;
|
||||
//cp->ismyvin = IsAssetsInput;
|
||||
break;
|
||||
case EVAL_FAUCET:
|
||||
strcpy(cp->unspendableCCaddr,FaucetCCaddr);
|
||||
strcpy(cp->normaladdr,FaucetNormaladdr);
|
||||
strcpy(cp->CChexstr,FaucetCChexstr);
|
||||
memcpy(cp->CCpriv,FaucetCCpriv,32);
|
||||
cp->validate = FaucetValidate;
|
||||
cp->ismyvin = IsFaucetInput;
|
||||
//cp->validate = FaucetValidate;
|
||||
//cp->ismyvin = IsFaucetInput;
|
||||
break;
|
||||
case EVAL_REWARDS:
|
||||
strcpy(cp->unspendableCCaddr,RewardsCCaddr);
|
||||
strcpy(cp->normaladdr,RewardsNormaladdr);
|
||||
strcpy(cp->CChexstr,RewardsCChexstr);
|
||||
memcpy(cp->CCpriv,RewardsCCpriv,32);
|
||||
cp->validate = RewardsValidate;
|
||||
cp->ismyvin = IsRewardsInput;
|
||||
//cp->validate = RewardsValidate;
|
||||
//cp->ismyvin = IsRewardsInput;
|
||||
break;
|
||||
case EVAL_DICE:
|
||||
strcpy(cp->unspendableCCaddr,DiceCCaddr);
|
||||
strcpy(cp->normaladdr,DiceNormaladdr);
|
||||
strcpy(cp->CChexstr,DiceCChexstr);
|
||||
memcpy(cp->CCpriv,DiceCCpriv,32);
|
||||
cp->validate = DiceValidate;
|
||||
cp->ismyvin = IsDiceInput;
|
||||
//cp->validate = DiceValidate;
|
||||
//cp->ismyvin = IsDiceInput;
|
||||
break;
|
||||
case EVAL_LOTTO:
|
||||
strcpy(cp->unspendableCCaddr,LottoCCaddr);
|
||||
strcpy(cp->normaladdr,LottoNormaladdr);
|
||||
strcpy(cp->CChexstr,LottoCChexstr);
|
||||
memcpy(cp->CCpriv,LottoCCpriv,32);
|
||||
cp->validate = LottoValidate;
|
||||
cp->ismyvin = IsLottoInput;
|
||||
//cp->validate = LottoValidate;
|
||||
//cp->ismyvin = IsLottoInput;
|
||||
break;
|
||||
case EVAL_FSM:
|
||||
strcpy(cp->unspendableCCaddr,FSMCCaddr);
|
||||
strcpy(cp->normaladdr,FSMNormaladdr);
|
||||
strcpy(cp->CChexstr,FSMCChexstr);
|
||||
memcpy(cp->CCpriv,FSMCCpriv,32);
|
||||
cp->validate = FSMValidate;
|
||||
cp->ismyvin = IsFSMInput;
|
||||
//cp->validate = FSMValidate;
|
||||
//cp->ismyvin = IsFSMInput;
|
||||
break;
|
||||
case EVAL_AUCTION:
|
||||
strcpy(cp->unspendableCCaddr,AuctionCCaddr);
|
||||
strcpy(cp->normaladdr,AuctionNormaladdr);
|
||||
strcpy(cp->CChexstr,AuctionCChexstr);
|
||||
memcpy(cp->CCpriv,AuctionCCpriv,32);
|
||||
cp->validate = AuctionValidate;
|
||||
cp->ismyvin = IsAuctionInput;
|
||||
//cp->validate = AuctionValidate;
|
||||
//cp->ismyvin = IsAuctionInput;
|
||||
break;
|
||||
case EVAL_HEIR:
|
||||
strcpy(cp->unspendableCCaddr,HeirCCaddr);
|
||||
strcpy(cp->normaladdr,HeirNormaladdr);
|
||||
strcpy(cp->CChexstr,HeirCChexstr);
|
||||
memcpy(cp->CCpriv,HeirCCpriv,32);
|
||||
cp->validate = HeirValidate;
|
||||
cp->ismyvin = IsHeirInput;
|
||||
//cp->validate = HeirValidate;
|
||||
//cp->ismyvin = IsHeirInput;
|
||||
break;
|
||||
case EVAL_CHANNELS:
|
||||
strcpy(cp->unspendableCCaddr,ChannelsCCaddr);
|
||||
strcpy(cp->normaladdr,ChannelsNormaladdr);
|
||||
strcpy(cp->CChexstr,ChannelsCChexstr);
|
||||
memcpy(cp->CCpriv,ChannelsCCpriv,32);
|
||||
cp->validate = ChannelsValidate;
|
||||
cp->ismyvin = IsChannelsInput;
|
||||
//cp->validate = ChannelsValidate;
|
||||
//cp->ismyvin = IsChannelsInput;
|
||||
break;
|
||||
case EVAL_ORACLES:
|
||||
strcpy(cp->unspendableCCaddr,OraclesCCaddr);
|
||||
strcpy(cp->normaladdr,OraclesNormaladdr);
|
||||
strcpy(cp->CChexstr,OraclesCChexstr);
|
||||
memcpy(cp->CCpriv,OraclesCCpriv,32);
|
||||
cp->validate = OraclesValidate;
|
||||
cp->ismyvin = IsOraclesInput;
|
||||
//cp->validate = OraclesValidate;
|
||||
//cp->ismyvin = IsOraclesInput;
|
||||
break;
|
||||
case EVAL_PRICES:
|
||||
strcpy(cp->unspendableCCaddr,PricesCCaddr);
|
||||
@@ -398,16 +391,16 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
|
||||
strcpy(cp->normaladdr,PaymentsNormaladdr);
|
||||
strcpy(cp->CChexstr,PaymentsCChexstr);
|
||||
memcpy(cp->CCpriv,PaymentsCCpriv,32);
|
||||
cp->validate = PaymentsValidate;
|
||||
cp->ismyvin = IsPaymentsInput;
|
||||
//cp->validate = PaymentsValidate;
|
||||
//cp->ismyvin = IsPaymentsInput;
|
||||
break;
|
||||
case EVAL_GATEWAYS:
|
||||
strcpy(cp->unspendableCCaddr,GatewaysCCaddr);
|
||||
strcpy(cp->normaladdr,GatewaysNormaladdr);
|
||||
strcpy(cp->CChexstr,GatewaysCChexstr);
|
||||
memcpy(cp->CCpriv,GatewaysCCpriv,32);
|
||||
cp->validate = GatewaysValidate;
|
||||
cp->ismyvin = IsGatewaysInput;
|
||||
//cp->validate = GatewaysValidate;
|
||||
//cp->ismyvin = IsGatewaysInput;
|
||||
break;
|
||||
|
||||
case EVAL_TOKENS:
|
||||
@@ -415,16 +408,16 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
|
||||
strcpy(cp->normaladdr, TokensNormaladdr);
|
||||
strcpy(cp->CChexstr, TokensCChexstr);
|
||||
memcpy(cp->CCpriv, TokensCCpriv, 32);
|
||||
cp->validate = TokensValidate;
|
||||
cp->ismyvin = IsTokensInput;
|
||||
//cp->validate = TokensValidate;
|
||||
//cp->ismyvin = IsTokensInput;
|
||||
break;
|
||||
case EVAL_IMPORTGATEWAY:
|
||||
strcpy(cp->unspendableCCaddr, ImportGatewayCCaddr);
|
||||
strcpy(cp->normaladdr, ImportGatewayNormaladdr);
|
||||
strcpy(cp->CChexstr, ImportGatewayCChexstr);
|
||||
memcpy(cp->CCpriv, ImportGatewayCCpriv, 32);
|
||||
cp->validate = ImportGatewayValidate;
|
||||
cp->ismyvin = IsImportGatewayInput;
|
||||
//cp->validate = ImportGatewayValidate;
|
||||
//cp->ismyvin = IsImportGatewayInput;
|
||||
break;
|
||||
default:
|
||||
if ( CClib_initcp(cp,evalcode) < 0 )
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
#ifndef CC_DICE_H
|
||||
#define CC_DICE_H
|
||||
|
||||
#include "CCinclude.h"
|
||||
|
||||
#define EVAL_DICE 0xe6
|
||||
|
||||
bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
|
||||
|
||||
std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds);
|
||||
std::string DiceBetFinish(uint8_t &funcid,uint256 &entropyused,int32_t &entropyvout,int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout,uint256 vin0txid,int32_t vin0vout);
|
||||
double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid);
|
||||
std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks);
|
||||
std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount);
|
||||
UniValue DiceInfo(uint256 diceid);
|
||||
UniValue DiceList();
|
||||
int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid, int32_t &entropytxs,bool random);
|
||||
|
||||
#endif
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
#ifndef CC_LOTTO_H
|
||||
#define CC_LOTTO_H
|
||||
|
||||
#include "CCinclude.h"
|
||||
|
||||
#define EVAL_LOTTO 0xe9
|
||||
|
||||
bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
|
||||
|
||||
UniValue LottoInfo(uint256 lottoid);
|
||||
UniValue LottoList();
|
||||
std::string LottoTicket(uint64_t txfee,int64_t numtickets);
|
||||
std::string LottoWinner(uint64_t txfee);
|
||||
|
||||
#endif
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
#ifndef CC_REWARDS_H
|
||||
#define CC_REWARDS_H
|
||||
|
||||
#include "CCinclude.h"
|
||||
#include <gmp.h>
|
||||
|
||||
#define EVAL_REWARDS 0xe5
|
||||
#define REWARDSCC_MAXAPR (COIN * 25)
|
||||
|
||||
bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
|
||||
UniValue RewardsInfo(uint256 rewardid);
|
||||
UniValue RewardsList();
|
||||
|
||||
std::string RewardsCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t APR,int64_t minseconds,int64_t maxseconds,int64_t mindeposit);
|
||||
std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount);
|
||||
std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount);
|
||||
std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid);
|
||||
|
||||
#endif
|
||||
1059
src/cc/CCtokens.cpp
1059
src/cc/CCtokens.cpp
File diff suppressed because it is too large
Load Diff
492
src/cc/CCtx.cpp
492
src/cc/CCtx.cpp
@@ -46,350 +46,14 @@ By using -addressindex=1, it allows tracking of all the CC addresses
|
||||
*/
|
||||
std::string FinalizeCCTx(uint64_t CCmask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret, std::vector<CPubKey> pubkeys)
|
||||
{
|
||||
UniValue sigData = FinalizeCCTxExt(false, CCmask, cp, mtx, mypk, txfee, opret, pubkeys);
|
||||
return sigData[JSON_HEXTX].getValStr();
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
|
||||
// extended version that supports signInfo object with conds to vins map for remote cc calls
|
||||
UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret, std::vector<CPubKey> pubkeys)
|
||||
{
|
||||
auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
|
||||
CTransaction vintx; std::string hex; CPubKey globalpk; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0;
|
||||
int64_t utxovalues[CC_MAXVINS],change,normalinputs=0,totaloutputs=0,normaloutputs=0,totalinputs=0,normalvins=0,ccvins=0;
|
||||
int32_t i,flag,mgret,utxovout,n,err = 0;
|
||||
char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], unspendabletokensaddr[64],CC1of2CCaddr[64];
|
||||
uint8_t *privkey = NULL, myprivkey[32] = { '\0' }, unspendablepriv[32] = { '\0' }, /*tokensunspendablepriv[32],*/ *msg32 = 0;
|
||||
CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond=0, *condCC2=0,*mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL;
|
||||
CPubKey unspendablepk /*, tokensunspendablepk*/;
|
||||
struct CCcontract_info *cpTokens, tokensC;
|
||||
UniValue sigData(UniValue::VARR),result(UniValue::VOBJ);
|
||||
const UniValue sigDataNull = NullUniValue;
|
||||
|
||||
globalpk = GetUnspendable(cp,0);
|
||||
n = mtx.vout.size();
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
if ( mtx.vout[i].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
normaloutputs += mtx.vout[i].nValue;
|
||||
totaloutputs += mtx.vout[i].nValue;
|
||||
}
|
||||
if ( (n= mtx.vin.size()) > CC_MAXVINS )
|
||||
{
|
||||
fprintf(stderr,"FinalizeCCTx: %d is too many vins\n",n);
|
||||
result.push_back(Pair(JSON_HEXTX, "0"));
|
||||
return result;
|
||||
}
|
||||
|
||||
//Myprivkey(myprivkey); // for NSPV mode we need to add myprivkey for the explicitly defined mypk param
|
||||
#ifdef ENABLE_WALLET
|
||||
// get privkey for mypk
|
||||
CKeyID keyID = mypk.GetID();
|
||||
CKey vchSecret;
|
||||
if (pwalletMain->GetKey(keyID, vchSecret))
|
||||
memcpy(myprivkey, vchSecret.begin(), sizeof(myprivkey));
|
||||
#endif
|
||||
|
||||
GetCCaddress(cp,myaddr,mypk);
|
||||
mycond = MakeCCcond1(cp->evalcode,mypk);
|
||||
|
||||
// to spend from single-eval evalcode 'unspendable' cc addr
|
||||
unspendablepk = GetUnspendable(cp, unspendablepriv);
|
||||
GetCCaddress(cp, unspendable, unspendablepk);
|
||||
othercond = MakeCCcond1(cp->evalcode, unspendablepk);
|
||||
GetCCaddress1of2(cp,CC1of2CCaddr,unspendablepk,unspendablepk);
|
||||
|
||||
//fprintf(stderr,"evalcode.%d (%s)\n",cp->evalcode,unspendable);
|
||||
|
||||
// tokens support:
|
||||
// to spend from dual/three-eval mypk vout
|
||||
GetTokensCCaddress(cp, mytokensaddr, mypk);
|
||||
// NOTE: if additionalEvalcode2 is not set it is a dual-eval (not three-eval) cc cond:
|
||||
mytokenscond = MakeTokensCCcond1(cp->evalcode, cp->additionalTokensEvalcode2, mypk);
|
||||
|
||||
// to spend from single-eval EVAL_TOKENS mypk
|
||||
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
|
||||
GetCCaddress(cpTokens, mysingletokensaddr, mypk);
|
||||
mysingletokenscond = MakeCCcond1(EVAL_TOKENS, mypk);
|
||||
|
||||
// to spend from dual/three-eval EVAL_TOKEN+evalcode 'unspendable' pk:
|
||||
GetTokensCCaddress(cp, unspendabletokensaddr, unspendablepk); // it may be a three-eval cc, if cp->additionalEvalcode2 is set
|
||||
othertokenscond = MakeTokensCCcond1(cp->evalcode, cp->additionalTokensEvalcode2, unspendablepk);
|
||||
|
||||
//Reorder vins so that for multiple normal vins all other except vin0 goes to the end
|
||||
//This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation.
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
if (i==0 && mtx.vin[i].prevout.n==10e8)
|
||||
continue;
|
||||
if ( myGetTransaction(mtx.vin[i].prevout.hash,vintx,hashBlock) != 0 && mtx.vin[i].prevout.n < vintx.vout.size() )
|
||||
{
|
||||
if ( vintx.vout[mtx.vin[i].prevout.n].scriptPubKey.IsPayToCryptoCondition() == 0 && ccvins==0)
|
||||
normalvins++;
|
||||
else ccvins++;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr,"vin.%d vout.%d is bigger than vintx.%d\n",i,mtx.vin[i].prevout.n,(int32_t)vintx.vout.size());
|
||||
memset(myprivkey,0,32);
|
||||
return UniValue(UniValue::VOBJ);
|
||||
}
|
||||
}
|
||||
if (normalvins>1 && ccvins)
|
||||
{
|
||||
for(i=1;i<normalvins;i++)
|
||||
{
|
||||
mtx.vin.push_back(mtx.vin[1]);
|
||||
mtx.vin.erase(mtx.vin.begin() + 1);
|
||||
}
|
||||
}
|
||||
memset(utxovalues,0,sizeof(utxovalues));
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
if (i==0 && mtx.vin[i].prevout.n==10e8) continue;
|
||||
if ( (mgret= myGetTransaction(mtx.vin[i].prevout.hash,vintx,hashBlock)) != 0 )
|
||||
{
|
||||
utxovout = mtx.vin[i].prevout.n;
|
||||
utxovalues[i] = vintx.vout[utxovout].nValue;
|
||||
totalinputs += utxovalues[i];
|
||||
if ( vintx.vout[utxovout].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
{
|
||||
//fprintf(stderr,"vin.%d is normal %.8f\n",i,(double)utxovalues[i]/COIN);
|
||||
normalinputs += utxovalues[i];
|
||||
vinimask |= (1LL << i);
|
||||
}
|
||||
else
|
||||
{
|
||||
mask |= (1LL << i);
|
||||
}
|
||||
} else fprintf(stderr,"FinalizeCCTx couldnt find %s mgret.%d\n",mtx.vin[i].prevout.hash.ToString().c_str(),mgret);
|
||||
}
|
||||
nmask = (1LL << n) - 1;
|
||||
if ( 0 && (mask & nmask) != (CCmask & nmask) )
|
||||
fprintf(stderr,"mask.%llx vs CCmask.%llx %llx %llx %llx\n",(long long)(mask & nmask),(long long)(CCmask & nmask),(long long)mask,(long long)CCmask,(long long)nmask);
|
||||
if ( totalinputs >= totaloutputs+2*txfee )
|
||||
{
|
||||
change = totalinputs - (totaloutputs+txfee);
|
||||
mtx.vout.push_back(CTxOut(change,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
}
|
||||
if ( opret.size() > 0 )
|
||||
mtx.vout.push_back(CTxOut(0,opret));
|
||||
PrecomputedTransactionData txdata(mtx);
|
||||
n = mtx.vin.size();
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
if (i==0 && mtx.vin[i].prevout.n==10e8)
|
||||
continue;
|
||||
if ( (mgret= myGetTransaction(mtx.vin[i].prevout.hash,vintx,hashBlock)) != 0 )
|
||||
{
|
||||
utxovout = mtx.vin[i].prevout.n;
|
||||
if ( vintx.vout[utxovout].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
{
|
||||
if ( HUSH_NSPV_FULLNODE )
|
||||
{
|
||||
if (!remote)
|
||||
{
|
||||
if (SignTx(mtx, i, vintx.vout[utxovout].nValue, vintx.vout[utxovout].scriptPubKey) == 0)
|
||||
fprintf(stderr, "signing error for vini.%d of %llx\n", i, (long long)vinimask);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if no myprivkey for mypk it means remote call from nspv superlite client
|
||||
// add sigData for superlite client
|
||||
UniValue cc(UniValue::VNULL);
|
||||
AddSigData2UniValue(sigData, i, cc, HexStr(vintx.vout[utxovout].scriptPubKey), vintx.vout[utxovout].nValue ); // store vin i with scriptPubKey
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
char addr[64];
|
||||
Getscriptaddress(addr,vintx.vout[utxovout].scriptPubKey);
|
||||
fprintf(stderr,"vout[%d] %.8f -> %s\n",utxovout,dstr(vintx.vout[utxovout].nValue),addr);
|
||||
}
|
||||
if ( NSPV_SignTx(mtx,i,vintx.vout[utxovout].nValue,vintx.vout[utxovout].scriptPubKey,0) == 0 )
|
||||
fprintf(stderr,"NSPV signing error for vini.%d of %llx\n",i,(long long)vinimask);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Getscriptaddress(destaddr,vintx.vout[utxovout].scriptPubKey);
|
||||
//fprintf(stderr,"FinalizeCCTx() vin.%d is CC %.8f -> (%s) vs %s\n",i,(double)utxovalues[i]/COIN,destaddr,mysingletokensaddr);
|
||||
//std::cerr << "FinalizeCCtx() searching destaddr=" << destaddr << " for vin[" << i << "] satoshis=" << utxovalues[i] << std::endl;
|
||||
if( strcmp(destaddr, myaddr) == 0 )
|
||||
{
|
||||
//fprintf(stderr, "FinalizeCCTx() matched cc myaddr (%s)\n", myaddr);
|
||||
privkey = myprivkey;
|
||||
cond = mycond;
|
||||
}
|
||||
else if (strcmp(destaddr, mytokensaddr) == 0) // if this is TokensCC1vout
|
||||
{
|
||||
privkey = myprivkey;
|
||||
cond = mytokenscond;
|
||||
//fprintf(stderr,"FinalizeCCTx() matched dual-eval TokensCC1vout my token addr.(%s)\n",mytokensaddr);
|
||||
}
|
||||
else if (strcmp(destaddr, mysingletokensaddr) == 0) // if this is TokensCC1vout
|
||||
{
|
||||
privkey = myprivkey;
|
||||
cond = mysingletokenscond;
|
||||
//fprintf(stderr, "FinalizeCCTx() matched single-eval token CC1vout my token addr.(%s)\n", mytokensaddr);
|
||||
}
|
||||
else if ( strcmp(destaddr,unspendable) == 0 )
|
||||
{
|
||||
privkey = unspendablepriv;
|
||||
cond = othercond;
|
||||
//fprintf(stderr,"FinalizeCCTx evalcode(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable);
|
||||
}
|
||||
else if (strcmp(destaddr, unspendabletokensaddr) == 0)
|
||||
{
|
||||
privkey = unspendablepriv;
|
||||
cond = othertokenscond;
|
||||
//fprintf(stderr,"FinalizeCCTx() matched unspendabletokensaddr dual/three-eval CC addr.(%s)\n",unspendabletokensaddr);
|
||||
}
|
||||
// check if this is the 2nd additional evalcode + 'unspendable' cc addr:
|
||||
else if ( strcmp(destaddr, cp->unspendableaddr2) == 0)
|
||||
{
|
||||
//fprintf(stderr,"FinalizeCCTx() matched %s unspendable2!\n",cp->unspendableaddr2);
|
||||
privkey = cp->unspendablepriv2;
|
||||
if( othercond2 == 0 )
|
||||
othercond2 = MakeCCcond1(cp->unspendableEvalcode2, cp->unspendablepk2);
|
||||
cond = othercond2;
|
||||
}
|
||||
// check if this is 3rd additional evalcode + 'unspendable' cc addr:
|
||||
else if ( strcmp(destaddr,cp->unspendableaddr3) == 0 )
|
||||
{
|
||||
//fprintf(stderr,"FinalizeCCTx() matched %s unspendable3!\n",cp->unspendableaddr3);
|
||||
privkey = cp->unspendablepriv3;
|
||||
if( othercond3 == 0 )
|
||||
othercond3 = MakeCCcond1(cp->unspendableEvalcode3, cp->unspendablepk3);
|
||||
cond = othercond3;
|
||||
}
|
||||
// check if this is spending from 1of2 cc coins addr:
|
||||
else if (strcmp(cp->coins1of2addr, destaddr) == 0)
|
||||
{
|
||||
//fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr);
|
||||
privkey = cp->coins1of2priv;//myprivkey;
|
||||
if (othercond1of2 == 0)
|
||||
othercond1of2 = MakeCCcond1of2(cp->evalcode, cp->coins1of2pk[0], cp->coins1of2pk[1]);
|
||||
cond = othercond1of2;
|
||||
}
|
||||
else if ( strcmp(CC1of2CCaddr,destaddr) == 0 )
|
||||
{
|
||||
//fprintf(stderr,"FinalizeCCTx() matched %s CC1of2CCaddr!\n",CC1of2CCaddr);
|
||||
privkey = unspendablepriv;
|
||||
if (condCC2 == 0)
|
||||
condCC2 = MakeCCcond1of2(cp->evalcode,unspendablepk,unspendablepk);
|
||||
cond = condCC2;
|
||||
}
|
||||
// check if this is spending from 1of2 cc tokens addr:
|
||||
else if (strcmp(cp->tokens1of2addr, destaddr) == 0)
|
||||
{
|
||||
//fprintf(stderr,"FinalizeCCTx() matched %s cp->tokens1of2addr!\n", cp->tokens1of2addr);
|
||||
privkey = cp->tokens1of2priv;//myprivkey;
|
||||
if (othercond1of2tokens == 0)
|
||||
// NOTE: if additionalEvalcode2 is not set then it is dual-eval cc else three-eval cc
|
||||
// TODO: verify evalcodes order if additionalEvalcode2 is not 0
|
||||
othercond1of2tokens = MakeTokensCCcond1of2(cp->evalcode, cp->additionalTokensEvalcode2, cp->tokens1of2pk[0], cp->tokens1of2pk[1]);
|
||||
cond = othercond1of2tokens;
|
||||
}
|
||||
else
|
||||
{
|
||||
flag = 0;
|
||||
if ( pubkeys != NULL_pubkeys )
|
||||
{
|
||||
char coinaddr[64];
|
||||
GetCCaddress1of2(cp,coinaddr,globalpk,pubkeys[i]);
|
||||
//fprintf(stderr,"%s + %s -> %s vs %s\n",HexStr(globalpk).c_str(),HexStr(pubkeys[i]).c_str(),coinaddr,destaddr);
|
||||
if ( strcmp(destaddr,coinaddr) == 0 )
|
||||
{
|
||||
privkey = cp->CCpriv;
|
||||
if ( othercond4 != 0 )
|
||||
cc_free(othercond4);
|
||||
othercond4 = MakeCCcond1of2(cp->evalcode,globalpk,pubkeys[i]);
|
||||
cond = othercond4;
|
||||
flag = 1;
|
||||
}
|
||||
} //else privkey = myprivkey;
|
||||
|
||||
if ( flag == 0 )
|
||||
{
|
||||
fprintf(stderr,"CC signing error: vini.%d has unknown CC address.(%s)\n",i,destaddr);
|
||||
memset(myprivkey,0,32);
|
||||
return sigDataNull;
|
||||
}
|
||||
}
|
||||
uint256 sighash = SignatureHash(CCPubKey(cond), mtx, i, SIGHASH_ALL,utxovalues[i],consensusBranchId, &txdata);
|
||||
if ( 0 )
|
||||
{
|
||||
int32_t z;
|
||||
for (z=0; z<32; z++)
|
||||
fprintf(stderr,"%02x",privkey[z]);
|
||||
fprintf(stderr," privkey, ");
|
||||
for (z=0; z<32; z++)
|
||||
fprintf(stderr,"%02x",((uint8_t *)sighash.begin())[z]);
|
||||
fprintf(stderr," sighash [%d] %.8f %x\n",i,(double)utxovalues[i]/COIN,consensusBranchId);
|
||||
}
|
||||
|
||||
if (!remote) // we have privkey in the wallet
|
||||
{
|
||||
if (cc_signTreeSecp256k1Msg32(cond, privkey, sighash.begin()) != 0)
|
||||
{
|
||||
mtx.vin[i].scriptSig = CCSig(cond);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "vini.%d has CC signing error address.(%s) %s\n", i, destaddr, EncodeHexTx(mtx).c_str());
|
||||
memset(myprivkey, 0, sizeof(myprivkey));
|
||||
return sigDataNull;
|
||||
}
|
||||
}
|
||||
else // no privkey locally - remote call
|
||||
{
|
||||
// serialize cc:
|
||||
UniValue ccjson;
|
||||
ccjson.read(cc_conditionToJSONString(cond));
|
||||
if (ccjson.empty())
|
||||
{
|
||||
fprintf(stderr, "vini.%d can't serialize CC.(%s) %s\n", i, destaddr, EncodeHexTx(mtx).c_str());
|
||||
memset(myprivkey, 0, sizeof(myprivkey));
|
||||
return sigDataNull;
|
||||
}
|
||||
|
||||
AddSigData2UniValue(sigData, i, ccjson, std::string(), vintx.vout[utxovout].nValue); // store vin i with scriptPubKey
|
||||
}
|
||||
}
|
||||
} else fprintf(stderr,"FinalizeCCTx2 couldnt find %s mgret.%d\n",mtx.vin[i].prevout.hash.ToString().c_str(),mgret);
|
||||
}
|
||||
if ( mycond != 0 )
|
||||
cc_free(mycond);
|
||||
if ( condCC2 != 0 )
|
||||
cc_free(condCC2);
|
||||
if ( othercond != 0 )
|
||||
cc_free(othercond);
|
||||
if ( othercond2 != 0 )
|
||||
cc_free(othercond2);
|
||||
if ( othercond3 != 0 )
|
||||
cc_free(othercond3);
|
||||
if ( othercond4 != 0 )
|
||||
cc_free(othercond4);
|
||||
if ( othercond1of2 != 0 )
|
||||
cc_free(othercond1of2);
|
||||
if ( othercond1of2tokens != 0 )
|
||||
cc_free(othercond1of2tokens);
|
||||
if ( mytokenscond != 0 )
|
||||
cc_free(mytokenscond);
|
||||
if ( mysingletokenscond != 0 )
|
||||
cc_free(mysingletokenscond);
|
||||
if ( othertokenscond != 0 )
|
||||
cc_free(othertokenscond);
|
||||
memset(myprivkey,0,sizeof(myprivkey));
|
||||
std::string strHex = EncodeHexTx(mtx);
|
||||
if ( strHex.size() > 0 )
|
||||
result.push_back(Pair(JSON_HEXTX, strHex));
|
||||
else {
|
||||
result.push_back(Pair(JSON_HEXTX, "0"));
|
||||
}
|
||||
if (sigData.size() > 0) result.push_back(Pair(JSON_SIGDATA,sigData));
|
||||
UniValue result(UniValue::VOBJ);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -399,187 +63,47 @@ void NSPV_CCtxids(std::vector<uint256> &txids,char *coinaddr,bool ccflag, uint8_
|
||||
|
||||
void SetCCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs,char *coinaddr,bool ccflag)
|
||||
{
|
||||
int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector<std::pair<uint160, int> > addresses;
|
||||
if ( HUSH_NSPV_SUPERLITE )
|
||||
{
|
||||
NSPV_CCunspents(unspentOutputs,coinaddr,ccflag);
|
||||
return;
|
||||
}
|
||||
n = (int32_t)strlen(coinaddr);
|
||||
addrstr.resize(n+1);
|
||||
ptr = (char *)addrstr.data();
|
||||
for (i=0; i<=n; i++)
|
||||
ptr[i] = coinaddr[i];
|
||||
CBitcoinAddress address(addrstr);
|
||||
if ( address.GetIndexKey(hashBytes, type, ccflag) == 0 )
|
||||
return;
|
||||
addresses.push_back(std::make_pair(hashBytes,type));
|
||||
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++)
|
||||
{
|
||||
if ( GetAddressUnspent((*it).first, (*it).second, unspentOutputs) == 0 )
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void SetCCtxids(std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,char *coinaddr,bool ccflag)
|
||||
{
|
||||
int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector<std::pair<uint160, int> > addresses;
|
||||
if ( HUSH_NSPV_SUPERLITE )
|
||||
{
|
||||
NSPV_CCtxids(addressIndex,coinaddr,ccflag);
|
||||
return;
|
||||
}
|
||||
n = (int32_t)strlen(coinaddr);
|
||||
addrstr.resize(n+1);
|
||||
ptr = (char *)addrstr.data();
|
||||
for (i=0; i<=n; i++)
|
||||
ptr[i] = coinaddr[i];
|
||||
CBitcoinAddress address(addrstr);
|
||||
if ( address.GetIndexKey(hashBytes, type, ccflag) == 0 )
|
||||
return;
|
||||
addresses.push_back(std::make_pair(hashBytes,type));
|
||||
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++)
|
||||
{
|
||||
if ( GetAddressIndex((*it).first, (*it).second, addressIndex) == 0 )
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void SetCCtxids(std::vector<uint256> &txids,char *coinaddr,bool ccflag, uint8_t evalcode, uint256 filtertxid, uint8_t func)
|
||||
{
|
||||
int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector<std::pair<uint160, int> > addresses;
|
||||
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
|
||||
if ( HUSH_NSPV_SUPERLITE )
|
||||
{
|
||||
NSPV_CCtxids(txids,coinaddr,ccflag,evalcode,filtertxid,func);
|
||||
return;
|
||||
}
|
||||
n = (int32_t)strlen(coinaddr);
|
||||
addrstr.resize(n+1);
|
||||
ptr = (char *)addrstr.data();
|
||||
for (i=0; i<=n; i++)
|
||||
ptr[i] = coinaddr[i];
|
||||
CBitcoinAddress address(addrstr);
|
||||
if ( address.GetIndexKey(hashBytes, type, ccflag) == 0 )
|
||||
return;
|
||||
addresses.push_back(std::make_pair(hashBytes,type));
|
||||
for (std::vector<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++)
|
||||
{
|
||||
if ( GetAddressIndex((*it).first, (*it).second, addressIndex) == 0 )
|
||||
return;
|
||||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it1=addressIndex.begin(); it1!=addressIndex.end(); it1++)
|
||||
{
|
||||
if (it1->second>=0) txids.push_back(it1->first.txhash);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout,int32_t CCflag)
|
||||
{
|
||||
uint256 txid; std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
SetCCunspents(unspentOutputs,coinaddr,CCflag!=0?true:false);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
if ( txid == utxotxid && utxovout == it->first.index )
|
||||
return(it->second.satoshis);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int64_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag,int32_t lockflag)
|
||||
{
|
||||
CCoins coins;
|
||||
//fprintf(stderr,"CCgettxoud %s/v%d\n",txid.GetHex().c_str(),vout);
|
||||
if ( mempoolflag != 0 )
|
||||
{
|
||||
if ( lockflag != 0 )
|
||||
{
|
||||
LOCK(mempool.cs);
|
||||
CCoinsViewMemPool view(pcoinsTip, mempool);
|
||||
if (!view.GetCoins(txid, coins))
|
||||
return(-1);
|
||||
else if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) != 0 )
|
||||
return(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCoinsViewMemPool view(pcoinsTip, mempool);
|
||||
if (!view.GetCoins(txid, coins))
|
||||
return(-1);
|
||||
else if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) != 0 )
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!pcoinsTip->GetCoins(txid, coins))
|
||||
return(-1);
|
||||
}
|
||||
if ( vout < coins.vout.size() )
|
||||
return(coins.vout[vout].nValue);
|
||||
else return(-1);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int32_t CCgetspenttxid(uint256 &spenttxid,int32_t &vini,int32_t &height,uint256 txid,int32_t vout)
|
||||
{
|
||||
CSpentIndexKey key(txid, vout);
|
||||
CSpentIndexValue value;
|
||||
if ( !GetSpentIndex(key, value) )
|
||||
return(-1);
|
||||
spenttxid = value.txid;
|
||||
vini = (int32_t)value.inputIndex;
|
||||
height = value.blockHeight;
|
||||
return(0);
|
||||
}
|
||||
|
||||
int64_t CCaddress_balance(char *coinaddr,int32_t CCflag)
|
||||
{
|
||||
int64_t sum = 0; std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
SetCCunspents(unspentOutputs,coinaddr,CCflag!=0?true:false);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
sum += it->second.satoshis;
|
||||
}
|
||||
return(sum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t CCfullsupply(uint256 tokenid)
|
||||
{
|
||||
uint256 hashBlock; int32_t numvouts; CTransaction tx; std::vector<uint8_t> origpubkey; std::string name,description;
|
||||
if ( myGetTransaction(tokenid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 )
|
||||
{
|
||||
if (DecodeTokenCreateOpRet(tx.vout[numvouts-1].scriptPubKey,origpubkey,name,description))
|
||||
{
|
||||
return(tx.vout[1].nValue);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int64_t CCtoken_balance(char *coinaddr,uint256 reftokenid)
|
||||
{
|
||||
int64_t price,sum = 0; int32_t numvouts; CTransaction tx; uint256 tokenid,txid,hashBlock;
|
||||
std::vector<uint8_t> vopretExtra;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
uint8_t evalCode;
|
||||
|
||||
SetCCunspents(unspentOutputs,coinaddr,true);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts=tx.vout.size()) > 0 )
|
||||
{
|
||||
char str[65];
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
if ( reftokenid==txid || (DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCode, tokenid, voutTokenPubkeys, oprets) != 0 && reftokenid == tokenid))
|
||||
{
|
||||
sum += it->second.satoshis;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(sum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *belowp,struct CC_utxo utxos[],int32_t numunspents,int64_t value)
|
||||
|
||||
@@ -546,46 +546,6 @@ int64_t CCduration(int32_t &numblocks,uint256 txid)
|
||||
return(duration);
|
||||
}
|
||||
|
||||
uint256 CCOraclesReverseScan(char const *logcategory,uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid)
|
||||
{
|
||||
CTransaction tx; uint256 hash,mhash,bhash,hashBlock,oracletxid; int32_t len,len2,numvouts;
|
||||
int64_t val,merkleht; CPubKey pk; std::vector<uint8_t>data; char str[65],str2[65];
|
||||
|
||||
txid = zeroid;
|
||||
LogPrint(logcategory,"start reverse scan %s\n",uint256_str(str,batontxid));
|
||||
while ( myGetTransaction(batontxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 )
|
||||
{
|
||||
LogPrint(logcategory,"check %s\n",uint256_str(str,batontxid));
|
||||
if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,bhash,pk,data) == 'D' && oracletxid == reforacletxid )
|
||||
{
|
||||
LogPrint(logcategory,"decoded %s\n",uint256_str(str,batontxid));
|
||||
if ( oracle_format(&hash,&merkleht,0,'I',(uint8_t *)data.data(),0,(int32_t)data.size()) == sizeof(int32_t) && merkleht == height )
|
||||
{
|
||||
len = oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size());
|
||||
len2 = oracle_format(&mhash,&val,0,'h',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)),(int32_t)data.size());
|
||||
|
||||
LogPrint(logcategory,"found merkleht.%d len.%d len2.%d %s %s\n",(int32_t)merkleht,len,len2,uint256_str(str,hash),uint256_str(str2,mhash));
|
||||
if ( len == sizeof(hash)+sizeof(int32_t) && len2 == 2*sizeof(mhash)+sizeof(int32_t) && mhash != zeroid )
|
||||
{
|
||||
txid = batontxid;
|
||||
LogPrint(logcategory,"set txid\n");
|
||||
return(mhash);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint(logcategory,"missing hash\n");
|
||||
return(zeroid);
|
||||
}
|
||||
}
|
||||
else LogPrint(logcategory,"height.%d vs search ht.%d\n",(int32_t)merkleht,(int32_t)height);
|
||||
batontxid = bhash;
|
||||
LogPrint(logcategory,"new hash %s\n",uint256_str(str,batontxid));
|
||||
} else break;
|
||||
}
|
||||
LogPrint(logcategory,"end of loop\n");
|
||||
return(zeroid);
|
||||
}
|
||||
|
||||
int32_t NSPV_coinaddr_inmempool(char const *logcategory,char *coinaddr,uint8_t CCflag);
|
||||
|
||||
int32_t myIs_coinaddr_inmempoolvout(char const *logcategory,char *coinaddr)
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
SHELL = /bin/sh
|
||||
CC = gcc
|
||||
CC_DARWIN = g++-8
|
||||
CC_WIN = x86_64-w64-mingw32-gcc-posix
|
||||
CFLAGS_DARWIN = -DBUILD_ROGUE -std=c++11 -arch x86_64 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -Wl,-undefined -Wl,dynamic_lookup -Wno-write-strings -shared -dynamiclib
|
||||
CFLAGS = -Wno-write-strings -DBUILD_ROGUE -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared
|
||||
CFLAGS_WIN = -Wno-write-strings -DBUILD_ROGUE -std=c++11 -I./rogue/x86_64-w64-mingw32/include -I./rogue/x86_64-w64-mingw32/include/ncursesw -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared
|
||||
DEBUGFLAGS = -O0 -D _DEBUG
|
||||
RELEASEFLAGS = -O2 -D NDEBUG -combine -fwhole-program
|
||||
$(info $(OS))
|
||||
OS := $(shell uname -s)
|
||||
$(info $(OS))
|
||||
TARGET = librogue.so
|
||||
TARGET_DARWIN = librogue.dylib
|
||||
TARGET_WIN = librogue.dll
|
||||
SOURCES = cclib.cpp
|
||||
#HEADERS = $(shell echo ../cryptoconditions/include/*.h) -I/usr/local/Cellar/gcc\@8/8.3.0/include/c++/8.3.0/
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SOURCES)
|
||||
$(info Building cclib to src/)
|
||||
ifeq ($(OS),Darwin)
|
||||
$(CC_DARWIN) $(CFLAGS_DARWIN) $(DEBUGFLAGS) -o $(TARGET_DARWIN) -c $(SOURCES)
|
||||
cp $(TARGET_DARWIN) ../libcc.dylib
|
||||
else ifeq ($(HOST),x86_64-w64-mingw32)
|
||||
$(info WINDOWS)
|
||||
$(CC_WIN) $(CFLAGS_WIN) $(DEBUGFLAGS) -o $(TARGET_WIN) -c $(SOURCES)
|
||||
cp $(TARGET_WIN) ../libcc.dll
|
||||
#else ifeq ($(WIN_HOST),True) - todo: pass ENV var from build.sh if WIN host
|
||||
else
|
||||
$(info LINUX)
|
||||
$(CC) $(CFLAGS) $(DEBUGFLAGS) -o $(TARGET) -c $(SOURCES)
|
||||
cp $(TARGET) ../libcc.so
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -rf $(TARGET)
|
||||
@@ -1,466 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCassets.h"
|
||||
#include "CCtokens.h"
|
||||
|
||||
/*
|
||||
Assets can be created or transferred.
|
||||
|
||||
native coins are also locked in the EVAL_ASSETS address, so we need a strict rule on when utxo in the special address are native coins and when they are assets. The specific rule that must not be violated is that vout0 for 'b'/'B' funcid are native coins. All other utxo locked in the special address are assets.
|
||||
|
||||
To create an asset use CC EVAL_ASSETS to create a transaction where vout[0] funds the assets. Externally each satoshi can be interpreted to represent 1 asset, or 100 million satoshis for one asset with 8 decimals, and the other decimals in between. The interpretation of the number of decimals is left to the higher level usages.
|
||||
|
||||
Once created, the assetid is the txid of the create transaction and using the assetid/0 it can spend the assets to however many outputs it creates. The restriction is that the last output must be an opreturn with the assetid. The sum of all but the first output needs to add up to the total assetoshis input. The first output is ignored and used for change from txfee.
|
||||
|
||||
What this means is that vout 0 of the creation txid and vouts 1 ... n-2 for transfer vouts are assetoshi outputs.
|
||||
|
||||
There is a special type of transfer to an unspendable address, that locks the asset and creates an offer for all. The details specified in the opreturn. In order to be compatible with the signing restrictions, the "unspendable" address is actually an address whose privkey is known to all. Funds sent to this address can only be spent if the swap parameters are fulfilled, or if the original pubkey cancels the offer by spending it.
|
||||
|
||||
Types of transactions:
|
||||
create name:description -> txid for assetid
|
||||
transfer <pubkey> <assetid> -> [{address:amount}, ... ] // like withdraw api
|
||||
selloffer <pubkey> <txid/vout> <amount> -> cancel or fillsell -> mempool txid or rejected, might not confirm
|
||||
buyoffer <amount> <assetid> <required> -> cancelbuy or fillbuy -> mempool txid or rejected, might not confirm
|
||||
exchange <pubkey> <txid/vout> <required> <required assetid> -> cancel or fillexchange -> mempool txid or rejected, might not confirm
|
||||
|
||||
assetsaddress <pubkey> // all assets end up in a special address for each pubkey
|
||||
assetbalance <pubkey> <assetid>
|
||||
assetutxos <pubkey> <assetid>
|
||||
assetsbalances <pubkey>
|
||||
asks <assetid>
|
||||
bids <assetid>
|
||||
swaps <assetid>
|
||||
|
||||
valid CC output: create or transfer or buyoffer or selloffer or exchange or cancel or fill
|
||||
|
||||
|
||||
buyoffer:
|
||||
vins.*: normal inputs (bid + change)
|
||||
vout.0: amount of bid to unspendable
|
||||
vout.1: CC output for marker
|
||||
vout.2: normal output for change (if any)
|
||||
vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
|
||||
|
||||
cancelbuy:
|
||||
vin.0: normal input
|
||||
vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
|
||||
vin.2: CC marker from buyoffer for txfee
|
||||
vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
|
||||
vout.1: vin.2 back to users pubkey
|
||||
vout.2: normal output for change (if any)
|
||||
vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid] 0 0 [origpubkey]
|
||||
|
||||
fillbuy:
|
||||
vin.0: normal input
|
||||
vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
|
||||
vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
|
||||
vout.0: remaining amount of bid to unspendable
|
||||
vout.1: vin.1 value to signer of vin.2
|
||||
vout.2: vin.2 assetoshis to original pubkey
|
||||
vout.3: CC output for assetoshis change (if any)
|
||||
vout.4: normal output for change (if any)
|
||||
vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
|
||||
|
||||
selloffer:
|
||||
vin.0: normal input
|
||||
vin.1+: valid CC output for sale
|
||||
vout.0: vin.1 assetoshis output to CC to unspendable
|
||||
vout.1: CC output for marker
|
||||
vout.2: CC output for change (if any)
|
||||
vout.3: normal output for change (if any)
|
||||
vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
|
||||
|
||||
exchange:
|
||||
vin.0: normal input
|
||||
vin.1+: valid CC output
|
||||
vout.0: vin.1 assetoshis output to CC to unspendable
|
||||
vout.1: CC output for change (if any)
|
||||
vout.2: normal output for change (if any)
|
||||
vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
|
||||
|
||||
cancel:
|
||||
vin.0: normal input
|
||||
vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
|
||||
vin.2: CC marker from selloffer for txfee
|
||||
vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
|
||||
vout.1: vin.2 back to users pubkey
|
||||
vout.2: normal output for change (if any)
|
||||
vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
|
||||
|
||||
fillsell:
|
||||
vin.0: normal input
|
||||
vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
|
||||
vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue
|
||||
vout.0: remaining assetoshis -> unspendable
|
||||
vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any
|
||||
vout.2: vin.2 value to original pubkey [origpubkey]
|
||||
vout.3: CC asset for change (if any)
|
||||
vout.4: CC asset2 for change (if any) 'E' only
|
||||
vout.5: normal output for change (if any)
|
||||
vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
|
||||
|
||||
fillexchange:
|
||||
vin.0: normal input
|
||||
vin.1: unspendable.(vout.0 assetoshis from exchange) exchangeTx.vout[0]
|
||||
vin.2+: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue
|
||||
vout.0: remaining assetoshis -> unspendable
|
||||
vout.1: vin.1 assetoshis to signer of vin.2 exchangeTx.vout[0].nValue -> any
|
||||
vout.2: vin.2 assetoshis2 to original pubkey [origpubkey]
|
||||
vout.3: normal output for change (if any)
|
||||
vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
// tx validation
|
||||
bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn)
|
||||
{
|
||||
static uint256 zero;
|
||||
CTxDestination address;
|
||||
CTransaction vinTx, createTx;
|
||||
uint256 hashBlock, assetid, assetid2;
|
||||
int32_t i,starti, numvins, numvouts, preventCCvins, preventCCvouts;
|
||||
int64_t remaining_price, nValue, assetoshis, outputsDummy,inputs,tmpprice,totalunits,ignore;
|
||||
std::vector<uint8_t> origpubkey, tmporigpubkey, ignorepubkey, vopretNonfungible, vopretNonfungibleDummy;
|
||||
uint8_t funcid, evalCodeInOpret;
|
||||
char destaddr[64], origNormalAddr[64], origTokensCCaddr[64], origCCaddrDummy[64];
|
||||
char tokensDualEvalUnspendableCCaddr[64], origAssetsCCaddr[64];
|
||||
|
||||
//return true;
|
||||
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
outputsDummy = inputs = 0;
|
||||
preventCCvins = preventCCvouts = -1;
|
||||
|
||||
if (numvouts == 0)
|
||||
return eval->Invalid("AssetValidate: no vouts");
|
||||
|
||||
if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 )
|
||||
return eval->Invalid("AssetValidate: invalid opreturn payload");
|
||||
|
||||
// non-fungible tokens support:
|
||||
GetNonfungibleData(assetid, vopretNonfungible);
|
||||
if (vopretNonfungible.size() > 0)
|
||||
cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
|
||||
|
||||
// find dual-eval tokens unspendable addr:
|
||||
GetTokensCCaddress(cpAssets, tokensDualEvalUnspendableCCaddr, GetUnspendable(cpAssets, NULL));
|
||||
// this is for marker validation:
|
||||
GetCCaddress(cpAssets, origAssetsCCaddr, origpubkey);
|
||||
|
||||
// we need this for validating single-eval tokens' vins/vous:
|
||||
struct CCcontract_info *cpTokens, tokensC;
|
||||
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
|
||||
|
||||
// find single-eval token user cc addr:
|
||||
//GetCCaddress(cpTokens, signleEvalTokensCCaddr, pubkey2pk(origpubkey));
|
||||
|
||||
//fprintf(stderr,"AssetValidate (%c)\n",funcid);
|
||||
|
||||
if( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid, createTx, hashBlock) == 0 )
|
||||
return eval->Invalid("cant find asset create txid");
|
||||
else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 )
|
||||
return eval->Invalid("cant find asset2 create txid");
|
||||
else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) // vin0 should be normal vin
|
||||
return eval->Invalid("illegal asset vin0");
|
||||
else if( numvouts < 2 )
|
||||
return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below
|
||||
else if( funcid != 'c' )
|
||||
{
|
||||
/* if( funcid == 't' )
|
||||
starti = 0;
|
||||
else
|
||||
starti = 1; */
|
||||
|
||||
if( assetid == zero )
|
||||
return eval->Invalid("illegal assetid");
|
||||
|
||||
else if (!AssetCalcAmounts(cpAssets, inputs, outputsDummy/*outputsDummy is calculated incorrectly but not used*/, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs
|
||||
return false; // returns false if some problems with reading vintxes
|
||||
}
|
||||
}
|
||||
|
||||
switch( funcid )
|
||||
{
|
||||
case 'c': // create wont be called to be verified as it has no CC inputs
|
||||
//vin.0: normal input
|
||||
//vout.0: issuance assetoshis to CC
|
||||
//vout.1: normal output for change (if any)
|
||||
//vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"<assetname>":"<description>"}]
|
||||
//if (evalCodeInOpret == EVAL_ASSETS)
|
||||
// return eval->Invalid("unexpected AssetValidate for createasset");
|
||||
// return
|
||||
return eval->Invalid("invalid asset funcid \'c\'");
|
||||
break;
|
||||
case 't': // transfer
|
||||
//vin.0: normal input
|
||||
//vin.1 .. vin.n-1: valid CC outputs
|
||||
//vout.0 to n-2: assetoshis output to CC
|
||||
//vout.n-2: normal output for change (if any)
|
||||
//vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
|
||||
//if (inputs == 0)
|
||||
// return eval->Invalid("no asset inputs for transfer");
|
||||
//fprintf(stderr,"transfer preliminarily validated %.8f -> %.8f (%d %d)\n",(double)inputs/COIN,(double)outputs/COIN,preventCCvins,preventCCvouts);
|
||||
return eval->Invalid("invalid asset funcid \'t\'");
|
||||
break;
|
||||
|
||||
case 'b': // buyoffer
|
||||
//vins.*: normal inputs (bid + change)
|
||||
//vout.0: amount of bid to unspendable
|
||||
//vout.1: CC output for marker
|
||||
//vout.2: normal output for change (if any)
|
||||
// vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
|
||||
|
||||
// as we don't use tokenconvert we should not be here:
|
||||
return eval->Invalid("invalid asset funcid (b)");
|
||||
|
||||
if( remaining_price == 0 )
|
||||
return eval->Invalid("illegal null amount for buyoffer");
|
||||
else if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr,0) == 0 ) // coins to assets unspendable cc addr
|
||||
return eval->Invalid("invalid vout for buyoffer");
|
||||
preventCCvins = 1;
|
||||
preventCCvouts = 1;
|
||||
fprintf(stderr,"buy offer validated to destaddr.(%s)\n",cpAssets->unspendableCCaddr);
|
||||
break;
|
||||
|
||||
case 'o': // cancelbuy
|
||||
//vin.0: normal input
|
||||
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
|
||||
//vin.2: CC marker from buyoffer for txfee
|
||||
//vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
|
||||
//vout.1: vin.2 back to users pubkey
|
||||
//vout.2: normal output for change (if any)
|
||||
//vout.n-1: opreturn [EVAL_ASSETS] ['o']
|
||||
if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, origCCaddrDummy, origNormalAddr, tx, assetid)) == 0 )
|
||||
return(false);
|
||||
else if( ConstrainVout(tx.vout[0],0, origNormalAddr, nValue) == 0 )
|
||||
return eval->Invalid("invalid refund for cancelbuy");
|
||||
preventCCvins = 3;
|
||||
preventCCvouts = 0;
|
||||
//fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origNormalAddr);
|
||||
break;
|
||||
|
||||
case 'B': // fillbuy:
|
||||
//vin.0: normal input
|
||||
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
|
||||
//vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
|
||||
//vout.0: remaining amount of bid to unspendable
|
||||
//vout.1: vin.1 value to signer of vin.2
|
||||
//vout.2: vin.2 assetoshis to original pubkey
|
||||
//vout.3: CC output for assetoshis change (if any)
|
||||
//vout.4: normal output for change (if any)
|
||||
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
|
||||
preventCCvouts = 4;
|
||||
|
||||
if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
|
||||
return(false);
|
||||
else if( numvouts < 4 )
|
||||
return eval->Invalid("not enough vouts for fillbuy");
|
||||
else if( tmporigpubkey != origpubkey )
|
||||
return eval->Invalid("mismatched origpubkeys for fillbuy");
|
||||
else
|
||||
{
|
||||
if( nValue != tx.vout[0].nValue + tx.vout[1].nValue )
|
||||
return eval->Invalid("locked value doesnt match vout0+1 fillbuy");
|
||||
else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change present
|
||||
{
|
||||
if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals)
|
||||
return eval->Invalid("vout2 doesnt go to origpubkey fillbuy");
|
||||
else if ( inputs != tx.vout[2].nValue + tx.vout[4].nValue )
|
||||
return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy");
|
||||
preventCCvouts ++;
|
||||
}
|
||||
else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals)
|
||||
return eval->Invalid("vout2 doesnt match inputs fillbuy");
|
||||
else if( ConstrainVout(tx.vout[1], 0, NULL, 0) == 0 )
|
||||
return eval->Invalid("vout1 is CC for fillbuy");
|
||||
else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to asset cc addr
|
||||
return eval->Invalid("invalid marker for original pubkey");
|
||||
else if( ValidateBidRemainder(remaining_price, tx.vout[0].nValue, nValue, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
|
||||
return eval->Invalid("mismatched remainder for fillbuy");
|
||||
else if( remaining_price != 0 )
|
||||
{
|
||||
if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins to asset unspendable cc addr
|
||||
return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy");
|
||||
}
|
||||
}
|
||||
//fprintf(stderr,"fillbuy validated\n");
|
||||
break;
|
||||
//case 'e': // selloffer
|
||||
// break; // disable swaps
|
||||
case 's': // selloffer
|
||||
//vin.0: normal input
|
||||
//vin.1+: valid CC output for sale
|
||||
//vout.0: vin.1 assetoshis output to CC to unspendable
|
||||
//vout.1: CC output for marker
|
||||
//vout.2: CC output for change (if any)
|
||||
//vout.3: normal output for change (if any)
|
||||
//'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
|
||||
//'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
|
||||
|
||||
// as we don't use tokenconvert we should not be here:
|
||||
return eval->Invalid("invalid asset funcid (s)");
|
||||
|
||||
preventCCvouts = 2;
|
||||
if( remaining_price == 0 )
|
||||
return eval->Invalid("illegal null remaining_price for selloffer");
|
||||
if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
return eval->Invalid("invalid normal vout1 for sellvin");
|
||||
if( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change presents
|
||||
{
|
||||
preventCCvouts++;
|
||||
if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // tokens to tokens unspendable cc addr. TODO: this in incorrect, should be assets if we got here!
|
||||
return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
|
||||
else if( tx.vout[0].nValue + tx.vout[2].nValue != inputs )
|
||||
return eval->Invalid("mismatched vout0+vout2 total for selloffer");
|
||||
}
|
||||
// no cc change:
|
||||
else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // tokens to tokens unspendable cc addr TODO: this in incorrect, should be assets if got here!
|
||||
return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
|
||||
//fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price);
|
||||
break;
|
||||
|
||||
case 'x': // cancel sell
|
||||
//vin.0: normal input
|
||||
//vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
|
||||
//vin.2: CC marker from selloffer for txfee
|
||||
//vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
|
||||
//vout.1: vin.2 back to users pubkey
|
||||
//vout.2: normal output for change (if any)
|
||||
//vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
|
||||
|
||||
if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) // NOTE:
|
||||
return(false);
|
||||
else if( ConstrainVout(tx.vout[0], 1, origTokensCCaddr, assetoshis) == 0 ) // tokens returning to originator cc addr
|
||||
return eval->Invalid("invalid vout for cancel");
|
||||
preventCCvins = 3;
|
||||
preventCCvouts = 1;
|
||||
break;
|
||||
|
||||
case 'S': // fillsell
|
||||
//vin.0: normal input
|
||||
//vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
|
||||
//'S'.vin.2+: normal output that satisfies selloffer (*tx.vin[2])->nValue
|
||||
//vout.0: remaining assetoshis -> unspendable
|
||||
//vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any
|
||||
//'S'.vout.2: vin.2 value to original pubkey [origpubkey]
|
||||
//vout.3: normal output for change (if any)
|
||||
//'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
|
||||
|
||||
if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
|
||||
return(false);
|
||||
else if( numvouts < 4 )
|
||||
return eval->Invalid("not enough vouts for fillask");
|
||||
else if( tmporigpubkey != origpubkey )
|
||||
return eval->Invalid("mismatched origpubkeys for fillask");
|
||||
else
|
||||
{
|
||||
if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue )
|
||||
return eval->Invalid("locked value doesnt match vout0+1 fillask");
|
||||
if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
|
||||
return eval->Invalid("mismatched remainder for fillask");
|
||||
else if( ConstrainVout(tx.vout[1], 1, NULL, 0) == 0 ) // do not check token buyer's cc addr
|
||||
return eval->Invalid("normal vout1 for fillask");
|
||||
else if( ConstrainVout(tx.vout[2], 0, origNormalAddr, 0) == 0 ) // coins to originator normal addr
|
||||
return eval->Invalid("normal vout1 for fillask");
|
||||
else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to originator asset cc addr
|
||||
return eval->Invalid("invalid marker for original pubkey");
|
||||
else if( remaining_price != 0 )
|
||||
{
|
||||
if ( ConstrainVout(tx.vout[0], 1, tokensDualEvalUnspendableCCaddr, 0) == 0 )
|
||||
return eval->Invalid("mismatched vout0 assets dual unspendable CCaddr for fill sell");
|
||||
}
|
||||
}
|
||||
//fprintf(stderr,"fill validated\n");
|
||||
break;
|
||||
case 'E': // fillexchange
|
||||
////////// not implemented yet ////////////
|
||||
return eval->Invalid("unexpected assets fillexchange funcid");
|
||||
break; // disable asset swaps
|
||||
//vin.0: normal input
|
||||
//vin.1: unspendable.(vout.0 assetoshis from selloffer) sellTx.vout[0]
|
||||
//vin.2+: valid CC assetid2 output that satisfies exchange (*tx.vin[2])->nValue
|
||||
//vout.0: remaining assetoshis -> unspendable
|
||||
//vout.1: vin.1 assetoshis to signer of vin.2 sellTx.vout[0].nValue -> any
|
||||
//vout.2: vin.2+ assetoshis2 to original pubkey [origpubkey]
|
||||
//vout.3: CC output for asset2 change (if any)
|
||||
//vout.3/4: normal output for change (if any)
|
||||
//vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
|
||||
|
||||
//if ( AssetExactAmounts(false, cp,inputs,outputs,eval,tx,assetid2) == false )
|
||||
// eval->Invalid("asset2 inputs != outputs");
|
||||
|
||||
////////// not implemented yet ////////////
|
||||
if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
|
||||
return(false);
|
||||
else if( numvouts < 3 )
|
||||
return eval->Invalid("not enough vouts for fillex");
|
||||
else if( tmporigpubkey != origpubkey )
|
||||
return eval->Invalid("mismatched origpubkeys for fillex");
|
||||
else
|
||||
{
|
||||
if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue )
|
||||
return eval->Invalid("locked value doesnt match vout0+1 fillex");
|
||||
else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
////////// not implemented yet ////////////
|
||||
{
|
||||
if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 )
|
||||
return eval->Invalid("vout2 doesnt go to origpubkey fillex");
|
||||
else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue )
|
||||
{
|
||||
fprintf(stderr,"inputs %.8f != %.8f + %.8f\n",(double)inputs/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN);
|
||||
return eval->Invalid("asset inputs doesnt match vout2+3 fillex");
|
||||
}
|
||||
}
|
||||
////////// not implemented yet ////////////
|
||||
else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 )
|
||||
return eval->Invalid("vout2 doesnt match inputs fillex");
|
||||
else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 )
|
||||
return eval->Invalid("vout1 is CC for fillex");
|
||||
fprintf(stderr,"assets vout0 %llu, vin1 %llu, vout2 %llu -> orig, vout1 %llu, total %llu\n",(long long)tx.vout[0].nValue,(long long)assetoshis,(long long)tx.vout[2].nValue,(long long)tx.vout[1].nValue,(long long)totalunits);
|
||||
if( ValidateSwapRemainder(remaining_price, tx.vout[0].nValue, assetoshis,tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
|
||||
return eval->Invalid("mismatched remainder for fillex");
|
||||
else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 )
|
||||
////////// not implemented yet ////////////
|
||||
return eval->Invalid("normal vout1 for fillex");
|
||||
else if( remaining_price != 0 )
|
||||
{
|
||||
if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 ) // TODO: unsure about this, but this is not impl yet anyway
|
||||
return eval->Invalid("mismatched vout0 AssetsCCaddr for fillex");
|
||||
}
|
||||
}
|
||||
////////// not implemented yet ////////////
|
||||
//fprintf(stderr,"fill validated\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr,"illegal assets funcid.(%c)\n",funcid);
|
||||
return eval->Invalid("unexpected assets funcid");
|
||||
//break;
|
||||
}
|
||||
|
||||
// what does this do?
|
||||
bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts); // seems we do not need this call as we already checked vouts well
|
||||
//std::cerr << "AssetsValidate() PreventCC returned=" << bPrevent << std::endl;
|
||||
return (bPrevent);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCauction.h"
|
||||
#include "../txmempool.h"
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
// start of consensus code
|
||||
|
||||
int64_t IsAuctionvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
|
||||
{
|
||||
char destaddr[64];
|
||||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
{
|
||||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
|
||||
return(tx.vout[v].nValue);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool AuctionExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
|
||||
{
|
||||
static uint256 zerohash;
|
||||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
//fprintf(stderr,"vini.%d\n",i);
|
||||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
|
||||
{
|
||||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
||||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
|
||||
return eval->Invalid("cant find vinTx");
|
||||
else
|
||||
{
|
||||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
||||
if ( hashBlock == zerohash )
|
||||
return eval->Invalid("cant Auction from mempool");
|
||||
if ( (assetoshis= IsAuctionvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
|
||||
inputs += assetoshis;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i<numvouts; i++)
|
||||
{
|
||||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
||||
if ( (assetoshis= IsAuctionvout(cp,tx,i)) != 0 )
|
||||
outputs += assetoshis;
|
||||
}
|
||||
if ( inputs != outputs+COIN+txfee )
|
||||
{
|
||||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
|
||||
return eval->Invalid("mismatched inputs != outputs + COIN + txfee");
|
||||
}
|
||||
else return(true);
|
||||
}
|
||||
|
||||
bool AuctionValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
|
||||
{
|
||||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval;
|
||||
return eval->Invalid("no validation yet");
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
preventCCvins = preventCCvouts = -1;
|
||||
if ( numvouts < 1 )
|
||||
return eval->Invalid("no vouts");
|
||||
else
|
||||
{
|
||||
//fprintf(stderr,"check vins\n");
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
|
||||
{
|
||||
fprintf(stderr,"Auctionget invalid vini\n");
|
||||
return eval->Invalid("illegal normal vini");
|
||||
}
|
||||
}
|
||||
//fprintf(stderr,"check amounts\n");
|
||||
if ( AuctionExactAmounts(cp,eval,tx,1,10000) == false )
|
||||
{
|
||||
fprintf(stderr,"Auctionget invalid amount\n");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
preventCCvouts = 1;
|
||||
if ( IsAuctionvout(cp,tx,0) != 0 )
|
||||
{
|
||||
preventCCvouts++;
|
||||
i = 1;
|
||||
} else i = 0;
|
||||
if ( tx.vout[i].nValue != COIN )
|
||||
return eval->Invalid("invalid Auction output");
|
||||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
|
||||
if ( retval != 0 )
|
||||
fprintf(stderr,"Auctionget validated\n");
|
||||
else fprintf(stderr,"Auctionget invalid\n");
|
||||
return(retval);
|
||||
}
|
||||
}
|
||||
}
|
||||
// end of consensus code
|
||||
|
||||
// helper functions for rpc calls in rpcwallet.cpp
|
||||
|
||||
int64_t AddAuctionInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
|
||||
{
|
||||
// add threshold check
|
||||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t n = 0;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
GetCCaddress(cp,coinaddr,pk);
|
||||
SetCCunspents(unspentOutputs,coinaddr,true);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
// prevent dup
|
||||
if ( it->second.satoshis < 1000000 )
|
||||
continue;
|
||||
if ( myGetTransaction(txid,vintx,hashBlock) != 0 )
|
||||
{
|
||||
if ( (nValue= IsAuctionvout(cp,vintx,(int32_t)it->first.index)) > 0 )
|
||||
{
|
||||
if ( total != 0 && maxinputs != 0 )
|
||||
mtx.vin.push_back(CTxIn(txid,(int32_t)it->first.index,CScript()));
|
||||
nValue = it->second.satoshis;
|
||||
totalinputs += nValue;
|
||||
n++;
|
||||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(totalinputs);
|
||||
}
|
||||
|
||||
std::string AuctionBid(uint64_t txfee,uint256 itemhash,int64_t amount)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,Auctionpk; CScript opret; int64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C;
|
||||
cp = CCinit(&C,EVAL_AUCTION);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
Auctionpk = GetUnspendable(cp,0);
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
if ( (inputs= AddAuctionInputs(cp,mtx,Auctionpk,nValue+txfee,60)) > 0 )
|
||||
{
|
||||
if ( inputs > nValue )
|
||||
CCchange = (inputs - nValue - txfee);
|
||||
if ( CCchange != 0 )
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_AUCTION,CCchange,Auctionpk));
|
||||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,opret));
|
||||
} else fprintf(stderr,"cant find Auction inputs\n");
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string AuctionDeliver(uint64_t txfee,uint256 itemhash,uint256 bidtxid)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,Auctionpk; CScript opret; int64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C;
|
||||
cp = CCinit(&C,EVAL_AUCTION);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
Auctionpk = GetUnspendable(cp,0);
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
if ( (inputs= AddAuctionInputs(cp,mtx,Auctionpk,nValue+txfee,60)) > 0 )
|
||||
{
|
||||
if ( inputs > nValue )
|
||||
CCchange = (inputs - nValue - txfee);
|
||||
if ( CCchange != 0 )
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_AUCTION,CCchange,Auctionpk));
|
||||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,opret));
|
||||
} else fprintf(stderr,"cant find Auction inputs\n");
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string AuctionPost(uint64_t txfee,uint256 itemhash,int64_t minbid,char *title,char *description)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,Auctionpk; int64_t funds = 0; CScript opret; struct CCcontract_info *cp,C;
|
||||
cp = CCinit(&C,EVAL_AUCTION);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
Auctionpk = GetUnspendable(cp,0);
|
||||
if ( AddNormalinputs(mtx,mypk,txfee,64) > 0 )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_AUCTION,funds,Auctionpk));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret));
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,305 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "hash.h"
|
||||
#include "main.h"
|
||||
#include "chain.h"
|
||||
#include "streams.h"
|
||||
#include "script/cc.h"
|
||||
#include "cc/betprotocol.h"
|
||||
#include "cc/eval.h"
|
||||
#include "cc/utils.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
int32_t hush_nextheight();
|
||||
|
||||
std::vector<CC*> BetProtocol::PlayerConditions()
|
||||
{
|
||||
std::vector<CC*> subs;
|
||||
for (int i=0; i<players.size(); i++)
|
||||
subs.push_back(CCNewSecp256k1(players[i]));
|
||||
return subs;
|
||||
}
|
||||
|
||||
|
||||
CC* BetProtocol::MakeDisputeCond()
|
||||
{
|
||||
CC *disputePoker = CCNewEval(E_MARSHAL(
|
||||
ss << disputeCode << VARINT(waitBlocks) << vmParams;
|
||||
));
|
||||
|
||||
CC *anySig = CCNewThreshold(1, PlayerConditions());
|
||||
|
||||
return CCNewThreshold(2, {disputePoker, anySig});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* spendFee is the amount assigned to each output, for the purposes of posting
|
||||
* dispute / evidence.
|
||||
*/
|
||||
CMutableTransaction BetProtocol::MakeSessionTx(CAmount spendFee)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
|
||||
CC *disputeCond = MakeDisputeCond();
|
||||
mtx.vout.push_back(CTxOut(spendFee, CCPubKey(disputeCond)));
|
||||
cc_free(disputeCond);
|
||||
|
||||
for (int i=0; i<players.size(); i++) {
|
||||
CC *cond = CCNewSecp256k1(players[i]);
|
||||
mtx.vout.push_back(CTxOut(spendFee, CCPubKey(cond)));
|
||||
cc_free(cond);
|
||||
}
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
CMutableTransaction BetProtocol::MakeDisputeTx(uint256 signedSessionTxHash, uint256 vmResultHash)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
|
||||
CC *disputeCond = MakeDisputeCond();
|
||||
mtx.vin.push_back(CTxIn(signedSessionTxHash, 0, CScript()));
|
||||
|
||||
std::vector<unsigned char> result(vmResultHash.begin(), vmResultHash.begin()+32);
|
||||
mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << result));
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
CMutableTransaction BetProtocol::MakePostEvidenceTx(uint256 signedSessionTxHash,
|
||||
int playerIdx, std::vector<unsigned char> state)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
|
||||
mtx.vin.push_back(CTxIn(signedSessionTxHash, playerIdx+1, CScript()));
|
||||
mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << state));
|
||||
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
CC* BetProtocol::MakePayoutCond(uint256 signedSessionTxHash)
|
||||
{
|
||||
// TODO: 2/3 majority
|
||||
CC* agree = CCNewThreshold(players.size(), PlayerConditions());
|
||||
|
||||
CC *import;
|
||||
{
|
||||
CC *importEval = CCNewEval(E_MARSHAL(
|
||||
ss << EVAL_IMPORTPAYOUT << signedSessionTxHash;
|
||||
));
|
||||
|
||||
CC *oneof = CCNewThreshold(1, PlayerConditions());
|
||||
|
||||
import = CCNewThreshold(2, {oneof, importEval});
|
||||
}
|
||||
|
||||
return CCNewThreshold(1, {agree, import});
|
||||
}
|
||||
|
||||
|
||||
CMutableTransaction BetProtocol::MakeStakeTx(CAmount totalPayout, uint256 signedSessionTxHash)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
|
||||
CC *payoutCond = MakePayoutCond(signedSessionTxHash);
|
||||
mtx.vout.push_back(CTxOut(totalPayout, CCPubKey(payoutCond)));
|
||||
cc_free(payoutCond);
|
||||
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
CMutableTransaction BetProtocol::MakeAgreePayoutTx(std::vector<CTxOut> payouts,
|
||||
uint256 signedStakeTxHash)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript()));
|
||||
mtx.vout = payouts;
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
CMutableTransaction BetProtocol::MakeImportPayoutTx(std::vector<CTxOut> payouts,
|
||||
CTransaction signedDisputeTx, uint256 signedStakeTxHash, MoMProof momProof)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
mtx.vin.push_back(CTxIn(signedStakeTxHash, 0, CScript()));
|
||||
mtx.vout = payouts;
|
||||
CScript proofData;
|
||||
proofData << OP_RETURN << E_MARSHAL(ss << momProof << signedDisputeTx);
|
||||
mtx.vout.insert(mtx.vout.begin(), CTxOut(0, proofData));
|
||||
return mtx;
|
||||
}
|
||||
|
||||
|
||||
bool GetOpReturnHash(CScript script, uint256 &hash)
|
||||
{
|
||||
std::vector<unsigned char> vHash;
|
||||
GetOpReturnData(script, vHash);
|
||||
if (vHash.size() != 32) return false;
|
||||
hash = uint256(vHash);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Crypto-Condition EVAL method that verifies a payout against a transaction
|
||||
* notarized on another chain.
|
||||
*
|
||||
* IN: params - condition params
|
||||
* IN: importTx - Payout transaction on value chain (HUSH)
|
||||
* IN: nIn - index of input of stake
|
||||
*
|
||||
* importTx: Spends stakeTx with payouts from asset chain
|
||||
*
|
||||
* in 0: Spends Stake TX and contains ImportPayout CC
|
||||
* out 0: OP_RETURN MomProof, disputeTx
|
||||
* out 1-: arbitrary payouts
|
||||
*
|
||||
* disputeTx: Spends sessionTx.0 (opener on asset chain)
|
||||
*
|
||||
* in 0: spends sessionTx.0
|
||||
* in 1-: anything
|
||||
* out 0: OP_RETURN hash of payouts
|
||||
* out 1-: anything
|
||||
*/
|
||||
bool Eval::ImportPayout(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
||||
{
|
||||
if (importTx.vout.size() == 0) return Invalid("no-vouts");
|
||||
|
||||
// load data from vout[0]
|
||||
MoMProof proof;
|
||||
CTransaction disputeTx;
|
||||
{
|
||||
std::vector<unsigned char> vopret;
|
||||
GetOpReturnData(importTx.vout[0].scriptPubKey, vopret);
|
||||
if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx))
|
||||
return Invalid("invalid-payload");
|
||||
}
|
||||
|
||||
// Check disputeTx.0 shows correct payouts
|
||||
{
|
||||
uint256 givenPayoutsHash;
|
||||
GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash);
|
||||
std::vector<CTxOut> payouts(importTx.vout.begin() + 1, importTx.vout.end());
|
||||
if (givenPayoutsHash != SerializeHash(payouts))
|
||||
return Invalid("wrong-payouts");
|
||||
}
|
||||
|
||||
// Check disputeTx spends sessionTx.0
|
||||
// condition ImportPayout params is session ID from other chain
|
||||
{
|
||||
uint256 sessionHash;
|
||||
if (!E_UNMARSHAL(params, ss >> sessionHash))
|
||||
return Invalid("malformed-params");
|
||||
if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0))
|
||||
return Invalid("wrong-session");
|
||||
}
|
||||
|
||||
// Check disputeTx solves momproof from vout[0]
|
||||
{
|
||||
NotarizationData data(0);
|
||||
if (!GetNotarizationData(proof.notarizationHash, data))
|
||||
return Invalid("coudnt-load-mom");
|
||||
|
||||
if (data.MoM != proof.branch.Exec(disputeTx.GetHash()))
|
||||
return Invalid("mom-check-fail");
|
||||
}
|
||||
|
||||
return Valid();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Crypto-Condition EVAL method that resolves a dispute of a session
|
||||
*
|
||||
* IN: vm - AppVM virtual machine to verify states
|
||||
* IN: params - condition params
|
||||
* IN: disputeTx - transaction attempting to resolve dispute
|
||||
* IN: nIn - index of input of dispute tx
|
||||
*
|
||||
* disputeTx: attempt to resolve a dispute
|
||||
*
|
||||
* in 0: Spends Session TX first output, reveals DisputeHeader
|
||||
* out 0: OP_RETURN hash of payouts
|
||||
*/
|
||||
bool Eval::DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn)
|
||||
{
|
||||
if (disputeTx.vout.size() == 0) return Invalid("no-vouts");
|
||||
|
||||
// get payouts hash
|
||||
uint256 payoutHash;
|
||||
if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash))
|
||||
return Invalid("invalid-payout-hash");
|
||||
|
||||
// load params
|
||||
uint16_t waitBlocks;
|
||||
std::vector<uint8_t> vmParams;
|
||||
if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams))
|
||||
return Invalid("malformed-params");
|
||||
|
||||
// ensure that enough time has passed
|
||||
{
|
||||
CTransaction sessionTx;
|
||||
CBlockIndex sessionBlock;
|
||||
|
||||
// if unconformed its too soon
|
||||
if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock))
|
||||
return Error("couldnt-get-parent");
|
||||
|
||||
if (GetCurrentHeight() < sessionBlock.GetHeight() + waitBlocks)
|
||||
return Invalid("dispute-too-soon"); // Not yet
|
||||
}
|
||||
|
||||
// get spends
|
||||
std::vector<CTransaction> spends;
|
||||
if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends))
|
||||
return Error("couldnt-get-spends");
|
||||
|
||||
// verify result from VM
|
||||
int maxLength = -1;
|
||||
uint256 bestPayout;
|
||||
for (int i=1; i<spends.size(); i++)
|
||||
{
|
||||
std::vector<unsigned char> vmState;
|
||||
if (spends[i].vout.size() == 0) continue;
|
||||
if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue;
|
||||
auto out = vm.evaluate(vmParams, vmState);
|
||||
uint256 resultHash = SerializeHash(out.second);
|
||||
if (out.first > maxLength) {
|
||||
maxLength = out.first;
|
||||
bestPayout = resultHash;
|
||||
}
|
||||
// The below means that if for any reason there is a draw, the first dispute wins
|
||||
else if (out.first == maxLength) {
|
||||
if (bestPayout != payoutHash) {
|
||||
fprintf(stderr, "WARNING: VM has multiple solutions of same length\n");
|
||||
bestPayout = resultHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxLength == -1) return Invalid("no-evidence");
|
||||
|
||||
return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout");
|
||||
}
|
||||
552
src/cc/cclib.cpp
552
src/cc/cclib.cpp
@@ -26,24 +26,11 @@
|
||||
#include "main.h"
|
||||
#include "chain.h"
|
||||
#include "core_io.h"
|
||||
#include "crosschain.h"
|
||||
#define FAUCET2SIZE COIN
|
||||
#define EVAL_FAUCET2 EVAL_FIRSTUSER
|
||||
#ifdef BUILD_ROGUE
|
||||
#define EVAL_ROGUE 17
|
||||
std::string MYCCLIBNAME = (char *)"rogue";
|
||||
|
||||
#elif BUILD_CUSTOMCC
|
||||
#ifdef BUILD_CUSTOMCC
|
||||
#include "customcc.h"
|
||||
|
||||
#elif BUILD_GAMESCC
|
||||
#include "gamescc.h"
|
||||
|
||||
#else
|
||||
#define EVAL_SUDOKU 17
|
||||
#define EVAL_MUSIG 18
|
||||
#define EVAL_DILITHIUM 19
|
||||
std::string MYCCLIBNAME = (char *)"sudoku";
|
||||
#endif
|
||||
|
||||
#ifndef BUILD_GAMESCC
|
||||
@@ -65,97 +52,13 @@ CClib_methods[] =
|
||||
{
|
||||
{ (char *)"faucet2", (char *)"fund", (char *)"amount", 1, 1, 'F', EVAL_FAUCET2 },
|
||||
{ (char *)"faucet2", (char *)"get", (char *)"<no args>", 0, 0, 'G', EVAL_FAUCET2 },
|
||||
#ifdef BUILD_ROGUE
|
||||
{ (char *)"rogue", (char *)"newgame", (char *)"maxplayers buyin", 0, 2, 'G', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"gameinfo", (char *)"gametxid", 1, 1, 'T', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"pending", (char *)"<no args>", 0, 0, 'P', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"register", (char *)"gametxid [playertxid]", 1, 2, 'R', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"keystrokes", (char *)"gametxid keystrokes", 2, 2, 'K', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"bailout", (char *)"gametxid", 1, 1, 'Q', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"highlander", (char *)"gametxid", 1, 1, 'H', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"playerinfo", (char *)"playertxid", 1, 1, 'I', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"players", (char *)"<no args>", 0, 0, 'D', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"games", (char *)"<no args>", 0, 0, 'F', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"setname", (char *)"pname", 1, 1, 'N', EVAL_ROGUE },
|
||||
{ (char *)"rogue", (char *)"extract", (char *)"gametxid [pubkey]", 1, 2, 'X', EVAL_ROGUE },
|
||||
#elif BUILD_CUSTOMCC
|
||||
#ifdef BUILD_CUSTOMCC
|
||||
RPC_FUNCS
|
||||
#elif BUILD_GAMESCC
|
||||
RPC_FUNCS
|
||||
#else
|
||||
{ (char *)"sudoku", (char *)"gen", (char *)"<no args>", 0, 0, 'G', EVAL_SUDOKU },
|
||||
{ (char *)"sudoku", (char *)"txidinfo", (char *)"txid", 1, 1, 'T', EVAL_SUDOKU },
|
||||
{ (char *)"sudoku", (char *)"pending", (char *)"<no args>", 0, 0, 'U', EVAL_SUDOKU },
|
||||
{ (char *)"sudoku", (char *)"solution", (char *)"txid solution timestamps[81]", 83, 83, 'S', EVAL_SUDOKU },
|
||||
{ (char *)"musig", (char *)"calcmsg", (char *)"sendtxid scriptPubKey", 2, 2, 'C', EVAL_MUSIG },
|
||||
{ (char *)"musig", (char *)"combine", (char *)"pubkeys ...", 2, 999999999, 'P', EVAL_MUSIG },
|
||||
{ (char *)"musig", (char *)"session", (char *)"myindex,numsigners,combined_pk,pkhash,msg32", 5, 5, 'R', EVAL_MUSIG },
|
||||
{ (char *)"musig", (char *)"commit", (char *)"pkhash,ind,commitment", 3, 3, 'H', EVAL_MUSIG },
|
||||
{ (char *)"musig", (char *)"nonce", (char *)"pkhash,ind,nonce", 3, 3, 'N', EVAL_MUSIG },
|
||||
{ (char *)"musig", (char *)"partialsig", (char *)"pkhash,ind,partialsig", 3, 3, 'S', EVAL_MUSIG },
|
||||
{ (char *)"musig", (char *)"verify", (char *)"msg sig pubkey", 3, 3, 'V', EVAL_MUSIG },
|
||||
{ (char *)"musig", (char *)"send", (char *)"combined_pk amount", 2, 2, 'x', EVAL_MUSIG },
|
||||
{ (char *)"musig", (char *)"spend", (char *)"sendtxid sig scriptPubKey", 3, 3, 'y', EVAL_MUSIG },
|
||||
{ (char *)"dilithium", (char *)"keypair", (char *)"[hexseed]", 0, 1, 'K', EVAL_DILITHIUM },
|
||||
{ (char *)"dilithium", (char *)"register", (char *)"handle, [hexseed]", 1, 2, 'R', EVAL_DILITHIUM },
|
||||
{ (char *)"dilithium", (char *)"handleinfo", (char *)"handle", 1, 1, 'I', EVAL_DILITHIUM },
|
||||
{ (char *)"dilithium", (char *)"sign", (char *)"msg [hexseed]", 1, 2, 'S', EVAL_DILITHIUM },
|
||||
{ (char *)"dilithium", (char *)"verify", (char *)"pubtxid msg sig", 3, 3, 'V', EVAL_DILITHIUM },
|
||||
{ (char *)"dilithium", (char *)"send", (char *)"handle pubtxid amount", 3, 3, 'x', EVAL_DILITHIUM },
|
||||
{ (char *)"dilithium", (char *)"spend", (char *)"sendtxid scriptPubKey [hexseed]", 2, 3, 'y', EVAL_DILITHIUM },
|
||||
{ (char *)"dilithium", (char *)"Qsend", (char *)"mypubtxid hexseed/'mypriv' destpubtxid,amount, ...", 4, 66, 'Q', EVAL_DILITHIUM },
|
||||
#endif
|
||||
};
|
||||
|
||||
std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params);
|
||||
|
||||
#ifdef BUILD_ROGUE
|
||||
int32_t rogue_replay(uint64_t seed,int32_t sleepmillis);
|
||||
bool rogue_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx);
|
||||
|
||||
UniValue rogue_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue rogue_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
|
||||
#else
|
||||
bool sudoku_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx);
|
||||
UniValue sudoku_txidinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue sudoku_generate(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue sudoku_solution(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue sudoku_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
|
||||
bool musig_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx);
|
||||
UniValue musig_calcmsg(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue musig_combine(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue musig_session(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue musig_commit(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue musig_nonce(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue musig_partialsig(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue musig_verify(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue musig_send(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue musig_spend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
|
||||
bool dilithium_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx);
|
||||
UniValue dilithium_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue dilithium_handleinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue dilithium_send(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue dilithium_spend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue dilithium_keypair(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue dilithium_sign(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue dilithium_verify(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue dilithium_Qsend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
|
||||
#endif
|
||||
|
||||
cJSON *cclib_reparse(int32_t *nump,char *jsonstr) // assumes origparams will be freed by caller
|
||||
{
|
||||
cJSON *params; char *newstr; int32_t i,j;
|
||||
@@ -192,440 +95,59 @@ cJSON *cclib_reparse(int32_t *nump,char *jsonstr) // assumes origparams will be
|
||||
|
||||
UniValue CClib_method(struct CCcontract_info *cp,char *method,char *jsonstr)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ); uint64_t txfee = 10000; int32_t m; cJSON *params = cclib_reparse(&m,jsonstr);
|
||||
fprintf(stderr,"method.(%s) -> (%s)\n",jsonstr!=0?jsonstr:"",params!=0?jprint(params,0):"");
|
||||
#ifdef BUILD_ROGUE
|
||||
if ( cp->evalcode == EVAL_ROGUE )
|
||||
{
|
||||
if ( strcmp(method,"newgame") == 0 )
|
||||
return(rogue_newgame(txfee,cp,params));
|
||||
else if ( strcmp(method,"pending") == 0 )
|
||||
return(rogue_pending(txfee,cp,params));
|
||||
else if ( strcmp(method,"gameinfo") == 0 )
|
||||
return(rogue_gameinfo(txfee,cp,params));
|
||||
else if ( strcmp(method,"register") == 0 )
|
||||
return(rogue_register(txfee,cp,params));
|
||||
else if ( strcmp(method,"keystrokes") == 0 )
|
||||
return(rogue_keystrokes(txfee,cp,params));
|
||||
else if ( strcmp(method,"bailout") == 0 )
|
||||
return(rogue_bailout(txfee,cp,params));
|
||||
else if ( strcmp(method,"highlander") == 0 )
|
||||
return(rogue_highlander(txfee,cp,params));
|
||||
else if ( strcmp(method,"extract") == 0 )
|
||||
return(rogue_extract(txfee,cp,params));
|
||||
else if ( strcmp(method,"playerinfo") == 0 )
|
||||
return(rogue_playerinfo(txfee,cp,params));
|
||||
else if ( strcmp(method,"players") == 0 )
|
||||
return(rogue_players(txfee,cp,params));
|
||||
else if ( strcmp(method,"games") == 0 )
|
||||
return(rogue_games(txfee,cp,params));
|
||||
else if ( strcmp(method,"setname") == 0 )
|
||||
return(rogue_setname(txfee,cp,params));
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","invalid rogue method"));
|
||||
result.push_back(Pair("method",method));
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
#elif BUILD_CUSTOMCC
|
||||
CUSTOM_DISPATCH
|
||||
#elif BUILD_GAMESCC
|
||||
CUSTOM_DISPATCH
|
||||
#else
|
||||
if ( cp->evalcode == EVAL_SUDOKU )
|
||||
{
|
||||
//printf("CClib_method params.%p\n",params);
|
||||
if ( strcmp(method,"txidinfo") == 0 )
|
||||
return(sudoku_txidinfo(txfee,cp,params));
|
||||
else if ( strcmp(method,"gen") == 0 )
|
||||
return(sudoku_generate(txfee,cp,params));
|
||||
else if ( strcmp(method,"solution") == 0 )
|
||||
return(sudoku_solution(txfee,cp,params));
|
||||
else if ( strcmp(method,"pending") == 0 )
|
||||
return(sudoku_pending(txfee,cp,params));
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","invalid sudoku method"));
|
||||
result.push_back(Pair("method",method));
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
else if ( cp->evalcode == EVAL_MUSIG )
|
||||
{
|
||||
//printf("CClib_method params.%p\n",params);
|
||||
if ( strcmp(method,"combine") == 0 )
|
||||
return(musig_combine(txfee,cp,params));
|
||||
else if ( strcmp(method,"calcmsg") == 0 )
|
||||
return(musig_calcmsg(txfee,cp,params));
|
||||
else if ( strcmp(method,"session") == 0 )
|
||||
return(musig_session(txfee,cp,params));
|
||||
else if ( strcmp(method,"commit") == 0 )
|
||||
return(musig_commit(txfee,cp,params));
|
||||
else if ( strcmp(method,"nonce") == 0 ) // returns combined nonce if ready
|
||||
return(musig_nonce(txfee,cp,params));
|
||||
else if ( strcmp(method,"partialsig") == 0 )
|
||||
return(musig_partialsig(txfee,cp,params));
|
||||
else if ( strcmp(method,"verify") == 0 )
|
||||
return(musig_verify(txfee,cp,params));
|
||||
else if ( strcmp(method,"send") == 0 )
|
||||
return(musig_send(txfee,cp,params));
|
||||
else if ( strcmp(method,"spend") == 0 )
|
||||
return(musig_spend(txfee,cp,params));
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","invalid musig method"));
|
||||
result.push_back(Pair("method",method));
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
else if ( cp->evalcode == EVAL_DILITHIUM )
|
||||
{
|
||||
if ( strcmp(method,"Qsend") == 0 )
|
||||
return(dilithium_Qsend(txfee,cp,params));
|
||||
else if ( strcmp(method,"send") == 0 )
|
||||
return(dilithium_send(txfee,cp,params));
|
||||
else if ( strcmp(method,"spend") == 0 )
|
||||
return(dilithium_spend(txfee,cp,params));
|
||||
else if ( strcmp(method,"keypair") == 0 )
|
||||
return(dilithium_keypair(txfee,cp,params));
|
||||
else if ( strcmp(method,"register") == 0 )
|
||||
return(dilithium_register(txfee,cp,params));
|
||||
else if ( strcmp(method,"handleinfo") == 0 )
|
||||
return(dilithium_handleinfo(txfee,cp,params));
|
||||
else if ( strcmp(method,"sign") == 0 )
|
||||
return(dilithium_sign(txfee,cp,params));
|
||||
else if ( strcmp(method,"verify") == 0 )
|
||||
return(dilithium_verify(txfee,cp,params));
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","invalid dilithium method"));
|
||||
result.push_back(Pair("method",method));
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","only sudoku supported for now"));
|
||||
result.push_back(Pair("evalcode",(int)cp->evalcode));
|
||||
return(result);
|
||||
}
|
||||
UniValue result(UniValue::VOBJ);
|
||||
return(result);
|
||||
}
|
||||
|
||||
UniValue CClib_info(struct CCcontract_info *cp)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i; char str[2];
|
||||
result.push_back(Pair("result","success"));
|
||||
result.push_back(Pair("CClib",CClib_name()));
|
||||
for (i=0; i<sizeof(CClib_methods)/sizeof(*CClib_methods); i++)
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
obj.push_back(Pair("evalcode",CClib_methods[i].evalcode));
|
||||
if ( CClib_methods[i].funcid < ' ' || CClib_methods[i].funcid >= 128 )
|
||||
obj.push_back(Pair("funcid",CClib_methods[i].funcid));
|
||||
else
|
||||
{
|
||||
str[0] = CClib_methods[i].funcid;
|
||||
str[1] = 0;
|
||||
obj.push_back(Pair("funcid",str));
|
||||
}
|
||||
obj.push_back(Pair("name",CClib_methods[i].CCname));
|
||||
obj.push_back(Pair("method",CClib_methods[i].method));
|
||||
obj.push_back(Pair("help",CClib_methods[i].help));
|
||||
obj.push_back(Pair("params_required",CClib_methods[i].numrequiredargs));
|
||||
obj.push_back(Pair("params_max",CClib_methods[i].maxargs));
|
||||
a.push_back(obj);
|
||||
}
|
||||
result.push_back(Pair("methods",a));
|
||||
UniValue result(UniValue::VOBJ);
|
||||
return(result);
|
||||
}
|
||||
|
||||
UniValue CClib(struct CCcontract_info *cp,char *method,char *jsonstr)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ); int32_t i; std::string rawtx; cJSON *params;
|
||||
//printf("CClib params.(%s)\n",jsonstr!=0?jsonstr:"");
|
||||
for (i=0; i<sizeof(CClib_methods)/sizeof(*CClib_methods); i++)
|
||||
{
|
||||
if ( cp->evalcode == CClib_methods[i].evalcode && strcmp(method,CClib_methods[i].method) == 0 )
|
||||
{
|
||||
if ( cp->evalcode == EVAL_FAUCET2 )
|
||||
{
|
||||
result.push_back(Pair("result","success"));
|
||||
result.push_back(Pair("method",CClib_methods[i].method));
|
||||
params = cJSON_Parse(jsonstr);
|
||||
rawtx = CClib_rawtxgen(cp,CClib_methods[i].funcid,params);
|
||||
free_json(params);
|
||||
result.push_back(Pair("rawtx",rawtx));
|
||||
return(result);
|
||||
} else return(CClib_method(cp,method,jsonstr));
|
||||
}
|
||||
}
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("method",method));
|
||||
result.push_back(Pair("error","method not found"));
|
||||
UniValue result(UniValue::VOBJ);
|
||||
return(result);
|
||||
}
|
||||
|
||||
int64_t IsCClibvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,char *cmpaddr)
|
||||
{
|
||||
char destaddr[64];
|
||||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
{
|
||||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cmpaddr) == 0 )
|
||||
return(tx.vout[v].nValue);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool CClibExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
|
||||
{
|
||||
static uint256 zerohash;
|
||||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
//fprintf(stderr,"vini.%d\n",i);
|
||||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
|
||||
{
|
||||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
||||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
|
||||
return eval->Invalid("cant find vinTx");
|
||||
else
|
||||
{
|
||||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
||||
if ( hashBlock == zerohash )
|
||||
return eval->Invalid("cant faucet2 from mempool");
|
||||
if ( (assetoshis= IsCClibvout(cp,vinTx,tx.vin[i].prevout.n,cp->unspendableCCaddr)) != 0 )
|
||||
inputs += assetoshis;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i<numvouts; i++)
|
||||
{
|
||||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
||||
if ( (assetoshis= IsCClibvout(cp,tx,i,cp->unspendableCCaddr)) != 0 )
|
||||
outputs += assetoshis;
|
||||
}
|
||||
if ( inputs != outputs+FAUCET2SIZE+txfee )
|
||||
{
|
||||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
|
||||
return eval->Invalid("mismatched inputs != outputs + FAUCET2SIZE + txfee");
|
||||
}
|
||||
else return(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx,unsigned int nIn)
|
||||
{
|
||||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
|
||||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
|
||||
if ( cp->evalcode != EVAL_FAUCET2 )
|
||||
{
|
||||
#ifdef BUILD_ROGUE
|
||||
return(rogue_validate(cp,height,eval,tx));
|
||||
#elif BUILD_CUSTOMCC
|
||||
return(custom_validate(cp,height,eval,tx));
|
||||
#elif BUILD_GAMESCC
|
||||
return(games_validate(cp,height,eval,tx));
|
||||
#else
|
||||
if ( cp->evalcode == EVAL_SUDOKU )
|
||||
return(sudoku_validate(cp,height,eval,tx));
|
||||
else if ( cp->evalcode == EVAL_MUSIG )
|
||||
return(musig_validate(cp,height,eval,tx));
|
||||
else if ( cp->evalcode == EVAL_DILITHIUM )
|
||||
return(dilithium_validate(cp,height,eval,tx));
|
||||
else return eval->Invalid("invalid evalcode");
|
||||
#endif
|
||||
}
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
preventCCvins = preventCCvouts = -1;
|
||||
if ( numvouts < 1 ) {
|
||||
return eval->Invalid("no vouts");
|
||||
} else {
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
|
||||
{
|
||||
fprintf(stderr,"faucetget invalid vini\n");
|
||||
return eval->Invalid("illegal normal vini");
|
||||
}
|
||||
}
|
||||
//fprintf(stderr,"check amounts\n");
|
||||
if ( CClibExactAmounts(cp,eval,tx,1,10000) == false ) {
|
||||
fprintf(stderr,"faucetget invalid amount\n");
|
||||
return false;
|
||||
} else {
|
||||
preventCCvouts = 1;
|
||||
if ( IsCClibvout(cp,tx,0,cp->unspendableCCaddr) != 0 )
|
||||
{
|
||||
preventCCvouts++;
|
||||
i = 1;
|
||||
} else i = 0;
|
||||
txid = tx.GetHash();
|
||||
memcpy(hash,&txid,sizeof(hash));
|
||||
fprintf(stderr,"check faucetget txid %s %02x/%02x\n",uint256_str(str,txid),hash[0],hash[31]);
|
||||
if ( tx.vout[i].nValue != FAUCET2SIZE )
|
||||
return eval->Invalid("invalid faucet output");
|
||||
else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 )
|
||||
return eval->Invalid("invalid faucetget txid");
|
||||
Getscriptaddress(destaddr,tx.vout[i].scriptPubKey);
|
||||
SetCCtxids(txids,destaddr,tx.vout[i].scriptPubKey.IsPayToCryptoCondition());
|
||||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
|
||||
{
|
||||
//int height = it->first.blockHeight;
|
||||
if ( CCduration(numblocks,it->first.txhash) > 0 && numblocks > 3 )
|
||||
{
|
||||
//fprintf(stderr,"would return error %s numblocks.%d ago\n",uint256_str(str,it->first.txhash),numblocks);
|
||||
return eval->Invalid("faucet2 is only for brand new addresses");
|
||||
}
|
||||
}
|
||||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
|
||||
if ( retval != 0 )
|
||||
fprintf(stderr,"faucet2get validated\n");
|
||||
else fprintf(stderr,"faucet2get invalid\n");
|
||||
return(retval);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t AddCClibInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs,char *cmpaddr,int32_t CCflag)
|
||||
{
|
||||
char coinaddr[64]; int64_t threshold,nValue,price,totalinputs = 0,txfee = 10000; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
GetCCaddress(cp,coinaddr,pk);
|
||||
SetCCunspents(unspentOutputs,coinaddr,CCflag!=0?true:false);
|
||||
if ( maxinputs > CC_MAXVINS )
|
||||
maxinputs = CC_MAXVINS;
|
||||
if ( maxinputs != 0 )
|
||||
threshold = total/maxinputs;
|
||||
else threshold = total;
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
vout = (int32_t)it->first.index;
|
||||
//char str[65]; fprintf(stderr,"%s check %s/v%d %.8f vs %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN,(double)threshold/COIN);
|
||||
if ( it->second.satoshis < threshold || it->second.satoshis == txfee )
|
||||
continue;
|
||||
// no need to prevent dup
|
||||
if ( myGetTransaction(txid,vintx,hashBlock) != 0 )
|
||||
{
|
||||
if ( (nValue= IsCClibvout(cp,vintx,vout,cmpaddr)) >= 1000000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 )
|
||||
{
|
||||
if ( total != 0 && maxinputs != 0 )
|
||||
mtx.vin.push_back(CTxIn(txid,vout,CScript()));
|
||||
nValue = it->second.satoshis;
|
||||
totalinputs += nValue;
|
||||
n++;
|
||||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
|
||||
break;
|
||||
} //else fprintf(stderr,"nValue %.8f too small or already spent in mempool\n",(double)nValue/COIN);
|
||||
} else fprintf(stderr,"couldnt get tx\n");
|
||||
}
|
||||
return(totalinputs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t AddCClibtxfee(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk)
|
||||
{
|
||||
char coinaddr[64]; int64_t nValue,txfee = 10000; uint256 txid,hashBlock; CTransaction vintx; int32_t vout;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
GetCCaddress(cp,coinaddr,pk);
|
||||
SetCCunspents(unspentOutputs,coinaddr,true);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
vout = (int32_t)it->first.index;
|
||||
//char str[65]; fprintf(stderr,"%s check %s/v%d %.8f vs %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN,(double)threshold/COIN);
|
||||
if ( it->second.satoshis < txfee )
|
||||
continue;
|
||||
if ( myGetTransaction(txid,vintx,hashBlock) != 0 )
|
||||
{
|
||||
if ( (nValue= IsCClibvout(cp,vintx,vout,coinaddr)) != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 )
|
||||
{
|
||||
mtx.vin.push_back(CTxIn(txid,vout,CScript()));
|
||||
return(it->second.satoshis);
|
||||
} //else fprintf(stderr,"nValue %.8f too small or already spent in mempool\n",(double)nValue/COIN);
|
||||
} else fprintf(stderr,"couldnt get tx\n");
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
std::string Faucet2Fund(struct CCcontract_info *cp,uint64_t txfee,int64_t funds)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,cclibpk; CScript opret;
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
cclibpk = GetUnspendable(cp,0);
|
||||
if ( AddNormalinputs2(mtx,funds+txfee,64) > 0 )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,cclibpk));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret));
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params)
|
||||
{
|
||||
CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,cclibpk; int64_t funds,txfee=0,inputs,CCchange=0,nValue=FAUCET2SIZE; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash;
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
if ( funcid == 'F' )
|
||||
{
|
||||
if ( cJSON_GetArraySize(params) > 0 )
|
||||
{
|
||||
funds = (int64_t)jdouble(jitem(params,0),0)*COIN + 0.0000000049;
|
||||
return(Faucet2Fund(cp,0,funds));
|
||||
} else return("");
|
||||
}
|
||||
else if ( funcid != 'G' )
|
||||
return("");
|
||||
cclibpk = GetUnspendable(cp,0);
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
if ( (inputs= AddCClibInputs(cp,mtx,cclibpk,nValue+txfee,60,cp->unspendableCCaddr,1)) > 0 )
|
||||
{
|
||||
if ( inputs > nValue )
|
||||
CCchange = (inputs - nValue - txfee);
|
||||
if ( CCchange != 0 )
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_FAUCET2,CCchange,cclibpk));
|
||||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
fprintf(stderr,"start at %u\n",(uint32_t)time(NULL));
|
||||
j = rand() & 0xfffffff;
|
||||
for (i=0; i<1000000; i++,j++)
|
||||
{
|
||||
tmpmtx = mtx;
|
||||
rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_FAUCET2 << (uint8_t)'G' << j));
|
||||
if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 )
|
||||
{
|
||||
len >>= 1;
|
||||
decode_hex(buf,len,(char *)rawhex.c_str());
|
||||
hash = bits256_doublesha256(0,buf,len);
|
||||
if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 )
|
||||
{
|
||||
fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL));
|
||||
return(rawhex);
|
||||
}
|
||||
//fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]);
|
||||
}
|
||||
}
|
||||
fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL));
|
||||
return("");
|
||||
} else fprintf(stderr,"cant find faucet inputs\n");
|
||||
return("");
|
||||
}
|
||||
|
||||
UniValue cclib_error(UniValue &result,const char *errorstr)
|
||||
{
|
||||
result.push_back(Pair("status","error"));
|
||||
result.push_back(Pair("error",errorstr));
|
||||
return(result);
|
||||
}
|
||||
|
||||
@@ -638,62 +160,10 @@ uint256 juint256(cJSON *obj)
|
||||
|
||||
int32_t cclib_parsehash(uint8_t *hash32,cJSON *item,int32_t len)
|
||||
{
|
||||
char *hexstr;
|
||||
if ( (hexstr= jstr(item,0)) != 0 && is_hexstr(hexstr,0) == len*2 )
|
||||
{
|
||||
decode_hex(hash32,len,hexstr);
|
||||
return(0);
|
||||
} else return(-1);
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef BUILD_ROGUE
|
||||
#include "rogue_rpc.cpp"
|
||||
#include "rogue/cursesd.c"
|
||||
#include "rogue/vers.c"
|
||||
#include "rogue/extern.c"
|
||||
#include "rogue/armor.c"
|
||||
#include "rogue/chase.c"
|
||||
#include "rogue/command.c"
|
||||
#include "rogue/daemon.c"
|
||||
#include "rogue/daemons.c"
|
||||
#include "rogue/fight.c"
|
||||
#include "rogue/init.c"
|
||||
#include "rogue/io.c"
|
||||
#include "rogue/list.c"
|
||||
#include "rogue/mach_dep.c"
|
||||
#include "rogue/rogue.c"
|
||||
#include "rogue/xcrypt.c"
|
||||
#include "rogue/mdport.c"
|
||||
#include "rogue/misc.c"
|
||||
#include "rogue/monsters.c"
|
||||
#include "rogue/move.c"
|
||||
#include "rogue/new_level.c"
|
||||
#include "rogue/options.c"
|
||||
#include "rogue/pack.c"
|
||||
#include "rogue/passages.c"
|
||||
#include "rogue/potions.c"
|
||||
#include "rogue/rings.c"
|
||||
#include "rogue/rip.c"
|
||||
#include "rogue/rooms.c"
|
||||
#include "rogue/save.c"
|
||||
#include "rogue/scrolls.c"
|
||||
#include "rogue/state.c"
|
||||
#include "rogue/sticks.c"
|
||||
#include "rogue/things.c"
|
||||
#include "rogue/weapons.c"
|
||||
#include "rogue/wizard.c"
|
||||
|
||||
#elif BUILD_CUSTOMCC
|
||||
#if BUILD_CUSTOMCC
|
||||
#include "customcc.cpp"
|
||||
|
||||
#elif BUILD_GAMESCC
|
||||
#include "rogue/cursesd.c"
|
||||
#include "gamescc.cpp"
|
||||
|
||||
#else
|
||||
#include "sudoku.cpp"
|
||||
#include "musig.cpp"
|
||||
#include "dilithium.c"
|
||||
//#include "../secp256k1/src/modules/musig/example.c"
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,807 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCchannels.h"
|
||||
|
||||
/*
|
||||
The idea here is to allow instant (mempool) payments that are secured by dPoW. In order to simplify things, channels CC will require creating reserves for each payee locked in the destination user's CC address. This will look like the payment is already made, but it is locked until further released. The dPoW protection comes from the cancel channel having a delayed effect until the next notarization. This way, if a payment release is made and the chain reorged, the same payment release will still be valid when it is re-broadcast into the mempool.
|
||||
|
||||
In order to achieve this effect, the payment release needs to be in a special form where its input cannot be spent only by the sender.
|
||||
|
||||
Given sender's payment to dest CC address, only the destination is able to spend, so we need to constrain that spending with a release mechanism. One idea is a 2of2 multisig, but that has the issue of needing confirmation and since a sender utxo is involved, subject to doublespend and we lose the speed. Another idea is release on secrets! since once revealed, the secret remains valid, this method is immune from double spend. Also, there is no worry about an MITM attack as the funds are only spendable by the destination pubkey and only with the secret. The secrets can be sent via any means, including OP_RETURN of normal transaction in the mempool.
|
||||
|
||||
Now the only remaining issue for sending is how to allocate funds to the secrets. This needs to be sent as hashes of secrets when the channel is created. A bruteforce method would be one secret per COIN, but for large amount channels this is cumbersome. A more practical approach is to have a set of secrets for each order of magnitude:
|
||||
|
||||
123.45 channel funds -> 1x secret100, 2x secret10, 3x secret1, 4x secret.1, 5x secret.01
|
||||
15 secrets achieves the 123.45 channel funding.
|
||||
|
||||
In order to avoid networking issues, the convention can be to send tx to normal address of destination with just an OP_RETURN, for the cost of txfee. For micropayments, a separate method of secret release needs to be established, but that is beyond the scope of this CC.
|
||||
|
||||
There is now the dPoW security that needs to be ensured. In order to close the channel, a tx needs to be confirmed that cancels the channel. As soon as this tx is seen, the destination will know that the channel will be closing soon, if the node is online. If not, the payments cant be credited anyway, so it seems no harm. Even after the channel is closed, it is possible for secrets to be releasing funds, but depending on when the notarization happens, it could invalidate the spends, so it is safest that as soon as the channel cancel tx is confirmed to invalidate any further payments released.
|
||||
|
||||
Given a channelclose and notarization confirmation (or enough blocks), the remaining funds needs to be able to come back to the sender. this means the funds need to be in a 1of2 CC multisig to allow either party to spend. Cheating is prevented by the additional rules of spending, ie. denomination secrets, or channelclose.
|
||||
|
||||
For efficiency we want to allow batch spend with multiple secrets to claim a single total
|
||||
|
||||
Second iteration:
|
||||
As implementing it, some efficieny gains to be made with a slightly different approach.
|
||||
Instead of separate secrets for each amount, a hashchain will be used, each releasing the same amount
|
||||
|
||||
To spend, the prior value in the hash chain is published, or can publish N deep. validation takes N hashes.
|
||||
|
||||
Also, in order to be able to track open channels, a tag is needed to be sent and better to send to a normal CC address for a pubkey to isolate the transactions for channel opens.
|
||||
|
||||
Possible third iteration:
|
||||
Let us try to setup a single "hot wallet" address to have all the channel funds and use it for payments to any destination. If there are no problems with reorgs and double spends, this would allow everyone to be "connected" to everyone else via the single special address.
|
||||
|
||||
So funds -> user's CC address along with hashchain, but likely best to have several utxo to span order of magnitudes.
|
||||
|
||||
a micropayment would then spend a utxo and attach a shared secret encoded unhashed link from the hashchain. That makes the receiver the only one that can decode the actual hashchain's prior value.
|
||||
|
||||
however, since this spend is only spendable by the sender, it is subject to a double spend attack. It seems it is a dead end. Alternative is to use the global CC address, but that commingles all funds from all users and any accounting error puts all funds at risk.
|
||||
|
||||
So, back to the second iteration, which is the only one so far that is immune from doublespend attack as the funds are already in the destination's CC address. One complication is that due to CC sorting of pubkeys, the address for sending and receiving is the same, so the destination pubkey needs to be attached to each opreturn.
|
||||
|
||||
Now when the prior hashchain value is sent via payment, it allows the receiver to spend the utxo, so the only protection needed is to prevent channel close from invalidating already made payments.
|
||||
|
||||
In order to allow multiple payments included in a single transaction, presentation of the N prior hashchain value can be used to get N payments and all the spends create a spending chain in sequential order of the hashchain.
|
||||
|
||||
*/
|
||||
|
||||
// start of consensus code
|
||||
|
||||
#define CC_MARKER_VALUE 10000
|
||||
|
||||
int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey srcpub, CPubKey destpub,int32_t v)
|
||||
{
|
||||
char destaddr[65],channeladdr[65],tokenschanneladdr[65];
|
||||
|
||||
GetCCaddress1of2(cp,channeladdr,srcpub,destpub);
|
||||
GetTokensCCaddress1of2(cp,tokenschanneladdr,srcpub,destpub);
|
||||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
{
|
||||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && (strcmp(destaddr,channeladdr) == 0 || strcmp(destaddr,tokenschanneladdr) == 0))
|
||||
return(tx.vout[v].nValue);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int64_t IsChannelsMarkervout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey pubkey,int32_t v)
|
||||
{
|
||||
char destaddr[65],ccaddr[65];
|
||||
|
||||
GetCCaddress(cp,ccaddr,pubkey);
|
||||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
{
|
||||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,ccaddr) == 0 )
|
||||
return(tx.vout[v].nValue);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
CScript EncodeChannelsOpRet(uint8_t funcid,uint256 tokenid,uint256 opentxid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain)
|
||||
{
|
||||
CScript opret; uint8_t evalcode = EVAL_CHANNELS;
|
||||
vscript_t vopret;
|
||||
|
||||
vopret = E_MARSHAL(ss << evalcode << funcid << opentxid << srcpub << destpub << numpayments << payment << hashchain);
|
||||
if (tokenid!=zeroid)
|
||||
{
|
||||
std::vector<CPubKey> pks;
|
||||
pks.push_back(srcpub);
|
||||
pks.push_back(destpub);
|
||||
return(EncodeTokenOpRet(tokenid,pks, std::make_pair(OPRETID_CHANNELSDATA, vopret)));
|
||||
}
|
||||
opret << OP_RETURN << vopret;
|
||||
return(opret);
|
||||
}
|
||||
|
||||
uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint256 &opentxid, CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain)
|
||||
{
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
std::vector<uint8_t> vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode;
|
||||
std::vector<CPubKey> pubkeys;
|
||||
|
||||
if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_CHANNELSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0)
|
||||
{
|
||||
vopret=vOpretExtra;
|
||||
}
|
||||
else GetOpReturnData(scriptPubKey, vopret);
|
||||
if ( vopret.size() > 2 )
|
||||
{
|
||||
script = (uint8_t *)vopret.data();
|
||||
if ( script[0] == EVAL_CHANNELS )
|
||||
{
|
||||
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> opentxid; ss >> srcpub; ss >> destpub; ss >> numpayments; ss >> payment; ss >> hashchain) != 0 )
|
||||
{
|
||||
return(f);
|
||||
}
|
||||
} else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "script[0] " << script[0] << " != EVAL_CHANNELS" << std::endl);
|
||||
} else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "not enough opret.[" << vopret.size() << "]" << std::endl);
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
|
||||
{
|
||||
uint256 txid,param3,tokenid;
|
||||
CPubKey srcpub,destpub;
|
||||
int32_t param1,numvouts; int64_t param2; uint8_t funcid;
|
||||
CTransaction vinTx; uint256 hashBlock; int64_t inputs=0,outputs=0;
|
||||
|
||||
if ((numvouts=tx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3))!=0)
|
||||
{
|
||||
switch (funcid)
|
||||
{
|
||||
case 'O':
|
||||
return (true);
|
||||
case 'P':
|
||||
if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
|
||||
return eval->Invalid("cant find vinTx");
|
||||
inputs = vinTx.vout[tx.vin[1].prevout.n].nValue;
|
||||
outputs = tx.vout[0].nValue + tx.vout[3].nValue;
|
||||
break;
|
||||
case 'C':
|
||||
if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
|
||||
return eval->Invalid("cant find vinTx");
|
||||
inputs = vinTx.vout[tx.vin[1].prevout.n].nValue;
|
||||
outputs = tx.vout[0].nValue;
|
||||
break;
|
||||
case 'R':
|
||||
if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 )
|
||||
return eval->Invalid("cant find vinTx");
|
||||
inputs = vinTx.vout[tx.vin[1].prevout.n].nValue;
|
||||
outputs = tx.vout[2].nValue;
|
||||
break;
|
||||
default:
|
||||
return (false);
|
||||
}
|
||||
if ( inputs != outputs )
|
||||
{
|
||||
LOGSTREAM("channelscc",CCLOG_INFO, stream << "inputs " << inputs << " vs outputs " << outputs << std::endl);
|
||||
return eval->Invalid("mismatched inputs != outputs");
|
||||
}
|
||||
else return (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return eval->Invalid("invalid op_return data");
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
|
||||
{
|
||||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numpayments,p1,param1; bool retval;
|
||||
uint256 txid,hashblock,p3,param3,opentxid,tmp_txid,genhashchain,hashchain,tokenid;
|
||||
uint8_t funcid,hash[32],hashdest[32]; char channeladdress[65],srcmarker[65],destmarker[65],destaddr[65],srcaddr[65],desttokensaddr[65],srctokensaddr[65];
|
||||
int64_t p2,param2,payment;
|
||||
CPubKey srcpub, destpub;
|
||||
CTransaction channelOpenTx,channelCloseTx,prevTx;
|
||||
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
preventCCvins = preventCCvouts = -1;
|
||||
if ( numvouts < 1 )
|
||||
return eval->Invalid("no vouts");
|
||||
else
|
||||
{
|
||||
if (ChannelsExactAmounts(cp,eval,tx,1,10000) == false )
|
||||
{
|
||||
return eval->Invalid("invalid channel inputs vs. outputs!");
|
||||
}
|
||||
else
|
||||
{
|
||||
txid = tx.GetHash();
|
||||
memcpy(hash,&txid,sizeof(hash));
|
||||
if ( (funcid = DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, opentxid, srcpub, destpub, param1, param2, param3)) != 0)
|
||||
{
|
||||
if (myGetTransaction(opentxid,channelOpenTx,hashblock)== 0)
|
||||
return eval->Invalid("invalid channelopen tx!");
|
||||
else if ((numvouts=channelOpenTx.vout.size()) > 0 && (DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 'O')
|
||||
return eval->Invalid("invalid channelopen OP_RETURN data!");
|
||||
GetCCaddress1of2(cp,channeladdress,srcpub,destpub);
|
||||
GetCCaddress(cp,srcmarker,srcpub);
|
||||
GetCCaddress(cp,destmarker,destpub);
|
||||
Getscriptaddress(srcaddr,CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG);
|
||||
Getscriptaddress(destaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG);
|
||||
_GetCCaddress(srctokensaddr,EVAL_TOKENS,srcpub);
|
||||
_GetCCaddress(desttokensaddr,EVAL_TOKENS,destpub);
|
||||
switch ( funcid )
|
||||
{
|
||||
case 'O':
|
||||
//vin.0: normal input
|
||||
//vout.0: CC vout for channel funding on CC1of2 pubkey
|
||||
//vout.1: CC vout marker to senders pubKey
|
||||
//vout.2: CC vout marker to receiver pubkey
|
||||
//vout.n-2: normal change
|
||||
//vout.n-1: opreturn - 'O' zerotxid senderspubkey receiverspubkey totalnumberofpayments paymentamount hashchain
|
||||
return eval->Invalid("unexpected ChannelsValidate for channelsopen!");
|
||||
case 'P':
|
||||
//vin.0: normal input
|
||||
//vin.1: CC input from channel funding
|
||||
//vin.2: CC input from src marker
|
||||
//vout.0: CC vout change to CC1of2 pubkey
|
||||
//vout.1: CC vout marker to senders pubKey
|
||||
//vout.2: CC vout marker to receiver pubkey
|
||||
//vout.3: normal output of payment amount to receiver pubkey
|
||||
//vout.n-2: normal change
|
||||
//vout.n-1: opreturn - 'P' opentxid senderspubkey receiverspubkey depth numpayments secret
|
||||
if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 )
|
||||
return eval->Invalid("vin.0 is normal for channelopen!");
|
||||
else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 )
|
||||
return eval->Invalid("vout.0 is CC or invalid amount for channelopen!");
|
||||
else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!");
|
||||
else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!");
|
||||
else if (hush_txnotarizedconfirmed(opentxid) == 0)
|
||||
return eval->Invalid("channelopen is not yet confirmed(notarized)!");
|
||||
else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
|
||||
return eval->Invalid("vin.0 is normal for channelpayment!");
|
||||
else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 )
|
||||
return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" is CC for channelpayment!");
|
||||
else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelpayment!");
|
||||
else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelpayment!");
|
||||
else if ( tokenid!=zeroid && ConstrainVout(tx.vout[3],1,desttokensaddr,param2*payment)==0 )
|
||||
return eval->Invalid("vout.3 is CC or invalid amount or invalid receiver for channelpayment!");
|
||||
else if ( tokenid==zeroid && ConstrainVout(tx.vout[3],0,destaddr,param2*payment)==0 )
|
||||
return eval->Invalid("vout.3 is normal or invalid amount or invalid receiver for channelpayment!");
|
||||
else if ( param1 > CHANNELS_MAXPAYMENTS)
|
||||
return eval->Invalid("too many payment increments!");
|
||||
else
|
||||
{
|
||||
endiancpy(hash, (uint8_t * ) & param3, 32);
|
||||
for (i = 0; i < numpayments-param1; i++)
|
||||
{
|
||||
vcalc_sha256(0, hashdest, hash, 32);
|
||||
memcpy(hash, hashdest, 32);
|
||||
}
|
||||
endiancpy((uint8_t*)&genhashchain,hashdest,32);
|
||||
if (hashchain!=genhashchain)
|
||||
return eval->Invalid("invalid secret for payment, does not reach final hashchain!");
|
||||
else if (tx.vout[3].nValue != param2*payment)
|
||||
return eval->Invalid("vout amount does not match number_of_payments*payment!");
|
||||
if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0)
|
||||
{
|
||||
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
|
||||
return eval->Invalid("invalid previous tx OP_RETURN data!");
|
||||
else if ( ConstrainVout(tx.vout[0],1,channeladdress,(p1-param2)*payment)==0 )
|
||||
return eval->Invalid("vout.0 is CC or invalid CC change amount for channelpayment!");
|
||||
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelpayment!");
|
||||
else if (param1+param2!=p1)
|
||||
return eval->Invalid("invalid payment depth!");
|
||||
else if (tx.vout[3].nValue > prevTx.vout[0].nValue)
|
||||
return eval->Invalid("not enough funds in channel for that amount!");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
//vin.0: normal input
|
||||
//vin.1: CC input from channel funding
|
||||
//vin.2: CC input from src marker
|
||||
//vout.0: CC vout for channel funding
|
||||
//vout.1: CC vout marker to senders pubKey
|
||||
//vout.2: CC vout marker to receiver pubkey
|
||||
//vout.n-2: normal change
|
||||
//vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey numpayments payment 0
|
||||
if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 )
|
||||
return eval->Invalid("vin.0 is normal for channelopen!");
|
||||
else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 )
|
||||
return eval->Invalid("vout.0 is CC or invalid amount for channelopen!");
|
||||
else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!");
|
||||
else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!");
|
||||
else if (hush_txnotarizedconfirmed(opentxid) == 0)
|
||||
return eval->Invalid("channelopen is not yet confirmed(notarized)!");
|
||||
else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
|
||||
return eval->Invalid("vin.0 is normal for channelclose!");
|
||||
else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 )
|
||||
return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" is CC for channelclose!");
|
||||
else if ( ConstrainVout(tx.vout[0],1,channeladdress,0)==0 )
|
||||
return eval->Invalid("vout.0 is CC for channelclose!");
|
||||
else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelclose!");
|
||||
else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelclose!");
|
||||
else if ( param1 > CHANNELS_MAXPAYMENTS)
|
||||
return eval->Invalid("too many payment increments!");
|
||||
else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0)
|
||||
{
|
||||
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
|
||||
return eval->Invalid("invalid previous tx OP_RETURN data!");
|
||||
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelclose!");
|
||||
else if (tx.vout[0].nValue != prevTx.vout[0].nValue)
|
||||
return eval->Invalid("invalid CC amount, amount must match funds in channel");
|
||||
}
|
||||
break;
|
||||
case 'R':
|
||||
//vin.0: normal input
|
||||
//vin.1: CC input from channel funding
|
||||
//vin.2: CC input from src marker
|
||||
//vout.0: CC vout marker to senders pubKey
|
||||
//vout.1: CC vout marker to receiver pubKey
|
||||
//vout.2: normal output of CC input to senders pubkey
|
||||
//vout.n-2: normal change
|
||||
//vout.n-1: opreturn - 'R' opentxid senderspubkey receiverspubkey numpayments payment closetxid
|
||||
if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 )
|
||||
return eval->Invalid("vin.0 is normal for channelopen!");
|
||||
else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 )
|
||||
return eval->Invalid("vout.0 is CC or invalid amount for channelopen!");
|
||||
else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!");
|
||||
else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!");
|
||||
else if (hush_txnotarizedconfirmed(opentxid) == 0)
|
||||
return eval->Invalid("channelopen is not yet confirmed(notarized)!");
|
||||
else if (hush_txnotarizedconfirmed(param3) == 0)
|
||||
return eval->Invalid("channelClose is not yet confirmed(notarized)!");
|
||||
else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
|
||||
return eval->Invalid("vin.0 is normal for channelrefund!");
|
||||
else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 )
|
||||
return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" CC for channelrefund!");
|
||||
else if ( ConstrainVout(tx.vout[0],1,srcmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.0 is CC marker to srcpub or invalid amount for channelrefund!");
|
||||
else if ( ConstrainVout(tx.vout[1],1,destmarker,CC_MARKER_VALUE)==0 )
|
||||
return eval->Invalid("vout.1 is CC marker to destpub or invalid amount for channelrefund!");
|
||||
else if ( tokenid!=zeroid && ConstrainVout(tx.vout[2],1,srctokensaddr,param1*payment)==0 )
|
||||
return eval->Invalid("vout.2 is CC or invalid amount or invalid receiver for channelrefund!");
|
||||
else if ( tokenid==zeroid && ConstrainVout(tx.vout[2],0,srcaddr,param1*payment)==0 )
|
||||
return eval->Invalid("vout.2 is normal or invalid amount or invalid receiver for channelrefund!");
|
||||
else if ( param1 > CHANNELS_MAXPAYMENTS)
|
||||
return eval->Invalid("too many payment increments!");
|
||||
else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0)
|
||||
{
|
||||
if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0)
|
||||
return eval->Invalid("invalid previous tx OP_RETURN data!");
|
||||
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker or invalid marker amount for channelrefund!");
|
||||
else if (tx.vout[2].nValue != prevTx.vout[0].nValue)
|
||||
return eval->Invalid("invalid amount, refund amount and funds in channel must match!");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"illegal channels funcid.(%c)\n",funcid);
|
||||
return eval->Invalid("unexpected channels funcid");
|
||||
}
|
||||
}
|
||||
else return eval->Invalid("unexpected channels missing funcid");
|
||||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
|
||||
if ( retval != 0 )
|
||||
LOGSTREAM("channels",CCLOG_INFO, stream << "Channels tx validated" << std::endl);
|
||||
else fprintf(stderr,"Channels tx invalid\n");
|
||||
return(retval);
|
||||
}
|
||||
}
|
||||
}
|
||||
// end of consensus code
|
||||
|
||||
// helper functions for rpc calls in rpcwallet.cpp
|
||||
|
||||
int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, CTransaction openTx, uint256 &prevtxid, CPubKey mypk)
|
||||
{
|
||||
char coinaddr[65]; int64_t param2,totalinputs = 0,numvouts; uint256 txid=zeroid,tmp_txid,hashBlock,param3,tokenid; CTransaction tx; int32_t marker,param1;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
CPubKey srcpub,destpub;
|
||||
uint8_t myprivkey[32];
|
||||
|
||||
if ((numvouts=openTx.vout.size()) > 0 && DecodeChannelsOpRet(openTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)=='O')
|
||||
{
|
||||
if (tokenid!=zeroid) GetTokensCCaddress1of2(cp,coinaddr,srcpub,destpub);
|
||||
else GetCCaddress1of2(cp,coinaddr,srcpub,destpub);
|
||||
SetCCunspents(unspentOutputs,coinaddr,true);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGSTREAM("channelscc",CCLOG_INFO, stream << "invalid channel open txid" << std::endl);
|
||||
return 0;
|
||||
}
|
||||
if (srcpub==mypk) marker=1;
|
||||
else marker=2;
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
if ( (int32_t)it->first.index==0 && myGetTransaction(it->first.txhash,tx,hashBlock) != 0 && (numvouts=tx.vout.size()) > 0)
|
||||
{
|
||||
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 &&
|
||||
(tmp_txid==openTx.GetHash() || tx.GetHash()==openTx.GetHash()) && IsChannelsMarkervout(cp,tx,marker==1?srcpub:destpub,marker)>0 &&
|
||||
(totalinputs=IsChannelsvout(cp,tx,srcpub,destpub,0))>0)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (txid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,0) != 0)
|
||||
{
|
||||
txid=zeroid;
|
||||
int32_t mindepth=CHANNELS_MAXPAYMENTS;
|
||||
std::vector<CTransaction> tmp_txs;
|
||||
myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,'P');
|
||||
for (std::vector<CTransaction>::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++)
|
||||
{
|
||||
const CTransaction &txmempool = *it;
|
||||
const uint256 &hash = txmempool.GetHash();
|
||||
|
||||
if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)=='P' &&
|
||||
tmp_txid==openTx.GetHash() && param1 < mindepth)
|
||||
{
|
||||
txid=hash;
|
||||
totalinputs=txmempool.vout[0].nValue;
|
||||
mindepth=param1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (txid != zeroid)
|
||||
{
|
||||
prevtxid=txid;
|
||||
mtx.vin.push_back(CTxIn(txid,0,CScript()));
|
||||
mtx.vin.push_back(CTxIn(txid,marker,CScript()));
|
||||
Myprivkey(myprivkey);
|
||||
if (tokenid!=zeroid) CCaddrTokens1of2set(cp,srcpub,destpub,myprivkey,coinaddr);
|
||||
else CCaddr1of2set(cp,srcpub,destpub,myprivkey,coinaddr);
|
||||
memset(myprivkey,0,32);
|
||||
return totalinputs;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
|
||||
UniValue ChannelOpen(const CPubKey& pk, uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment, uint256 tokenid)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
uint8_t hash[32],hashdest[32]; uint64_t amount,tokens=0,funds; int32_t i; uint256 hashchain,entropy,hentropy;
|
||||
CPubKey mypk; struct CCcontract_info *cp,*cpTokens,C,CTokens;
|
||||
|
||||
if ( numpayments <= 0 || payment <= 0 || numpayments > CHANNELS_MAXPAYMENTS )
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid ChannelOpen param numpayments." << numpayments << " payment." << payment << " - max_numpayments." << CHANNELS_MAXPAYMENTS);
|
||||
if (!destpub.IsFullyValid())
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid destination pubkey");
|
||||
if (numpayments <1)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid number of payments, must be greater than 0");
|
||||
if (payment <1)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid payment amount, must be greater than 0");
|
||||
cp = CCinit(&C,EVAL_CHANNELS);
|
||||
cpTokens = CCinit(&CTokens,EVAL_TOKENS);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
|
||||
funds = numpayments * payment;
|
||||
if (tokenid!=zeroid)
|
||||
{
|
||||
amount=AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,5,pk.IsValid());
|
||||
tokens=AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, funds, 64);
|
||||
}
|
||||
else amount=AddNormalinputs(mtx,mypk,funds+txfee+2*CC_MARKER_VALUE,64,pk.IsValid());
|
||||
if (amount+tokens >= funds+txfee+2*CC_MARKER_VALUE)
|
||||
{
|
||||
hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1);
|
||||
endiancpy(hash,(uint8_t *)&hentropy,32);
|
||||
for (i=0; i<numpayments; i++)
|
||||
{
|
||||
vcalc_sha256(0,hashdest,hash,32);
|
||||
memcpy(hash,hashdest,32);
|
||||
}
|
||||
endiancpy((uint8_t *)&hashchain,hashdest,32);
|
||||
if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub));
|
||||
else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub));
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk));
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub));
|
||||
if (tokenid!=zeroid && tokens>funds) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokens-funds,mypk));
|
||||
return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',tokenid,zeroid,mypk,destpub,numpayments,payment,hashchain)));
|
||||
}
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding funds");
|
||||
}
|
||||
|
||||
UniValue ChannelPayment(const CPubKey& pk, uint64_t txfee,uint256 opentxid,int64_t amount, uint256 secret)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,srcpub,destpub; uint256 txid,hashchain,gensecret,hashblock,entropy,hentropy,prevtxid,param3,tokenid;
|
||||
struct CCcontract_info *cp,C; int32_t i,funcid,prevdepth,numvouts,numpayments,totalnumpayments;
|
||||
int64_t payment,change,funds,param2;
|
||||
uint8_t hash[32],hashdest[32];
|
||||
CTransaction channelOpenTx,prevTx;
|
||||
|
||||
cp = CCinit(&C,EVAL_CHANNELS);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
|
||||
if (amount <1)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid payment amount, must be greater than 0");
|
||||
if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid");
|
||||
if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, totalnumpayments, payment, hashchain)=='O')
|
||||
{
|
||||
if (mypk != srcpub && mypk != destpub)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "this is not our channel");
|
||||
else if (amount % payment != 0 || amount<payment)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid amount, not a magnitude of payment size");
|
||||
else if (mypk == destpub && secret==zeroid) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid secret, secret is necessary when making payment from destination");
|
||||
}
|
||||
else
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx");
|
||||
if (hush_txnotarizedconfirmed(opentxid)==false) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsopen tx not yet confirmed/notarized");
|
||||
if (AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0)
|
||||
{
|
||||
if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && (change=funds-amount)>=0)
|
||||
{
|
||||
numpayments=amount/payment;
|
||||
if (myGetTransaction(prevtxid,prevTx,hashblock) != 0 && (numvouts=prevTx.vout.size()) > 0 &&
|
||||
((funcid = DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, prevdepth, param2, param3)) != 0) &&
|
||||
(funcid == 'P' || funcid=='O'))
|
||||
{
|
||||
if (numpayments > prevdepth)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "not enough funds in channel for that amount");
|
||||
else if (numpayments == 0)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid amount");
|
||||
if (secret!=zeroid)
|
||||
{
|
||||
endiancpy(hash, (uint8_t * ) & secret, 32);
|
||||
for (i = 0; i < totalnumpayments-(prevdepth-numpayments); i++)
|
||||
{
|
||||
vcalc_sha256(0, hashdest, hash, 32);
|
||||
memcpy(hash, hashdest, 32);
|
||||
}
|
||||
endiancpy((uint8_t * ) & gensecret, hashdest, 32);
|
||||
if (gensecret!=hashchain) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid secret supplied");
|
||||
}
|
||||
else
|
||||
{
|
||||
hentropy = DiceHashEntropy(entropy,channelOpenTx.vin[0].prevout.hash,channelOpenTx.vin[0].prevout.n,1);
|
||||
if (prevdepth-numpayments)
|
||||
{
|
||||
endiancpy(hash, (uint8_t * ) & hentropy, 32);
|
||||
for (i = 0; i < prevdepth-numpayments; i++)
|
||||
{
|
||||
vcalc_sha256(0, hashdest, hash, 32);
|
||||
memcpy(hash, hashdest, 32);
|
||||
}
|
||||
endiancpy((uint8_t * ) & secret, hashdest, 32);
|
||||
}
|
||||
else endiancpy((uint8_t * ) & secret, (uint8_t * ) & hentropy, 32);
|
||||
}
|
||||
}
|
||||
else
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid previous tx");
|
||||
if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub));
|
||||
else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub));
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,srcpub));
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub));
|
||||
if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, amount, destpub));
|
||||
else mtx.vout.push_back(CTxOut(amount, CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG));
|
||||
return (FinalizeCCTxExt(pk.IsValid(), 0, cp, mtx, mypk, txfee, EncodeChannelsOpRet('P', tokenid, opentxid, srcpub, destpub, prevdepth-numpayments, numpayments, secret)));
|
||||
}
|
||||
else
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs");
|
||||
}
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs");
|
||||
}
|
||||
|
||||
UniValue ChannelClose(const CPubKey& pk, uint64_t txfee,uint256 opentxid)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,srcpub,destpub; struct CCcontract_info *cp,C;
|
||||
CTransaction channelOpenTx;
|
||||
uint256 hashblock,tmp_txid,prevtxid,hashchain,tokenid;
|
||||
int32_t numvouts,numpayments;
|
||||
int64_t payment,funds;
|
||||
|
||||
// verify this is one of our outbound channels
|
||||
cp = CCinit(&C,EVAL_CHANNELS);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
|
||||
if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid");
|
||||
if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain)!='O')
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx");
|
||||
if (hush_txnotarizedconfirmed(opentxid)==false)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream <<"channelsopen tx not yet confirmed/notarized");
|
||||
if (mypk != srcpub)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "cannot close, you are not channel owner");
|
||||
if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0 )
|
||||
{
|
||||
if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0)
|
||||
{
|
||||
if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub));
|
||||
else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub));
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk));
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',tokenid,opentxid,mypk,destpub,funds/payment,payment,zeroid)));
|
||||
}
|
||||
else
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs");
|
||||
}
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs");
|
||||
}
|
||||
|
||||
UniValue ChannelRefund(const CPubKey& pk, uint64_t txfee,uint256 opentxid,uint256 closetxid)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk; struct CCcontract_info *cp,C; int64_t funds,payment,param2;
|
||||
int32_t i,numpayments,numvouts,param1;
|
||||
uint256 hashchain,hashblock,txid,prevtxid,param3,tokenid;
|
||||
CTransaction channelOpenTx,channelCloseTx,prevTx;
|
||||
CPubKey srcpub,destpub;
|
||||
|
||||
// verify stoptxid and origtxid match and are mine
|
||||
cp = CCinit(&C,EVAL_CHANNELS);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
|
||||
if (myGetTransaction(closetxid,channelCloseTx,hashblock) == 0)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel close txid");
|
||||
if ((numvouts=channelCloseTx.vout.size()) < 1 || DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,param1,param2,param3)!='C')
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel close tx");
|
||||
if (hush_txnotarizedconfirmed(closetxid)==false)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsclose tx not yet confirmed/notarized");
|
||||
if (txid!=opentxid)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "open and close txid are not from same channel");
|
||||
if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid");
|
||||
if (hush_txnotarizedconfirmed(opentxid)==false)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsopen tx not yet confirmed/notarized");
|
||||
if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,numpayments,payment,hashchain)!='O')
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx");
|
||||
if (mypk != srcpub)
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "cannot refund, you are not the channel owner");
|
||||
if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0 )
|
||||
{
|
||||
if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0)
|
||||
{
|
||||
if ((myGetTransaction(prevtxid,prevTx,hashblock) != 0) && (numvouts=prevTx.vout.size()) > 0 &&
|
||||
DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3) != 0)
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk));
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub));
|
||||
if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,funds,mypk));
|
||||
else mtx.vout.push_back(CTxOut(funds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',tokenid,opentxid,mypk,destpub,funds/payment,payment,closetxid)));
|
||||
}
|
||||
else
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "previous tx is invalid");
|
||||
}
|
||||
else
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs");
|
||||
}
|
||||
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs");
|
||||
}
|
||||
|
||||
UniValue ChannelsList(const CPubKey& pk)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ); std::vector<uint256> txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,tmp_txid,param3,tokenid;
|
||||
CTransaction tx; char myCCaddr[65],str[512],pub[34]; CPubKey mypk,srcpub,destpub; int32_t vout,numvouts,param1;
|
||||
int64_t nValue,param2;
|
||||
|
||||
cp = CCinit(&C,EVAL_CHANNELS);
|
||||
mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
|
||||
GetCCaddress(cp,myCCaddr,mypk);
|
||||
SetCCtxids(txids,myCCaddr,true,EVAL_CHANNELS,zeroid,'O');
|
||||
result.push_back(Pair("result","success"));
|
||||
result.push_back(Pair("name","Channels List"));
|
||||
for (std::vector<uint256>::const_iterator it=txids.begin(); it!=txids.end(); it++)
|
||||
{
|
||||
txid = *it;
|
||||
if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 )
|
||||
{
|
||||
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O')
|
||||
{
|
||||
sprintf(str,"%lld payments of %lld satoshi to %s",(long long)param1,(long long)param2,pubkey33_str(pub,(uint8_t *)&destpub));
|
||||
result.push_back(Pair(txid.GetHex().data(),str));
|
||||
}
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
UniValue ChannelsInfo(const CPubKey& pk,uint256 channeltxid)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ),array(UniValue::VARR); CTransaction tx,opentx; uint256 txid,tmp_txid,hashBlock,param3,opentxid,hashchain,tokenid;
|
||||
struct CCcontract_info *cp,C; char CCaddr[65],addr[65],str[512]; int32_t vout,numvouts,param1,numpayments;
|
||||
int64_t param2,payment; CPubKey srcpub,destpub,mypk;
|
||||
std::vector<uint256> txids; std::vector<CTransaction> txs;
|
||||
|
||||
cp = CCinit(&C,EVAL_CHANNELS);
|
||||
mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
|
||||
|
||||
if (myGetTransaction(channeltxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 &&
|
||||
(DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'O'))
|
||||
{
|
||||
GetCCaddress1of2(cp,CCaddr,srcpub,destpub);
|
||||
Getscriptaddress(addr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG);
|
||||
result.push_back(Pair("result","success"));
|
||||
result.push_back(Pair("Channel CC address",CCaddr));
|
||||
result.push_back(Pair("Destination address",addr));
|
||||
result.push_back(Pair("Number of payments",param1));
|
||||
if(tokenid!=zeroid)
|
||||
{
|
||||
result.push_back(Pair("Token id",tokenid.GetHex().data()));
|
||||
result.push_back(Pair("Denomination (token satoshi)",i64tostr(param2)));
|
||||
result.push_back(Pair("Amount (token satoshi)",i64tostr(param1*param2)));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("Denomination (satoshi)",i64tostr(param2)));
|
||||
result.push_back(Pair("Amount (satoshi)",i64tostr(param1*param2)));
|
||||
}
|
||||
GetCCaddress(cp,CCaddr,mypk);
|
||||
SetCCtxids(txids,CCaddr,true,EVAL_CHANNELS,channeltxid,0);
|
||||
for (std::vector<uint256>::const_iterator it=txids.begin(); it!=txids.end(); it++)
|
||||
{
|
||||
if (myGetTransaction(*it,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 &&
|
||||
DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 && (tmp_txid==channeltxid || tx.GetHash()==channeltxid))
|
||||
txs.push_back(tx);
|
||||
}
|
||||
std::vector<CTransaction> tmp_txs;
|
||||
myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,'P');
|
||||
for (std::vector<CTransaction>::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++)
|
||||
{
|
||||
const CTransaction &txmempool = *it;
|
||||
|
||||
if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'P' && tmp_txid==channeltxid)
|
||||
txs.push_back(txmempool);
|
||||
}
|
||||
for (std::vector<CTransaction>::const_iterator it=txs.begin(); it!=txs.end(); it++)
|
||||
{
|
||||
tx=*it;
|
||||
if ((numvouts= tx.vout.size()) > 0 )
|
||||
{
|
||||
UniValue obj(UniValue::VOBJ);
|
||||
if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O' && tx.GetHash()==channeltxid)
|
||||
{
|
||||
obj.push_back(Pair("Open",tx.GetHash().GetHex().data()));
|
||||
}
|
||||
else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'P' && opentxid==channeltxid)
|
||||
{
|
||||
if (myGetTransaction(opentxid,opentx,hashBlock) != 0 && (numvouts=opentx.vout.size()) > 0 &&
|
||||
DecodeChannelsOpRet(opentx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain) == 'O')
|
||||
{
|
||||
Getscriptaddress(str,tx.vout[3].scriptPubKey);
|
||||
obj.push_back(Pair("Payment",tx.GetHash().GetHex().data()));
|
||||
obj.push_back(Pair("Number of payments",param2));
|
||||
obj.push_back(Pair("Amount",param2*payment));
|
||||
obj.push_back(Pair("Destination",str));
|
||||
obj.push_back(Pair("Secret",param3.ToString().c_str()));
|
||||
obj.push_back(Pair("Payments left",param1));
|
||||
}
|
||||
}
|
||||
else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'C' && opentxid==channeltxid)
|
||||
{
|
||||
obj.push_back(Pair("Close",tx.GetHash().GetHex().data()));
|
||||
}
|
||||
else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'R' && opentxid==channeltxid)
|
||||
{
|
||||
Getscriptaddress(str,tx.vout[2].scriptPubKey);
|
||||
obj.push_back(Pair("Refund",tx.GetHash().GetHex().data()));
|
||||
obj.push_back(Pair("Amount",param1*param2));
|
||||
obj.push_back(Pair("Destination",str));
|
||||
}
|
||||
array.push_back(obj);
|
||||
}
|
||||
}
|
||||
result.push_back(Pair("Transactions",array));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("Error","Channel not found!"));
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
1871
src/cc/dice.cpp
1871
src/cc/dice.cpp
File diff suppressed because it is too large
Load Diff
3732
src/cc/dilithium.c
3732
src/cc/dilithium.c
File diff suppressed because one or more lines are too long
@@ -1,102 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "hash.h"
|
||||
#include "chain.h"
|
||||
#include "version.h"
|
||||
#include "script/cc.h"
|
||||
#include "cc/eval.h"
|
||||
#include "cc/betprotocol.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
|
||||
/*
|
||||
* Crypto-Condition EVAL method that resolves a dispute of a session
|
||||
*
|
||||
* IN: vm - AppVM virtual machine to verify states
|
||||
* IN: params - condition params
|
||||
* IN: disputeTx - transaction attempting to resolve dispute
|
||||
* IN: nIn - index of input of dispute tx
|
||||
*
|
||||
* disputeTx: attempt to resolve a dispute
|
||||
*
|
||||
* in 0: Spends Session TX first output, reveals DisputeHeader
|
||||
* out 0: OP_RETURN hash of payouts
|
||||
*/
|
||||
bool Eval::DisputePayout(AppVM &vm, std::vector<uint8_t> params, const CTransaction &disputeTx, unsigned int nIn)
|
||||
{
|
||||
if (disputeTx.vout.size() == 0) return Invalid("no-vouts");
|
||||
|
||||
// get payouts hash
|
||||
uint256 payoutHash;
|
||||
if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash))
|
||||
return Invalid("invalid-payout-hash");
|
||||
|
||||
// load params
|
||||
uint16_t waitBlocks;
|
||||
std::vector<uint8_t> vmParams;
|
||||
if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams))
|
||||
return Invalid("malformed-params");
|
||||
|
||||
// ensure that enough time has passed
|
||||
{
|
||||
CTransaction sessionTx;
|
||||
CBlockIndex sessionBlock;
|
||||
|
||||
// if unconformed its too soon
|
||||
if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock))
|
||||
return Error("couldnt-get-parent");
|
||||
|
||||
if (GetCurrentHeight() < sessionBlock.GetHeight() + waitBlocks)
|
||||
return Invalid("dispute-too-soon"); // Not yet
|
||||
}
|
||||
|
||||
// get spends
|
||||
std::vector<CTransaction> spends;
|
||||
if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends))
|
||||
return Error("couldnt-get-spends");
|
||||
|
||||
// verify result from VM
|
||||
int maxLength = -1;
|
||||
uint256 bestPayout;
|
||||
for (int i=1; i<spends.size(); i++)
|
||||
{
|
||||
std::vector<unsigned char> vmState;
|
||||
if (spends[i].vout.size() == 0) continue;
|
||||
if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue;
|
||||
auto out = vm.evaluate(vmParams, vmState);
|
||||
uint256 resultHash = SerializeHash(out.second);
|
||||
if (out.first > maxLength) {
|
||||
maxLength = out.first;
|
||||
bestPayout = resultHash;
|
||||
}
|
||||
// The below means that if for any reason there is a draw, the first dispute wins
|
||||
else if (out.first == maxLength) {
|
||||
if (bestPayout != payoutHash) {
|
||||
fprintf(stderr, "WARNING: VM has multiple solutions of same length\n");
|
||||
bestPayout = resultHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxLength == -1) return Invalid("no-evidence");
|
||||
|
||||
return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout");
|
||||
}
|
||||
@@ -37,32 +37,6 @@ extern pthread_mutex_t HUSH_CC_mutex;
|
||||
|
||||
bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn)
|
||||
{
|
||||
EvalRef eval;
|
||||
pthread_mutex_lock(&HUSH_CC_mutex);
|
||||
bool out = eval->Dispatch(cond, tx, nIn);
|
||||
pthread_mutex_unlock(&HUSH_CC_mutex);
|
||||
if ( eval->state.IsValid() != out)
|
||||
fprintf(stderr,"out %d vs %d isValid\n",(int32_t)out,(int32_t)eval->state.IsValid());
|
||||
//assert(eval->state.IsValid() == out);
|
||||
|
||||
if (eval->state.IsValid()) return true;
|
||||
|
||||
std::string lvl = eval->state.IsInvalid() ? "Invalid" : "Error!";
|
||||
fprintf(stderr, "CC Eval %s %s: %s spending tx %s\n",
|
||||
EvalToStr(cond->code[0]).data(),
|
||||
lvl.data(),
|
||||
eval->state.GetRejectReason().data(),
|
||||
tx.vin[nIn].prevout.hash.GetHex().data());
|
||||
if (eval->state.IsError()) fprintf(stderr, "Culprit: %s\n", EncodeHexTx(tx).data());
|
||||
CTransaction tmp;
|
||||
if (mempool.lookup(tx.GetHash(), tmp))
|
||||
{
|
||||
// This is to remove a payments airdrop if it gets stuck in the mempool.
|
||||
// Miner will mine 1 invalid block, but doesnt stop them mining until a restart.
|
||||
// This would almost never happen in normal use.
|
||||
std::list<CTransaction> dummy;
|
||||
mempool.remove(tx,dummy,true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -72,50 +46,7 @@ bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn)
|
||||
*/
|
||||
bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
|
||||
{
|
||||
struct CCcontract_info *cp;
|
||||
if (cond->codeLength == 0)
|
||||
return Invalid("empty-eval");
|
||||
|
||||
uint8_t ecode = cond->code[0];
|
||||
if ( ASSETCHAINS_CCDISABLES[ecode] != 0 )
|
||||
{
|
||||
// check if a height activation has been set.
|
||||
if ( mapHeightEvalActivate[ecode] == 0 || this->GetCurrentHeight() == 0 || mapHeightEvalActivate[ecode] > this->GetCurrentHeight() )
|
||||
{
|
||||
fprintf(stderr,"%s evalcode.%d %02x\n",txTo.GetHash().GetHex().c_str(),ecode,ecode);
|
||||
fprintf(stderr, "ac_ccactivateht: evalcode.%i activates at height.%i vs current height.%i\n", ecode, mapHeightEvalActivate[ecode], this->GetCurrentHeight());
|
||||
return Invalid("disabled-code, -ac_ccenables didnt include this ecode");
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> vparams(cond->code+1, cond->code+cond->codeLength);
|
||||
if ( ecode >= EVAL_FIRSTUSER && ecode <= EVAL_LASTUSER )
|
||||
{
|
||||
if ( ASSETCHAINS_CCLIB.size() > 0 && ASSETCHAINS_CCLIB == CClib_name() )
|
||||
return CClib_Dispatch(cond,this,vparams,txTo,nIn);
|
||||
else return Invalid("mismatched -ac_cclib vs CClib_name");
|
||||
}
|
||||
cp = &CCinfos[(int32_t)ecode];
|
||||
if ( cp->didinit == 0 )
|
||||
{
|
||||
CCinit(cp,ecode);
|
||||
cp->didinit = 1;
|
||||
}
|
||||
|
||||
switch ( ecode )
|
||||
{
|
||||
case EVAL_IMPORTPAYOUT:
|
||||
return ImportPayout(vparams, txTo, nIn);
|
||||
break;
|
||||
|
||||
case EVAL_IMPORTCOIN:
|
||||
return ImportCoin(vparams, txTo, nIn);
|
||||
break;
|
||||
|
||||
default:
|
||||
return(ProcessCC(cp,this, vparams, txTo, nIn));
|
||||
break;
|
||||
}
|
||||
return Invalid("invalid-code, dont forget to add EVAL_NEWCC to Eval::Dispatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,21 +59,13 @@ bool Eval::GetSpendsConfirmed(uint256 hash, std::vector<CTransaction> &spends) c
|
||||
|
||||
bool Eval::GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const
|
||||
{
|
||||
return(myGetTransaction(hash, txOut,hashBlock));
|
||||
/*if (!myGetTransaction(hash, txOut,hashBlock)) {
|
||||
return(myGetTransaction(hash, txOut,hashBlock));
|
||||
} else return(true);*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Eval::GetTxConfirmed(const uint256 &hash, CTransaction &txOut, CBlockIndex &block) const
|
||||
{
|
||||
uint256 hashBlock;
|
||||
if (!GetTxUnconfirmed(hash, txOut, hashBlock))
|
||||
return false;
|
||||
if (hashBlock.IsNull() || !GetBlock(hashBlock, block))
|
||||
return false;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int Eval::GetCurrentHeight() const
|
||||
@@ -152,12 +75,6 @@ unsigned int Eval::GetCurrentHeight() const
|
||||
|
||||
bool Eval::GetBlock(uint256 hash, CBlockIndex& blockIdx) const
|
||||
{
|
||||
auto r = mapBlockIndex.find(hash);
|
||||
if (r != mapBlockIndex.end()) {
|
||||
blockIdx = *r->second;
|
||||
return true;
|
||||
}
|
||||
fprintf(stderr, "CC Eval Error: Can't get block from index\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -171,13 +88,7 @@ int32_t Eval::GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t time
|
||||
|
||||
bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const
|
||||
{
|
||||
if (tx.vin.size() < 11) return false;
|
||||
|
||||
CrosschainAuthority auth;
|
||||
auth.requiredSigs = 11;
|
||||
auth.size = GetNotaries(auth.notaries, height, timestamp);
|
||||
|
||||
return CheckTxAuthority(tx, auth);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get MoM from a notarization tx hash (on HUSH)
|
||||
@@ -207,6 +118,7 @@ std::string Eval::GetAssetchainsSymbol() const
|
||||
/*
|
||||
* Notarization data, ie, OP_RETURN payload in notarization transactions
|
||||
*/
|
||||
// This is used by notarizationdb.cpp
|
||||
bool ParseNotarizationOpReturn(const CTransaction &tx, NotarizationData &data)
|
||||
{
|
||||
if (tx.vout.size() < 2) return false;
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCfaucet.h"
|
||||
#include "../txmempool.h"
|
||||
|
||||
/*
|
||||
This file implements a simple CC faucet as an example of how to make a new CC contract. It wont have any fancy sybil protection but will serve the purpose of a fully automated faucet.
|
||||
|
||||
In order to implement a faucet, we need to have it funded. Once it is funded, anybody should be able to get some reasonable small amount.
|
||||
|
||||
This leads to needing to lock the funding in a CC protected output. And to put a spending limit. We can do a per transaction spending limit quite easily with vout constraints. However, that would allow anybody to issue thousands of transactions per block, so we also need to add a rate limiter.
|
||||
|
||||
To implement this, we can simply make any faucet vout fund the faucet. Then we can limit the number of confirmed utxo by combining faucet outputs and then only using utxo which are confirmed. This combined with a vout size limit will drastically limit the funds that can be withdrawn from the faucet.
|
||||
*/
|
||||
|
||||
// start of consensus code
|
||||
|
||||
int64_t IsFaucetvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
|
||||
{
|
||||
char destaddr[64];
|
||||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
{
|
||||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
|
||||
return(tx.vout[v].nValue);
|
||||
//else fprintf(stderr,"dest.%s vs (%s)\n",destaddr,cp->unspendableCCaddr);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool FaucetExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
|
||||
{
|
||||
static uint256 zerohash;
|
||||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
//fprintf(stderr,"vini.%d\n",i);
|
||||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
|
||||
{
|
||||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
||||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
|
||||
return eval->Invalid("cant find vinTx");
|
||||
else
|
||||
{
|
||||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
||||
if ( hashBlock == zerohash )
|
||||
return eval->Invalid("cant faucet from mempool");
|
||||
if ( (assetoshis= IsFaucetvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
|
||||
inputs += assetoshis;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i<numvouts; i++)
|
||||
{
|
||||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
||||
if ( (assetoshis= IsFaucetvout(cp,tx,i)) != 0 )
|
||||
outputs += assetoshis;
|
||||
}
|
||||
if ( inputs != outputs+FAUCETSIZE+txfee )
|
||||
{
|
||||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
|
||||
return eval->Invalid("mismatched inputs != outputs + FAUCETSIZE + txfee");
|
||||
}
|
||||
else return(true);
|
||||
}
|
||||
|
||||
bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
|
||||
{
|
||||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
|
||||
std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
preventCCvins = preventCCvouts = -1;
|
||||
if ( numvouts < 1 )
|
||||
return eval->Invalid("no vouts");
|
||||
else
|
||||
{
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
|
||||
{
|
||||
fprintf(stderr,"faucetget invalid vini\n");
|
||||
return eval->Invalid("illegal normal vini");
|
||||
}
|
||||
}
|
||||
//fprintf(stderr,"check amounts\n");
|
||||
if ( FaucetExactAmounts(cp,eval,tx,1,10000) == false )
|
||||
{
|
||||
fprintf(stderr,"faucetget invalid amount\n");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
preventCCvouts = 1;
|
||||
if ( IsFaucetvout(cp,tx,0) != 0 )
|
||||
{
|
||||
preventCCvouts++;
|
||||
i = 1;
|
||||
} else i = 0;
|
||||
txid = tx.GetHash();
|
||||
memcpy(hash,&txid,sizeof(hash));
|
||||
fprintf(stderr,"check faucetget txid %s %02x/%02x\n",uint256_str(str,txid),hash[0],hash[31]);
|
||||
if ( tx.vout[i].nValue != FAUCETSIZE )
|
||||
return eval->Invalid("invalid faucet output");
|
||||
else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 )
|
||||
return eval->Invalid("invalid faucetget txid");
|
||||
Getscriptaddress(destaddr,tx.vout[i].scriptPubKey);
|
||||
SetCCtxids(txids,destaddr,tx.vout[i].scriptPubKey.IsPayToCryptoCondition());
|
||||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=txids.begin(); it!=txids.end(); it++)
|
||||
{
|
||||
//int height = it->first.blockHeight;
|
||||
if ( CCduration(numblocks,it->first.txhash) > 0 && numblocks > 3 )
|
||||
{
|
||||
return eval->Invalid("faucet is only for brand new addresses");
|
||||
}
|
||||
//fprintf(stderr,"txid %s numblocks.%d ago\n",uint256_str(str,it->first.txhash),numblocks);
|
||||
}
|
||||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
|
||||
if ( retval != 0 )
|
||||
fprintf(stderr,"faucetget validated\n");
|
||||
else fprintf(stderr,"faucetget invalid\n");
|
||||
return(retval);
|
||||
}
|
||||
}
|
||||
}
|
||||
// end of consensus code
|
||||
|
||||
// helper functions for rpc calls in rpcwallet.cpp
|
||||
|
||||
int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
|
||||
{
|
||||
char coinaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
GetCCaddress(cp,coinaddr,pk);
|
||||
SetCCunspents(unspentOutputs,coinaddr,true);
|
||||
if ( maxinputs > CC_MAXVINS )
|
||||
maxinputs = CC_MAXVINS;
|
||||
if ( maxinputs > 0 )
|
||||
threshold = total/maxinputs;
|
||||
else threshold = total;
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
vout = (int32_t)it->first.index;
|
||||
if ( it->second.satoshis < threshold )
|
||||
continue;
|
||||
//char str[65]; fprintf(stderr,"check %s/v%d %.8f`\n",uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
|
||||
// no need to prevent dup
|
||||
if ( myGetTransaction(txid,vintx,hashBlock) != 0 )
|
||||
{
|
||||
if ( (nValue= IsFaucetvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 )
|
||||
{
|
||||
if ( total != 0 && maxinputs != 0 )
|
||||
mtx.vin.push_back(CTxIn(txid,vout,CScript()));
|
||||
nValue = it->second.satoshis;
|
||||
totalinputs += nValue;
|
||||
n++;
|
||||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
|
||||
break;
|
||||
} else fprintf(stderr,"vout.%d nValue %.8f too small or already spent in mempool\n",vout,(double)nValue/COIN);
|
||||
} else fprintf(stderr,"couldn't get tx\n");
|
||||
}
|
||||
return(totalinputs);
|
||||
}
|
||||
|
||||
UniValue FaucetGet(const CPubKey& pk, uint64_t txfee)
|
||||
{
|
||||
CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey faucetpk; int64_t inputs,CCchange=0,nValue=FAUCETSIZE; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash;
|
||||
cp = CCinit(&C,EVAL_FAUCET);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
faucetpk = GetUnspendable(cp,0);
|
||||
CPubKey mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
|
||||
if ( (inputs= AddFaucetInputs(cp,mtx,faucetpk,nValue+txfee,60)) > 0 )
|
||||
{
|
||||
if ( inputs > nValue )
|
||||
CCchange = (inputs - nValue - txfee);
|
||||
if ( CCchange != 0 )
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_FAUCET,CCchange,faucetpk));
|
||||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
fprintf(stderr,"start at %u\n",(uint32_t)time(NULL));
|
||||
j = rand() & 0xfffffff;
|
||||
for (i=0; i<1000000; i++,j++)
|
||||
{
|
||||
tmpmtx = mtx;
|
||||
UniValue result = FinalizeCCTxExt(pk.IsValid (),-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_FAUCET << (uint8_t)'G' << j));
|
||||
if ( (len= (int32_t)result[JSON_HEXTX].getValStr().size()) > 0 && len < 65536 )
|
||||
{
|
||||
len >>= 1;
|
||||
decode_hex(buf,len,(char *)result[JSON_HEXTX].getValStr().c_str());
|
||||
hash = bits256_doublesha256(0,buf,len);
|
||||
if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 )
|
||||
{
|
||||
fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL));
|
||||
return result;
|
||||
}
|
||||
//fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]);
|
||||
}
|
||||
}
|
||||
CCERR_RESULT("faucet",CCLOG_ERROR, stream << "couldn't generate valid txid " << (uint32_t)time(NULL));
|
||||
} else CCERR_RESULT("faucet",CCLOG_ERROR, stream << "can't find faucet inputs");
|
||||
}
|
||||
|
||||
UniValue FaucetFund(const CPubKey& pk, uint64_t txfee,int64_t funds)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey faucetpk; CScript opret; struct CCcontract_info *cp,C;
|
||||
|
||||
cp = CCinit(&C,EVAL_FAUCET);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
CPubKey mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey());
|
||||
faucetpk = GetUnspendable(cp,0);
|
||||
if ( AddNormalinputs(mtx,mypk,funds+txfee,64,pk.IsValid()) > 0 )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_FAUCET,funds,faucetpk));
|
||||
return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,opret));
|
||||
}
|
||||
CCERR_RESULT("faucet",CCLOG_ERROR, stream << "can't find normal inputs");
|
||||
}
|
||||
|
||||
UniValue FaucetInfo()
|
||||
{
|
||||
UniValue result(UniValue::VOBJ); char numstr[64];
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey faucetpk; struct CCcontract_info *cp,C; int64_t funding;
|
||||
result.push_back(Pair("result","success"));
|
||||
result.push_back(Pair("name","Faucet"));
|
||||
cp = CCinit(&C,EVAL_FAUCET);
|
||||
faucetpk = GetUnspendable(cp,0);
|
||||
funding = AddFaucetInputs(cp,mtx,faucetpk,0,0);
|
||||
sprintf(numstr,"%.8f",(double)funding/COIN);
|
||||
result.push_back(Pair("funding",numstr));
|
||||
return(result);
|
||||
}
|
||||
192
src/cc/fsm.cpp
192
src/cc/fsm.cpp
@@ -1,192 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCfsm.h"
|
||||
#include "../txmempool.h"
|
||||
|
||||
/*
|
||||
FSM CC is a highlevel CC contract that mostly uses other CC contracts. A finite state machine is defined, which combines triggers, payments and whatever other events/actions into a state machine
|
||||
|
||||
*/
|
||||
|
||||
// start of consensus code
|
||||
|
||||
int64_t IsFSMvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
|
||||
{
|
||||
char destaddr[64];
|
||||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
{
|
||||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
|
||||
return(tx.vout[v].nValue);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool FSMExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
|
||||
{
|
||||
static uint256 zerohash;
|
||||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
//fprintf(stderr,"vini.%d\n",i);
|
||||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
|
||||
{
|
||||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
||||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
|
||||
return eval->Invalid("cant find vinTx");
|
||||
else
|
||||
{
|
||||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
||||
if ( hashBlock == zerohash )
|
||||
return eval->Invalid("cant FSM from mempool");
|
||||
if ( (assetoshis= IsFSMvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
|
||||
inputs += assetoshis;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i<numvouts; i++)
|
||||
{
|
||||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
||||
if ( (assetoshis= IsFSMvout(cp,tx,i)) != 0 )
|
||||
outputs += assetoshis;
|
||||
}
|
||||
if ( inputs != outputs+COIN+txfee )
|
||||
{
|
||||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
|
||||
return eval->Invalid("mismatched inputs != outputs + COIN + txfee");
|
||||
}
|
||||
else return(true);
|
||||
}
|
||||
|
||||
bool FSMValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
|
||||
{
|
||||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval;
|
||||
return eval->Invalid("no validation yet");
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
preventCCvins = preventCCvouts = -1;
|
||||
if ( numvouts < 1 )
|
||||
return eval->Invalid("no vouts");
|
||||
else
|
||||
{
|
||||
//fprintf(stderr,"check vins\n");
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
|
||||
{
|
||||
fprintf(stderr,"fsmget invalid vini\n");
|
||||
return eval->Invalid("illegal normal vini");
|
||||
}
|
||||
}
|
||||
//fprintf(stderr,"check amounts\n");
|
||||
if ( FSMExactAmounts(cp,eval,tx,1,10000) == false )
|
||||
{
|
||||
fprintf(stderr,"fsmget invalid amount\n");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
preventCCvouts = 1;
|
||||
if ( IsFSMvout(cp,tx,0) != 0 )
|
||||
{
|
||||
preventCCvouts++;
|
||||
i = 1;
|
||||
} else i = 0;
|
||||
if ( tx.vout[i].nValue != COIN )
|
||||
return eval->Invalid("invalid fsm output");
|
||||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
|
||||
if ( retval != 0 )
|
||||
fprintf(stderr,"fsmget validated\n");
|
||||
else fprintf(stderr,"fsmget invalid\n");
|
||||
return(retval);
|
||||
}
|
||||
}
|
||||
}
|
||||
// end of consensus code
|
||||
|
||||
// helper functions for rpc calls in rpcwallet.cpp
|
||||
|
||||
int64_t AddFSMInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
|
||||
{
|
||||
// add threshold check
|
||||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t n = 0;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
GetCCaddress(cp,coinaddr,pk);
|
||||
SetCCunspents(unspentOutputs,coinaddr,true);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
// prevent dup
|
||||
if ( it->second.satoshis < 1000000 )
|
||||
continue;
|
||||
if ( myGetTransaction(txid,vintx,hashBlock) != 0 )
|
||||
{
|
||||
if ( (nValue= IsFSMvout(cp,vintx,(int32_t)it->first.index)) > 0 )
|
||||
{
|
||||
if ( total != 0 && maxinputs != 0 )
|
||||
mtx.vin.push_back(CTxIn(txid,(int32_t)it->first.index,CScript()));
|
||||
nValue = it->second.satoshis;
|
||||
totalinputs += nValue;
|
||||
n++;
|
||||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(totalinputs);
|
||||
}
|
||||
|
||||
std::string FSMList()
|
||||
{
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string FSMCreate(uint64_t txfee,std::string name,std::string states)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,fsmpk; CScript opret; int64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C;
|
||||
cp = CCinit(&C,EVAL_FSM);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
fsmpk = GetUnspendable(cp,0);
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
if ( (inputs= AddFSMInputs(cp,mtx,fsmpk,nValue+txfee,60)) > 0 )
|
||||
{
|
||||
if ( inputs > nValue )
|
||||
CCchange = (inputs - nValue - txfee);
|
||||
if ( CCchange != 0 )
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_FSM,CCchange,fsmpk));
|
||||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,opret));
|
||||
} else fprintf(stderr,"cant find fsm inputs\n");
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string FSMInfo(uint256 fsmtxid)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,fsmpk; int64_t funds = 0; CScript opret; struct CCcontract_info *cp,C;
|
||||
cp = CCinit(&C,EVAL_FSM);
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
fsmpk = GetUnspendable(cp,0);
|
||||
return("");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,292 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include "prices.h"
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#ifdef BUILD_GAMESCC
|
||||
#include "../rogue/cursesd.h"
|
||||
#else
|
||||
#include <curses.h>
|
||||
#endif
|
||||
|
||||
#define SATOSHIDEN ((uint64_t)100000000L)
|
||||
#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"prices",cmdstr,0,0,0)
|
||||
extern int64_t Net_change,Betsize;
|
||||
|
||||
int random_tetromino(struct games_state *rs)
|
||||
{
|
||||
rs->seed = _games_rngnext(rs->seed);
|
||||
return(rs->seed % NUM_TETROMINOS);
|
||||
}
|
||||
|
||||
int32_t pricesdata(struct games_player *P,void *ptr)
|
||||
{
|
||||
tetris_game *tg = (tetris_game *)ptr;
|
||||
P->gold = tg->points;
|
||||
P->dungeonlevel = tg->level;
|
||||
//fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level);
|
||||
return(0);
|
||||
}
|
||||
|
||||
void sleep_milli(int milliseconds)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = milliseconds * 1000 * 1000;
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
struct games_state globalR;
|
||||
extern char Gametxidstr[];
|
||||
int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c);
|
||||
uint64_t get_btcusd();
|
||||
int32_t issue_bet(struct games_state *rs,int64_t x,int64_t betsize);
|
||||
|
||||
void *gamesiterate(struct games_state *rs)
|
||||
{
|
||||
bool running = true; uint32_t eventid = 0; int64_t price;
|
||||
if ( rs->guiflag != 0 || rs->sleeptime != 0 )
|
||||
{
|
||||
initscr(); // initialize curses
|
||||
cbreak(); // pass key presses to program, but not signals
|
||||
noecho(); // don't echo key presses to screen
|
||||
timeout(0);
|
||||
}
|
||||
while ( running != 0 )
|
||||
{
|
||||
//running = tg_tick(rs,tg,move);
|
||||
if ( rs->guiflag != 0 || rs->sleeptime != 0 )
|
||||
{
|
||||
}
|
||||
if ( rs->guiflag != 0 )
|
||||
{
|
||||
#ifdef STANDALONE
|
||||
price = get_btcusd();
|
||||
//fprintf(stderr,"%llu -> t%u %.4f\n",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000);
|
||||
//issue_games_events(rs,Gametxidstr,eventid,price);
|
||||
issue_bet(rs,price,Betsize);
|
||||
eventid++;
|
||||
doupdate();
|
||||
sleep(10);
|
||||
switch ( getch() )
|
||||
{
|
||||
case '+': Net_change++; break;
|
||||
case '-': Net_change--; break;
|
||||
case '0': Net_change = 0; break;
|
||||
case '$': Betsize = SATOSHIDEN; break;
|
||||
case '^': Betsize += (Betsize >> 3); break;
|
||||
case '/': Betsize -= (Betsize >> 3); break;
|
||||
}
|
||||
/*if ( (counter++ % 10) == 0 )
|
||||
doupdate();
|
||||
c = games_readevent(rs);
|
||||
if ( c <= 0x7f || skipcount == 0x3fff )
|
||||
{
|
||||
if ( skipcount > 0 )
|
||||
issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000);
|
||||
if ( c <= 0x7f )
|
||||
issue_games_events(rs,Gametxidstr,eventid,c);
|
||||
if ( tg->level != prevlevel )
|
||||
{
|
||||
flushkeystrokes(rs,0);
|
||||
prevlevel = tg->level;
|
||||
}
|
||||
skipcount = 0;
|
||||
} else skipcount++;*/
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( rs->replaydone != 0 )
|
||||
break;
|
||||
if ( rs->sleeptime != 0 )
|
||||
{
|
||||
sleep_milli(1);
|
||||
}
|
||||
/*if ( skipcount == 0 )
|
||||
{
|
||||
c = games_readevent(rs);
|
||||
//fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level);
|
||||
if ( (c & 0x4000) == 0x4000 )
|
||||
{
|
||||
skipcount = (c & 0x3fff);
|
||||
c = 'S';
|
||||
}
|
||||
}
|
||||
if ( skipcount > 0 )
|
||||
skipcount--;*/
|
||||
}
|
||||
eventid++;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifdef STANDALONE
|
||||
#include <ncurses.h>
|
||||
#include "dapps/dappstd.c"
|
||||
int64_t Net_change,Betsize = SATOSHIDEN;
|
||||
|
||||
char *send_curl(char *url,char *fname)
|
||||
{
|
||||
char *retstr;
|
||||
retstr = issue_curl(url);
|
||||
return(retstr);
|
||||
}
|
||||
|
||||
cJSON *get_urljson(char *url,char *fname)
|
||||
{
|
||||
char *jsonstr; cJSON *json = 0;
|
||||
if ( (jsonstr= send_curl(url,fname)) != 0 )
|
||||
{
|
||||
//printf("(%s) -> (%s)\n",url,jsonstr);
|
||||
json = cJSON_Parse(jsonstr);
|
||||
free(jsonstr);
|
||||
}
|
||||
return(json);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// start of dapp
|
||||
//////////////////////////////////////////////
|
||||
|
||||
uint64_t get_btcusd()
|
||||
{
|
||||
cJSON *pjson,*bpi,*usd; char str[512]; uint64_t x,newprice,mult,btcusd = 0;
|
||||
if ( (pjson= get_urljson((char *)"http://api.coindesk.com/v1/bpi/currentprice.json",(char *)"/tmp/oraclefeed.json")) != 0 )
|
||||
{
|
||||
if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 && (usd= jobj(bpi,(char *)"USD")) != 0 )
|
||||
{
|
||||
btcusd = jdouble(usd,(char *)"rate_float") * SATOSHIDEN;
|
||||
mult = 10000 + Net_change*10;
|
||||
newprice = (btcusd * mult) / 10000;
|
||||
x = ((uint64_t)time(NULL) << 32) | ((newprice / 10000) & 0xffffffff);
|
||||
sprintf(str,"BTC/USD %.4f -> Betsize %.8f (^ / to change) && %.4f Net %.1f%% [+ - to change]\n",dstr(btcusd),dstr(Betsize),dstr(newprice),(double)100*(mult-10000)/10000);
|
||||
mvaddstr(0, 0, str);
|
||||
clrtoeol();
|
||||
doupdate();
|
||||
}
|
||||
free_json(pjson);
|
||||
}
|
||||
return(x);
|
||||
}
|
||||
|
||||
char *clonestr(char *str)
|
||||
{
|
||||
char *clone; int32_t len;
|
||||
if ( str == 0 || str[0] == 0 )
|
||||
{
|
||||
printf("warning cloning nullstr.%p\n",str);
|
||||
#ifdef __APPLE__
|
||||
while ( 1 ) sleep(1);
|
||||
#endif
|
||||
str = (char *)"<nullstr>";
|
||||
}
|
||||
len = strlen(str);
|
||||
clone = (char *)calloc(1,len+16);
|
||||
strcpy(clone,str);
|
||||
return(clone);
|
||||
}
|
||||
|
||||
int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c)
|
||||
{
|
||||
static FILE *fp;
|
||||
char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1;
|
||||
if ( fp == 0 )
|
||||
fp = fopen("events.log","wb");
|
||||
rs->buffered[rs->num++] = c;
|
||||
if ( 1 )
|
||||
{
|
||||
if ( sizeof(c) == 1 )
|
||||
sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",(uint8_t)c&0xff,gametxidstr,eventid);
|
||||
else if ( sizeof(c) == 2 )
|
||||
sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",(uint16_t)c&0xffff,gametxidstr,eventid);
|
||||
else if ( sizeof(c) == 4 )
|
||||
sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",(uint32_t)c&0xffffffff,gametxidstr,eventid);
|
||||
else if ( sizeof(c) == 8 )
|
||||
sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid);
|
||||
if ( (retstr= hush_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 )
|
||||
{
|
||||
if ( (retjson= cJSON_Parse(retstr)) != 0 )
|
||||
{
|
||||
if ( (resobj= jobj(retjson,(char *)"result")) != 0 )
|
||||
{
|
||||
retval = 0;
|
||||
if ( fp != 0 )
|
||||
{
|
||||
fprintf(fp,"%s\n",jprint(resobj,0));
|
||||
fflush(fp);
|
||||
}
|
||||
}
|
||||
free_json(retjson);
|
||||
} else fprintf(fp,"error parsing %s\n",retstr);
|
||||
free(retstr);
|
||||
} else fprintf(fp,"error issuing method %s\n",params);
|
||||
return(retval);
|
||||
} else return(0);
|
||||
}
|
||||
|
||||
int32_t issue_bet(struct games_state *rs,int64_t x,int64_t betsize)
|
||||
{
|
||||
char params[512],hexstr[64],*retstr; cJSON *retjson,*resobj; int32_t i,retval = -1;
|
||||
memset(hexstr,0,sizeof(hexstr));
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
sprintf(&hexstr[i<<1],"%02x",(uint8_t)(x & 0xff));
|
||||
x >>= 8;
|
||||
}
|
||||
sprintf(params,"[\"bet\",\"17\",\"[%.8f,%%22%s%%22]\"]",dstr(betsize),hexstr);
|
||||
if ( (retstr= hush_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 )
|
||||
{
|
||||
if ( (retjson= cJSON_Parse(retstr)) != 0 )
|
||||
{
|
||||
if ( (resobj= jobj(retjson,(char *)"result")) != 0 )
|
||||
{
|
||||
retval = 0;
|
||||
//fprintf(stderr,"%s\n",jprint(resobj,0));
|
||||
}
|
||||
free_json(retjson);
|
||||
}
|
||||
free(retstr);
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
||||
int prices(int argc, char **argv)
|
||||
{
|
||||
struct games_state *rs = &globalR;
|
||||
int32_t c,skipcount=0; uint32_t eventid = 0;
|
||||
memset(rs,0,sizeof(*rs));
|
||||
rs->guiflag = 1;
|
||||
rs->sleeptime = 1; // non-zero to allow refresh()
|
||||
if ( argc >= 2 && strlen(argv[2]) == 64 )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
rs->origseed = _strtoui64(argv[1], NULL, 10);
|
||||
#else
|
||||
rs->origseed = atol(argv[1]); // windows, but not MSVC
|
||||
#endif // _MSC_VER
|
||||
#else
|
||||
rs->origseed = atol(argv[1]); // non-windows
|
||||
#endif // _WIN32
|
||||
rs->seed = rs->origseed;
|
||||
if ( argc >= 3 )
|
||||
{
|
||||
strcpy(Gametxidstr,argv[2]);
|
||||
fprintf(stderr,"setplayerdata %s\n",Gametxidstr);
|
||||
if ( games_setplayerdata(rs,Gametxidstr) < 0 )
|
||||
{
|
||||
fprintf(stderr,"invalid gametxid, or already started\n");
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
} else rs->seed = 777;
|
||||
gamesiterate(rs);
|
||||
//gamesbailout(rs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
std::string MYCCLIBNAME = (char *)"prices";
|
||||
|
||||
#define PRICES_BETPERIOD 3
|
||||
UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag);
|
||||
extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33];
|
||||
|
||||
#define bstr(x) ((double)((uint32_t)x) / 10000.)
|
||||
|
||||
struct prices_bar
|
||||
{
|
||||
uint64_t open,high,low,close,sum;
|
||||
int32_t num;
|
||||
};
|
||||
|
||||
int32_t prices_barupdate(struct prices_bar *bar,uint64_t pricebits)
|
||||
{
|
||||
uint32_t uprice,timestamp;
|
||||
timestamp = (uint32_t)(pricebits >> 32);
|
||||
uprice = (uint32_t)pricebits;
|
||||
bar->sum += uprice, bar->num++;
|
||||
if ( bar->open == 0 )
|
||||
bar->open = bar->high = bar->low = pricebits;
|
||||
if ( uprice > (uint32_t)bar->high )
|
||||
bar->high = pricebits;
|
||||
else if ( uprice < (uint32_t)bar->low )
|
||||
bar->low = pricebits;
|
||||
bar->close = pricebits;
|
||||
return(0);
|
||||
}
|
||||
|
||||
int64_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebits)
|
||||
{
|
||||
int64_t a,dist = 0;
|
||||
if ( aveprice != 0 )
|
||||
{
|
||||
a = (pricebits & 0xffffffff);
|
||||
dist = (a - aveprice);
|
||||
dist *= dist;
|
||||
//fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice);
|
||||
}
|
||||
return(dist);
|
||||
}
|
||||
|
||||
void prices_bardisp(struct prices_bar *bar)
|
||||
{
|
||||
if ( bar->num == 0 )
|
||||
fprintf(stderr,"BAR null\n");
|
||||
else fprintf(stderr,"BAR ave %.4f (O %.4f, H %.4f, L %.4f, C %.4f)\n",bstr(bar->sum/bar->num),bstr(bar->open),bstr(bar->high),bstr(bar->low),bstr(bar->close));
|
||||
}
|
||||
|
||||
int64_t prices_blockinfo(int32_t height,char *acaddr)
|
||||
{
|
||||
std::vector<uint8_t> vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t dist,mindist=(1LL<<60),prizefund = 0; int32_t mini=-1,i,n,vini,numvouts,iter; struct prices_bar refbar;
|
||||
if ( (pindex= hush_chainactive(height)) != 0 )
|
||||
{
|
||||
if ( hush_blockload(block,pindex) == 0 )
|
||||
{
|
||||
n = block.vtx.size();
|
||||
vini = 0;
|
||||
memset(&refbar,0,sizeof(refbar));
|
||||
for (iter=0; iter<2; iter++)
|
||||
{
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
tx = block.vtx[i];
|
||||
if ( myGetTransaction(tx.vin[vini].prevout.hash,vintx,hashBlock) == 0 )
|
||||
continue;
|
||||
else if ( tx.vin[vini].prevout.n >= vintx.vout.size() || Getscriptaddress(destaddr,vintx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 )
|
||||
continue;
|
||||
else if ( (numvouts= tx.vout.size()) > 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a )
|
||||
{
|
||||
GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret);
|
||||
if ( vopret.size() == 8 )
|
||||
{
|
||||
E_UNMARSHAL(vopret,ss >> pricebits);
|
||||
timestamp = (uint32_t)(pricebits >> 32);
|
||||
uprice = (uint32_t)pricebits;
|
||||
if ( iter == 0 )
|
||||
{
|
||||
prizefund += tx.vout[0].nValue;
|
||||
if ( strcmp(acaddr,destaddr) == 0 )
|
||||
{
|
||||
//fprintf(stderr,"REF ");
|
||||
prices_barupdate(&refbar,pricebits);
|
||||
}
|
||||
}
|
||||
else if ( strcmp(acaddr,destaddr) != 0 )
|
||||
{
|
||||
dist = prices_bardist(&refbar,aveprice,pricebits);
|
||||
if ( dist < mindist )
|
||||
{
|
||||
mindist = dist;
|
||||
mini = i;
|
||||
}
|
||||
fprintf(stderr,"mini.%d i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%lld\n",mini,i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,(long long)dist);
|
||||
}
|
||||
} else return(-3);
|
||||
}
|
||||
}
|
||||
if ( iter == 0 )
|
||||
{
|
||||
prices_bardisp(&refbar);
|
||||
if ( refbar.num != 0 )
|
||||
aveprice = (uint32_t)refbar.sum / refbar.num;
|
||||
}
|
||||
}
|
||||
return(prizefund);
|
||||
} else return(-2);
|
||||
} else return(-1);
|
||||
}
|
||||
|
||||
UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ); char acaddr[64]; CPubKey acpk,mypk,gamespk; int64_t prizefund = 0; int32_t height,nextheight = hush_nextheight();
|
||||
if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 )
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error"," no -ac_pubkey for price reference"));
|
||||
return(result);
|
||||
}
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
gamespk = GetUnspendable(cp,0);
|
||||
acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33);
|
||||
Getscriptaddress(acaddr,CScript() << ParseHex(HexStr(acpk)) << OP_CHECKSIG);
|
||||
if ( params != 0 && cJSON_GetArraySize(params) == 1 )
|
||||
{
|
||||
height = juint(jitem(params,0),0);
|
||||
result.push_back(Pair("height",(int64_t)height));
|
||||
if ( (prizefund= prices_blockinfo(height,acaddr)) < 0 )
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("errorcode",prizefund));
|
||||
result.push_back(Pair("error","blockinfo error"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// display bets
|
||||
if ( height <= nextheight-PRICES_BETPERIOD )
|
||||
{
|
||||
// settle bets by first nonzero reference bar
|
||||
}
|
||||
result.push_back(Pair("prizefund",ValueFromAmount(prizefund)));
|
||||
result.push_back(Pair("result","success"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","couldnt parse"));
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; uint64_t price; CPubKey gamespk,mypk,acpk;
|
||||
if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 )
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error"," no -ac_pubkey for price reference"));
|
||||
return(result);
|
||||
}
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
gamespk = GetUnspendable(cp,0);
|
||||
acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33);
|
||||
if ( params != 0 && cJSON_GetArraySize(params) == 2 )
|
||||
{
|
||||
amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049;
|
||||
if ( cclib_parsehash((uint8_t *)&price,jitem(params,1),8) < 0 )
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","couldnt parsehash"));
|
||||
return(result);
|
||||
}
|
||||
if ( mypk == acpk )
|
||||
{
|
||||
amount = 0; // i am the reference price feed
|
||||
//fprintf(stderr,"i am the reference\n");
|
||||
}
|
||||
//fprintf(stderr,"amount %llu price %llx\n",(long long)amount,(long long)price);
|
||||
if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk));
|
||||
rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,CScript() << OP_RETURN << price);
|
||||
return(games_rawtxresult(result,rawtx,1));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","not enough funds"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","couldnt parse"));
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
void prices_update(uint32_t timestamp,uint32_t uprice,int32_t ismine)
|
||||
{
|
||||
//fprintf(stderr,"%s t%u %.4f %16llx\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000,(long long)((uint64_t)timestamp<<32) | uprice);
|
||||
}
|
||||
|
||||
// game specific code for daemon
|
||||
void games_packitemstr(char *packitemstr,struct games_packitem *item)
|
||||
{
|
||||
strcpy(packitemstr,"");
|
||||
}
|
||||
|
||||
int64_t games_cashout(struct games_player *P)
|
||||
{
|
||||
int32_t dungeonlevel = P->dungeonlevel; int64_t mult=1000,cashout = 0;
|
||||
cashout = (uint64_t)P->gold * mult * dungeonlevel * dungeonlevel;
|
||||
return(cashout);
|
||||
}
|
||||
|
||||
void pricesplayerjson(UniValue &obj,struct games_player *P)
|
||||
{
|
||||
obj.push_back(Pair("packsize",(int64_t)P->packsize));
|
||||
obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints));
|
||||
obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff)));
|
||||
obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16)));
|
||||
obj.push_back(Pair("level",(int64_t)P->level));
|
||||
obj.push_back(Pair("experience",(int64_t)P->experience));
|
||||
obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel));
|
||||
}
|
||||
|
||||
int32_t disp_gamesplayer(char *str,struct games_player *P)
|
||||
{
|
||||
str[0] = 0;
|
||||
//if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 )
|
||||
// return(-1);
|
||||
sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector<uint8_t> payload)
|
||||
{
|
||||
uint256 gametxid; int32_t i,len; char str[67]; int64_t price; uint32_t eventid = 0;
|
||||
if ( (len= payload.size()) > 36 )
|
||||
{
|
||||
len -= 36;
|
||||
for (i=0; i<32; i++)
|
||||
((uint8_t *)&gametxid)[i] = payload[len+i];
|
||||
eventid = (uint32_t)payload[len+32];
|
||||
eventid |= (uint32_t)payload[len+33] << 8;
|
||||
eventid |= (uint32_t)payload[len+34] << 16;
|
||||
eventid |= (uint32_t)payload[len+35] << 24;
|
||||
for (i=0; i<len&&i<sizeof(price); i++)
|
||||
((uint8_t *)&price)[7-i] = payload[i];
|
||||
prices_update((uint32_t)(price >> 32),(uint32_t)(price & 0xffffffff),pk == pubkey2pk(Mypubkey()));
|
||||
//fprintf(stderr,"%llu -> t%u %.4f ",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000);
|
||||
//fprintf(stderr," got payload, from %s %s/e%d\n",pubkey33_str(str,(uint8_t *)&pk),gametxid.GetHex().c_str(),eventid);
|
||||
return(0);
|
||||
} else return(-1);
|
||||
}
|
||||
|
||||
bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx)
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#ifndef H_PRICES_H
|
||||
#define H_PRICES_H
|
||||
|
||||
/***************************************************************************/
|
||||
/** https://github.com/brenns10/tetris
|
||||
@file main.c
|
||||
@author Stephen Brennan
|
||||
@date Created Wednesday, 10 June 2015
|
||||
@brief Main program for tetris.
|
||||
@copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised
|
||||
BSD License. See LICENSE.txt for details.
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
Convert a tetromino type to its corresponding cell.
|
||||
*/
|
||||
#define TYPE_TO_CELL(x) ((x)+1)
|
||||
|
||||
/*
|
||||
Strings for how you would print a tetris board.
|
||||
*/
|
||||
#define TC_EMPTY_STR " "
|
||||
#define TC_BLOCK_STR "\u2588"
|
||||
|
||||
/*
|
||||
Questions about a tetris cell.
|
||||
*/
|
||||
#define TC_IS_EMPTY(x) ((x) == TC_EMPTY)
|
||||
#define TC_IS_FILLED(x) (!TC_IS_EMPTY(x))
|
||||
|
||||
/*
|
||||
How many cells in a tetromino?
|
||||
*/
|
||||
#define TETRIS 4
|
||||
/*
|
||||
How many tetrominos?
|
||||
*/
|
||||
#define NUM_TETROMINOS 7
|
||||
/*
|
||||
How many orientations of a tetromino?
|
||||
*/
|
||||
#define NUM_ORIENTATIONS 4
|
||||
|
||||
/*
|
||||
Level constants.
|
||||
*/
|
||||
#define MAX_LEVEL 19
|
||||
#define LINES_PER_LEVEL 10
|
||||
|
||||
/*
|
||||
A "cell" is a 1x1 block within a tetris board.
|
||||
*/
|
||||
typedef enum {
|
||||
TC_EMPTY, TC_CELLI, TC_CELLJ, TC_CELLL, TC_CELLO, TC_CELLS, TC_CELLT, TC_CELLZ
|
||||
} tetris_cell;
|
||||
|
||||
/*
|
||||
A "type" is a type/shape of a tetromino. Not including orientation.
|
||||
*/
|
||||
typedef enum {
|
||||
TET_I, TET_J, TET_L, TET_O, TET_S, TET_T, TET_Z
|
||||
} tetris_type;
|
||||
|
||||
/*
|
||||
A row,column pair. Negative numbers allowed, because we need them for
|
||||
offsets.
|
||||
*/
|
||||
typedef struct {
|
||||
int row;
|
||||
int col;
|
||||
} tetris_location;
|
||||
|
||||
/*
|
||||
A "block" is a struct that contains information about a tetromino.
|
||||
Specifically, what type it is, what orientation it has, and where it is.
|
||||
*/
|
||||
typedef struct {
|
||||
int typ;
|
||||
int ori;
|
||||
tetris_location loc;
|
||||
} tetris_block;
|
||||
|
||||
/*
|
||||
All possible moves to give as input to the game.
|
||||
*/
|
||||
typedef enum {
|
||||
TM_LEFT, TM_RIGHT, TM_CLOCK, TM_COUNTER, TM_DROP, TM_HOLD, TM_NONE
|
||||
} tetris_move;
|
||||
|
||||
/*
|
||||
A game object!
|
||||
*/
|
||||
typedef struct {
|
||||
/*
|
||||
Game board stuff:
|
||||
*/
|
||||
int rows;
|
||||
int cols;
|
||||
/*
|
||||
Scoring information:
|
||||
*/
|
||||
int points;
|
||||
int level;
|
||||
/*
|
||||
Falling block is the one currently going down. Next block is the one that
|
||||
will be falling after this one. Stored is the block that you can swap out.
|
||||
*/
|
||||
tetris_block falling;
|
||||
tetris_block next;
|
||||
tetris_block stored;
|
||||
/*
|
||||
Number of game ticks until the block will move down.
|
||||
*/
|
||||
int ticks_till_gravity;
|
||||
/*
|
||||
Number of lines until you advance to the next level.
|
||||
*/
|
||||
int lines_remaining;
|
||||
char board[];
|
||||
} tetris_game;
|
||||
|
||||
/*
|
||||
This array stores all necessary information about the cells that are filled by
|
||||
each tetromino. The first index is the type of the tetromino (i.e. shape,
|
||||
e.g. I, J, Z, etc.). The next index is the orientation (0-3). The final
|
||||
array contains 4 tetris_location objects, each mapping to an offset from a
|
||||
point on the upper left that is the tetromino "origin".
|
||||
*/
|
||||
extern const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS];
|
||||
|
||||
/*
|
||||
This array tells you how many ticks per gravity by level. Decreases as level
|
||||
increases, to add difficulty.
|
||||
*/
|
||||
extern const int GRAVITY_LEVEL[MAX_LEVEL+1];
|
||||
|
||||
// Data structure manipulation.
|
||||
void tg_init(tetris_game *obj, int rows, int cols);
|
||||
tetris_game *tg_create(struct games_state *rs,int rows, int cols);
|
||||
void tg_destroy(tetris_game *obj);
|
||||
void tg_delete(tetris_game *obj);
|
||||
tetris_game *tg_load(FILE *f);
|
||||
void tg_save(tetris_game *obj, FILE *f);
|
||||
|
||||
// Public methods not related to memory:
|
||||
char tg_get(tetris_game *obj, int row, int col);
|
||||
bool tg_check(tetris_game *obj, int row, int col);
|
||||
bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move);
|
||||
void tg_print(tetris_game *obj, FILE *f);
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
#define GAMENAME "prices" // name of executable
|
||||
#define GAMEMAIN prices // main program of game
|
||||
#define GAMEPLAYERJSON pricesplayerjson // displays game specific json
|
||||
#define GAMEDATA pricesdata // extracts data from game specific variables into games_state
|
||||
#define CHAINNAME "PRICES" // -ac_name=
|
||||
typedef uint64_t gamesevent; // can be 8, 16, 32, or 64 bits
|
||||
|
||||
#define MAXPACK 23
|
||||
struct games_packitem
|
||||
{
|
||||
int32_t type,launch,count,which,hplus,dplus,arm,flags,group;
|
||||
char damage[8],hurldmg[8];
|
||||
};
|
||||
|
||||
struct games_player
|
||||
{
|
||||
int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet;
|
||||
struct games_packitem gamespack[MAXPACK];
|
||||
};
|
||||
|
||||
struct games_state
|
||||
{
|
||||
uint64_t seed,origseed;
|
||||
char *keystrokeshex;
|
||||
uint32_t needflush,replaydone;
|
||||
int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum;
|
||||
FILE *logfp;
|
||||
struct games_player P;
|
||||
gamesevent buffered[5000],*keystrokes;
|
||||
uint8_t playerdata[8192];
|
||||
};
|
||||
extern struct games_state globalR;
|
||||
void *gamesiterate(struct games_state *rs);
|
||||
int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag);
|
||||
|
||||
void games_packitemstr(char *packitemstr,struct games_packitem *item);
|
||||
uint64_t _games_rngnext(uint64_t initseed);
|
||||
int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis);
|
||||
gamesevent games_revendian(gamesevent revx);
|
||||
int32_t disp_gamesplayer(char *str,struct games_player *P);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,907 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#include "tetris.h"
|
||||
|
||||
/*
|
||||
In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure.
|
||||
|
||||
also, the standalone game needs to support argv of seed gametxid, along with replay args
|
||||
*/
|
||||
|
||||
int random_tetromino(struct games_state *rs)
|
||||
{
|
||||
rs->seed = _games_rngnext(rs->seed);
|
||||
return(rs->seed % NUM_TETROMINOS);
|
||||
}
|
||||
|
||||
int32_t tetrisdata(struct games_player *P,void *ptr)
|
||||
{
|
||||
tetris_game *tg = (tetris_game *)ptr;
|
||||
P->gold = tg->points;
|
||||
P->dungeonlevel = tg->level;
|
||||
//fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/** https://github.com/brenns10/tetris
|
||||
@file main.c
|
||||
@author Stephen Brennan
|
||||
@date Created Wednesday, 10 June 2015
|
||||
@brief Main program for tetris.
|
||||
@copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised
|
||||
BSD License. See LICENSE.txt for details.
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#include <stdio.h> // for FILE
|
||||
#include <stdbool.h> // for bool
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef BUILD_GAMESCC
|
||||
#include "../rogue/cursesd.h"
|
||||
#else
|
||||
#include <curses.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
|
||||
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
|
||||
|
||||
/*******************************************************************************
|
||||
Array Definitions
|
||||
*******************************************************************************/
|
||||
|
||||
const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS] =
|
||||
{
|
||||
// I
|
||||
{{{1, 0}, {1, 1}, {1, 2}, {1, 3}},
|
||||
{{0, 2}, {1, 2}, {2, 2}, {3, 2}},
|
||||
{{3, 0}, {3, 1}, {3, 2}, {3, 3}},
|
||||
{{0, 1}, {1, 1}, {2, 1}, {3, 1}}},
|
||||
// J
|
||||
{{{0, 0}, {1, 0}, {1, 1}, {1, 2}},
|
||||
{{0, 1}, {0, 2}, {1, 1}, {2, 1}},
|
||||
{{1, 0}, {1, 1}, {1, 2}, {2, 2}},
|
||||
{{0, 1}, {1, 1}, {2, 0}, {2, 1}}},
|
||||
// L
|
||||
{{{0, 2}, {1, 0}, {1, 1}, {1, 2}},
|
||||
{{0, 1}, {1, 1}, {2, 1}, {2, 2}},
|
||||
{{1, 0}, {1, 1}, {1, 2}, {2, 0}},
|
||||
{{0, 0}, {0, 1}, {1, 1}, {2, 1}}},
|
||||
// O
|
||||
{{{0, 1}, {0, 2}, {1, 1}, {1, 2}},
|
||||
{{0, 1}, {0, 2}, {1, 1}, {1, 2}},
|
||||
{{0, 1}, {0, 2}, {1, 1}, {1, 2}},
|
||||
{{0, 1}, {0, 2}, {1, 1}, {1, 2}}},
|
||||
// S
|
||||
{{{0, 1}, {0, 2}, {1, 0}, {1, 1}},
|
||||
{{0, 1}, {1, 1}, {1, 2}, {2, 2}},
|
||||
{{1, 1}, {1, 2}, {2, 0}, {2, 1}},
|
||||
{{0, 0}, {1, 0}, {1, 1}, {2, 1}}},
|
||||
// T
|
||||
{{{0, 1}, {1, 0}, {1, 1}, {1, 2}},
|
||||
{{0, 1}, {1, 1}, {1, 2}, {2, 1}},
|
||||
{{1, 0}, {1, 1}, {1, 2}, {2, 1}},
|
||||
{{0, 1}, {1, 0}, {1, 1}, {2, 1}}},
|
||||
// Z
|
||||
{{{0, 0}, {0, 1}, {1, 1}, {1, 2}},
|
||||
{{0, 2}, {1, 1}, {1, 2}, {2, 1}},
|
||||
{{1, 0}, {1, 1}, {2, 1}, {2, 2}},
|
||||
{{0, 1}, {1, 0}, {1, 1}, {2, 0}}},
|
||||
};
|
||||
|
||||
const int GRAVITY_LEVEL[MAX_LEVEL+1] = {
|
||||
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||
50, 48, 46, 44, 42, 40, 38, 36, 34, 32,
|
||||
//10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
30, 28, 26, 24, 22, 20, 16, 12, 8, 4
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
Helper Functions for Blocks
|
||||
*******************************************************************************/
|
||||
|
||||
void sleep_milli(int milliseconds)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = milliseconds * 1000 * 1000;
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
Return the block at the given row and column.
|
||||
*/
|
||||
char tg_get(tetris_game *obj, int row, int column)
|
||||
{
|
||||
return obj->board[obj->cols * row + column];
|
||||
}
|
||||
|
||||
/*
|
||||
Set the block at the given row and column.
|
||||
*/
|
||||
static void tg_set(tetris_game *obj, int row, int column, char value)
|
||||
{
|
||||
obj->board[obj->cols * row + column] = value;
|
||||
}
|
||||
|
||||
/*
|
||||
Check whether a row and column are in bounds.
|
||||
*/
|
||||
bool tg_check(tetris_game *obj, int row, int col)
|
||||
{
|
||||
return 0 <= row && row < obj->rows && 0 <= col && col < obj->cols;
|
||||
}
|
||||
|
||||
/*
|
||||
Place a block onto the board.
|
||||
*/
|
||||
static void tg_put(tetris_game *obj, tetris_block block)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < TETRIS; i++) {
|
||||
tetris_location cell = TETROMINOS[block.typ][block.ori][i];
|
||||
tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col,
|
||||
TYPE_TO_CELL(block.typ));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Clear a block out of the board.
|
||||
*/
|
||||
static void tg_remove(tetris_game *obj, tetris_block block)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < TETRIS; i++) {
|
||||
tetris_location cell = TETROMINOS[block.typ][block.ori][i];
|
||||
tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, TC_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Check if a block can be placed on the board.
|
||||
*/
|
||||
static bool tg_fits(tetris_game *obj, tetris_block block)
|
||||
{
|
||||
int i, r, c;
|
||||
for (i = 0; i < TETRIS; i++) {
|
||||
tetris_location cell = TETROMINOS[block.typ][block.ori][i];
|
||||
r = block.loc.row + cell.row;
|
||||
c = block.loc.col + cell.col;
|
||||
if (!tg_check(obj, r, c) || TC_IS_FILLED(tg_get(obj, r, c))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Create a new falling block and populate the next falling block with a random
|
||||
one.
|
||||
*/
|
||||
static void tg_new_falling(struct games_state *rs,tetris_game *obj)
|
||||
{
|
||||
// Put in a new falling tetromino.
|
||||
obj->falling = obj->next;
|
||||
obj->next.typ = random_tetromino(rs);
|
||||
obj->next.ori = 0;
|
||||
obj->next.loc.row = 0;
|
||||
obj->next.loc.col = obj->cols/2 - 2;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Game Turn Helpers
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
Tick gravity, and move the block down if gravity should act.
|
||||
*/
|
||||
static void tg_do_gravity_tick(struct games_state *rs,tetris_game *obj)
|
||||
{
|
||||
obj->ticks_till_gravity--;
|
||||
if (obj->ticks_till_gravity <= 0) {
|
||||
tg_remove(obj, obj->falling);
|
||||
obj->falling.loc.row++;
|
||||
if (tg_fits(obj, obj->falling)) {
|
||||
obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level];
|
||||
} else {
|
||||
obj->falling.loc.row--;
|
||||
tg_put(obj, obj->falling);
|
||||
|
||||
tg_new_falling(rs,obj);
|
||||
}
|
||||
tg_put(obj, obj->falling);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Move the falling tetris block left (-1) or right (+1).
|
||||
*/
|
||||
static void tg_move(tetris_game *obj, int direction)
|
||||
{
|
||||
tg_remove(obj, obj->falling);
|
||||
obj->falling.loc.col += direction;
|
||||
if (!tg_fits(obj, obj->falling)) {
|
||||
obj->falling.loc.col -= direction;
|
||||
}
|
||||
tg_put(obj, obj->falling);
|
||||
}
|
||||
|
||||
/*
|
||||
Send the falling tetris block to the bottom.
|
||||
*/
|
||||
static void tg_down(struct games_state *rs,tetris_game *obj)
|
||||
{
|
||||
tg_remove(obj, obj->falling);
|
||||
while (tg_fits(obj, obj->falling)) {
|
||||
obj->falling.loc.row++;
|
||||
}
|
||||
obj->falling.loc.row--;
|
||||
tg_put(obj, obj->falling);
|
||||
tg_new_falling(rs,obj);
|
||||
}
|
||||
|
||||
/*
|
||||
Rotate the falling block in either direction (+/-1).
|
||||
*/
|
||||
static void tg_rotate(tetris_game *obj, int direction)
|
||||
{
|
||||
tg_remove(obj, obj->falling);
|
||||
|
||||
while (true) {
|
||||
obj->falling.ori = (obj->falling.ori + direction) % NUM_ORIENTATIONS;
|
||||
|
||||
// If the new orientation fits, we're done.
|
||||
if (tg_fits(obj, obj->falling))
|
||||
break;
|
||||
|
||||
// Otherwise, try moving left to make it fit.
|
||||
obj->falling.loc.col--;
|
||||
if (tg_fits(obj, obj->falling))
|
||||
break;
|
||||
|
||||
// Finally, try moving right to make it fit.
|
||||
obj->falling.loc.col += 2;
|
||||
if (tg_fits(obj, obj->falling))
|
||||
break;
|
||||
|
||||
// Put it back in its original location and try the next orientation.
|
||||
obj->falling.loc.col--;
|
||||
// Worst case, we come back to the original orientation and it fits, so this
|
||||
// loop will terminate.
|
||||
}
|
||||
|
||||
tg_put(obj, obj->falling);
|
||||
}
|
||||
|
||||
/*
|
||||
Swap the falling block with the block in the hold buffer.
|
||||
*/
|
||||
static void tg_hold(struct games_state *rs,tetris_game *obj)
|
||||
{
|
||||
tg_remove(obj, obj->falling);
|
||||
if (obj->stored.typ == -1) {
|
||||
obj->stored = obj->falling;
|
||||
tg_new_falling(rs,obj);
|
||||
} else {
|
||||
int typ = obj->falling.typ, ori = obj->falling.ori;
|
||||
obj->falling.typ = obj->stored.typ;
|
||||
obj->falling.ori = obj->stored.ori;
|
||||
obj->stored.typ = typ;
|
||||
obj->stored.ori = ori;
|
||||
while (!tg_fits(obj, obj->falling)) {
|
||||
obj->falling.loc.row--;
|
||||
if (tg_fits(obj, obj->falling)) {
|
||||
break;
|
||||
}
|
||||
obj->falling.loc.col--;
|
||||
if (tg_fits(obj, obj->falling)) {
|
||||
break;
|
||||
}
|
||||
obj->falling.loc.col += 2;
|
||||
}
|
||||
}
|
||||
tg_put(obj, obj->falling);
|
||||
}
|
||||
|
||||
/*
|
||||
Perform the action specified by the move.
|
||||
*/
|
||||
static void tg_handle_move(struct games_state *rs,tetris_game *obj, tetris_move move)
|
||||
{
|
||||
switch (move) {
|
||||
case TM_LEFT:
|
||||
//fprintf(stderr,"LEFT ");
|
||||
tg_move(obj, -1);
|
||||
break;
|
||||
case TM_RIGHT:
|
||||
//fprintf(stderr,"RIGHT ");
|
||||
tg_move(obj, 1);
|
||||
break;
|
||||
case TM_DROP:
|
||||
tg_down(rs,obj);
|
||||
break;
|
||||
case TM_CLOCK:
|
||||
tg_rotate(obj, 1);
|
||||
break;
|
||||
case TM_COUNTER:
|
||||
tg_rotate(obj, -1);
|
||||
break;
|
||||
case TM_HOLD:
|
||||
tg_hold(rs,obj);
|
||||
break;
|
||||
default:
|
||||
// pass
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Return true if line i is full.
|
||||
*/
|
||||
static bool tg_line_full(tetris_game *obj, int i)
|
||||
{
|
||||
int j;
|
||||
for (j = 0; j < obj->cols; j++) {
|
||||
if (TC_IS_EMPTY(tg_get(obj, i, j)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Shift every row above r down one.
|
||||
*/
|
||||
static void tg_shift_lines(tetris_game *obj, int r)
|
||||
{
|
||||
int i, j;
|
||||
for (i = r-1; i >= 0; i--) {
|
||||
for (j = 0; j < obj->cols; j++) {
|
||||
tg_set(obj, i+1, j, tg_get(obj, i, j));
|
||||
tg_set(obj, i, j, TC_EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Find rows that are filled, remove them, shift, and return the number of
|
||||
cleared rows.
|
||||
*/
|
||||
static int tg_check_lines(tetris_game *obj)
|
||||
{
|
||||
int i, nlines = 0;
|
||||
tg_remove(obj, obj->falling); // don't want to mess up falling block
|
||||
|
||||
for (i = obj->rows-1; i >= 0; i--) {
|
||||
if (tg_line_full(obj, i)) {
|
||||
tg_shift_lines(obj, i);
|
||||
i++; // do this line over again since they're shifted
|
||||
nlines++;
|
||||
}
|
||||
}
|
||||
|
||||
tg_put(obj, obj->falling); // replace
|
||||
return nlines;
|
||||
}
|
||||
|
||||
/*
|
||||
Adjust the score for the game, given how many lines were just cleared.
|
||||
*/
|
||||
static void tg_adjust_score(tetris_game *obj, int lines_cleared)
|
||||
{
|
||||
static int line_multiplier[] = {0, 40, 100, 300, 1200};
|
||||
obj->points += line_multiplier[lines_cleared] * (obj->level + 1);
|
||||
if (lines_cleared >= obj->lines_remaining) {
|
||||
obj->level = MIN(MAX_LEVEL, obj->level + 1);
|
||||
lines_cleared -= obj->lines_remaining;
|
||||
obj->lines_remaining = LINES_PER_LEVEL - lines_cleared;
|
||||
} else {
|
||||
obj->lines_remaining -= lines_cleared;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Return true if the game is over.
|
||||
*/
|
||||
static bool tg_game_over(tetris_game *obj)
|
||||
{
|
||||
int i, j;
|
||||
bool over = false;
|
||||
tg_remove(obj, obj->falling);
|
||||
for (i = 0; i < 2; i++) {
|
||||
for (j = 0; j < obj->cols; j++) {
|
||||
if (TC_IS_FILLED(tg_get(obj, i, j))) {
|
||||
over = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
tg_put(obj, obj->falling);
|
||||
return over;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
Main Public Functions
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
Do a single game tick: process gravity, user input, and score. Return true if
|
||||
the game is still running, false if it is over.
|
||||
*/
|
||||
bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move)
|
||||
{
|
||||
int lines_cleared;
|
||||
// Handle gravity.
|
||||
tg_do_gravity_tick(rs,obj);
|
||||
|
||||
// Handle input.
|
||||
tg_handle_move(rs,obj, move);
|
||||
|
||||
// Check for cleared lines
|
||||
lines_cleared = tg_check_lines(obj);
|
||||
|
||||
tg_adjust_score(obj, lines_cleared);
|
||||
|
||||
// Return whether the game will continue (NOT whether it's over)
|
||||
return !tg_game_over(obj);
|
||||
}
|
||||
|
||||
void tg_init(struct games_state *rs,tetris_game *obj, int rows, int cols)
|
||||
{
|
||||
// Initialization logic
|
||||
obj->rows = rows;
|
||||
obj->cols = cols;
|
||||
//obj->board = (char *)malloc(rows * cols);
|
||||
memset(obj->board, TC_EMPTY, rows * cols);
|
||||
obj->points = 0;
|
||||
obj->level = 0;
|
||||
obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level];
|
||||
obj->lines_remaining = LINES_PER_LEVEL;
|
||||
//srand(time(NULL));
|
||||
tg_new_falling(rs,obj);
|
||||
tg_new_falling(rs,obj);
|
||||
obj->stored.typ = -1;
|
||||
obj->stored.ori = 0;
|
||||
obj->stored.loc.row = 0;
|
||||
obj->next.loc.col = obj->cols/2 - 2;
|
||||
//printf("%d", obj->falling.loc.col);
|
||||
}
|
||||
|
||||
tetris_game *tg_create(struct games_state *rs,int rows, int cols)
|
||||
{
|
||||
tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game) + rows*cols);
|
||||
tg_init(rs,obj, rows, cols);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*void tg_destroy(tetris_game *obj)
|
||||
{
|
||||
// Cleanup logic
|
||||
free(obj->board);
|
||||
}*/
|
||||
|
||||
void tg_delete(tetris_game *obj) {
|
||||
//tg_destroy(obj);
|
||||
free(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
Load a game from a file.
|
||||
|
||||
tetris_game *tg_load(FILE *f)
|
||||
{
|
||||
tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game));
|
||||
if (fread(obj, sizeof(tetris_game), 1, f) != 1 )
|
||||
{
|
||||
fprintf(stderr,"read game error\n");
|
||||
free(obj);
|
||||
obj = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
obj->board = (char *)malloc(obj->rows * obj->cols);
|
||||
if (fread(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols )
|
||||
{
|
||||
fprintf(stderr,"fread error\n");
|
||||
free(obj->board);
|
||||
free(obj);
|
||||
obj = 0;
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}*/
|
||||
|
||||
/*
|
||||
Save a game to a file.
|
||||
|
||||
void tg_save(tetris_game *obj, FILE *f)
|
||||
{
|
||||
if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 )
|
||||
fprintf(stderr,"error writing tetrisgame\n");
|
||||
else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols )
|
||||
fprintf(stderr,"error writing board\n");
|
||||
}*/
|
||||
|
||||
/*
|
||||
Print a game board to a file. Really just for early debugging.
|
||||
*/
|
||||
void tg_print(tetris_game *obj, FILE *f) {
|
||||
int i, j;
|
||||
for (i = 0; i < obj->rows; i++) {
|
||||
for (j = 0; j < obj->cols; j++) {
|
||||
if (TC_IS_EMPTY(tg_get(obj, i, j))) {
|
||||
fputs(TC_EMPTY_STR, f);
|
||||
} else {
|
||||
fputs(TC_BLOCK_STR, f);
|
||||
}
|
||||
}
|
||||
fputc('\n', f);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
2 columns per cell makes the game much nicer.
|
||||
*/
|
||||
#define COLS_PER_CELL 2
|
||||
/*
|
||||
Macro to print a cell of a specific type to a window.
|
||||
*/
|
||||
#define ADD_BLOCK(w,x) waddch((w),' '|A_REVERSE|COLOR_PAIR(x)); \
|
||||
waddch((w),' '|A_REVERSE|COLOR_PAIR(x))
|
||||
#define ADD_EMPTY(w) waddch((w), ' '); waddch((w), ' ')
|
||||
|
||||
/*
|
||||
Print the tetris board onto the ncurses window.
|
||||
*/
|
||||
void display_board(WINDOW *w, tetris_game *obj)
|
||||
{
|
||||
int i, j;
|
||||
box(w, 0, 0);
|
||||
for (i = 0; i < obj->rows; i++) {
|
||||
wmove(w, 1 + i, 1);
|
||||
for (j = 0; j < obj->cols; j++) {
|
||||
if (TC_IS_FILLED(tg_get(obj, i, j))) {
|
||||
ADD_BLOCK(w,tg_get(obj, i, j));
|
||||
} else {
|
||||
ADD_EMPTY(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
wnoutrefresh(w);
|
||||
}
|
||||
|
||||
/*
|
||||
Display a tetris piece in a dedicated window.
|
||||
*/
|
||||
void display_piece(WINDOW *w, tetris_block block)
|
||||
{
|
||||
int b;
|
||||
tetris_location c;
|
||||
wclear(w);
|
||||
box(w, 0, 0);
|
||||
if (block.typ == -1) {
|
||||
wnoutrefresh(w);
|
||||
return;
|
||||
}
|
||||
for (b = 0; b < TETRIS; b++) {
|
||||
c = TETROMINOS[block.typ][block.ori][b];
|
||||
wmove(w, c.row + 1, c.col * COLS_PER_CELL + 1);
|
||||
ADD_BLOCK(w, TYPE_TO_CELL(block.typ));
|
||||
}
|
||||
wnoutrefresh(w);
|
||||
}
|
||||
|
||||
/*
|
||||
Display score information in a dedicated window.
|
||||
*/
|
||||
void display_score(WINDOW *w, tetris_game *tg)
|
||||
{
|
||||
wclear(w);
|
||||
box(w, 0, 0);
|
||||
wprintw(w, (char *)"Score\n%d\n", tg->points);
|
||||
wprintw(w, (char *)"Level\n%d\n", tg->level);
|
||||
wprintw(w, (char *)"Lines\n%d\n", tg->lines_remaining);
|
||||
wnoutrefresh(w);
|
||||
}
|
||||
|
||||
/*
|
||||
Save and exit the game.
|
||||
|
||||
void save(tetris_game *game, WINDOW *w)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
wclear(w);
|
||||
box(w, 0, 0); // return the border
|
||||
wmove(w, 1, 1);
|
||||
wprintw(w, (char *)"Save and exit? [Y/n] ");
|
||||
wrefresh(w);
|
||||
timeout(-1);
|
||||
if (getch() == 'n') {
|
||||
timeout(0);
|
||||
return;
|
||||
}
|
||||
f = fopen("tetris.save", "w");
|
||||
tg_save(game, f);
|
||||
fclose(f);
|
||||
tg_delete(game);
|
||||
endwin();
|
||||
fprintf(stderr,"Game saved to \"tetris.save\".\n");
|
||||
fprintf(stderr,"Resume by passing the filename as an argument to this program.\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}*/
|
||||
|
||||
/*
|
||||
Do the NCURSES initialization steps for color blocks.
|
||||
*/
|
||||
void init_colors(void)
|
||||
{
|
||||
start_color();
|
||||
//init_color(COLOR_ORANGE, 1000, 647, 0);
|
||||
init_pair(TC_CELLI, COLOR_CYAN, COLOR_BLACK);
|
||||
init_pair(TC_CELLJ, COLOR_BLUE, COLOR_BLACK);
|
||||
init_pair(TC_CELLL, COLOR_WHITE, COLOR_BLACK);
|
||||
init_pair(TC_CELLO, COLOR_YELLOW, COLOR_BLACK);
|
||||
init_pair(TC_CELLS, COLOR_GREEN, COLOR_BLACK);
|
||||
init_pair(TC_CELLT, COLOR_MAGENTA, COLOR_BLACK);
|
||||
init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK);
|
||||
}
|
||||
|
||||
struct games_state globalR;
|
||||
extern char Gametxidstr[];
|
||||
int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c);
|
||||
gamesevent games_readevent(struct games_state *rs);
|
||||
|
||||
void *gamesiterate(struct games_state *rs)
|
||||
{
|
||||
uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE;
|
||||
gamesevent c; uint16_t skipcount=0; int32_t prevlevel; uint32_t eventid = 0; tetris_game *tg;
|
||||
WINDOW *board, *next, *hold, *score;
|
||||
if ( rs->guiflag != 0 || rs->sleeptime != 0 )
|
||||
{
|
||||
// NCURSES initialization:
|
||||
initscr(); // initialize curses
|
||||
cbreak(); // pass key presses to program, but not signals
|
||||
noecho(); // don't echo key presses to screen
|
||||
keypad(stdscr, TRUE); // allow arrow keys
|
||||
timeout(0); // no blocking on getch()
|
||||
curs_set(0); // set the cursor to invisible
|
||||
init_colors(); // setup tetris colors
|
||||
}
|
||||
tg = tg_create(rs,22, 10);
|
||||
prevlevel = tg->level;
|
||||
// Create windows for each section of the interface.
|
||||
board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0);
|
||||
next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1);
|
||||
hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1);
|
||||
score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1);
|
||||
while ( running != 0 )
|
||||
{
|
||||
running = tg_tick(rs,tg,move);
|
||||
if ( 1 && (rs->guiflag != 0 || rs->sleeptime != 0) )
|
||||
{
|
||||
display_board(board,tg);
|
||||
display_piece(next,tg->next);
|
||||
display_piece(hold,tg->stored);
|
||||
display_score(score,tg);
|
||||
}
|
||||
if ( rs->guiflag != 0 )
|
||||
{
|
||||
#ifdef STANDALONE
|
||||
sleep_milli(15);
|
||||
if ( (counter++ % 10) == 0 )
|
||||
doupdate();
|
||||
c = games_readevent(rs);
|
||||
if ( c <= 0x7f || skipcount == 0x3fff )
|
||||
{
|
||||
if ( skipcount > 0 )
|
||||
issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000);
|
||||
if ( c <= 0x7f )
|
||||
issue_games_events(rs,Gametxidstr,eventid,c);
|
||||
if ( tg->level != prevlevel )
|
||||
{
|
||||
flushkeystrokes(rs,0);
|
||||
prevlevel = tg->level;
|
||||
}
|
||||
skipcount = 0;
|
||||
} else skipcount++;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( rs->replaydone != 0 )
|
||||
break;
|
||||
if ( rs->sleeptime != 0 )
|
||||
{
|
||||
sleep_milli(1);
|
||||
if ( (counter++ % 20) == 0 )
|
||||
doupdate();
|
||||
}
|
||||
if ( skipcount == 0 )
|
||||
{
|
||||
c = games_readevent(rs);
|
||||
//fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level);
|
||||
if ( (c & 0x4000) == 0x4000 )
|
||||
{
|
||||
skipcount = (c & 0x3fff);
|
||||
c = 'S';
|
||||
}
|
||||
}
|
||||
if ( skipcount > 0 )
|
||||
skipcount--;
|
||||
}
|
||||
eventid++;
|
||||
switch ( c )
|
||||
{
|
||||
case 'h':
|
||||
move = TM_LEFT;
|
||||
break;
|
||||
case 'l':
|
||||
move = TM_RIGHT;
|
||||
break;
|
||||
case 'k':
|
||||
move = TM_CLOCK;
|
||||
break;
|
||||
case 'j':
|
||||
move = TM_DROP;
|
||||
break;
|
||||
case 'q':
|
||||
running = false;
|
||||
move = TM_NONE;
|
||||
break;
|
||||
/*case 'p':
|
||||
wclear(board);
|
||||
box(board, 0, 0);
|
||||
wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2);
|
||||
wprintw(board, "PAUSED");
|
||||
wrefresh(board);
|
||||
timeout(-1);
|
||||
getch();
|
||||
timeout(0);
|
||||
move = TM_NONE;
|
||||
break;
|
||||
case 's':
|
||||
save(tg, board);
|
||||
move = TM_NONE;
|
||||
break;*/
|
||||
case ' ':
|
||||
move = TM_HOLD;
|
||||
break;
|
||||
default:
|
||||
move = TM_NONE;
|
||||
}
|
||||
}
|
||||
return(tg);
|
||||
}
|
||||
|
||||
#ifdef STANDALONE
|
||||
/*
|
||||
Main tetris game!
|
||||
*/
|
||||
#include "dapps/dappstd.c"
|
||||
|
||||
|
||||
char *clonestr(char *str)
|
||||
{
|
||||
char *clone; int32_t len;
|
||||
if ( str == 0 || str[0] == 0 )
|
||||
{
|
||||
printf("warning cloning nullstr.%p\n",str);
|
||||
#ifdef __APPLE__
|
||||
while ( 1 ) sleep(1);
|
||||
#endif
|
||||
str = (char *)"<nullstr>";
|
||||
}
|
||||
len = strlen(str);
|
||||
clone = (char *)calloc(1,len+16);
|
||||
strcpy(clone,str);
|
||||
return(clone);
|
||||
}
|
||||
|
||||
int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c)
|
||||
{
|
||||
static FILE *fp;
|
||||
char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1;
|
||||
if ( fp == 0 )
|
||||
fp = fopen("events.log","wb");
|
||||
rs->buffered[rs->num++] = c;
|
||||
if ( 0 )
|
||||
{
|
||||
if ( sizeof(c) == 1 )
|
||||
sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",(uint8_t)c&0xff,gametxidstr,eventid);
|
||||
else if ( sizeof(c) == 2 )
|
||||
sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",(uint16_t)c&0xffff,gametxidstr,eventid);
|
||||
else if ( sizeof(c) == 4 )
|
||||
sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",(uint32_t)c&0xffffffff,gametxidstr,eventid);
|
||||
else if ( sizeof(c) == 8 )
|
||||
sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid);
|
||||
if ( (retstr= hush_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 )
|
||||
{
|
||||
if ( (retjson= cJSON_Parse(retstr)) != 0 )
|
||||
{
|
||||
if ( (resobj= jobj(retjson,(char *)"result")) != 0 )
|
||||
{
|
||||
retval = 0;
|
||||
if ( fp != 0 )
|
||||
{
|
||||
fprintf(fp,"%s\n",jprint(resobj,0));
|
||||
fflush(fp);
|
||||
}
|
||||
}
|
||||
free_json(retjson);
|
||||
} else fprintf(fp,"error parsing %s\n",retstr);
|
||||
free(retstr);
|
||||
} else fprintf(fp,"error issuing method %s\n",params);
|
||||
return(retval);
|
||||
} else return(0);
|
||||
}
|
||||
|
||||
int tetris(int argc, char **argv)
|
||||
{
|
||||
struct games_state *rs = &globalR;
|
||||
int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg = 0;
|
||||
memset(rs,0,sizeof(*rs));
|
||||
rs->guiflag = 1;
|
||||
rs->sleeptime = 1; // non-zero to allow refresh()
|
||||
if ( argc >= 2 && strlen(argv[2]) == 64 )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
rs->origseed = _strtoui64(argv[1], NULL, 10);
|
||||
#else
|
||||
rs->origseed = atol(argv[1]); // windows, but not MSVC
|
||||
#endif // _MSC_VER
|
||||
#else
|
||||
rs->origseed = atol(argv[1]); // non-windows
|
||||
#endif // _WIN32
|
||||
rs->seed = rs->origseed;
|
||||
if ( argc >= 3 )
|
||||
{
|
||||
strcpy(Gametxidstr,argv[2]);
|
||||
fprintf(stderr,"setplayerdata %s\n",Gametxidstr);
|
||||
if ( games_setplayerdata(rs,Gametxidstr) < 0 )
|
||||
{
|
||||
fprintf(stderr,"invalid gametxid, or already started\n");
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
} else rs->seed = 777;
|
||||
|
||||
/* Load file if given a filename.
|
||||
if (argc >= 2) {
|
||||
FILE *f = fopen(argv[1], "r");
|
||||
if (f == NULL) {
|
||||
perror("tetris");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
tg = tg_load(f);
|
||||
fclose(f);
|
||||
} else {
|
||||
// Otherwise create new game.
|
||||
tg = tg_create(rs,22, 10);
|
||||
}*/
|
||||
|
||||
// Game loop
|
||||
tg = (tetris_game *)gamesiterate(rs);
|
||||
gamesbailout(rs);
|
||||
// Deinitialize NCurses
|
||||
wclear(stdscr);
|
||||
endwin();
|
||||
// Output ending message.
|
||||
printf("Game over!\n");
|
||||
printf("You finished with %d points on level %d.\n", tg->points, tg->level);
|
||||
|
||||
// Deinitialize Tetris
|
||||
tg_delete(tg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
std::string MYCCLIBNAME = (char *)"gamescc";
|
||||
|
||||
// game specific code for daemon
|
||||
void games_packitemstr(char *packitemstr,struct games_packitem *item)
|
||||
{
|
||||
strcpy(packitemstr,"");
|
||||
}
|
||||
|
||||
int64_t games_cashout(struct games_player *P)
|
||||
{
|
||||
int32_t dungeonlevel = P->dungeonlevel; int64_t mult=10000,cashout = 0;
|
||||
cashout = (uint64_t)P->gold * mult;
|
||||
return(cashout);
|
||||
}
|
||||
|
||||
void tetrisplayerjson(UniValue &obj,struct games_player *P)
|
||||
{
|
||||
obj.push_back(Pair("packsize",(int64_t)P->packsize));
|
||||
obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints));
|
||||
obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff)));
|
||||
obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16)));
|
||||
obj.push_back(Pair("level",(int64_t)P->level));
|
||||
obj.push_back(Pair("experience",(int64_t)P->experience));
|
||||
obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel));
|
||||
}
|
||||
|
||||
int32_t disp_gamesplayer(char *str,struct games_player *P)
|
||||
{
|
||||
str[0] = 0;
|
||||
//if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 )
|
||||
// return(-1);
|
||||
sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector<uint8_t> payload)
|
||||
{
|
||||
uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0;
|
||||
if ( (len= payload.size()) > 36 )
|
||||
{
|
||||
len -= 36;
|
||||
for (i=0; i<32; i++)
|
||||
((uint8_t *)&gametxid)[i] = payload[len+i];
|
||||
eventid = (uint32_t)payload[len+32];
|
||||
eventid |= (uint32_t)payload[len+33] << 8;
|
||||
eventid |= (uint32_t)payload[len+34] << 16;
|
||||
eventid |= (uint32_t)payload[len+35] << 24;
|
||||
//for (i=0; i<len; i++)
|
||||
// fprintf(stderr,"%02x",payload[i]);
|
||||
//fprintf(stderr," got payload, from %s %s/e%d\n",pubkey33_str(str,(uint8_t *)&pk),gametxid.GetHex().c_str(),eventid);
|
||||
return(0);
|
||||
} else return(-1);
|
||||
}
|
||||
|
||||
UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
UniValue result;
|
||||
return(result);
|
||||
}
|
||||
|
||||
UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
UniValue result;
|
||||
return(result);
|
||||
}
|
||||
|
||||
bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx)
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
|
||||
#ifndef H_TETRIS_H
|
||||
#define H_TETRIS_H
|
||||
|
||||
/***************************************************************************/
|
||||
/** https://github.com/brenns10/tetris
|
||||
@file main.c
|
||||
@author Stephen Brennan
|
||||
@date Created Wednesday, 10 June 2015
|
||||
@brief Main program for tetris.
|
||||
@copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised
|
||||
BSD License. See LICENSE.txt for details.
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
Convert a tetromino type to its corresponding cell.
|
||||
*/
|
||||
#define TYPE_TO_CELL(x) ((x)+1)
|
||||
|
||||
/*
|
||||
Strings for how you would print a tetris board.
|
||||
*/
|
||||
#define TC_EMPTY_STR " "
|
||||
#define TC_BLOCK_STR "\u2588"
|
||||
|
||||
/*
|
||||
Questions about a tetris cell.
|
||||
*/
|
||||
#define TC_IS_EMPTY(x) ((x) == TC_EMPTY)
|
||||
#define TC_IS_FILLED(x) (!TC_IS_EMPTY(x))
|
||||
|
||||
/*
|
||||
How many cells in a tetromino?
|
||||
*/
|
||||
#define TETRIS 4
|
||||
/*
|
||||
How many tetrominos?
|
||||
*/
|
||||
#define NUM_TETROMINOS 7
|
||||
/*
|
||||
How many orientations of a tetromino?
|
||||
*/
|
||||
#define NUM_ORIENTATIONS 4
|
||||
|
||||
/*
|
||||
Level constants.
|
||||
*/
|
||||
#define MAX_LEVEL 19
|
||||
#define LINES_PER_LEVEL 10
|
||||
|
||||
/*
|
||||
A "cell" is a 1x1 block within a tetris board.
|
||||
*/
|
||||
typedef enum {
|
||||
TC_EMPTY, TC_CELLI, TC_CELLJ, TC_CELLL, TC_CELLO, TC_CELLS, TC_CELLT, TC_CELLZ
|
||||
} tetris_cell;
|
||||
|
||||
/*
|
||||
A "type" is a type/shape of a tetromino. Not including orientation.
|
||||
*/
|
||||
typedef enum {
|
||||
TET_I, TET_J, TET_L, TET_O, TET_S, TET_T, TET_Z
|
||||
} tetris_type;
|
||||
|
||||
/*
|
||||
A row,column pair. Negative numbers allowed, because we need them for
|
||||
offsets.
|
||||
*/
|
||||
typedef struct {
|
||||
int row;
|
||||
int col;
|
||||
} tetris_location;
|
||||
|
||||
/*
|
||||
A "block" is a struct that contains information about a tetromino.
|
||||
Specifically, what type it is, what orientation it has, and where it is.
|
||||
*/
|
||||
typedef struct {
|
||||
int typ;
|
||||
int ori;
|
||||
tetris_location loc;
|
||||
} tetris_block;
|
||||
|
||||
/*
|
||||
All possible moves to give as input to the game.
|
||||
*/
|
||||
typedef enum {
|
||||
TM_LEFT, TM_RIGHT, TM_CLOCK, TM_COUNTER, TM_DROP, TM_HOLD, TM_NONE
|
||||
} tetris_move;
|
||||
|
||||
/*
|
||||
A game object!
|
||||
*/
|
||||
typedef struct {
|
||||
/*
|
||||
Game board stuff:
|
||||
*/
|
||||
int rows;
|
||||
int cols;
|
||||
/*
|
||||
Scoring information:
|
||||
*/
|
||||
int points;
|
||||
int level;
|
||||
/*
|
||||
Falling block is the one currently going down. Next block is the one that
|
||||
will be falling after this one. Stored is the block that you can swap out.
|
||||
*/
|
||||
tetris_block falling;
|
||||
tetris_block next;
|
||||
tetris_block stored;
|
||||
/*
|
||||
Number of game ticks until the block will move down.
|
||||
*/
|
||||
int ticks_till_gravity;
|
||||
/*
|
||||
Number of lines until you advance to the next level.
|
||||
*/
|
||||
int lines_remaining;
|
||||
char board[];
|
||||
} tetris_game;
|
||||
|
||||
/*
|
||||
This array stores all necessary information about the cells that are filled by
|
||||
each tetromino. The first index is the type of the tetromino (i.e. shape,
|
||||
e.g. I, J, Z, etc.). The next index is the orientation (0-3). The final
|
||||
array contains 4 tetris_location objects, each mapping to an offset from a
|
||||
point on the upper left that is the tetromino "origin".
|
||||
*/
|
||||
extern const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS];
|
||||
|
||||
/*
|
||||
This array tells you how many ticks per gravity by level. Decreases as level
|
||||
increases, to add difficulty.
|
||||
*/
|
||||
extern const int GRAVITY_LEVEL[MAX_LEVEL+1];
|
||||
|
||||
// Data structure manipulation.
|
||||
void tg_init(tetris_game *obj, int rows, int cols);
|
||||
tetris_game *tg_create(struct games_state *rs,int rows, int cols);
|
||||
void tg_destroy(tetris_game *obj);
|
||||
void tg_delete(tetris_game *obj);
|
||||
tetris_game *tg_load(FILE *f);
|
||||
void tg_save(tetris_game *obj, FILE *f);
|
||||
|
||||
// Public methods not related to memory:
|
||||
char tg_get(tetris_game *obj, int row, int col);
|
||||
bool tg_check(tetris_game *obj, int row, int col);
|
||||
bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move);
|
||||
void tg_print(tetris_game *obj, FILE *f);
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
#define GAMENAME "tetris" // name of executable
|
||||
#define GAMEMAIN tetris // main program of game
|
||||
#define GAMEPLAYERJSON tetrisplayerjson // displays game specific json
|
||||
#define GAMEDATA tetrisdata // extracts data from game specific variables into games_state
|
||||
#define CHAINNAME "GTEST" // -ac_name=
|
||||
typedef uint16_t gamesevent; // can be 8, 16, 32, or 64 bits
|
||||
|
||||
#define MAXPACK 23
|
||||
struct games_packitem
|
||||
{
|
||||
int32_t type,launch,count,which,hplus,dplus,arm,flags,group;
|
||||
char damage[8],hurldmg[8];
|
||||
};
|
||||
|
||||
struct games_player
|
||||
{
|
||||
int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet;
|
||||
struct games_packitem gamespack[MAXPACK];
|
||||
};
|
||||
|
||||
struct games_state
|
||||
{
|
||||
uint64_t seed,origseed;
|
||||
char *keystrokeshex;
|
||||
uint32_t needflush,replaydone;
|
||||
int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum;
|
||||
FILE *logfp;
|
||||
struct games_player P;
|
||||
gamesevent buffered[5000],*keystrokes;
|
||||
uint8_t playerdata[8192];
|
||||
};
|
||||
extern struct games_state globalR;
|
||||
void *gamesiterate(struct games_state *rs);
|
||||
int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag);
|
||||
|
||||
void games_packitemstr(char *packitemstr,struct games_packitem *item);
|
||||
uint64_t _games_rngnext(uint64_t initseed);
|
||||
int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis);
|
||||
gamesevent games_revendian(gamesevent revx);
|
||||
int32_t disp_gamesplayer(char *str,struct games_player *P);
|
||||
|
||||
#endif
|
||||
|
||||
1813
src/cc/gamescc.cpp
1813
src/cc/gamescc.cpp
File diff suppressed because it is too large
Load Diff
123
src/cc/gamescc.h
123
src/cc/gamescc.h
@@ -1,123 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
#ifndef H_GAMESCC_H
|
||||
#define H_GAMESCC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#define GAMES_RNGMULT 11109
|
||||
#define GAMES_RNGOFFSET 13849
|
||||
#define GAMES_MAXRNGS 10000
|
||||
|
||||
#ifndef STANDALONE
|
||||
|
||||
#define ENABLE_WALLET
|
||||
extern CWallet* pwalletMain;
|
||||
|
||||
#include "CCinclude.h"
|
||||
#include "secp256k1.h"
|
||||
|
||||
|
||||
#define EVAL_GAMES (EVAL_FAUCET2+1)
|
||||
#define GAMES_TXFEE 10000
|
||||
#define GAMES_MAXITERATIONS 777
|
||||
#define GAMES_MAXKEYSTROKESGAP 60
|
||||
#define GAMES_MAXPLAYERS 64
|
||||
#define GAMES_REGISTRATIONSIZE (100 * 10000)
|
||||
#define GAMES_REGISTRATION 1
|
||||
|
||||
|
||||
#define MYCCNAME "games"
|
||||
|
||||
std::string Games_pname;
|
||||
|
||||
#define RPC_FUNCS \
|
||||
{ (char *)MYCCNAME, (char *)"rng", (char *)"hash,playerid", 1, 2, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"fund", (char *)"amount", 1, 1, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"players", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"games", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"pending", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"setname", (char *)"pname", 1, 1, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"newgame", (char *)"maxplayers,buyin", 2, 2, 'C', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"playerinfo", (char *)"playertxid", 1, 1, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"gameinfo", (char *)"gametxid", 1, 1, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"keystrokes", (char *)"txid,hexstr", 2, 2, 'K', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"bailout", (char *)"gametxid", 1, 1, 'Q', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"highlander", (char *)"gametxid", 1, 1, 'H', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"events", (char *)"eventshex [gametxid [eventid]]", 1, 3, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"extract", (char *)"gametxid [pubkey]", 1, 2, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"bet", (char *)"amount hexstr", 2, 2, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"settle", (char *)"height", 1, 1, ' ', EVAL_GAMES }, \
|
||||
{ (char *)MYCCNAME, (char *)"register", (char *)"gametxid [playertxid]", 1, 2, 'R', EVAL_GAMES },
|
||||
|
||||
bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx);
|
||||
UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params);
|
||||
|
||||
#define CUSTOM_DISPATCH \
|
||||
if ( cp->evalcode == EVAL_GAMES ) \
|
||||
{ \
|
||||
if ( strcmp(method,"rng") == 0 ) \
|
||||
return(games_rng(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"rngnext") == 0 ) \
|
||||
return(games_rngnext(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"newgame") == 0 ) \
|
||||
return(games_newgame(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"gameinfo") == 0 ) \
|
||||
return(games_gameinfo(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"register") == 0 ) \
|
||||
return(games_register(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"events") == 0 ) \
|
||||
return(games_events(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"players") == 0 ) \
|
||||
return(games_players(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"games") == 0 ) \
|
||||
return(games_games(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"pending") == 0 ) \
|
||||
return(games_pending(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"setname") == 0 ) \
|
||||
return(games_setname(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"playerinfo") == 0 ) \
|
||||
return(games_playerinfo(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"keystrokes") == 0 ) \
|
||||
return(games_keystrokes(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"extract") == 0 ) \
|
||||
return(games_extract(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"bailout") == 0 ) \
|
||||
return(games_bailout(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"highlander") == 0 ) \
|
||||
return(games_highlander(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"fund") == 0 ) \
|
||||
return(games_fund(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"bet") == 0 ) \
|
||||
return(games_bet(txfee,cp,params)); \
|
||||
else if ( strcmp(method,"settle") == 0 ) \
|
||||
return(games_settle(txfee,cp,params)); \
|
||||
else \
|
||||
{ \
|
||||
result.push_back(Pair("result","error")); \
|
||||
result.push_back(Pair("error","invalid gamescc method")); \
|
||||
return(result); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1552
src/cc/gateways.cpp
1552
src/cc/gateways.cpp
File diff suppressed because it is too large
Load Diff
1293
src/cc/heir.cpp
1293
src/cc/heir.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,676 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
#ifndef HEIR_VALIDATE_H
|
||||
#define HEIR_VALIDATE_H
|
||||
|
||||
#include "CCinclude.h"
|
||||
#include "CCHeir.h"
|
||||
|
||||
#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos)
|
||||
|
||||
// makes coin initial tx opret
|
||||
vscript_t EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo);
|
||||
vscript_t EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan);
|
||||
|
||||
uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &isHeirSpendingBegan);
|
||||
//uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxid, uint8_t &isHeirSpendingBegan, bool noLogging = false);
|
||||
//uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false);
|
||||
uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging = false);
|
||||
uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging = false);
|
||||
|
||||
inline static bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); }
|
||||
inline static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); }
|
||||
|
||||
// helper class to allow polymorphic behaviour for HeirXXX() functions in case of coins
|
||||
class CoinHelper {
|
||||
public:
|
||||
|
||||
static uint8_t getMyEval() { return EVAL_HEIR; }
|
||||
static int64_t addOwnerInputs(uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) {
|
||||
return AddNormalinputs(mtx, ownerPubkey, total, maxinputs);
|
||||
}
|
||||
|
||||
static CScript makeCreateOpRet(uint256 dummyid, std::vector<CPubKey> dummyPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) {
|
||||
return CScript() << OP_RETURN << EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo);
|
||||
}
|
||||
static CScript makeAddOpRet(uint256 dummyid, std::vector<CPubKey> dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
|
||||
return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan);
|
||||
}
|
||||
static CScript makeClaimOpRet(uint256 dummyid, std::vector<CPubKey> dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
|
||||
return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan);
|
||||
}
|
||||
static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) {
|
||||
return MakeCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey);
|
||||
}
|
||||
static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) {
|
||||
return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG);
|
||||
}
|
||||
/* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) {
|
||||
return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG);
|
||||
} */
|
||||
static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) {
|
||||
struct CCcontract_info *cpHeir, heirC;
|
||||
cpHeir = CCinit(&heirC, EVAL_HEIR);
|
||||
return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey);
|
||||
}
|
||||
static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr)
|
||||
{
|
||||
uint8_t mypriv[32];
|
||||
Myprivkey(mypriv);
|
||||
CCaddr1of2set(cp, ownerPubkey, heirPubkey,mypriv, coinaddr);
|
||||
memset(mypriv,0,sizeof(mypriv));
|
||||
}
|
||||
};
|
||||
|
||||
// helper class to allow polymorphic behaviour for HeirXXX() functions in case of tokens
|
||||
class TokenHelper {
|
||||
public:
|
||||
static uint8_t getMyEval() { return EVAL_TOKENS; }
|
||||
static int64_t addOwnerInputs(uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) {
|
||||
struct CCcontract_info *cpHeir, heirC;
|
||||
cpHeir = CCinit(&heirC, EVAL_TOKENS);
|
||||
return AddTokenCCInputs(cpHeir, mtx, ownerPubkey, tokenid, total, maxinputs);
|
||||
}
|
||||
|
||||
static CScript makeCreateOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) {
|
||||
return EncodeTokenOpRet(tokenid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_HEIRDATA, EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo)));
|
||||
}
|
||||
static CScript makeAddOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
|
||||
return EncodeTokenOpRet(tokenid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)));
|
||||
}
|
||||
static CScript makeClaimOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
|
||||
return EncodeTokenOpRet(tokenid, voutTokenPubkeys,
|
||||
std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan)));
|
||||
}
|
||||
|
||||
static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) {
|
||||
return MakeTokensCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey);
|
||||
}
|
||||
static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) {
|
||||
return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS
|
||||
}
|
||||
/* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) {
|
||||
return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS
|
||||
} */
|
||||
static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) {
|
||||
struct CCcontract_info *cpHeir, heirC;
|
||||
cpHeir = CCinit(&heirC, EVAL_HEIR);
|
||||
return GetTokensCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey);
|
||||
}
|
||||
|
||||
static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) {
|
||||
uint8_t mypriv[32];
|
||||
Myprivkey(mypriv);
|
||||
CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, mypriv, coinaddr);
|
||||
memset(mypriv,0,sizeof(mypriv));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Small framework for vins and vouts validation implementing a variation of 'chain of responsibility' pattern:
|
||||
* It consists of two classes CInputValidationPlan and COutputValidationPlan which both are configured with an array of vectors of validators
|
||||
* (These validators are derived from the class CValidatorBase).
|
||||
*
|
||||
* A example of a validator may verify for a vout if its public key corresponds to the public key which is stored in opreturn.
|
||||
* Or, vin validator may check if this vin depicts correctly to the CC contract's address.
|
||||
*
|
||||
* For validating vins CInputValidator additionally is provided with an instance of a class derived from the CInputIdentifierBase class.
|
||||
* this identifier class allows to select identical vins (for example, normal vins or cc input vins) and apply validators from the corresponding vector to it.
|
||||
* Note: CInputValidator treats that at least one identified vin should be present, otherwise it returns eval->invalid() and false.
|
||||
*
|
||||
* For validating vouts COutputValidator is configured for each vector of validators with the vout index to which these validators are applied
|
||||
* (see constructors of both CInputValidator and COutputValidator)
|
||||
*/
|
||||
|
||||
/**
|
||||
* base class for all validators
|
||||
*/
|
||||
class CValidatorBase
|
||||
{
|
||||
public:
|
||||
CValidatorBase(CCcontract_info* cp) : m_cp(cp) {}
|
||||
virtual bool isVinValidator() const = 0;
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const = 0;
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const = 0;
|
||||
|
||||
protected:
|
||||
CCcontract_info * m_cp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for classes which identify vins as normal or cc inputs
|
||||
*/
|
||||
class CInputIdentifierBase
|
||||
{
|
||||
public:
|
||||
CInputIdentifierBase(CCcontract_info* cp) : m_cp(cp) {}
|
||||
virtual std::string inputName() const = 0;
|
||||
virtual bool identifyInput(CTxIn vin) const = 0;
|
||||
protected:
|
||||
CCcontract_info * m_cp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates an array containing rows of validators
|
||||
* Each row is a vector of validators (zero is possible) for validating vins or prev tx's vouts
|
||||
* this validation plan is used for validating tx inputs
|
||||
*/
|
||||
template <typename TValidatorBase>
|
||||
class CInputValidationPlan
|
||||
{
|
||||
using ValidatorsRow = std::vector<TValidatorBase*>;
|
||||
|
||||
public:
|
||||
|
||||
// Pushes a row of validators for validating a vin or vout
|
||||
// @param CInputIdentifierBase* pointer to class-identifier which determines several identical adjacent vins (like in schema "vin.0+: normal inputs")
|
||||
// @param pargs parameter pack of zero or more pointer to validator objects
|
||||
// Why pointers? because we store the base class in validators' row and then call its virtual functions
|
||||
template <typename TValidatorBaseX, typename... ARGS>
|
||||
void pushValidators(CInputIdentifierBase *identifier, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ...
|
||||
{
|
||||
ValidatorsRow vValidators({ (TValidatorBase*)pargs... });
|
||||
m_arrayValidators.push_back(std::make_pair(identifier, vValidators));
|
||||
}
|
||||
|
||||
// validate tx inputs and corresponding prev tx vouts
|
||||
bool validate(const CTransaction& tx, Eval* eval)
|
||||
{
|
||||
std::string message = "<empty>";
|
||||
//std::cerr << "CInputValidationPlan::validate() starting vins validation..." << std::endl;
|
||||
|
||||
int32_t ival = 0;
|
||||
int32_t iv = 0;
|
||||
int32_t numv = tx.vin.size();
|
||||
int32_t numValidators = m_arrayValidators.size();
|
||||
|
||||
// run over vins:
|
||||
while (iv < numv && ival < numValidators) {
|
||||
|
||||
int32_t identifiedCount = 0;
|
||||
CInputIdentifierBase *identifier = m_arrayValidators[ival].first;
|
||||
// check if this is 'our' input:
|
||||
while (iv < numv && identifier->identifyInput(tx.vin[iv])) {
|
||||
|
||||
// get prev tx:
|
||||
CTransaction prevTx, *pPrevTxOrNull = NULL;
|
||||
uint256 hashBlock;
|
||||
|
||||
if (!eval->GetTxUnconfirmed(tx.vin[iv].prevout.hash, prevTx, hashBlock)) {
|
||||
std::ostringstream stream;
|
||||
stream << "can't find vinTx for vin=" << iv << ".";
|
||||
return eval->Invalid(stream.str().c_str());
|
||||
}
|
||||
pPrevTxOrNull = &prevTx; // TODO: get prev tx only if it required (i.e. if vout validators are present)
|
||||
|
||||
// exec 'validators' from validator row of ival index, for tx.vin[iv]
|
||||
if (!execValidatorsInRow(&tx, pPrevTxOrNull, iv, ival, message)) {
|
||||
std::ostringstream stream;
|
||||
stream << "invalid tx vin[" << iv << "]:" << message;
|
||||
return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid'
|
||||
}
|
||||
|
||||
identifiedCount++; // how many vins we identified
|
||||
iv++; // advance to the next vin
|
||||
}
|
||||
|
||||
// CInputValidationPlan treats that there must be at least one identified vin for configured validators' row
|
||||
// like in 'vin.0: normal input'
|
||||
if (identifiedCount == 0) {
|
||||
std::ostringstream stream;
|
||||
stream << "can't find required vins for " << identifier->inputName() << ".";
|
||||
return eval->Invalid(stream.str().c_str());
|
||||
}
|
||||
|
||||
ival++; // advance to the next validator row
|
||||
// and it will try the same vin with the new CInputIdentifierBase and validators row
|
||||
}
|
||||
|
||||
// validation is successful if all validators have been used (i.e. ival = numValidators)
|
||||
if (ival < numValidators) {
|
||||
std::cerr << "CInputValidationPlan::validate() incorrect tx" << " ival=" << ival << " numValidators=" << numValidators << std::endl;
|
||||
return eval->Invalid("incorrect tx structure: not all required vins are present.");
|
||||
}
|
||||
|
||||
//std::cerr << "CInputValidationPlan::validate() returns with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv)
|
||||
bool execValidatorsInRow(const CTransaction* pTx, const CTransaction* pPrevTx, int32_t iv, int32_t ival, std::string& refMessage) const
|
||||
{
|
||||
// check boundaries:
|
||||
if (ival < 0 || ival >= m_arrayValidators.size()) {
|
||||
std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size();
|
||||
refMessage = "internal error: incorrect param ival index";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iv < 0 || iv >= pTx->vin.size()) {
|
||||
std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size();
|
||||
refMessage = "internal error: incorrect param iv index";
|
||||
return false;
|
||||
}
|
||||
|
||||
// get requested row of validators:
|
||||
ValidatorsRow vValidators = m_arrayValidators[ival].second;
|
||||
|
||||
//std::cerr << "CInputValidationPlan::execValidatorsInRow() calling validators" << " for vin iv=" << iv << " ival=" << ival << std::endl;
|
||||
|
||||
for (auto v : vValidators) {
|
||||
bool result;
|
||||
|
||||
if (v->isVinValidator())
|
||||
// validate this vin and previous vout:
|
||||
result = v->validateVin(pTx->vin[iv], pPrevTx->vout, pTx->vin[iv].prevout.n, refMessage);
|
||||
else
|
||||
// if it is vout validator pass the previous tx vout:
|
||||
result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], pTx->vin[iv].prevout.n, refMessage);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return true; // validation OK
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
//std::map<CInputIdentifierBase*, ValidatorsRow> m_arrayValidators;
|
||||
std::vector< std::pair<CInputIdentifierBase*, ValidatorsRow> > m_arrayValidators;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulates an array containing rows of validators
|
||||
* Each row is a vector of validators (zero is possible) for validating vouts
|
||||
* this validation plan is used for validating tx outputs
|
||||
*/
|
||||
template <typename TValidatorBase>
|
||||
class COutputValidationPlan
|
||||
{
|
||||
using ValidatorsRow = std::vector<TValidatorBase*>;
|
||||
|
||||
public:
|
||||
// Pushes a row of validators for validating a vout
|
||||
// @param ivout index to vout to validate
|
||||
// @param pargs parameter pack of zero or more pointer to validator objects
|
||||
// Why pointers? because we store base class and call its virtual functions
|
||||
|
||||
template <typename TValidatorBaseX, typename... ARGS>
|
||||
void pushValidators(int32_t ivout, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ...
|
||||
{
|
||||
ValidatorsRow vValidators({ (TValidatorBase*)pargs... });
|
||||
m_arrayValidators.push_back(std::make_pair(ivout, vValidators));
|
||||
}
|
||||
|
||||
// validate tx outputs
|
||||
bool validate(const CTransaction& tx, Eval* eval)
|
||||
{
|
||||
std::string message = "<empty>";
|
||||
//std::cerr << "COutputValidationPlan::validateOutputs() starting vouts validation..." << std::endl;
|
||||
|
||||
int32_t ival = 0;
|
||||
int32_t numVouts = tx.vout.size();
|
||||
int32_t numValidators = m_arrayValidators.size();
|
||||
|
||||
// run over vouts:
|
||||
while (ival < numValidators) {
|
||||
|
||||
int32_t ivout = m_arrayValidators[ival].first;
|
||||
if (ivout >= numVouts) {
|
||||
std::cerr << "COutputValidationPlan::validate() incorrect tx" << "for ival=" << ival << " in tx.vout no such ivout=" << ivout << std::endl;
|
||||
return eval->Invalid("incorrect tx structure: not all required vouts are present.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// exec 'validators' from validator row of ival index, for tx.vout[ivout]
|
||||
if (!execValidatorsInRow(&tx, ivout, ival, message)) {
|
||||
std::ostringstream stream;
|
||||
stream << "invalid tx vout[" << ivout << "]:" << message;
|
||||
return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid'
|
||||
}
|
||||
}
|
||||
ival++; // advance to the next vout
|
||||
}
|
||||
//std::cerr << "COutputValidationPlan::validate() returns with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv)
|
||||
bool execValidatorsInRow(const CTransaction* pTx, int32_t iv, int32_t ival, std::string& refMessage) const
|
||||
{
|
||||
// check boundaries:
|
||||
if (ival < 0 || ival >= m_arrayValidators.size()) {
|
||||
std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size();
|
||||
refMessage = "internal error: incorrect param ival index";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iv < 0 || iv >= pTx->vout.size()) {
|
||||
std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size();
|
||||
refMessage = "internal error: incorrect param iv index";
|
||||
return false;
|
||||
}
|
||||
|
||||
// get requested row of validators:
|
||||
ValidatorsRow vValidators = m_arrayValidators[ival].second;
|
||||
|
||||
//std::cerr << "COutputValidationPlan::execRow() calling validators" << " for vout iv=" << iv << " ival=" << ival << std::endl;
|
||||
|
||||
for (auto v : vValidators) {
|
||||
|
||||
if (!v->isVinValidator()) {
|
||||
// if this is a 'in' validation plan then pass the previous tx vout:
|
||||
bool result = v->validateVout(pTx->vout[iv], iv, refMessage);
|
||||
if (!result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return true; // validation OK
|
||||
}
|
||||
|
||||
private:
|
||||
//std::map<int32_t, ValidatorsRow> m_mapValidators;
|
||||
std::vector< std::pair<int32_t, ValidatorsRow> > m_arrayValidators;
|
||||
|
||||
};
|
||||
|
||||
class CNormalInputIdentifier : CInputIdentifierBase {
|
||||
public:
|
||||
CNormalInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {}
|
||||
virtual std::string inputName() const { return std::string("normal input"); }
|
||||
virtual bool identifyInput(CTxIn vin) const {
|
||||
return !IsCCInput(vin.scriptSig);
|
||||
}
|
||||
};
|
||||
|
||||
class CCCInputIdentifier : CInputIdentifierBase {
|
||||
public:
|
||||
CCCInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {}
|
||||
virtual std::string inputName() const { return std::string("CC input"); }
|
||||
virtual bool identifyInput(CTxIn vin) const {
|
||||
return IsCCInput(vin.scriptSig);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Validates 1of2address for vout (may be used for either this or prev tx)
|
||||
*/
|
||||
template <class Helper> class CCC1of2AddressValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
CCC1of2AddressValidator(CCcontract_info* cp, CScript opRetScript, std::string customMessage = "") :
|
||||
m_fundingOpretScript(opRetScript), m_customMessage(customMessage), CValidatorBase(cp) {}
|
||||
|
||||
virtual bool isVinValidator() const { return false; }
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
|
||||
{
|
||||
//std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl;
|
||||
CPubKey ownerPubkey, heirPubkey;
|
||||
int64_t inactivityTime;
|
||||
std::string heirName, memo;
|
||||
uint256 tokenid;
|
||||
|
||||
uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true);
|
||||
if (funcId == 0) {
|
||||
message = m_customMessage + std::string(" invalid opreturn format");
|
||||
std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
char shouldBeAddr[65], ccAddr[65];
|
||||
|
||||
//GetCCaddress1of2(m_cp, shouldBeAddr, ownerPubkey, heirPubkey);
|
||||
Helper::GetCoinsOrTokensCCaddress1of2(shouldBeAddr, ownerPubkey, heirPubkey);
|
||||
|
||||
if (vout.scriptPubKey.IsPayToCryptoCondition()) {
|
||||
if (Getscriptaddress(ccAddr, vout.scriptPubKey) && strcmp(shouldBeAddr, ccAddr) == 0) {
|
||||
//std::cerr << "CCC1of2AddressValidator::validateVout() exits with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
message = m_customMessage + std::string(" incorrect heir funding address: incorrect pubkey(s)");
|
||||
}
|
||||
}
|
||||
else {
|
||||
message = m_customMessage + std::string(" incorrect heir funding address: not a 1of2addr");
|
||||
}
|
||||
|
||||
std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl;
|
||||
return false;
|
||||
}
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return false; }
|
||||
|
||||
private:
|
||||
CScript m_fundingOpretScript;
|
||||
std::string m_customMessage;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Validates if this is vout to owner or heir from opret (funding or change)
|
||||
*/
|
||||
template <class Helper> class CMyPubkeyVoutValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
CMyPubkeyVoutValidator(CCcontract_info* cp, CScript opRetScript, bool checkNormals)
|
||||
: m_fundingOpretScript(opRetScript), m_checkNormals(checkNormals), CValidatorBase(cp) { }
|
||||
|
||||
virtual bool isVinValidator() const { return false; }
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
|
||||
{
|
||||
//std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl;
|
||||
|
||||
CPubKey ownerPubkey, heirPubkey;
|
||||
int64_t inactivityTime;
|
||||
std::string heirName, memo;
|
||||
uint256 tokenid;
|
||||
|
||||
///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl;
|
||||
|
||||
// get both pubkeys:
|
||||
uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true);
|
||||
if (funcId == 0) {
|
||||
message = std::string("invalid opreturn format");
|
||||
return false;
|
||||
}
|
||||
|
||||
CScript ownerScript;
|
||||
CScript heirScript;
|
||||
if (m_checkNormals) { //not used, incorrect check, too strict
|
||||
ownerScript = CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey;
|
||||
heirScript = CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey;
|
||||
std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(coin,owner)=" << CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(coin,heir)=" << CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl;
|
||||
}
|
||||
else {
|
||||
ownerScript = Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey;
|
||||
heirScript = Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey;
|
||||
std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(owner)=" << Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(heir)=" << Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl;
|
||||
}
|
||||
|
||||
// recreate scriptPubKey for owner and heir and compare it with that of the vout to check:
|
||||
if (vout.scriptPubKey == ownerScript || vout.scriptPubKey == heirScript) {
|
||||
// this is vout to owner or heir addr:
|
||||
//std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with false (not the owner's or heir's addresses)" << std::endl;
|
||||
message = std::string("invalid pubkey");
|
||||
return false;
|
||||
}
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
|
||||
|
||||
private:
|
||||
CScript m_fundingOpretScript;
|
||||
//uint256 m_lasttxid;
|
||||
bool m_checkNormals;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the user is the heir and the heir is allowed to spend (duration > inactivityTime)
|
||||
*/
|
||||
template <class Helper> class CHeirSpendValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
CHeirSpendValidator(CCcontract_info* cp, CScript opRetScript, uint256 latesttxid, uint8_t isHeirSpendingBegan)
|
||||
: m_fundingOpretScript(opRetScript), m_latesttxid(latesttxid), m_isHeirSpendingBegan(isHeirSpendingBegan), CValidatorBase(cp) {}
|
||||
|
||||
virtual bool isVinValidator() const { return false; }
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
|
||||
{
|
||||
//std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl;
|
||||
|
||||
CPubKey ownerPubkey, heirPubkey;
|
||||
int64_t inactivityTime;
|
||||
std::string heirName, memo;
|
||||
uint256 tokenid;
|
||||
|
||||
// get heir pubkey:
|
||||
uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true);
|
||||
if (funcId == 0) {
|
||||
message = std::string("invalid opreturn format");
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t numblocks;
|
||||
int64_t durationSec = CCduration(numblocks, m_latesttxid);
|
||||
|
||||
// recreate scriptPubKey for heir and compare it with that of the vout:
|
||||
if (vout.scriptPubKey == Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey) {
|
||||
// this is the heir is trying to spend
|
||||
if (!m_isHeirSpendingBegan && durationSec <= inactivityTime) {
|
||||
message = "heir is not allowed yet to spend funds";
|
||||
std::cerr << "CHeirSpendValidator::validateVout() heir is not allowed yet to spend funds" << std::endl;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// heir is allowed to spend
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//std::cerr << "CHeirSpendValidator::validateVout() exits with true" << std::endl;
|
||||
|
||||
// this is not heir:
|
||||
return true;
|
||||
}
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
|
||||
|
||||
private:
|
||||
CScript m_fundingOpretScript;
|
||||
uint256 m_latesttxid;
|
||||
uint8_t m_isHeirSpendingBegan;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates this opreturn and compares it with the opreturn from the previous tx
|
||||
*/
|
||||
template <class Helper> class COpRetValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
COpRetValidator(CCcontract_info* cp, CScript opret)
|
||||
: m_fundingOpretScript(opret), CValidatorBase(cp) {}
|
||||
|
||||
virtual bool isVinValidator() const { return false; }
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
|
||||
{
|
||||
//std::cerr << "COpRetValidator::validateVout() entered" << std::endl;
|
||||
|
||||
uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid;
|
||||
uint8_t dummyIsHeirSpendingBegan;
|
||||
|
||||
uint8_t funcId = DecodeHeirEitherOpRet(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true);
|
||||
if (funcId == 0) {
|
||||
message = std::string("invalid opreturn format");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t initialFuncId = DecodeHeirEitherOpRet(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan, true);
|
||||
if (initialFuncId == 0) {
|
||||
message = std::string("invalid initial tx opreturn format");
|
||||
return false;
|
||||
}
|
||||
|
||||
// validation rules:
|
||||
if (!isMyFuncId(funcId)) {
|
||||
message = std::string("invalid funcid in opret");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeid(Helper) == typeid(TokenHelper)) {
|
||||
if (tokenid != initialTokenid) {
|
||||
message = std::string("invalid tokenid in opret");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//std::cerr << "COpRetValidator::validateVout() exits with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
|
||||
|
||||
private:
|
||||
CScript m_fundingOpretScript;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* marker spending prevention validator,
|
||||
* returns false if for tx with funcid=F vout.1 is being tried to spend
|
||||
*/
|
||||
template <class Helper> class CMarkerValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
CMarkerValidator(CCcontract_info* cp)
|
||||
: CValidatorBase(cp) { }
|
||||
|
||||
virtual bool isVinValidator() const { return true; } // this is vin validator
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; }
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const {
|
||||
|
||||
uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid;
|
||||
uint8_t dummyIsHeirSpendingBegan;
|
||||
|
||||
//std::cerr << "CMarkerValidator::validateVin() prevVout.size()=" << prevVout.size() << " prevN=" << prevN << std::endl;
|
||||
if (prevVout.size() > 0) {
|
||||
|
||||
// get funcId for prev tx:
|
||||
uint8_t funcId = DecodeHeirEitherOpRet(prevVout[prevVout.size()-1].scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true);
|
||||
|
||||
//std::cerr << "CMarkerValidator::validateVin() funcId=" << (funcId?funcId:' ') << std::endl;
|
||||
|
||||
if (funcId == 'F' && prevN == 1) { // do not allow to spend 'F' marker's vout
|
||||
message = std::string("spending marker not allowed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//std::cerr << "CMarkerValidator::validateVin() exits with true" << std::endl;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* empty validator always returns true
|
||||
*/
|
||||
template <class Helper> class CNullValidator : CValidatorBase
|
||||
{
|
||||
public:
|
||||
CNullValidator(CCcontract_info* cp)
|
||||
: CValidatorBase(cp) { }
|
||||
|
||||
virtual bool isVinValidator() const { return false; }
|
||||
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; }
|
||||
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,764 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "cc/eval.h"
|
||||
#include "cc/utils.h"
|
||||
#include "importcoin.h"
|
||||
#include "crosschain.h"
|
||||
#include "primitives/transaction.h"
|
||||
#include "cc/CCinclude.h"
|
||||
#include <wolfssl/wolfcrypt/sha.h>
|
||||
#include "cc/CCtokens.h"
|
||||
|
||||
#include "key_io.h"
|
||||
#define CODA_BURN_ADDRESS "KPrrRoPfHOnNpZZQ6laHXdQDkSQDkVHaN0V+LizLlHxz7NaA59sBAAAA"
|
||||
/*
|
||||
* CC Eval method for import coin.
|
||||
*
|
||||
* This method should control every parameter of the ImportCoin transaction, since it has no signature
|
||||
* to protect it from malleability.
|
||||
|
||||
##### 0xffffffff is a special CCid for single chain/dual daemon imports
|
||||
*/
|
||||
|
||||
extern std::string ASSETCHAINS_SELFIMPORT;
|
||||
extern uint16_t ASSETCHAINS_CODAPORT,ASSETCHAINS_BEAMPORT;
|
||||
extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33];
|
||||
extern uint256 HUSH_EARLYTXID;
|
||||
|
||||
// utilities from gateways.cpp
|
||||
uint256 BitcoinGetProofMerkleRoot(const std::vector<uint8_t> &proofData, std::vector<uint256> &txids);
|
||||
uint256 GatewaysReverseScan(uint256 &txid, int32_t height, uint256 reforacletxid, uint256 batontxid);
|
||||
int32_t GatewaysCointxidExists(struct CCcontract_info *cp, uint256 cointxid);
|
||||
uint8_t DecodeImportGatewayBindOpRet(char *burnaddr,const CScript &scriptPubKey,std::string &coin,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector<CPubKey> &importgatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype);
|
||||
int64_t ImportGatewayVerify(char *refburnaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 burntxid,const std::string deposithex,std::vector<uint8_t>proof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2);
|
||||
char *nonportable_path(char *str);
|
||||
char *portable_path(char *str);
|
||||
void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep);
|
||||
void *filestr(long *allocsizep,char *_fname);
|
||||
|
||||
cJSON* CodaRPC(char **retstr,char const *arg0,char const *arg1,char const *arg2,char const *arg3,char const *arg4,char const *arg5)
|
||||
{
|
||||
char cmdstr[5000],fname[256],*jsonstr;
|
||||
long fsize;
|
||||
cJSON *retjson=NULL;
|
||||
|
||||
sprintf(fname,"/tmp/coda.%s",arg0);
|
||||
sprintf(cmdstr,"coda.exe client %s %s %s %s %s %s > %s 2>&1",arg0,arg1,arg2,arg3,arg4,arg5,fname);
|
||||
*retstr = 0;
|
||||
if (system(cmdstr)<0) return (retjson);
|
||||
if ( (jsonstr=(char *)filestr(&fsize,fname)) != 0 )
|
||||
{
|
||||
jsonstr[strlen(jsonstr)-1]='\0';
|
||||
if ( (strncmp(jsonstr,"Merkle List of transactions:",28)!=0) || (retjson= cJSON_Parse(jsonstr+29)) == 0)
|
||||
*retstr=jsonstr;
|
||||
else free(jsonstr);
|
||||
}
|
||||
return(retjson);
|
||||
}
|
||||
|
||||
// makes source tx for self import tx
|
||||
CMutableTransaction MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount)
|
||||
{
|
||||
const int64_t txfee = 10000;
|
||||
int64_t inputs, change;
|
||||
CPubKey myPubKey = Mypubkey();
|
||||
struct CCcontract_info *cpDummy, C;
|
||||
|
||||
cpDummy = CCinit(&C, EVAL_TOKENS); // this is just for FinalizeCCTx to work
|
||||
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
|
||||
if (AddNormalinputs(mtx, myPubKey, 2 * txfee, 4) == 0) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeSelfImportSourceTx() warning: cannot find normal inputs for txfee" << std::endl);
|
||||
}
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(dest);
|
||||
mtx.vout.push_back(CTxOut(txfee, scriptPubKey));
|
||||
|
||||
//make opret with 'burned' amount:
|
||||
FinalizeCCTx(0, cpDummy, mtx, myPubKey, txfee, CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN << (uint8_t)'A' << amount));
|
||||
return mtx;
|
||||
}
|
||||
|
||||
// make sure vin is signed by pubkey33
|
||||
bool CheckVinPubKey(const CTransaction &sourcetx, int32_t i, uint8_t pubkey33[33])
|
||||
{
|
||||
CTransaction vintx;
|
||||
uint256 blockHash;
|
||||
char destaddr[64], pkaddr[64];
|
||||
|
||||
if (i < 0 || i >= sourcetx.vin.size())
|
||||
return false;
|
||||
|
||||
if( !myGetTransaction(sourcetx.vin[i].prevout.hash, vintx, blockHash) ) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVinPubKey() could not load vintx" << sourcetx.vin[i].prevout.hash.GetHex() << std::endl);
|
||||
return false;
|
||||
}
|
||||
if( sourcetx.vin[i].prevout.n < vintx.vout.size() && Getscriptaddress(destaddr, vintx.vout[sourcetx.vin[i].prevout.n].scriptPubKey) != 0 )
|
||||
{
|
||||
pubkey2addr(pkaddr, pubkey33);
|
||||
if (strcmp(pkaddr, destaddr) == 0) {
|
||||
return true;
|
||||
}
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVinPubKey() mismatched vin[" << i << "].prevout.n=" << sourcetx.vin[i].prevout.n << " -> destaddr=" << destaddr << " vs pkaddr=" << pkaddr << std::endl);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ac_import=PUBKEY support:
|
||||
// prepare a tx for creating import tx and quasi-burn tx
|
||||
int32_t GetSelfimportProof(const CMutableTransaction sourceMtx, CMutableTransaction &templateMtx, ImportProof &proofNull) // find burnTx with hash from "other" daemon
|
||||
{
|
||||
MerkleBranch newBranch;
|
||||
CMutableTransaction tmpmtx;
|
||||
//CTransaction sourcetx;
|
||||
|
||||
tmpmtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
|
||||
/*
|
||||
if (!E_UNMARSHAL(ParseHex(rawsourcetx), ss >> sourcetx)) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: could not unmarshal source tx" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (sourcetx.vout.size() == 0) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: vout size is 0" << std::endl);
|
||||
return -1;
|
||||
} */
|
||||
|
||||
/*if (ivout < 0) { // "ivout < 0" means "find"
|
||||
// try to find vout
|
||||
CPubKey myPubkey = Mypubkey();
|
||||
ivout = 0;
|
||||
// skip change:
|
||||
if (sourcetx.vout[ivout].scriptPubKey == (CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG))
|
||||
ivout++;
|
||||
}
|
||||
|
||||
if (ivout >= sourcetx.vout.size()) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: needed vout not found" << std::endl);
|
||||
return -1;
|
||||
} */
|
||||
|
||||
int32_t ivout = 0;
|
||||
|
||||
// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "GetSelfimportProof: using vout[" << ivout << "] of the passed rawtx" << std::endl);
|
||||
|
||||
CScript scriptPubKey = sourceMtx.vout[ivout].scriptPubKey;
|
||||
|
||||
//mtx is template for import tx
|
||||
templateMtx = sourceMtx;
|
||||
templateMtx.fOverwintered = tmpmtx.fOverwintered;
|
||||
|
||||
//malleability fix for burn tx:
|
||||
//mtx.nExpiryHeight = tmpmtx.nExpiryHeight;
|
||||
templateMtx.nExpiryHeight = sourceMtx.nExpiryHeight;
|
||||
|
||||
templateMtx.nVersionGroupId = tmpmtx.nVersionGroupId;
|
||||
templateMtx.nVersion = tmpmtx.nVersion;
|
||||
templateMtx.vout.clear();
|
||||
templateMtx.vout.resize(1);
|
||||
|
||||
uint8_t evalCode, funcId;
|
||||
int64_t burnAmount;
|
||||
vscript_t vopret;
|
||||
if( !GetOpReturnData(sourceMtx.vout.back().scriptPubKey, vopret) ||
|
||||
!E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> burnAmount)) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof() could not unmarshal source tx opret" << std::endl);
|
||||
return -1;
|
||||
}
|
||||
templateMtx.vout[0].nValue = burnAmount;
|
||||
templateMtx.vout[0].scriptPubKey = scriptPubKey;
|
||||
|
||||
// not sure we need this now as we create sourcetx ourselves:
|
||||
/*if (sourcetx.GetHash() != sourcetxid) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: passed source txid incorrect" << std::endl);
|
||||
return(-1);
|
||||
}*/
|
||||
|
||||
// check ac_pubkey:
|
||||
if (!CheckVinPubKey(sourceMtx, 0, ASSETCHAINS_OVERRIDE_PUBKEY33)) {
|
||||
return -1;
|
||||
}
|
||||
proofNull = ImportProof(std::make_pair(sourceMtx.GetHash(), newBranch));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// make import tx with burntx and dual daemon
|
||||
std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string srcaddr, std::vector<CTxOut> vouts)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()),burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk; uint256 codaburntxid; std::vector<unsigned char> dummyproof;
|
||||
int32_t i,numvouts,n,m; std::string coin,error; struct CCcontract_info *cp, C;
|
||||
cJSON *result,*tmp,*tmp1; unsigned char hash[SHA256_DIGEST_LENGTH+1];
|
||||
char out[SHA256_DIGEST_LENGTH*2+1],*retstr,*destaddr,*receiver; TxProof txProof; uint64_t amount;
|
||||
|
||||
cp = CCinit(&C, EVAL_GATEWAYS);
|
||||
if (txfee == 0)
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
SHA256_CTX sha256;
|
||||
SHA256_Init(&sha256);
|
||||
SHA256_Update(&sha256, receipt.c_str(), receipt.size());
|
||||
SHA256_Final(hash, &sha256);
|
||||
for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
|
||||
{
|
||||
sprintf(out + (i * 2), "%02x", hash[i]);
|
||||
}
|
||||
out[65]='\0';
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: hash=" << out << std::endl);
|
||||
codaburntxid.SetHex(out);
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: receipt=" << receipt << " codaburntxid=" << codaburntxid.GetHex().data() << " amount=" << (double)amount / COIN << std::endl);
|
||||
result=CodaRPC(&retstr,"prove-payment","-address",srcaddr.c_str(),"-receipt-chain-hash",receipt.c_str(),"");
|
||||
if (result==0)
|
||||
{
|
||||
if (retstr!=0)
|
||||
{
|
||||
CCerror=std::string("CodaRPC: ")+retstr;
|
||||
free(retstr);
|
||||
}
|
||||
return("");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((tmp=jobj(jitem(jarray(&n,result,(char *)"payments"),0),(char *)"payload"))!=0 && (destaddr=jstr(jobj(tmp,(char *)"common"),(char *)"memo"))!=0 &&
|
||||
(receiver=jstr(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"receiver"))!=0 && (amount=j64bits(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"amount"))!=0)
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: receiver=" << receiver << " destaddr=" << destaddr << " amount=" << amount << std::endl);
|
||||
if (strcmp(receiver,CODA_BURN_ADDRESS)!=0)
|
||||
{
|
||||
CCerror="MakeCodaImportTx: invalid burn address, coins do not go to predefined burn address - ";
|
||||
CCerror+=CODA_BURN_ADDRESS;
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl);
|
||||
free(result);
|
||||
return("");
|
||||
}
|
||||
CTxDestination dest = DecodeDestination(destaddr);
|
||||
CScript scriptPubKey = GetScriptForDestination(dest);
|
||||
if (vouts[0]!=CTxOut(amount*COIN,scriptPubKey))
|
||||
{
|
||||
CCerror="MakeCodaImportTx: invalid destination address, burnTx memo!=importTx destination";
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl);
|
||||
free(result);
|
||||
return("");
|
||||
}
|
||||
if (amount*COIN!=vouts[0].nValue)
|
||||
{
|
||||
CCerror="MakeCodaImportTx: invalid amount, burnTx amount!=importTx amount";
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl);
|
||||
free(result);
|
||||
return("");
|
||||
}
|
||||
burntx.vin.push_back(CTxIn(codaburntxid,0,CScript()));
|
||||
burntx.vout.push_back(MakeBurnOutput(amount*COIN,0xffffffff,"CODA",vouts,dummyproof,srcaddr,receipt));
|
||||
return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof,burntx,vouts)));
|
||||
}
|
||||
else
|
||||
{
|
||||
CCerror="MakeCodaImportTx: invalid Coda burn tx";
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl);
|
||||
free(result);
|
||||
return("");
|
||||
}
|
||||
|
||||
}
|
||||
CCerror="MakeCodaImportTx: error fetching Coda tx";
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl);
|
||||
free(result);
|
||||
return("");
|
||||
}
|
||||
|
||||
// use proof from the above functions to validate the import
|
||||
|
||||
int32_t CheckBEAMimport(TxProof proof,std::vector<uint8_t> rawproof,CTransaction burnTx,std::vector<CTxOut> payouts)
|
||||
{
|
||||
// check with dual-BEAM daemon via ASSETCHAINS_BEAMPORT for validity of burnTx
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int32_t CheckCODAimport(CTransaction importTx,CTransaction burnTx,std::vector<CTxOut> payouts,std::string srcaddr,std::string receipt)
|
||||
{
|
||||
cJSON *result,*tmp,*tmp1; char *retstr,out[SHA256_DIGEST_LENGTH*2+1]; unsigned char hash[SHA256_DIGEST_LENGTH+1]; int i,n,m;
|
||||
SHA256_CTX sha256; uint256 codaburntxid; char *destaddr,*receiver; uint64_t amount;
|
||||
|
||||
// check with dual-CODA daemon via ASSETCHAINS_CODAPORT for validity of burnTx
|
||||
SHA256_Init(&sha256);
|
||||
SHA256_Update(&sha256, receipt.c_str(), receipt.size());
|
||||
SHA256_Final(hash, &sha256);
|
||||
for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
|
||||
{
|
||||
sprintf(out + (i * 2), "%02x", hash[i]);
|
||||
}
|
||||
out[65]='\0';
|
||||
codaburntxid.SetHex(out);
|
||||
result=CodaRPC(&retstr,"prove-payment","-address",srcaddr.c_str(),"-receipt-chain-hash",receipt.c_str(),"");
|
||||
if (result==0)
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CodaRPC error: " << retstr << std::endl);
|
||||
free(retstr);
|
||||
return (-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((tmp=jobj(jitem(jarray(&n,result,(char *)"payments"),0),(char *)"payload"))==0 || (destaddr=jstr(jobj(tmp,(char *)"common"),(char *)"memo"))==0 ||
|
||||
(receiver=jstr(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"receiver"))==0 || (amount=j64bits(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"amount"))==0)
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid Coda burn tx" << jprint(result,1) << std::endl);
|
||||
free(result);
|
||||
return (-1);
|
||||
}
|
||||
CTxDestination dest = DecodeDestination(destaddr);
|
||||
CScript scriptPubKey = GetScriptForDestination(dest);
|
||||
if (payouts[0]!=CTxOut(amount*COIN,scriptPubKey));
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "Destination address in burn tx does not match destination in import tx" << std::endl);
|
||||
free(result);
|
||||
return (-1);
|
||||
}
|
||||
if (strcmp(receiver,CODA_BURN_ADDRESS)!=0)
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid burn address " << jstr(tmp1,(char *)"receiver") << std::endl);
|
||||
free(result);
|
||||
return (-1);
|
||||
}
|
||||
if (amount*COIN!=payouts[0].nValue)
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "Burn amount and import amount not matching, " << j64bits(tmp,(char *)"amount") << " - " << payouts[0].nValue/COIN << std::endl);
|
||||
free(result);
|
||||
return (-1);
|
||||
}
|
||||
if (burnTx.vin[0].prevout.hash!=codaburntxid || importTx.vin[0].prevout.hash!=burnTx.GetHash())
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid import/burn tx vin" << std::endl);
|
||||
free(result);
|
||||
return (-1);
|
||||
}
|
||||
free(result);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction burnTx,std::string refcoin,std::vector<uint8_t> proof,
|
||||
uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256> txids,uint256 burntxid,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub, int64_t amount)
|
||||
{
|
||||
CTransaction oracletx,bindtx,regtx; int32_t i,m,n=0,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype;
|
||||
uint256 txid,oracletxid,tmporacletxid,merkleroot,mhash,hashBlock;
|
||||
std::string name,desc,format,coin; std::vector<CTxOut> vouts; CPubKey regpk;
|
||||
std::vector<CPubKey> pubkeys,tmppubkeys,tmppublishers; char markeraddr[64],deposit[64],destaddr[64],tmpdest[64]; int64_t datafee;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
|
||||
// ASSETCHAINS_SELFIMPORT is coin
|
||||
if (HUSH_EARLYTXID!=zeroid && bindtxid!=HUSH_EARLYTXID)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid import gateway. On this chain only valid import gateway is " << HUSH_EARLYTXID.GetHex() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
// check for valid burn from external coin blockchain and if valid return(0);
|
||||
if (myGetTransaction(bindtxid, bindtx, hashBlock) == 0 || (numvouts = bindtx.vout.size()) <= 0)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport cant find bindtxid=" << bindtxid.GetHex() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (DecodeImportGatewayBindOpRet(deposit,bindtx.vout[numvouts - 1].scriptPubKey,coin,oracletxid,M,N,tmppubkeys,taddr,prefix,prefix2,wiftype) != 'B')
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid bind tx. bindtxid=" << bindtxid.GetHex() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (refcoin!=coin)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid coin " << refcoin << "!=" << coin << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if ( N == 0 || N > 15 || M > N )
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid N or M " << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (tmppubkeys.size()!=N)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport not enough pubkeys for given N " << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (hush_txnotarizedconfirmed(bindtxid) == false)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport bindtx not yet confirmed/notarized" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (myGetTransaction(oracletxid, oracletx, hashBlock) == 0 || (numvouts = oracletx.vout.size()) <= 0)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport cant find oracletxid=" << oracletxid.GetHex() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey,name,desc,format) != 'C')
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (name!=refcoin || format!="Ihh")
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
CCtxidaddr(markeraddr,oracletxid);
|
||||
SetCCunspents(unspentOutputs,markeraddr,false);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
if ( myGetTransaction(txid,regtx,hashBlock) != 0 && regtx.vout.size() > 0
|
||||
&& DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid )
|
||||
{
|
||||
pubkeys.push_back(regpk);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (pubkeys.size()!=tmppubkeys.size())
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport different number of bind and oracle pubkeys " << tmppubkeys.size() << "!=" << pubkeys.size() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
merkleroot = zeroid;
|
||||
for (i = m = 0; i < n; i++)
|
||||
{
|
||||
if ((mhash = CCOraclesReverseScan("importgateway-1",txid, height, oracletxid, OraclesBatontxid(oracletxid, pubkeys[i]))) != zeroid)
|
||||
{
|
||||
if (merkleroot == zeroid)
|
||||
merkleroot = mhash, m = 1;
|
||||
else if (mhash == merkleroot)
|
||||
m ++;
|
||||
tmppublishers.push_back(pubkeys[i]);
|
||||
}
|
||||
}
|
||||
if (publishers.size()!=tmppublishers.size())
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport different number of publishers for burtx in oracle" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl );
|
||||
return(-1);
|
||||
}
|
||||
else if ( ImportGatewayVerify(deposit,oracletxid,burnvout,refcoin,burntxid,rawburntx,proof,merkleroot,destpub,taddr,prefix,prefix2) != amount )
|
||||
{
|
||||
CCerror = strprintf("burntxid didnt validate !");
|
||||
LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (importTx.vout[0].nValue!=amount)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport import amount different than in burntx" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
Getscriptaddress(destaddr,importTx.vout[0].scriptPubKey);
|
||||
Getscriptaddress(tmpdest,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG);
|
||||
if (strcmp(destaddr,tmpdest)!=0)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport import coins destination different than in burntx" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t CheckPUBKEYimport(TxProof proof,std::vector<uint8_t> rawproof,CTransaction burnTx,std::vector<CTxOut> payouts)
|
||||
{
|
||||
// if burnTx has ASSETCHAINS_PUBKEY vin, it is valid return(0);
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "proof txid=" << proof.first.GetHex() << std::endl);
|
||||
|
||||
uint256 sourcetxid = proof.first, hashBlock;
|
||||
CTransaction sourcetx;
|
||||
|
||||
if (!myGetTransaction(sourcetxid, sourcetx, hashBlock)) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "could not load source txid=" << sourcetxid.GetHex() << std::endl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sourcetx.vout.size() == 0) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "no vouts in source txid=" << sourcetxid.GetHex() << std::endl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// might be malleable:
|
||||
if (burnTx.nExpiryHeight != sourcetx.nExpiryHeight) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "burntx nExpiryHeight incorrect for source txid=" << sourcetxid.GetHex() << std::endl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//ac_pubkey check:
|
||||
if (!CheckVinPubKey(sourcetx, 0, ASSETCHAINS_OVERRIDE_PUBKEY33)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get source tx opret:
|
||||
std::vector<uint8_t> vopret;
|
||||
uint8_t evalCode, funcId;
|
||||
int64_t amount;
|
||||
|
||||
if (!GetOpReturnData(sourcetx.vout.back().scriptPubKey, vopret) ||
|
||||
vopret.size() == 0 ||
|
||||
!E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> amount) ||
|
||||
evalCode != EVAL_IMPORTCOIN || funcId != 'A') {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "none or incorrect opret to validate in source txid=" << sourcetxid.GetHex() << std::endl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "importTx amount=" << payouts[0].nValue << " burnTx amount=" << burnTx.vout[0].nValue << " opret amount=" << amount << " source txid=" << sourcetxid.GetHex() << std::endl);
|
||||
|
||||
// amount malleability check with the opret from the source tx:
|
||||
if (payouts[0].nValue != amount) { // assume that burntx amount is checked in the common code in Eval::ImportCoin()
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "importTx amount != amount in the opret of source txid=" << sourcetxid.GetHex() << std::endl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool CheckMigration(Eval *eval, const CTransaction &importTx, const CTransaction &burnTx, std::vector<CTxOut> & payouts, const ImportProof &proof, const std::vector<uint8_t> &rawproof)
|
||||
{
|
||||
vscript_t vimportOpret;
|
||||
if (!GetOpReturnData(importTx.vout.back().scriptPubKey, vimportOpret) ||
|
||||
vimportOpret.empty())
|
||||
return eval->Invalid("invalid-import-tx-no-opret");
|
||||
|
||||
|
||||
uint256 tokenid = zeroid;
|
||||
if (vimportOpret.begin()[0] == EVAL_TOKENS) { // for tokens (new opret with tokens)
|
||||
struct CCcontract_info *cpTokens, CCtokens_info;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
uint8_t evalCodeInOpret;
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
vscript_t vnonfungibleOpret;
|
||||
|
||||
cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS);
|
||||
|
||||
if (DecodeTokenOpRet(importTx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) == 0)
|
||||
return eval->Invalid("cannot-decode-import-tx-token-opret");
|
||||
|
||||
uint8_t nonfungibleEvalCode = EVAL_TOKENS; // init to no non-fungibles
|
||||
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vnonfungibleOpret);
|
||||
if (!vnonfungibleOpret.empty())
|
||||
nonfungibleEvalCode = vnonfungibleOpret.begin()[0];
|
||||
|
||||
// check if burn tx at least has cc evaltoken vins (we cannot get cc input)
|
||||
bool hasTokenVin = false;
|
||||
for (auto vin : burnTx.vin)
|
||||
if (cpTokens->ismyvin(vin.scriptSig))
|
||||
hasTokenVin = true;
|
||||
if (!hasTokenVin)
|
||||
return eval->Invalid("burn-tx-has-no-token-vins");
|
||||
|
||||
// calc outputs for burn tx
|
||||
CAmount ccBurnOutputs = 0;
|
||||
for (auto v : burnTx.vout)
|
||||
if (v.scriptPubKey.IsPayToCryptoCondition() &&
|
||||
CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(nonfungibleEvalCode, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY)))) // burned to dead pubkey
|
||||
ccBurnOutputs += v.nValue;
|
||||
|
||||
// calc outputs for import tx
|
||||
CAmount ccImportOutputs = 0;
|
||||
for (auto v : importTx.vout)
|
||||
if (v.scriptPubKey.IsPayToCryptoCondition() &&
|
||||
!IsTokenMarkerVout(v)) // should not be marker here
|
||||
ccImportOutputs += v.nValue;
|
||||
|
||||
if (ccBurnOutputs != ccImportOutputs)
|
||||
return eval->Invalid("token-cc-burned-output-not-equal-cc-imported-output");
|
||||
|
||||
}
|
||||
else if (vimportOpret.begin()[0] != EVAL_IMPORTCOIN) {
|
||||
return eval->Invalid("import-tx-incorrect-opret-eval");
|
||||
}
|
||||
|
||||
// for tokens check burn, import, tokenbase tx
|
||||
if (!tokenid.IsNull()) {
|
||||
|
||||
std::string sourceSymbol;
|
||||
CTransaction tokenbaseTx;
|
||||
if (!E_UNMARSHAL(rawproof, ss >> sourceSymbol; ss >> tokenbaseTx))
|
||||
return eval->Invalid("cannot-unmarshal-rawproof-for-tokens");
|
||||
|
||||
uint256 sourceTokenId;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
uint8_t evalCodeInOpret;
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
if (burnTx.vout.size() > 0 && DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCodeInOpret, sourceTokenId, voutTokenPubkeys, oprets) == 0)
|
||||
return eval->Invalid("cannot-decode-burn-tx-token-opret");
|
||||
|
||||
if (sourceTokenId != tokenbaseTx.GetHash()) // check tokenid in burn tx opret maches the passed tokenbase tx (to prevent cheating by importing user)
|
||||
return eval->Invalid("incorrect-token-creation-tx-passed");
|
||||
|
||||
std::vector<std::pair<uint8_t, vscript_t>> opretsSrc;
|
||||
vscript_t vorigpubkeySrc;
|
||||
std::string nameSrc, descSrc;
|
||||
if (DecodeTokenCreateOpRet(tokenbaseTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsSrc) == 0)
|
||||
return eval->Invalid("cannot-decode-token-creation-tx");
|
||||
|
||||
std::vector<std::pair<uint8_t, vscript_t>> opretsImport;
|
||||
vscript_t vorigpubkeyImport;
|
||||
std::string nameImport, descImport;
|
||||
if (importTx.vout.size() == 0 || DecodeTokenCreateOpRet(importTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsImport) == 0)
|
||||
return eval->Invalid("cannot-decode-token-import-tx");
|
||||
|
||||
// check that name,pubkey,description in import tx correspond ones in token creation tx in the source chain:
|
||||
if (vorigpubkeySrc != vorigpubkeyImport ||
|
||||
nameSrc != nameImport ||
|
||||
descSrc != descImport)
|
||||
return eval->Invalid("import-tx-token-params-incorrect");
|
||||
}
|
||||
|
||||
|
||||
// Check burntx shows correct outputs hash
|
||||
// if (payoutsHash != SerializeHash(payouts)) // done in ImportCoin
|
||||
// return eval->Invalid("wrong-payouts");
|
||||
|
||||
|
||||
TxProof merkleBranchProof;
|
||||
std::vector<uint256> notaryTxids;
|
||||
|
||||
// Check proof confirms existance of burnTx
|
||||
if (proof.IsMerkleBranch(merkleBranchProof)) {
|
||||
uint256 target = merkleBranchProof.second.Exec(burnTx.GetHash());
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "Eval::ImportCoin() momom target=" << target.GetHex() << " merkleBranchProof.first=" << merkleBranchProof.first.GetHex() << std::endl);
|
||||
if (!CheckMoMoM(merkleBranchProof.first, target)) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MoMoM check failed for importtx=" << importTx.GetHash().GetHex() << std::endl);
|
||||
return eval->Invalid("momom-check-fail");
|
||||
}
|
||||
}
|
||||
else if (proof.IsNotaryTxids(notaryTxids)) {
|
||||
if (!CheckNotariesApproval(burnTx.GetHash(), notaryTxids)) {
|
||||
return eval->Invalid("notaries-approval-check-fail");
|
||||
}
|
||||
}
|
||||
else {
|
||||
return eval->Invalid("invalid-import-proof");
|
||||
}
|
||||
|
||||
/* if (vimportOpret.begin()[0] == EVAL_TOKENS)
|
||||
return eval->Invalid("test-invalid-tokens-are-good!!");
|
||||
else
|
||||
return eval->Invalid("test-invalid-coins-are-good!!"); */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
||||
{
|
||||
ImportProof proof;
|
||||
CTransaction burnTx;
|
||||
std::vector<CTxOut> payouts;
|
||||
CAmount txfee = 10000, amount;
|
||||
int32_t height, burnvout;
|
||||
std::vector<CPubKey> publishers;
|
||||
uint32_t targetCcid;
|
||||
std::string targetSymbol, srcaddr, destaddr, receipt, rawburntx;
|
||||
uint256 payoutsHash, bindtxid, burntxid;
|
||||
std::vector<uint8_t> rawproof;
|
||||
std::vector<uint256> txids;
|
||||
CPubKey destpub;
|
||||
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "Validating import tx..., txid=" << importTx.GetHash().GetHex() << std::endl);
|
||||
|
||||
if (importTx.vout.size() < 2)
|
||||
return Invalid("too-few-vouts");
|
||||
// params
|
||||
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
|
||||
return Invalid("invalid-params");
|
||||
// Control all aspects of this transaction
|
||||
// It should not be at all malleable
|
||||
if (ASSETCHAINS_SELFIMPORT!="PEGSCC" && MakeImportCoinTransaction(proof, burnTx, payouts, importTx.nExpiryHeight).GetHash() != importTx.GetHash()) // ExistsImportTombstone prevents from duplication
|
||||
return Invalid("non-canonical");
|
||||
// burn params
|
||||
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash, rawproof))
|
||||
return Invalid("invalid-burn-tx");
|
||||
|
||||
if (burnTx.vout.size() == 0)
|
||||
return Invalid("invalid-burn-tx-no-vouts");
|
||||
|
||||
// check burned normal amount >= import amount && burned amount <= import amount + txfee (extra txfee is for miners and relaying, see GetImportCoinValue() func)
|
||||
CAmount burnAmount = burnTx.vout.back().nValue;
|
||||
if (burnAmount == 0)
|
||||
return Invalid("invalid-burn-amount");
|
||||
CAmount totalOut = 0;
|
||||
for (auto v : importTx.vout)
|
||||
if (!v.scriptPubKey.IsPayToCryptoCondition())
|
||||
totalOut += v.nValue;
|
||||
if (totalOut > burnAmount || totalOut < burnAmount - txfee)
|
||||
return Invalid("payout-too-high-or-too-low");
|
||||
|
||||
// Check burntx shows correct outputs hash
|
||||
if (payoutsHash != SerializeHash(payouts))
|
||||
return Invalid("wrong-payouts");
|
||||
if (targetCcid < HUSH_FIRSTFUNGIBLEID)
|
||||
return Invalid("chain-not-fungible");
|
||||
|
||||
if ( targetCcid != 0xffffffff )
|
||||
{
|
||||
|
||||
if (targetCcid != GetAssetchainsCC() || targetSymbol != GetAssetchainsSymbol())
|
||||
return Invalid("importcoin-wrong-chain");
|
||||
|
||||
if (!CheckMigration(this, importTx, burnTx, payouts, proof, rawproof))
|
||||
return false; // eval->Invalid() is called in the func
|
||||
}
|
||||
else
|
||||
{
|
||||
TxProof merkleBranchProof;
|
||||
if (!proof.IsMerkleBranch(merkleBranchProof))
|
||||
return Invalid("invalid-import-proof-for-0xFFFFFFFF");
|
||||
|
||||
if ( targetSymbol == "BEAM" )
|
||||
{
|
||||
if ( ASSETCHAINS_BEAMPORT == 0 )
|
||||
return Invalid("BEAM-import-without-port");
|
||||
else if ( CheckBEAMimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 )
|
||||
return Invalid("BEAM-import-failure");
|
||||
}
|
||||
else if ( targetSymbol == "CODA" )
|
||||
{
|
||||
if ( ASSETCHAINS_CODAPORT == 0 )
|
||||
return Invalid("CODA-import-without-port");
|
||||
else if ( UnmarshalBurnTx(burnTx,srcaddr,receipt)==0 || CheckCODAimport(importTx,burnTx,payouts,srcaddr,receipt) < 0 )
|
||||
return Invalid("CODA-import-failure");
|
||||
}
|
||||
else if ( targetSymbol == "PEGSCC" )
|
||||
{
|
||||
if ( ASSETCHAINS_SELFIMPORT != "PEGSCC" )
|
||||
return Invalid("PEGSCC-import-when-not PEGSCC");
|
||||
// else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 )
|
||||
// return Invalid("PEGSCC-import-failure");
|
||||
}
|
||||
else if ( targetSymbol == "PUBKEY" )
|
||||
{
|
||||
if ( ASSETCHAINS_SELFIMPORT != "PUBKEY" )
|
||||
return Invalid("PUBKEY-import-when-not PUBKEY");
|
||||
else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 )
|
||||
return Invalid("PUBKEY-import-failure");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( targetSymbol != ASSETCHAINS_SELFIMPORT )
|
||||
return Invalid("invalid-gateway-import-coin");
|
||||
else if ( UnmarshalBurnTx(burnTx,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount)==0 || CheckGATEWAYimport(importTx,burnTx,targetSymbol,rawproof,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount) < 0 )
|
||||
return Invalid("GATEWAY-import-failure");
|
||||
}
|
||||
}
|
||||
|
||||
// return Invalid("test-invalid");
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "Valid import tx! txid=" << importTx.GetHash().GetHex() << std::endl);
|
||||
|
||||
return Valid();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,94 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "chain.h"
|
||||
#include "streams.h"
|
||||
#include "cc/eval.h"
|
||||
#include "cc/betprotocol.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
|
||||
/*
|
||||
* Crypto-Condition EVAL method that verifies a payout against a transaction
|
||||
* notarized on another chain.
|
||||
*
|
||||
* IN: params - condition params
|
||||
* IN: importTx - Payout transaction on value chain (HUSH)
|
||||
* IN: nIn - index of input of stake
|
||||
*
|
||||
* importTx: Spends stakeTx with payouts from asset chain
|
||||
*
|
||||
* in 0: Spends Stake TX and contains ImportPayout CC
|
||||
* out 0: OP_RETURN MomProof, disputeTx
|
||||
* out 1-: arbitrary payouts
|
||||
*
|
||||
* disputeTx: Spends sessionTx.0 (opener on asset chain)
|
||||
*
|
||||
* in 0: spends sessionTx.0
|
||||
* in 1-: anything
|
||||
* out 0: OP_RETURN hash of payouts
|
||||
* out 1-: anything
|
||||
*/
|
||||
bool Eval::ImportPayout(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
||||
{
|
||||
if (importTx.vout.size() == 0) return Invalid("no-vouts");
|
||||
|
||||
// load data from vout[0]
|
||||
MoMProof proof;
|
||||
CTransaction disputeTx;
|
||||
{
|
||||
std::vector<unsigned char> vopret;
|
||||
GetOpReturnData(importTx.vout[0].scriptPubKey, vopret);
|
||||
if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx))
|
||||
return Invalid("invalid-payload");
|
||||
}
|
||||
|
||||
// Check disputeTx.0 shows correct payouts
|
||||
{
|
||||
uint256 givenPayoutsHash;
|
||||
GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash);
|
||||
std::vector<CTxOut> payouts(importTx.vout.begin() + 1, importTx.vout.end());
|
||||
if (givenPayoutsHash != SerializeHash(payouts))
|
||||
return Invalid("wrong-payouts");
|
||||
}
|
||||
|
||||
// Check disputeTx spends sessionTx.0
|
||||
// condition ImportPayout params is session ID from other chain
|
||||
{
|
||||
uint256 sessionHash;
|
||||
if (!E_UNMARSHAL(params, ss >> sessionHash))
|
||||
return Invalid("malformed-params");
|
||||
if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0))
|
||||
return Invalid("wrong-session");
|
||||
}
|
||||
|
||||
// Check disputeTx solves momproof from vout[0]
|
||||
{
|
||||
NotarizationData data;
|
||||
if (!GetNotarizationData(proof.notarizationHash, data))
|
||||
return Invalid("coudnt-load-mom");
|
||||
|
||||
if (data.MoM != proof.Exec(disputeTx.GetHash()))
|
||||
return Invalid("mom-check-fail");
|
||||
}
|
||||
|
||||
return Valid();
|
||||
}
|
||||
344
src/cc/lotto.cpp
344
src/cc/lotto.cpp
@@ -1,344 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "CClotto.h"
|
||||
#include "../txmempool.h"
|
||||
|
||||
/*
|
||||
A blockchain lotto has the problem of generating the deterministic random numbers needed to get a winner in a way that doesnt allow cheating. If we save the entropy for later publishing and display the hash of the entropy, it is true that the players wont know what the entropy value is, however the creator of the lotto funds will be able to know and simply create a winning ticket when the jackpot is large enough.
|
||||
|
||||
We also need to avoid chain reorgs from disclosing the entropy and then allowing people to submit a winning ticket calculated based on the disclosed entropy (see attack vector in dice.cpp)
|
||||
|
||||
As usual it needs to be provably fair and random
|
||||
|
||||
The solution is for everybody to post the hash of their entropy when purchasing tickets. Then at the time of the drawing, nodes would post their entropy over an N block period to avoid censorship attack. After the N block period, then we have valid entropy that we know was locked in prior to the start of the N blocks and that nobody would have been able to know ahead of time the final entropy value.
|
||||
|
||||
As long as one node submits a high entropy value, then just by combining all the submissions together, we get the drawing's entropy value. Given that, the usual process can determine if there was a winner at the specified odds. In fact, all the nodes are able to determine exactly how many winners there were and whether to validate 1/w payouts to the w winners or rollover the jackpot to the next drawing.
|
||||
|
||||
To remove the need for an autopayout, the winning node(s) would need to submit a 1/w payout tx, this would be able to be done at any time and the winner does not have to have submitted proof of hentropy. In order to prevent a player from opportunistically withholding their entropy, the lotto creator will post the original proof of hentropy after the N block player submission period. This masks to all the players the final value of entropy.
|
||||
|
||||
Attack vector: the lotto creator can have many player tickets in reserve all with their entropy ready to submit, but based on the actual submissions, find the one which gives him the best outcome. since all the player submissions will be known via mempool, along with the original hentropy. However the lotto creator would have to mine the final block in order to know the order of the player tickets.
|
||||
|
||||
Thinking about this evil miner attack, it seems pretty bad, so a totally new approach is needed. Preferably with a simple enough protocol. Let us remove any special knowledge by the lotto creator, so like the faucet, it seems just that there is a single lotto for a chain.
|
||||
|
||||
>>>>>>>>>>>> second iteration
|
||||
|
||||
What we need is something that gives each ticket an equal chance at the jackpot, without allowing miner or relayer to gain an advantage. ultimately the jackpot payout tx needs to be confirmed, so there needs to be some number of blocks to make a claim to avoid censorship attack. If onchain entropy is needed, then it should be reduced to 1 bit per block to reduce the grinding that is possible. This does mean a block miner for the last bit of entropy can double their chances at winning, but the alternative is to have an external source of entropy, which creates its own set of issues like what prevents the nodes getting the external entropy from cheating?
|
||||
|
||||
Conveniently the lotto mechanics are similar to a PoS staking, so it can be based on everybody trying to stake a single lotto jackpot.
|
||||
|
||||
The calculation would need to be based on the payout address and utxosize, so relayers cant intercept it to steal the jackpot.
|
||||
|
||||
each jackpot would effectively restart the lotto
|
||||
|
||||
the funds from new lotto tickets can be spent by the jackpot, but those tickets can still win the new jackpot
|
||||
|
||||
each set of tickets (utxo) would become eligible to claim the jackpot after some time is elapsed so the entropy for that utxo can be obtained. [6 bits * 32 + 1 bit * 16] 48 blocks
|
||||
|
||||
It is possible to have a jackpot but miss out on it due to not claiming it. To minimize the effect from this, each ticket would have one chance to win, which can be calculated and a jackpot claim submitted just once.
|
||||
|
||||
in order to randomize the timing of claim, a txid PoW similar to faucetget will maximize the chance of only a single jackpot txid that can propagate throughout the mempools, which will prevent the second one broadcast. Granted the mining node can override this if they also have a winning ticket, but assuming the PoS lottery makes it unlikely for two winners in a single block, this is not a big issue.
|
||||
|
||||
In order to adapt the difficulty of winning the lotto, but not requiring recalculating all past tickets, as new lotto tickets are sold without a jackpot, it needs to become easier to win. Basically as the lotto jackpot gets bigger and bigger, it keeps getting easier to win! This convergence will avoid having unwinnable jackpots.
|
||||
|
||||
rpc calls
|
||||
lottoinfo
|
||||
lottotickets <numtickets>
|
||||
lottostatus
|
||||
lottowinner tickethash ticketid
|
||||
|
||||
*/
|
||||
|
||||
// start of consensus code
|
||||
|
||||
int64_t IsLottovout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
|
||||
{
|
||||
char destaddr[64];
|
||||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
{
|
||||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
|
||||
return(tx.vout[v].nValue);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool LottoExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
|
||||
{
|
||||
static uint256 zerohash;
|
||||
CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
//fprintf(stderr,"vini.%d\n",i);
|
||||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
|
||||
{
|
||||
//fprintf(stderr,"vini.%d check mempool\n",i);
|
||||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
|
||||
return eval->Invalid("cant find vinTx");
|
||||
else
|
||||
{
|
||||
//fprintf(stderr,"vini.%d check hash and vout\n",i);
|
||||
if ( hashBlock == zerohash )
|
||||
return eval->Invalid("cant Lotto from mempool");
|
||||
if ( (assetoshis= IsLottovout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
|
||||
inputs += assetoshis;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i<numvouts; i++)
|
||||
{
|
||||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
||||
if ( (assetoshis= IsLottovout(cp,tx,i)) != 0 )
|
||||
outputs += assetoshis;
|
||||
}
|
||||
if ( inputs != outputs+txfee )
|
||||
{
|
||||
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
|
||||
return eval->Invalid("mismatched inputs != outputs + txfee");
|
||||
}
|
||||
else return(true);
|
||||
}
|
||||
|
||||
bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
|
||||
{
|
||||
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval;
|
||||
return eval->Invalid("no validation yet");
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
preventCCvins = preventCCvouts = -1;
|
||||
if ( numvouts < 1 )
|
||||
return eval->Invalid("no vouts");
|
||||
else
|
||||
{
|
||||
//fprintf(stderr,"check vins\n");
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
|
||||
{
|
||||
fprintf(stderr,"Lottoget invalid vini\n");
|
||||
return eval->Invalid("illegal normal vini");
|
||||
}
|
||||
}
|
||||
//fprintf(stderr,"check amounts\n");
|
||||
if ( LottoExactAmounts(cp,eval,tx,1,10000) == false )
|
||||
{
|
||||
fprintf(stderr,"Lottoget invalid amount\n");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
preventCCvouts = 1;
|
||||
if ( IsLottovout(cp,tx,0) != 0 )
|
||||
{
|
||||
preventCCvouts++;
|
||||
i = 1;
|
||||
} else i = 0;
|
||||
if ( tx.vout[i].nValue != COIN )
|
||||
return eval->Invalid("invalid Lotto output");
|
||||
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
|
||||
if ( retval != 0 )
|
||||
fprintf(stderr,"Lottoget validated\n");
|
||||
else fprintf(stderr,"Lottoget invalid\n");
|
||||
return(retval);
|
||||
}
|
||||
}
|
||||
}
|
||||
// end of consensus code
|
||||
|
||||
// helper functions for rpc calls in rpcwallet.cpp
|
||||
|
||||
int64_t AddLottoInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
|
||||
{
|
||||
// add threshold check
|
||||
char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t n = 0;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
GetCCaddress(cp,coinaddr,pk);
|
||||
SetCCunspents(unspentOutputs,coinaddr,true);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
// prevent dup
|
||||
if ( it->second.satoshis < COIN )
|
||||
continue;
|
||||
if ( myGetTransaction(txid,vintx,hashBlock) != 0 )
|
||||
{
|
||||
if ( (nValue= IsLottovout(cp,vintx,(int32_t)it->first.index)) > 0 )
|
||||
{
|
||||
if ( total != 0 && maxinputs != 0 )
|
||||
mtx.vin.push_back(CTxIn(txid,(int32_t)it->first.index,CScript()));
|
||||
nValue = it->second.satoshis;
|
||||
totalinputs += nValue;
|
||||
n++;
|
||||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(totalinputs);
|
||||
}
|
||||
|
||||
uint8_t DecodeLottoFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int32_t ticketsize,int32_t odds,int32_t firstheight,int32_t period,uint256 hentropy)
|
||||
{
|
||||
std::vector<uint8_t> vopret; uint8_t *script,e,f;
|
||||
GetOpReturnData(scriptPubKey, vopret);
|
||||
script = (uint8_t *)vopret.data();
|
||||
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> ticketsize; ss >> odds; ss >> firstheight; ss >> period; ss >> hentropy) != 0 )
|
||||
{
|
||||
if ( e == EVAL_LOTTO && f == 'F' )
|
||||
return(f);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int64_t LottoPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
|
||||
{
|
||||
char coinaddr[64]; uint64_t sbits; int64_t nValue,lockedfunds; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
lockedfunds = 0;
|
||||
GetCCaddress(cp,coinaddr,pk);
|
||||
SetCCunspents(unspentOutputs,coinaddr,true);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
vout = (int32_t)it->first.index;
|
||||
if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
{
|
||||
// need to implement this! if ( (funcid= DecodeLottoOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'F' || funcid == 'T' )
|
||||
{
|
||||
if ( refsbits == sbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
|
||||
{
|
||||
if ( (nValue= IsLottovout(cp,tx,vout)) > 0 )
|
||||
lockedfunds += nValue;
|
||||
else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN);
|
||||
} //else fprintf(stderr,"else case\n");
|
||||
} //else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
|
||||
}
|
||||
}
|
||||
return(lockedfunds);
|
||||
}
|
||||
|
||||
UniValue LottoInfo(uint256 lottoid)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ); uint256 hashBlock,hentropy; CTransaction vintx; uint64_t lockedfunds,sbits; int32_t ticketsize,odds,firstheight,period; CPubKey lottopk; struct CCcontract_info *cp,C; char str[67],numstr[65];
|
||||
if ( myGetTransaction(lottoid,vintx,hashBlock) == 0 )
|
||||
{
|
||||
fprintf(stderr,"cant find lottoid\n");
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","cant find lottoid"));
|
||||
return(result);
|
||||
}
|
||||
if ( vintx.vout.size() > 0 && DecodeLottoFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,ticketsize,odds,firstheight,period,hentropy) == 0 )
|
||||
{
|
||||
fprintf(stderr,"lottoid isnt lotto creation txid\n");
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","lottoid isnt lotto creation txid"));
|
||||
return(result);
|
||||
}
|
||||
result.push_back(Pair("result","success"));
|
||||
result.push_back(Pair("lottoid",uint256_str(str,lottoid)));
|
||||
unstringbits(str,sbits);
|
||||
result.push_back(Pair("name",str));
|
||||
result.push_back(Pair("sbits",sbits));
|
||||
result.push_back(Pair("ticketsize",ticketsize));
|
||||
result.push_back(Pair("odds",odds));
|
||||
cp = CCinit(&C,EVAL_LOTTO);
|
||||
lottopk = GetUnspendable(cp,0);
|
||||
lockedfunds = LottoPlanFunds(sbits,cp,lottopk,lottoid);
|
||||
sprintf(numstr,"%.8f",(double)lockedfunds/COIN);
|
||||
result.push_back(Pair("jackpot",numstr));
|
||||
return(result);
|
||||
}
|
||||
|
||||
UniValue LottoList()
|
||||
{
|
||||
UniValue result(UniValue::VARR); std::vector<uint256> txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,hentropy; CTransaction vintx; uint64_t sbits; int32_t ticketsize,odds,firstheight,period; char str[65];
|
||||
cp = CCinit(&C,EVAL_LOTTO);
|
||||
SetCCtxids(txids,cp->normaladdr,true,cp->evalcode,zeroid,'F');
|
||||
for (std::vector<uint256>::const_iterator it=txids.begin(); it!=txids.end(); it++)
|
||||
{
|
||||
txid = *it;
|
||||
if ( myGetTransaction(txid,vintx,hashBlock) != 0 )
|
||||
{
|
||||
if ( vintx.vout.size() > 0 && DecodeLottoFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,ticketsize,odds,firstheight,period,hentropy) == 'F' )
|
||||
{
|
||||
result.push_back(uint256_str(str,txid));
|
||||
}
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
std::string LottoCreate(uint64_t txfee,char *planstr,int64_t funding,int32_t ticketsize,int32_t odds,int32_t firstheight,int32_t period)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
uint256 entropy,hentropy; CPubKey mypk,lottopk; uint64_t sbits; int64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C;
|
||||
cp = CCinit(&C,EVAL_LOTTO);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
lottopk = GetUnspendable(cp,0);
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
sbits = stringbits(planstr);
|
||||
if ( AddNormalinputs(mtx,mypk,funding+txfee,60) > 0 )
|
||||
{
|
||||
hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1);
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,funding,lottopk));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_LOTTO << (uint8_t)'F' << sbits << ticketsize << odds << firstheight << period << hentropy)));
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string LottoTicket(uint64_t txfee,uint256 lottoid,int64_t numtickets)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,lottopk; CScript opret; int64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C;
|
||||
cp = CCinit(&C,EVAL_LOTTO);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
lottopk = GetUnspendable(cp,0);
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
if ( (inputs= AddLottoInputs(cp,mtx,lottopk,nValue+txfee,60)) > 0 )
|
||||
{
|
||||
if ( inputs > nValue )
|
||||
CCchange = (inputs - nValue - txfee);
|
||||
if ( CCchange != 0 )
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,CCchange,lottopk));
|
||||
mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,opret));
|
||||
} else fprintf(stderr,"cant find Lotto inputs\n");
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string LottoWinner(uint64_t txfee)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,lottopk; int64_t winnings = 0; CScript opret; struct CCcontract_info *cp,C;
|
||||
cp = CCinit(&C,EVAL_LOTTO);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
lottopk = GetUnspendable(cp,0);
|
||||
if ( AddNormalinputs(mtx,mypk,txfee,64) > 0 )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,winnings,lottopk));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret));
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp
|
||||
cp gamescc.so ../libcc.so
|
||||
cd ..
|
||||
make
|
||||
cd cc
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
echo pricescc.so
|
||||
gcc -O3 -DBUILD_GAMESCC -DBUILD_PRICES -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o pricescc.so cclib.cpp
|
||||
echo prices
|
||||
cd games
|
||||
gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE -DBUILD_PRICES ../gamescc.cpp -lncurses -lcurl -o prices
|
||||
cd ..
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Copyright 2016-2023 The Hush developers
|
||||
cd rogue;
|
||||
make clean;
|
||||
|
||||
if [ "$HOST" = "x86_64-w64-mingw32" ]; then
|
||||
echo "Not supported"
|
||||
exit 1
|
||||
./configure --host=x86_64-w64-mingw32
|
||||
echo $PWD
|
||||
# wget ...
|
||||
tar xvfz x86_64-w64-mingw32.tar.gz && rm x86_64-w64-mingw32.tar.gz
|
||||
echo lib archive cleaned
|
||||
echo $PWD
|
||||
if make -f Makefile_win "$@"; then
|
||||
echo rogue.exe build SUCCESSFUL
|
||||
cd ..
|
||||
else
|
||||
echo rogue.exe build FAILED
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo building rogue...
|
||||
./configure
|
||||
if make "$@"; then
|
||||
echo rogue build SUCCESSFUL
|
||||
cd ..
|
||||
else
|
||||
echo rogue build FAILED
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if make -f Makefile_rogue "$@"; then
|
||||
echo ROGUE BUILD SUCCESSFUL
|
||||
else
|
||||
echo ROGUE BUILD FAILED
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,7 +0,0 @@
|
||||
echo gamescc.so with tetris
|
||||
gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp
|
||||
echo tetris dapp
|
||||
cd games
|
||||
gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE ../gamescc.cpp -lncurses -lcurl -o tetris
|
||||
cd ..
|
||||
|
||||
873
src/cc/musig.cpp
873
src/cc/musig.cpp
@@ -1,873 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
/* first make a combined pk:
|
||||
|
||||
./hush-cli -ac_name=MUSIG cclib combine 18 '["02fb6aa0b96cad24d46b5da93eba3864c45ce07a73bba12da530ae841e140fcf28","0255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4"]'
|
||||
{
|
||||
"pkhash": "5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b",
|
||||
"combined_pk": "03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b",
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
the combined_pk and pkhash will be needed for various other rpc calls
|
||||
|
||||
second, send 1 coin to the combined_pk
|
||||
./hush-cli -ac_name=MUSIG cclib send 18 '["03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b",1]'
|
||||
{
|
||||
"hex": "0400008085202f8901a980664dffc810725a79ffb89ac48be4c7b6bade9b789732fcf871acf8e81a2e010000006a47304402207e52763661ecd2c34a65d6623950be11794825db71576dc11894c606ddc317800220028fef46dc20630d0fdf22647b5d4ff0f1c47cf75f48702d0a91d5589eff99d001210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ffffffff031008f60500000000302ea22c8020c71ddb3aac7f9b9e4bdacf032aaa8b8e4433c4ff9f8a43cebb9c1f5da96928a48103120c008203000401cce09aa4350000000023210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac0000000000000000266a2412782103f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b00000000920500000000000000000000000000",
|
||||
"txid": "5ce74037a153ee210413b48d4e88638b99825a2de1a1f1aa0d36ebf93019824c",
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
sendrawtransaction of the above hex.
|
||||
./hush-cli -ac_name=MUSIG getrawtransaction 5ce74037a153ee210413b48d4e88638b99825a2de1a1f1aa0d36ebf93019824c 1
|
||||
"vout": [
|
||||
{
|
||||
"value": 1.00010000,
|
||||
"valueSat": 100010000,
|
||||
"n": 0,
|
||||
"scriptPubKey": {
|
||||
"asm": "a22c8020c71ddb3aac7f9b9e4bdacf032aaa8b8e4433c4ff9f8a43cebb9c1f5da96928a48103120c008203000401 OP_CHECKCRYPTOCONDITION",
|
||||
"hex": "2ea22c8020c71ddb3aac7f9b9e4bdacf032aaa8b8e4433c4ff9f8a43cebb9c1f5da96928a48103120c008203000401cc",
|
||||
"reqSigs": 1,
|
||||
"type": "cryptocondition",
|
||||
"addresses": [
|
||||
"RKWS7jxyjPX9iaJttk8iMKf1AumanKypez"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value": 8.99980000,
|
||||
"valueSat": 899980000,
|
||||
"n": 1,
|
||||
"scriptPubKey": {
|
||||
"asm": "0255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4 OP_CHECKSIG",
|
||||
"hex": "210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac",
|
||||
"reqSigs": 1,
|
||||
"type": "pubkey",
|
||||
"addresses": [
|
||||
"RVQjvGdRbYLJ49bfH4SAFseipvwE3UdoDw"
|
||||
]
|
||||
}
|
||||
|
||||
script: 210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac
|
||||
|
||||
sendtxid: 5ce74037a153ee210413b48d4e88638b99825a2de1a1f1aa0d36ebf93019824c
|
||||
|
||||
get the msg we need to sign:
|
||||
|
||||
./hush-cli -ac_name=MUSIG cclib calcmsg 18 '["5ce74037a153ee210413b48d4e88638b99825a2de1a1f1aa0d36ebf93019824c","210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac"]'
|
||||
|
||||
{
|
||||
"msg": "f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75",
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
the "msg" is what needs to be signed to create a valid spend
|
||||
|
||||
now on each signing node, a session needs to be created:
|
||||
5 args: ind, numsigners, combined_pk, pkhash, message to be signed
|
||||
|
||||
on node with pubkey: 02fb6aa0b96cad24d46b5da93eba3864c45ce07a73bba12da530ae841e140fcf28
|
||||
./hush-cli -ac_name=MUSIG cclib session 18 '[0,2,"03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b","5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b","f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75"]'
|
||||
{
|
||||
"myind": 0,
|
||||
"numsigners": 2,
|
||||
"commitment": "bbea1f2562eca01b9a1393c5dc188bdd44551aebf684f4459930f59dde01f7ae",
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
on node with pubkey: 0255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4
|
||||
./hush-cli -ac_name=MUSIG cclib session 18 '[1,2,"03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b","5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b","f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75"]'
|
||||
{
|
||||
"myind": 1,
|
||||
"numsigners": 2,
|
||||
"commitment": "c2291acb747a75b1a40014d8eb0cc90a1360f74d413f65f78e20a7de45eda851",
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
now we need to get the commitment from each node to the other one. the session already put the commitment for each node into the global struct. Keep in mind there is a single global struct with session unique to each cclib session call. that means no restarting any deamon in the middle of the process on any of the nodes and only call cclib session a single time. this is an artificial restriction just to simplify the initial implementation of musig
|
||||
./hush-cli -ac_name=MUSIG cclib commit 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b","1","c2291acb747a75b1a40014d8eb0cc90a1360f74d413f65f78e20a7de45eda851"]'
|
||||
{
|
||||
"added_index": 1,
|
||||
"myind": 0,
|
||||
"nonce": "02fec7a9310c959a0a97b86bc3f8c30d392d1fb51793915898c568f73f1f70476b",
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
./hush-cli -ac_name=MUSIG cclib commit 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b",0,"d242cff13fa8c9b83248e4219fda459ada146b885f2171481f1b0f66c66d94ad"]'
|
||||
{
|
||||
"added_index": 0,
|
||||
"myind": 1,
|
||||
"nonce": "039365deaaaea089d509ba4c9f846de2baf4aa04cf6b26fa2c1cd818553e47f80c",
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
Now exchange the revealed nonces to each node:
|
||||
./hush-cli -ac_name=MUSIG cclib nonce 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b","1","039365deaaaea089d509ba4c9f846de2baf4aa04cf6b26fa2c1cd818553e47f80c"]'
|
||||
{
|
||||
"added_index": 1,
|
||||
"myind": 0,
|
||||
"partialsig": "1d65c09cd9bffe4f0604227e66cd7cd221480bbb08262fe885563a9df7cf8f5b",
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
./hush-cli -ac_name=MUSIG cclib nonce 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b",0,"02fec7a9310c959a0a97b86bc3f8c30d392d1fb51793915898c568f73f1f70476b"]'
|
||||
{
|
||||
"added_index": 0,
|
||||
"myind": 1,
|
||||
"partialsig": "4a3795e6801b355102c617390cf5a462061e082e35dc2ed8f8b1fab54cc0769e",
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
Almost there! final step is to exchange the partial sigs between signers
|
||||
./hush-cli -ac_name=MUSIG cclib partialsig 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b","1","4a3795e6801b355102c617390cf5a462061e082e35dc2ed8f8b1fab54cc0769e"]'
|
||||
{
|
||||
"added_index": 1,
|
||||
"result": "success",
|
||||
"combinedsig": "a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9"
|
||||
}
|
||||
|
||||
./hush-cli -ac_name=MUSIG cclib partialsig 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b",0,"1d65c09cd9bffe4f0604227e66cd7cd221480bbb08262fe885563a9df7cf8f5b"]'
|
||||
{
|
||||
"added_index": 0,
|
||||
"result": "success",
|
||||
"combinedsig": "a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9"
|
||||
}
|
||||
|
||||
Notice both nodes generated the same combined signature!
|
||||
|
||||
Now for a sanity test, we can use the verify call to make sure this sig will work with the msg needed for the spend:
|
||||
|
||||
./hush-cli -ac_name=MUSIG cclib verify 18 '["f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75","03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b","a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9"]'
|
||||
{
|
||||
"msg": "f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75",
|
||||
"combined_pk": "03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b",
|
||||
"combinedsig": "a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9",
|
||||
"result": "success"
|
||||
}
|
||||
|
||||
and finally the spend: sendtxid, scriptPubKey, musig
|
||||
|
||||
./hush-cli -ac_name=MUSIG cclib spend 18 '["5ce74037a153ee210413b48d4e88638b99825a2de1a1f1aa0d36ebf93019824c","210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac","a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9"]'
|
||||
{
|
||||
"scriptpubkey": "210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac",
|
||||
"msg": "f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75",
|
||||
"combined_pk": "03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b",
|
||||
"combinedsig": "a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9",
|
||||
"hex": "0400008085202f89014c821930f9eb360daaf1a1e12d5a82998b63884e8db4130421ee53a13740e75c000000007b4c79a276a072a26ba067a5658021032d29d6545a2aafad795d9cf50912ecade549137
|
||||
163934dfb2895ebc0e211ce8a81409671a60db89b3bc58966f3acc80194479b1a43d868e95a11ebc5609646d18710341a8ff92a7817571980307f5d660cc00a2735ac6333e0a7191243f1263f1959a100af03800112
|
||||
a10001ffffffff0200e1f5050000000023210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac0000000000000000686a4c6512792103f016c348437c7422eed92d865aa9789614f
|
||||
75327cada463eefc566126b54785b40a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f900000000a805
|
||||
00000000000000000000000000",
|
||||
"txid": "910635bf69a047fc90567a83ff12e47b753f470658b6d0855ec96e07e7349a8a",
|
||||
"result": "success"
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
#define USE_BASIC_CONFIG
|
||||
#define ENABLE_MODULE_MUSIG
|
||||
#include "../secp256k1/src/basic-config.h"
|
||||
#include "../secp256k1/include/secp256k1.h"
|
||||
#include "../secp256k1/src/ecmult.h"
|
||||
#include "../secp256k1/src/ecmult_gen.h"
|
||||
|
||||
typedef struct { unsigned char data[64]; } secp256k1_schnorrsig;
|
||||
struct secp256k1_context_struct {
|
||||
secp256k1_ecmult_context ecmult_ctx;
|
||||
secp256k1_ecmult_gen_context ecmult_gen_ctx;
|
||||
secp256k1_callback illegal_callback;
|
||||
secp256k1_callback error_callback;
|
||||
};
|
||||
|
||||
|
||||
//#include "../secp256k1/include/secp256k1.h"
|
||||
//#include "../secp256k1/include/secp256k1_schnorrsig.h"
|
||||
#include "../secp256k1/include/secp256k1_musig.h"
|
||||
|
||||
|
||||
extern "C" int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
|
||||
extern "C" int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *msg32, const secp256k1_pubkey *pk);
|
||||
extern "C" int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsig* sig, const unsigned char *in64);
|
||||
extern "C" int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_pubkey *combined_pk, unsigned char *pk_hash32, const secp256k1_pubkey *pubkeys, size_t n_pubkeys);
|
||||
extern "C" int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, size_t n_signers, size_t my_index, const unsigned char *seckey);
|
||||
extern "C" int secp256k1_schnorrsig_serialize(const secp256k1_context* ctx, unsigned char *out64, const secp256k1_schnorrsig* sig);
|
||||
|
||||
#define MUSIG_PREVN 0 // for now, just use vout0 for the musig output
|
||||
#define MUSIG_TXFEE 10000
|
||||
|
||||
struct musig_info
|
||||
{
|
||||
secp256k1_musig_session session;
|
||||
secp256k1_pubkey combined_pk;
|
||||
uint8_t *nonce_commitments,**commitment_ptrs; // 32*N_SIGNERS
|
||||
secp256k1_musig_session_signer_data *signer_data; //[N_SIGNERS];
|
||||
secp256k1_pubkey *nonces; //[N_SIGNERS];
|
||||
secp256k1_musig_partial_signature *partial_sig; //[N_SIGNERS];
|
||||
int32_t myind,num,numcommits,numnonces,numpartials;
|
||||
uint8_t msg[32],pkhash[32],combpk[33];
|
||||
};
|
||||
|
||||
std::vector <struct musig_info *> MUSIG;
|
||||
|
||||
struct musig_info *musig_infocreate(int32_t myind,int32_t num)
|
||||
{
|
||||
int32_t i; struct musig_info *mp = (struct musig_info *)calloc(1,sizeof(*mp));
|
||||
mp->myind = myind, mp->num = num;
|
||||
mp->nonce_commitments = (uint8_t *)calloc(num,32);
|
||||
mp->commitment_ptrs = (uint8_t **)calloc(num,sizeof(*mp->commitment_ptrs));
|
||||
for (i=0; i<num; i++)
|
||||
mp->commitment_ptrs[i] = &mp->nonce_commitments[i*32];
|
||||
mp->signer_data = (secp256k1_musig_session_signer_data *)calloc(num,sizeof(*mp->signer_data));
|
||||
mp->nonces = (secp256k1_pubkey *)calloc(num,sizeof(*mp->nonces));
|
||||
mp->partial_sig = (secp256k1_musig_partial_signature *)calloc(num,sizeof(*mp->partial_sig));
|
||||
return(mp);
|
||||
}
|
||||
|
||||
void musig_infofree(struct musig_info *mp)
|
||||
{
|
||||
if ( mp->partial_sig != 0 )
|
||||
{
|
||||
GetRandBytes((uint8_t *)mp->partial_sig,mp->num*sizeof(*mp->partial_sig));
|
||||
free(mp->partial_sig);
|
||||
}
|
||||
if ( mp->nonces != 0 )
|
||||
{
|
||||
GetRandBytes((uint8_t *)mp->nonces,mp->num*sizeof(*mp->nonces));
|
||||
free(mp->nonces);
|
||||
}
|
||||
if ( mp->signer_data != 0 )
|
||||
{
|
||||
GetRandBytes((uint8_t *)mp->signer_data,mp->num*sizeof(*mp->signer_data));
|
||||
free(mp->signer_data);
|
||||
}
|
||||
if ( mp->nonce_commitments != 0 )
|
||||
{
|
||||
GetRandBytes((uint8_t *)mp->nonce_commitments,mp->num*32);
|
||||
free(mp->nonce_commitments);
|
||||
}
|
||||
if ( mp->commitment_ptrs != 0 )
|
||||
{
|
||||
GetRandBytes((uint8_t *)mp->commitment_ptrs,mp->num*sizeof(*mp->commitment_ptrs));
|
||||
free(mp->commitment_ptrs);
|
||||
}
|
||||
GetRandBytes((uint8_t *)mp,sizeof(*mp));
|
||||
free(mp);
|
||||
}
|
||||
|
||||
CScript musig_sendopret(uint8_t funcid,CPubKey pk)
|
||||
{
|
||||
CScript opret; uint8_t evalcode = EVAL_MUSIG;
|
||||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << pk);
|
||||
return(opret);
|
||||
}
|
||||
|
||||
uint8_t musig_sendopretdecode(CPubKey &pk,CScript scriptPubKey)
|
||||
{
|
||||
std::vector<uint8_t> vopret; uint8_t e,f;
|
||||
GetOpReturnData(scriptPubKey,vopret);
|
||||
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk) != 0 && e == EVAL_MUSIG && f == 'x' )
|
||||
{
|
||||
return(f);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
CScript musig_spendopret(uint8_t funcid,CPubKey pk,std::vector<uint8_t> musig64)
|
||||
{
|
||||
CScript opret; uint8_t evalcode = EVAL_MUSIG;
|
||||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << pk << musig64);
|
||||
return(opret);
|
||||
}
|
||||
|
||||
uint8_t musig_spendopretdecode(CPubKey &pk,std::vector<uint8_t> &musig64,CScript scriptPubKey)
|
||||
{
|
||||
std::vector<uint8_t> vopret; uint8_t e,f;
|
||||
GetOpReturnData(scriptPubKey,vopret);
|
||||
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> musig64) != 0 && e == EVAL_MUSIG && f == 'y' )
|
||||
{
|
||||
return(f);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t musig_parsepubkey(secp256k1_context *ctx,secp256k1_pubkey &spk,cJSON *item)
|
||||
{
|
||||
char *hexstr;
|
||||
if ( (hexstr= jstr(item,0)) != 0 && is_hexstr(hexstr,0) == 66 )
|
||||
{
|
||||
CPubKey pk(ParseHex(hexstr));
|
||||
if ( secp256k1_ec_pubkey_parse(ctx,&spk,pk.begin(),33) > 0 )
|
||||
return(1);
|
||||
} else return(-1);
|
||||
}
|
||||
|
||||
int32_t musig_msghash(uint8_t *msg,uint256 prevhash,int32_t prevn,CTxOut vout,CPubKey pk)
|
||||
{
|
||||
CScript data; uint256 hash; int32_t len = 0;
|
||||
data << E_MARSHAL(ss << prevhash << prevn << vout << pk);
|
||||
hash = Hash(data.begin(),data.end());
|
||||
memcpy(msg,&hash,sizeof(hash));
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t musig_prevoutmsg(uint8_t *msg,uint256 sendtxid,CScript scriptPubKey)
|
||||
{
|
||||
CTransaction vintx; uint256 hashBlock; int32_t numvouts; CTxOut vout; CPubKey pk;
|
||||
memset(msg,0,32);
|
||||
if ( myGetTransaction(sendtxid,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 )
|
||||
{
|
||||
if ( musig_sendopretdecode(pk,vintx.vout[numvouts-1].scriptPubKey) == 'x' )
|
||||
{
|
||||
vout.nValue = vintx.vout[MUSIG_PREVN].nValue - MUSIG_TXFEE;
|
||||
vout.scriptPubKey = scriptPubKey;
|
||||
return(musig_msghash(msg,sendtxid,MUSIG_PREVN,vout,pk));
|
||||
}
|
||||
}
|
||||
return(-1);
|
||||
}
|
||||
|
||||
UniValue musig_calcmsg(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ); uint256 sendtxid; int32_t i,zeros=0; uint8_t msg[32]; char *scriptstr,str[65]; int32_t n;
|
||||
if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
|
||||
{
|
||||
if ( n == 2 )
|
||||
{
|
||||
sendtxid = juint256(jitem(params,0));
|
||||
scriptstr = jstr(jitem(params,1),0);
|
||||
if ( is_hexstr(scriptstr,0) != 0 )
|
||||
{
|
||||
CScript scriptPubKey;
|
||||
scriptPubKey.resize(strlen(scriptstr)/2);
|
||||
decode_hex(&scriptPubKey[0],strlen(scriptstr)/2,scriptstr);
|
||||
musig_prevoutmsg(msg,sendtxid,scriptPubKey);
|
||||
for (i=0; i<32; i++)
|
||||
{
|
||||
sprintf(&str[i<<1],"%02x",msg[i]);
|
||||
if ( msg[i] == 0 )
|
||||
zeros++;
|
||||
}
|
||||
str[64] = 0;
|
||||
if ( zeros != 32 )
|
||||
{
|
||||
result.push_back(Pair("msg",str));
|
||||
result.push_back(Pair("result","success"));
|
||||
return(result);
|
||||
} else return(cclib_error(result,"null result, make sure params are sendtxid, scriptPubKey"));
|
||||
} else return(cclib_error(result,"script is not hex"));
|
||||
} else return(cclib_error(result,"need exactly 2 parameters: sendtxid, scriptPubKey"));
|
||||
} else return(cclib_error(result,"couldnt parse params"));
|
||||
}
|
||||
|
||||
UniValue musig_combine(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
static secp256k1_context *ctx;
|
||||
size_t clen = CPubKey::PUBLIC_KEY_SIZE;
|
||||
UniValue result(UniValue::VOBJ); CPubKey pk; int32_t i,n; uint8_t pkhash[32]; char *hexstr,str[67]; secp256k1_pubkey combined_pk,spk; std::vector<secp256k1_pubkey> pubkeys;
|
||||
if ( ctx == 0 )
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
|
||||
{
|
||||
//fprintf(stderr,"n.%d args.(%s)\n",n,jprint(params,0));
|
||||
for (i=0; i<n; i++)
|
||||
{
|
||||
if ( musig_parsepubkey(ctx,spk,jitem(params,i)) < 0 )
|
||||
return(cclib_error(result,"error parsing pk"));
|
||||
pubkeys.push_back(spk);
|
||||
}
|
||||
if ( secp256k1_musig_pubkey_combine(ctx,NULL,&combined_pk,pkhash,&pubkeys[0],n) > 0 )
|
||||
{
|
||||
if ( secp256k1_ec_pubkey_serialize(ctx,(uint8_t *)pk.begin(),&clen,&combined_pk,SECP256K1_EC_COMPRESSED) > 0 && clen == 33 )
|
||||
{
|
||||
for (i=0; i<32; i++)
|
||||
sprintf(&str[i<<1],"%02x",pkhash[i]);
|
||||
str[64] = 0;
|
||||
result.push_back(Pair("pkhash",str));
|
||||
|
||||
for (i=0; i<33; i++)
|
||||
sprintf(&str[i<<1],"%02x",((uint8_t *)pk.begin())[i]);
|
||||
str[66] = 0;
|
||||
result.push_back(Pair("combined_pk",str));
|
||||
result.push_back(Pair("result","success"));
|
||||
return(result);
|
||||
} else return(cclib_error(result,"error serializeing combined_pk"));
|
||||
} else return(cclib_error(result,"error combining pukbeys"));
|
||||
} else return(cclib_error(result,"need pubkeys params"));
|
||||
}
|
||||
|
||||
UniValue musig_session(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
static secp256k1_context *ctx;
|
||||
UniValue result(UniValue::VOBJ); int32_t i,n,myind,num,musiglocation; char *pkstr,*pkhashstr,*msgstr; uint8_t session[32],msg[32],pkhash[32],privkey[32],pub33[33]; CPubKey pk; char str[67];
|
||||
if ( ctx == 0 )
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 5 )
|
||||
{
|
||||
myind = juint(jitem(params,0),0);
|
||||
num = juint(jitem(params,1),0);
|
||||
if ( myind < 0 || myind >= num || num <= 0 )
|
||||
return(cclib_error(result,"illegal myindex and numsigners"));
|
||||
if ( n > 5 )
|
||||
musiglocation = juint(jitem(params,5),0);
|
||||
else if ( n == 5 )
|
||||
musiglocation = 0;
|
||||
//printf("number of params.%i musiglocation.%i\n",n,musiglocation);
|
||||
if ( MUSIG.size() > musiglocation )
|
||||
{
|
||||
for (int i = 0; i < MUSIG.size()-1; i++)
|
||||
musig_infofree(MUSIG[i]);
|
||||
MUSIG.clear();
|
||||
}
|
||||
struct musig_info *temp_musig = musig_infocreate(myind,num);
|
||||
MUSIG.push_back(temp_musig);
|
||||
if ( musig_parsepubkey(ctx,MUSIG[musiglocation]->combined_pk,jitem(params,2)) < 0 )
|
||||
return(cclib_error(result,"error parsing combined_pubkey"));
|
||||
else if ( cclib_parsehash(MUSIG[musiglocation]->pkhash,jitem(params,3),32) < 0 )
|
||||
return(cclib_error(result,"error parsing pkhash"));
|
||||
else if ( cclib_parsehash(MUSIG[musiglocation]->msg,jitem(params,4),32) < 0 )
|
||||
return(cclib_error(result,"error parsing msg"));
|
||||
Myprivkey(privkey);
|
||||
GetRandBytes(session,32);
|
||||
/** Initializes a signing session for a signer
|
||||
*
|
||||
* Returns: 1: session is successfully initialized
|
||||
* 0: session could not be initialized: secret key or secret nonce overflow
|
||||
* Args: ctx: pointer to a context object, initialized for signing (cannot
|
||||
* be NULL)
|
||||
* Out: session: the session structure to initialize (cannot be NULL)
|
||||
* signers: an array of signers' data to be initialized. Array length must
|
||||
* equal to `n_signers` (cannot be NULL)
|
||||
* nonce_commitment32: filled with a 32-byte commitment to the generated nonce
|
||||
* (cannot be NULL)
|
||||
* In: session_id32: a *unique* 32-byte ID to assign to this session (cannot be
|
||||
* NULL). If a non-unique session_id32 was given then a partial
|
||||
* signature will LEAK THE SECRET KEY.
|
||||
* msg32: the 32-byte message to be signed. Shouldn't be NULL unless you
|
||||
* require sharing public nonces before the message is known
|
||||
* because it reduces nonce misuse resistance. If NULL, must be
|
||||
* set with `musig_session_set_msg` before signing and verifying.
|
||||
* combined_pk: the combined public key of all signers (cannot be NULL)
|
||||
* pk_hash32: the 32-byte hash of the signers' individual keys (cannot be
|
||||
* NULL)
|
||||
* n_signers: length of signers array. Number of signers participating in
|
||||
* the MuSig. Must be greater than 0 and at most 2^32 - 1.
|
||||
* my_index: index of this signer in the signers array
|
||||
* seckey: the signer's 32-byte secret key (cannot be NULL)
|
||||
*/
|
||||
//fprintf(stderr, "SESSION: struct_size.%li using struct %i\n",MUSIG.size(), musiglocation);
|
||||
if ( secp256k1_musig_session_initialize(ctx,&MUSIG[musiglocation]->session,MUSIG[musiglocation]->signer_data, &MUSIG[musiglocation]->nonce_commitments[MUSIG[musiglocation]->myind * 32],session,MUSIG[musiglocation]->msg,&MUSIG[musiglocation]->combined_pk,MUSIG[musiglocation]->pkhash,MUSIG[musiglocation]->num,MUSIG[musiglocation]->myind,privkey) > 0 )
|
||||
{
|
||||
memset(session,0,sizeof(session));
|
||||
result.push_back(Pair("myind",(int64_t)myind));
|
||||
result.push_back(Pair("numsigners",(int64_t)num));
|
||||
for (i=0; i<32; i++)
|
||||
sprintf(&str[i<<1],"%02x",MUSIG[musiglocation]->nonce_commitments[MUSIG[musiglocation]->myind*32 + i]);
|
||||
str[64] = 0;
|
||||
if ( n == 5 )
|
||||
MUSIG[musiglocation]->numcommits = 1;
|
||||
result.push_back(Pair("commitment",str));
|
||||
result.push_back(Pair("result","success"));
|
||||
memset(privkey,0,sizeof(privkey));
|
||||
return(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(privkey,0,sizeof(privkey));
|
||||
memset(session,0,sizeof(session));
|
||||
return(cclib_error(result,"couldnt initialize session"));
|
||||
}
|
||||
} else return(cclib_error(result,"wrong number of params, need 5: myindex, numsigners, combined_pk, pkhash, msg32"));
|
||||
}
|
||||
|
||||
UniValue musig_commit(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
static secp256k1_context *ctx;
|
||||
size_t clen = CPubKey::PUBLIC_KEY_SIZE;
|
||||
UniValue result(UniValue::VOBJ); int32_t i,n,ind,myind; uint8_t pkhash[32]; CPubKey pk; char str[67];
|
||||
if ( ctx == 0 )
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 3 )
|
||||
{
|
||||
if ( n > 3 )
|
||||
myind = juint(jitem(params,3),0);
|
||||
else if ( n == 3 )
|
||||
myind = 0;
|
||||
if ( cclib_parsehash(pkhash,jitem(params,0),32) < 0 )
|
||||
return(cclib_error(result,"error parsing pkhash"));
|
||||
else if ( memcmp(MUSIG[myind]->pkhash,pkhash,32) != 0 )
|
||||
return(cclib_error(result,"pkhash doesnt match session pkhash"));
|
||||
else if ( (ind= juint(jitem(params,1),0)) < 0 || ind >= MUSIG[myind]->num )
|
||||
return(cclib_error(result,"illegal ind for session"));
|
||||
else if ( cclib_parsehash(&MUSIG[myind]->nonce_commitments[ind*32],jitem(params,2),32) < 0 )
|
||||
return(cclib_error(result,"error parsing commitment"));
|
||||
/** Gets the signer's public nonce given a list of all signers' data with commitments
|
||||
*
|
||||
* Returns: 1: public nonce is written in nonce
|
||||
* 0: signer data is missing commitments or session isn't initialized
|
||||
* for signing
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* session: the signing session to get the nonce from (cannot be NULL)
|
||||
* signers: an array of signers' data initialized with
|
||||
* `musig_session_initialize`. Array length must equal to
|
||||
* `n_commitments` (cannot be NULL)
|
||||
* Out: nonce: the nonce (cannot be NULL)
|
||||
* In: commitments: array of 32-byte nonce commitments (cannot be NULL)
|
||||
* n_commitments: the length of commitments and signers array. Must be the total
|
||||
* number of signers participating in the MuSig.
|
||||
*/
|
||||
result.push_back(Pair("added_index",ind));
|
||||
//fprintf(stderr, "COMMIT: struct_size.%li using_struct.%i added_index.%i\n",MUSIG.size(), myind, ind);
|
||||
MUSIG[myind]->numcommits++;
|
||||
if ( MUSIG[myind]->numcommits >= MUSIG[myind]->num && secp256k1_musig_session_get_public_nonce(ctx,&MUSIG[myind]->session,MUSIG[myind]->signer_data,&MUSIG[myind]->nonces[MUSIG[myind]->myind],MUSIG[myind]->commitment_ptrs,MUSIG[myind]->num) > 0 )
|
||||
{
|
||||
if ( secp256k1_ec_pubkey_serialize(ctx,(uint8_t *)pk.begin(),&clen,&MUSIG[myind]->nonces[MUSIG[myind]->myind],SECP256K1_EC_COMPRESSED) > 0 && clen == 33 )
|
||||
{
|
||||
for (i=0; i<33; i++)
|
||||
sprintf(&str[i<<1],"%02x",((uint8_t *)pk.begin())[i]);
|
||||
str[66] = 0;
|
||||
if ( n == 3 )
|
||||
MUSIG[myind]->numnonces = 1;
|
||||
result.push_back(Pair("myind",MUSIG[myind]->myind));
|
||||
result.push_back(Pair("nonce",str));
|
||||
result.push_back(Pair("result","success"));
|
||||
} else return(cclib_error(result,"error serializing nonce (pubkey)"));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(Pair("status","not enough commitments"));
|
||||
result.push_back(Pair("result","success"));
|
||||
}
|
||||
return(result);
|
||||
} else return(cclib_error(result,"wrong number of params, need 3: pkhash, ind, commitment"));
|
||||
}
|
||||
|
||||
UniValue musig_nonce(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
static secp256k1_context *ctx;
|
||||
UniValue result(UniValue::VOBJ); int32_t i,n,ind,myind; uint8_t pkhash[32],psig[32]; CPubKey pk; char str[67];
|
||||
if ( ctx == 0 )
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 3 )
|
||||
{
|
||||
if ( n > 3 )
|
||||
myind = juint(jitem(params,3),0);
|
||||
else if ( n == 3 )
|
||||
myind = 0;
|
||||
if ( cclib_parsehash(pkhash,jitem(params,0),32) < 0 )
|
||||
return(cclib_error(result,"error parsing pkhash"));
|
||||
else if ( memcmp(MUSIG[myind]->pkhash,pkhash,32) != 0 )
|
||||
return(cclib_error(result,"pkhash doesnt match session pkhash"));
|
||||
else if ( (ind= juint(jitem(params,1),0)) < 0 || ind >= MUSIG[myind]->num )
|
||||
return(cclib_error(result,"illegal ind for session"));
|
||||
else if ( musig_parsepubkey(ctx,MUSIG[myind]->nonces[ind],jitem(params,2)) < 0 )
|
||||
return(cclib_error(result,"error parsing nonce"));
|
||||
result.push_back(Pair("added_index",ind));
|
||||
/** Checks a signer's public nonce against a commitment to said nonce, and update
|
||||
* data structure if they match
|
||||
*
|
||||
* Returns: 1: commitment was valid, data structure updated
|
||||
* 0: commitment was invalid, nothing happened
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* signer: pointer to the signer data to update (cannot be NULL). Must have
|
||||
* been used with `musig_session_get_public_nonce` or initialized
|
||||
* with `musig_session_initialize_verifier`.
|
||||
* In: nonce: signer's alleged public nonce (cannot be NULL)
|
||||
*/
|
||||
MUSIG[myind]->numnonces++;
|
||||
//fprintf(stderr, "NONCE: struct_size.%li using_struct.%i added_index.%i numnounces.%i num.%i\n",MUSIG.size(), myind, ind, MUSIG[myind]->numnonces, MUSIG[myind]->num);
|
||||
if ( MUSIG[myind]->numnonces < MUSIG[myind]->num )
|
||||
{
|
||||
result.push_back(Pair("status","not enough nonces"));
|
||||
result.push_back(Pair("result","success"));
|
||||
return(result);
|
||||
}
|
||||
for (i=0; i<MUSIG[myind]->num; i++)
|
||||
{
|
||||
if ( secp256k1_musig_set_nonce(ctx,&MUSIG[myind]->signer_data[i],&MUSIG[myind]->nonces[i]) == 0 )
|
||||
return(cclib_error(result,"error setting nonce"));
|
||||
}
|
||||
/** Updates a session with the combined public nonce of all signers. The combined
|
||||
* public nonce is the sum of every signer's public nonce.
|
||||
*
|
||||
* Returns: 1: nonces are successfully combined
|
||||
* 0: a signer's nonce is missing
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* session: session to update with the combined public nonce (cannot be
|
||||
* NULL)
|
||||
* signers: an array of signers' data, which must have had public nonces
|
||||
* set with `musig_set_nonce`. Array length must equal to `n_signers`
|
||||
* (cannot be NULL)
|
||||
* n_signers: the length of the signers array. Must be the total number of
|
||||
* signers participating in the MuSig.
|
||||
* Out: nonce_is_negated: a pointer to an integer that indicates if the combined
|
||||
* public nonce had to be negated.
|
||||
* adaptor: point to add to the combined public nonce. If NULL, nothing is
|
||||
* added to the combined nonce.
|
||||
*/
|
||||
if ( secp256k1_musig_session_combine_nonces(ctx,&MUSIG[myind]->session,MUSIG[myind]->signer_data,MUSIG[myind]->num,NULL,NULL) > 0 )
|
||||
{
|
||||
if ( secp256k1_musig_partial_sign(ctx,&MUSIG[myind]->session,&MUSIG[myind]->partial_sig[MUSIG[myind]->myind]) > 0 )
|
||||
{
|
||||
if ( secp256k1_musig_partial_signature_serialize(ctx,psig,&MUSIG[myind]->partial_sig[MUSIG[myind]->myind]) > 0 )
|
||||
{
|
||||
for (i=0; i<32; i++)
|
||||
sprintf(&str[i<<1],"%02x",psig[i]);
|
||||
str[64] = 0;
|
||||
result.push_back(Pair("myind",MUSIG[myind]->myind));
|
||||
result.push_back(Pair("partialsig",str));
|
||||
result.push_back(Pair("result","success"));
|
||||
if ( n == 3 )
|
||||
MUSIG[myind]->numpartials = 1;
|
||||
return(result);
|
||||
} else return(cclib_error(result,"error serializing partial sig"));
|
||||
} else return(cclib_error(result,"error making partial sig"));
|
||||
} else return(cclib_error(result,"error combining nonces"));
|
||||
} else return(cclib_error(result,"wrong number of params, need 3: pkhash, ind, nonce"));
|
||||
}
|
||||
|
||||
UniValue musig_partialsig(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
static secp256k1_context *ctx;
|
||||
UniValue result(UniValue::VOBJ); int32_t i,ind,n,myind; uint8_t pkhash[32],psig[32],out64[64]; char str[129]; secp256k1_schnorrsig sig;
|
||||
if ( ctx == 0 )
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 3 )
|
||||
{
|
||||
if ( n > 3 )
|
||||
myind = juint(jitem(params,3),0);
|
||||
else if ( n == 3 )
|
||||
myind = 0;
|
||||
if ( cclib_parsehash(pkhash,jitem(params,0),32) < 0 )
|
||||
return(cclib_error(result,"error parsing pkhash"));
|
||||
else if ( memcmp(MUSIG[myind]->pkhash,pkhash,32) != 0 )
|
||||
return(cclib_error(result,"pkhash doesnt match session pkhash"));
|
||||
else if ( (ind= juint(jitem(params,1),0)) < 0 || ind >= MUSIG[myind]->num )
|
||||
return(cclib_error(result,"illegal ind for session"));
|
||||
else if ( cclib_parsehash(psig,jitem(params,2),32) < 0 )
|
||||
return(cclib_error(result,"error parsing psig"));
|
||||
else if ( secp256k1_musig_partial_signature_parse(ctx,&MUSIG[myind]->partial_sig[ind],psig) == 0 )
|
||||
return(cclib_error(result,"error parsing partialsig"));
|
||||
result.push_back(Pair("added_index",ind));
|
||||
//fprintf(stderr, "SIG: struct_size.%li using_struct.%i added_index.%i\n",MUSIG.size(), myind, ind);
|
||||
MUSIG[myind]->numpartials++;
|
||||
if ( MUSIG[myind]->numpartials >= MUSIG[myind]->num && secp256k1_musig_partial_sig_combine(ctx,&MUSIG[myind]->session,&sig,MUSIG[myind]->partial_sig,MUSIG[myind]->num) > 0 )
|
||||
{
|
||||
if ( secp256k1_schnorrsig_serialize(ctx,out64,&sig) > 0 )
|
||||
{
|
||||
result.push_back(Pair("result","success"));
|
||||
for (i=0; i<64; i++)
|
||||
sprintf(&str[i<<1],"%02x",out64[i]);
|
||||
str[128] = 0;
|
||||
result.push_back(Pair("combinedsig",str));
|
||||
} else return(cclib_error(result,"error serializing combinedsig"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( secp256k1_musig_partial_signature_serialize(ctx,psig,&MUSIG[myind]->partial_sig[MUSIG[myind]->myind]) > 0 )
|
||||
{
|
||||
result.push_back(Pair("myind",ind));
|
||||
for (i=0; i<32; i++)
|
||||
sprintf(&str[i<<1],"%02x",psig[i]);
|
||||
str[64] = 0;
|
||||
result.push_back(Pair("partialsig",str));
|
||||
result.push_back(Pair("result","success"));
|
||||
result.push_back(Pair("status","need more partialsigs"));
|
||||
} else return(cclib_error(result,"error generating my partialsig"));
|
||||
}
|
||||
return(result);
|
||||
} else return(cclib_error(result,"wrong number of params, need 3: pkhash, ind, partialsig"));
|
||||
}
|
||||
|
||||
//int testmain(void);
|
||||
UniValue musig_verify(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
static secp256k1_context *ctx;
|
||||
UniValue result(UniValue::VOBJ); int32_t i,n; uint8_t msg[32],musig64[64]; secp256k1_pubkey combined_pk; secp256k1_schnorrsig musig; char str[129];
|
||||
//testmain();
|
||||
if ( ctx == 0 )
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
if ( params != 0 && (n= cJSON_GetArraySize(params)) == 3 )
|
||||
{
|
||||
if ( cclib_parsehash(msg,jitem(params,0),32) < 0 )
|
||||
return(cclib_error(result,"error parsing pkhash"));
|
||||
else if ( musig_parsepubkey(ctx,combined_pk,jitem(params,1)) < 0 )
|
||||
return(cclib_error(result,"error parsing combined_pk"));
|
||||
else if ( cclib_parsehash(musig64,jitem(params,2),64) < 0 )
|
||||
return(cclib_error(result,"error parsing musig64"));
|
||||
for (i=0; i<32; i++)
|
||||
sprintf(&str[i*2],"%02x",msg[i]);
|
||||
str[64] = 0;
|
||||
result.push_back(Pair("msg",str));
|
||||
result.push_back(Pair("combined_pk",jstr(jitem(params,1),0)));
|
||||
for (i=0; i<64; i++)
|
||||
sprintf(&str[i*2],"%02x",musig64[i]);
|
||||
str[128] = 0;
|
||||
result.push_back(Pair("combinedsig",str));
|
||||
if ( secp256k1_schnorrsig_parse(ctx,&musig,&musig64[0]) > 0 )
|
||||
{
|
||||
if ( secp256k1_schnorrsig_verify(ctx,&musig,msg,&combined_pk) > 0 )
|
||||
{
|
||||
result.push_back(Pair("result","success"));
|
||||
return(result);
|
||||
} else return(cclib_error(result,"musig didnt verify"));
|
||||
} else return(cclib_error(result,"couldnt parse musig64"));
|
||||
} else return(cclib_error(result,"wrong number of params, need 3: msg, combined_pk, combinedsig"));
|
||||
}
|
||||
|
||||
// helpers for rpc calls that generate/validate onchain tx
|
||||
|
||||
UniValue musig_rawtxresult(UniValue &result,std::string rawtx)
|
||||
{
|
||||
CTransaction tx;
|
||||
if ( rawtx.size() > 0 )
|
||||
{
|
||||
result.push_back(Pair("hex",rawtx));
|
||||
if ( DecodeHexTx(tx,rawtx) != 0 )
|
||||
{
|
||||
//if ( broadcastflag != 0 && myAddtomempool(tx) != 0 )
|
||||
// RelayTransaction(tx);
|
||||
result.push_back(Pair("txid",tx.GetHash().ToString()));
|
||||
result.push_back(Pair("result","success"));
|
||||
} else result.push_back(Pair("error","decode hex"));
|
||||
} else result.push_back(Pair("error","couldnt finalize CCtx"));
|
||||
return(result);
|
||||
}
|
||||
|
||||
UniValue musig_send(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
UniValue result(UniValue::VOBJ); int32_t n; char *hexstr; std::string rawtx; int64_t amount; CPubKey musigpk,mypk;
|
||||
if ( txfee == 0 )
|
||||
txfee = MUSIG_TXFEE;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
musigpk = GetUnspendable(cp,0);
|
||||
if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
|
||||
{
|
||||
if ( n == 2 && (hexstr= jstr(jitem(params,0),0)) != 0 && is_hexstr(hexstr,0) == 66 )
|
||||
{
|
||||
CPubKey pk(ParseHex(hexstr));
|
||||
amount = jdouble(jitem(params,1),0) * COIN + 0.0000000049;
|
||||
if ( amount >= 3*txfee && AddNormalinputs(mtx,mypk,amount+2*txfee,64) >= amount+2*txfee )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount+txfee,musigpk));
|
||||
rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,musig_sendopret('x',pk));
|
||||
return(musig_rawtxresult(result,rawtx));
|
||||
} else return(cclib_error(result,"couldnt find funds or less than 0.0003"));
|
||||
} else return(cclib_error(result,"must have 2 params: pk, amount"));
|
||||
} else return(cclib_error(result,"not enough parameters"));
|
||||
}
|
||||
|
||||
UniValue musig_spend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
|
||||
{
|
||||
static secp256k1_context *ctx;
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey mypk,pk; secp256k1_pubkey combined_pk; char *scriptstr,*musigstr; uint8_t msg[32]; CTransaction vintx; uint256 prevhash,hashBlock; int32_t i,n,numvouts; char str[129]; CTxOut vout; secp256k1_schnorrsig musig;
|
||||
if ( ctx == 0 )
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 )
|
||||
{
|
||||
if ( n == 3 )
|
||||
{
|
||||
prevhash = juint256(jitem(params,0));
|
||||
scriptstr = jstr(jitem(params,1),0);
|
||||
musigstr = jstr(jitem(params,2),0);
|
||||
if ( is_hexstr(scriptstr,0) != 0 && is_hexstr(musigstr,0) == 128 )
|
||||
{
|
||||
if ( txfee == 0 )
|
||||
txfee = MUSIG_TXFEE;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
std::vector<uint8_t> musig64(ParseHex(musigstr));
|
||||
CScript scriptPubKey;
|
||||
scriptPubKey.resize(strlen(scriptstr)/2);
|
||||
decode_hex(&scriptPubKey[0],strlen(scriptstr)/2,scriptstr);
|
||||
if ( myGetTransaction(prevhash,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 )
|
||||
{
|
||||
vout.nValue = vintx.vout[0].nValue - txfee;
|
||||
vout.scriptPubKey = scriptPubKey;
|
||||
if ( musig_sendopretdecode(pk,vintx.vout[numvouts-1].scriptPubKey) == 'x' )
|
||||
{
|
||||
if ( secp256k1_schnorrsig_parse((const secp256k1_context *)ctx,&musig,(const uint8_t *)&musig64[0]) > 0 &&
|
||||
secp256k1_ec_pubkey_parse(ctx,&combined_pk,pk.begin(),33) > 0 )
|
||||
{
|
||||
musig_prevoutmsg(msg,prevhash,vout.scriptPubKey);
|
||||
{
|
||||
for (i=0; i<32; i++)
|
||||
sprintf(&str[i*2],"%02x",msg[i]);
|
||||
str[64] = 0;
|
||||
result.push_back(Pair("msg",str));
|
||||
for (i=0; i<33; i++)
|
||||
sprintf(&str[i*2],"%02x",((uint8_t *)pk.begin())[i]);
|
||||
str[66] = 0;
|
||||
result.push_back(Pair("combined_pk",str));
|
||||
for (i=0; i<64; i++)
|
||||
sprintf(&str[i*2],"%02x",musig64[i]);
|
||||
str[128] = 0;
|
||||
result.push_back(Pair("combinedsig",str));
|
||||
}
|
||||
if ( !secp256k1_schnorrsig_verify((const secp256k1_context *)ctx,&musig,(const uint8_t *)msg,(const secp256k1_pubkey *)&combined_pk) )
|
||||
{
|
||||
return(cclib_error(result,"musig didnt validate"));
|
||||
}
|
||||
mtx.vin.push_back(CTxIn(prevhash,MUSIG_PREVN));
|
||||
mtx.vout.push_back(vout);
|
||||
rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,musig_spendopret('y',pk,musig64));
|
||||
return(musig_rawtxresult(result,rawtx));
|
||||
} else return(cclib_error(result,"couldnt parse pk or musig"));
|
||||
} else return(cclib_error(result,"couldnt decode send opret"));
|
||||
} else return(cclib_error(result,"couldnt find vin0"));
|
||||
} else return(cclib_error(result,"script or musig is not hex"));
|
||||
} else return(cclib_error(result,"need to have exactly 3 params sendtxid, scriptPubKey, musig"));
|
||||
} else return(cclib_error(result,"params parse error"));
|
||||
}
|
||||
|
||||
bool musig_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx)
|
||||
{
|
||||
static secp256k1_context *ctx;
|
||||
secp256k1_pubkey combined_pk; CPubKey pk,checkpk; secp256k1_schnorrsig musig; uint256 hashBlock; CTransaction vintx; int32_t numvouts; std::vector<uint8_t> musig64; uint8_t msg[32];
|
||||
if ( ctx == 0 )
|
||||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
if ( tx.vout.size() != 2 )
|
||||
return eval->Invalid("numvouts != 2");
|
||||
else if ( tx.vin.size() != 1 )
|
||||
return eval->Invalid("numvins != 1");
|
||||
else if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
|
||||
return eval->Invalid("illegal normal vin0");
|
||||
else if ( myGetTransaction(tx.vin[0].prevout.hash,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 )
|
||||
{
|
||||
if ( musig_sendopretdecode(pk,vintx.vout[numvouts-1].scriptPubKey) == 'x' )
|
||||
{
|
||||
if ( musig_spendopretdecode(checkpk,musig64,tx.vout[tx.vout.size()-1].scriptPubKey) == 'y' )
|
||||
{
|
||||
if ( pk == checkpk )
|
||||
{
|
||||
if ( secp256k1_schnorrsig_parse((const secp256k1_context *)ctx,&musig,(const uint8_t *)&musig64[0]) > 0 &&
|
||||
secp256k1_ec_pubkey_parse(ctx,&combined_pk,pk.begin(),33) > 0 )
|
||||
{
|
||||
musig_prevoutmsg(msg,tx.vin[0].prevout.hash,tx.vout[0].scriptPubKey);
|
||||
if ( !secp256k1_schnorrsig_verify((const secp256k1_context *)ctx,&musig,(const uint8_t *)msg,(const secp256k1_pubkey *)&combined_pk) )
|
||||
return eval->Invalid("failed schnorrsig_verify");
|
||||
else return(true);
|
||||
} else return eval->Invalid("couldnt parse pk or musig");
|
||||
} else return eval->Invalid("combined_pk didnt match send opret");
|
||||
} else return eval->Invalid("failed decode musig spendopret");
|
||||
} else return eval->Invalid("couldnt decode send opret");
|
||||
} else return eval->Invalid("couldnt find vin0 tx");
|
||||
}
|
||||
1233
src/cc/oracles.cpp
1233
src/cc/oracles.cpp
File diff suppressed because it is too large
Load Diff
1572
src/cc/payments.cpp
1572
src/cc/payments.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,756 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCrewards.h"
|
||||
|
||||
/*
|
||||
The rewards CC contract is initially for OOT, which needs this functionality. However, many of the attributes can be parameterized to allow different rewards programs to run. Multiple rewards plans could even run on the same blockchain, though the user would need to choose which one to lock funds into.
|
||||
|
||||
At the high level, the user would lock funds for some amount of time and at the end of it, would get all the locked funds back with an additional reward. So there needs to be a lock funds and unlock funds ability. Additionally, the rewards need to come from somewhere, so similar to the faucet, there would be a way to fund the reward.
|
||||
|
||||
Additional requirements are for the user to be able to lock funds via SPV. This requirement in turns forces the creation of a way for anybody to be able to unlock the funds as that operation requires a native daemon running and cant be done over SPV. The idea is to allow anybody to run a script that would unlock all funds that are matured. As far as the user is concerned, he locks his funds via SPV and after some time it comes back with an extra reward.
|
||||
|
||||
In reality, the funds are locked into a CC address that is unspendable, except for some special conditions and it needs to come back to the address that funded the lock. In order to implement this, several things are clear.
|
||||
|
||||
1) each locked CC utxo needs to be linked to a specific rewards plan
|
||||
2) each locked CC utxo needs to know the only address that it can be unlocked into
|
||||
3) SPV requirement means the lock transaction needs to be able to be created without any CC signing
|
||||
|
||||
The opreturn will be used to store the name of the rewards plan and all funding and locked funds with the same plan will use the same pool of rewards. plan names will be limited to 8 chars and encoded into a uint64_t.
|
||||
|
||||
The initial funding transaction will have all the parameters for the rewards plan encoded in the vouts. Additional fundings will just increase the available CC utxo for the rewards.
|
||||
|
||||
Locks wont have any CC vins, but will send to the RewardsCCaddress, with the plan stringbits in the opreturn. vout1 will have the unlock address and no other destination is valid.
|
||||
|
||||
Unlock does a CC spend to the vout1 address
|
||||
|
||||
|
||||
createfunding
|
||||
vins.*: normal inputs
|
||||
vout.0: CC vout for funding
|
||||
vout.1: normal marker vout for easy searching
|
||||
vout.2: normal change
|
||||
vout.n-1: opreturn 'F' sbits APR minseconds maxseconds mindeposit
|
||||
|
||||
addfunding
|
||||
vins.*: normal inputs
|
||||
vout.0: CC vout for funding
|
||||
vout.1: normal change
|
||||
vout.n-1: opreturn 'A' sbits fundingtxid
|
||||
|
||||
lock
|
||||
vins.*: normal inputs
|
||||
vout.0: CC vout for locked funds
|
||||
vout.1: normal output to unlock address
|
||||
vout.2: change
|
||||
vout.n-1: opreturn 'L' sbits fundingtxid
|
||||
|
||||
unlock
|
||||
vin.0: locked funds CC vout.0 from lock
|
||||
vin.1+: funding CC vout.0 from 'F' and 'A' and 'U'
|
||||
vout.0: funding CC change
|
||||
vout.1: normal output to unlock address
|
||||
vout.n-1: opreturn 'U' sbits fundingtxid
|
||||
|
||||
*/
|
||||
|
||||
/// the following are compatible with windows
|
||||
/// mpz_set_lli sets a long long singed int to a big num mpz_t for very large integer math
|
||||
extern void mpz_set_lli( mpz_t rop, long long op );
|
||||
// mpz_get_si2 gets a mpz_t and returns a signed long long int
|
||||
extern int64_t mpz_get_si2( mpz_t op );
|
||||
// mpz_get_ui2 gets a mpz_t and returns a unsigned long long int
|
||||
extern uint64_t mpz_get_ui2( mpz_t op );
|
||||
|
||||
uint64_t RewardsCalc(int64_t amount, uint256 txid, int64_t APR, int64_t minseconds, int64_t maxseconds, uint32_t timestamp)
|
||||
{
|
||||
int32_t numblocks; int64_t duration; uint64_t reward = 0;
|
||||
//fprintf(stderr,"minseconds %llu maxseconds %llu\n",(long long)minseconds,(long long)maxseconds);
|
||||
if ( (duration= CCduration(numblocks,txid)) < minseconds )
|
||||
{
|
||||
fprintf(stderr,"duration %lli < minseconds %lli\n",(long long)duration,(long long)minseconds);
|
||||
return(0);
|
||||
//duration = (uint32_t)time(NULL) - (1532713903 - 3600 * 24);
|
||||
} else if ( duration > maxseconds )
|
||||
duration = maxseconds;
|
||||
/* if ( 0 ) // amount * APR * duration / COIN * 100 * 365*24*3600
|
||||
reward = (((amount * APR) / COIN) * duration) / (365*24*3600LL * 100);
|
||||
else reward = (((amount * duration) / (365 * 24 * 3600LL)) * (APR / 1000000)) / 10000;
|
||||
*/
|
||||
if ( !hush_hardfork_active(timestamp) )
|
||||
reward = (((amount * duration) / (365 * 24 * 3600LL)) * (APR / 1000000)) / 10000;
|
||||
else
|
||||
{
|
||||
// declare and init the mpz_t big num variables
|
||||
mpz_t mpzAmount, mpzDuration, mpzReward, mpzAPR, mpzModifier;
|
||||
mpz_init(mpzAmount);
|
||||
mpz_init(mpzDuration);
|
||||
mpz_init(mpzAPR);
|
||||
mpz_init(mpzReward);
|
||||
mpz_init(mpzModifier);
|
||||
|
||||
// set the inputs to big num variables
|
||||
mpz_set_lli(mpzAmount, amount);
|
||||
mpz_set_lli(mpzDuration, duration);
|
||||
mpz_set_lli(mpzAPR, APR);
|
||||
mpz_set_lli(mpzModifier, COIN*100*365*24*3600LL);
|
||||
|
||||
// (amount * APR * duration)
|
||||
mpz_mul(mpzReward, mpzAmount, mpzDuration);
|
||||
mpz_mul(mpzReward, mpzReward, mpzAPR);
|
||||
|
||||
// total_of_above / (COIN * 100 * 365*24*3600LL)
|
||||
mpz_tdiv_q(mpzReward, mpzReward, mpzModifier);
|
||||
|
||||
// set result to variable we can use and return it.
|
||||
reward = mpz_get_ui2(mpzReward);
|
||||
}
|
||||
if ( reward > amount )
|
||||
reward = amount;
|
||||
fprintf(stderr, "amount.%lli duration.%lli APR.%lli reward.%llu\n", (long long)amount, (long long)duration, (long long)APR, (long long)reward);
|
||||
//fprintf(stderr,"amount %.8f %.8f %llu -> duration.%llu reward %.8f vals %.8f %.8f\n",(double)amount/COIN,((double)amount * APR)/COIN,(long long)((amount * APR) / (COIN * 365*24*3600)),(long long)duration,(double)reward/COIN,(double)((amount * duration) / (365 * 24 * 3600LL))/COIN,(double)(((amount * duration) / (365 * 24 * 3600LL)) * (APR / 1000000))/COIN);
|
||||
return(reward);
|
||||
}
|
||||
|
||||
CScript EncodeRewardsFundingOpRet(uint8_t funcid,uint64_t sbits,uint64_t APR,uint64_t minseconds,uint64_t maxseconds,uint64_t mindeposit)
|
||||
{
|
||||
CScript opret; uint8_t evalcode = EVAL_REWARDS;
|
||||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << APR << minseconds << maxseconds << mindeposit);
|
||||
return(opret);
|
||||
}
|
||||
|
||||
uint8_t DecodeRewardsFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
|
||||
{
|
||||
std::vector<uint8_t> vopret; uint8_t *script,e,f;
|
||||
GetOpReturnData(scriptPubKey, vopret);
|
||||
script = (uint8_t *)vopret.data();
|
||||
if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
|
||||
{
|
||||
if ( e == EVAL_REWARDS && f == 'F' )
|
||||
return(f);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
CScript EncodeRewardsOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid)
|
||||
{
|
||||
CScript opret; uint8_t evalcode = EVAL_REWARDS;
|
||||
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid);
|
||||
return(opret);
|
||||
}
|
||||
|
||||
uint8_t DecodeRewardsOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid)
|
||||
{
|
||||
std::vector<uint8_t> vopret; uint8_t *script,e,f,funcid; uint64_t APR,minseconds,maxseconds,mindeposit;
|
||||
GetOpReturnData(scriptPubKey, vopret);
|
||||
if ( vopret.size() > 2 )
|
||||
{
|
||||
script = (uint8_t *)vopret.data();
|
||||
if ( script[0] == EVAL_REWARDS )
|
||||
{
|
||||
if ( script[1] == 'F' )
|
||||
{
|
||||
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> APR; ss >> minseconds; ss >> maxseconds; ss >> mindeposit) != 0 )
|
||||
{
|
||||
fundingtxid = txid;
|
||||
return('F');
|
||||
} else fprintf(stderr,"unmarshal error for F\n");
|
||||
}
|
||||
else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid) != 0 )
|
||||
{
|
||||
if ( e == EVAL_REWARDS && (f == 'L' || f == 'U' || f == 'A') )
|
||||
return(f);
|
||||
else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f);
|
||||
}
|
||||
} else fprintf(stderr,"script[0] %02x != EVAL_REWARDS\n",script[0]);
|
||||
} else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size());
|
||||
return(0);
|
||||
}
|
||||
|
||||
int64_t IsRewardsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid)
|
||||
{
|
||||
char destaddr[64]; uint64_t sbits; uint256 fundingtxid,txid; uint8_t funcid; int32_t numvouts;
|
||||
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 && (numvouts= (int32_t)tx.vout.size()) > 0 )
|
||||
{
|
||||
txid = tx.GetHash();
|
||||
if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 && sbits == refsbits && (fundingtxid == reffundingtxid || txid == reffundingtxid) )
|
||||
{
|
||||
|
||||
if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
|
||||
return(tx.vout[v].nValue);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool RewardsExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t txfee,uint64_t refsbits,uint256 reffundingtxid)
|
||||
{
|
||||
static uint256 zerohash;
|
||||
CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
|
||||
{
|
||||
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
|
||||
return eval->Invalid("always should find vin, but didnt");
|
||||
else
|
||||
{
|
||||
if ( hashBlock == zerohash )
|
||||
return eval->Invalid("cant rewards from mempool");
|
||||
if ( (assetoshis= IsRewardsvout(cp,vinTx,tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 )
|
||||
inputs += assetoshis;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i<numvouts; i++)
|
||||
{
|
||||
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
|
||||
if ( (assetoshis= IsRewardsvout(cp,tx,i,refsbits,reffundingtxid)) != 0 )
|
||||
outputs += assetoshis;
|
||||
}
|
||||
if ( inputs != outputs+txfee )
|
||||
{
|
||||
fprintf(stderr,"inputs %llu vs outputs %llu txfee %llu\n",(long long)inputs,(long long)outputs,(long long)txfee);
|
||||
return eval->Invalid("mismatched inputs != outputs + txfee");
|
||||
}
|
||||
else return(true);
|
||||
}
|
||||
|
||||
bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
|
||||
{
|
||||
uint256 txid,fundingtxid,hashBlock,vinfundingtxid; uint64_t vinsbits,sbits,APR,minseconds,maxseconds,mindeposit,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx;
|
||||
int64_t dummy;
|
||||
numvins = tx.vin.size();
|
||||
numvouts = tx.vout.size();
|
||||
preventCCvins = preventCCvouts = -1;
|
||||
if ( numvouts < 1 )
|
||||
return eval->Invalid("no vouts");
|
||||
else
|
||||
{
|
||||
txid = tx.GetHash();
|
||||
if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 )
|
||||
{
|
||||
if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 )
|
||||
return eval->Invalid("cant find fundingtxid");
|
||||
else if ( fundingTx.vout.size() > 0 && DecodeRewardsFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 'F' )
|
||||
return eval->Invalid("fundingTx not valid");
|
||||
if ( APR > REWARDSCC_MAXAPR )
|
||||
return eval->Invalid("excessive APR");
|
||||
switch ( funcid )
|
||||
{
|
||||
case 'F':
|
||||
//vins.*: normal inputs
|
||||
//vout.0: CC vout for funding
|
||||
//vout.1: normal marker vout for easy searching
|
||||
//vout.2: normal change
|
||||
//vout.n-1: opreturn 'F' sbits APR minseconds maxseconds mindeposit
|
||||
return eval->Invalid("unexpected RewardsValidate for createfunding");
|
||||
break;
|
||||
case 'A':
|
||||
//vins.*: normal inputs
|
||||
//vout.0: CC vout for funding
|
||||
//vout.1: normal change
|
||||
//vout.n-1: opreturn 'A' sbits fundingtxid
|
||||
return eval->Invalid("unexpected RewardsValidate for addfunding");
|
||||
break;
|
||||
case 'L':
|
||||
//vins.*: normal inputs
|
||||
//vout.0: CC vout for locked funds
|
||||
//vout.1: normal output to unlock address
|
||||
//vout.2: change
|
||||
//vout.n-1: opreturn 'L' sbits fundingtxid
|
||||
return eval->Invalid("unexpected RewardsValidate for lock");
|
||||
break;
|
||||
case 'U':
|
||||
//vin.0: locked funds CC vout.0 from lock
|
||||
//vin.1+: funding CC vout.0 from 'F' and 'A' and 'U'
|
||||
//vout.0: funding CC change or recover normal payout
|
||||
//vout.1: normal output to unlock address
|
||||
//vout.n-1: opreturn 'U' sbits fundingtxid
|
||||
if ( fundingtxid == tx.vin[0].prevout.hash )
|
||||
return eval->Invalid("cant unlock fundingtxid");
|
||||
else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 )
|
||||
return eval->Invalid("always should find vin.0, but didnt");
|
||||
else if ( DecodeRewardsOpRet(tx.vin[0].prevout.hash,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid) != 'L' )
|
||||
return eval->Invalid("can only unlock locktxid");
|
||||
else if ( fundingtxid != vinfundingtxid )
|
||||
return eval->Invalid("mismatched vinfundingtxid");
|
||||
for (i=0; i<numvins; i++)
|
||||
{
|
||||
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 )
|
||||
return eval->Invalid("unexpected normal vin for unlock");
|
||||
}
|
||||
if ( !CheckTxFee(tx, txfee, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime, dummy) )
|
||||
return eval->Invalid("txfee is too high");
|
||||
amount = vinTx.vout[0].nValue;
|
||||
reward = RewardsCalc((int64_t)amount,tx.vin[0].prevout.hash,(int64_t)APR,(int64_t)minseconds,(int64_t)maxseconds,GetLatestTimestamp(eval->GetCurrentHeight()));
|
||||
if ( reward == 0 )
|
||||
return eval->Invalid("no eligible rewards");
|
||||
if ( numvins == 1 && tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
{
|
||||
if ( tx.vout[1].nValue != 10000 )
|
||||
return eval->Invalid("wrong marker vout value");
|
||||
else if ( tx.vout[1].scriptPubKey != tx.vout[0].scriptPubKey )
|
||||
return eval->Invalid("unlock recover tx vout.1 mismatched scriptPubKey");
|
||||
else if ( tx.vout[0].scriptPubKey != vinTx.vout[1].scriptPubKey )
|
||||
return eval->Invalid("unlock recover tx vout.0 mismatched scriptPubKey");
|
||||
else if ( tx.vout[0].nValue > vinTx.vout[0].nValue )
|
||||
return eval->Invalid("unlock recover tx vout.0 mismatched amounts");
|
||||
else if ( tx.vout[2].nValue > 0 )
|
||||
return eval->Invalid("unlock recover tx vout.1 nonz amount");
|
||||
else return(true);
|
||||
}
|
||||
if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
return eval->Invalid("unlock tx vout.0 is normal output");
|
||||
else if ( numvouts != 3 )
|
||||
return eval->Invalid("unlock tx wrong number of vouts");
|
||||
else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
return eval->Invalid("unlock tx vout.0 is normal output");
|
||||
else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
return eval->Invalid("unlock tx vout.1 is CC output");
|
||||
else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey )
|
||||
return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey");
|
||||
if ( RewardsExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue,sbits,fundingtxid) == 0 )
|
||||
return false;
|
||||
else if ( tx.vout[1].nValue > amount+reward )
|
||||
return eval->Invalid("unlock tx vout.1 isnt amount+reward");
|
||||
else if ( tx.vout[2].nValue > 0 )
|
||||
return eval->Invalid("unlock tx vout.2 isnt 0");
|
||||
preventCCvouts = 1;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"illegal rewards funcid.(%c)\n",funcid);
|
||||
return eval->Invalid("unexpected rewards funcid");
|
||||
break;
|
||||
}
|
||||
} else return eval->Invalid("unexpected rewards missing funcid");
|
||||
return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
static uint64_t myIs_unlockedtx_inmempool(uint256 &txid,int32_t &vout,uint64_t refsbits,uint256 reffundingtxid,uint64_t needed)
|
||||
{
|
||||
uint8_t funcid; uint64_t sbits,nValue; uint256 fundingtxid; char str[65];
|
||||
memset(&txid,0,sizeof(txid));
|
||||
vout = -1;
|
||||
nValue = 0;
|
||||
BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx)
|
||||
{
|
||||
const CTransaction &tx = e.GetTx();
|
||||
if ( tx.vout.size() > 0 && tx.vout[0].nValue >= needed )
|
||||
{
|
||||
const uint256 &hash = tx.GetHash();
|
||||
if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,hash,0) == 0 )
|
||||
{
|
||||
if ( (funcid= DecodeRewardsOpRet(hash,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'U' && sbits == refsbits && fundingtxid == reffundingtxid )
|
||||
{
|
||||
txid = hash;
|
||||
vout = 0;
|
||||
nValue = tx.vout[0].nValue;
|
||||
fprintf(stderr,"found 'U' %s %.8f in unspent in mempool\n",uint256_str(str,txid),(double)nValue/COIN);
|
||||
return(nValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return(nValue);
|
||||
}
|
||||
|
||||
// 'L' vs 'F' and 'A'
|
||||
int64_t AddRewardsInputs(CScript &scriptPubKey,uint64_t maxseconds,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid)
|
||||
{
|
||||
char coinaddr[64],str[65]; uint64_t threshold,sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t numblocks,j,vout,n = 0; uint8_t funcid;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
GetCCaddress(cp,coinaddr,pk);
|
||||
SetCCunspents(unspentOutputs,coinaddr,true);
|
||||
if ( maxinputs > CC_MAXVINS )
|
||||
maxinputs = CC_MAXVINS;
|
||||
if ( maxinputs > 0 )
|
||||
threshold = total/maxinputs;
|
||||
else threshold = total;
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
vout = (int32_t)it->first.index;
|
||||
if ( it->second.satoshis < threshold )
|
||||
continue;
|
||||
//fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN);
|
||||
for (j=0; j<mtx.vin.size(); j++)
|
||||
if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
|
||||
break;
|
||||
if ( j != mtx.vin.size() )
|
||||
continue;
|
||||
if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 )
|
||||
{
|
||||
if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 )
|
||||
{
|
||||
if ( sbits != refsbits || fundingtxid != reffundingtxid )
|
||||
continue;
|
||||
if ( maxseconds == 0 && funcid != 'F' && funcid != 'A' && funcid != 'U' )
|
||||
continue;
|
||||
else if ( maxseconds != 0 && funcid != 'L' )
|
||||
{
|
||||
if ( CCduration(numblocks,txid) < maxseconds )
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr,"maxseconds.%d (%c) %.8f %.8f\n",(int32_t)maxseconds,funcid,(double)tx.vout[vout].nValue/COIN,(double)it->second.satoshis/COIN);
|
||||
if ( total != 0 && maxinputs != 0 )
|
||||
{
|
||||
if ( maxseconds != 0 )
|
||||
scriptPubKey = tx.vout[1].scriptPubKey;
|
||||
mtx.vin.push_back(CTxIn(txid,vout,CScript()));
|
||||
}
|
||||
totalinputs += it->second.satoshis;
|
||||
n++;
|
||||
if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
|
||||
break;
|
||||
} else fprintf(stderr,"null funcid\n");
|
||||
}
|
||||
}
|
||||
if ( maxseconds == 0 && totalinputs < total && (maxinputs == 0 || n < maxinputs-1) )
|
||||
{
|
||||
fprintf(stderr,"search mempool for unlocked and unspent CC rewards output for %.8f\n",(double)(total-totalinputs)/COIN);
|
||||
if ( (nValue= myIs_unlockedtx_inmempool(txid,vout,refsbits,reffundingtxid,total-totalinputs)) > 0 )
|
||||
{
|
||||
mtx.vin.push_back(CTxIn(txid,vout,CScript()));
|
||||
fprintf(stderr,"added mempool vout for %.8f\n",(double)nValue/COIN);
|
||||
totalinputs += nValue;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return(totalinputs);
|
||||
}
|
||||
|
||||
int64_t RewardsPlanFunds(uint64_t &lockedfunds,uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid)
|
||||
{
|
||||
char coinaddr[64]; uint64_t sbits; int64_t nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
lockedfunds = 0;
|
||||
GetCCaddress(cp,coinaddr,pk);
|
||||
SetCCunspents(unspentOutputs,coinaddr,true);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
vout = (int32_t)it->first.index;
|
||||
if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 )
|
||||
{
|
||||
if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'F' || funcid == 'A' || funcid == 'U' || funcid == 'L' )
|
||||
{
|
||||
if ( refsbits == sbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid )
|
||||
{
|
||||
if ( (nValue= IsRewardsvout(cp,tx,vout,sbits,fundingtxid)) > 0 )
|
||||
{
|
||||
if ( funcid == 'L' )
|
||||
lockedfunds += nValue;
|
||||
else totalinputs += nValue;
|
||||
}
|
||||
else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN);
|
||||
} //else fprintf(stderr,"else case\n");
|
||||
} else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN);
|
||||
}
|
||||
}
|
||||
return(totalinputs);
|
||||
}
|
||||
|
||||
bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewardspk,uint64_t &APR,uint64_t &minseconds,uint64_t &maxseconds,uint64_t &mindeposit)
|
||||
{
|
||||
char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx;
|
||||
std::vector<uint256> txids;
|
||||
GetCCaddress(cp,CCaddr,rewardspk);
|
||||
SetCCtxids(txids,CCaddr,true,cp->evalcode,zeroid,'F');
|
||||
for (std::vector<uint256>::const_iterator it=txids.begin(); it!=txids.end(); it++)
|
||||
{
|
||||
//int height = it->first.blockHeight;
|
||||
txid = *it;
|
||||
if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 )
|
||||
{
|
||||
//char str[65]; fprintf(stderr,"rewards plan %s\n",uint256_str(str,txid));
|
||||
if ( DecodeRewardsFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 'F' )
|
||||
{
|
||||
if ( sbits == refsbits )
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
UniValue RewardsInfo(uint256 rewardsid)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; uint64_t lockedfunds,APR,minseconds,maxseconds,mindeposit,sbits,funding; CPubKey rewardspk; struct CCcontract_info *cp,C; char str[67],numstr[65];
|
||||
if ( myGetTransaction(rewardsid,vintx,hashBlock) == 0 )
|
||||
{
|
||||
fprintf(stderr,"cant find fundingtxid\n");
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","cant find fundingtxid"));
|
||||
return(result);
|
||||
}
|
||||
if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) == 0 )
|
||||
{
|
||||
fprintf(stderr,"fundingtxid isnt rewards creation txid\n");
|
||||
result.push_back(Pair("result","error"));
|
||||
result.push_back(Pair("error","fundingtxid isnt rewards creation txid"));
|
||||
return(result);
|
||||
}
|
||||
result.push_back(Pair("result","success"));
|
||||
result.push_back(Pair("fundingtxid",uint256_str(str,rewardsid)));
|
||||
unstringbits(str,sbits);
|
||||
result.push_back(Pair("name",str));
|
||||
result.push_back(Pair("sbits",sbits));
|
||||
sprintf(numstr,"%.8f",(double)APR/COIN);
|
||||
result.push_back(Pair("APR",numstr));
|
||||
result.push_back(Pair("minseconds",minseconds));
|
||||
result.push_back(Pair("maxseconds",maxseconds));
|
||||
sprintf(numstr,"%.8f",(double)mindeposit/COIN);
|
||||
result.push_back(Pair("mindeposit",numstr));
|
||||
cp = CCinit(&C,EVAL_REWARDS);
|
||||
rewardspk = GetUnspendable(cp,0);
|
||||
funding = RewardsPlanFunds(lockedfunds,sbits,cp,rewardspk,rewardsid);
|
||||
sprintf(numstr,"%.8f",(double)funding/COIN);
|
||||
result.push_back(Pair("funding",numstr));
|
||||
sprintf(numstr,"%.8f",(double)lockedfunds/COIN);
|
||||
result.push_back(Pair("locked",numstr));
|
||||
return(result);
|
||||
}
|
||||
|
||||
UniValue RewardsList()
|
||||
{
|
||||
UniValue result(UniValue::VARR); std::vector<uint256> txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits,APR,minseconds,maxseconds,mindeposit; char str[65];
|
||||
cp = CCinit(&C,EVAL_REWARDS);
|
||||
SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,zeroid,'F');
|
||||
for (std::vector<uint256>::const_iterator it=txids.begin(); it!=txids.end(); it++)
|
||||
{
|
||||
txid = *it;
|
||||
if ( myGetTransaction(txid,vintx,hashBlock) != 0 )
|
||||
{
|
||||
if ( vintx.vout.size() > 0 && DecodeRewardsFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,APR,minseconds,maxseconds,mindeposit) != 0 )
|
||||
{
|
||||
result.push_back(uint256_str(str,txid));
|
||||
}
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
std::string RewardsCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t APR,int64_t minseconds,int64_t maxseconds,int64_t mindeposit)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
|
||||
if ( funds < COIN || mindeposit < 0 || minseconds < 0 || maxseconds < 0 )
|
||||
{
|
||||
fprintf(stderr,"negative parameter error\n");
|
||||
return("");
|
||||
}
|
||||
if ( APR > REWARDSCC_MAXAPR )
|
||||
{
|
||||
fprintf(stderr,"25%% APR is maximum\n");
|
||||
return("");
|
||||
}
|
||||
cp = CCinit(&C,EVAL_REWARDS);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
rewardspk = GetUnspendable(cp,0);
|
||||
sbits = stringbits(planstr);
|
||||
if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) != 0 )
|
||||
{
|
||||
fprintf(stderr,"Rewards plan (%s) already exists\n",planstr);
|
||||
return("");
|
||||
}
|
||||
if ( AddNormalinputs(mtx,mypk,funds+2*txfee,64) > 0 )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,rewardspk));
|
||||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(rewardspk)) << OP_CHECKSIG));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsFundingOpRet('F',sbits,APR,minseconds,maxseconds,mindeposit)));
|
||||
}
|
||||
fprintf(stderr,"cant find enough inputs\n");
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string RewardsAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,rewardspk; CScript opret; uint64_t sbits,a,b,c,d; struct CCcontract_info *cp,C;
|
||||
if ( amount < 0 )
|
||||
{
|
||||
fprintf(stderr,"negative parameter error\n");
|
||||
return("");
|
||||
}
|
||||
cp = CCinit(&C,EVAL_REWARDS);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
rewardspk = GetUnspendable(cp,0);
|
||||
sbits = stringbits(planstr);
|
||||
if ( RewardsPlanExists(cp,sbits,rewardspk,a,b,c,d) == 0 )
|
||||
{
|
||||
CCerror = strprintf("Rewards plan %s doesnt exist\n",planstr);
|
||||
fprintf(stderr,"%s\n",CCerror.c_str());
|
||||
return("");
|
||||
}
|
||||
sbits = stringbits(planstr);
|
||||
if ( AddNormalinputs(mtx,mypk,amount+txfee,64) > 0 )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,rewardspk));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('A',sbits,fundingtxid)));
|
||||
} else {
|
||||
CCerror = "cant find enough inputs";
|
||||
fprintf(stderr,"%s\n", CCerror.c_str());
|
||||
}
|
||||
CCerror = "cant find fundingtxid";
|
||||
fprintf(stderr,"%s\n", CCerror.c_str());
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string RewardsLock(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t deposit)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CPubKey mypk,rewardspk; CScript opret; uint64_t lockedfunds,sbits,funding,APR,minseconds,maxseconds,mindeposit; struct CCcontract_info *cp,C;
|
||||
if ( deposit < txfee )
|
||||
{
|
||||
CCerror = "deposit amount less than txfee";
|
||||
fprintf(stderr,"%s\n",CCerror.c_str());
|
||||
return("");
|
||||
}
|
||||
cp = CCinit(&C,EVAL_REWARDS);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
rewardspk = GetUnspendable(cp,0);
|
||||
sbits = stringbits(planstr);
|
||||
if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
|
||||
{
|
||||
CCerror = strprintf("Rewards plan %s doesnt exist\n",planstr);
|
||||
fprintf(stderr,"%s\n",CCerror.c_str());
|
||||
return("");
|
||||
}
|
||||
if ( deposit < mindeposit )
|
||||
{
|
||||
CCerror = strprintf("Rewards plan %s deposit %.8f < mindeposit %.8f\n",planstr,(double)deposit/COIN,(double)mindeposit/COIN);
|
||||
fprintf(stderr,"%s\n",CCerror.c_str());
|
||||
return("");
|
||||
}
|
||||
if ( (funding= RewardsPlanFunds(lockedfunds,sbits,cp,rewardspk,fundingtxid)) >= deposit ) // arbitrary cmpval
|
||||
{
|
||||
if ( AddNormalinputs(mtx,mypk,deposit+2*txfee,64) > 0 )
|
||||
{
|
||||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,deposit,rewardspk));
|
||||
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
|
||||
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeRewardsOpRet('L',sbits,fundingtxid)));
|
||||
} else {
|
||||
CCerror = strprintf("cant find enough inputs %.8f not enough for %.8f, make sure you imported privkey for the -pubkey address\n",(double)funding/COIN,(double)deposit/COIN);
|
||||
fprintf(stderr,"%s\n",CCerror.c_str());
|
||||
}
|
||||
}
|
||||
fprintf(stderr,"cant find rewards inputs funding %.8f locked %.8f vs deposit %.8f\n",(double)funding/COIN,(double)lockedfunds/COIN,(double)deposit/COIN);
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 locktxid)
|
||||
{
|
||||
CMutableTransaction firstmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight());
|
||||
CTransaction tx; char coinaddr[64]; CPubKey mypk,rewardspk; CScript scriptPubKey,ignore; uint256 hashBlock; uint64_t sbits,APR,minseconds,maxseconds,mindeposit; int64_t funding,reward=0,amount=0,inputs,CCchange=0; struct CCcontract_info *cp,C;
|
||||
cp = CCinit(&C,EVAL_REWARDS);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
rewardspk = GetUnspendable(cp,0);
|
||||
mypk = pubkey2pk(Mypubkey());
|
||||
sbits = stringbits(planstr);
|
||||
if ( locktxid == fundingtxid )
|
||||
{
|
||||
fprintf(stderr,"Rewards plan cant unlock fundingtxid\n");
|
||||
CCerror = "Rewards plan cant unlock fundingtxid";
|
||||
return("");
|
||||
}
|
||||
if ( RewardsPlanExists(cp,sbits,rewardspk,APR,minseconds,maxseconds,mindeposit) == 0 )
|
||||
{
|
||||
fprintf(stderr,"Rewards plan %s doesnt exist\n",planstr);
|
||||
CCerror = "Rewards plan does not exist";
|
||||
return("");
|
||||
}
|
||||
fprintf(stderr,"APR %.8f minseconds.%llu maxseconds.%llu mindeposit %.8f\n",(double)APR/COIN,(long long)minseconds,(long long)maxseconds,(double)mindeposit/COIN);
|
||||
if ( locktxid == zeroid )
|
||||
amount = AddRewardsInputs(scriptPubKey,maxseconds,cp,mtx,rewardspk,(1LL << 30),1,sbits,fundingtxid);
|
||||
else
|
||||
{
|
||||
GetCCaddress(cp,coinaddr,rewardspk);
|
||||
if ( (amount= CCutxovalue(coinaddr,locktxid,0,1)) == 0 )
|
||||
{
|
||||
fprintf(stderr,"%s locktxid/v0 is spent\n",coinaddr);
|
||||
CCerror = "locktxid/v0 is spent";
|
||||
return("");
|
||||
}
|
||||
if ( myGetTransaction(locktxid,tx,hashBlock) != 0 && tx.vout.size() > 0 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
|
||||
{
|
||||
scriptPubKey = tx.vout[1].scriptPubKey;
|
||||
mtx.vin.push_back(CTxIn(locktxid,0,CScript()));
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr,"%s no normal vout.1 in locktxid\n",coinaddr);
|
||||
CCerror = "no normal vout.1 in locktxid";
|
||||
return("");
|
||||
}
|
||||
}
|
||||
if ( amount > txfee )
|
||||
{
|
||||
reward = RewardsCalc((int64_t)amount,mtx.vin[0].prevout.hash,(int64_t)APR,(int64_t)minseconds,(int64_t)maxseconds,hush_chainactive_timestamp());
|
||||
if ( scriptPubKey.size() > 0 )
|
||||
{
|
||||
if ( reward > txfee )
|
||||
{
|
||||
firstmtx = mtx;
|
||||
if ( (inputs= AddRewardsInputs(ignore,0,cp,mtx,rewardspk,reward+txfee,30,sbits,fundingtxid)) >= reward+txfee )
|
||||
{
|
||||
if ( inputs >= (reward + 2*txfee) )
|
||||
CCchange = (inputs - (reward + txfee));
|
||||
fprintf(stderr,"inputs %.8f CCchange %.8f amount %.8f reward %.8f\n",(double)inputs/COIN,(double)CCchange/COIN,(double)amount/COIN,(double)reward/COIN);
|
||||
mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,rewardspk));
|
||||
mtx.vout.push_back(CTxOut(amount+reward,scriptPubKey));
|
||||
return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid)));
|
||||
}
|
||||
else
|
||||
{
|
||||
firstmtx.vout.push_back(CTxOut(amount-txfee*2,scriptPubKey));
|
||||
fprintf(stderr,"not enough rewards funds to payout %.8f, recover mode tx\n",(double)(reward+txfee)/COIN);
|
||||
return(FinalizeCCTx(-1LL,cp,firstmtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CCerror = strprintf("reward %.8f is <= the transaction fee", reward);
|
||||
fprintf(stderr,"%s\n", CCerror.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CCerror = "invalid scriptPubKey";
|
||||
fprintf(stderr,"%s\n", CCerror.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CCerror = "amount must be more than txfee";
|
||||
fprintf(stderr,"%s\n", CCerror.c_str());
|
||||
}
|
||||
fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN);
|
||||
return("");
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
Rogue: Exploring the Dungeons of Doom
|
||||
Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name(s) of the author(s) nor the names of other contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
===========================================================================
|
||||
|
||||
Portions of this software (state.c, mdport.c) are based on the work
|
||||
of Nicholas J. Kisseberth. Used under license:
|
||||
|
||||
Copyright (C) 1999, 2000, 2005 Nicholas J. Kisseberth
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name(s) of the author(s) nor the names of other contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
===========================================================================
|
||||
|
||||
Portions of this software (xcrypt.c) are based on the work
|
||||
of David Burren. Used under license:
|
||||
|
||||
FreeSec: libcrypt
|
||||
|
||||
Copyright (C) 1994 David Burren
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name(s) of the author(s) nor the names of other contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
@@ -1,225 +0,0 @@
|
||||
# Copyright (c) 2016-2023 The Hush developers
|
||||
# Distributed under the GPLv3 software license, see the accompanying
|
||||
# file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
###############################################################################
|
||||
#
|
||||
# Makefile for rogue
|
||||
#
|
||||
# Rogue: Exploring the Dungeons of Doom
|
||||
# Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
# All rights reserved.
|
||||
#
|
||||
# See the file LICENSE.TXT for full copyright and licensing information.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
###############################################################################
|
||||
# Site configuration occurs beneath this comment
|
||||
# Typically ./configure (autoconf tools) configures this section
|
||||
# This section could be manually configured if autoconf/configure fails
|
||||
###############################################################################
|
||||
|
||||
DISTNAME=@PACKAGE_TARNAME@@PACKAGE_VERSION@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@-@PACKAGE_VERSION@
|
||||
PROGRAM=@PROGRAM@
|
||||
|
||||
O=o
|
||||
|
||||
#CC=gcc
|
||||
CC = @CC@
|
||||
|
||||
#CFLAGS=-O2
|
||||
CFLAGS= @CFLAGS@ -fPIC
|
||||
|
||||
LIBS=-lcurses -lcurl
|
||||
#LIBS = @LIBS@
|
||||
|
||||
#RM=rm -f
|
||||
RM = rm -f
|
||||
|
||||
#GROFF=groff
|
||||
GROFF = @GROFF@
|
||||
|
||||
#NROFF=nroff
|
||||
NROFF = @NROFF@
|
||||
|
||||
#TBL=tbl
|
||||
TBL = @TBL@
|
||||
|
||||
#COLCRT=colcrt
|
||||
COLCRT = @COLCRT@
|
||||
|
||||
#SED=sed
|
||||
SED = @SED@
|
||||
|
||||
#SCOREFILE=rogue54.scr
|
||||
SCOREFILE = @SCOREFILE@
|
||||
|
||||
#LOCKFILE=rogue54.lck
|
||||
LOCKFILE = @LOCKFILE@
|
||||
|
||||
#GROUPOWNER=games
|
||||
GROUPOWNER = @GROUPOWNER@
|
||||
|
||||
#CPPFLAGS=-DHAVE_CONFIG_H
|
||||
CPPFLAGS =@DEFS@ @CPPFLAGS@
|
||||
|
||||
#DISTFILE = $(PROGRAM)
|
||||
DISTFILE = $(DISTNAME)-@TARGET@
|
||||
|
||||
INSTALL=./install-sh
|
||||
|
||||
#INSTGROUP=-g games
|
||||
INSTGROUP=
|
||||
#INSTOWNER=-u root
|
||||
INSTOWNER=
|
||||
|
||||
CHGRP=chgrp
|
||||
|
||||
MKDIR=mkdir
|
||||
|
||||
TOUCH=touch
|
||||
|
||||
RMDIR=rmdir
|
||||
|
||||
CHMOD=chmod
|
||||
|
||||
DESTDIR=
|
||||
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
datarootdir=@datarootdir@
|
||||
datadir=@datadir@
|
||||
bindir=@bindir@
|
||||
mandir=@mandir@
|
||||
docdir=@docdir@
|
||||
man6dir = $(mandir)/man6
|
||||
|
||||
###############################################################################
|
||||
# Site configuration occurs above this comment
|
||||
# It should not be necessary to change anything below this comment
|
||||
###############################################################################
|
||||
|
||||
HDRS = rogue.h extern.h score.h
|
||||
OBJS1 = vers.$(O) extern.$(O) armor.$(O) chase.$(O) command.$(O) \
|
||||
daemon.$(O) daemons.$(O) fight.$(O) init.$(O) io.$(O) list.$(O) \
|
||||
mach_dep.$(O) rogue.$(O) mdport.$(O) misc.$(O) monsters.$(O) \
|
||||
move.$(O) new_level.$(O)
|
||||
OBJS2 = options.$(O) pack.$(O) passages.$(O) potions.$(O) rings.$(O) \
|
||||
rip.$(O) rooms.$(O) save.$(O) scrolls.$(O) state.$(O) sticks.$(O) \
|
||||
things.$(O) weapons.$(O) wizard.$(O) xcrypt.$(O)
|
||||
OBJS = main.$(O) $(OBJS1) $(OBJS2)
|
||||
CFILES = vers.c extern.c armor.c chase.c command.c daemon.c \
|
||||
daemons.c fight.c init.c io.c list.c mach_dep.c \
|
||||
main.c mdport.c misc.c monsters.c move.c new_level.c \
|
||||
options.c pack.c passages.c potions.c rings.c rip.c \
|
||||
rooms.c save.c scrolls.c state.c sticks.c things.c \
|
||||
weapons.c wizard.c xcrypt.c
|
||||
MISC_C = findpw.c scedit.c scmisc.c
|
||||
DOCSRC = rogue.me.in rogue.6.in rogue.doc.in rogue.html.in rogue.cat.in
|
||||
DOCS = $(PROGRAM).doc $(PROGRAM).html $(PROGRAM).cat $(PROGRAM).me \
|
||||
$(PROGRAM).6
|
||||
AFILES = configure Makefile.in configure.ac config.h.in config.sub config.guess \
|
||||
install-sh rogue.6.in rogue.me.in rogue.html.in rogue.doc.in rogue.cat.in
|
||||
MISC = Makefile.std LICENSE.TXT rogue54.sln rogue54.vcproj rogue.spec \
|
||||
rogue.png rogue.desktop
|
||||
|
||||
.SUFFIXES: .obj
|
||||
|
||||
.c.obj:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) /c $*.c
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $*.c
|
||||
|
||||
#$(RM) rogue.so ; $(CC) -shared -o rogue.so cursesd.c $(OBJS1) $(OBJS2); $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
|
||||
|
||||
$(PROGRAM): $(HDRS) $(OBJS)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS1); $(RM) main.$(O) ; $(RM) rogue.so
|
||||
$(RM) $(OBJS2)
|
||||
$(RM) core a.exe a.out a.exe.stackdump $(PROGRAM) $(PROGRAM).exe
|
||||
$(RM) $(PROGRAM).tar $(PROGRAM).tar.gz $(PROGRAM).zip
|
||||
$(RM) $(DISTNAME)/*
|
||||
-rmdir $(DISTNAME)
|
||||
|
||||
maintainer-clean:
|
||||
$(RM) config.h
|
||||
$(RM) Makefile
|
||||
$(RM) config.status
|
||||
$(RM) -r autom4te.cache
|
||||
$(RM) config.log
|
||||
$(RM) $(PROGRAM).scr $(PROGRAM).lck
|
||||
|
||||
stddocs:
|
||||
sed -e 's/@PROGRAM@/rogue/' -e 's/@SCOREFILE@/rogue.scr/' rogue.6.in > rogue.6
|
||||
sed -e 's/@PROGRAM@/rogue/' -e 's/@SCOREFILE@/rogue.scr/' rogue.me.in > rogue.me
|
||||
sed -e 's/@PROGRAM@/rogue/' -e 's/@SCOREFILE@/rogue.scr/' rogue.html.in > rogue,html
|
||||
sed -e 's/@PROGRAM@/rogue/' -e 's/@SCOREFILE@/rogue.scr/' rogue.doc.in > rogue.doc
|
||||
sed -e 's/@PROGRAM@/rogue/' -e 's/@SCOREFILE@/rogue.scr/' rogue.cat.in > rogue.cat
|
||||
|
||||
dist.src:
|
||||
$(MAKE) $(MAKEFILE) clean
|
||||
mkdir $(DISTNAME)
|
||||
cp $(CFILES) $(HDRS) $(MISC) $(AFILES) $(DISTNAME)
|
||||
tar cf $(DISTNAME)-src.tar $(DISTNAME)
|
||||
gzip -f $(DISTNAME)-src.tar
|
||||
rm -fr $(DISTNAME)
|
||||
|
||||
findpw: findpw.c xcrypt.o mdport.o xcrypt.o
|
||||
$(CC) -s -o findpw findpw.c xcrypt.o mdport.o -lcurses
|
||||
|
||||
scedit: scedit.o scmisc.o vers.o mdport.o xcrypt.o
|
||||
$(CC) -s -o scedit vers.o scedit.o scmisc.o mdport.o xcrypt.o -lcurses
|
||||
|
||||
scmisc.o scedit.o:
|
||||
$(CC) -O -c $(SF) $*.c
|
||||
|
||||
$(PROGRAM).doc: rogue.me
|
||||
if test "x$(GROFF)" != "x" -a "x$(SED)" != "x" ; then \
|
||||
$(GROFF) -P-c -t -me -Tascii rogue.me | $(SED) -e 's/.\x08//g' > $(PROGRAM).doc ;\
|
||||
elif test "x$(NROFF)" != "x" -a "x$(TBL)" != "x" -a "x$(COLCRT)" != "x" ; then \
|
||||
tbl rogue.me | $(NROFF) -me | colcrt - > $(PROGRAM).doc ;\
|
||||
fi
|
||||
|
||||
$(PROGRAM).cat: rogue.6
|
||||
if test "x$(GROFF)" != "x" -a "x$(SED)" != "x" ; then \
|
||||
$(GROFF) -Tascii -man rogue.6 | $(SED) -e 's/.\x08//g' > $(PROGRAM).cat ;\
|
||||
elif test "x$(NROFF)" != "x" -a "x$(TBL)" != "x" -a "x$(COLCRT)" != "x" ; then \
|
||||
$(NROFF) -man rogue.6 | $(COLCRT) - > $(PROGRAM).cat ;\
|
||||
fi
|
||||
|
||||
dist: clean $(PROGRAM)
|
||||
tar cf $(DISTFILE).tar $(PROGRAM) LICENSE.TXT $(DOCS)
|
||||
gzip -f $(DISTFILE).tar
|
||||
|
||||
install: $(PROGRAM)
|
||||
-$(TOUCH) test
|
||||
-if test ! -f $(DESTDIR)$(SCOREFILE) ; then $(INSTALL) -m 0664 test $(DESTDIR)$(SCOREFILE) ; fi
|
||||
-$(INSTALL) -m 0755 $(PROGRAM) $(DESTDIR)$(bindir)/$(PROGRAM)
|
||||
-if test "x$(GROUPOWNER)" != "x" ; then \
|
||||
$(CHGRP) $(GROUPOWNER) $(DESTDIR)$(SCOREFILE) ; \
|
||||
$(CHGRP) $(GROUPOWNER) $(DESTDIR)$(bindir)/$(PROGRAM) ; \
|
||||
$(CHMOD) 02755 $(DESTDIR)$(bindir)/$(PROGRAM) ; \
|
||||
$(CHMOD) 0464 $(DESTDIR)$(SCOREFILE) ; \
|
||||
fi
|
||||
-if test -d $(man6dir) ; then $(INSTALL) -m 0644 rogue.6 $(DESTDIR)$(man6dir)/$(PROGRAM).6 ; fi
|
||||
-if test ! -d $(man6dir) ; then $(INSTALL) -m 0644 rogue.6 $(DESTDIR)$(mandir)/$(PROGRAM).6 ; fi
|
||||
-$(INSTALL) -m 0644 rogue.doc $(DESTDIR)$(docdir)/$(PROGRAM).doc
|
||||
-$(INSTALL) -m 0644 rogue.html $(DESTDIR)$(docdir)/$(PROGRAM).html
|
||||
-$(INSTALL) -m 0644 rogue.cat $(DESTDIR)$(docdir)/$(PROGRAM).cat
|
||||
-$(INSTALL) -m 0644 LICENSE.TXT $(DESTDIR)$(docdir)/LICENSE.TXT
|
||||
-$(INSTALL) -m 0644 rogue.me $(DESTDIR)$(docdir)/$(PROGRAM).me
|
||||
-if test ! -f $(DESTDIR)$(LOCKFILE) ; then $(INSTALL) -m 0666 test $(DESTDIR)$(LOCKFILE) ; $(RM) $(DESTDIR)$(LOCKFILE) ; fi
|
||||
-$(RM) test
|
||||
|
||||
uninstall:
|
||||
-$(RM) $(DESTDIR)$(bindir)/$(PROGRAM)
|
||||
-$(RM) $(DESTDIR)$(man6dir)/$(PROGRAM).6
|
||||
-$(RM) $(DESTDIR)$(docdir)$(PROGRAM)/$(PROGRAM).doc
|
||||
-$(RM) $(DESTDIR)$(LOCKFILE)
|
||||
-$(RMDIR) $(DESTDIR)$(docdir)$(PROGRAM)
|
||||
|
||||
reinstall: uninstall install
|
||||
@@ -1,158 +0,0 @@
|
||||
#
|
||||
# Makefile for rogue
|
||||
# @(#)Makefile 4.21 (Berkeley) 02/04/99
|
||||
#
|
||||
# Rogue: Exploring the Dungeons of Doom
|
||||
# Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
# All rights reserved.
|
||||
#
|
||||
# See the file LICENSE.TXT for full copyright and licensing information.
|
||||
#
|
||||
|
||||
DISTNAME = rogue5.4.4
|
||||
PROGRAM = rogue54
|
||||
O = o
|
||||
HDRS = rogue.h extern.h score.h
|
||||
OBJS1 = vers.$(O) extern.$(O) armor.$(O) chase.$(O) command.$(O) \
|
||||
daemon.$(O) daemons.$(O) fight.$(O) init.$(O) io.$(O) list.$(O) \
|
||||
mach_dep.$(O) main.$(O) mdport.$(O) misc.$(O) monsters.$(O) \
|
||||
move.$(O) new_level.$(O)
|
||||
OBJS2 = options.$(O) pack.$(O) passages.$(O) potions.$(O) rings.$(O) \
|
||||
rip.$(O) rooms.$(O) save.$(O) scrolls.$(O) state.$(O) sticks.$(O) \
|
||||
things.$(O) weapons.$(O) wizard.$(O) xcrypt.$(O)
|
||||
OBJS = $(OBJS1) $(OBJS2)
|
||||
CFILES = vers.c extern.c armor.c chase.c command.c daemon.c \
|
||||
daemons.c fight.c init.c io.c list.c mach_dep.c \
|
||||
main.c mdport.c misc.c monsters.c move.c new_level.c \
|
||||
options.c pack.c passages.c potions.c rings.c rip.c \
|
||||
rooms.c save.c scrolls.c state.c sticks.c things.c \
|
||||
weapons.c wizard.c xcrypt.c
|
||||
MISC_C = findpw.c scedit.c scmisc.c
|
||||
DOCSRC = rogue.me.in rogue.6.in rogue.doc.in rogue.html.in rogue.cat.in
|
||||
DOCS = $(PROGRAM).doc $(PROGRAM).html $(PROGRAM).cat $(PROGRAM).me \
|
||||
$(PROGRAM).6
|
||||
AFILES = configure Makefile.in configure.ac config.h.in config.sub config.guess \
|
||||
install-sh rogue.6.in rogue.me.in rogue.html.in rogue.doc.in rogue.cat.in
|
||||
MISC = Makefile.std LICENSE.TXT rogue54.sln rogue54.vcproj rogue.spec \
|
||||
rogue.png rogue.desktop
|
||||
CC = gcc
|
||||
FEATURES = -DALLSCORES -DSCOREFILE=\"$(SCOREFILE)\" -DLOCKFILE=\"$(LOCKFILE)\"
|
||||
CPPFLAGS =
|
||||
CFLAGS = -O3
|
||||
LDFLAGS =
|
||||
LIBS = -lcurses
|
||||
RM = rm -f
|
||||
MAKEFILE = -f Makefile.std
|
||||
SCOREFILE= $(PROGRAM).scr
|
||||
LOCKFILE = $(PROGRAM).lck
|
||||
OUTFLAG = -o
|
||||
EXE =
|
||||
|
||||
.SUFFIXES: .obj
|
||||
|
||||
.c.obj:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(FEATURES) /c $*.c
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) $(FEATURES) -c $*.c
|
||||
|
||||
$(PROGRAM): $(HDRS) $(OBJS) fixdocs
|
||||
$(CC) $(LDFLAGS) $(OBJS) $(LIBS) $(OUTFLAG)$@$(EXE)
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS1)
|
||||
$(RM) $(OBJS2)
|
||||
$(RM) core a.exe a.out a.exe.stackdump $(PROGRAM) $(PROGRAM).exe $(PROGRAM).lck
|
||||
$(RM) $(PROGRAM).tar $(PROGRAM).tar.gz $(PROGRAM).zip
|
||||
$(RM) $(DISTNAME)/*
|
||||
|
||||
dist.src:
|
||||
$(MAKE) $(MAKEFILE) clean
|
||||
mkdir $(DISTNAME)
|
||||
cp $(CFILES) $(HDRS) $(MISC) $(AFILES) $(DISTNAME)
|
||||
tar cf $(DISTNAME)-src.tar $(DISTNAME)
|
||||
gzip -f $(DISTNAME)-src.tar
|
||||
rm -fr $(DISTNAME)
|
||||
|
||||
findpw: findpw.c xcrypt.o mdport.o xcrypt.o
|
||||
$(CC) -s -o findpw findpw.c xcrypt.o mdport.o -lcurses
|
||||
|
||||
scedit: scedit.o scmisc.o vers.o mdport.o xcrypt.o
|
||||
$(CC) -s -o scedit vers.o scedit.o scmisc.o mdport.o xcrypt.o -lcurses
|
||||
|
||||
scmisc.o scedit.o:
|
||||
$(CC) -O -c $(SF) $*.c
|
||||
|
||||
doc.nroff:
|
||||
tbl rogue.me | nroff -me | colcrt - > rogue.doc
|
||||
nroff -man rogue.6 | colcrt - > rogue.cat
|
||||
|
||||
doc.groff:
|
||||
groff -P-c -t -me -Tascii rogue.me | sed -e 's/.\x08//g' > rogue.doc
|
||||
groff -man rogue.6 | sed -e 's/.\x08//g' > rogue.cat
|
||||
|
||||
fixdocs:
|
||||
sed -e 's/@PROGRAM@/$(PROGRAM)/' -e 's/@SCOREFILE@/$(SCOREFILE)/' rogue.6.in > $(PROGRAM).6
|
||||
sed -e 's/@PROGRAM@/$(PROGRAM)/' -e 's/@SCOREFILE@/$(SCOREFILE)/' rogue.me.in > $(PROGRAM).me
|
||||
sed -e 's/@PROGRAM@/$(PROGRAM)/' -e 's/@SCOREFILE@/$(SCOREFILE)/' rogue.html.in > $(PROGRAM).html
|
||||
sed -e 's/@PROGRAM@/$(PROGRAM)/' -e 's/@SCOREFILE@/$(SCOREFILE)/' rogue.doc.in > $(PROGRAM).doc
|
||||
sed -e 's/@PROGRAM@/$(PROGRAM)/' -e 's/@SCOREFILE@/$(SCOREFILE)/' rogue.cat.in > $(PROGRAM).cat
|
||||
|
||||
dist.irix:
|
||||
$(MAKE) $(MAKEFILE) clean
|
||||
$(MAKE) $(MAKEFILE) CC=cc $(PROGRAM)
|
||||
tar cf $(DISTNAME)-irix.tar $(PROGRAM) LICENSE.TXT $(DOCS)
|
||||
gzip -f $(DISTNAME)-irix.tar
|
||||
|
||||
dist.aix:
|
||||
$(MAKE) $(MAKEFILE) clean
|
||||
$(MAKE) $(MAKEFILE) CC=xlc CFLAGS="-qmaxmem=16768 -O3 -qstrict" $(PROGRAM)
|
||||
tar cf $(DISTNAME)-aix.tar $(PROGRAM) LICENSE.TXT $(DOCS)
|
||||
gzip -f $(DISTNAME)-aix.tar
|
||||
|
||||
dist.linux:
|
||||
$(MAKE) $(MAKEFILE) clean
|
||||
$(MAKE) $(MAKEFILE) $(PROGRAM)
|
||||
tar cf $(DISTNAME)-linux.tar $(PROGRAM) LICENSE.TXT $(DOCS)
|
||||
gzip -f $(DISTNAME)-linux.tar
|
||||
|
||||
dist.interix:
|
||||
@$(MAKE) $(MAKEFILE) clean
|
||||
@$(MAKE) $(MAKEFILE) CFLAGS="-ansi" $(PROGRAM)
|
||||
tar cf $(DISTNAME)-interix.tar $(PROGRAM) LICENSE.TXT $(DOCS)
|
||||
gzip -f $(DISTNAME)-interix.tar
|
||||
|
||||
dist.cygwin:
|
||||
@$(MAKE) $(MAKEFILE) --no-print-directory clean
|
||||
@$(MAKE) $(MAKEFILE) CPPFLAGS="-I/usr/include/ncurses" --no-print-directory $(PROGRAM)
|
||||
tar cf $(DISTNAME)-cygwin.tar $(PROGRAM).exe LICENSE.TXT $(DOCS)
|
||||
gzip -f $(DISTNAME)-cygwin.tar
|
||||
|
||||
#
|
||||
# Use MINGW32-MAKE to build this target
|
||||
#
|
||||
dist.mingw32:
|
||||
@$(MAKE) $(MAKEFILE) --no-print-directory RM="cmd /c del" clean
|
||||
@$(MAKE) $(MAKEFILE) --no-print-directory CPPFLAGS="-I../pdcurses" LIBS="../pdcurses/pdcurses.a" $(PROGRAM)
|
||||
cmd /c del $(DISTNAME)-mingw32.zip
|
||||
zip $(DISTNAME)-mingw32.zip $(PROGRAM).exe LICENSE.TXT $(DOCS)
|
||||
|
||||
dist.djgpp:
|
||||
@$(MAKE) $(MAKEFILE) --no-print-directory clean
|
||||
@$(MAKE) $(MAKEFILE) --no-print-directory LDFLAGS="-L$(DJDIR)/LIB" \
|
||||
LIBS="-lpdcurses" $(PROGRAM)
|
||||
rm -f $(DISTNAME)-djgpp.zip
|
||||
zip $(DISTNAME)-djgpp.zip $(PROGRAM) LICENSE.TXT $(DOCS)
|
||||
|
||||
#
|
||||
# Use NMAKE to build this targer
|
||||
#
|
||||
|
||||
dist.win32:
|
||||
@$(MAKE) $(MAKEFILE) /NOLOGO O="obj" RM="-del" clean
|
||||
@$(MAKE) $(MAKEFILE) /NOLOGO O="obj" CC="CL" \
|
||||
LIBS="..\pdcurses\pdcurses.lib shell32.lib user32.lib Advapi32.lib" \
|
||||
EXE=".exe" OUTFLAG="/Fe" CPPFLAGS="-I..\pdcurses" \
|
||||
CFLAGS="-nologo -Ox -wd4033 -wd4716" $(PROGRAM)
|
||||
-del $(DISTNAME)-win32.zip
|
||||
zip $(DISTNAME)-win32.zip $(PROGRAM).exe LICENSE.TXT $(DOCS)
|
||||
@@ -1,222 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# Makefile for rogue
|
||||
#
|
||||
# Rogue: Exploring the Dungeons of Doom
|
||||
# Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
# All rights reserved.
|
||||
#
|
||||
# See the file LICENSE.TXT for full copyright and licensing information.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
###############################################################################
|
||||
# Site configuration occurs beneath this comment
|
||||
# Typically ./configure (autoconf tools) configures this section
|
||||
# This section could be manually configured if autoconf/configure fails
|
||||
###############################################################################
|
||||
|
||||
DISTNAME=rogue5.4.4
|
||||
PACKAGE_TARNAME = rogue-5.4.4
|
||||
PROGRAM=rogue
|
||||
|
||||
O=o
|
||||
|
||||
#CC=gcc
|
||||
CC = x86_64-w64-mingw32-gcc
|
||||
|
||||
#CFLAGS=-O2
|
||||
CFLAGS= -g -O2 -I./x86_64-w64-mingw32/include -I./x86_64-w64-mingw32/include/ncursesw
|
||||
|
||||
#LIBS=-lcurses
|
||||
LIBS = -L./x86_64-w64-mingw32/lib -lncursesw -lcurl
|
||||
|
||||
#RM=rm -f
|
||||
RM = rm -f
|
||||
|
||||
#GROFF=groff
|
||||
GROFF = groff
|
||||
|
||||
#NROFF=nroff
|
||||
NROFF = nroff
|
||||
|
||||
#TBL=tbl
|
||||
TBL = tbl
|
||||
|
||||
#COLCRT=colcrt
|
||||
COLCRT = colcrt
|
||||
|
||||
#SED=sed
|
||||
SED = sed
|
||||
|
||||
#SCOREFILE=rogue54.scr
|
||||
SCOREFILE = rogue.scr
|
||||
|
||||
#LOCKFILE=rogue54.lck
|
||||
LOCKFILE = rogue.lck
|
||||
|
||||
#GROUPOWNER=games
|
||||
GROUPOWNER =
|
||||
|
||||
#CPPFLAGS=-DHAVE_CONFIG_H
|
||||
CPPFLAGS =-DHAVE_CONFIG_H
|
||||
|
||||
#DISTFILE = $(PROGRAM)
|
||||
DISTFILE = $(DISTNAME)-x86_64-w64-mingw32
|
||||
|
||||
INSTALL=./install-sh
|
||||
|
||||
#INSTGROUP=-g games
|
||||
INSTGROUP=
|
||||
#INSTOWNER=-u root
|
||||
INSTOWNER=
|
||||
|
||||
CHGRP=chgrp
|
||||
|
||||
MKDIR=mkdir
|
||||
|
||||
TOUCH=touch
|
||||
|
||||
RMDIR=rmdir
|
||||
|
||||
CHMOD=chmod
|
||||
|
||||
DESTDIR=
|
||||
|
||||
prefix=/usr/local
|
||||
exec_prefix=${prefix}
|
||||
datarootdir=${prefix}/share
|
||||
datadir=${datarootdir}
|
||||
bindir=${exec_prefix}/bin
|
||||
mandir=${datarootdir}/man
|
||||
docdir=${datarootdir}/doc/${PACKAGE_TARNAME}
|
||||
man6dir = $(mandir)/man6
|
||||
|
||||
###############################################################################
|
||||
# Site configuration occurs above this comment
|
||||
# It should not be necessary to change anything below this comment
|
||||
###############################################################################
|
||||
|
||||
HDRS = rogue.h extern.h score.h
|
||||
OBJS1 = vers.$(O) extern.$(O) armor.$(O) chase.$(O) command.$(O) \
|
||||
daemon.$(O) daemons.$(O) fight.$(O) init.$(O) io.$(O) list.$(O) \
|
||||
mach_dep.$(O) rogue.$(O) mdport.$(O) misc.$(O) monsters.$(O) \
|
||||
move.$(O) new_level.$(O)
|
||||
OBJS2 = options.$(O) pack.$(O) passages.$(O) potions.$(O) rings.$(O) \
|
||||
rip.$(O) rooms.$(O) save.$(O) scrolls.$(O) state.$(O) sticks.$(O) \
|
||||
things.$(O) weapons.$(O) wizard.$(O) xcrypt.$(O)
|
||||
OBJS = main.$(O) $(OBJS1) $(OBJS2)
|
||||
CFILES = vers.c extern.c armor.c chase.c command.c daemon.c \
|
||||
daemons.c fight.c init.c io.c list.c mach_dep.c \
|
||||
main.c mdport.c misc.c monsters.c move.c new_level.c \
|
||||
options.c pack.c passages.c potions.c rings.c rip.c \
|
||||
rooms.c save.c scrolls.c state.c sticks.c things.c \
|
||||
weapons.c wizard.c xcrypt.c
|
||||
MISC_C = findpw.c scedit.c scmisc.c
|
||||
DOCSRC = rogue.me.in rogue.6.in rogue.doc.in rogue.html.in rogue.cat.in
|
||||
DOCS = $(PROGRAM).doc $(PROGRAM).html $(PROGRAM).cat $(PROGRAM).me \
|
||||
$(PROGRAM).6
|
||||
AFILES = configure Makefile.in configure.ac config.h.in config.sub config.guess \
|
||||
install-sh rogue.6.in rogue.me.in rogue.html.in rogue.doc.in rogue.cat.in
|
||||
MISC = Makefile.std LICENSE.TXT rogue54.sln rogue54.vcproj rogue.spec \
|
||||
rogue.png rogue.desktop
|
||||
|
||||
.SUFFIXES: .obj
|
||||
|
||||
.c.obj:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) /c $*.c
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $*.c
|
||||
|
||||
#$(RM) rogue.so ; $(CC) -shared -o rogue.so cursesd.c $(OBJS1) $(OBJS2); $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
|
||||
|
||||
$(PROGRAM): $(HDRS) $(OBJS)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@.exe
|
||||
|
||||
clean:
|
||||
$(RM) $(OBJS1); $(RM) main.$(O) ; $(RM) rogue.so
|
||||
$(RM) $(OBJS2)
|
||||
$(RM) core a.exe a.out a.exe.stackdump $(PROGRAM) $(PROGRAM).exe
|
||||
$(RM) $(PROGRAM).tar $(PROGRAM).tar.gz $(PROGRAM).zip
|
||||
$(RM) $(DISTNAME)/*
|
||||
-rmdir $(DISTNAME)
|
||||
|
||||
maintainer-clean:
|
||||
$(RM) config.h
|
||||
$(RM) Makefile
|
||||
$(RM) config.status
|
||||
$(RM) -r autom4te.cache
|
||||
$(RM) config.log
|
||||
$(RM) $(PROGRAM).scr $(PROGRAM).lck
|
||||
|
||||
stddocs:
|
||||
sed -e 's/rogue/rogue/' -e 's/rogue.scr/rogue.scr/' rogue.6.in > rogue.6
|
||||
sed -e 's/rogue/rogue/' -e 's/rogue.scr/rogue.scr/' rogue.me.in > rogue.me
|
||||
sed -e 's/rogue/rogue/' -e 's/rogue.scr/rogue.scr/' rogue.html.in > rogue,html
|
||||
sed -e 's/rogue/rogue/' -e 's/rogue.scr/rogue.scr/' rogue.doc.in > rogue.doc
|
||||
sed -e 's/rogue/rogue/' -e 's/rogue.scr/rogue.scr/' rogue.cat.in > rogue.cat
|
||||
|
||||
dist.src:
|
||||
$(MAKE) $(MAKEFILE) clean
|
||||
mkdir $(DISTNAME)
|
||||
cp $(CFILES) $(HDRS) $(MISC) $(AFILES) $(DISTNAME)
|
||||
tar cf $(DISTNAME)-src.tar $(DISTNAME)
|
||||
gzip -f $(DISTNAME)-src.tar
|
||||
rm -fr $(DISTNAME)
|
||||
|
||||
findpw: findpw.c xcrypt.o mdport.o xcrypt.o
|
||||
$(CC) -s -o findpw findpw.c xcrypt.o mdport.o -lcurses
|
||||
|
||||
scedit: scedit.o scmisc.o vers.o mdport.o xcrypt.o
|
||||
$(CC) -s -o scedit vers.o scedit.o scmisc.o mdport.o xcrypt.o -lcurses
|
||||
|
||||
scmisc.o scedit.o:
|
||||
$(CC) -O -c $(SF) $*.c
|
||||
|
||||
$(PROGRAM).doc: rogue.me
|
||||
if test "x$(GROFF)" != "x" -a "x$(SED)" != "x" ; then \
|
||||
$(GROFF) -P-c -t -me -Tascii rogue.me | $(SED) -e 's/.\x08//g' > $(PROGRAM).doc ;\
|
||||
elif test "x$(NROFF)" != "x" -a "x$(TBL)" != "x" -a "x$(COLCRT)" != "x" ; then \
|
||||
tbl rogue.me | $(NROFF) -me | colcrt - > $(PROGRAM).doc ;\
|
||||
fi
|
||||
|
||||
$(PROGRAM).cat: rogue.6
|
||||
if test "x$(GROFF)" != "x" -a "x$(SED)" != "x" ; then \
|
||||
$(GROFF) -Tascii -man rogue.6 | $(SED) -e 's/.\x08//g' > $(PROGRAM).cat ;\
|
||||
elif test "x$(NROFF)" != "x" -a "x$(TBL)" != "x" -a "x$(COLCRT)" != "x" ; then \
|
||||
$(NROFF) -man rogue.6 | $(COLCRT) - > $(PROGRAM).cat ;\
|
||||
fi
|
||||
|
||||
dist: clean $(PROGRAM)
|
||||
tar cf $(DISTFILE).tar $(PROGRAM) LICENSE.TXT $(DOCS)
|
||||
gzip -f $(DISTFILE).tar
|
||||
|
||||
install: $(PROGRAM)
|
||||
-$(TOUCH) test
|
||||
-if test ! -f $(DESTDIR)$(SCOREFILE) ; then $(INSTALL) -m 0664 test $(DESTDIR)$(SCOREFILE) ; fi
|
||||
-$(INSTALL) -m 0755 $(PROGRAM) $(DESTDIR)$(bindir)/$(PROGRAM)
|
||||
-if test "x$(GROUPOWNER)" != "x" ; then \
|
||||
$(CHGRP) $(GROUPOWNER) $(DESTDIR)$(SCOREFILE) ; \
|
||||
$(CHGRP) $(GROUPOWNER) $(DESTDIR)$(bindir)/$(PROGRAM) ; \
|
||||
$(CHMOD) 02755 $(DESTDIR)$(bindir)/$(PROGRAM) ; \
|
||||
$(CHMOD) 0464 $(DESTDIR)$(SCOREFILE) ; \
|
||||
fi
|
||||
-if test -d $(man6dir) ; then $(INSTALL) -m 0644 rogue.6 $(DESTDIR)$(man6dir)/$(PROGRAM).6 ; fi
|
||||
-if test ! -d $(man6dir) ; then $(INSTALL) -m 0644 rogue.6 $(DESTDIR)$(mandir)/$(PROGRAM).6 ; fi
|
||||
-$(INSTALL) -m 0644 rogue.doc $(DESTDIR)$(docdir)/$(PROGRAM).doc
|
||||
-$(INSTALL) -m 0644 rogue.html $(DESTDIR)$(docdir)/$(PROGRAM).html
|
||||
-$(INSTALL) -m 0644 rogue.cat $(DESTDIR)$(docdir)/$(PROGRAM).cat
|
||||
-$(INSTALL) -m 0644 LICENSE.TXT $(DESTDIR)$(docdir)/LICENSE.TXT
|
||||
-$(INSTALL) -m 0644 rogue.me $(DESTDIR)$(docdir)/$(PROGRAM).me
|
||||
-if test ! -f $(DESTDIR)$(LOCKFILE) ; then $(INSTALL) -m 0666 test $(DESTDIR)$(LOCKFILE) ; $(RM) $(DESTDIR)$(LOCKFILE) ; fi
|
||||
-$(RM) test
|
||||
|
||||
uninstall:
|
||||
-$(RM) $(DESTDIR)$(bindir)/$(PROGRAM)
|
||||
-$(RM) $(DESTDIR)$(man6dir)/$(PROGRAM).6
|
||||
-$(RM) $(DESTDIR)$(docdir)$(PROGRAM)/$(PROGRAM).doc
|
||||
-$(RM) $(DESTDIR)$(LOCKFILE)
|
||||
-$(RMDIR) $(DESTDIR)$(docdir)$(PROGRAM)
|
||||
|
||||
reinstall: uninstall install
|
||||
@@ -1,92 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* This file contains misc functions for dealing with armor
|
||||
* @(#)armor.c 4.14 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <curses.h>
|
||||
#include "rogue.h"
|
||||
|
||||
/*
|
||||
* wear:
|
||||
* The player wants to wear something, so let him/her put it on.
|
||||
*/
|
||||
void
|
||||
wear(struct rogue_state *rs)
|
||||
{
|
||||
register THING *obj;
|
||||
register char *sp;
|
||||
|
||||
if ((obj = get_item(rs,"wear", ARMOR)) == NULL)
|
||||
return;
|
||||
if (cur_armor != NULL)
|
||||
{
|
||||
addmsg(rs,"you are already wearing some");
|
||||
if (!terse)
|
||||
addmsg(rs,". You'll have to take it off first");
|
||||
endmsg(rs);
|
||||
after = FALSE;
|
||||
return;
|
||||
}
|
||||
if (obj->o_type != ARMOR)
|
||||
{
|
||||
msg(rs,"you can't wear that");
|
||||
return;
|
||||
}
|
||||
waste_time(rs);
|
||||
obj->o_flags |= ISKNOW;
|
||||
sp = inv_name(obj, TRUE);
|
||||
cur_armor = obj;
|
||||
if (!terse)
|
||||
addmsg(rs,"you are now ");
|
||||
msg(rs,"wearing %s", sp);
|
||||
}
|
||||
|
||||
/*
|
||||
* take_off:
|
||||
* Get the armor off of the players back
|
||||
*/
|
||||
void
|
||||
take_off(struct rogue_state *rs)
|
||||
{
|
||||
register THING *obj;
|
||||
|
||||
if ((obj = cur_armor) == NULL)
|
||||
{
|
||||
after = FALSE;
|
||||
if (terse)
|
||||
msg(rs,"not wearing armor");
|
||||
else
|
||||
msg(rs,"you aren't wearing any armor");
|
||||
return;
|
||||
}
|
||||
if (!dropcheck(rs,cur_armor))
|
||||
return;
|
||||
cur_armor = NULL;
|
||||
if (terse)
|
||||
addmsg(rs,"was");
|
||||
else
|
||||
addmsg(rs,"you used to be");
|
||||
msg(rs," wearing %c) %s", obj->o_packch, inv_name(obj, TRUE));
|
||||
}
|
||||
|
||||
/*
|
||||
* waste_time:
|
||||
* Do nothing but let other things happen
|
||||
*/
|
||||
void
|
||||
waste_time(struct rogue_state *rs)
|
||||
{
|
||||
do_daemons(rs,BEFORE);
|
||||
do_fuses(rs,BEFORE);
|
||||
do_daemons(rs,AFTER);
|
||||
do_fuses(rs,AFTER);
|
||||
}
|
||||
@@ -1,544 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Code for one creature to chase another
|
||||
*
|
||||
* @(#)chase.c 4.57 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <curses.h>
|
||||
#include "rogue.h"
|
||||
|
||||
#define DRAGONSHOT 5 /* one chance in DRAGONSHOT that a dragon will flame */
|
||||
|
||||
static coord ch_ret; /* Where chasing takes you */
|
||||
|
||||
/*
|
||||
* runners:
|
||||
* Make all the running monsters move.
|
||||
*/
|
||||
void
|
||||
runners(struct rogue_state *rs,int arg)
|
||||
{
|
||||
register THING *tp;
|
||||
THING *next;
|
||||
bool wastarget;
|
||||
static coord orig_pos;
|
||||
|
||||
for (tp = mlist; tp != NULL; tp = next)
|
||||
{
|
||||
/* remember this in case the monster's "next" is changed */
|
||||
next = next(tp);
|
||||
if (!on(*tp, ISHELD) && on(*tp, ISRUN))
|
||||
{
|
||||
orig_pos = tp->t_pos;
|
||||
wastarget = on(*tp, ISTARGET);
|
||||
if (move_monst(rs,tp) == -1)
|
||||
continue;
|
||||
if (on(*tp, ISFLY) && dist_cp(&hero, &tp->t_pos) >= 3)
|
||||
move_monst(rs,tp);
|
||||
if (wastarget && !ce(orig_pos, tp->t_pos))
|
||||
{
|
||||
tp->t_flags &= ~ISTARGET;
|
||||
to_death = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (has_hit)
|
||||
{
|
||||
endmsg(rs);
|
||||
has_hit = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* move_monst:
|
||||
* Execute a single turn of running for a monster
|
||||
*/
|
||||
int
|
||||
move_monst(struct rogue_state *rs,THING *tp)
|
||||
{
|
||||
if (!on(*tp, ISSLOW) || tp->t_turn)
|
||||
if (do_chase(rs,tp) == -1)
|
||||
return(-1);
|
||||
if (on(*tp, ISHASTE))
|
||||
if (do_chase(rs,tp) == -1)
|
||||
return(-1);
|
||||
tp->t_turn ^= TRUE;
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* relocate:
|
||||
* Make the monster's new location be the specified one, updating
|
||||
* all the relevant state.
|
||||
*/
|
||||
void
|
||||
relocate(struct rogue_state *rs,THING *th, coord *new_loc)
|
||||
{
|
||||
struct room *oroom;
|
||||
|
||||
if (!ce(*new_loc, th->t_pos))
|
||||
{
|
||||
mvaddch(th->t_pos.y, th->t_pos.x, th->t_oldch);
|
||||
th->t_room = roomin(rs,new_loc);
|
||||
set_oldch(th, new_loc);
|
||||
oroom = th->t_room;
|
||||
moat(th->t_pos.y, th->t_pos.x) = NULL;
|
||||
|
||||
if (oroom != th->t_room)
|
||||
th->t_dest = find_dest(rs,th);
|
||||
th->t_pos = *new_loc;
|
||||
moat(new_loc->y, new_loc->x) = th;
|
||||
}
|
||||
move(new_loc->y, new_loc->x);
|
||||
if (see_monst(th))
|
||||
addch(th->t_disguise);
|
||||
else if (on(player, SEEMONST))
|
||||
{
|
||||
standout();
|
||||
addch(th->t_type);
|
||||
standend();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* do_chase:
|
||||
* Make one thing chase another.
|
||||
*/
|
||||
int
|
||||
do_chase(struct rogue_state *rs,THING *th)
|
||||
{
|
||||
register coord *cp;
|
||||
register struct room *rer, *ree; /* room of chaser, room of chasee */
|
||||
register int mindist = 32767, curdist;
|
||||
register bool stoprun = FALSE; /* TRUE means we are there */
|
||||
register bool door;
|
||||
register THING *obj;
|
||||
static coord DEST; /* Temporary destination for chaser */
|
||||
|
||||
rer = th->t_room; /* Find room of chaser */
|
||||
if (on(*th, ISGREED) && rer->r_goldval == 0)
|
||||
th->t_dest = &hero; /* If gold has been taken, run after hero */
|
||||
if (th->t_dest == &hero) /* Find room of chasee */
|
||||
ree = proom;
|
||||
else
|
||||
ree = roomin(rs,th->t_dest);
|
||||
/*
|
||||
* We don't count doors as inside rooms for this routine
|
||||
*/
|
||||
door = (chat(th->t_pos.y, th->t_pos.x) == DOOR);
|
||||
/*
|
||||
* If the object of our desire is in a different room,
|
||||
* and we are not in a corridor, run to the door nearest to
|
||||
* our goal.
|
||||
*/
|
||||
over:
|
||||
if (rer != ree)
|
||||
{
|
||||
for (cp = rer->r_exit; cp < &rer->r_exit[rer->r_nexits]; cp++)
|
||||
{
|
||||
curdist = dist_cp(th->t_dest, cp);
|
||||
if (curdist < mindist)
|
||||
{
|
||||
DEST = *cp;
|
||||
mindist = curdist;
|
||||
}
|
||||
}
|
||||
if (door)
|
||||
{
|
||||
rer = &passages[flat(th->t_pos.y, th->t_pos.x) & F_PNUM];
|
||||
door = FALSE;
|
||||
goto over;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEST = *th->t_dest;
|
||||
/*
|
||||
* For dragons check and see if (a) the hero is on a straight
|
||||
* line from it, and (b) that it is within shooting distance,
|
||||
* but outside of striking range.
|
||||
*/
|
||||
if (th->t_type == 'D' && (th->t_pos.y == hero.y || th->t_pos.x == hero.x
|
||||
|| abs(th->t_pos.y - hero.y) == abs(th->t_pos.x - hero.x))
|
||||
&& dist_cp(&th->t_pos, &hero) <= BOLT_LENGTH * BOLT_LENGTH
|
||||
&& !on(*th, ISCANC) && rnd(DRAGONSHOT) == 0)
|
||||
{
|
||||
delta.y = sign(hero.y - th->t_pos.y);
|
||||
delta.x = sign(hero.x - th->t_pos.x);
|
||||
if (has_hit)
|
||||
endmsg(rs);
|
||||
fire_bolt(rs,&th->t_pos, &delta, "flame");
|
||||
running = FALSE;
|
||||
count = 0;
|
||||
quiet = 0;
|
||||
if (to_death && !on(*th, ISTARGET))
|
||||
{
|
||||
to_death = FALSE;
|
||||
kamikaze = FALSE;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This now contains what we want to run to this time
|
||||
* so we run to it. If we hit it we either want to fight it
|
||||
* or stop running
|
||||
*/
|
||||
if (!chase(th, &DEST))
|
||||
{
|
||||
if (ce(DEST, hero))
|
||||
{
|
||||
return( attack(rs,th) );
|
||||
}
|
||||
else if (ce(DEST, *th->t_dest))
|
||||
{
|
||||
for (obj = lvl_obj; obj != NULL; obj = next(obj))
|
||||
if (th->t_dest == &obj->o_pos)
|
||||
{
|
||||
detach(lvl_obj, obj);
|
||||
attach(th->t_pack, obj);
|
||||
chat(obj->o_pos.y, obj->o_pos.x) =
|
||||
(th->t_room->r_flags & ISGONE) ? PASSAGE : FLOOR;
|
||||
th->t_dest = find_dest(rs,th);
|
||||
break;
|
||||
}
|
||||
if (th->t_type != 'F')
|
||||
stoprun = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (th->t_type == 'F')
|
||||
return(0);
|
||||
}
|
||||
relocate(rs,th, &ch_ret);
|
||||
/*
|
||||
* And stop running if need be
|
||||
*/
|
||||
if (stoprun && ce(th->t_pos, *(th->t_dest)))
|
||||
th->t_flags &= ~ISRUN;
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* set_oldch:
|
||||
* Set the oldch character for the monster
|
||||
*/
|
||||
void
|
||||
set_oldch(THING *tp, coord *cp)
|
||||
{
|
||||
char sch;
|
||||
|
||||
if (ce(tp->t_pos, *cp))
|
||||
return;
|
||||
|
||||
sch = tp->t_oldch;
|
||||
tp->t_oldch = CCHAR( mvinch(cp->y,cp->x) );
|
||||
if (!on(player, ISBLIND))
|
||||
{
|
||||
if ((sch == FLOOR || tp->t_oldch == FLOOR) &&
|
||||
(tp->t_room->r_flags & ISDARK))
|
||||
tp->t_oldch = ' ';
|
||||
else if (dist_cp(cp, &hero) <= LAMPDIST && see_floor)
|
||||
tp->t_oldch = chat(cp->y, cp->x);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* see_monst:
|
||||
* Return TRUE if the hero can see the monster
|
||||
*/
|
||||
bool
|
||||
see_monst(THING *mp)
|
||||
{
|
||||
int y, x;
|
||||
|
||||
if (on(player, ISBLIND))
|
||||
return FALSE;
|
||||
if (on(*mp, ISINVIS) && !on(player, CANSEE))
|
||||
return FALSE;
|
||||
y = mp->t_pos.y;
|
||||
x = mp->t_pos.x;
|
||||
if (dist(y, x, hero.y, hero.x) < LAMPDIST)
|
||||
{
|
||||
if (y != hero.y && x != hero.x &&
|
||||
!step_ok(chat(y, hero.x)) && !step_ok(chat(hero.y, x)))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
if (mp->t_room != proom)
|
||||
return FALSE;
|
||||
return ((bool)!(mp->t_room->r_flags & ISDARK));
|
||||
}
|
||||
|
||||
/*
|
||||
* runto:
|
||||
* Set a monster running after the hero.
|
||||
*/
|
||||
void
|
||||
runto(struct rogue_state *rs,coord *runner)
|
||||
{
|
||||
register THING *tp;
|
||||
|
||||
/*
|
||||
* If we couldn't find him, something is funny
|
||||
*/
|
||||
#ifdef MASTER
|
||||
if ((tp = moat(runner->y, runner->x)) == NULL)
|
||||
msg(rs,"couldn't find monster in runto at (%d,%d)", runner->y, runner->x);
|
||||
#else
|
||||
tp = moat(runner->y, runner->x);
|
||||
#endif
|
||||
/*
|
||||
* Start the beastie running
|
||||
*/
|
||||
tp->t_flags |= ISRUN;
|
||||
tp->t_flags &= ~ISHELD;
|
||||
tp->t_dest = find_dest(rs,tp);
|
||||
}
|
||||
|
||||
/*
|
||||
* chase:
|
||||
* Find the spot for the chaser(er) to move closer to the
|
||||
* chasee(ee). Returns TRUE if we want to keep on chasing later
|
||||
* FALSE if we reach the goal.
|
||||
*/
|
||||
bool
|
||||
chase(THING *tp, coord *ee)
|
||||
{
|
||||
register THING *obj;
|
||||
register int x, y;
|
||||
register int curdist, thisdist;
|
||||
register coord *er = &tp->t_pos;
|
||||
register char ch;
|
||||
register int plcnt = 1;
|
||||
static coord tryp;
|
||||
|
||||
/*
|
||||
* If the thing is confused, let it move randomly. Invisible
|
||||
* Stalkers are slightly confused all of the time, and bats are
|
||||
* quite confused all the time
|
||||
*/
|
||||
if ((on(*tp, ISHUH) && rnd(5) != 0) || (tp->t_type == 'P' && rnd(5) == 0)
|
||||
|| (tp->t_type == 'B' && rnd(2) == 0))
|
||||
{
|
||||
/*
|
||||
* get a valid random move
|
||||
*/
|
||||
ch_ret = *rndmove(tp);
|
||||
curdist = dist_cp(&ch_ret, ee);
|
||||
/*
|
||||
* Small chance that it will become un-confused
|
||||
*/
|
||||
if (rnd(20) == 0)
|
||||
tp->t_flags &= ~ISHUH;
|
||||
}
|
||||
/*
|
||||
* Otherwise, find the empty spot next to the chaser that is
|
||||
* closest to the chasee.
|
||||
*/
|
||||
else
|
||||
{
|
||||
register int ey, ex;
|
||||
/*
|
||||
* This will eventually hold where we move to get closer
|
||||
* If we can't find an empty spot, we stay where we are.
|
||||
*/
|
||||
curdist = dist_cp(er, ee);
|
||||
ch_ret = *er;
|
||||
|
||||
ey = er->y + 1;
|
||||
if (ey >= NUMLINES - 1)
|
||||
ey = NUMLINES - 2;
|
||||
ex = er->x + 1;
|
||||
if (ex >= NUMCOLS)
|
||||
ex = NUMCOLS - 1;
|
||||
|
||||
for (x = er->x - 1; x <= ex; x++)
|
||||
{
|
||||
if (x < 0)
|
||||
continue;
|
||||
tryp.x = x;
|
||||
for (y = er->y - 1; y <= ey; y++)
|
||||
{
|
||||
tryp.y = y;
|
||||
if (!diag_ok(er, &tryp))
|
||||
continue;
|
||||
ch = winat(y, x);
|
||||
if (step_ok(ch))
|
||||
{
|
||||
/*
|
||||
* If it is a scroll, it might be a scare monster scroll
|
||||
* so we need to look it up to see what type it is.
|
||||
*/
|
||||
if (ch == SCROLL)
|
||||
{
|
||||
for (obj = lvl_obj; obj != NULL; obj = next(obj))
|
||||
{
|
||||
if (y == obj->o_pos.y && x == obj->o_pos.x)
|
||||
break;
|
||||
}
|
||||
if (obj != NULL && obj->o_which == S_SCARE)
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* It can also be a Xeroc, which we shouldn't step on
|
||||
*/
|
||||
if ((obj = moat(y, x)) != NULL && obj->t_type == 'X')
|
||||
continue;
|
||||
/*
|
||||
* If we didn't find any scrolls at this place or it
|
||||
* wasn't a scare scroll, then this place counts
|
||||
*/
|
||||
thisdist = dist(y, x, ee->y, ee->x);
|
||||
if (thisdist < curdist)
|
||||
{
|
||||
plcnt = 1;
|
||||
ch_ret = tryp;
|
||||
curdist = thisdist;
|
||||
}
|
||||
else if (thisdist == curdist && rnd(++plcnt) == 0)
|
||||
{
|
||||
ch_ret = tryp;
|
||||
curdist = thisdist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (bool)(curdist != 0 && !ce(ch_ret, hero));
|
||||
}
|
||||
|
||||
/*
|
||||
* roomin:
|
||||
* Find what room some coordinates are in. NULL means they aren't
|
||||
* in any room.
|
||||
*/
|
||||
struct room *
|
||||
roomin(struct rogue_state *rs,coord *cp)
|
||||
{
|
||||
register struct room *rp;
|
||||
register char *fp;
|
||||
|
||||
|
||||
fp = &flat(cp->y, cp->x);
|
||||
if (*fp & F_PASS)
|
||||
return &passages[*fp & F_PNUM];
|
||||
|
||||
for (rp = rooms; rp < &rooms[MAXROOMS]; rp++)
|
||||
if (cp->x <= rp->r_pos.x + rp->r_max.x && rp->r_pos.x <= cp->x
|
||||
&& cp->y <= rp->r_pos.y + rp->r_max.y && rp->r_pos.y <= cp->y)
|
||||
return rp;
|
||||
|
||||
msg(rs,"in some bizarre place (%d, %d)", unc(*cp));
|
||||
#ifdef MASTER
|
||||
abort();
|
||||
return NULL;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* diag_ok:
|
||||
* Check to see if the move is legal if it is diagonal
|
||||
*/
|
||||
bool
|
||||
diag_ok(coord *sp, coord *ep)
|
||||
{
|
||||
if (ep->x < 0 || ep->x >= NUMCOLS || ep->y <= 0 || ep->y >= NUMLINES - 1)
|
||||
return FALSE;
|
||||
if (ep->x == sp->x || ep->y == sp->y)
|
||||
return TRUE;
|
||||
return (bool)(step_ok(chat(ep->y, sp->x)) && step_ok(chat(sp->y, ep->x)));
|
||||
}
|
||||
|
||||
/*
|
||||
* cansee:
|
||||
* Returns true if the hero can see a certain coordinate.
|
||||
*/
|
||||
bool
|
||||
cansee(struct rogue_state *rs,int y, int x)
|
||||
{
|
||||
register struct room *rer;
|
||||
static coord tp;
|
||||
|
||||
if (on(player, ISBLIND))
|
||||
return FALSE;
|
||||
if (dist(y, x, hero.y, hero.x) < LAMPDIST)
|
||||
{
|
||||
if (flat(y, x) & F_PASS)
|
||||
if (y != hero.y && x != hero.x &&
|
||||
!step_ok(chat(y, hero.x)) && !step_ok(chat(hero.y, x)))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
/*
|
||||
* We can only see if the hero in the same room as
|
||||
* the coordinate and the room is lit or if it is close.
|
||||
*/
|
||||
tp.y = y;
|
||||
tp.x = x;
|
||||
return (bool)((rer = roomin(rs,&tp)) == proom && !(rer->r_flags & ISDARK));
|
||||
}
|
||||
|
||||
/*
|
||||
* find_dest:
|
||||
* find the proper destination for the monster
|
||||
*/
|
||||
coord *
|
||||
find_dest(struct rogue_state *rs,THING *tp)
|
||||
{
|
||||
register THING *obj;
|
||||
register int prob;
|
||||
|
||||
if ((prob = monsters[tp->t_type - 'A'].m_carry) <= 0 || tp->t_room == proom
|
||||
|| see_monst(tp))
|
||||
return &hero;
|
||||
for (obj = lvl_obj; obj != NULL; obj = next(obj))
|
||||
{
|
||||
if (obj->o_type == SCROLL && obj->o_which == S_SCARE)
|
||||
continue;
|
||||
if (roomin(rs,&obj->o_pos) == tp->t_room && rnd(100) < prob)
|
||||
{
|
||||
for (tp = mlist; tp != NULL; tp = next(tp))
|
||||
if (tp->t_dest == &obj->o_pos)
|
||||
break;
|
||||
if (tp == NULL)
|
||||
return &obj->o_pos;
|
||||
}
|
||||
}
|
||||
return &hero;
|
||||
}
|
||||
|
||||
/*
|
||||
* dist:
|
||||
* Calculate the "distance" between to points. Actually,
|
||||
* this calculates d^2, not d, but that's good enough for
|
||||
* our purposes, since it's only used comparitively.
|
||||
*/
|
||||
int
|
||||
dist(int y1, int x1, int y2, int x2)
|
||||
{
|
||||
return ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
|
||||
}
|
||||
|
||||
/*
|
||||
* dist_cp:
|
||||
* Call dist() with appropriate arguments for coord pointers
|
||||
*/
|
||||
int
|
||||
dist_cp(coord *c1, coord *c2)
|
||||
{
|
||||
return dist(c1->y, c1->x, c2->y, c2->x);
|
||||
}
|
||||
@@ -1,847 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Read and execute the user commands
|
||||
*
|
||||
* @(#)command.c 4.73 (Berkeley) 08/06/83
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <curses.h>
|
||||
#include "rogue.h"
|
||||
|
||||
/*
|
||||
* command:
|
||||
* Process the user commands
|
||||
*/
|
||||
void
|
||||
command(struct rogue_state *rs)
|
||||
{
|
||||
register char ch;
|
||||
register int ntimes = 1; /* Number of player moves */
|
||||
char *fp;
|
||||
THING *mp;
|
||||
static char countch, direction, newcount = FALSE;
|
||||
if (on(player, ISHASTE))
|
||||
ntimes++;
|
||||
/*
|
||||
* Let the daemons start up
|
||||
*/
|
||||
do_daemons(rs,BEFORE);
|
||||
do_fuses(rs,BEFORE);
|
||||
while (ntimes--)
|
||||
{
|
||||
if ( rs->replaydone != 0 )
|
||||
return;
|
||||
again = FALSE;
|
||||
if (has_hit)
|
||||
{
|
||||
endmsg(rs);
|
||||
has_hit = FALSE;
|
||||
}
|
||||
/*
|
||||
* these are illegal things for the player to be, so if any are
|
||||
* set, someone's been poking in memeory
|
||||
*/
|
||||
if (on(player, ISSLOW|ISGREED|ISINVIS|ISREGEN|ISTARGET))
|
||||
exit(1);
|
||||
|
||||
look(rs,TRUE);
|
||||
if (!running)
|
||||
door_stop = FALSE;
|
||||
status(rs);
|
||||
lastscore = purse;
|
||||
move(hero.y, hero.x);
|
||||
if ( rs->sleeptime != 0 )
|
||||
{
|
||||
if (!((running || count) && jump))
|
||||
refresh(); /* Draw screen */
|
||||
}
|
||||
take = 0;
|
||||
after = TRUE;
|
||||
/*
|
||||
* Read command or continue run
|
||||
*/
|
||||
#ifdef MASTER
|
||||
if (wizard)
|
||||
noscore = TRUE;
|
||||
#endif
|
||||
if (!no_command)
|
||||
{
|
||||
if (running || to_death)
|
||||
ch = runch;
|
||||
else if (count)
|
||||
ch = countch;
|
||||
else
|
||||
{
|
||||
ch = readchar(rs);
|
||||
move_on = FALSE;
|
||||
if (mpos != 0) /* Erase message if its there */
|
||||
msg(rs,"");
|
||||
}
|
||||
}
|
||||
else
|
||||
ch = '.';
|
||||
if (no_command)
|
||||
{
|
||||
if (--no_command == 0)
|
||||
{
|
||||
player.t_flags |= ISRUN;
|
||||
msg(rs,"you can move again");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* check for prefixes
|
||||
*/
|
||||
newcount = FALSE;
|
||||
if (isdigit(ch))
|
||||
{
|
||||
count = 0;
|
||||
newcount = TRUE;
|
||||
while (isdigit(ch))
|
||||
{
|
||||
count = count * 10 + (ch - '0');
|
||||
if (count > 255)
|
||||
count = 255;
|
||||
ch = readchar(rs);
|
||||
}
|
||||
countch = ch;
|
||||
/*
|
||||
* turn off count for commands which don't make sense
|
||||
* to repeat
|
||||
*/
|
||||
if ( rs->guiflag == 0 && rs->replaydone != 0 )
|
||||
ch = 'Q';
|
||||
switch (ch)
|
||||
{
|
||||
case CTRL('B'): case CTRL('H'): case CTRL('J'):
|
||||
case CTRL('K'): case CTRL('L'): case CTRL('N'):
|
||||
case CTRL('U'): case CTRL('Y'):
|
||||
case '.': case 'a': case 'b': case 'h': case 'j':
|
||||
case 'k': case 'l': case 'm': case 'n': case 'q':
|
||||
case 'r': case 's': case 't': case 'u': case 'y':
|
||||
case 'z': case 'B': case 'C': case 'H': case 'I':
|
||||
case 'J': case 'K': case 'L': case 'N': case 'U':
|
||||
case 'Y':
|
||||
#ifdef MASTER
|
||||
case CTRL('D'): case CTRL('A'):
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* execute a command
|
||||
*/
|
||||
if (count && !running)
|
||||
count--;
|
||||
if (ch != 'a' && ch != ESCAPE && !(running || count || to_death))
|
||||
{
|
||||
l_last_comm = last_comm;
|
||||
l_last_dir = last_dir;
|
||||
l_last_pick = last_pick;
|
||||
last_comm = ch;
|
||||
last_dir = '\0';
|
||||
last_pick = NULL;
|
||||
}
|
||||
over:
|
||||
switch (ch)
|
||||
{
|
||||
case ',': {
|
||||
THING *obj = NULL;
|
||||
int found = 0;
|
||||
for (obj = lvl_obj; obj != NULL; obj = next(obj))
|
||||
{
|
||||
if (obj->o_pos.y == hero.y && obj->o_pos.x == hero.x)
|
||||
{
|
||||
found=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
if (levit_check(rs))
|
||||
;
|
||||
else
|
||||
pick_up(rs,(char)obj->o_type);
|
||||
}
|
||||
else {
|
||||
if (!terse)
|
||||
addmsg(rs,"there is ");
|
||||
addmsg(rs,"nothing here");
|
||||
if (!terse)
|
||||
addmsg(rs," to pick up");
|
||||
endmsg(rs);
|
||||
}
|
||||
}
|
||||
when '!': shell(rs);
|
||||
when 'h': do_move(rs,0, -1);
|
||||
when 'j': do_move(rs,1, 0);
|
||||
when 'k': do_move(rs,-1, 0);
|
||||
when 'l': do_move(rs,0, 1);
|
||||
when 'y': do_move(rs,-1, -1);
|
||||
when 'u': do_move(rs,-1, 1);
|
||||
when 'b': do_move(rs,1, -1);
|
||||
when 'n': do_move(rs,1, 1);
|
||||
when 'H': do_run('h');
|
||||
when 'J': do_run('j');
|
||||
when 'K': do_run('k');
|
||||
when 'L': do_run('l');
|
||||
when 'Y': do_run('y');
|
||||
when 'U': do_run('u');
|
||||
when 'B': do_run('b');
|
||||
when 'N': do_run('n');
|
||||
when CTRL('H'): case CTRL('J'): case CTRL('K'): case CTRL('L'):
|
||||
case CTRL('Y'): case CTRL('U'): case CTRL('B'): case CTRL('N'):
|
||||
{
|
||||
if (!on(player, ISBLIND))
|
||||
{
|
||||
door_stop = TRUE;
|
||||
firstmove = TRUE;
|
||||
}
|
||||
if (count && !newcount)
|
||||
ch = direction;
|
||||
else
|
||||
{
|
||||
ch += ('A' - CTRL('A'));
|
||||
direction = ch;
|
||||
}
|
||||
goto over;
|
||||
}
|
||||
when 'F':
|
||||
kamikaze = TRUE;
|
||||
/* FALLTHROUGH */
|
||||
case 'f':
|
||||
if (!get_dir(rs))
|
||||
{
|
||||
after = FALSE;
|
||||
break;
|
||||
}
|
||||
delta.y += hero.y;
|
||||
delta.x += hero.x;
|
||||
if ( ((mp = moat(delta.y, delta.x)) == NULL)
|
||||
|| ((!see_monst(mp)) && !on(player, SEEMONST)))
|
||||
{
|
||||
if (!terse)
|
||||
addmsg(rs,"I see ");
|
||||
msg(rs,"no monster there");
|
||||
after = FALSE;
|
||||
}
|
||||
else if (diag_ok(&hero, &delta))
|
||||
{
|
||||
to_death = TRUE;
|
||||
max_hit = 0;
|
||||
mp->t_flags |= ISTARGET;
|
||||
runch = ch = dir_ch;
|
||||
goto over;
|
||||
}
|
||||
when 't':
|
||||
if (!get_dir(rs))
|
||||
after = FALSE;
|
||||
else
|
||||
missile(rs,delta.y, delta.x);
|
||||
when 'a':
|
||||
if (last_comm == '\0')
|
||||
{
|
||||
msg(rs,"you haven't typed a command yet");
|
||||
after = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch = last_comm;
|
||||
again = TRUE;
|
||||
goto over;
|
||||
}
|
||||
case 'q': quaff(rs);
|
||||
break;
|
||||
when 'Q':
|
||||
after = FALSE;
|
||||
q_comm = TRUE;
|
||||
if ( _quit() > 0 )
|
||||
{
|
||||
if ( rs->guiflag != 0 )
|
||||
{
|
||||
if (rs->needflush == 0 )
|
||||
rs->needflush = (uint32_t)time(NULL);
|
||||
rogue_bailout(rs);
|
||||
} else rs->replaydone = (uint32_t)time(NULL);
|
||||
}
|
||||
q_comm = FALSE;
|
||||
return;
|
||||
when 'i': after = FALSE; inventory(rs,pack, 0);
|
||||
when 'I': after = FALSE; picky_inven(rs);
|
||||
when 'd': drop(rs);
|
||||
when 'r': read_scroll(rs);
|
||||
when 'e': eat(rs);
|
||||
when 'w': wield(rs);
|
||||
when 'W': wear(rs);
|
||||
when 'T': take_off(rs);
|
||||
when 'P': ring_on(rs);
|
||||
when 'R': ring_off(rs);
|
||||
when 'o': option(rs); after = FALSE;
|
||||
when 'c': call(rs); after = FALSE;
|
||||
|
||||
when '>': after = FALSE; d_level(rs);
|
||||
if ( rs->guiflag != 0 && rs->needflush == 0 )
|
||||
rs->needflush = (uint32_t)time(NULL);
|
||||
|
||||
when '<': after = FALSE; u_level(rs);
|
||||
if ( rs->guiflag != 0 && rs->needflush == 0 )
|
||||
rs->needflush = (uint32_t)time(NULL);
|
||||
|
||||
when '?': after = FALSE; help(rs);
|
||||
when '/': after = FALSE; identify(rs);
|
||||
when 's': search(rs);
|
||||
when 'z':
|
||||
if (get_dir(rs))
|
||||
do_zap(rs);
|
||||
else
|
||||
after = FALSE;
|
||||
when 'D': after = FALSE; discovered(rs);
|
||||
when CTRL('P'): after = FALSE; msg(rs,huh);
|
||||
when CTRL('R'):
|
||||
after = FALSE;
|
||||
clearok(curscr,TRUE);
|
||||
wrefresh(curscr);
|
||||
when 'v':
|
||||
after = FALSE;
|
||||
msg(rs,"version %s. (mctesq was here)", release);
|
||||
when 'S':
|
||||
after = FALSE;
|
||||
#ifdef STANDALONE
|
||||
save_game(rs);
|
||||
#else
|
||||
msg(rs,"Saving is disabled, use bailout rpc");
|
||||
#endif
|
||||
when '.': ; /* Rest command */
|
||||
when ' ': after = FALSE; /* "Legal" illegal command */
|
||||
when '^':
|
||||
after = FALSE;
|
||||
if (get_dir(rs)) {
|
||||
delta.y += hero.y;
|
||||
delta.x += hero.x;
|
||||
fp = &flat(delta.y, delta.x);
|
||||
if (!terse)
|
||||
addmsg(rs,"You have found ");
|
||||
if (chat(delta.y, delta.x) != TRAP)
|
||||
msg(rs,"no trap there");
|
||||
else if (on(player, ISHALU))
|
||||
msg(rs,(char *)tr_name[rnd(NTRAPS)]);
|
||||
else {
|
||||
msg(rs,(char *)tr_name[*fp & F_TMASK]);
|
||||
*fp |= F_SEEN;
|
||||
}
|
||||
}
|
||||
#ifdef MASTER
|
||||
when '+':
|
||||
after = FALSE;
|
||||
if (wizard)
|
||||
{
|
||||
wizard = FALSE;
|
||||
turn_see(rs,TRUE);
|
||||
msg(rs,"not wizard any more");
|
||||
}
|
||||
else
|
||||
{
|
||||
wizard = passwd();
|
||||
if (wizard)
|
||||
{
|
||||
noscore = TRUE;
|
||||
turn_see(rs,FALSE);
|
||||
msg(rs,"you are suddenly as smart as Ken Arnold in dungeon #%d", dnum);
|
||||
}
|
||||
else
|
||||
msg(rs,"sorry");
|
||||
}
|
||||
#endif
|
||||
when ESCAPE: /* Escape */
|
||||
door_stop = FALSE;
|
||||
count = 0;
|
||||
after = FALSE;
|
||||
again = FALSE;
|
||||
when 'm':
|
||||
move_on = TRUE;
|
||||
if (!get_dir(rs))
|
||||
after = FALSE;
|
||||
else
|
||||
{
|
||||
ch = dir_ch;
|
||||
countch = dir_ch;
|
||||
goto over;
|
||||
}
|
||||
when ')': current(rs,cur_weapon, "wielding", NULL);
|
||||
when ']': current(rs,cur_armor, "wearing", NULL);
|
||||
when '=':
|
||||
current(rs,cur_ring[LEFT], "wearing",
|
||||
terse ? (char *)"(L)" : (char *)"on left hand");
|
||||
current(rs,cur_ring[RIGHT], "wearing",
|
||||
terse ? (char *)"(R)" : (char *)"on right hand");
|
||||
when '@':
|
||||
stat_msg = TRUE;
|
||||
status(rs);
|
||||
stat_msg = FALSE;
|
||||
after = FALSE;
|
||||
otherwise:
|
||||
after = FALSE;
|
||||
#ifdef MASTER
|
||||
if (wizard) switch (ch)
|
||||
{
|
||||
case '|': msg(rs,"@ %d,%d", hero.y, hero.x);
|
||||
when 'C': create_obj();
|
||||
when '$': msg(rs,"inpack = %d", inpack);
|
||||
when CTRL('G'): inventory(rs,lvl_obj, 0);
|
||||
when CTRL('W'): whatis(rs,FALSE, 0);
|
||||
when CTRL('D'): level++; new_level();
|
||||
when CTRL('A'): level--; new_level();
|
||||
when CTRL('F'): show_map();
|
||||
when CTRL('T'): teleport();
|
||||
when CTRL('E'): msg(rs,"food left: %d", food_left);
|
||||
when CTRL('C'): add_pass();
|
||||
when CTRL('X'): turn_see(rs,on(player, SEEMONST));
|
||||
when CTRL('~'):
|
||||
{
|
||||
THING *item;
|
||||
|
||||
if ((item = get_item(rs,"charge", STICK)) != NULL)
|
||||
item->o_charges = 10000;
|
||||
}
|
||||
when CTRL('I'):
|
||||
{
|
||||
int i;
|
||||
THING *obj;
|
||||
|
||||
for (i = 0; i < 9; i++)
|
||||
raise_level(rs);
|
||||
/*
|
||||
* Give him a sword (+1,+1)
|
||||
*/
|
||||
obj = new_item();
|
||||
init_weapon(obj, TWOSWORD);
|
||||
obj->o_hplus = 1;
|
||||
obj->o_dplus = 1;
|
||||
add_pack(rs,obj, TRUE);
|
||||
cur_weapon = obj;
|
||||
/*
|
||||
* And his suit of armor
|
||||
*/
|
||||
obj = new_item();
|
||||
obj->o_type = ARMOR;
|
||||
obj->o_which = PLATE_MAIL;
|
||||
obj->o_arm = -5;
|
||||
obj->o_flags |= ISKNOW;
|
||||
obj->o_count = 1;
|
||||
obj->o_group = 0;
|
||||
cur_armor = obj;
|
||||
add_pack(rs,obj, TRUE);
|
||||
}
|
||||
when '*' :
|
||||
pr_list();
|
||||
otherwise:
|
||||
illcom(rs,ch);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
illcom(rs,ch);
|
||||
}
|
||||
/*
|
||||
* turn off flags if no longer needed
|
||||
*/
|
||||
if (!running)
|
||||
door_stop = FALSE;
|
||||
}
|
||||
/*
|
||||
* If he ran into something to take, let him pick it up.
|
||||
*/
|
||||
if (take != 0)
|
||||
pick_up(rs,take);
|
||||
if (!running)
|
||||
door_stop = FALSE;
|
||||
if (!after)
|
||||
ntimes++;
|
||||
}
|
||||
do_daemons(rs,AFTER);
|
||||
do_fuses(rs,AFTER);
|
||||
if (ISRING(LEFT, R_SEARCH))
|
||||
search(rs);
|
||||
else if (ISRING(LEFT, R_TELEPORT) && rnd(50) == 0)
|
||||
teleport(rs);
|
||||
if (ISRING(RIGHT, R_SEARCH))
|
||||
search(rs);
|
||||
else if (ISRING(RIGHT, R_TELEPORT) && rnd(50) == 0)
|
||||
teleport(rs);
|
||||
}
|
||||
|
||||
/*
|
||||
* illcom:
|
||||
* What to do with an illegal command
|
||||
*/
|
||||
void
|
||||
illcom(struct rogue_state *rs,int ch)
|
||||
{
|
||||
save_msg = FALSE;
|
||||
count = 0;
|
||||
msg(rs,"illegal command '%s'", unctrl(ch));
|
||||
save_msg = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* search:
|
||||
* player gropes about him to find hidden things.
|
||||
*/
|
||||
void
|
||||
search(struct rogue_state *rs)
|
||||
{
|
||||
register int y, x;
|
||||
register char *fp;
|
||||
register int ey, ex;
|
||||
int probinc;
|
||||
bool found;
|
||||
|
||||
ey = hero.y + 1;
|
||||
ex = hero.x + 1;
|
||||
probinc = (on(player, ISHALU) ? 3 : 0);
|
||||
probinc += (on(player, ISBLIND) ? 2 : 0);
|
||||
found = FALSE;
|
||||
for (y = hero.y - 1; y <= ey; y++)
|
||||
for (x = hero.x - 1; x <= ex; x++)
|
||||
{
|
||||
if (y == hero.y && x == hero.x)
|
||||
continue;
|
||||
fp = &flat(y, x);
|
||||
if (!(*fp & F_REAL))
|
||||
switch (chat(y, x))
|
||||
{
|
||||
case '|':
|
||||
case '-':
|
||||
if (rnd(5 + probinc) != 0)
|
||||
break;
|
||||
chat(y, x) = DOOR;
|
||||
msg(rs,"a secret door");
|
||||
foundone:
|
||||
found = TRUE;
|
||||
*fp |= F_REAL;
|
||||
count = FALSE;
|
||||
running = FALSE;
|
||||
break;
|
||||
case FLOOR:
|
||||
if (rnd(2 + probinc) != 0)
|
||||
break;
|
||||
chat(y, x) = TRAP;
|
||||
if (!terse)
|
||||
addmsg(rs,"you found ");
|
||||
if (on(player, ISHALU))
|
||||
msg(rs,(char *)tr_name[rnd(NTRAPS)]);
|
||||
else {
|
||||
msg(rs,(char *)tr_name[*fp & F_TMASK]);
|
||||
*fp |= F_SEEN;
|
||||
}
|
||||
goto foundone;
|
||||
break;
|
||||
case ' ':
|
||||
if (rnd(3 + probinc) != 0)
|
||||
break;
|
||||
chat(y, x) = PASSAGE;
|
||||
goto foundone;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
look(rs,FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* help:
|
||||
* Give single character help, or the whole mess if he wants it
|
||||
*/
|
||||
void
|
||||
help(struct rogue_state *rs)
|
||||
{
|
||||
register const struct h_list *strp;
|
||||
register char helpch;
|
||||
register int numprint, cnt;
|
||||
msg(rs,"character you want help for (* for all): ");
|
||||
helpch = readchar(rs);
|
||||
mpos = 0;
|
||||
/*
|
||||
* If its not a *, print the right help string
|
||||
* or an error if he typed a funny character.
|
||||
*/
|
||||
if (helpch != '*')
|
||||
{
|
||||
move(0, 0);
|
||||
for (strp = helpstr; strp->h_desc != NULL; strp++)
|
||||
if (strp->h_ch == helpch)
|
||||
{
|
||||
lower_msg = TRUE;
|
||||
msg(rs,"%s%s", unctrl(strp->h_ch), strp->h_desc);
|
||||
lower_msg = FALSE;
|
||||
return;
|
||||
}
|
||||
msg(rs,"unknown character '%s'", unctrl(helpch));
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Here we print help for everything.
|
||||
* Then wait before we return to command mode
|
||||
*/
|
||||
numprint = 0;
|
||||
for (strp = helpstr; strp->h_desc != NULL; strp++)
|
||||
if (strp->h_print)
|
||||
numprint++;
|
||||
if (numprint & 01) /* round odd numbers up */
|
||||
numprint++;
|
||||
numprint /= 2;
|
||||
if (numprint > LINES - 1)
|
||||
numprint = LINES - 1;
|
||||
|
||||
wclear(hw);
|
||||
cnt = 0;
|
||||
for (strp = helpstr; strp->h_desc != NULL; strp++)
|
||||
if (strp->h_print)
|
||||
{
|
||||
wmove(hw, cnt % numprint, cnt >= numprint ? COLS / 2 : 0);
|
||||
if (strp->h_ch)
|
||||
waddstr(hw, unctrl(strp->h_ch));
|
||||
waddstr(hw, strp->h_desc);
|
||||
if (++cnt >= numprint * 2)
|
||||
break;
|
||||
}
|
||||
wmove(hw, LINES - 1, 0);
|
||||
waddstr(hw, "--Press space to continue--");
|
||||
wrefresh(hw);
|
||||
wait_for(rs,' ');
|
||||
clearok(stdscr, TRUE);
|
||||
/*
|
||||
refresh();
|
||||
*/
|
||||
msg(rs,"");
|
||||
touchwin(stdscr);
|
||||
wrefresh(stdscr);
|
||||
}
|
||||
|
||||
/*
|
||||
* identify:
|
||||
* Tell the player what a certain thing is.
|
||||
*/
|
||||
void
|
||||
identify(struct rogue_state *rs)
|
||||
{
|
||||
register int ch;
|
||||
register const struct h_list *hp;
|
||||
register char *str;
|
||||
static const struct h_list ident_list[] = {
|
||||
{'|', "wall of a room", FALSE},
|
||||
{'-', "wall of a room", FALSE},
|
||||
{GOLD, "gold", FALSE},
|
||||
{STAIRS, "a staircase", FALSE},
|
||||
{DOOR, "door", FALSE},
|
||||
{FLOOR, "room floor", FALSE},
|
||||
{PLAYER, "you", FALSE},
|
||||
{PASSAGE, "passage", FALSE},
|
||||
{TRAP, "trap", FALSE},
|
||||
{POTION, "potion", FALSE},
|
||||
{SCROLL, "scroll", FALSE},
|
||||
{FOOD, "food", FALSE},
|
||||
{WEAPON, "weapon", FALSE},
|
||||
{' ', "solid rock", FALSE},
|
||||
{ARMOR, "armor", FALSE},
|
||||
{AMULET, "the Amulet of Yendor", FALSE},
|
||||
{RING, "ring", FALSE},
|
||||
{STICK, "wand or staff", FALSE},
|
||||
{'\0'}
|
||||
};
|
||||
|
||||
msg(rs,"what do you want identified? ");
|
||||
ch = readchar(rs);
|
||||
mpos = 0;
|
||||
if (ch == ESCAPE)
|
||||
{
|
||||
msg(rs,"");
|
||||
return;
|
||||
}
|
||||
if (isupper(ch))
|
||||
str = monsters[ch-'A'].m_name;
|
||||
else
|
||||
{
|
||||
str = "unknown character";
|
||||
for (hp = ident_list; hp->h_ch != '\0'; hp++)
|
||||
if (hp->h_ch == ch)
|
||||
{
|
||||
str = hp->h_desc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
msg(rs,"'%s': %s", unctrl(ch), str);
|
||||
}
|
||||
|
||||
/*
|
||||
* d_level:
|
||||
* He wants to go down a level
|
||||
*/
|
||||
void
|
||||
d_level(struct rogue_state *rs)
|
||||
{
|
||||
if (levit_check(rs))
|
||||
return;
|
||||
if (chat(hero.y, hero.x) != STAIRS)
|
||||
msg(rs,"I see no way down");
|
||||
else
|
||||
{
|
||||
level++;
|
||||
seenstairs = FALSE;
|
||||
new_level(rs);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* u_level:
|
||||
* He wants to go up a level
|
||||
*/
|
||||
void
|
||||
u_level(struct rogue_state *rs)
|
||||
{
|
||||
if (levit_check(rs))
|
||||
return;
|
||||
if (chat(hero.y, hero.x) == STAIRS)
|
||||
if (amulet)
|
||||
{
|
||||
level--;
|
||||
if (level == 0)
|
||||
total_winner(rs);
|
||||
new_level(rs);
|
||||
msg(rs,"you feel a wrenching sensation in your gut");
|
||||
}
|
||||
else
|
||||
msg(rs,"your way is magically blocked");
|
||||
else
|
||||
msg(rs,"I see no way up");
|
||||
}
|
||||
|
||||
/*
|
||||
* levit_check:
|
||||
* Check to see if she's levitating, and if she is, print an
|
||||
* appropriate message.
|
||||
*/
|
||||
bool
|
||||
levit_check(struct rogue_state *rs)
|
||||
{
|
||||
if (!on(player, ISLEVIT))
|
||||
return FALSE;
|
||||
msg(rs,"You can't. You're floating off the ground!");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* call:
|
||||
* Allow a user to call a potion, scroll, or ring something
|
||||
*/
|
||||
void
|
||||
call(struct rogue_state *rs)
|
||||
{
|
||||
register THING *obj;
|
||||
register const struct obj_info *op = NULL;
|
||||
register char **guess; const char *elsewise = NULL;
|
||||
register const bool *know;
|
||||
|
||||
obj = get_item(rs,"call", CALLABLE);
|
||||
/*
|
||||
* Make certain that it is somethings that we want to wear
|
||||
*/
|
||||
if (obj == NULL)
|
||||
return;
|
||||
switch (obj->o_type)
|
||||
{
|
||||
case RING:
|
||||
op = &ring_info[obj->o_which];
|
||||
elsewise = r_stones[obj->o_which];
|
||||
goto norm;
|
||||
when POTION:
|
||||
op = &pot_info[obj->o_which];
|
||||
elsewise = p_colors[obj->o_which];
|
||||
goto norm;
|
||||
when SCROLL:
|
||||
op = &scr_info[obj->o_which];
|
||||
elsewise = s_names[obj->o_which];
|
||||
goto norm;
|
||||
when STICK:
|
||||
op = &ws_info[obj->o_which];
|
||||
elsewise = ws_made[obj->o_which];
|
||||
norm:
|
||||
know = &op->oi_know;
|
||||
guess = (char **)&op->oi_guess;
|
||||
if (*guess != NULL)
|
||||
elsewise = *guess;
|
||||
when FOOD:
|
||||
msg(rs,"you can't call that anything");
|
||||
return;
|
||||
otherwise:
|
||||
guess = &obj->o_label;
|
||||
know = NULL;
|
||||
elsewise = obj->o_label;
|
||||
}
|
||||
if (know != NULL && *know)
|
||||
{
|
||||
msg(rs,"that has already been identified");
|
||||
return;
|
||||
}
|
||||
if (elsewise != NULL && elsewise == *guess)
|
||||
{
|
||||
if (!terse)
|
||||
addmsg(rs,"Was ");
|
||||
msg(rs,"called \"%s\"", elsewise);
|
||||
}
|
||||
if (terse)
|
||||
msg(rs,"call it: ");
|
||||
else
|
||||
msg(rs,"what do you want to call it? ");
|
||||
|
||||
if (elsewise == NULL)
|
||||
strcpy(prbuf, "");
|
||||
else
|
||||
strcpy(prbuf, elsewise);
|
||||
if (get_str(rs,prbuf, stdscr) == NORM)
|
||||
{
|
||||
if (*guess != NULL)
|
||||
free(*guess);
|
||||
*guess = (char *)malloc((unsigned int) strlen(prbuf) + 1);
|
||||
strcpy(*guess, prbuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* current:
|
||||
* Print the current weapon/armor
|
||||
*/
|
||||
void
|
||||
current(struct rogue_state *rs,THING *cur, char *how, char *where)
|
||||
{
|
||||
after = FALSE;
|
||||
if (cur != NULL)
|
||||
{
|
||||
if (!terse)
|
||||
addmsg(rs,"you are %s (", how);
|
||||
inv_describe = FALSE;
|
||||
addmsg(rs,"%c) %s", cur->o_packch, inv_name(cur, TRUE));
|
||||
inv_describe = TRUE;
|
||||
if (where)
|
||||
addmsg(rs," %s", where);
|
||||
endmsg(rs);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!terse)
|
||||
addmsg(rs,"you are ");
|
||||
addmsg(rs,"%s nothing", how);
|
||||
if (where)
|
||||
addmsg(rs," %s", where);
|
||||
endmsg(rs);
|
||||
}
|
||||
}
|
||||
1500
src/cc/rogue/config.guess
vendored
1500
src/cc/rogue/config.guess
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,269 +0,0 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define if scorefile is top scores, not top players */
|
||||
#undef ALLSCORES
|
||||
|
||||
/* Define if checktime feature should be enabled */
|
||||
#undef CHECKTIME
|
||||
|
||||
/* Define to group owner of setgid executable */
|
||||
#undef GROUPOWNER
|
||||
|
||||
/* Define to 1 if you have the `alarm' function. */
|
||||
#undef HAVE_ALARM
|
||||
|
||||
/* Define to 1 if you have the <arpa/inet.h> header file. */
|
||||
#undef HAVE_ARPA_INET_H
|
||||
|
||||
/* Define to 1 if libcurses is requested */
|
||||
#undef HAVE_CURSES_H
|
||||
|
||||
/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
|
||||
#undef HAVE_DOPRNT
|
||||
|
||||
/* Define to 1 if you have the `erasechar' function. */
|
||||
#undef HAVE_ERASECHAR
|
||||
|
||||
/* Define if ncurses has ESCDELAY variable */
|
||||
#undef HAVE_ESCDELAY
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#undef HAVE_FCNTL_H
|
||||
|
||||
/* Define to 1 if you have the `fork' function. */
|
||||
#undef HAVE_FORK
|
||||
|
||||
/* Define to 1 if you have the `getgid' function. */
|
||||
#undef HAVE_GETGID
|
||||
|
||||
/* Define to 1 if you have the `getloadavg' function. */
|
||||
#undef HAVE_GETLOADAVG
|
||||
|
||||
/* Define to 1 if you have the `getpass' function. */
|
||||
#undef HAVE_GETPASS
|
||||
|
||||
/* Define to 1 if you have the `getpwuid' function. */
|
||||
#undef HAVE_GETPWUID
|
||||
|
||||
/* Define to 1 if you have the `getuid' function. */
|
||||
#undef HAVE_GETUID
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the `killchar' function. */
|
||||
#undef HAVE_KILLCHAR
|
||||
|
||||
/* Define to 1 if you have the <limits.h> header file. */
|
||||
#undef HAVE_LIMITS_H
|
||||
|
||||
/* Define to 1 if you have the `loadav' function. */
|
||||
#undef HAVE_LOADAV
|
||||
|
||||
/* Define to 1 if `lstat' has the bug that it succeeds when given the
|
||||
zero-length file name argument. */
|
||||
#undef HAVE_LSTAT_EMPTY_STRING_BUG
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define to 1 if you have the `memset' function. */
|
||||
#undef HAVE_MEMSET
|
||||
|
||||
/* Define to 1 if libncurses is requested */
|
||||
#undef HAVE_NCURSES_H
|
||||
|
||||
/* Define to 1 if you have the <ncurses/term.h> header file. */
|
||||
#undef HAVE_NCURSES_TERM_H
|
||||
|
||||
/* Define to 1 if you have the `nlist' function. */
|
||||
#undef HAVE_NLIST
|
||||
|
||||
/* Define to 1 if you have the <nlist.h> header file. */
|
||||
#undef HAVE_NLIST_H
|
||||
|
||||
/* Define to 1 if you have the <process.h> header file. */
|
||||
#undef HAVE_PROCESS_H
|
||||
|
||||
/* Define to 1 if you have the <pwd.h> header file. */
|
||||
#undef HAVE_PWD_H
|
||||
|
||||
/* Define to 1 if you have the `setenv' function. */
|
||||
#undef HAVE_SETENV
|
||||
|
||||
/* Define to 1 if you have the `setgid' function. */
|
||||
#undef HAVE_SETGID
|
||||
|
||||
/* Define to 1 if you have the `setregid' function. */
|
||||
#undef HAVE_SETREGID
|
||||
|
||||
/* Define to 1 if you have the `setresgid' function. */
|
||||
#undef HAVE_SETRESGID
|
||||
|
||||
/* Define to 1 if you have the `setresuid' function. */
|
||||
#undef HAVE_SETRESUID
|
||||
|
||||
/* Define to 1 if you have the `setreuid' function. */
|
||||
#undef HAVE_SETREUID
|
||||
|
||||
/* Define to 1 if you have the `setuid' function. */
|
||||
#undef HAVE_SETUID
|
||||
|
||||
/* Define to 1 if you have the `spawnl' function. */
|
||||
#undef HAVE_SPAWNL
|
||||
|
||||
/* Define to 1 if `stat' has the bug that it succeeds when given the
|
||||
zero-length file name argument. */
|
||||
#undef HAVE_STAT_EMPTY_STRING_BUG
|
||||
|
||||
/* Define to 1 if stdbool.h conforms to C99. */
|
||||
#undef HAVE_STDBOOL_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the `strchr' function. */
|
||||
#undef HAVE_STRCHR
|
||||
|
||||
/* Define to 1 if you have the `strerror' function. */
|
||||
#undef HAVE_STRERROR
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/ioctl.h> header file. */
|
||||
#undef HAVE_SYS_IOCTL_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#undef HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <sys/utsname.h> header file. */
|
||||
#undef HAVE_SYS_UTSNAME_H
|
||||
|
||||
/* Define to 1 if you have the <termios.h> header file. */
|
||||
#undef HAVE_TERMIOS_H
|
||||
|
||||
/* Define to 1 if you have the <term.h> header file. */
|
||||
#undef HAVE_TERM_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to 1 if you have the <utmp.h> header file. */
|
||||
#undef HAVE_UTMP_H
|
||||
|
||||
/* Define to 1 if you have the `vfork' function. */
|
||||
#undef HAVE_VFORK
|
||||
|
||||
/* Define to 1 if you have the <vfork.h> header file. */
|
||||
#undef HAVE_VFORK_H
|
||||
|
||||
/* Define to 1 if you have the `vprintf' function. */
|
||||
#undef HAVE_VPRINTF
|
||||
|
||||
/* Define to 1 if `fork' works. */
|
||||
#undef HAVE_WORKING_FORK
|
||||
|
||||
/* Define to 1 if `vfork' works. */
|
||||
#undef HAVE_WORKING_VFORK
|
||||
|
||||
/* Define to 1 if the system has the type `_Bool'. */
|
||||
#undef HAVE__BOOL
|
||||
|
||||
/* Define to 1 if you have the `_spawnl' function. */
|
||||
#undef HAVE__SPAWNL
|
||||
|
||||
/* define if we should use program's load average function instead of system
|
||||
*/
|
||||
#undef LOADAV
|
||||
|
||||
/* Define to file to use for scoreboard lockfile */
|
||||
#undef LOCKFILE
|
||||
|
||||
/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
|
||||
slash. */
|
||||
#undef LSTAT_FOLLOWS_SLASHED_SYMLINK
|
||||
|
||||
/* Define to include wizard mode */
|
||||
#undef MASTER
|
||||
|
||||
/* Define if maxusers feature should be enabled */
|
||||
#undef MAXLOAD
|
||||
|
||||
/* Define if maxusers feature should be enabled */
|
||||
#undef MAXUSERS
|
||||
|
||||
/* kernel file to pass to nlist() when reading load average (unlikely to work)
|
||||
*/
|
||||
#undef NAMELIST
|
||||
|
||||
/* word for the number of scores to store in scoreboard */
|
||||
#undef NUMNAME
|
||||
|
||||
/* number of scores to store in scoreboard */
|
||||
#undef NUMSCORES
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define crypt(3) wizard mode password */
|
||||
#undef PASSWD
|
||||
|
||||
/* Define as the return type of signal handlers (`int' or `void'). */
|
||||
#undef RETSIGTYPE
|
||||
|
||||
/* Define to file to use for scoreboard */
|
||||
#undef SCOREFILE
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
|
||||
#undef TM_IN_SYS_TIME
|
||||
|
||||
/* define if we should use program's user counting function instead of
|
||||
system's */
|
||||
#undef UCOUNT
|
||||
|
||||
/* utmp like file to pass to ucount() when counting online users (unlikely to
|
||||
work) */
|
||||
#undef UTMP
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
#undef const
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
#undef gid_t
|
||||
|
||||
/* Define to `int' if <sys/types.h> does not define. */
|
||||
#undef pid_t
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
||||
|
||||
/* Define to `int' if <sys/types.h> doesn't define. */
|
||||
#undef uid_t
|
||||
|
||||
/* Define as `fork' if `vfork' does not work. */
|
||||
#undef vfork
|
||||
1608
src/cc/rogue/config.sub
vendored
1608
src/cc/rogue/config.sub
vendored
File diff suppressed because it is too large
Load Diff
7511
src/cc/rogue/configure
vendored
7511
src/cc/rogue/configure
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,246 +0,0 @@
|
||||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.56)
|
||||
AC_INIT([Rogue],[5.4.4], [yendor@rogueforge.net])
|
||||
AC_CONFIG_SRCDIR([armor.c])
|
||||
AC_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_FILES([Makefile rogue.6 rogue.cat rogue.doc rogue.html rogue.me])
|
||||
AC_CANONICAL_SYSTEM([])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
|
||||
# Checks for libraries.
|
||||
|
||||
# Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS([arpa/inet.h sys/utsname.h pwd.h fcntl.h limits.h nlist.h stdlib.h string.h sys/ioctl.h termios.h unistd.h utmp.h term.h ncurses/term.h process.h])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_HEADER_STDBOOL
|
||||
AC_C_CONST
|
||||
AC_TYPE_UID_T
|
||||
AC_TYPE_SIZE_T
|
||||
AC_STRUCT_TM
|
||||
MP_WITH_CURSES
|
||||
# Checks for library functions.
|
||||
AC_FUNC_FORK
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
AC_FUNC_LSTAT
|
||||
AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
|
||||
AC_TYPE_SIGNAL
|
||||
AC_FUNC_STAT
|
||||
AC_FUNC_VPRINTF
|
||||
AC_CHECK_FUNCS([erasechar killchar alarm getpass memset setenv strchr nlist _spawnl spawnl getpwuid loadav getloadavg strerror setresgid setregid setgid setresuid setreuid setuid getuid getgid])
|
||||
|
||||
AC_CHECK_PROG([NROFF], [nroff], [nroff],)
|
||||
AC_CHECK_PROG([GROFF], [groff], [groff],)
|
||||
AC_CHECK_PROG([COLCRT], [colcrt], [colcrt],)
|
||||
AC_CHECK_PROG([TBL], [tbl], [tbl],)
|
||||
AC_CHECK_PROG([SED], [sed], [sed],)
|
||||
|
||||
AC_ARG_WITH(program-name, AC_HELP_STRING([--with-program-name=NAME],[alternate executable name]),[progname="$withval" ], [progname="rogue"] )
|
||||
PROGRAM=$progname
|
||||
AC_SUBST(PROGRAM)
|
||||
|
||||
AC_ARG_ENABLE(setgid, AC_HELP_STRING([--enable-setgid=NAME],[install executable as setgid with group ownership of NAME @<:@default=no@:>@])],[],[])
|
||||
AC_MSG_CHECKING([if using setgid execute bit])
|
||||
if test "x$enable_setgid" = "xno" ; then
|
||||
GROUPOWNER=
|
||||
elif test "x$enable_setgid" = "xyes" ; then
|
||||
GROUPOWNER=games
|
||||
elif test "x$enable_setgid" = "x" ; then
|
||||
GROUPOWNER=
|
||||
else
|
||||
GROUPOWNER=$enable_setgid
|
||||
fi
|
||||
|
||||
if test "x$GROUPOWNER" != "x" ; then
|
||||
AC_DEFINE_UNQUOTED([GROUPOWNER],[$GROUPOWNER], [Define to group owner of setgid executable])
|
||||
AC_MSG_RESULT([$GROUPOWNER])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
|
||||
AC_SUBST(GROUPOWNER)
|
||||
|
||||
AC_ARG_ENABLE([scorefile],[AC_HELP_STRING([--enable-scorefile=SCOREFILE], [enable scoreboard with given filename])],[],[])
|
||||
AC_MSG_CHECKING([for scoreboard file])
|
||||
if test "x$enable_scorefile" = "xno" ; then
|
||||
SCOREFILE=
|
||||
elif test "x$enable_scorefile" = "xyes" ; then
|
||||
SCOREFILE=$progname.scr
|
||||
elif test "x$enable_scorefile" = "x" ; then
|
||||
SCOREFILE=$progname.scr
|
||||
else
|
||||
SCOREFILE=$enable_scorefile
|
||||
fi
|
||||
|
||||
if test "x$SCOREFILE" != "x" ; then
|
||||
AC_DEFINE_UNQUOTED([SCOREFILE], ["$SCOREFILE"], [Define to file to use for scoreboard])
|
||||
AC_MSG_RESULT([$SCOREFILE])
|
||||
else
|
||||
AC_MSG_RESULT([disabled])
|
||||
fi
|
||||
|
||||
AC_SUBST(SCOREFILE)
|
||||
|
||||
AC_ARG_ENABLE([lockfile],[AC_HELP_STRING([--enable-lockfile=LOCKFILE], [enable scoreboard lockfile with given filename])],[],[])
|
||||
AC_MSG_CHECKING([for scoreboard lockfile file])
|
||||
if test "x$enable_lockfile" = "xno" ; then
|
||||
LOCKFILE=
|
||||
elif test "x$enable_lockfile" = "xyes" ; then
|
||||
LOCKFILE=$progname.lck
|
||||
elif test "x$enable_lockfile" = "x" ; then
|
||||
LOCKFILE=$progname.lck
|
||||
else
|
||||
LOCKFILE=$enable_lockfile
|
||||
fi
|
||||
|
||||
if test "x$LOCKFILE" != "x" ; then
|
||||
AC_DEFINE_UNQUOTED([LOCKFILE], ["$LOCKFILE"], [Define to file to use for scoreboard lockfile])
|
||||
AC_MSG_RESULT([$LOCKFILE])
|
||||
else
|
||||
AC_MSG_RESULT([disabled])
|
||||
fi
|
||||
|
||||
AC_SUBST(LOCKFILE)
|
||||
|
||||
AC_ARG_ENABLE([wizardmode],[AC_HELP_STRING([--enable-wizardmode], [enable availability of wizard mode @<:@default=no@:>@])],[],[])
|
||||
AC_MSG_CHECKING([if wizard mode is enabled])
|
||||
if test "x$enable_wizardmode" = "xno" ; then
|
||||
AC_MSG_RESULT([no])
|
||||
elif test "x$enable_wizardmode" = "x" ; then
|
||||
AC_MSG_RESULT([no])
|
||||
else
|
||||
AC_DEFINE([MASTER], [], [Define to include wizard mode])
|
||||
if test "x$enable_wizardmode" != "xyes" ; then
|
||||
AC_DEFINE_UNQUOTED([PASSWD],[$enable_wizardmode], [Define crypt(3) wizard mode password])
|
||||
fi
|
||||
AC_MSG_RESULT([yes])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([allscores],[AC_HELP_STRING([--enable-allscores], [enable scoreboard to show top scores, not just top players @<:@default=yes@:>@])],[],[enable_allscores=yes])
|
||||
AC_MSG_CHECKING([if allscores is enabled])
|
||||
if test "x$enable_allscores" = "xyes" ; then
|
||||
AC_DEFINE([ALLSCORES], [1], [Define if scorefile is top scores, not top players])
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([checktime],[AC_HELP_STRING([--enable-checktime], [enable checktime @<:@default=no@:>@])],[],[])
|
||||
AC_MSG_CHECKING([if checktime is enabled])
|
||||
if test "x$enable_checktime" = "xyes" ; then
|
||||
AC_DEFINE([CHECKTIME], [1], [Define if checktime feature should be enabled])
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([maxload],[AC_HELP_STRING([--enable-maxload], [enable maxload @<:@default=no@:>@])],[],[])
|
||||
AC_MSG_CHECKING([runtime execution limit (maximum system load average)])
|
||||
if test "x$enable_maxload" = "xyes" ; then
|
||||
AC_DEFINE([MAXLOAD], [100], [Define if maxload feature should be enabled])
|
||||
AC_MSG_RESULT([100])
|
||||
elif test "x$enable_maxload" = "x" ; then
|
||||
AC_MSG_RESULT([unlimited])
|
||||
elif test "x$enable_maxload" = "xno" ; then
|
||||
AC_MSG_RESULT([unlimited])
|
||||
else
|
||||
AC_DEFINE_UNQUOTED([MAXLOAD], [$enable_maxload], [Define if maxload feature should be enabled])
|
||||
AC_MSG_RESULT([$enable_maxload])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([maxusers],[AC_HELP_STRING([--enable-maxusers], [enable maxuser @<:@default=no@:>@])],[],[])
|
||||
AC_MSG_CHECKING([runtime execution limit (maximum online system users)])
|
||||
if test "x$enable_maxusers" = "xyes" ; then
|
||||
AC_DEFINE([MAXUSERS], [100], [Define if maxusers feature should be enabled])
|
||||
AC_MSG_RESULT([100])
|
||||
elif test "x$enable_maxusers" = "x" ; then
|
||||
AC_MSG_RESULT([unlimited])
|
||||
elif test "x$enable_maxload" = "xno" ; then
|
||||
AC_MSG_RESULT([unlimited])
|
||||
else
|
||||
AC_DEFINE_UNQUOTED([MAXLOAD], [$enable_maxusers], [Define if maxusers feature should be enabled])
|
||||
AC_MSG_RESULT([$enable_maxusers])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([numscores],[AC_HELP_STRING([--enable-numscores], [number of scores to store in scoreboard @<:@default=10@:>@])],[],[])
|
||||
AC_MSG_CHECKING([what the number of scores to store in scoreboard is])
|
||||
if test "x$numscores" = "xyes" ; then
|
||||
AC_DEFINE([NUMSCORES], [10], [number of scores to store in scoreboard])
|
||||
AC_MSG_RESULT([10])
|
||||
elif test "x$enable_numscores" = "x" ; then
|
||||
AC_DEFINE([NUMSCORES], [10], [number of scores to store in scoreboard])
|
||||
AC_MSG_RESULT([10])
|
||||
elif test "x$enable_numscores" = "xno" ; then
|
||||
AC_DEFINE([NUMSCORES], [10], [number of scores to store in scoreboard])
|
||||
AC_MSG_RESULT([10])
|
||||
else
|
||||
AC_DEFINE_UNQUOTED([NUMSCORES], [$enable_numscores], [number of scores to store in scoreboard])
|
||||
AC_MSG_RESULT([$enable_numscores])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([numname],[AC_HELP_STRING([--enable-numname], [word for number of scores to store in scoreboard @<:@default=Ten@:>@])],[],[])
|
||||
AC_MSG_CHECKING([word for the number of scores to store in scoreboard is])
|
||||
if test "x$enable_numname" = "xyes" ; then
|
||||
AC_DEFINE([NUMNAME], ["Ten"], [word for the number of scores to store in scoreboard])
|
||||
AC_MSG_RESULT([Ten])
|
||||
elif test "x$enable_numname" = "x" ; then
|
||||
AC_DEFINE([NUMNAME], ["Ten"], [word for the number of scores to store in scoreboard])
|
||||
AC_MSG_RESULT([Ten])
|
||||
elif test "x$enable_numname" = "xno" ; then
|
||||
AC_DEFINE([NUMNAME], ["Ten"], [word for the number of scores to store in scoreboard])
|
||||
AC_MSG_RESULT([Ten])
|
||||
else
|
||||
AC_DEFINE_UNQUOTED([NUMNAME], ["$enable_numname"], [word for the number of scores to store in scoreboard])
|
||||
AC_MSG_RESULT([$enable_numname])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([loadav],[AC_HELP_STRING([--enable-loadav=NAMELIST], [use program's load average function (unlikely to work) @<:@default=no@:>@])],[],[])
|
||||
AC_MSG_CHECKING([whether to use program's built in load average function])
|
||||
if test "x$enable_loadav" = "xyes" ; then
|
||||
AC_DEFINE([LOADAV], [], [define if we should use program's load average function instead of system])
|
||||
AC_DEFINE([NAMELIST], [/vmunix], [kernel file to pass to nlist() when reading load average (unlikely to work)])
|
||||
AC_MSG_RESULT([/vmunix])
|
||||
elif test "x$enable_loadav" = "x" ; then
|
||||
AC_MSG_RESULT([no])
|
||||
elif test "x$enable_loadav" = "xno" ; then
|
||||
AC_MSG_RESULT([no])
|
||||
else
|
||||
AC_DEFINE([LOADAV], [], [define if we should use program's load average function instead of system])
|
||||
AC_DEFINE_UNQUOTED([NAMELIST], [$enable_loadav], [kernel file to pass to nlist() when reading load average (unlikely to work)])
|
||||
AC_MSG_RESULT([$enable_loadav])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE([ucount],[AC_HELP_STRING([--enable-ucount=UTMPFILE], [use program's own function to count users (unlikely to work) @<:@default=no@:>@])],[],[])
|
||||
AC_MSG_CHECKING([whether to use program's built in user counting function])
|
||||
if test "x$enable_ucount" = "xyes" ; then
|
||||
AC_DEFINE([UCOUNT], [], [define if we should use program's user counting function instead of system's])
|
||||
AC_DEFINE([UTMP], [/etc/utmp], [utmp like file to pass to ucount() when counting online users (unlikely to work)])
|
||||
AC_MSG_RESULT([/etc/utmp])
|
||||
elif test "x$enable_ucount" = "x" ; then
|
||||
AC_MSG_RESULT([no])
|
||||
elif test "x$enable_count" = "xno" ; then
|
||||
AC_MSG_RESULT([no])
|
||||
else
|
||||
AC_DEFINE([UCOUNT], [], [define if we should use program's user counting function instead of system's])
|
||||
AC_DEFINE_UNQUOTED([UTMP], [$enable_ucount], [utmp like file to pass to ucount() when counting online users (unlikely to work)])
|
||||
AC_MSG_RESULT([$enable_ucount])
|
||||
fi
|
||||
|
||||
TARGET=$target
|
||||
AC_SUBST(TARGET)
|
||||
|
||||
AC_MSG_CHECKING([whether to docdir is defined])
|
||||
if test "x$docdir" = "x" ; then
|
||||
AC_MSG_RESULT([docdir undefined])
|
||||
docdir=\${datadir}/doc/\${PACKAGE_TARNAME}
|
||||
AC_SUBST(docdir)
|
||||
else
|
||||
AC_MSG_RESULT([docdir defined])
|
||||
fi
|
||||
|
||||
AC_OUTPUT
|
||||
@@ -1,435 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#include "cursesd.h"
|
||||
|
||||
static int32_t endwinflag;
|
||||
WINDOW *stdscr,*curscr;
|
||||
int32_t ESCDELAY;
|
||||
|
||||
WINDOW *newwin(int32_t nlines,int32_t ncols,int32_t begin_y,int32_t begin_x)
|
||||
{
|
||||
WINDOW *scr = 0;
|
||||
if ( nlines == LINES && ncols == COLS && begin_y == 0 && begin_x == 0 )
|
||||
scr = (WINDOW *)calloc(1,sizeof(*stdscr));
|
||||
curscr = scr;
|
||||
return(scr);
|
||||
}
|
||||
|
||||
WINDOW *initscr()
|
||||
{
|
||||
if ( stdscr == 0 )
|
||||
stdscr = newwin(LINES,COLS,0,0);
|
||||
return(stdscr);
|
||||
}
|
||||
|
||||
int32_t delwin(WINDOW *win)
|
||||
{
|
||||
free(win);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t wmove(WINDOW *win, int32_t y, int32_t x)
|
||||
{
|
||||
win->y = y;
|
||||
win->x = x;
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t move(int32_t y, int32_t x)
|
||||
{
|
||||
return(wmove(stdscr,y,x));
|
||||
}
|
||||
|
||||
int32_t werase(WINDOW *win)
|
||||
{
|
||||
memset(win->screen,' ',sizeof(win->screen));
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t wclear(WINDOW *win)
|
||||
{
|
||||
werase(win);
|
||||
clearok(win,TRUE);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t wclrtoeol(WINDOW *win)
|
||||
{
|
||||
if ( win->x < COLS-1 )
|
||||
memset(&win->screen[win->y][win->x],' ',COLS - win->x);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t wclrtobot(WINDOW *win)
|
||||
{
|
||||
wclrtoeol(win);
|
||||
if ( win->y < LINES-1 )
|
||||
memset(&win->screen[win->y+1][0],' ',COLS);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t erase(void)
|
||||
{
|
||||
return(werase(stdscr));
|
||||
}
|
||||
|
||||
int32_t clear(void)
|
||||
{
|
||||
return(wclear(stdscr));
|
||||
}
|
||||
|
||||
int32_t clrtobot(void)
|
||||
{
|
||||
return(wclrtobot(stdscr));
|
||||
}
|
||||
|
||||
int32_t clrtoeol(void)
|
||||
{
|
||||
return(wclrtoeol(stdscr));
|
||||
}
|
||||
|
||||
int32_t waddch(WINDOW *win, chtype ch)
|
||||
{
|
||||
int32_t i;
|
||||
if ( ch == '\t' )
|
||||
{
|
||||
for (i=0; i<8; i++)
|
||||
{
|
||||
if ( win->x >= COLS-1 )
|
||||
break;
|
||||
win->x++;
|
||||
if ( (win->x & 7) == 0 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( ch == '\n' )
|
||||
{
|
||||
wclrtoeol(win);
|
||||
win->x = 0;
|
||||
win->y++;
|
||||
if ( win->y >= LINES )
|
||||
win->y = 0;
|
||||
}
|
||||
else if ( ch == '\b' )
|
||||
{
|
||||
if ( win->x > 0 )
|
||||
win->x--;
|
||||
}
|
||||
else
|
||||
{
|
||||
win->screen[win->y][win->x++] = ch;
|
||||
if ( win->x >= COLS )
|
||||
{
|
||||
win->x = 0;
|
||||
win->y++;
|
||||
if ( win->y >= LINES )
|
||||
win->y = 0;
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t mvwaddch(WINDOW *win, int32_t y, int32_t x, chtype ch)
|
||||
{
|
||||
win->y = y;
|
||||
win->x = x;
|
||||
return(waddch(win,ch));
|
||||
}
|
||||
|
||||
int32_t addch(chtype ch)
|
||||
{
|
||||
return(waddch(stdscr,ch));
|
||||
}
|
||||
|
||||
int32_t mvaddch(int32_t y, int32_t x, chtype ch)
|
||||
{
|
||||
return(mvwaddch(stdscr,y,x,ch));
|
||||
}
|
||||
|
||||
int32_t waddstr(WINDOW *win, const char *str)
|
||||
{
|
||||
int32_t i;
|
||||
//fprintf(stderr,"%s\n",str);
|
||||
for (i=0; str[i]!=0; i++)
|
||||
waddch(win,str[i]);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t waddnstr(WINDOW *win, const char *str, int32_t n)
|
||||
{
|
||||
int32_t i;
|
||||
for (i=0; str[i]!=0 && i<n; i++)
|
||||
waddch(win,str[i]);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t mvwaddstr(WINDOW *win, int32_t y, int32_t x, const char *str)
|
||||
{
|
||||
win->y = y;
|
||||
win->x = x;
|
||||
return(waddstr(win,str));
|
||||
}
|
||||
|
||||
int32_t mvwaddnstr(WINDOW *win, int32_t y, int32_t x, const char *str, int32_t n)
|
||||
{
|
||||
win->y = y;
|
||||
win->x = x;
|
||||
return(waddnstr(win,str,n));
|
||||
}
|
||||
|
||||
int32_t addstr(const char *str)
|
||||
{
|
||||
return(waddstr(stdscr,str));
|
||||
}
|
||||
|
||||
int32_t addnstr(const char *str, int32_t n)
|
||||
{
|
||||
return(waddnstr(stdscr,str,n));
|
||||
}
|
||||
|
||||
int32_t mvaddstr(int32_t y, int32_t x, const char *str)
|
||||
{
|
||||
stdscr->y = y;
|
||||
stdscr->x = x;
|
||||
return(waddstr(stdscr,str));
|
||||
}
|
||||
|
||||
int32_t mvaddnstr(int32_t y, int32_t x, const char *str, int32_t n)
|
||||
{
|
||||
stdscr->y = y;
|
||||
stdscr->x = x;
|
||||
return(waddnstr(stdscr,str,n));
|
||||
}
|
||||
|
||||
int32_t printw(char *fmt,...)
|
||||
{
|
||||
char str[512]; int32_t ret; va_list myargs; // Declare a va_list type variable
|
||||
va_start(myargs,fmt); // Initialise the va_list variable with the ... after fmt
|
||||
ret = vsprintf(str,fmt,myargs); // Forward the '...' to vsprintf
|
||||
va_end(myargs); // Clean up the va_list
|
||||
return(addstr(str));
|
||||
}
|
||||
|
||||
int32_t wprintw(WINDOW *win,char *fmt,...)
|
||||
{
|
||||
char str[512]; int32_t ret; va_list myargs; // Declare a va_list type variable
|
||||
va_start(myargs,fmt); // Initialise the va_list variable with the ... after fmt
|
||||
ret = vsprintf(str,fmt,myargs); // Forward the '...' to vsprintf
|
||||
va_end(myargs); // Clean up the va_list
|
||||
return(waddstr(win,str));
|
||||
}
|
||||
|
||||
int32_t mvprintw(int32_t y,int32_t x,char *fmt,...)
|
||||
{
|
||||
char str[512]; int32_t ret; va_list myargs; // Declare a va_list type variable
|
||||
va_start(myargs,fmt); // Initialise the va_list variable with the ... after fmt
|
||||
ret = vsprintf(str,fmt,myargs); // Forward the '...' to vsprintf
|
||||
va_end(myargs); // Clean up the va_list
|
||||
stdscr->y = y;
|
||||
stdscr->x = x;
|
||||
return(addstr(str));
|
||||
}
|
||||
|
||||
int32_t mvwprintw(WINDOW *win,int32_t y,int32_t x,char *fmt,...)
|
||||
{
|
||||
char str[512]; int32_t ret; va_list myargs; // Declare a va_list type variable
|
||||
va_start(myargs,fmt); // Initialise the va_list variable with the ... after fmt
|
||||
ret = vsprintf(str,fmt,myargs); // Forward the '...' to vsprintf
|
||||
va_end(myargs); // Clean up the va_list
|
||||
win->y = y;
|
||||
win->x = x;
|
||||
return(waddstr(win,str));
|
||||
}
|
||||
|
||||
chtype winch(WINDOW *win)
|
||||
{
|
||||
return(win->screen[win->y][win->x]);
|
||||
}
|
||||
|
||||
chtype inch(void)
|
||||
{
|
||||
return(winch(stdscr));
|
||||
}
|
||||
|
||||
chtype mvwinch(WINDOW *win, int32_t y, int32_t x)
|
||||
{
|
||||
win->y = y;
|
||||
win->x = x;
|
||||
return(win->screen[win->y][win->x]);
|
||||
}
|
||||
|
||||
chtype mvinch(int32_t y, int32_t x)
|
||||
{
|
||||
return(mvwinch(stdscr,y,x));
|
||||
}
|
||||
|
||||
int32_t mvcur(int32_t oldrow, int32_t oldcol, int32_t newrow, int32_t newcol)
|
||||
{
|
||||
stdscr->y = newrow;
|
||||
stdscr->x = newcol;
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t endwin(void)
|
||||
{
|
||||
if ( stdscr != 0 )
|
||||
free(stdscr), stdscr = 0;
|
||||
endwinflag = 1;
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t isendwin(void)
|
||||
{
|
||||
return(endwinflag);
|
||||
}
|
||||
|
||||
int32_t refresh(void)
|
||||
{
|
||||
endwinflag = 0;
|
||||
return(wrefresh(stdscr));
|
||||
}
|
||||
|
||||
int32_t redrawwin(WINDOW *win)
|
||||
{
|
||||
return(wrefresh(win));
|
||||
}
|
||||
|
||||
int32_t wredrawln(WINDOW *win, int32_t beg_line, int32_t num_lines)
|
||||
{
|
||||
return(wrefresh(win));
|
||||
}
|
||||
|
||||
// functions with no data side effect
|
||||
#ifdef they_are_macros
|
||||
int32_t wrefresh(WINDOW *win)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t wnoutrefresh(WINDOW *win)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t doupdate(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t touchwin(WINDOW *win)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t standout(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t standend(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t raw(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t keypad(WINDOW *win, bool bf)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t noecho(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t flushinp(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t clearok(WINDOW *win, bool bf)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t idlok(WINDOW *win, bool bf)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t leaveok(WINDOW *win, bool bf)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t mvwin(WINDOW *win, int32_t y, int32_t x) // stub
|
||||
{
|
||||
fprintf(stderr,"unexpected call to mvwin\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
WINDOW *subwin(WINDOW *orig, int32_t nlines, int32_t ncols, int32_t begin_y, int32_t begin_x)
|
||||
{
|
||||
fprintf(stderr,"unexpected and unsupported call to subwin\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
char erasechar(void)
|
||||
{
|
||||
fprintf(stderr,"unexpected and unsupported call to erasechar\n");
|
||||
return(8);
|
||||
}
|
||||
|
||||
char killchar(void)
|
||||
{
|
||||
fprintf(stderr,"unexpected and unsupported call to erasechar\n");
|
||||
return(3);
|
||||
}
|
||||
|
||||
int32_t wgetnstr(WINDOW *win, char *str, int32_t n) // stub
|
||||
{
|
||||
fprintf(stderr,"unexpected and unsupported call to mvgetnstr\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
#ifndef __MINGW32__
|
||||
int32_t getch(void)
|
||||
{
|
||||
fprintf(stderr,"unexpected and unsupported call to getch\n");
|
||||
return(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t md_readchar(void)
|
||||
{
|
||||
fprintf(stderr,"unexpected and unsupported call to md_readchar\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
char *unctrl(char c)
|
||||
{
|
||||
static char ctrlstr[5];
|
||||
sprintf(ctrlstr,"^%%%02x",c);
|
||||
return(ctrlstr);
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef H_CURSESD_H
|
||||
#define H_CURSESD_H
|
||||
|
||||
#define KEY_OFFSET 0x100
|
||||
#define KEY_DOWN (KEY_OFFSET + 0x02) /* Down arrow key */
|
||||
#define KEY_UP (KEY_OFFSET + 0x03) /* Up arrow key */
|
||||
#define KEY_LEFT (KEY_OFFSET + 0x04) /* Left arrow key */
|
||||
#define KEY_RIGHT (KEY_OFFSET + 0x05) /* Right arrow key */
|
||||
|
||||
|
||||
#define COLOR_BLACK 0
|
||||
|
||||
#ifdef PDC_RGB /* RGB */
|
||||
# define COLOR_RED 1
|
||||
# define COLOR_GREEN 2
|
||||
# define COLOR_BLUE 4
|
||||
#else /* BGR */
|
||||
# define COLOR_BLUE 1
|
||||
# define COLOR_GREEN 2
|
||||
# define COLOR_RED 4
|
||||
#endif
|
||||
|
||||
#define COLOR_CYAN (COLOR_BLUE | COLOR_GREEN)
|
||||
#define COLOR_MAGENTA (COLOR_RED | COLOR_BLUE)
|
||||
#define COLOR_YELLOW (COLOR_RED | COLOR_GREEN)
|
||||
|
||||
#define COLOR_WHITE 7
|
||||
|
||||
#define LINES 24
|
||||
#define COLS 80
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h> /* we need va_list */
|
||||
#include <stddef.h> /* we want wchar_t */
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define ERR (-1)
|
||||
|
||||
struct cursesd_info
|
||||
{
|
||||
uint8_t screen[LINES][COLS];
|
||||
int32_t x,y;
|
||||
};
|
||||
typedef struct cursesd_info WINDOW;
|
||||
extern WINDOW *stdscr,*curscr;
|
||||
extern int32_t ESCDELAY;
|
||||
typedef char chtype;
|
||||
|
||||
#ifndef __MINGW32__
|
||||
int32_t getch(void); // stub
|
||||
#endif
|
||||
|
||||
int32_t md_readchar(void); // stub
|
||||
|
||||
WINDOW *initscr(void);
|
||||
int32_t endwin(void);
|
||||
int32_t isendwin(void);
|
||||
//SCREEN *newterm(const char *type, FILE *outfd, FILE *infd);
|
||||
//SCREEN *set_term(SCREEN *new);
|
||||
//void delscreen(SCREEN* sp);
|
||||
|
||||
int32_t refresh(void);
|
||||
int32_t wrefresh(WINDOW *win);
|
||||
//int32_t wnoutrefresh(WINDOW *win);
|
||||
//int32_t doupdate(void);
|
||||
int32_t redrawwin(WINDOW *win);
|
||||
int32_t wredrawln(WINDOW *win, int32_t beg_line, int32_t num_lines);
|
||||
|
||||
int32_t erase(void);
|
||||
int32_t werase(WINDOW *win);
|
||||
int32_t clear(void);
|
||||
int32_t wclear(WINDOW *win);
|
||||
int32_t clrtobot(void);
|
||||
int32_t wclrtobot(WINDOW *win);
|
||||
int32_t clrtoeol(void);
|
||||
int32_t wclrtoeol(WINDOW *win);
|
||||
|
||||
int32_t standout(void);
|
||||
int32_t standend(void);
|
||||
int32_t raw(void);
|
||||
int32_t noecho(void);
|
||||
int32_t flushinp(void);
|
||||
int32_t keypad(WINDOW *win, bool bf);
|
||||
|
||||
int32_t clearok(WINDOW *win, bool bf);
|
||||
int32_t idlok(WINDOW *win, bool bf);
|
||||
int32_t leaveok(WINDOW *win, bool bf);
|
||||
|
||||
WINDOW *newwin(int32_t nlines,int32_t ncols,int32_t begin_y,int32_t begin_x); // only LINES,COLS,0,0
|
||||
int32_t delwin(WINDOW *win);
|
||||
int32_t touchwin(WINDOW *win); // stub
|
||||
WINDOW *subwin(WINDOW *orig, int32_t nlines, int32_t ncols, int32_t begin_y, int32_t begin_x); // stub
|
||||
int32_t mvwin(WINDOW *win, int32_t y, int32_t x); // stub
|
||||
int32_t mvcur(int32_t oldrow, int32_t oldcol, int32_t newrow, int32_t newcol);
|
||||
|
||||
char erasechar(void); // stub
|
||||
char killchar(void); // stub
|
||||
|
||||
int32_t move(int32_t y, int32_t x);
|
||||
int32_t wmove(WINDOW *win, int32_t y, int32_t x);
|
||||
|
||||
chtype inch(void);
|
||||
chtype winch(WINDOW *win);
|
||||
chtype mvinch(int32_t y, int32_t x);
|
||||
chtype mvwinch(WINDOW *win, int32_t y, int32_t x);
|
||||
|
||||
int32_t addch(chtype ch);
|
||||
int32_t waddch(WINDOW *win, chtype ch);
|
||||
int32_t mvaddch(int32_t y, int32_t x, chtype ch);
|
||||
int32_t mvwaddch(WINDOW *win, int32_t y, int32_t x, chtype ch);
|
||||
|
||||
int32_t addstr(const char *str);
|
||||
int32_t addnstr(const char *str, int32_t n);
|
||||
int32_t waddstr(WINDOW *win, const char *str);
|
||||
int32_t waddnstr(WINDOW *win, const char *str, int32_t n);
|
||||
int32_t mvaddstr(int32_t y, int32_t x, const char *str);
|
||||
int32_t mvaddnstr(int32_t y, int32_t x, const char *str, int32_t n);
|
||||
int32_t mvwaddstr(WINDOW *win, int32_t y, int32_t x, const char *str);
|
||||
int32_t mvwaddnstr(WINDOW *win, int32_t y, int32_t x, const char *str, int32_t n);
|
||||
|
||||
int32_t wgetnstr(WINDOW *win, char *str, int32_t n); // stub
|
||||
int32_t printw(char *fmt,...);
|
||||
int32_t wprintw(WINDOW *win,char *fmt,...);
|
||||
int32_t mvprintw(int32_t y,int32_t x,char *fmt,...);
|
||||
int32_t mvwprintw(WINDOW *win,int32_t y,int32_t x,char *fmt,...);
|
||||
|
||||
char *unctrl(char c);
|
||||
|
||||
#define A_CHARTEXT 0xff
|
||||
#define baudrate() 9600
|
||||
#define getmaxx(a) COLS
|
||||
#define getmaxy(a) LINES
|
||||
#define getyx(win,_argfory,_argforx) _argfory = win->y, _argforx = win->x
|
||||
|
||||
// functions with only visible effects
|
||||
#define wrefresh(win) 0
|
||||
#define wnoutrefresh(win) 0
|
||||
#define doupdate() 0
|
||||
#define touchwin(win) 0
|
||||
#define standout() 0
|
||||
#define standend() 0
|
||||
#define raw() 0
|
||||
#define keypad(win,bf) 0
|
||||
#define noecho() 0
|
||||
#define flushinp() 0
|
||||
#define clearok(win,bf) 0
|
||||
#define idlok(win,bf) 0
|
||||
#define leaveok(win,bf) 0
|
||||
#define halfdelay(x) 0
|
||||
#define nocbreak() 0
|
||||
#define cbreak() 0
|
||||
#define curs_set(x) 0
|
||||
|
||||
// for tetris
|
||||
#define init_pair(a,b,c) 0
|
||||
#define start_color() 0
|
||||
#define box(a,b,c) 0
|
||||
#define A_REVERSE 0
|
||||
#define COLOR_PAIR(a) 0
|
||||
#define timeout(x) 0
|
||||
// end for tetris
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Contains functions for dealing with things that happen in the
|
||||
* future.
|
||||
*
|
||||
* @(#)daemon.c 4.7 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <curses.h>
|
||||
#include "rogue.h"
|
||||
|
||||
#define EMPTY 0
|
||||
#define DAEMON -1
|
||||
#define MAXDAEMONS 20
|
||||
|
||||
#define _X_ { EMPTY }
|
||||
|
||||
struct delayed_action d_list[MAXDAEMONS] = {
|
||||
_X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_,
|
||||
_X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_,
|
||||
};
|
||||
|
||||
/*
|
||||
* d_slot:
|
||||
* Find an empty slot in the daemon/fuse list
|
||||
*/
|
||||
struct delayed_action *
|
||||
d_slot()
|
||||
{
|
||||
register struct delayed_action *dev;
|
||||
|
||||
for (dev = d_list; dev <= &d_list[MAXDAEMONS-1]; dev++)
|
||||
if (dev->d_type == EMPTY)
|
||||
return dev;
|
||||
#ifdef MASTER
|
||||
debug("Ran out of fuse slots");
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* find_slot:
|
||||
* Find a particular slot in the table
|
||||
*/
|
||||
struct delayed_action *
|
||||
find_slot(void (*func)(struct rogue_state *rs,int))
|
||||
{
|
||||
register struct delayed_action *dev;
|
||||
|
||||
for (dev = d_list; dev <= &d_list[MAXDAEMONS-1]; dev++)
|
||||
if (dev->d_type != EMPTY && func == dev->d_func)
|
||||
return dev;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* start_daemon:
|
||||
* Start a daemon, takes a function.
|
||||
*/
|
||||
void
|
||||
start_daemon(void (*func)(struct rogue_state *rs,int), int arg, int type)
|
||||
{
|
||||
register struct delayed_action *dev;
|
||||
|
||||
dev = d_slot();
|
||||
dev->d_type = type;
|
||||
dev->d_func = func;
|
||||
dev->d_arg = arg;
|
||||
dev->d_time = DAEMON;
|
||||
}
|
||||
|
||||
/*
|
||||
* kill_daemon:
|
||||
* Remove a daemon from the list
|
||||
*/
|
||||
void
|
||||
kill_daemon(void (*func)(struct rogue_state *rs,int))
|
||||
{
|
||||
register struct delayed_action *dev;
|
||||
|
||||
if ((dev = find_slot(func)) == NULL)
|
||||
return;
|
||||
/*
|
||||
* Take it out of the list
|
||||
*/
|
||||
dev->d_type = EMPTY;
|
||||
}
|
||||
|
||||
/*
|
||||
* do_daemons:
|
||||
* Run all the daemons that are active with the current flag,
|
||||
* passing the argument to the function.
|
||||
*/
|
||||
void
|
||||
do_daemons(struct rogue_state *rs,int flag)
|
||||
{
|
||||
register struct delayed_action *dev;
|
||||
|
||||
/*
|
||||
* Loop through the devil list
|
||||
*/
|
||||
for (dev = d_list; dev <= &d_list[MAXDAEMONS-1]; dev++)
|
||||
/*
|
||||
* Executing each one, giving it the proper arguments
|
||||
*/
|
||||
if (dev->d_type == flag && dev->d_time == DAEMON)
|
||||
(*dev->d_func)(rs,dev->d_arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* fuse:
|
||||
* Start a fuse to go off in a certain number of turns
|
||||
*/
|
||||
void
|
||||
fuse(void (*func)(struct rogue_state *rs,int), int arg, int time, int type)
|
||||
{
|
||||
register struct delayed_action *wire;
|
||||
|
||||
wire = d_slot();
|
||||
wire->d_type = type;
|
||||
wire->d_func = func;
|
||||
wire->d_arg = arg;
|
||||
wire->d_time = time;
|
||||
}
|
||||
|
||||
/*
|
||||
* lengthen:
|
||||
* Increase the time until a fuse goes off
|
||||
*/
|
||||
void
|
||||
lengthen(void (*func)(struct rogue_state *rs,int), int xtime)
|
||||
{
|
||||
register struct delayed_action *wire;
|
||||
|
||||
if ((wire = find_slot(func)) == NULL)
|
||||
return;
|
||||
wire->d_time += xtime;
|
||||
}
|
||||
|
||||
/*
|
||||
* extinguish:
|
||||
* Put out a fuse
|
||||
*/
|
||||
void
|
||||
extinguish(void (*func)(struct rogue_state *rs,int))
|
||||
{
|
||||
register struct delayed_action *wire;
|
||||
|
||||
if ((wire = find_slot(func)) == NULL)
|
||||
return;
|
||||
wire->d_type = EMPTY;
|
||||
}
|
||||
|
||||
/*
|
||||
* do_fuses:
|
||||
* Decrement counters and start needed fuses
|
||||
*/
|
||||
|
||||
/*char *actionfunc_str(char *str,void *ptr)
|
||||
{
|
||||
if ( ptr == (void *)runners )
|
||||
strcpy(str,"runners");
|
||||
else if ( ptr == (void *)doctor )
|
||||
strcpy(str,"doctor");
|
||||
else if ( ptr == (void *)stomach )
|
||||
strcpy(str,"stomach");
|
||||
else if ( ptr == (void *)nohaste )
|
||||
strcpy(str,"nohaste");
|
||||
else if ( ptr == (void *)unconfuse )
|
||||
strcpy(str,"unconfuse");
|
||||
else if ( ptr == (void *)swander )
|
||||
strcpy(str,"swander");
|
||||
else if ( ptr == (void *)come_down )
|
||||
strcpy(str,"come_down");
|
||||
else if ( ptr == (void *)unsee )
|
||||
strcpy(str,"unsee");
|
||||
else if ( ptr == (void *)sight )
|
||||
strcpy(str,"sight");
|
||||
else if ( ptr == (void *)land )
|
||||
strcpy(str,"land");
|
||||
else if ( ptr == (void *)rollwand )
|
||||
strcpy(str,"rollwand");
|
||||
else if ( ptr == (void *)visuals )
|
||||
strcpy(str,"visuals");
|
||||
else if ( ptr == (void *)turn_see )
|
||||
strcpy(str,"turn_see");
|
||||
else strcpy(str,"no match");
|
||||
return(str);
|
||||
}*/
|
||||
|
||||
void
|
||||
do_fuses(struct rogue_state *rs,int flag)
|
||||
{
|
||||
register struct delayed_action *wire; char str[64];
|
||||
|
||||
/*
|
||||
* Step though the list
|
||||
*/
|
||||
for (wire = d_list; wire <= &d_list[MAXDAEMONS-1]; wire++)
|
||||
/*
|
||||
* Decrementing counters and starting things we want. We also need
|
||||
* to remove the fuse from the list once it has gone off.
|
||||
*/
|
||||
if (flag == wire->d_type && wire->d_time > 0 && --wire->d_time == 0)
|
||||
{
|
||||
//if ( fp != 0 )
|
||||
// fprintf(fp,"t.%d %d %s, ",wire->d_type,wire->d_time,actionfunc_str(str,wire->d_func));
|
||||
wire->d_type = EMPTY;
|
||||
(*wire->d_func)(rs,wire->d_arg);
|
||||
}
|
||||
}
|
||||
@@ -1,364 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* All the daemon and fuse functions are in here
|
||||
*
|
||||
* @(#)daemons.c 4.24 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <curses.h>
|
||||
#include "rogue.h"
|
||||
|
||||
/*
|
||||
* doctor:
|
||||
* A healing daemon that restors hit points after rest
|
||||
*/
|
||||
void
|
||||
doctor(struct rogue_state *rs,int arg)
|
||||
{
|
||||
register int lv, ohp;
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"doctor\n");
|
||||
|
||||
lv = pstats.s_lvl;
|
||||
ohp = pstats.s_hpt;
|
||||
quiet++;
|
||||
if (lv < 8)
|
||||
{
|
||||
if (quiet + (lv << 1) > 20)
|
||||
pstats.s_hpt++;
|
||||
}
|
||||
else
|
||||
if (quiet >= 3)
|
||||
pstats.s_hpt += rnd(lv - 7) + 1;
|
||||
if (ISRING(LEFT, R_REGEN))
|
||||
pstats.s_hpt++;
|
||||
if (ISRING(RIGHT, R_REGEN))
|
||||
pstats.s_hpt++;
|
||||
if (ohp != pstats.s_hpt)
|
||||
{
|
||||
if (pstats.s_hpt > max_hp)
|
||||
pstats.s_hpt = max_hp;
|
||||
quiet = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Swander:
|
||||
* Called when it is time to start rolling for wandering monsters
|
||||
*/
|
||||
void
|
||||
swander(struct rogue_state *rs,int arg)
|
||||
{
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"swander\n");
|
||||
start_daemon(rollwand, 0, BEFORE);
|
||||
}
|
||||
|
||||
/*
|
||||
* rollwand:
|
||||
* Called to roll to see if a wandering monster starts up
|
||||
*/
|
||||
int between = 0;
|
||||
void
|
||||
rollwand(struct rogue_state *rs,int arg)
|
||||
{
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"rollwand\n");
|
||||
if (++between >= 4)
|
||||
{
|
||||
if (roll(1, 6) == 4)
|
||||
{
|
||||
wanderer(rs);
|
||||
kill_daemon(rollwand);
|
||||
fuse(swander, 0, WANDERTIME, BEFORE);
|
||||
}
|
||||
between = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* unconfuse:
|
||||
* Release the poor player from his confusion
|
||||
*/
|
||||
void
|
||||
unconfuse(struct rogue_state *rs,int arg)
|
||||
{
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"unconfuse\n");
|
||||
player.t_flags &= ~ISHUH;
|
||||
msg(rs,"you feel less %s now", choose_str("trippy", "confused"));
|
||||
}
|
||||
|
||||
/*
|
||||
* unsee:
|
||||
* Turn off the ability to see invisible
|
||||
*/
|
||||
void
|
||||
unsee(struct rogue_state *rs,int arg)
|
||||
{
|
||||
register THING *th;
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"unsee\n");
|
||||
|
||||
for (th = mlist; th != NULL; th = next(th))
|
||||
if (on(*th, ISINVIS) && see_monst(th))
|
||||
mvaddch(th->t_pos.y, th->t_pos.x, th->t_oldch);
|
||||
player.t_flags &= ~CANSEE;
|
||||
}
|
||||
|
||||
/*
|
||||
* sight:
|
||||
* He gets his sight back
|
||||
*/
|
||||
void
|
||||
sight(struct rogue_state *rs,int arg)
|
||||
{
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"sight\n");
|
||||
if (on(player, ISBLIND))
|
||||
{
|
||||
extinguish(sight);
|
||||
player.t_flags &= ~ISBLIND;
|
||||
if (!(proom->r_flags & ISGONE))
|
||||
enter_room(rs,&hero);
|
||||
msg(rs,choose_str("far out! Everything is all cosmic again",
|
||||
"the veil of darkness lifts"));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* nohaste:
|
||||
* End the hasting
|
||||
*/
|
||||
void
|
||||
nohaste(struct rogue_state *rs,int arg)
|
||||
{
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"nohaste\n");
|
||||
player.t_flags &= ~ISHASTE;
|
||||
msg(rs,"you feel yourself slowing down");
|
||||
}
|
||||
|
||||
/*
|
||||
* stomach:
|
||||
* Digest the hero's food
|
||||
*/
|
||||
void
|
||||
stomach(struct rogue_state *rs,int arg)
|
||||
{
|
||||
register int oldfood;
|
||||
int orig_hungry = hungry_state;
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"stomach\n");
|
||||
|
||||
if (food_left <= 0)
|
||||
{
|
||||
if (food_left-- < -STARVETIME)
|
||||
death(rs,'s');
|
||||
/*
|
||||
* the hero is fainting
|
||||
*/
|
||||
if (no_command || rnd(5) != 0)
|
||||
return;
|
||||
no_command += rnd(8) + 4;
|
||||
hungry_state = 3;
|
||||
if (!terse)
|
||||
addmsg(rs,choose_str("the munchies overpower your motor capabilities. ",
|
||||
"you feel too weak from lack of food. "));
|
||||
msg(rs,choose_str("You freak out", "You faint"));
|
||||
}
|
||||
else
|
||||
{
|
||||
oldfood = food_left;
|
||||
food_left -= ring_eat(LEFT) + ring_eat(RIGHT) + 1 - amulet;
|
||||
|
||||
if (food_left < MORETIME && oldfood >= MORETIME)
|
||||
{
|
||||
hungry_state = 2;
|
||||
msg(rs,choose_str("the munchies are interfering with your motor capabilites",
|
||||
"you are starting to feel weak"));
|
||||
}
|
||||
else if (food_left < 2 * MORETIME && oldfood >= 2 * MORETIME)
|
||||
{
|
||||
hungry_state = 1;
|
||||
if (terse)
|
||||
msg(rs,choose_str("getting the munchies", "getting hungry"));
|
||||
else
|
||||
msg(rs,choose_str("you are getting the munchies",
|
||||
"you are starting to get hungry"));
|
||||
}
|
||||
}
|
||||
if (hungry_state != orig_hungry) {
|
||||
player.t_flags &= ~ISRUN;
|
||||
running = FALSE;
|
||||
to_death = FALSE;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* come_down:
|
||||
* Take the hero down off her acid trip.
|
||||
*/
|
||||
void
|
||||
come_down(struct rogue_state *rs,int arg)
|
||||
{
|
||||
register THING *tp;
|
||||
register bool seemonst;
|
||||
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"come_down\n");
|
||||
if (!on(player, ISHALU))
|
||||
return;
|
||||
|
||||
kill_daemon(visuals);
|
||||
player.t_flags &= ~ISHALU;
|
||||
|
||||
if (on(player, ISBLIND))
|
||||
return;
|
||||
|
||||
/*
|
||||
* undo the things
|
||||
*/
|
||||
for (tp = lvl_obj; tp != NULL; tp = next(tp))
|
||||
if (cansee(rs,tp->o_pos.y, tp->o_pos.x))
|
||||
mvaddch(tp->o_pos.y, tp->o_pos.x, tp->o_type);
|
||||
|
||||
/*
|
||||
* undo the monsters
|
||||
*/
|
||||
seemonst = on(player, SEEMONST);
|
||||
for (tp = mlist; tp != NULL; tp = next(tp))
|
||||
{
|
||||
move(tp->t_pos.y, tp->t_pos.x);
|
||||
if (cansee(rs,tp->t_pos.y, tp->t_pos.x))
|
||||
if (!on(*tp, ISINVIS) || on(player, CANSEE))
|
||||
addch(tp->t_disguise);
|
||||
else
|
||||
addch(chat(tp->t_pos.y, tp->t_pos.x));
|
||||
else if (seemonst)
|
||||
{
|
||||
standout();
|
||||
addch(tp->t_type);
|
||||
standend();
|
||||
}
|
||||
}
|
||||
msg(rs,"Everything looks SO boring now.");
|
||||
}
|
||||
|
||||
/*
|
||||
* visuals:
|
||||
* change the characters for the player
|
||||
*/
|
||||
void
|
||||
visuals(struct rogue_state *rs,int arg)
|
||||
{
|
||||
register THING *tp;
|
||||
register bool seemonst;
|
||||
|
||||
if (!after || (running && jump))
|
||||
return;
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"visuals\n");
|
||||
/*
|
||||
* change the things
|
||||
*/
|
||||
for (tp = lvl_obj; tp != NULL; tp = next(tp))
|
||||
if (cansee(rs,tp->o_pos.y, tp->o_pos.x))
|
||||
mvaddch(tp->o_pos.y, tp->o_pos.x, rnd_thing());
|
||||
|
||||
/*
|
||||
* change the stairs
|
||||
*/
|
||||
if (!seenstairs && cansee(rs,stairs.y, stairs.x))
|
||||
mvaddch(stairs.y, stairs.x, rnd_thing());
|
||||
|
||||
/*
|
||||
* change the monsters
|
||||
*/
|
||||
seemonst = on(player, SEEMONST);
|
||||
for (tp = mlist; tp != NULL; tp = next(tp))
|
||||
{
|
||||
move(tp->t_pos.y, tp->t_pos.x);
|
||||
if (see_monst(tp))
|
||||
{
|
||||
if (tp->t_type == 'X' && tp->t_disguise != 'X')
|
||||
addch(rnd_thing());
|
||||
else
|
||||
addch(rnd(26) + 'A');
|
||||
}
|
||||
else if (seemonst)
|
||||
{
|
||||
standout();
|
||||
addch(rnd(26) + 'A');
|
||||
standend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* land:
|
||||
* Land from a levitation potion
|
||||
*/
|
||||
void
|
||||
land(struct rogue_state *rs,int arg)
|
||||
{
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"land\n");
|
||||
player.t_flags &= ~ISLEVIT;
|
||||
msg(rs,choose_str("bummer! You've hit the ground",
|
||||
"you float gently to the ground"));
|
||||
}
|
||||
|
||||
/*
|
||||
* turn_see:
|
||||
* Put on or off seeing monsters on this level
|
||||
*/
|
||||
bool
|
||||
turn_see(struct rogue_state *rs,bool turn_off)
|
||||
{
|
||||
THING *mp;
|
||||
bool can_see, add_new;
|
||||
if ( rs->logfp != 0 )
|
||||
fprintf(rs->logfp,"turn_see\n");
|
||||
|
||||
add_new = FALSE;
|
||||
for (mp = mlist; mp != NULL; mp = next(mp))
|
||||
{
|
||||
move(mp->t_pos.y, mp->t_pos.x);
|
||||
can_see = see_monst(mp);
|
||||
if (turn_off)
|
||||
{
|
||||
if (!can_see)
|
||||
addch(mp->t_oldch);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!can_see)
|
||||
standout();
|
||||
if (!on(player, ISHALU))
|
||||
addch(mp->t_type);
|
||||
else
|
||||
addch(rnd(26) + 'A');
|
||||
if (!can_see)
|
||||
{
|
||||
standend();
|
||||
add_new ^= 1;//add_new++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (turn_off)
|
||||
player.t_flags &= ~SEEMONST;
|
||||
else
|
||||
player.t_flags |= SEEMONST;
|
||||
return add_new;
|
||||
}
|
||||
|
||||
@@ -1,519 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* global variable initializaton
|
||||
*
|
||||
* @(#)extern.c 4.82 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <curses.h>
|
||||
#include "rogue.h"
|
||||
|
||||
|
||||
|
||||
bool after; /* True if we want after daemons */
|
||||
bool again; /* Repeating the last command */
|
||||
int noscore; /* Was a wizard sometime */
|
||||
bool seenstairs; /* Have seen the stairs (for lsd) */
|
||||
bool amulet = FALSE; /* He found the amulet */
|
||||
bool door_stop = FALSE; /* Stop running when we pass a door */
|
||||
bool fight_flush = FALSE; /* True if toilet input */
|
||||
bool firstmove = FALSE; /* First move after setting door_stop */
|
||||
bool got_ltc = FALSE; /* We have gotten the local tty chars */
|
||||
bool has_hit = FALSE; /* Has a "hit" message pending in msg */
|
||||
bool in_shell = FALSE; /* True if executing a shell */
|
||||
bool inv_describe = TRUE; /* Say which way items are being used */
|
||||
bool jump = FALSE; /* Show running as series of jumps */
|
||||
bool kamikaze = FALSE; /* to_death really to DEATH */
|
||||
bool lower_msg = FALSE; /* Messages should start w/lower case */
|
||||
bool move_on = FALSE; /* Next move shouldn't pick up items */
|
||||
bool msg_esc = FALSE; /* Check for ESC from msg's --More-- */
|
||||
bool passgo = FALSE; /* Follow passages */
|
||||
bool playing = TRUE; /* True until he quits */
|
||||
bool q_comm = FALSE; /* Are we executing a 'Q' command? */
|
||||
bool running = FALSE; /* True if player is running */
|
||||
bool save_msg = TRUE; /* Remember last msg */
|
||||
bool see_floor = TRUE; /* Show the lamp illuminated floor */
|
||||
bool stat_msg = FALSE; /* Should status() print as a msg() */
|
||||
bool terse = FALSE; /* True if we should be short */
|
||||
bool to_death = FALSE; /* Fighting is to the death! */
|
||||
bool tombstone = TRUE; /* Print out tombstone at end */
|
||||
#ifdef MASTER
|
||||
int wizard = FALSE; /* True if allows wizard commands */
|
||||
#endif
|
||||
bool pack_used[26] = { /* Is the character used in the pack? */
|
||||
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
|
||||
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
|
||||
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
|
||||
};
|
||||
|
||||
char dir_ch; /* Direction from last get_dir() call */
|
||||
char runch; /* Direction player is running */
|
||||
char take; /* Thing she is taking */
|
||||
|
||||
int orig_dsusp; /* Original dsusp char */
|
||||
char file_name[MAXSTR]; /* Save file name */
|
||||
char huh[MAXSTR]; /* The last message printed */
|
||||
char prbuf[2*MAXSTR]; /* buffer for sprintfs */
|
||||
char whoami[MAXSTR]; /* Name of player */
|
||||
char fruit[MAXSTR] = /* Favorite fruit */
|
||||
{ 's', 'l', 'i', 'm', 'e', '-', 'm', 'o', 'l', 'd', '\0' };
|
||||
char home[MAXSTR] = { '\0' }; /* User's home directory */
|
||||
char l_last_comm = '\0'; /* Last last_comm */
|
||||
char l_last_dir = '\0'; /* Last last_dir */
|
||||
char last_comm = '\0'; /* Last command typed */
|
||||
char last_dir = '\0'; /* Last direction given */
|
||||
|
||||
int n_objs; /* # items listed in inventory() call */
|
||||
int ntraps; /* Number of traps on this level */
|
||||
int hungry_state = 0; /* How hungry is he */
|
||||
int inpack = 0; /* Number of things in pack */
|
||||
int inv_type = 0; /* Type of inventory to use */
|
||||
int level = 1; /* What level she is on */
|
||||
int max_hit; /* Max damage done to her in to_death */
|
||||
int max_level; /* Deepest player has gone */
|
||||
int mpos = 0; /* Where cursor is on top line */
|
||||
int no_food = 0; /* Number of levels without food */
|
||||
|
||||
int count = 0; /* Number of times to repeat command */
|
||||
int food_left; /* Amount of food in hero's stomach */
|
||||
int lastscore = -1; /* Score before this turn */
|
||||
int no_command = 0; /* Number of turns asleep */
|
||||
int no_move = 0; /* Number of turns held in place */
|
||||
int purse = 0; /* How much gold he has */
|
||||
int quiet = 0; /* Number of quiet turns */
|
||||
int vf_hit = 0; /* Number of time flytrap has hit */
|
||||
|
||||
|
||||
//int dnum; /* Dungeon number */
|
||||
uint64_t seed; /* Random number seed */
|
||||
|
||||
coord delta; /* Change indicated to get_dir() */
|
||||
coord oldpos; /* Position before last look() call */
|
||||
coord stairs; /* Location of staircase */
|
||||
|
||||
PLACE places[MAXLINES*MAXCOLS]; /* level map */
|
||||
|
||||
const char *p_colors[MAXPOTIONS]; /* Colors of the potions */
|
||||
const char *r_stones[MAXRINGS]; /* Stone settings of the rings */
|
||||
const char *ws_made[MAXSTICKS]; /* What sticks are made of */
|
||||
const char *ws_type[MAXSTICKS]; /* Is it a wand or a staff */
|
||||
|
||||
THING *cur_armor; /* What he is wearing */
|
||||
THING *cur_ring[2]; /* Which rings are being worn */
|
||||
THING *cur_weapon; /* Which weapon he is weilding */
|
||||
THING *l_last_pick = NULL; /* Last last_pick */
|
||||
THING *last_pick = NULL; /* Last object picked in get_item() */
|
||||
THING *lvl_obj = NULL; /* List of objects on this level */
|
||||
THING *mlist = NULL; /* List of monsters on the level */
|
||||
struct room *oldrp; /* Roomin(&oldpos) */
|
||||
THING player; /* His stats */
|
||||
WINDOW *hw = NULL; /* used as a scratch window */
|
||||
char *s_names[MAXSCROLLS]; /* Names of the scrolls */
|
||||
FILE *scoreboard = NULL; /* File descriptor for score file */
|
||||
|
||||
#define INIT_STATS { 16, 0, 1, 10, 12, "1x4", 12 }
|
||||
|
||||
struct stats max_stats = INIT_STATS; /* The maximum for the player */
|
||||
struct stats orig_max_stats = INIT_STATS;
|
||||
|
||||
struct monster monsters[26];
|
||||
struct room passages[MAXPASS],rooms[MAXROOMS]; /* One for each room -- A level */
|
||||
struct obj_info things[NUMTHINGS],ring_info[MAXRINGS],pot_info[MAXPOTIONS],arm_info[MAXARMORS],scr_info[MAXSCROLLS],weap_info[MAXWEAPONS + 1],ws_info[MAXSTICKS];
|
||||
|
||||
////////////// constants
|
||||
#define ___ 1
|
||||
#define XX 10
|
||||
|
||||
const struct obj_info origthings[NUMTHINGS] = {
|
||||
{ 0, 26 }, /* potion */
|
||||
{ 0, 36 }, /* scroll */
|
||||
{ 0, 16 }, /* food */
|
||||
{ 0, 7 }, /* weapon */
|
||||
{ 0, 7 }, /* armor */
|
||||
{ 0, 4 }, /* ring */
|
||||
{ 0, 4 }, /* stick */
|
||||
};
|
||||
|
||||
const struct room origpassages[MAXPASS] = /* One for each passage */
|
||||
{
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} },
|
||||
{ {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }
|
||||
};
|
||||
const struct monster origmonsters[26] =
|
||||
{
|
||||
/* Name CARRY FLAG str, exp, lvl, amr, hpt, dmg */
|
||||
{ "aquator", 0, ISMEAN, { XX, 20, 5, 2, ___, "0x0/0x0" } },
|
||||
{ "bat", 0, ISFLY, { XX, 1, 1, 3, ___, "1x2" } },
|
||||
{ "centaur", 15, 0, { XX, 17, 4, 4, ___, "1x2/1x5/1x5" } },
|
||||
{ "dragon", 100, ISMEAN, { XX,5000, 10, -1, ___, "1x8/1x8/3x10" } },
|
||||
{ "emu", 0, ISMEAN, { XX, 2, 1, 7, ___, "1x2" } },
|
||||
{ "venus flytrap", 0, ISMEAN, { XX, 80, 8, 3, ___, "%%%x0" } },
|
||||
/* NOTE: the damage is %%% so that xstr won't merge this */
|
||||
/* string with others, since it is written on in the program */
|
||||
{ "griffin", 20, ISMEAN|ISFLY|ISREGEN, { XX,2000, 13, 2, ___, "4x3/3x5" } },
|
||||
{ "hobgoblin", 0, ISMEAN, { XX, 3, 1, 5, ___, "1x8" } },
|
||||
{ "ice monster", 0, 0, { XX, 5, 1, 9, ___, "0x0" } },
|
||||
{ "jabberwock", 70, 0, { XX,3000, 15, 6, ___, "2x12/2x4" } },
|
||||
{ "kestrel", 0, ISMEAN|ISFLY, { XX, 1, 1, 7, ___, "1x4" } },
|
||||
{ "leprechaun", 0, 0, { XX, 10, 3, 8, ___, "1x1" } },
|
||||
{ "medusa", 40, ISMEAN, { XX,200, 8, 2, ___, "3x4/3x4/2x5" } },
|
||||
{ "nymph", 100, 0, { XX, 37, 3, 9, ___, "0x0" } },
|
||||
{ "orc", 15, ISGREED,{ XX, 5, 1, 6, ___, "1x8" } },
|
||||
{ "phantom", 0, ISINVIS,{ XX,120, 8, 3, ___, "4x4" } },
|
||||
{ "quagga", 0, ISMEAN, { XX, 15, 3, 3, ___, "1x5/1x5" } },
|
||||
{ "rattlesnake", 0, ISMEAN, { XX, 9, 2, 3, ___, "1x6" } },
|
||||
{ "snake", 0, ISMEAN, { XX, 2, 1, 5, ___, "1x3" } },
|
||||
{ "troll", 50, ISREGEN|ISMEAN,{ XX, 120, 6, 4, ___, "1x8/1x8/2x6" } },
|
||||
{ "black unicorn", 0, ISMEAN, { XX,190, 7, -2, ___, "1x9/1x9/2x9" } },
|
||||
{ "vampire", 20, ISREGEN|ISMEAN,{ XX,350, 8, 1, ___, "1x10" } },
|
||||
{ "wraith", 0, 0, { XX, 55, 5, 4, ___, "1x6" } },
|
||||
{ "xeroc", 30, 0, { XX,100, 7, 7, ___, "4x4" } },
|
||||
{ "yeti", 30, 0, { XX, 50, 4, 6, ___, "1x6/1x6" } },
|
||||
{ "zombie", 0, ISMEAN, { XX, 6, 2, 8, ___, "1x8" } }
|
||||
};
|
||||
#undef ___
|
||||
#undef XX
|
||||
|
||||
const struct obj_info origarm_info[MAXARMORS] = {
|
||||
{ "leather armor", 20, 20, NULL, FALSE },
|
||||
{ "ring mail", 15, 25, NULL, FALSE },
|
||||
{ "studded leather armor", 15, 20, NULL, FALSE },
|
||||
{ "scale mail", 13, 30, NULL, FALSE },
|
||||
{ "chain mail", 12, 75, NULL, FALSE },
|
||||
{ "splint mail", 10, 80, NULL, FALSE },
|
||||
{ "banded mail", 10, 90, NULL, FALSE },
|
||||
{ "plate mail", 5, 150, NULL, FALSE },
|
||||
};
|
||||
const struct obj_info origpot_info[MAXPOTIONS] = {
|
||||
{ "confusion", 7, 5, NULL, FALSE },
|
||||
{ "hallucination", 8, 5, NULL, FALSE },
|
||||
{ "poison", 8, 5, NULL, FALSE },
|
||||
{ "gain strength", 13, 150, NULL, FALSE },
|
||||
{ "see invisible", 3, 100, NULL, FALSE },
|
||||
{ "healing", 13, 130, NULL, FALSE },
|
||||
{ "monster detection", 6, 130, NULL, FALSE },
|
||||
{ "magic detection", 6, 105, NULL, FALSE },
|
||||
{ "raise level", 2, 250, NULL, FALSE },
|
||||
{ "extra healing", 5, 200, NULL, FALSE },
|
||||
{ "haste self", 5, 190, NULL, FALSE },
|
||||
{ "restore strength", 13, 130, NULL, FALSE },
|
||||
{ "blindness", 5, 5, NULL, FALSE },
|
||||
{ "levitation", 6, 75, NULL, FALSE },
|
||||
};
|
||||
const struct obj_info origring_info[MAXRINGS] = {
|
||||
{ "protection", 9, 400, NULL, FALSE },
|
||||
{ "add strength", 9, 400, NULL, FALSE },
|
||||
{ "sustain strength", 5, 280, NULL, FALSE },
|
||||
{ "searching", 10, 420, NULL, FALSE },
|
||||
{ "see invisible", 10, 310, NULL, FALSE },
|
||||
{ "adornment", 1, 10, NULL, FALSE },
|
||||
{ "aggravate monster", 10, 10, NULL, FALSE },
|
||||
{ "dexterity", 8, 440, NULL, FALSE },
|
||||
{ "increase damage", 8, 400, NULL, FALSE },
|
||||
{ "regeneration", 4, 460, NULL, FALSE },
|
||||
{ "slow digestion", 9, 240, NULL, FALSE },
|
||||
{ "teleportation", 5, 30, NULL, FALSE },
|
||||
{ "stealth", 7, 470, NULL, FALSE },
|
||||
{ "maintain armor", 5, 380, NULL, FALSE },
|
||||
};
|
||||
const struct obj_info origscr_info[MAXSCROLLS] = {
|
||||
{ "monster confusion", 7, 140, NULL, FALSE },
|
||||
{ "magic mapping", 4, 150, NULL, FALSE },
|
||||
{ "hold monster", 2, 180, NULL, FALSE },
|
||||
{ "sleep", 3, 5, NULL, FALSE },
|
||||
{ "enchant armor", 7, 160, NULL, FALSE },
|
||||
{ "identify potion", 10, 80, NULL, FALSE },
|
||||
{ "identify scroll", 10, 80, NULL, FALSE },
|
||||
{ "identify weapon", 6, 80, NULL, FALSE },
|
||||
{ "identify armor", 7, 100, NULL, FALSE },
|
||||
{ "identify ring, wand or staff", 10, 115, NULL, FALSE },
|
||||
{ "scare monster", 3, 200, NULL, FALSE },
|
||||
{ "food detection", 2, 60, NULL, FALSE },
|
||||
{ "teleportation", 5, 165, NULL, FALSE },
|
||||
{ "enchant weapon", 8, 150, NULL, FALSE },
|
||||
{ "create monster", 4, 75, NULL, FALSE },
|
||||
{ "remove curse", 7, 105, NULL, FALSE },
|
||||
{ "aggravate monsters", 3, 20, NULL, FALSE },
|
||||
{ "protect armor", 2, 250, NULL, FALSE },
|
||||
};
|
||||
const struct obj_info origweap_info[MAXWEAPONS + 1] = {
|
||||
{ "mace", 11, 8, NULL, FALSE },
|
||||
{ "long sword", 11, 15, NULL, FALSE },
|
||||
{ "short bow", 12, 15, NULL, FALSE },
|
||||
{ "arrow", 12, 1, NULL, FALSE },
|
||||
{ "dagger", 8, 3, NULL, FALSE },
|
||||
{ "two handed sword", 10, 75, NULL, FALSE },
|
||||
{ "dart", 12, 2, NULL, FALSE },
|
||||
{ "shuriken", 12, 5, NULL, FALSE },
|
||||
{ "spear", 12, 5, NULL, FALSE },
|
||||
{ NULL, 0 }, /* DO NOT REMOVE: fake entry for dragon's breath */
|
||||
};
|
||||
const struct obj_info origws_info[MAXSTICKS] = {
|
||||
{ "light", 12, 250, NULL, FALSE },
|
||||
{ "invisibility", 6, 5, NULL, FALSE },
|
||||
{ "lightning", 3, 330, NULL, FALSE },
|
||||
{ "fire", 3, 330, NULL, FALSE },
|
||||
{ "cold", 3, 330, NULL, FALSE },
|
||||
{ "polymorph", 15, 310, NULL, FALSE },
|
||||
{ "magic missile", 10, 170, NULL, FALSE },
|
||||
{ "haste monster", 10, 5, NULL, FALSE },
|
||||
{ "slow monster", 11, 350, NULL, FALSE },
|
||||
{ "drain life", 9, 300, NULL, FALSE },
|
||||
{ "nothing", 1, 5, NULL, FALSE },
|
||||
{ "teleport away", 6, 340, NULL, FALSE },
|
||||
{ "teleport to", 6, 50, NULL, FALSE },
|
||||
{ "cancellation", 5, 280, NULL, FALSE },
|
||||
};
|
||||
|
||||
const struct h_list helpstr[] = {
|
||||
{'?', " prints help", TRUE},
|
||||
{'/', " identify object", TRUE},
|
||||
{'h', " left", TRUE},
|
||||
{'j', " down", TRUE},
|
||||
{'k', " up", TRUE},
|
||||
{'l', " right", TRUE},
|
||||
{'y', " up & left", TRUE},
|
||||
{'u', " up & right", TRUE},
|
||||
{'b', " down & left", TRUE},
|
||||
{'n', " down & right", TRUE},
|
||||
{'H', " run left", FALSE},
|
||||
{'J', " run down", FALSE},
|
||||
{'K', " run up", FALSE},
|
||||
{'L', " run right", FALSE},
|
||||
{'Y', " run up & left", FALSE},
|
||||
{'U', " run up & right", FALSE},
|
||||
{'B', " run down & left", FALSE},
|
||||
{'N', " run down & right", FALSE},
|
||||
{CTRL('H'), " run left until adjacent", FALSE},
|
||||
{CTRL('J'), " run down until adjacent", FALSE},
|
||||
{CTRL('K'), " run up until adjacent", FALSE},
|
||||
{CTRL('L'), " run right until adjacent", FALSE},
|
||||
{CTRL('Y'), " run up & left until adjacent", FALSE},
|
||||
{CTRL('U'), " run up & right until adjacent", FALSE},
|
||||
{CTRL('B'), " run down & left until adjacent", FALSE},
|
||||
{CTRL('N'), " run down & right until adjacent", FALSE},
|
||||
{'\0', " <SHIFT><dir>: run that way", TRUE},
|
||||
{'\0', " <CTRL><dir>: run till adjacent", TRUE},
|
||||
{'f', "<dir> fight till death or near death", TRUE},
|
||||
{'t', "<dir> throw something", TRUE},
|
||||
{'m', "<dir> move onto without picking up", TRUE},
|
||||
{'z', "<dir> zap a wand in a direction", TRUE},
|
||||
{'^', "<dir> identify trap type", TRUE},
|
||||
{'s', " search for trap/secret door", TRUE},
|
||||
{'>', " go down a staircase", TRUE},
|
||||
{'<', " go up a staircase", TRUE},
|
||||
{'.', " rest for a turn", TRUE},
|
||||
{',', " pick something up", TRUE},
|
||||
{'i', " inventory", TRUE},
|
||||
{'I', " inventory single item", TRUE},
|
||||
{'q', " quaff potion", TRUE},
|
||||
{'r', " read scroll", TRUE},
|
||||
{'e', " eat food", TRUE},
|
||||
{'w', " wield a weapon", TRUE},
|
||||
{'W', " wear armor", TRUE},
|
||||
{'T', " take armor off", TRUE},
|
||||
{'P', " put on ring", TRUE},
|
||||
{'R', " remove ring", TRUE},
|
||||
{'d', " drop object", TRUE},
|
||||
{'c', " call object", TRUE},
|
||||
{'a', " repeat last command", TRUE},
|
||||
{')', " print current weapon", TRUE},
|
||||
{']', " print current armor", TRUE},
|
||||
{'=', " print current rings", TRUE},
|
||||
{'@', " print current stats", TRUE},
|
||||
{'D', " recall what's been discovered", TRUE},
|
||||
{'o', " examine/set options", TRUE},
|
||||
{CTRL('R'), " redraw screen", TRUE},
|
||||
{CTRL('P'), " repeat last message", TRUE},
|
||||
{ESCAPE, " cancel command", TRUE},
|
||||
{'S', " save game", TRUE},
|
||||
{'Q', " quit", TRUE},
|
||||
{'!', " shell escape", TRUE},
|
||||
{'F', "<dir> fight till either of you dies", TRUE},
|
||||
{'v', " print version number", TRUE},
|
||||
{0, NULL }
|
||||
};
|
||||
|
||||
const char *inv_t_name[] = {
|
||||
"Overwrite",
|
||||
"Slow",
|
||||
"Clear"
|
||||
};
|
||||
|
||||
const char *tr_name[] = { /* Names of the traps */
|
||||
"a trapdoor",
|
||||
"an arrow trap",
|
||||
"a sleeping gas trap",
|
||||
"a beartrap",
|
||||
"a teleport trap",
|
||||
"a poison dart trap",
|
||||
"a rust trap",
|
||||
"a mysterious trap"
|
||||
};
|
||||
|
||||
const int32_t a_class[MAXARMORS] = { /* Armor class for each armor type */
|
||||
8, /* LEATHER */
|
||||
7, /* RING_MAIL */
|
||||
7, /* STUDDED_LEATHER */
|
||||
6, /* SCALE_MAIL */
|
||||
5, /* CHAIN_MAIL */
|
||||
4, /* SPLINT_MAIL */
|
||||
4, /* BANDED_MAIL */
|
||||
3, /* PLATE_MAIL */
|
||||
};
|
||||
|
||||
const int32_t e_levels[] = {
|
||||
10L,
|
||||
20L,
|
||||
40L,
|
||||
80L,
|
||||
160L,
|
||||
320L,
|
||||
640L,
|
||||
1300L,
|
||||
2600L,
|
||||
5200L,
|
||||
13000L,
|
||||
26000L,
|
||||
50000L,
|
||||
100000L,
|
||||
200000L,
|
||||
400000L,
|
||||
800000L,
|
||||
2000000L,
|
||||
4000000L,
|
||||
8000000L,
|
||||
0L
|
||||
};
|
||||
|
||||
#include <memory.h>
|
||||
extern int between;
|
||||
extern int group;
|
||||
extern coord nh;
|
||||
|
||||
void externs_clear()
|
||||
{
|
||||
int i;
|
||||
after = 0; /* True if we want after daemons */
|
||||
again = 0; /* Repeating the last command */
|
||||
noscore = 0; /* Was a wizard sometime */
|
||||
seenstairs = 0; /* Have seen the stairs (for lsd) */
|
||||
amulet = FALSE; /* He found the amulet */
|
||||
door_stop = FALSE; /* Stop running when we pass a door */
|
||||
fight_flush = FALSE; /* True if toilet input */
|
||||
firstmove = FALSE; /* First move after setting door_stop */
|
||||
got_ltc = FALSE; /* We have gotten the local tty chars */
|
||||
has_hit = FALSE; /* Has a "hit" message pending in msg */
|
||||
in_shell = FALSE; /* True if executing a shell */
|
||||
inv_describe = TRUE; /* Say which way items are being used */
|
||||
jump = FALSE; /* Show running as series of jumps */
|
||||
kamikaze = FALSE; /* to_death really to DEATH */
|
||||
lower_msg = FALSE; /* Messages should start w/lower case */
|
||||
move_on = FALSE; /* Next move shouldn't pick up items */
|
||||
msg_esc = FALSE; /* Check for ESC from msg's --More-- */
|
||||
passgo = FALSE; /* Follow passages */
|
||||
playing = TRUE; /* True until he quits */
|
||||
q_comm = FALSE; /* Are we executing a 'Q' command? */
|
||||
running = FALSE; /* True if player is running */
|
||||
save_msg = TRUE; /* Remember last msg */
|
||||
see_floor = TRUE; /* Show the lamp illuminated floor */
|
||||
stat_msg = FALSE; /* Should status() print as a msg() */
|
||||
terse = FALSE; /* True if we should be short */
|
||||
to_death = FALSE; /* Fighting is to the death! */
|
||||
tombstone = TRUE; /* Print out tombstone at end */
|
||||
#ifdef MASTER
|
||||
int wizard = FALSE; /* True if allows wizard commands */
|
||||
#endif
|
||||
for (i=0; i<26; i++)
|
||||
pack_used[i] = FALSE;
|
||||
for (i=0; i<MAXSCROLLS; i++)
|
||||
if ( s_names[i] != 0 )
|
||||
free(s_names[i]);
|
||||
memset(s_names,0,sizeof(s_names));
|
||||
|
||||
dir_ch = 0; /* Direction from last get_dir() call */
|
||||
memset(file_name,0,sizeof(file_name));
|
||||
memset(huh,0,sizeof(huh));
|
||||
memset(p_colors,0,sizeof(p_colors));
|
||||
memset(prbuf,0,sizeof(prbuf));
|
||||
memset(r_stones,0,sizeof(r_stones));
|
||||
//memset(whoami,0,sizeof(whoami));
|
||||
memset(ws_made,0,sizeof(ws_made));
|
||||
memset(ws_type,0,sizeof(ws_type));
|
||||
runch = 0; /* Direction player is running */
|
||||
take = 0; /* Thing she is taking */
|
||||
orig_dsusp = 0; /* Original dsusp char */
|
||||
memset(home,0,sizeof(home));
|
||||
l_last_comm = '\0'; /* Last last_comm */
|
||||
l_last_dir = '\0'; /* Last last_dir */
|
||||
last_comm = '\0'; /* Last command typed */
|
||||
last_dir = '\0'; /* Last direction given */
|
||||
|
||||
n_objs = 0; /* # items listed in inventory() call */
|
||||
ntraps = 0; /* Number of traps on this level */
|
||||
hungry_state = 0; /* How hungry is he */
|
||||
inpack = 0; /* Number of things in pack */
|
||||
inv_type = 0; /* Type of inventory to use */
|
||||
level = 1; /* What level she is on */
|
||||
max_hit= 0; /* Max damage done to her in to_death */
|
||||
max_level = 0; /* Deepest player has gone */
|
||||
mpos = 0; /* Where cursor is on top line */
|
||||
no_food = 0; /* Number of levels without food */
|
||||
|
||||
count = 0; /* Number of times to repeat command */
|
||||
if ( scoreboard != NULL )
|
||||
{
|
||||
fclose(scoreboard);
|
||||
scoreboard = NULL; /* File descriptor for score file */
|
||||
}
|
||||
food_left = 0; /* Amount of food in hero's stomach */
|
||||
lastscore = -1; /* Score before this turn */
|
||||
no_command = 0; /* Number of turns asleep */
|
||||
no_move = 0; /* Number of turns held in place */
|
||||
purse = 0; /* How much gold he has */
|
||||
quiet = 0; /* Number of quiet turns */
|
||||
vf_hit = 0; /* Number of time flytrap has hit */
|
||||
|
||||
|
||||
memset(&delta,0,sizeof(delta));
|
||||
memset(&oldpos,0,sizeof(oldpos));
|
||||
memset(&stairs,0,sizeof(stairs));
|
||||
|
||||
memset(places,0,sizeof(places));
|
||||
cur_armor = NULL; /* What he is wearing */
|
||||
cur_ring[0] = cur_ring[1] = NULL; /* Which rings are being worn */
|
||||
cur_weapon = NULL; /* Which weapon he is weilding */
|
||||
l_last_pick = NULL; /* Last last_pick */
|
||||
last_pick = NULL; /* Last object picked in get_item() */
|
||||
lvl_obj = NULL; /* List of objects on this level */
|
||||
mlist = NULL; /* List of monsters on the level */
|
||||
memset(&player,0,sizeof(player)); /* His stats */
|
||||
/* restart of game */
|
||||
|
||||
max_stats = orig_max_stats; /* The maximum for the player */
|
||||
|
||||
oldrp = 0; /* Roomin(&oldpos) */
|
||||
memset(rooms,0,sizeof(rooms)); /* One for each room -- A level */
|
||||
|
||||
between = 0;
|
||||
group = 0;
|
||||
memset(&nh,0,sizeof(nh));
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Defines for things used in mach_dep.c
|
||||
*
|
||||
* @(#)extern.h 4.35 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
#ifndef H_EXTERN_ROGUE_H
|
||||
#define H_EXTERN_ROGUE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#ifdef PDCURSES
|
||||
#undef HAVE_UNISTD_H
|
||||
#undef HAVE_LIMITS_H
|
||||
#undef HAVE_MEMORY_H
|
||||
#undef HAVE_STRING_H
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
#include "config.h"
|
||||
#elif defined(__DJGPP__)
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#define HAVE_PROCESS_H 1
|
||||
#define HAVE_PWD_H 1
|
||||
#define HAVE_TERMIOS_H 1
|
||||
#define HAVE_SETGID 1
|
||||
#define HAVE_GETGID 1
|
||||
#define HAVE_SETUID 1
|
||||
#define HAVE_GETUID 1
|
||||
#define HAVE_GETPASS 1
|
||||
#define HAVE_SPAWNL 1
|
||||
#define HAVE_ALARM 1
|
||||
#define HAVE_ERASECHAR 1
|
||||
#define HAVE_KILLCHAR 1
|
||||
#elif defined(_WIN32)
|
||||
#define HAVE_CURSES_H
|
||||
#define HAVE_TERM_H
|
||||
#define HAVE__SPAWNL
|
||||
#define HAVE_SYS_TYPES_H
|
||||
#define HAVE_PROCESS_H
|
||||
#define HAVE_ERASECHAR 1
|
||||
#define HAVE_KILLCHAR 1
|
||||
#elif defined(__CYGWIN__)
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#define HAVE_PWD_H 1
|
||||
#define HAVE_PWD_H 1
|
||||
#define HAVE_SYS_UTSNAME_H 1
|
||||
#define HAVE_ARPA_INET_H 1
|
||||
#define HAVE_UNISTD_H 1
|
||||
#define HAVE_TERMIOS_H 1
|
||||
#define HAVE_NCURSES_TERM_H 1
|
||||
#define HAVE_ESCDELAY
|
||||
#define HAVE_SETGID 1
|
||||
#define HAVE_GETGID 1
|
||||
#define HAVE_SETUID 1
|
||||
#define HAVE_GETUID 1
|
||||
#define HAVE_GETPASS 1
|
||||
#define HAVE_GETPWUID 1
|
||||
#define HAVE_WORKING_FORK 1
|
||||
#define HAVE_ALARM 1
|
||||
#define HAVE_SPAWNL 1
|
||||
#define HAVE__SPAWNL 1
|
||||
#define HAVE_ERASECHAR 1
|
||||
#define HAVE_KILLCHAR 1
|
||||
#else /* POSIX */
|
||||
#define HAVE_SYS_TYPES_H 1
|
||||
#define HAVE_PWD_H 1
|
||||
#define HAVE_PWD_H 1
|
||||
#define HAVE_SYS_UTSNAME_H 1
|
||||
#define HAVE_ARPA_INET_H 1
|
||||
#define HAVE_UNISTD_H 1
|
||||
#define HAVE_TERMIOS_H 1
|
||||
#define HAVE_TERM_H 1
|
||||
#define HAVE_SETGID 1
|
||||
#define HAVE_GETGID 1
|
||||
#define HAVE_SETUID 1
|
||||
#define HAVE_GETUID 1
|
||||
#define HAVE_SETREUID 1
|
||||
#define HAVE_SETREGID 1
|
||||
#define HAVE_GETPASS 1
|
||||
#define HAVE_GETPWUID 1
|
||||
#define HAVE_WORKING_FORK 1
|
||||
#define HAVE_ERASECHAR 1
|
||||
#define HAVE_KILLCHAR 1
|
||||
#ifndef _AIX
|
||||
#define HAVE_GETLOADAVG 1
|
||||
#endif
|
||||
#define HAVE_ALARM 1
|
||||
#endif
|
||||
|
||||
#ifdef __DJGPP__
|
||||
#undef HAVE_GETPWUID /* DJGPP's limited version doesn't even work as documented */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Don't change the constants, since they are used for sizes in many
|
||||
* places in the program.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#undef SIGTSTP
|
||||
|
||||
#define MAXSTR 1024 /* maximum length of strings */
|
||||
#define MAXLINES 32 /* maximum number of screen lines used */
|
||||
#define MAXCOLS 80 /* maximum number of screen columns used */
|
||||
|
||||
#define RN ((int32_t)((seed = seed*11109+13849) >> 16) & 0xffff)
|
||||
#ifdef CTRL
|
||||
#undef CTRL
|
||||
#endif
|
||||
#define CTRL(c) (c & 037)
|
||||
|
||||
/*
|
||||
* Now all the global variables
|
||||
*/
|
||||
|
||||
extern bool got_ltc, in_shell;
|
||||
extern int wizard;
|
||||
extern char fruit[], prbuf[], whoami[];
|
||||
extern int orig_dsusp;
|
||||
extern FILE *scoreboard;
|
||||
|
||||
/*
|
||||
* Function types
|
||||
*/
|
||||
void externs_clear();
|
||||
|
||||
void auto_save(int);
|
||||
void endit(int sig);
|
||||
void fatal(char *);
|
||||
void getltchars(void);
|
||||
void leave(int);
|
||||
void my_exit(int st);
|
||||
void playltchars(void);
|
||||
void quit(int);
|
||||
int32_t _quit();
|
||||
|
||||
void resetltchars(void);
|
||||
void set_order(int *order, int numthings);
|
||||
void tstp(int ignored);
|
||||
|
||||
char *killname(char monst, bool doart);
|
||||
char *nothing(char type);
|
||||
char *type_name(int type);
|
||||
|
||||
#ifdef CHECKTIME
|
||||
int checkout(void);
|
||||
#endif
|
||||
|
||||
int md_chmod(char *filename, int mode);
|
||||
char *md_crypt(char *key, char *salt);
|
||||
int md_dsuspchar(void);
|
||||
int md_erasechar(void);
|
||||
char *md_gethomedir(void);
|
||||
char *md_getusername(void);
|
||||
int md_getuid(void);
|
||||
char *md_getpass(char *prompt);
|
||||
int md_getpid(void);
|
||||
char *md_getrealname(int uid);
|
||||
void md_init(void);
|
||||
int md_killchar(void);
|
||||
void md_normaluser(void);
|
||||
void md_raw_standout(void);
|
||||
void md_raw_standend(void);
|
||||
int md_readchar(void);
|
||||
int md_setdsuspchar(int c);
|
||||
int md_shellescape(void);
|
||||
void md_sleep(int s);
|
||||
int md_suspchar(void);
|
||||
int md_hasclreol(void);
|
||||
int md_unlink(char *file);
|
||||
int md_unlink_open_file(char *file, FILE *inf);
|
||||
void md_tstpsignal(void);
|
||||
void md_tstphold(void);
|
||||
void md_tstpresume(void);
|
||||
void md_ignoreallsignals(void);
|
||||
void md_onsignal_autosave(void);
|
||||
void md_onsignal_exit(void);
|
||||
void md_onsignal_default(void);
|
||||
int md_issymlink(char *sp);
|
||||
|
||||
int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,684 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* All the fighting gets done here
|
||||
*
|
||||
* @(#)fight.c 4.67 (Berkeley) 09/06/83
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <curses.h>
|
||||
//#include <string.h>
|
||||
//#include <ctype.h>
|
||||
#include "rogue.h"
|
||||
|
||||
//#define EQSTR(a, b) (strcmp(a, b) == 0)
|
||||
|
||||
const char *h_names[] = { /* strings for hitting */
|
||||
" scored an excellent hit on ",
|
||||
" hit ",
|
||||
" have injured ",
|
||||
" swing and hit ",
|
||||
" scored an excellent hit on ",
|
||||
" hit ",
|
||||
" has injured ",
|
||||
" swings and hits "
|
||||
};
|
||||
|
||||
const char *m_names[] = { /* strings for missing */
|
||||
" miss",
|
||||
" swing and miss",
|
||||
" barely miss",
|
||||
" don't hit",
|
||||
" misses",
|
||||
" swings and misses",
|
||||
" barely misses",
|
||||
" doesn't hit",
|
||||
};
|
||||
|
||||
/*
|
||||
* adjustments to hit probabilities due to strength
|
||||
*/
|
||||
static const int str_plus[] = {
|
||||
-7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
|
||||
};
|
||||
|
||||
/*
|
||||
* adjustments to damage done due to strength
|
||||
*/
|
||||
static const int add_dam[] = {
|
||||
-7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3,
|
||||
3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6
|
||||
};
|
||||
|
||||
/*
|
||||
* fight:
|
||||
* The player attacks the monster.
|
||||
*/
|
||||
int
|
||||
fight(struct rogue_state *rs,coord *mp, THING *weap, bool thrown)
|
||||
{
|
||||
register THING *tp;
|
||||
register bool did_hit = TRUE;
|
||||
register char *mname, ch;
|
||||
|
||||
/*
|
||||
* Find the monster we want to fight
|
||||
*/
|
||||
#ifdef MASTER
|
||||
if ((tp = moat(mp->y, mp->x)) == NULL)
|
||||
debug("Fight what @ %d,%d", mp->y, mp->x);
|
||||
#else
|
||||
tp = moat(mp->y, mp->x);
|
||||
#endif
|
||||
/*
|
||||
* Since we are fighting, things are not quiet so no healing takes
|
||||
* place.
|
||||
*/
|
||||
count = 0;
|
||||
quiet = 0;
|
||||
runto(rs,mp);
|
||||
/*
|
||||
* Let him know it was really a xeroc (if it was one).
|
||||
*/
|
||||
ch = '\0';
|
||||
if (tp->t_type == 'X' && tp->t_disguise != 'X' && !on(player, ISBLIND))
|
||||
{
|
||||
tp->t_disguise = 'X';
|
||||
if (on(player, ISHALU)) {
|
||||
ch = (char)(rnd(26) + 'A');
|
||||
mvaddch(tp->t_pos.y, tp->t_pos.x, ch);
|
||||
}
|
||||
msg(rs,choose_str("heavy! That's a nasty critter!",
|
||||
"wait! That's a xeroc!"));
|
||||
if (!thrown)
|
||||
return FALSE;
|
||||
}
|
||||
mname = set_mname(tp);
|
||||
did_hit = FALSE;
|
||||
has_hit = (terse && !to_death);
|
||||
if (roll_em(&player, tp, weap, thrown))
|
||||
{
|
||||
did_hit = FALSE;
|
||||
if (thrown)
|
||||
thunk(rs,weap, mname, terse);
|
||||
else
|
||||
hit(rs,(char *) NULL, mname, terse);
|
||||
if (on(player, CANHUH))
|
||||
{
|
||||
did_hit = TRUE;
|
||||
tp->t_flags |= ISHUH;
|
||||
player.t_flags &= ~CANHUH;
|
||||
endmsg(rs);
|
||||
has_hit = FALSE;
|
||||
msg(rs,"your hands stop glowing %s", pick_color("red"));
|
||||
}
|
||||
if (tp->t_stats.s_hpt <= 0)
|
||||
killed(rs,tp, TRUE);
|
||||
else if (did_hit && !on(player, ISBLIND))
|
||||
msg(rs,"%s appears confused", mname);
|
||||
did_hit = TRUE;
|
||||
}
|
||||
else
|
||||
if (thrown)
|
||||
bounce(rs,weap, mname, terse);
|
||||
else
|
||||
miss(rs,(char *) NULL, mname, terse);
|
||||
return did_hit;
|
||||
}
|
||||
|
||||
/*
|
||||
* attack:
|
||||
* The monster attacks the player
|
||||
*/
|
||||
int
|
||||
attack(struct rogue_state *rs,THING *mp)
|
||||
{
|
||||
register char *mname;
|
||||
register int oldhp;
|
||||
|
||||
/*
|
||||
* Since this is an attack, stop running and any healing that was
|
||||
* going on at the time.
|
||||
*/
|
||||
running = FALSE;
|
||||
count = 0;
|
||||
quiet = 0;
|
||||
if (to_death && !on(*mp, ISTARGET))
|
||||
{
|
||||
to_death = FALSE;
|
||||
kamikaze = FALSE;
|
||||
}
|
||||
if (mp->t_type == 'X' && mp->t_disguise != 'X' && !on(player, ISBLIND))
|
||||
{
|
||||
mp->t_disguise = 'X';
|
||||
if (on(player, ISHALU))
|
||||
mvaddch(mp->t_pos.y, mp->t_pos.x, rnd(26) + 'A');
|
||||
}
|
||||
mname = set_mname(mp);
|
||||
oldhp = pstats.s_hpt;
|
||||
if (roll_em(mp, &player, (THING *) NULL, FALSE))
|
||||
{
|
||||
if (mp->t_type != 'I')
|
||||
{
|
||||
if (has_hit)
|
||||
addmsg(rs,". ");
|
||||
hit(rs,mname, (char *) NULL, FALSE);
|
||||
}
|
||||
else
|
||||
if (has_hit)
|
||||
endmsg(rs);
|
||||
has_hit = FALSE;
|
||||
if (pstats.s_hpt <= 0)
|
||||
death(rs,mp->t_type); /* Bye bye life ... */
|
||||
else if (!kamikaze)
|
||||
{
|
||||
oldhp -= pstats.s_hpt;
|
||||
if (oldhp > max_hit)
|
||||
max_hit = oldhp;
|
||||
if (pstats.s_hpt <= max_hit)
|
||||
to_death = FALSE;
|
||||
}
|
||||
if (!on(*mp, ISCANC))
|
||||
switch (mp->t_type)
|
||||
{
|
||||
case 'A':
|
||||
/*
|
||||
* If an aquator hits, you can lose armor class.
|
||||
*/
|
||||
rust_armor(rs,cur_armor);
|
||||
when 'I':
|
||||
/*
|
||||
* The ice monster freezes you
|
||||
*/
|
||||
player.t_flags &= ~ISRUN;
|
||||
if (!no_command)
|
||||
{
|
||||
addmsg(rs,"you are frozen");
|
||||
if (!terse)
|
||||
addmsg(rs," by the %s", mname);
|
||||
endmsg(rs);
|
||||
}
|
||||
no_command += rnd(2) + 2;
|
||||
if (no_command > BORE_LEVEL)
|
||||
death(rs,'h');
|
||||
when 'R':
|
||||
/*
|
||||
* Rattlesnakes have poisonous bites
|
||||
*/
|
||||
if (!save(VS_POISON))
|
||||
{
|
||||
if (!ISWEARING(R_SUSTSTR))
|
||||
{
|
||||
chg_str(-1);
|
||||
if (!terse)
|
||||
msg(rs,"you feel a bite in your leg and now feel weaker");
|
||||
else
|
||||
msg(rs,"a bite has weakened you");
|
||||
}
|
||||
else if (!to_death)
|
||||
{
|
||||
if (!terse)
|
||||
msg(rs,"a bite momentarily weakens you");
|
||||
else
|
||||
msg(rs,"bite has no effect");
|
||||
}
|
||||
}
|
||||
when 'W':
|
||||
case 'V':
|
||||
/*
|
||||
* Wraiths might drain energy levels, and Vampires
|
||||
* can steal max_hp
|
||||
*/
|
||||
if (rnd(100) < (mp->t_type == 'W' ? 15 : 30))
|
||||
{
|
||||
register int fewer;
|
||||
|
||||
if (mp->t_type == 'W')
|
||||
{
|
||||
if (pstats.s_exp == 0)
|
||||
death(rs,'W'); /* All levels gone */
|
||||
if (--pstats.s_lvl == 0)
|
||||
{
|
||||
pstats.s_exp = 0;
|
||||
pstats.s_lvl = 1;
|
||||
}
|
||||
else
|
||||
pstats.s_exp = e_levels[pstats.s_lvl-1]+1;
|
||||
fewer = roll(1, 10);
|
||||
}
|
||||
else
|
||||
fewer = roll(1, 3);
|
||||
pstats.s_hpt -= fewer;
|
||||
max_hp -= fewer;
|
||||
if (pstats.s_hpt <= 0)
|
||||
pstats.s_hpt = 1;
|
||||
if (max_hp <= 0)
|
||||
death(rs,mp->t_type);
|
||||
msg(rs,"you suddenly feel weaker");
|
||||
}
|
||||
when 'F':
|
||||
/*
|
||||
* Venus Flytrap stops the poor guy from moving
|
||||
*/
|
||||
player.t_flags |= ISHELD;
|
||||
sprintf(monsters['F'-'A'].m_stats.s_dmg,"%dx1", ++vf_hit);
|
||||
if (--pstats.s_hpt <= 0)
|
||||
death(rs,'F');
|
||||
when 'L':
|
||||
{
|
||||
/*
|
||||
* Leperachaun steals some gold
|
||||
*/
|
||||
register int lastpurse;
|
||||
|
||||
lastpurse = purse;
|
||||
purse -= GOLDCALC;
|
||||
if (!save(VS_MAGIC))
|
||||
purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC;
|
||||
if (purse < 0)
|
||||
purse = 0;
|
||||
remove_mon(rs,&mp->t_pos, mp, FALSE);
|
||||
mp=NULL;
|
||||
if (purse != lastpurse)
|
||||
msg(rs,"your purse feels lighter");
|
||||
}
|
||||
when 'N':
|
||||
{
|
||||
THING *obj, *steal; int nobj;
|
||||
|
||||
/*
|
||||
* Nymph's steal a magic item, look through the pack
|
||||
* and pick out one we like.
|
||||
*/
|
||||
steal = NULL;
|
||||
for (nobj = 0, obj = pack; obj != NULL; obj = next(obj))
|
||||
if (obj != cur_armor && obj != cur_weapon
|
||||
&& obj != cur_ring[LEFT] && obj != cur_ring[RIGHT]
|
||||
&& is_magic(obj) && rnd(++nobj) == 0)
|
||||
steal = obj;
|
||||
if (steal != NULL)
|
||||
{
|
||||
remove_mon(rs,&mp->t_pos, moat(mp->t_pos.y, mp->t_pos.x), FALSE);
|
||||
mp=NULL;
|
||||
leave_pack(rs,steal, FALSE, FALSE);
|
||||
msg(rs,"she stole %s!", inv_name(steal, TRUE));
|
||||
if ( steal->o_count <= 0 )
|
||||
discard(steal);
|
||||
}
|
||||
}
|
||||
otherwise:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (mp->t_type != 'I')
|
||||
{
|
||||
if (has_hit)
|
||||
{
|
||||
addmsg(rs,". ");
|
||||
has_hit = FALSE;
|
||||
}
|
||||
if (mp->t_type == 'F')
|
||||
{
|
||||
pstats.s_hpt -= vf_hit;
|
||||
if (pstats.s_hpt <= 0)
|
||||
death(rs,mp->t_type); /* Bye bye life ... */
|
||||
}
|
||||
miss(rs,mname, (char *) NULL, FALSE);
|
||||
}
|
||||
if (fight_flush && !to_death)
|
||||
flush_type();
|
||||
count = 0;
|
||||
status(rs);
|
||||
if (mp == NULL)
|
||||
return(-1);
|
||||
else
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* set_mname:
|
||||
* return the monster name for the given monster
|
||||
*/
|
||||
char *
|
||||
set_mname(THING *tp)
|
||||
{
|
||||
int ch;
|
||||
char *mname;
|
||||
static char tbuf[MAXSTR] = { 't', 'h', 'e', ' ' };
|
||||
|
||||
if (!see_monst(tp) && !on(player, SEEMONST))
|
||||
return (terse ? (char *)"it" : (char *)"something");
|
||||
else if (on(player, ISHALU))
|
||||
{
|
||||
move(tp->t_pos.y, tp->t_pos.x);
|
||||
ch = toascii(inch());
|
||||
if (!isupper(ch))
|
||||
ch = rnd(26);
|
||||
else
|
||||
ch -= 'A';
|
||||
mname = monsters[ch].m_name;
|
||||
}
|
||||
else
|
||||
mname = monsters[tp->t_type - 'A'].m_name;
|
||||
strcpy(&tbuf[4], mname);
|
||||
return tbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* swing:
|
||||
* Returns true if the swing hits
|
||||
*/
|
||||
int
|
||||
swing(int at_lvl, int op_arm, int wplus)
|
||||
{
|
||||
int res = rnd(20);
|
||||
int need = (20 - at_lvl) - op_arm;
|
||||
|
||||
return (res + wplus >= need);
|
||||
}
|
||||
|
||||
/*
|
||||
* roll_em:
|
||||
* Roll several attacks
|
||||
*/
|
||||
bool
|
||||
roll_em(THING *thatt, THING *thdef, THING *weap, bool hurl)
|
||||
{
|
||||
register struct stats *att, *def;
|
||||
register char *cp;
|
||||
register int ndice, nsides, def_arm;
|
||||
register bool did_hit = FALSE;
|
||||
register int hplus;
|
||||
register int dplus;
|
||||
register int damage;
|
||||
|
||||
att = &thatt->t_stats;
|
||||
def = &thdef->t_stats;
|
||||
if (weap == NULL)
|
||||
{
|
||||
cp = att->s_dmg;
|
||||
dplus = 0;
|
||||
hplus = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
hplus = (weap == NULL ? 0 : weap->o_hplus);
|
||||
dplus = (weap == NULL ? 0 : weap->o_dplus);
|
||||
if (weap == cur_weapon)
|
||||
{
|
||||
if (ISRING(LEFT, R_ADDDAM))
|
||||
dplus += cur_ring[LEFT]->o_arm;
|
||||
else if (ISRING(LEFT, R_ADDHIT))
|
||||
hplus += cur_ring[LEFT]->o_arm;
|
||||
if (ISRING(RIGHT, R_ADDDAM))
|
||||
dplus += cur_ring[RIGHT]->o_arm;
|
||||
else if (ISRING(RIGHT, R_ADDHIT))
|
||||
hplus += cur_ring[RIGHT]->o_arm;
|
||||
}
|
||||
cp = weap->o_damage;
|
||||
if (hurl)
|
||||
{
|
||||
if ((weap->o_flags&ISMISL) && cur_weapon != NULL &&
|
||||
cur_weapon->o_which == weap->o_launch)
|
||||
{
|
||||
cp = weap->o_hurldmg;
|
||||
hplus += cur_weapon->o_hplus;
|
||||
dplus += cur_weapon->o_dplus;
|
||||
}
|
||||
else if (weap->o_launch < 0)
|
||||
cp = weap->o_hurldmg;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If the creature being attacked is not running (alseep or held)
|
||||
* then the attacker gets a plus four bonus to hit.
|
||||
*/
|
||||
if (!on(*thdef, ISRUN))
|
||||
hplus += 4;
|
||||
def_arm = def->s_arm;
|
||||
if (def == &pstats)
|
||||
{
|
||||
if (cur_armor != NULL)
|
||||
def_arm = cur_armor->o_arm;
|
||||
if (ISRING(LEFT, R_PROTECT))
|
||||
def_arm -= cur_ring[LEFT]->o_arm;
|
||||
if (ISRING(RIGHT, R_PROTECT))
|
||||
def_arm -= cur_ring[RIGHT]->o_arm;
|
||||
}
|
||||
while(cp != NULL && *cp != '\0')
|
||||
{
|
||||
ndice = atoi(cp);
|
||||
if ((cp = strchr(cp, 'x')) == NULL)
|
||||
break;
|
||||
nsides = atoi(++cp);
|
||||
if (swing(att->s_lvl, def_arm, hplus + str_plus[att->s_str]))
|
||||
{
|
||||
int proll;
|
||||
|
||||
proll = roll(ndice, nsides);
|
||||
#ifdef MASTER
|
||||
if (ndice + nsides > 0 && proll <= 0)
|
||||
debug("Damage for %dx%d came out %d, dplus = %d, add_dam = %d, def_arm = %d", ndice, nsides, proll, dplus, add_dam[att->s_str], def_arm);
|
||||
#endif
|
||||
damage = dplus + proll + add_dam[att->s_str];
|
||||
def->s_hpt -= max(0, damage);
|
||||
did_hit = TRUE;
|
||||
}
|
||||
if ((cp = strchr(cp, '/')) == NULL)
|
||||
break;
|
||||
cp++;
|
||||
}
|
||||
return did_hit;
|
||||
}
|
||||
|
||||
/*
|
||||
* prname:
|
||||
* The print name of a combatant
|
||||
*/
|
||||
char *
|
||||
prname(char *mname, bool upper)
|
||||
{
|
||||
static char tbuf[MAXSTR];
|
||||
|
||||
*tbuf = '\0';
|
||||
if (mname == 0)
|
||||
strcpy(tbuf, "you");
|
||||
else
|
||||
strcpy(tbuf, mname);
|
||||
if (upper)
|
||||
*tbuf = (char) toupper(*tbuf);
|
||||
return tbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* thunk:
|
||||
* A missile hits a monster
|
||||
*/
|
||||
void
|
||||
thunk(struct rogue_state *rs,THING *weap, char *mname, bool noend)
|
||||
{
|
||||
if (to_death)
|
||||
return;
|
||||
if (weap->o_type == WEAPON)
|
||||
addmsg(rs,"the %s hits ", weap_info[weap->o_which].oi_name);
|
||||
else
|
||||
addmsg(rs,"you hit ");
|
||||
addmsg(rs,"%s", mname);
|
||||
if (!noend)
|
||||
endmsg(rs);
|
||||
}
|
||||
|
||||
/*
|
||||
* hit:
|
||||
* Print a message to indicate a succesful hit
|
||||
*/
|
||||
|
||||
void
|
||||
hit(struct rogue_state *rs,char *er, char *ee, bool noend)
|
||||
{
|
||||
int32_t i; const char *s;
|
||||
|
||||
if (to_death)
|
||||
return;
|
||||
addmsg(rs,prname(er, TRUE));
|
||||
if (terse)
|
||||
s = " hit";
|
||||
else
|
||||
{
|
||||
i = rnd(4);
|
||||
if (er != NULL)
|
||||
i += 4;
|
||||
s = h_names[i];
|
||||
}
|
||||
addmsg(rs,(char *)s);
|
||||
if (!terse)
|
||||
addmsg(rs,prname(ee, FALSE));
|
||||
if (!noend)
|
||||
endmsg(rs);
|
||||
}
|
||||
|
||||
/*
|
||||
* miss:
|
||||
* Print a message to indicate a poor swing
|
||||
*/
|
||||
void
|
||||
miss(struct rogue_state *rs,char *er, char *ee, bool noend)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (to_death)
|
||||
return;
|
||||
addmsg(rs,prname(er, TRUE));
|
||||
if (terse)
|
||||
i = 0;
|
||||
else
|
||||
i = rnd(4);
|
||||
if (er != NULL)
|
||||
i += 4;
|
||||
addmsg(rs,(char *)m_names[i]);
|
||||
if (!terse)
|
||||
addmsg(rs," %s", prname(ee, FALSE));
|
||||
if (!noend)
|
||||
endmsg(rs);
|
||||
}
|
||||
|
||||
/*
|
||||
* bounce:
|
||||
* A missile misses a monster
|
||||
*/
|
||||
void
|
||||
bounce(struct rogue_state *rs,THING *weap, char *mname, bool noend)
|
||||
{
|
||||
if (to_death)
|
||||
return;
|
||||
if (weap->o_type == WEAPON)
|
||||
addmsg(rs,"the %s misses ", weap_info[weap->o_which].oi_name);
|
||||
else
|
||||
addmsg(rs,"you missed ");
|
||||
addmsg(rs,mname);
|
||||
if (!noend)
|
||||
endmsg(rs);
|
||||
}
|
||||
|
||||
/*
|
||||
* remove_mon:
|
||||
* Remove a monster from the screen
|
||||
*/
|
||||
void
|
||||
remove_mon(struct rogue_state *rs,coord *mp, THING *tp, bool waskill)
|
||||
{
|
||||
register THING *obj, *nexti;
|
||||
for (obj = tp->t_pack; obj != NULL; obj = nexti)
|
||||
{
|
||||
nexti = next(obj);
|
||||
obj->o_pos = tp->t_pos;
|
||||
detach(tp->t_pack, obj);
|
||||
if (waskill)
|
||||
fall(rs,obj, FALSE);
|
||||
else discard(obj);
|
||||
}
|
||||
moat(mp->y, mp->x) = NULL;
|
||||
mvaddch(mp->y, mp->x, tp->t_oldch);
|
||||
detach(mlist, tp);
|
||||
if (on(*tp, ISTARGET))
|
||||
{
|
||||
kamikaze = FALSE;
|
||||
to_death = FALSE;
|
||||
if (fight_flush)
|
||||
flush_type();
|
||||
}
|
||||
discard(tp);
|
||||
}
|
||||
|
||||
/*
|
||||
* killed:
|
||||
* Called to put a monster to death
|
||||
*/
|
||||
void
|
||||
killed(struct rogue_state *rs,THING *tp, bool pr)
|
||||
{
|
||||
char *mname;
|
||||
|
||||
pstats.s_exp += tp->t_stats.s_exp;
|
||||
|
||||
/*
|
||||
* If the monster was a venus flytrap, un-hold him
|
||||
*/
|
||||
switch (tp->t_type)
|
||||
{
|
||||
case 'F':
|
||||
player.t_flags &= ~ISHELD;
|
||||
vf_hit = 0;
|
||||
strcpy(monsters['F'-'A'].m_stats.s_dmg, "000x0");
|
||||
when 'L':
|
||||
{
|
||||
THING *gold;
|
||||
|
||||
if (fallpos(&tp->t_pos, &tp->t_room->r_gold) && level >= max_level)
|
||||
{
|
||||
gold = new_item();
|
||||
gold->o_type = GOLD;
|
||||
gold->o_goldval = GOLDCALC;
|
||||
if (save(VS_MAGIC))
|
||||
gold->o_goldval += GOLDCALC + GOLDCALC
|
||||
+ GOLDCALC + GOLDCALC;
|
||||
attach(tp->t_pack, gold);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Get rid of the monster.
|
||||
*/
|
||||
mname = set_mname(tp);
|
||||
remove_mon(rs,&tp->t_pos, tp, TRUE);
|
||||
if (pr)
|
||||
{
|
||||
if (has_hit)
|
||||
{
|
||||
addmsg(rs,". Defeated ");
|
||||
has_hit = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!terse)
|
||||
addmsg(rs,"you have ");
|
||||
addmsg(rs,"defeated ");
|
||||
}
|
||||
msg(rs,mname);
|
||||
}
|
||||
/*
|
||||
* Do adjustments if he went up a level
|
||||
*/
|
||||
check_level(rs);
|
||||
if (fight_flush)
|
||||
flush_type();
|
||||
}
|
||||
@@ -1,498 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* global variable initializaton
|
||||
*
|
||||
* @(#)init.c 4.31 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <curses.h>
|
||||
//#include <ctype.h>
|
||||
//#include <string.h>
|
||||
#include "rogue.h"
|
||||
|
||||
/*
|
||||
* init_player:
|
||||
* Roll her up
|
||||
*/
|
||||
void rogue_restoreobject(THING *o,struct rogue_packitem *item);
|
||||
|
||||
int32_t rogue_total(THING *o)
|
||||
{
|
||||
if ( (o->o_flags & ISMANY) != 0 )
|
||||
return(1);
|
||||
else return(o->o_count);
|
||||
}
|
||||
|
||||
void restore_player(struct rogue_state *rs)
|
||||
{
|
||||
int32_t i,total = 0; THING *obj;
|
||||
//rs->P.gold = purse;
|
||||
max_hp = rs->P.hitpoints;
|
||||
//pstats.s_hpt = max_hp;
|
||||
pstats.s_str = rs->P.strength & 0xffff;
|
||||
if ( (max_stats.s_str= (rs->P.strength >> 16) & 0xffff) == 0 )
|
||||
max_stats.s_str = 16;
|
||||
if ( pstats.s_str > max_stats.s_str )
|
||||
pstats.s_str = max_stats.s_str;
|
||||
pstats.s_lvl = rs->P.level;
|
||||
pstats.s_exp = rs->P.experience;
|
||||
for (i=0; i<rs->P.packsize&&i<MAXPACK; i++)
|
||||
{
|
||||
obj = new_item();
|
||||
rogue_restoreobject(obj,&rs->P.roguepack[i]);
|
||||
total += rogue_total(obj);
|
||||
if ( total > ROGUE_MAXTOTAL )
|
||||
break;
|
||||
add_pack(rs,obj,TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void init_player(struct rogue_state *rs)
|
||||
{
|
||||
register THING *obj; int32_t i;
|
||||
pstats = max_stats;
|
||||
food_left = HUNGERTIME;
|
||||
|
||||
if ( rs->restoring != 0 )
|
||||
{
|
||||
// duplicate rng usage of normal case
|
||||
obj = new_item();
|
||||
init_weapon(obj, MACE);
|
||||
free(obj);
|
||||
obj = new_item();
|
||||
init_weapon(obj, BOW);
|
||||
free(obj);
|
||||
obj = new_item();
|
||||
init_weapon(obj, ARROW);
|
||||
obj->o_count = rnd(15) + 25;
|
||||
free(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Give him some food
|
||||
*/
|
||||
obj = new_item();
|
||||
obj->o_type = FOOD;
|
||||
obj->o_count = 1;
|
||||
add_pack(rs,obj, TRUE);
|
||||
/*
|
||||
* And his suit of armor
|
||||
*/
|
||||
obj = new_item();
|
||||
obj->o_type = ARMOR;
|
||||
obj->o_which = RING_MAIL;
|
||||
obj->o_arm = a_class[RING_MAIL] - 1;
|
||||
obj->o_flags |= ISKNOW;
|
||||
obj->o_count = 1;
|
||||
cur_armor = obj;
|
||||
add_pack(rs,obj, TRUE);
|
||||
/*
|
||||
* Give him his weaponry. First a mace.
|
||||
*/
|
||||
obj = new_item();
|
||||
init_weapon(obj, MACE);
|
||||
obj->o_hplus = 1;
|
||||
obj->o_dplus = 1;
|
||||
obj->o_flags |= ISKNOW;
|
||||
add_pack(rs,obj, TRUE);
|
||||
cur_weapon = obj;
|
||||
/*
|
||||
* Now a +1 bow
|
||||
*/
|
||||
obj = new_item();
|
||||
init_weapon(obj, BOW);
|
||||
obj->o_hplus = 1;
|
||||
obj->o_flags |= ISKNOW;
|
||||
add_pack(rs,obj, TRUE);
|
||||
/*
|
||||
* Now some arrows
|
||||
*/
|
||||
obj = new_item();
|
||||
init_weapon(obj, ARROW);
|
||||
obj->o_count = rnd(15) + 25;
|
||||
obj->o_flags |= ISKNOW;
|
||||
add_pack(rs,obj, TRUE);
|
||||
//fprintf(stderr,"initial o_count.%d\n",obj->o_count); sleep(3);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Contains defintions and functions for dealing with things like
|
||||
* potions and scrolls
|
||||
*/
|
||||
|
||||
const char *rainbow[] = {
|
||||
"amber",
|
||||
"aquamarine",
|
||||
"black",
|
||||
"blue",
|
||||
"brown",
|
||||
"clear",
|
||||
"crimson",
|
||||
"cyan",
|
||||
"ecru",
|
||||
"gold",
|
||||
"green",
|
||||
"grey",
|
||||
"magenta",
|
||||
"orange",
|
||||
"pink",
|
||||
"plaid",
|
||||
"purple",
|
||||
"red",
|
||||
"silver",
|
||||
"tan",
|
||||
"tangerine",
|
||||
"topaz",
|
||||
"turquoise",
|
||||
"vermilion",
|
||||
"violet",
|
||||
"white",
|
||||
"yellow",
|
||||
};
|
||||
|
||||
#define NCOLORS (sizeof rainbow / sizeof (char *))
|
||||
int cNCOLORS = NCOLORS;
|
||||
|
||||
static const char *sylls[] = {
|
||||
"a", "ab", "ag", "aks", "ala", "an", "app", "arg", "arze", "ash",
|
||||
"bek", "bie", "bit", "bjor", "blu", "bot", "bu", "byt", "comp",
|
||||
"con", "cos", "cre", "dalf", "dan", "den", "do", "e", "eep", "el",
|
||||
"eng", "er", "ere", "erk", "esh", "evs", "fa", "fid", "fri", "fu",
|
||||
"gan", "gar", "glen", "gop", "gre", "ha", "hyd", "i", "ing", "ip",
|
||||
"ish", "it", "ite", "iv", "jo", "kho", "kli", "klis", "la", "lech",
|
||||
"mar", "me", "mi", "mic", "mik", "mon", "mung", "mur", "nej",
|
||||
"nelg", "nep", "ner", "nes", "nes", "nih", "nin", "o", "od", "ood",
|
||||
"org", "orn", "ox", "oxy", "pay", "ple", "plu", "po", "pot",
|
||||
"prok", "re", "rea", "rhov", "ri", "ro", "rog", "rok", "rol", "sa",
|
||||
"san", "sat", "sef", "seh", "shu", "ski", "sna", "sne", "snik",
|
||||
"sno", "so", "sol", "sri", "sta", "sun", "ta", "tab", "tem",
|
||||
"ther", "ti", "tox", "trol", "tue", "turs", "u", "ulk", "um", "un",
|
||||
"uni", "ur", "val", "viv", "vly", "vom", "wah", "wed", "werg",
|
||||
"wex", "whon", "wun", "xo", "y", "yot", "yu", "zant", "zeb", "zim",
|
||||
"zok", "zon", "zum",
|
||||
};
|
||||
|
||||
const STONE stones[] = {
|
||||
{ "agate", 25},
|
||||
{ "alexandrite", 40},
|
||||
{ "amethyst", 50},
|
||||
{ "carnelian", 40},
|
||||
{ "diamond", 300},
|
||||
{ "emerald", 300},
|
||||
{ "germanium", 225},
|
||||
{ "granite", 5},
|
||||
{ "garnet", 50},
|
||||
{ "jade", 150},
|
||||
{ "kryptonite", 300},
|
||||
{ "lapis lazuli", 50},
|
||||
{ "moonstone", 50},
|
||||
{ "obsidian", 15},
|
||||
{ "onyx", 60},
|
||||
{ "opal", 200},
|
||||
{ "pearl", 220},
|
||||
{ "peridot", 63},
|
||||
{ "ruby", 350},
|
||||
{ "sapphire", 285},
|
||||
{ "stibotantalite", 200},
|
||||
{ "tiger eye", 50},
|
||||
{ "topaz", 60},
|
||||
{ "turquoise", 70},
|
||||
{ "taaffeite", 300},
|
||||
{ "zircon", 80},
|
||||
};
|
||||
|
||||
#define NSTONES (sizeof stones / sizeof (STONE))
|
||||
int cNSTONES = NSTONES;
|
||||
|
||||
const char *wood[] = {
|
||||
"avocado wood",
|
||||
"balsa",
|
||||
"bamboo",
|
||||
"banyan",
|
||||
"birch",
|
||||
"cedar",
|
||||
"cherry",
|
||||
"cinnibar",
|
||||
"cypress",
|
||||
"dogwood",
|
||||
"driftwood",
|
||||
"ebony",
|
||||
"elm",
|
||||
"eucalyptus",
|
||||
"fall",
|
||||
"hemlock",
|
||||
"holly",
|
||||
"ironwood",
|
||||
"kukui wood",
|
||||
"mahogany",
|
||||
"manzanita",
|
||||
"maple",
|
||||
"oaken",
|
||||
"persimmon wood",
|
||||
"pecan",
|
||||
"pine",
|
||||
"poplar",
|
||||
"redwood",
|
||||
"rosewood",
|
||||
"spruce",
|
||||
"teak",
|
||||
"walnut",
|
||||
"zebrawood",
|
||||
};
|
||||
|
||||
#define NWOOD (sizeof wood / sizeof (char *))
|
||||
int cNWOOD = NWOOD;
|
||||
|
||||
const char *metal[] = {
|
||||
"aluminum",
|
||||
"beryllium",
|
||||
"bone",
|
||||
"brass",
|
||||
"bronze",
|
||||
"copper",
|
||||
"electrum",
|
||||
"gold",
|
||||
"iron",
|
||||
"lead",
|
||||
"magnesium",
|
||||
"mercury",
|
||||
"nickel",
|
||||
"pewter",
|
||||
"platinum",
|
||||
"steel",
|
||||
"silver",
|
||||
"silicon",
|
||||
"tin",
|
||||
"titanium",
|
||||
"tungsten",
|
||||
"zinc",
|
||||
};
|
||||
|
||||
#define NMETAL (sizeof metal / sizeof (char *))
|
||||
int cNMETAL = NMETAL;
|
||||
#define MAX3(a,b,c) (a > b ? (a > c ? a : c) : (b > c ? b : c))
|
||||
|
||||
static bool used[MAX3(NCOLORS, NSTONES, NWOOD)];
|
||||
|
||||
/*
|
||||
* init_colors:
|
||||
* Initialize the potion color scheme for this time
|
||||
*/
|
||||
void
|
||||
init_colors()
|
||||
{
|
||||
register int i, j;
|
||||
memset(used,0,sizeof(used));
|
||||
for (i = 0; i < NCOLORS; i++)
|
||||
used[i] = FALSE;
|
||||
for (i = 0; i < MAXPOTIONS; i++)
|
||||
{
|
||||
do
|
||||
j = rnd(NCOLORS);
|
||||
until (!used[j]);
|
||||
used[j] = TRUE;
|
||||
p_colors[i] = rainbow[j];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* init_names:
|
||||
* Generate the names of the various scrolls
|
||||
*/
|
||||
#define MAXNAME 40 /* Max number of characters in a name */
|
||||
|
||||
void
|
||||
init_names()
|
||||
{
|
||||
register int nsyl;
|
||||
register char *cp; const char *sp;
|
||||
register int i, nwords;
|
||||
|
||||
for (i = 0; i < MAXSCROLLS; i++)
|
||||
{
|
||||
cp = prbuf;
|
||||
nwords = rnd(3) + 2;
|
||||
while (nwords--)
|
||||
{
|
||||
nsyl = rnd(3) + 1;
|
||||
while (nsyl--)
|
||||
{
|
||||
sp = sylls[rnd((sizeof sylls) / (sizeof (char *)))];
|
||||
if (&cp[strlen(sp)] > &prbuf[MAXNAME])
|
||||
break;
|
||||
while (*sp)
|
||||
*cp++ = *sp++;
|
||||
}
|
||||
*cp++ = ' ';
|
||||
}
|
||||
*--cp = '\0';
|
||||
s_names[i] = (char *) malloc((unsigned) strlen(prbuf)+1);
|
||||
strcpy(s_names[i], prbuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* init_stones:
|
||||
* Initialize the ring stone setting scheme for this time
|
||||
*/
|
||||
void
|
||||
init_stones()
|
||||
{
|
||||
register int i, j;
|
||||
for (i = 0; i < NSTONES; i++)
|
||||
used[i] = FALSE;
|
||||
for (i = 0; i < MAXRINGS; i++)
|
||||
{
|
||||
do
|
||||
j = rnd(NSTONES);
|
||||
until (!used[j]);
|
||||
used[j] = TRUE;
|
||||
r_stones[i] = stones[j].st_name;
|
||||
ring_info[i].oi_worth += stones[j].st_value;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* init_materials:
|
||||
* Initialize the construction materials for wands and staffs
|
||||
*/
|
||||
void
|
||||
init_materials()
|
||||
{
|
||||
register int i, j;
|
||||
register const char *str;
|
||||
static bool metused[NMETAL];
|
||||
memset(metused,0,sizeof(metused));
|
||||
for (i = 0; i < NWOOD; i++)
|
||||
used[i] = FALSE;
|
||||
for (i = 0; i < NMETAL; i++)
|
||||
metused[i] = FALSE;
|
||||
for (i = 0; i < MAXSTICKS; i++)
|
||||
{
|
||||
for (;;)
|
||||
if (rnd(2) == 0)
|
||||
{
|
||||
j = rnd(NMETAL);
|
||||
if (!metused[j])
|
||||
{
|
||||
ws_type[i] = "wand";
|
||||
str = metal[j];
|
||||
metused[j] = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
j = rnd(NWOOD);
|
||||
if (!used[j])
|
||||
{
|
||||
ws_type[i] = "staff";
|
||||
str = wood[j];
|
||||
used[j] = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ws_made[i] = str;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MASTER
|
||||
# define NT NUMTHINGS, "things"
|
||||
# define MP MAXPOTIONS, "potions"
|
||||
# define MS MAXSCROLLS, "scrolls"
|
||||
# define MR MAXRINGS, "rings"
|
||||
# define MWS MAXSTICKS, "sticks"
|
||||
# define MW MAXWEAPONS, "weapons"
|
||||
# define MA MAXARMORS, "armor"
|
||||
#else
|
||||
# define NT NUMTHINGS
|
||||
# define MP MAXPOTIONS
|
||||
# define MS MAXSCROLLS
|
||||
# define MR MAXRINGS
|
||||
# define MWS MAXSTICKS
|
||||
# define MW MAXWEAPONS
|
||||
# define MA MAXARMORS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* sumprobs:
|
||||
* Sum up the probabilities for items appearing
|
||||
*/
|
||||
void
|
||||
sumprobs(struct obj_info *info, int bound
|
||||
#ifdef MASTER
|
||||
, char *name
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#ifdef MASTER
|
||||
struct obj_info *start = info;
|
||||
#endif
|
||||
struct obj_info *endp;
|
||||
endp = info + bound;
|
||||
while (++info < endp)
|
||||
info->oi_prob += (info - 1)->oi_prob;
|
||||
#ifdef MASTER
|
||||
badcheck(name, start, bound);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* init_probs:
|
||||
* Initialize the probabilities for the various items
|
||||
*/
|
||||
void
|
||||
init_probs()
|
||||
{
|
||||
sumprobs(things, NT);
|
||||
sumprobs(pot_info, MP);
|
||||
sumprobs(scr_info, MS);
|
||||
sumprobs(ring_info, MR);
|
||||
sumprobs(ws_info, MWS);
|
||||
sumprobs(weap_info, MW);
|
||||
sumprobs(arm_info, MA);
|
||||
}
|
||||
|
||||
#ifdef MASTER
|
||||
/*
|
||||
* badcheck:
|
||||
* Check to see if a series of probabilities sums to 100
|
||||
*/
|
||||
void
|
||||
badcheck(char *name, struct obj_info *info, int bound)
|
||||
{
|
||||
register struct obj_info *end;
|
||||
|
||||
if (info[bound - 1].oi_prob == 100)
|
||||
return;
|
||||
printf("\nBad percentages for %s (bound = %d):\n", name, bound);
|
||||
for (end = &info[bound]; info < end; info++)
|
||||
printf("%3d%% %s\n", info->oi_prob, info->oi_name);
|
||||
printf("[hit RETURN to continue]");
|
||||
fflush(stdout);
|
||||
while (getchar() != '\n')
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* pick_color:
|
||||
* If he is halucinating, pick a random color name and return it,
|
||||
* otherwise return the given color.
|
||||
*/
|
||||
char *
|
||||
pick_color(char *col)
|
||||
{
|
||||
return (on(player, ISHALU) ? (char *)rainbow[rnd(NCOLORS)] : col);
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2005-05-14.22
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch. It can only install one file at a time, a restriction
|
||||
# shared with many OS's install programs.
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit="${DOITPROG-}"
|
||||
|
||||
# put in absolute paths if you don't have them in your path; or use env. vars.
|
||||
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
mkdirprog="${MKDIRPROG-mkdir}"
|
||||
|
||||
chmodcmd="$chmodprog 0755"
|
||||
chowncmd=
|
||||
chgrpcmd=
|
||||
stripcmd=
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dstarg=
|
||||
no_target_directory=
|
||||
|
||||
usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
-c (ignored)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test -n "$1"; do
|
||||
case $1 in
|
||||
-c) shift
|
||||
continue;;
|
||||
|
||||
-d) dir_arg=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) chmodcmd="$chmodprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-s) stripcmd=$stripprog
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-t) dstarg=$2
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-T) no_target_directory=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
*) # When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
test -n "$dir_arg$dstarg" && break
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dstarg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dstarg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dstarg=$arg
|
||||
done
|
||||
break;;
|
||||
esac
|
||||
done
|
||||
|
||||
if test -z "$1"; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call `install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names starting with `-'.
|
||||
case $src in
|
||||
-*) src=./$src ;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
src=
|
||||
|
||||
if test -d "$dst"; then
|
||||
mkdircmd=:
|
||||
chmodcmd=
|
||||
else
|
||||
mkdircmd=$mkdirprog
|
||||
fi
|
||||
else
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dstarg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dst=$dstarg
|
||||
# Protect names starting with `-'.
|
||||
case $dst in
|
||||
-*) dst=./$dst ;;
|
||||
esac
|
||||
|
||||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test -n "$no_target_directory"; then
|
||||
echo "$0: $dstarg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst/`basename "$src"`
|
||||
fi
|
||||
fi
|
||||
|
||||
# This sed command emulates the dirname command.
|
||||
dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
|
||||
|
||||
# Make sure that the destination directory exists.
|
||||
|
||||
# Skip lots of stat calls in the usual case.
|
||||
if test ! -d "$dstdir"; then
|
||||
defaultIFS='
|
||||
'
|
||||
IFS="${IFS-$defaultIFS}"
|
||||
|
||||
oIFS=$IFS
|
||||
# Some sh's can't handle IFS=/ for some reason.
|
||||
IFS='%'
|
||||
set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
|
||||
shift
|
||||
IFS=$oIFS
|
||||
|
||||
pathcomp=
|
||||
|
||||
while test $# -ne 0 ; do
|
||||
pathcomp=$pathcomp$1
|
||||
shift
|
||||
if test ! -d "$pathcomp"; then
|
||||
$mkdirprog "$pathcomp"
|
||||
# mkdir can fail with a `File exist' error in case several
|
||||
# install-sh are creating the directory concurrently. This
|
||||
# is OK.
|
||||
test -d "$pathcomp" || exit
|
||||
fi
|
||||
pathcomp=$pathcomp/
|
||||
done
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
$doit $mkdircmd "$dst" \
|
||||
&& { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
|
||||
&& { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
|
||||
&& { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
|
||||
&& { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
|
||||
|
||||
else
|
||||
dstfile=`basename "$dst"`
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=$dstdir/_inst.$$_
|
||||
rmtmp=$dstdir/_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
trap '(exit $?); exit' 1 2 13 15
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
$doit $cpprog "$src" "$dsttmp" &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
|
||||
&& { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
|
||||
&& { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
|
||||
&& { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
{ $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \
|
||||
|| {
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
if test -f "$dstdir/$dstfile"; then
|
||||
$doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
|
||||
|| $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
|
||||
|| {
|
||||
echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
else
|
||||
:
|
||||
fi
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
|
||||
}
|
||||
}
|
||||
fi || { (exit 1); exit 1; }
|
||||
done
|
||||
|
||||
# The final little trick to "correctly" pass the exit status to the exit trap.
|
||||
{
|
||||
(exit 0); exit 0
|
||||
}
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
||||
@@ -1,340 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Various input/output functions
|
||||
*
|
||||
* @(#)io.c 4.32 (Berkeley) 02/05/99
|
||||
*/
|
||||
|
||||
//#include <stdarg.h>
|
||||
//#include <curses.h>
|
||||
//#include <ctype.h>
|
||||
//#include <string.h>
|
||||
|
||||
#include "rogue.h"
|
||||
|
||||
/*
|
||||
* msg:
|
||||
* Display a message at the top of the screen.
|
||||
*/
|
||||
#define MAXMSG (NUMCOLS - sizeof "--More--")
|
||||
|
||||
static char msgbuf[2*MAXMSG+1];
|
||||
static int newpos = 0;
|
||||
|
||||
/* VARARGS1 */
|
||||
int
|
||||
msg(struct rogue_state *rs,char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
/*
|
||||
* if the string is "", just clear the line
|
||||
*/
|
||||
if (*fmt == '\0')
|
||||
{
|
||||
move(0, 0);
|
||||
clrtoeol();
|
||||
mpos = 0;
|
||||
return ~ESCAPE;
|
||||
}
|
||||
/*
|
||||
* otherwise add to the message and flush it out
|
||||
*/
|
||||
va_start(args, fmt);
|
||||
doadd(rs,fmt, args);
|
||||
va_end(args);
|
||||
return endmsg(rs);
|
||||
}
|
||||
|
||||
/*
|
||||
* addmsg:
|
||||
* Add things to the current message
|
||||
*/
|
||||
/* VARARGS1 */
|
||||
void
|
||||
addmsg(struct rogue_state *rs,char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
doadd(rs,fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/*
|
||||
* endmsg:
|
||||
* Display a new msg (giving him a chance to see the previous one
|
||||
* if it is up there with the --More--)
|
||||
*/
|
||||
int
|
||||
endmsg(struct rogue_state *rs)
|
||||
{
|
||||
char ch;
|
||||
|
||||
if (save_msg)
|
||||
strcpy(huh, msgbuf);
|
||||
if (mpos)
|
||||
{
|
||||
look(rs,FALSE);
|
||||
mvaddstr(0, mpos, "--More--");
|
||||
if ( rs->sleeptime != 0 )
|
||||
refresh();
|
||||
if (!msg_esc)
|
||||
wait_for(rs,' ');
|
||||
else
|
||||
{
|
||||
while ((ch = readchar(rs)) != ' ')
|
||||
if (ch == ESCAPE)
|
||||
{
|
||||
msgbuf[0] = '\0';
|
||||
mpos = 0;
|
||||
newpos = 0;
|
||||
msgbuf[0] = '\0';
|
||||
return ESCAPE;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* All messages should start with uppercase, except ones that
|
||||
* start with a pack addressing character
|
||||
*/
|
||||
if (islower(msgbuf[0]) && !lower_msg && msgbuf[1] != ')')
|
||||
msgbuf[0] = (char) toupper(msgbuf[0]);
|
||||
mvaddstr(0, 0, msgbuf);
|
||||
clrtoeol();
|
||||
mpos = newpos;
|
||||
newpos = 0;
|
||||
msgbuf[0] = '\0';
|
||||
if ( rs->sleeptime != 0 )
|
||||
refresh();
|
||||
return ~ESCAPE;
|
||||
}
|
||||
|
||||
/*
|
||||
* doadd:
|
||||
* Perform an add onto the message buffer
|
||||
*/
|
||||
void
|
||||
doadd(struct rogue_state *rs,char *fmt, va_list args)
|
||||
{
|
||||
static char buf[MAXSTR];
|
||||
|
||||
/*
|
||||
* Do the printf into buf
|
||||
*/
|
||||
vsprintf(buf, fmt, args);
|
||||
if (strlen(buf) + newpos >= MAXMSG)
|
||||
endmsg(rs);
|
||||
strcat(msgbuf, buf);
|
||||
newpos = (int) strlen(msgbuf);
|
||||
}
|
||||
|
||||
/*
|
||||
* step_ok:
|
||||
* Returns true if it is ok to step on ch
|
||||
*/
|
||||
int
|
||||
step_ok(int ch)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case ' ':
|
||||
case '|':
|
||||
case '-':
|
||||
return FALSE;
|
||||
default:
|
||||
return (!isalpha(ch));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* readchar:
|
||||
* Reads and returns a character, checking for gross input errors
|
||||
*/
|
||||
char
|
||||
readchar(struct rogue_state *rs)
|
||||
{
|
||||
char c,ch = -1;
|
||||
if ( rs != 0 && rs->guiflag == 0 )
|
||||
{
|
||||
static uint32_t counter;
|
||||
if ( rs->ind < rs->numkeys )
|
||||
{
|
||||
c = rs->keystrokes[rs->ind++];
|
||||
if ( 0 )
|
||||
{
|
||||
static FILE *fp; static int32_t counter;
|
||||
if ( fp == 0 )
|
||||
fp = fopen("log","wb");
|
||||
if ( fp != 0 )
|
||||
{
|
||||
fprintf(fp,"%d: (%c) hp.%d num.%d gold.%d seed.%llu\n",counter,c,pstats.s_hpt,num_packitems(rs),purse,(long long)seed);
|
||||
fflush(fp);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
while ( c == 'Q' && rs->ind < rs->numkeys )
|
||||
{
|
||||
//fprintf(stderr,"Got 'Q' next (%c)\n",rs->keystrokes[rs->ind]); sleep(2);
|
||||
if ( rs->keystrokes[rs->ind] == 'y' )
|
||||
return(c);
|
||||
rs->ind++;
|
||||
c = rs->keystrokes[rs->ind++];
|
||||
}
|
||||
return(c);
|
||||
}
|
||||
if ( rs->replaydone != 0 && counter++ < 3 )
|
||||
fprintf(stderr,"replay finished but readchar called\n");
|
||||
rs->replaydone = (uint32_t)time(NULL);
|
||||
if ( counter < 3 || (counter & 1) == 0 )
|
||||
return('y');
|
||||
else return(ESCAPE);
|
||||
}
|
||||
if ( rs == 0 || rs->guiflag != 0 )
|
||||
{
|
||||
ch = (char) md_readchar();
|
||||
|
||||
if (ch == 3)
|
||||
{
|
||||
_quit();
|
||||
return(27);
|
||||
}
|
||||
if ( rs != 0 && rs->guiflag != 0 )
|
||||
{
|
||||
if ( rs->num < sizeof(rs->buffered) )
|
||||
{
|
||||
rs->buffered[rs->num++] = ch;
|
||||
if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 )
|
||||
{
|
||||
rs->needflush = (uint32_t)time(NULL);
|
||||
//fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered));
|
||||
//sleep(3);
|
||||
}
|
||||
} else fprintf(stderr,"buffer filled without flushed\n");
|
||||
}
|
||||
} else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs);
|
||||
return(ch);
|
||||
}
|
||||
|
||||
/*
|
||||
* status:
|
||||
* Display the important stats line. Keep the cursor where it was.
|
||||
*/
|
||||
void
|
||||
status(struct rogue_state *rs)
|
||||
{
|
||||
register int oy, ox, temp;
|
||||
static int hpwidth = 0;
|
||||
static int s_hungry = 0;
|
||||
static int s_lvl = 0;
|
||||
static int s_pur = -1;
|
||||
static int s_hp = 0;
|
||||
static int s_arm = 0;
|
||||
static str_t s_str = 0;
|
||||
static int s_exp = 0;
|
||||
static char *state_name[] =
|
||||
{
|
||||
"", "Hungry", "Weak", "Faint"
|
||||
};
|
||||
|
||||
/*
|
||||
* If nothing has changed since the last status, don't
|
||||
* bother.
|
||||
*/
|
||||
temp = (cur_armor != NULL ? cur_armor->o_arm : pstats.s_arm);
|
||||
if (s_hp == pstats.s_hpt && s_exp == pstats.s_exp && s_pur == purse
|
||||
&& s_arm == temp && s_str == pstats.s_str && s_lvl == level
|
||||
&& s_hungry == hungry_state
|
||||
&& !stat_msg
|
||||
)
|
||||
return;
|
||||
|
||||
s_arm = temp;
|
||||
|
||||
getyx(stdscr, oy, ox);
|
||||
if (s_hp != max_hp)
|
||||
{
|
||||
temp = max_hp;
|
||||
s_hp = max_hp;
|
||||
for (hpwidth = 0; temp; hpwidth++)
|
||||
temp /= 10;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save current status
|
||||
*/
|
||||
s_lvl = level;
|
||||
s_pur = purse;
|
||||
s_hp = pstats.s_hpt;
|
||||
s_str = pstats.s_str;
|
||||
s_exp = pstats.s_exp;
|
||||
s_hungry = hungry_state;
|
||||
|
||||
if (stat_msg)
|
||||
{
|
||||
move(0, 0);
|
||||
msg(rs,"Level: %d Gold: %-5d Hp: %*d(%*d) Str: %2d(%d) Arm: %-2d Exp: %d/%ld %s",
|
||||
level, purse, hpwidth, pstats.s_hpt, hpwidth, max_hp, pstats.s_str,
|
||||
max_stats.s_str, 10 - s_arm, pstats.s_lvl, pstats.s_exp,
|
||||
state_name[hungry_state]);
|
||||
}
|
||||
else
|
||||
{
|
||||
move(STATLINE, 0);
|
||||
|
||||
printw("Level: %d Gold: %-5d Hp: %*d(%*d) Str: %2d(%d) Arm: %-2d Exp: %d/%d %s",
|
||||
level, purse, hpwidth, pstats.s_hpt, hpwidth, max_hp, pstats.s_str,
|
||||
max_stats.s_str, 10 - s_arm, pstats.s_lvl, pstats.s_exp,
|
||||
state_name[hungry_state]);
|
||||
}
|
||||
|
||||
clrtoeol();
|
||||
move(oy, ox);
|
||||
}
|
||||
|
||||
/*
|
||||
* wait_for
|
||||
* Sit around until the guy types the right key
|
||||
*/
|
||||
void
|
||||
wait_for(struct rogue_state *rs,int ch)
|
||||
{
|
||||
register char c;
|
||||
|
||||
if (ch == '\n')
|
||||
while ((c = readchar(rs)) != '\n' && c != '\r')
|
||||
{
|
||||
if ( rs->replaydone != 0 )
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
while (readchar(rs) != ch)
|
||||
{
|
||||
if ( rs->replaydone != 0 )
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* show_win:
|
||||
* Function used to display a window and wait before returning
|
||||
*/
|
||||
void
|
||||
show_win(struct rogue_state *rs,char *message)
|
||||
{
|
||||
WINDOW *win;
|
||||
|
||||
win = hw;
|
||||
wmove(win, 0, 0);
|
||||
waddstr(win, message);
|
||||
touchwin(win);
|
||||
wmove(win, hero.y, hero.x);
|
||||
wrefresh(win);
|
||||
wait_for(rs,' ');
|
||||
clearok(curscr, TRUE);
|
||||
touchwin(stdscr);
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Functions for dealing with linked lists of goodies
|
||||
*
|
||||
* @(#)list.c 4.12 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <curses.h>
|
||||
//#include <memory.h>
|
||||
#include "rogue.h"
|
||||
|
||||
#ifdef MASTER
|
||||
int total = 0; /* total dynamic memory bytes */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* discard:
|
||||
* Free up an item
|
||||
*/
|
||||
|
||||
//#define ENABLE_DEBUG
|
||||
#define MAX_DEBUGPTRS 100000
|
||||
|
||||
int32_t itemcounter;
|
||||
THING *thingptrs[MAX_DEBUGPTRS];
|
||||
int32_t numptrs;
|
||||
|
||||
int32_t thing_find(THING *item)
|
||||
{
|
||||
#ifdef ENABLE_DEBUG
|
||||
int32_t i;
|
||||
for (i=0; i<numptrs; i++)
|
||||
if ( item == thingptrs[i] )
|
||||
return(i);
|
||||
return(-1);
|
||||
#else
|
||||
return(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
discard(THING *item)
|
||||
{
|
||||
#ifdef MASTER
|
||||
total--;
|
||||
#endif
|
||||
#ifdef ENABLE_DEBUG
|
||||
{
|
||||
int32_t i;
|
||||
for (i=0; i<numptrs; i++)
|
||||
if ( item == thingptrs[i] )
|
||||
{
|
||||
thingptrs[i] = thingptrs[--numptrs];
|
||||
thingptrs[numptrs] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
THING *list = pack;
|
||||
for (; list != NULL; list = next(list))
|
||||
{
|
||||
if ( list == item )
|
||||
{
|
||||
fprintf(stderr,"pack item discarded? (%s)\n",inv_name(list,FALSE));
|
||||
sleep(3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
itemcounter--;
|
||||
free((char *) item);
|
||||
}
|
||||
|
||||
void garbage_collect()
|
||||
{
|
||||
return;
|
||||
int32_t i;
|
||||
fprintf(stderr,"numptrs.%d free them\n",numptrs);
|
||||
for (i=0; i<numptrs; i++)
|
||||
{
|
||||
//fprintf(stderr,"%p _t_type.%d otype.%d (%c)\n",thingptrs[i],thingptrs[i]->_t._t_type,thingptrs[i]->o_type,thingptrs[i]->o_type);
|
||||
free(thingptrs[i]);
|
||||
}
|
||||
memset(thingptrs,0,sizeof(thingptrs));
|
||||
numptrs = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* detach:
|
||||
* takes an item out of whatever linked list it might be in
|
||||
*/
|
||||
|
||||
void
|
||||
_detach(THING **list, THING *item)
|
||||
{
|
||||
if (*list == item)
|
||||
*list = next(item);
|
||||
if (prev(item) != NULL)
|
||||
item->l_prev->l_next = next(item);
|
||||
if (next(item) != NULL)
|
||||
item->l_next->l_prev = prev(item);
|
||||
item->l_next = NULL;
|
||||
item->l_prev = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* _attach:
|
||||
* add an item to the head of a list
|
||||
*/
|
||||
|
||||
void
|
||||
_attach(THING **list, THING *item)
|
||||
{
|
||||
if (*list != NULL)
|
||||
{
|
||||
item->l_next = *list;
|
||||
(*list)->l_prev = item;
|
||||
item->l_prev = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->l_next = NULL;
|
||||
item->l_prev = NULL;
|
||||
}
|
||||
*list = item;
|
||||
}
|
||||
|
||||
/*
|
||||
* _free_list:
|
||||
* Throw the whole blamed thing away
|
||||
*/
|
||||
|
||||
void
|
||||
_free_list(THING **ptr)
|
||||
{
|
||||
THING *item;
|
||||
while (*ptr != NULL)
|
||||
{
|
||||
item = *ptr;
|
||||
*ptr = next(item);
|
||||
discard(item);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* new_item
|
||||
* Get a new item with a specified size
|
||||
*/
|
||||
|
||||
THING *
|
||||
new_item(void)
|
||||
{
|
||||
THING *item;
|
||||
|
||||
#ifdef MASTER
|
||||
if ((item = (THING *)calloc(1, sizeof *item)) == NULL)
|
||||
msg(rs,"ran out of memory after %d items", total);
|
||||
else
|
||||
total++;
|
||||
#else
|
||||
item = (THING *)calloc(1, sizeof *item);
|
||||
#endif
|
||||
#ifdef ENABLE_DEBUG
|
||||
if ( numptrs < MAX_DEBUGPTRS )
|
||||
{
|
||||
thingptrs[numptrs++] = item;
|
||||
if ( (++itemcounter % 100) == 0 )
|
||||
fprintf(stderr,"itemcounter.%d\n",itemcounter);
|
||||
}
|
||||
#endif
|
||||
item->l_next = NULL;
|
||||
item->l_prev = NULL;
|
||||
return item;
|
||||
}
|
||||
@@ -1,462 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Various installation dependent routines
|
||||
*
|
||||
* @(#)mach_dep.c 4.37 (Berkeley) 05/23/83
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The various tuneable defines are:
|
||||
*
|
||||
* SCOREFILE Where/if the score file should live.
|
||||
* ALLSCORES Score file is top ten scores, not top ten
|
||||
* players. This is only useful when only a few
|
||||
* people will be playing; otherwise the score file
|
||||
* gets hogged by just a few people.
|
||||
* NUMSCORES Number of scores in the score file (default 10).
|
||||
* NUMNAME String version of NUMSCORES (first character
|
||||
* should be capitalized) (default "Ten").
|
||||
* MAXLOAD What (if any) the maximum load average should be
|
||||
* when people are playing. Since it is divided
|
||||
* by 10, to specify a load limit of 4.0, MAXLOAD
|
||||
* should be "40". If defined, then
|
||||
* LOADAV Should it use it's own routine to get
|
||||
* the load average?
|
||||
* NAMELIST If so, where does the system namelist
|
||||
* hide?
|
||||
* MAXUSERS What (if any) the maximum user count should be
|
||||
* when people are playing. If defined, then
|
||||
* UCOUNT Should it use it's own routine to count
|
||||
* users?
|
||||
* UTMP If so, where does the user list hide?
|
||||
* CHECKTIME How often/if it should check during the game
|
||||
* for high load average.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
//#include <curses.h>
|
||||
#include "rogue.h"
|
||||
#include "extern.h"
|
||||
|
||||
#define NOOP(x) (x += 0)
|
||||
|
||||
# ifndef NUMSCORES
|
||||
# define NUMSCORES 10
|
||||
# define NUMNAME "Ten"
|
||||
# endif
|
||||
|
||||
unsigned int numscores = NUMSCORES;
|
||||
char *Numname = NUMNAME;
|
||||
|
||||
# ifdef ALLSCORES
|
||||
bool allscore = TRUE;
|
||||
# else /* ALLSCORES */
|
||||
bool allscore = FALSE;
|
||||
# endif /* ALLSCORES */
|
||||
|
||||
#ifdef CHECKTIME
|
||||
static int num_checks; /* times we've gone over in checkout() */
|
||||
#endif /* CHECKTIME */
|
||||
|
||||
/*
|
||||
* init_check:
|
||||
* Check out too see if it is proper to play the game now
|
||||
*/
|
||||
|
||||
void
|
||||
init_check()
|
||||
{
|
||||
#if defined(MAXLOAD) || defined(MAXUSERS)
|
||||
if (too_much())
|
||||
{
|
||||
printf("Sorry, %s, but the system is too loaded now.\n", whoami);
|
||||
printf("Try again later. Meanwhile, why not enjoy a%s %s?\n",
|
||||
vowelstr(fruit), fruit);
|
||||
if (author())
|
||||
printf("However, since you're a good guy, it's up to you\n");
|
||||
else
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* open_score:
|
||||
* Open up the score file for future use
|
||||
*/
|
||||
|
||||
void
|
||||
open_score()
|
||||
{
|
||||
#ifdef SCOREFILE
|
||||
char *scorefile = SCOREFILE;
|
||||
/*
|
||||
* We drop setgid privileges after opening the score file, so subsequent
|
||||
* open()'s will fail. Just reuse the earlier filehandle.
|
||||
*/
|
||||
|
||||
if (scoreboard != NULL) {
|
||||
rewind(scoreboard);
|
||||
return;
|
||||
}
|
||||
|
||||
scoreboard = fopen(scorefile, "r+");
|
||||
|
||||
if ((scoreboard == NULL) && (errno == ENOENT))
|
||||
{
|
||||
scoreboard = fopen(scorefile, "w+");
|
||||
md_chmod(scorefile,0664);
|
||||
}
|
||||
|
||||
if (scoreboard == NULL) {
|
||||
fprintf(stderr, "Could not open %s for writing: %s\n", scorefile, strerror(errno));
|
||||
fflush(stderr);
|
||||
}
|
||||
#else
|
||||
scoreboard = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* setup:
|
||||
* Get starting setup for all games
|
||||
*/
|
||||
|
||||
void
|
||||
setup()
|
||||
{
|
||||
#ifdef CHECKTIME
|
||||
int checkout();
|
||||
#endif
|
||||
|
||||
#ifdef DUMP
|
||||
md_onsignal_autosave();
|
||||
#else
|
||||
md_onsignal_default();
|
||||
#endif
|
||||
|
||||
#ifdef CHECKTIME
|
||||
md_start_checkout_timer(CHECKTIME*60);
|
||||
num_checks = 0;
|
||||
#endif
|
||||
|
||||
raw(); /* Raw mode */
|
||||
noecho(); /* Echo off */
|
||||
keypad(stdscr,1);
|
||||
getltchars(); /* get the local tty chars */
|
||||
}
|
||||
|
||||
/*
|
||||
* getltchars:
|
||||
* Get the local tty chars for later use
|
||||
*/
|
||||
|
||||
void
|
||||
getltchars()
|
||||
{
|
||||
got_ltc = TRUE;
|
||||
orig_dsusp = md_dsuspchar();
|
||||
md_setdsuspchar( md_suspchar() );
|
||||
}
|
||||
|
||||
/*
|
||||
* resetltchars:
|
||||
* Reset the local tty chars to original values.
|
||||
*/
|
||||
void
|
||||
resetltchars(void)
|
||||
{
|
||||
if (got_ltc) {
|
||||
md_setdsuspchar(orig_dsusp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* playltchars:
|
||||
* Set local tty chars to the values we use when playing.
|
||||
*/
|
||||
void
|
||||
playltchars(void)
|
||||
{
|
||||
if (got_ltc) {
|
||||
md_setdsuspchar( md_suspchar() );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* start_score:
|
||||
* Start the scoring sequence
|
||||
*/
|
||||
|
||||
void
|
||||
start_score()
|
||||
{
|
||||
#ifdef CHECKTIME
|
||||
md_stop_checkout_timer();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* is_symlink:
|
||||
* See if the file has a symbolic link
|
||||
*/
|
||||
bool
|
||||
is_symlink(char *sp)
|
||||
{
|
||||
#ifdef S_IFLNK
|
||||
struct stat sbuf2;
|
||||
|
||||
if (lstat(sp, &sbuf2) < 0)
|
||||
return FALSE;
|
||||
else
|
||||
return ((sbuf2.st_mode & S_IFMT) != S_IFREG);
|
||||
#else
|
||||
NOOP(sp);
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(MAXLOAD) || defined(MAXUSERS)
|
||||
/*
|
||||
* too_much:
|
||||
* See if the system is being used too much for this game
|
||||
*/
|
||||
bool
|
||||
too_much()
|
||||
{
|
||||
#ifdef MAXLOAD
|
||||
double avec[3];
|
||||
#else
|
||||
int cnt;
|
||||
#endif
|
||||
|
||||
#ifdef MAXLOAD
|
||||
md_loadav(avec);
|
||||
if (avec[1] > (MAXLOAD / 10.0))
|
||||
return TRUE;
|
||||
#endif
|
||||
#ifdef MAXUSERS
|
||||
if (ucount() > MAXUSERS)
|
||||
return TRUE;
|
||||
#endif
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* author:
|
||||
* See if a user is an author of the program
|
||||
*/
|
||||
bool
|
||||
author()
|
||||
{
|
||||
#ifdef MASTER
|
||||
if (wizard)
|
||||
return TRUE;
|
||||
#endif
|
||||
switch (md_getuid())
|
||||
{
|
||||
case -1:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CHECKTIME
|
||||
/*
|
||||
* checkout:
|
||||
* Check each CHECKTIME seconds to see if the load is too high
|
||||
*/
|
||||
|
||||
checkout(struct rogue_state *rs,int sig)
|
||||
{
|
||||
static char *msgs[] = {
|
||||
"The load is too high to be playing. Please leave in %0.1f minutes",
|
||||
"Please save your game. You have %0.1f minutes",
|
||||
"Last warning. You have %0.1f minutes to leave",
|
||||
};
|
||||
int checktime;
|
||||
|
||||
if (too_much())
|
||||
{
|
||||
if (author())
|
||||
{
|
||||
num_checks = 1;
|
||||
chmsg(rs,"The load is rather high, O exaulted one");
|
||||
}
|
||||
else if (num_checks++ == 3)
|
||||
fatal("Sorry. You took too long. You are dead\n");
|
||||
checktime = (CHECKTIME * 60) / num_checks;
|
||||
chmsg(rs,msgs[num_checks - 1], ((double) checktime / 60.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (num_checks)
|
||||
{
|
||||
num_checks = 0;
|
||||
chmsg(rs,"The load has dropped back down. You have a reprieve");
|
||||
}
|
||||
checktime = (CHECKTIME * 60);
|
||||
}
|
||||
|
||||
md_start_checkout_timer(checktime);
|
||||
}
|
||||
|
||||
/*
|
||||
* chmsg:
|
||||
* checkout()'s version of msg. If we are in the middle of a
|
||||
* shell, do a printf instead of a msg to a the refresh.
|
||||
*/
|
||||
/* VARARGS1 */
|
||||
|
||||
chmsg(struct rogue_state *rs,char *fmt, int arg)
|
||||
{
|
||||
if (!in_shell)
|
||||
msg(rs,fmt, arg);
|
||||
else
|
||||
{
|
||||
printf(fmt, arg);
|
||||
putchar('\n');
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UCOUNT
|
||||
/*
|
||||
* ucount:
|
||||
* count number of users on the system
|
||||
*/
|
||||
#include <utmp.h>
|
||||
|
||||
struct utmp buf;
|
||||
|
||||
int
|
||||
ucount()
|
||||
{
|
||||
struct utmp *up;
|
||||
FILE *utmp;
|
||||
int count;
|
||||
|
||||
if ((utmp = fopen(UTMP, "r")) == NULL)
|
||||
return 0;
|
||||
|
||||
up = &buf;
|
||||
count = 0;
|
||||
|
||||
while (fread(up, 1, sizeof (*up), utmp) > 0)
|
||||
if (buf.ut_name[0] != '\0')
|
||||
count++;
|
||||
fclose(utmp);
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* lock_sc:
|
||||
* lock the score file. If it takes too long, ask the user if
|
||||
* they care to wait. Return TRUE if the lock is successful.
|
||||
*/
|
||||
static FILE *lfd = NULL;
|
||||
bool
|
||||
lock_sc()
|
||||
{
|
||||
#if defined(SCOREFILE) && defined(LOCKFILE)
|
||||
int cnt;
|
||||
static struct stat sbuf;
|
||||
char *lockfile = LOCKFILE;
|
||||
|
||||
over:
|
||||
if ((lfd=fopen(lockfile, "w+")) != NULL)
|
||||
return TRUE;
|
||||
for (cnt = 0; cnt < 5; cnt++)
|
||||
{
|
||||
md_sleep(1);
|
||||
if ((lfd=fopen(lockfile, "w+")) != NULL)
|
||||
return TRUE;
|
||||
}
|
||||
if (stat(lockfile, &sbuf) < 0)
|
||||
{
|
||||
lfd=fopen(lockfile, "w+");
|
||||
return TRUE;
|
||||
}
|
||||
if (time(NULL) - sbuf.st_mtime > 10)
|
||||
{
|
||||
if (md_unlink(lockfile) < 0)
|
||||
return FALSE;
|
||||
goto over;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("The score file is very busy. Do you want to wait longer\n");
|
||||
printf("for it to become free so your score can get posted?\n");
|
||||
printf("If so, type \"y\"\n");
|
||||
if (fgets(prbuf, MAXSTR, stdin) != 0 )
|
||||
;
|
||||
if (prbuf[0] == 'y')
|
||||
for (;;)
|
||||
{
|
||||
if ((lfd=fopen(lockfile, "w+")) != 0)
|
||||
return TRUE;
|
||||
if (stat(lockfile, &sbuf) < 0)
|
||||
{
|
||||
lfd=fopen(lockfile, "w+");
|
||||
return TRUE;
|
||||
}
|
||||
if (time(NULL) - sbuf.st_mtime > 10)
|
||||
{
|
||||
if (md_unlink(lockfile) < 0)
|
||||
return FALSE;
|
||||
}
|
||||
md_sleep(1);
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
#else
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* unlock_sc:
|
||||
* Unlock the score file
|
||||
*/
|
||||
|
||||
void
|
||||
unlock_sc()
|
||||
{
|
||||
#if defined(SCOREFILE) && defined(LOCKFILE)
|
||||
if (lfd != NULL)
|
||||
fclose(lfd);
|
||||
lfd = NULL;
|
||||
md_unlink(LOCKFILE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* flush_type:
|
||||
* Flush typeahead for traps, etc.
|
||||
*/
|
||||
|
||||
void
|
||||
flush_type()
|
||||
{
|
||||
flushinp();
|
||||
}
|
||||
1000
src/cc/rogue/main.c
1000
src/cc/rogue/main.c
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,620 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* All sorts of miscellaneous routines
|
||||
*
|
||||
* @(#)misc.c 4.66 (Berkeley) 08/06/83
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <curses.h>
|
||||
//#include <string.h>
|
||||
//#include <ctype.h>
|
||||
#include "rogue.h"
|
||||
|
||||
/*
|
||||
* look:
|
||||
* A quick glance all around the player
|
||||
*/
|
||||
#undef DEBUG
|
||||
|
||||
|
||||
void
|
||||
look(struct rogue_state *rs,bool wakeup)
|
||||
{
|
||||
int x, y;
|
||||
int ch;
|
||||
THING *tp;
|
||||
PLACE *pp;
|
||||
struct room *rp;
|
||||
int ey, ex;
|
||||
int passcount;
|
||||
char pfl, *fp, pch;
|
||||
int sy, sx, sumhero = 0, diffhero = 0;
|
||||
# ifdef DEBUG
|
||||
static bool done = FALSE;
|
||||
|
||||
if (done)
|
||||
return;
|
||||
done = TRUE;
|
||||
# endif /* DEBUG */
|
||||
passcount = 0;
|
||||
rp = proom;
|
||||
if (!ce(oldpos, hero))
|
||||
{
|
||||
erase_lamp(&oldpos, oldrp);
|
||||
oldpos = hero;
|
||||
oldrp = rp;
|
||||
}
|
||||
ey = hero.y + 1;
|
||||
ex = hero.x + 1;
|
||||
sx = hero.x - 1;
|
||||
sy = hero.y - 1;
|
||||
if (door_stop && !firstmove && running)
|
||||
{
|
||||
sumhero = hero.y + hero.x;
|
||||
diffhero = hero.y - hero.x;
|
||||
}
|
||||
pp = INDEX(hero.y, hero.x);
|
||||
pch = pp->p_ch;
|
||||
pfl = pp->p_flags;
|
||||
|
||||
for (y = sy; y <= ey; y++)
|
||||
if (y > 0 && y < NUMLINES - 1) for (x = sx; x <= ex; x++)
|
||||
{
|
||||
if (x < 0 || x >= NUMCOLS)
|
||||
continue;
|
||||
if (!on(player, ISBLIND))
|
||||
{
|
||||
if (y == hero.y && x == hero.x)
|
||||
continue;
|
||||
}
|
||||
|
||||
pp = INDEX(y, x);
|
||||
ch = pp->p_ch;
|
||||
if (ch == ' ') /* nothing need be done with a ' ' */
|
||||
continue;
|
||||
fp = &pp->p_flags;
|
||||
if (pch != DOOR && ch != DOOR)
|
||||
if ((pfl & F_PASS) != (*fp & F_PASS))
|
||||
continue;
|
||||
if (((*fp & F_PASS) || ch == DOOR) &&
|
||||
((pfl & F_PASS) || pch == DOOR))
|
||||
{
|
||||
if (hero.x != x && hero.y != y &&
|
||||
!step_ok(chat(y, hero.x)) && !step_ok(chat(hero.y, x)))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tp = pp->p_monst) == NULL)
|
||||
ch = trip_ch(y, x, ch);
|
||||
else
|
||||
if (on(player, SEEMONST) && on(*tp, ISINVIS))
|
||||
{
|
||||
if (door_stop && !firstmove)
|
||||
running = FALSE;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wakeup)
|
||||
wake_monster(rs,y, x);
|
||||
if (see_monst(tp))
|
||||
{
|
||||
if (on(player, ISHALU))
|
||||
ch = rnd(26) + 'A';
|
||||
else
|
||||
ch = tp->t_disguise;
|
||||
}
|
||||
}
|
||||
if (on(player, ISBLIND) && (y != hero.y || x != hero.x))
|
||||
continue;
|
||||
|
||||
move(y, x);
|
||||
|
||||
if ((proom->r_flags & ISDARK) && !see_floor && ch == FLOOR)
|
||||
ch = ' ';
|
||||
|
||||
if (tp != NULL || ch != CCHAR( inch() ))
|
||||
addch(ch);
|
||||
|
||||
if (door_stop && !firstmove && running)
|
||||
{
|
||||
switch (runch)
|
||||
{
|
||||
case 'h':
|
||||
if (x == ex)
|
||||
continue;
|
||||
when 'j':
|
||||
if (y == sy)
|
||||
continue;
|
||||
when 'k':
|
||||
if (y == ey)
|
||||
continue;
|
||||
when 'l':
|
||||
if (x == sx)
|
||||
continue;
|
||||
when 'y':
|
||||
if ((y + x) - sumhero >= 1)
|
||||
continue;
|
||||
when 'u':
|
||||
if ((y - x) - diffhero >= 1)
|
||||
continue;
|
||||
when 'n':
|
||||
if ((y + x) - sumhero <= -1)
|
||||
continue;
|
||||
when 'b':
|
||||
if ((y - x) - diffhero <= -1)
|
||||
continue;
|
||||
}
|
||||
switch (ch)
|
||||
{
|
||||
case DOOR:
|
||||
if (x == hero.x || y == hero.y)
|
||||
running = FALSE;
|
||||
break;
|
||||
case PASSAGE:
|
||||
if (x == hero.x || y == hero.y)
|
||||
passcount++;
|
||||
break;
|
||||
case FLOOR:
|
||||
case '|':
|
||||
case '-':
|
||||
case ' ':
|
||||
break;
|
||||
default:
|
||||
running = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (door_stop && !firstmove && passcount > 1)
|
||||
running = FALSE;
|
||||
if (!running || !jump)
|
||||
mvaddch(hero.y, hero.x, PLAYER);
|
||||
# ifdef DEBUG
|
||||
done = FALSE;
|
||||
# endif /* DEBUG */
|
||||
}
|
||||
|
||||
/*
|
||||
* trip_ch:
|
||||
* Return the character appropriate for this space, taking into
|
||||
* account whether or not the player is tripping.
|
||||
*/
|
||||
int
|
||||
trip_ch(int y, int x, int ch)
|
||||
{
|
||||
if (on(player, ISHALU) && after)
|
||||
switch (ch)
|
||||
{
|
||||
case FLOOR:
|
||||
case ' ':
|
||||
case PASSAGE:
|
||||
case '-':
|
||||
case '|':
|
||||
case DOOR:
|
||||
case TRAP:
|
||||
break;
|
||||
default:
|
||||
if (y != stairs.y || x != stairs.x || !seenstairs)
|
||||
ch = rnd_thing();
|
||||
break;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
/*
|
||||
* erase_lamp:
|
||||
* Erase the area shown by a lamp in a dark room.
|
||||
*/
|
||||
|
||||
void
|
||||
erase_lamp(coord *pos, struct room *rp)
|
||||
{
|
||||
int y, x, ey, sy, ex;
|
||||
|
||||
if (!(see_floor && (rp->r_flags & (ISGONE|ISDARK)) == ISDARK
|
||||
&& !on(player,ISBLIND)))
|
||||
return;
|
||||
|
||||
ey = pos->y + 1;
|
||||
ex = pos->x + 1;
|
||||
sy = pos->y - 1;
|
||||
for (x = pos->x - 1; x <= ex; x++)
|
||||
for (y = sy; y <= ey; y++)
|
||||
{
|
||||
if (y == hero.y && x == hero.x)
|
||||
continue;
|
||||
move(y, x);
|
||||
if (inch() == FLOOR)
|
||||
addch(' ');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* show_floor:
|
||||
* Should we show the floor in her room at this time?
|
||||
*/
|
||||
bool
|
||||
show_floor()
|
||||
{
|
||||
if ((proom->r_flags & (ISGONE|ISDARK)) == ISDARK && !on(player, ISBLIND))
|
||||
return see_floor;
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* find_obj:
|
||||
* Find the unclaimed object at y, x
|
||||
*/
|
||||
THING *
|
||||
find_obj(struct rogue_state *rs,int y, int x)
|
||||
{
|
||||
THING *obj;
|
||||
|
||||
for (obj = lvl_obj; obj != NULL; obj = next(obj))
|
||||
{
|
||||
if (obj->o_pos.y == y && obj->o_pos.x == x)
|
||||
return obj;
|
||||
}
|
||||
#ifdef MASTER
|
||||
sprintf(prbuf, "Non-object %d,%d", y, x);
|
||||
msg(rs,prbuf);
|
||||
return NULL;
|
||||
#else
|
||||
/* NOTREACHED */
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* eat:
|
||||
* She wants to eat something, so let her try
|
||||
*/
|
||||
|
||||
void
|
||||
eat(struct rogue_state *rs)
|
||||
{
|
||||
THING *obj;
|
||||
|
||||
if ((obj = get_item(rs,"eat", FOOD)) == NULL)
|
||||
return;
|
||||
if (obj->o_type != FOOD)
|
||||
{
|
||||
if (!terse)
|
||||
msg(rs,"ugh, you would get ill if you ate that");
|
||||
else
|
||||
msg(rs,"that's Inedible!");
|
||||
return;
|
||||
}
|
||||
if (food_left < 0)
|
||||
food_left = 0;
|
||||
if ((food_left += HUNGERTIME - 200 + rnd(400)) > STOMACHSIZE)
|
||||
food_left = STOMACHSIZE;
|
||||
hungry_state = 0;
|
||||
if (obj == cur_weapon)
|
||||
cur_weapon = NULL;
|
||||
if (obj->o_which == 1)
|
||||
msg(rs,"my, that was a yummy %s", fruit);
|
||||
else
|
||||
if (rnd(100) > 70)
|
||||
{
|
||||
pstats.s_exp++;
|
||||
msg(rs,"%s, this food tastes awful", choose_str("bummer", "yuk"));
|
||||
check_level(rs);
|
||||
}
|
||||
else
|
||||
msg(rs,"%s, that tasted good", choose_str("oh, wow", "yum"));
|
||||
leave_pack(rs,obj, FALSE, FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* check_level:
|
||||
* Check to see if the guy has gone up a level.
|
||||
*/
|
||||
|
||||
void
|
||||
check_level(struct rogue_state *rs)
|
||||
{
|
||||
int i, add, olevel;
|
||||
|
||||
for (i = 0; e_levels[i] != 0; i++)
|
||||
if (e_levels[i] > pstats.s_exp)
|
||||
break;
|
||||
i++;
|
||||
olevel = pstats.s_lvl;
|
||||
pstats.s_lvl = i;
|
||||
if (i > olevel)
|
||||
{
|
||||
add = roll(i - olevel, 10);
|
||||
max_hp += add;
|
||||
pstats.s_hpt += add;
|
||||
msg(rs,"welcome to level %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* chg_str:
|
||||
* used to modify the playes strength. It keeps track of the
|
||||
* highest it has been, just in case
|
||||
*/
|
||||
|
||||
void
|
||||
chg_str(int amt)
|
||||
{
|
||||
//auto jl777: strange compiler error
|
||||
uint32_t comp;
|
||||
|
||||
if (amt == 0)
|
||||
return;
|
||||
//add_str(&pstats.s_str, amt);
|
||||
pstats.s_str += amt;
|
||||
if ( pstats.s_str < 3 )
|
||||
pstats.s_str = 3;
|
||||
else if ( pstats.s_str > 31 )
|
||||
pstats.s_str = 31;
|
||||
comp = pstats.s_str;
|
||||
if (ISRING(LEFT, R_ADDSTR))
|
||||
{
|
||||
// add_str(&comp, -cur_ring[LEFT]->o_arm);
|
||||
comp += -cur_ring[LEFT]->o_arm;
|
||||
if ( comp < 3 )
|
||||
comp = 3;
|
||||
else if ( comp > 31 )
|
||||
comp = 31;
|
||||
}
|
||||
if (ISRING(RIGHT, R_ADDSTR))
|
||||
{
|
||||
//add_str(&comp, -cur_ring[RIGHT]->o_arm);
|
||||
comp += -cur_ring[RIGHT]->o_arm;
|
||||
if ( comp < 3 )
|
||||
comp = 3;
|
||||
else if ( comp > 31 )
|
||||
comp = 31;
|
||||
}
|
||||
if ( comp > max_stats.s_str )
|
||||
max_stats.s_str = comp;
|
||||
}
|
||||
|
||||
/*
|
||||
* add_str:
|
||||
* Perform the actual add, checking upper and lower bound limits
|
||||
*/
|
||||
void
|
||||
add_str(str_t *sp, int amt)
|
||||
{
|
||||
if ((*sp += amt) < 3)
|
||||
*sp = 3;
|
||||
else if (*sp > 31)
|
||||
*sp = 31;
|
||||
}
|
||||
|
||||
/*
|
||||
* add_haste:
|
||||
* Add a haste to the player
|
||||
*/
|
||||
bool
|
||||
add_haste(struct rogue_state *rs,bool potion)
|
||||
{
|
||||
if (on(player, ISHASTE))
|
||||
{
|
||||
no_command += rnd(8);
|
||||
player.t_flags &= ~(ISRUN|ISHASTE);
|
||||
extinguish(nohaste);
|
||||
msg(rs,"you faint from exhaustion");
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
player.t_flags |= ISHASTE;
|
||||
if (potion)
|
||||
fuse(nohaste, 0, rnd(4)+4, AFTER);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* aggravate:
|
||||
* Aggravate all the monsters on this level
|
||||
*/
|
||||
|
||||
void
|
||||
aggravate(struct rogue_state *rs)
|
||||
{
|
||||
THING *mp;
|
||||
|
||||
for (mp = mlist; mp != NULL; mp = next(mp))
|
||||
runto(rs,&mp->t_pos);
|
||||
}
|
||||
|
||||
/*
|
||||
* vowelstr:
|
||||
* For printfs: if string starts with a vowel, return "n" for an
|
||||
* "an".
|
||||
*/
|
||||
char *
|
||||
vowelstr(char *str)
|
||||
{
|
||||
switch (*str)
|
||||
{
|
||||
case 'a': case 'A':
|
||||
case 'e': case 'E':
|
||||
case 'i': case 'I':
|
||||
case 'o': case 'O':
|
||||
case 'u': case 'U':
|
||||
return "n";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* is_current:
|
||||
* See if the object is one of the currently used items
|
||||
*/
|
||||
bool
|
||||
is_current(struct rogue_state *rs,THING *obj)
|
||||
{
|
||||
if (obj == NULL)
|
||||
return FALSE;
|
||||
if (obj == cur_armor || obj == cur_weapon || obj == cur_ring[LEFT]
|
||||
|| obj == cur_ring[RIGHT])
|
||||
{
|
||||
if (!terse)
|
||||
addmsg(rs,"That's already ");
|
||||
msg(rs,"in use");
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_dir:
|
||||
* Set up the direction co_ordinate for use in varios "prefix"
|
||||
* commands
|
||||
*/
|
||||
bool
|
||||
get_dir(struct rogue_state *rs)
|
||||
{
|
||||
char *prompt;
|
||||
bool gotit;
|
||||
static coord last_delt= {0,0};
|
||||
|
||||
if (again && last_dir != '\0')
|
||||
{
|
||||
delta.y = last_delt.y;
|
||||
delta.x = last_delt.x;
|
||||
dir_ch = last_dir;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!terse)
|
||||
msg(rs,prompt = "which direction? ");
|
||||
else
|
||||
prompt = "direction: ";
|
||||
do
|
||||
{
|
||||
gotit = TRUE;
|
||||
switch (dir_ch = readchar(rs))
|
||||
{
|
||||
case 'h': case'H': delta.y = 0; delta.x = -1;
|
||||
when 'j': case'J': delta.y = 1; delta.x = 0;
|
||||
when 'k': case'K': delta.y = -1; delta.x = 0;
|
||||
when 'l': case'L': delta.y = 0; delta.x = 1;
|
||||
when 'y': case'Y': delta.y = -1; delta.x = -1;
|
||||
when 'u': case'U': delta.y = -1; delta.x = 1;
|
||||
when 'b': case'B': delta.y = 1; delta.x = -1;
|
||||
when 'n': case'N': delta.y = 1; delta.x = 1;
|
||||
when ESCAPE: last_dir = '\0'; reset_last(); return FALSE;
|
||||
otherwise:
|
||||
mpos = 0;
|
||||
msg(rs,prompt);
|
||||
gotit = FALSE;
|
||||
}
|
||||
} until (gotit);
|
||||
if (isupper(dir_ch))
|
||||
dir_ch = (char) tolower(dir_ch);
|
||||
last_dir = dir_ch;
|
||||
last_delt.y = delta.y;
|
||||
last_delt.x = delta.x;
|
||||
}
|
||||
if (on(player, ISHUH) && rnd(5) == 0)
|
||||
do
|
||||
{
|
||||
delta.y = rnd(3) - 1;
|
||||
delta.x = rnd(3) - 1;
|
||||
} while (delta.y == 0 && delta.x == 0);
|
||||
mpos = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* sign:
|
||||
* Return the sign of the number
|
||||
*/
|
||||
int
|
||||
sign(int nm)
|
||||
{
|
||||
if (nm < 0)
|
||||
return -1;
|
||||
else
|
||||
return (nm > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* spread:
|
||||
* Give a spread around a given number (+/- 20%)
|
||||
*/
|
||||
int
|
||||
spread(int nm)
|
||||
{
|
||||
return nm - nm / 20 + rnd(nm / 10);
|
||||
}
|
||||
|
||||
/*
|
||||
* call_it:
|
||||
* Call an object something after use.
|
||||
*/
|
||||
|
||||
void
|
||||
call_it(struct rogue_state *rs,struct obj_info *info)
|
||||
{
|
||||
if (info->oi_know)
|
||||
{
|
||||
if (info->oi_guess)
|
||||
{
|
||||
free(info->oi_guess);
|
||||
info->oi_guess = NULL;
|
||||
}
|
||||
}
|
||||
else if (!info->oi_guess)
|
||||
{
|
||||
msg(rs,terse ? (char *)"call it: " : (char *)"what do you want to call it? ");
|
||||
if (get_str(rs,prbuf, stdscr) == NORM)
|
||||
{
|
||||
if (info->oi_guess != NULL)
|
||||
free(info->oi_guess);
|
||||
info->oi_guess = (char *)malloc((unsigned int) strlen(prbuf) + 1);
|
||||
strcpy(info->oi_guess, prbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rnd_thing:
|
||||
* Pick a random thing appropriate for this level
|
||||
*/
|
||||
char
|
||||
rnd_thing()
|
||||
{
|
||||
int i;
|
||||
static char thing_list[] = {
|
||||
POTION, SCROLL, RING, STICK, FOOD, WEAPON, ARMOR, STAIRS, GOLD, AMULET
|
||||
};
|
||||
|
||||
if (level >= AMULETLEVEL)
|
||||
i = rnd(sizeof thing_list / sizeof (char));
|
||||
else
|
||||
i = rnd(sizeof thing_list / sizeof (char) - 1);
|
||||
return thing_list[i];
|
||||
}
|
||||
|
||||
/*
|
||||
str str:
|
||||
* Choose the first or second string depending on whether it the
|
||||
* player is tripping
|
||||
*/
|
||||
char *
|
||||
choose_str(char *ts, char *ns)
|
||||
{
|
||||
return (on(player, ISHALU) ? ts : ns);
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* File with various monster functions in it, none as evil as KYC
|
||||
*
|
||||
* @(#)monsters.c 4.46 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <curses.h>
|
||||
//#include <string.h>
|
||||
#include "rogue.h"
|
||||
//#include <ctype.h>
|
||||
|
||||
/*
|
||||
* List of monsters in rough order of vorpalness
|
||||
*/
|
||||
static char lvl_mons[] = {
|
||||
'K', 'E', 'B', 'S', 'H', 'I', 'R', 'O', 'Z', 'L', 'C', 'Q', 'A',
|
||||
'N', 'Y', 'F', 'T', 'W', 'P', 'X', 'U', 'M', 'V', 'G', 'J', 'D'
|
||||
};
|
||||
|
||||
static char wand_mons[] = {
|
||||
'K', 'E', 'B', 'S', 'H', 0, 'R', 'O', 'Z', 0, 'C', 'Q', 'A',
|
||||
0, 'Y', 0, 'T', 'W', 'P', 0, 'U', 'M', 'V', 'G', 'J', 0
|
||||
};
|
||||
|
||||
/*
|
||||
* randmonster:
|
||||
* Pick a monster to show up. The lower the level,
|
||||
* the meaner the monster.
|
||||
*/
|
||||
char
|
||||
randmonster(bool wander)
|
||||
{
|
||||
int d;
|
||||
char *mons;
|
||||
|
||||
mons = (wander ? wand_mons : lvl_mons);
|
||||
do
|
||||
{
|
||||
d = level + (rnd(10) - 6);
|
||||
if (d < 0)
|
||||
d = rnd(5);
|
||||
if (d > 25)
|
||||
d = rnd(5) + 21;
|
||||
} while (mons[d] == 0);
|
||||
return mons[d];
|
||||
}
|
||||
|
||||
/*
|
||||
* new_monster:
|
||||
* Pick a new monster and add it to the list
|
||||
*/
|
||||
|
||||
void
|
||||
new_monster(struct rogue_state *rs,THING *tp, char type, coord *cp)
|
||||
{
|
||||
struct monster *mp;
|
||||
int lev_add;
|
||||
|
||||
if ((lev_add = level - AMULETLEVEL) < 0)
|
||||
lev_add = 0;
|
||||
attach(mlist, tp);
|
||||
tp->t_type = type;
|
||||
tp->t_disguise = type;
|
||||
tp->t_pos = *cp;
|
||||
move(cp->y, cp->x);
|
||||
tp->t_oldch = CCHAR( inch() );
|
||||
tp->t_room = roomin(rs,cp);
|
||||
moat(cp->y, cp->x) = tp;
|
||||
mp = &monsters[tp->t_type-'A'];
|
||||
tp->t_stats.s_lvl = mp->m_stats.s_lvl + lev_add;
|
||||
tp->t_stats.s_maxhp = tp->t_stats.s_hpt = roll(tp->t_stats.s_lvl, 8);
|
||||
tp->t_stats.s_arm = mp->m_stats.s_arm - lev_add;
|
||||
strcpy(tp->t_stats.s_dmg,mp->m_stats.s_dmg);
|
||||
tp->t_stats.s_str = mp->m_stats.s_str;
|
||||
tp->t_stats.s_exp = mp->m_stats.s_exp + lev_add * 10 + exp_add(tp);
|
||||
tp->t_flags = mp->m_flags;
|
||||
if (level > 29)
|
||||
tp->t_flags |= ISHASTE;
|
||||
tp->t_turn = TRUE;
|
||||
tp->t_pack = NULL;
|
||||
if (ISWEARING(R_AGGR))
|
||||
runto(rs,cp);
|
||||
if (type == 'X')
|
||||
tp->t_disguise = rnd_thing();
|
||||
}
|
||||
|
||||
/*
|
||||
* expadd:
|
||||
* Experience to add for this monster's level/hit points
|
||||
*/
|
||||
int
|
||||
exp_add(THING *tp)
|
||||
{
|
||||
int mod;
|
||||
|
||||
if (tp->t_stats.s_lvl == 1)
|
||||
mod = tp->t_stats.s_maxhp / 8;
|
||||
else
|
||||
mod = tp->t_stats.s_maxhp / 6;
|
||||
if (tp->t_stats.s_lvl > 9)
|
||||
mod *= 20;
|
||||
else if (tp->t_stats.s_lvl > 6)
|
||||
mod *= 4;
|
||||
return mod;
|
||||
}
|
||||
|
||||
/*
|
||||
* wanderer:
|
||||
* Create a new wandering monster and aim it at the player
|
||||
*/
|
||||
|
||||
void
|
||||
wanderer(struct rogue_state *rs)
|
||||
{
|
||||
THING *tp;
|
||||
static coord cp;
|
||||
|
||||
tp = new_item();
|
||||
do
|
||||
{
|
||||
find_floor(rs,(struct room *) NULL, &cp, FALSE, TRUE);
|
||||
} while (roomin(rs,&cp) == proom);
|
||||
new_monster(rs,tp, randmonster(TRUE), &cp);
|
||||
if (on(player, SEEMONST))
|
||||
{
|
||||
standout();
|
||||
if (!on(player, ISHALU))
|
||||
addch(tp->t_type);
|
||||
else
|
||||
addch(rnd(26) + 'A');
|
||||
standend();
|
||||
}
|
||||
runto(rs,&tp->t_pos);
|
||||
#ifdef MASTER
|
||||
if (wizard)
|
||||
msg(rs,"started a wandering %s", monsters[tp->t_type-'A'].m_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* wake_monster:
|
||||
* What to do when the hero steps next to a monster
|
||||
*/
|
||||
THING *
|
||||
wake_monster(struct rogue_state *rs,int y, int x)
|
||||
{
|
||||
THING *tp;
|
||||
struct room *rp;
|
||||
char ch, *mname;
|
||||
|
||||
#ifdef MASTER
|
||||
if ((tp = moat(y, x)) == NULL)
|
||||
msg(rs,"can't find monster in wake_monster");
|
||||
#else
|
||||
tp = moat(y, x);
|
||||
if (tp == NULL)
|
||||
endwin(), abort();
|
||||
#endif
|
||||
ch = tp->t_type;
|
||||
/*
|
||||
* Every time he sees mean monster, it might start chasing him
|
||||
*/
|
||||
if (!on(*tp, ISRUN) && rnd(3) != 0 && on(*tp, ISMEAN) && !on(*tp, ISHELD)
|
||||
&& !ISWEARING(R_STEALTH) && !on(player, ISLEVIT))
|
||||
{
|
||||
tp->t_dest = &hero;
|
||||
tp->t_flags |= ISRUN;
|
||||
}
|
||||
if (ch == 'M' && !on(player, ISBLIND) && !on(player, ISHALU)
|
||||
&& !on(*tp, ISFOUND) && !on(*tp, ISCANC) && on(*tp, ISRUN))
|
||||
{
|
||||
rp = proom;
|
||||
if ((rp != NULL && !(rp->r_flags & ISDARK))
|
||||
|| dist(y, x, hero.y, hero.x) < LAMPDIST)
|
||||
{
|
||||
tp->t_flags |= ISFOUND;
|
||||
if (!save(VS_MAGIC))
|
||||
{
|
||||
if (on(player, ISHUH))
|
||||
lengthen(unconfuse, spread(HUHDURATION));
|
||||
else
|
||||
fuse(unconfuse, 0, spread(HUHDURATION), AFTER);
|
||||
player.t_flags |= ISHUH;
|
||||
mname = set_mname(tp);
|
||||
addmsg(rs,"%s", mname);
|
||||
if (strcmp(mname, "it") != 0)
|
||||
addmsg(rs,"'");
|
||||
msg(rs,"s gaze has confused you");
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Let greedy ones guard gold
|
||||
*/
|
||||
if (on(*tp, ISGREED) && !on(*tp, ISRUN))
|
||||
{
|
||||
tp->t_flags |= ISRUN;
|
||||
if (proom->r_goldval)
|
||||
tp->t_dest = &proom->r_gold;
|
||||
else
|
||||
tp->t_dest = &hero;
|
||||
}
|
||||
return tp;
|
||||
}
|
||||
|
||||
/*
|
||||
* give_pack:
|
||||
* Give a pack to a monster if it deserves one
|
||||
*/
|
||||
|
||||
void
|
||||
give_pack(struct rogue_state *rs,THING *tp)
|
||||
{
|
||||
if (level >= max_level && rnd(100) < monsters[tp->t_type-'A'].m_carry)
|
||||
{
|
||||
//fprintf(stderr,"give_pack\n");
|
||||
attach(tp->t_pack, new_thing(rs));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* save_throw:
|
||||
* See if a creature save against something
|
||||
*/
|
||||
int
|
||||
save_throw(int which, THING *tp)
|
||||
{
|
||||
int need;
|
||||
|
||||
need = 14 + which - tp->t_stats.s_lvl / 2;
|
||||
return (roll(1, 20) >= need);
|
||||
}
|
||||
|
||||
/*
|
||||
* save:
|
||||
* See if he saves against various nasty things
|
||||
*/
|
||||
int
|
||||
save(int which)
|
||||
{
|
||||
if (which == VS_MAGIC)
|
||||
{
|
||||
if (ISRING(LEFT, R_PROTECT))
|
||||
which -= cur_ring[LEFT]->o_arm;
|
||||
if (ISRING(RIGHT, R_PROTECT))
|
||||
which -= cur_ring[RIGHT]->o_arm;
|
||||
}
|
||||
return save_throw(which, &player);
|
||||
}
|
||||
@@ -1,429 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* hero movement commands
|
||||
*
|
||||
* @(#)move.c 4.49 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <curses.h>
|
||||
//#include <ctype.h>
|
||||
#include "rogue.h"
|
||||
|
||||
/*
|
||||
* used to hold the new hero position
|
||||
*/
|
||||
|
||||
coord nh;
|
||||
|
||||
/*
|
||||
* do_run:
|
||||
* Start the hero running
|
||||
*/
|
||||
|
||||
void
|
||||
do_run(char ch)
|
||||
{
|
||||
running = TRUE;
|
||||
after = FALSE;
|
||||
runch = ch;
|
||||
}
|
||||
|
||||
/*
|
||||
* do_move:
|
||||
* Check to see that a move is legal. If it is handle the
|
||||
* consequences (fighting, picking up, etc.)
|
||||
*/
|
||||
|
||||
void
|
||||
do_move(struct rogue_state *rs,int dy, int dx)
|
||||
{
|
||||
char ch, fl;
|
||||
|
||||
firstmove = FALSE;
|
||||
if (no_move)
|
||||
{
|
||||
no_move--;
|
||||
msg(rs,"you are still stuck in the bear trap");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Do a confused move (maybe)
|
||||
*/
|
||||
if (on(player, ISHUH) && rnd(5) != 0)
|
||||
{
|
||||
nh = *rndmove(&player);
|
||||
if (ce(nh, hero))
|
||||
{
|
||||
after = FALSE;
|
||||
running = FALSE;
|
||||
to_death = FALSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
over:
|
||||
nh.y = hero.y + dy;
|
||||
nh.x = hero.x + dx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if he tried to move off the screen or make an illegal
|
||||
* diagonal move, and stop him if he did.
|
||||
*/
|
||||
if (nh.x < 0 || nh.x >= NUMCOLS || nh.y <= 0 || nh.y >= NUMLINES - 1)
|
||||
goto hit_bound;
|
||||
if (!diag_ok(&hero, &nh))
|
||||
{
|
||||
after = FALSE;
|
||||
running = FALSE;
|
||||
return;
|
||||
}
|
||||
if (running && ce(hero, nh))
|
||||
after = running = FALSE;
|
||||
fl = flat(nh.y, nh.x);
|
||||
ch = winat(nh.y, nh.x);
|
||||
if (!(fl & F_REAL) && ch == FLOOR)
|
||||
{
|
||||
if (!on(player, ISLEVIT))
|
||||
{
|
||||
chat(nh.y, nh.x) = ch = TRAP;
|
||||
flat(nh.y, nh.x) |= F_REAL;
|
||||
}
|
||||
}
|
||||
else if (on(player, ISHELD) && ch != 'F')
|
||||
{
|
||||
msg(rs,"you are being held");
|
||||
return;
|
||||
}
|
||||
switch (ch)
|
||||
{
|
||||
case ' ':
|
||||
case '|':
|
||||
case '-':
|
||||
hit_bound:
|
||||
if (passgo && running && (proom->r_flags & ISGONE)
|
||||
&& !on(player, ISBLIND))
|
||||
{
|
||||
bool b1, b2;
|
||||
|
||||
switch (runch)
|
||||
{
|
||||
case 'h':
|
||||
case 'l':
|
||||
b1 = (bool)(hero.y != 1 && turn_ok(hero.y - 1, hero.x));
|
||||
b2 = (bool)(hero.y != NUMLINES - 2 && turn_ok(hero.y + 1, hero.x));
|
||||
if (!(b1 ^ b2))
|
||||
break;
|
||||
if (b1)
|
||||
{
|
||||
runch = 'k';
|
||||
dy = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
runch = 'j';
|
||||
dy = 1;
|
||||
}
|
||||
dx = 0;
|
||||
turnref();
|
||||
goto over;
|
||||
case 'j':
|
||||
case 'k':
|
||||
b1 = (bool)(hero.x != 0 && turn_ok(hero.y, hero.x - 1));
|
||||
b2 = (bool)(hero.x != NUMCOLS - 1 && turn_ok(hero.y, hero.x + 1));
|
||||
if (!(b1 ^ b2))
|
||||
break;
|
||||
if (b1)
|
||||
{
|
||||
runch = 'h';
|
||||
dx = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
runch = 'l';
|
||||
dx = 1;
|
||||
}
|
||||
dy = 0;
|
||||
turnref();
|
||||
goto over;
|
||||
}
|
||||
}
|
||||
running = FALSE;
|
||||
after = FALSE;
|
||||
break;
|
||||
case DOOR:
|
||||
running = FALSE;
|
||||
if (flat(hero.y, hero.x) & F_PASS)
|
||||
enter_room(rs,&nh);
|
||||
goto move_stuff;
|
||||
case TRAP:
|
||||
ch = be_trapped(rs,&nh);
|
||||
if (ch == T_DOOR || ch == T_TELEP)
|
||||
return;
|
||||
goto move_stuff;
|
||||
case PASSAGE:
|
||||
/*
|
||||
* when you're in a corridor, you don't know if you're in
|
||||
* a maze room or not, and there ain't no way to find out
|
||||
* if you're leaving a maze room, so it is necessary to
|
||||
* always recalculate proom.
|
||||
*/
|
||||
proom = roomin(rs,&hero);
|
||||
goto move_stuff;
|
||||
case FLOOR:
|
||||
if (!(fl & F_REAL))
|
||||
be_trapped(rs,&hero);
|
||||
goto move_stuff;
|
||||
case STAIRS:
|
||||
seenstairs = TRUE;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
running = FALSE;
|
||||
if (isupper(ch) || moat(nh.y, nh.x))
|
||||
fight(rs,&nh, cur_weapon, FALSE);
|
||||
else
|
||||
{
|
||||
if (ch != STAIRS)
|
||||
take = ch;
|
||||
move_stuff:
|
||||
mvaddch(hero.y, hero.x, floor_at());
|
||||
if ((fl & F_PASS) && chat(oldpos.y, oldpos.x) == DOOR)
|
||||
leave_room(rs,&nh);
|
||||
hero = nh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* turn_ok:
|
||||
* Decide whether it is legal to turn onto the given space
|
||||
*/
|
||||
bool
|
||||
turn_ok(int y, int x)
|
||||
{
|
||||
PLACE *pp;
|
||||
|
||||
pp = INDEX(y, x);
|
||||
return (pp->p_ch == DOOR
|
||||
|| (pp->p_flags & (F_REAL|F_PASS)) == (F_REAL|F_PASS));
|
||||
}
|
||||
|
||||
/*
|
||||
* turnref:
|
||||
* Decide whether to refresh at a passage turning or not
|
||||
*/
|
||||
|
||||
void
|
||||
turnref()
|
||||
{
|
||||
PLACE *pp;
|
||||
|
||||
pp = INDEX(hero.y, hero.x);
|
||||
if (!(pp->p_flags & F_SEEN))
|
||||
{
|
||||
if (jump)
|
||||
{
|
||||
leaveok(stdscr, TRUE);
|
||||
if ( globalR.sleeptime != 0 )
|
||||
refresh();
|
||||
leaveok(stdscr, FALSE);
|
||||
}
|
||||
pp->p_flags |= F_SEEN;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* door_open:
|
||||
* Called to illuminate a room. If it is dark, remove anything
|
||||
* that might move.
|
||||
*/
|
||||
|
||||
void
|
||||
door_open(struct rogue_state *rs,struct room *rp)
|
||||
{
|
||||
int y, x;
|
||||
|
||||
if (!(rp->r_flags & ISGONE))
|
||||
for (y = rp->r_pos.y; y < rp->r_pos.y + rp->r_max.y; y++)
|
||||
for (x = rp->r_pos.x; x < rp->r_pos.x + rp->r_max.x; x++)
|
||||
if (isupper(winat(y, x)))
|
||||
wake_monster(rs,y, x);
|
||||
}
|
||||
|
||||
/*
|
||||
* be_trapped:
|
||||
* The guy stepped on a trap.... Make him pay.
|
||||
*/
|
||||
char
|
||||
be_trapped(struct rogue_state *rs,coord *tc)
|
||||
{
|
||||
PLACE *pp;
|
||||
THING *arrow;
|
||||
char tr;
|
||||
|
||||
if (on(player, ISLEVIT))
|
||||
return T_RUST; /* anything that's not a door or teleport */
|
||||
running = FALSE;
|
||||
count = FALSE;
|
||||
pp = INDEX(tc->y, tc->x);
|
||||
pp->p_ch = TRAP;
|
||||
tr = pp->p_flags & F_TMASK;
|
||||
pp->p_flags |= F_SEEN;
|
||||
switch (tr)
|
||||
{
|
||||
case T_DOOR:
|
||||
level++;
|
||||
new_level(rs);
|
||||
msg(rs,"you fell into a trap!");
|
||||
when T_BEAR:
|
||||
no_move += BEARTIME;
|
||||
msg(rs,"you are caught in a bear trap");
|
||||
when T_MYST:
|
||||
switch(rnd(11))
|
||||
{
|
||||
case 0: msg(rs,"you are suddenly in a parallel dimension");
|
||||
when 1: msg(rs,"the light in here suddenly seems %s", rainbow[rnd(cNCOLORS)]);
|
||||
when 2: msg(rs,"you feel a sting in the side of your neck");
|
||||
when 3: msg(rs,"multi-colored lines swirl around you, then fade");
|
||||
when 4: msg(rs,"a %s light flashes in your eyes", rainbow[rnd(cNCOLORS)]);
|
||||
when 5: msg(rs,"a spike shoots past your ear!");
|
||||
when 6: msg(rs,"%s sparks dance across your armor", rainbow[rnd(cNCOLORS)]);
|
||||
when 7: msg(rs,"you suddenly feel very thirsty");
|
||||
when 8: msg(rs,"you feel time speed up suddenly");
|
||||
when 9: msg(rs,"time now seems to be going slower");
|
||||
when 10: msg(rs,"you pack turns %s!", rainbow[rnd(cNCOLORS)]);
|
||||
}
|
||||
when T_SLEEP:
|
||||
no_command += SLEEPTIME;
|
||||
player.t_flags &= ~ISRUN;
|
||||
msg(rs,"a strange white mist envelops you and you fall asleep");
|
||||
when T_ARROW:
|
||||
if (swing(pstats.s_lvl - 1, pstats.s_arm, 1))
|
||||
{
|
||||
pstats.s_hpt -= roll(1, 6);
|
||||
if (pstats.s_hpt <= 0)
|
||||
{
|
||||
msg(rs,"an arrow killed you");
|
||||
death(rs,'a');
|
||||
}
|
||||
else
|
||||
msg(rs,"oh no! An arrow shot you");
|
||||
}
|
||||
else
|
||||
{
|
||||
arrow = new_item();
|
||||
init_weapon(arrow, ARROW);
|
||||
arrow->o_count = 1;
|
||||
arrow->o_pos = hero;
|
||||
fall(rs,arrow, FALSE);
|
||||
msg(rs,"an arrow shoots past you");
|
||||
}
|
||||
when T_TELEP:
|
||||
/*
|
||||
* since the hero's leaving, look() won't put a TRAP
|
||||
* down for us, so we have to do it ourself
|
||||
*/
|
||||
teleport(rs);
|
||||
mvaddch(tc->y, tc->x, TRAP);
|
||||
when T_DART:
|
||||
if (!swing(pstats.s_lvl+1, pstats.s_arm, 1))
|
||||
msg(rs,"a small dart whizzes by your ear and vanishes");
|
||||
else
|
||||
{
|
||||
pstats.s_hpt -= roll(1, 4);
|
||||
if (pstats.s_hpt <= 0)
|
||||
{
|
||||
msg(rs,"a poisoned dart killed you");
|
||||
death(rs,'d');
|
||||
}
|
||||
if (!ISWEARING(R_SUSTSTR) && !save(VS_POISON))
|
||||
chg_str(-1);
|
||||
msg(rs,"a small dart just hit you in the shoulder");
|
||||
}
|
||||
when T_RUST:
|
||||
msg(rs,"a gush of water hits you on the head");
|
||||
rust_armor(rs,cur_armor);
|
||||
}
|
||||
flush_type();
|
||||
return tr;
|
||||
}
|
||||
|
||||
/*
|
||||
* rndmove:
|
||||
* Move in a random direction if the monster/person is confused
|
||||
*/
|
||||
coord *
|
||||
rndmove(THING *who)
|
||||
{
|
||||
THING *obj;
|
||||
int x, y;
|
||||
char ch;
|
||||
static coord ret; /* what we will be returning */
|
||||
|
||||
y = ret.y = who->t_pos.y + rnd(3) - 1;
|
||||
x = ret.x = who->t_pos.x + rnd(3) - 1;
|
||||
/*
|
||||
* Now check to see if that's a legal move. If not, don't move.
|
||||
* (I.e., bump into the wall or whatever)
|
||||
*/
|
||||
if (y == who->t_pos.y && x == who->t_pos.x)
|
||||
return &ret;
|
||||
if (!diag_ok(&who->t_pos, &ret))
|
||||
goto bad;
|
||||
else
|
||||
{
|
||||
ch = winat(y, x);
|
||||
if (!step_ok(ch))
|
||||
goto bad;
|
||||
if (ch == SCROLL)
|
||||
{
|
||||
for (obj = lvl_obj; obj != NULL; obj = next(obj))
|
||||
if (y == obj->o_pos.y && x == obj->o_pos.x)
|
||||
break;
|
||||
if (obj != NULL && obj->o_which == S_SCARE)
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
return &ret;
|
||||
|
||||
bad:
|
||||
ret = who->t_pos;
|
||||
return &ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* rust_armor:
|
||||
* Rust the given armor, if it is a legal kind to rust, and we
|
||||
* aren't wearing a magic ring.
|
||||
*/
|
||||
|
||||
void
|
||||
rust_armor(struct rogue_state *rs,THING *arm)
|
||||
{
|
||||
if (arm == NULL || arm->o_type != ARMOR || arm->o_which == LEATHER ||
|
||||
arm->o_arm >= 9)
|
||||
return;
|
||||
|
||||
if ((arm->o_flags & ISPROT) || ISWEARING(R_SUSTARM))
|
||||
{
|
||||
if (!to_death)
|
||||
msg(rs,"the rust vanishes instantly");
|
||||
}
|
||||
else
|
||||
{
|
||||
arm->o_arm++;
|
||||
if (!terse)
|
||||
msg(rs,"your armor appears to be weaker now. Oh my!");
|
||||
else
|
||||
msg(rs,"your armor weakens");
|
||||
}
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* new_level:
|
||||
* Dig and draw a new level
|
||||
*
|
||||
* @(#)new_level.c 4.38 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <curses.h>
|
||||
//#include <string.h>
|
||||
#include "rogue.h"
|
||||
|
||||
#define TREAS_ROOM 20 /* one chance in TREAS_ROOM for a treasure room */
|
||||
#define MAXTREAS 10 /* maximum number of treasures in a treasure room */
|
||||
#define MINTREAS 2 /* minimum number of treasures in a treasure room */
|
||||
|
||||
void
|
||||
new_level(struct rogue_state *rs)
|
||||
{
|
||||
THING *tp;
|
||||
PLACE *pp;
|
||||
char *sp;
|
||||
int i;
|
||||
if ( 0 )
|
||||
{
|
||||
static FILE *fp;
|
||||
if ( fp == 0 )
|
||||
fp = fopen("debug","wb");
|
||||
if ( fp != 0 )
|
||||
{
|
||||
fprintf(fp,"newlevel seed.%llu\n",(long long)seed);
|
||||
fflush(fp);
|
||||
}
|
||||
}
|
||||
player.t_flags &= ~ISHELD; /* unhold when you go down just in case */
|
||||
if (level > max_level)
|
||||
max_level = level;
|
||||
/*
|
||||
* Clean things off from last level
|
||||
*/
|
||||
for (pp = places; pp < &places[MAXCOLS*MAXLINES]; pp++)
|
||||
{
|
||||
pp->p_ch = ' ';
|
||||
pp->p_flags = F_REAL;
|
||||
pp->p_monst = NULL;
|
||||
}
|
||||
clear();
|
||||
/*
|
||||
* Free up the monsters on the last level
|
||||
*/
|
||||
for (tp = mlist; tp != NULL; tp = next(tp))
|
||||
free_list(tp->t_pack);
|
||||
free_list(mlist);
|
||||
/*
|
||||
* Throw away stuff left on the previous level (if anything)
|
||||
*/
|
||||
free_list(lvl_obj);
|
||||
do_rooms(rs); /* Draw rooms */
|
||||
do_passages(rs); /* Draw passages */
|
||||
no_food++;
|
||||
//fprintf(stderr,"new_level.%d\n",level);
|
||||
put_things(rs); /* Place objects (if any) */
|
||||
/*
|
||||
* Place the traps
|
||||
*/
|
||||
if (rnd(10) < level)
|
||||
{
|
||||
ntraps = rnd(level / 4) + 1;
|
||||
if (ntraps > MAXTRAPS)
|
||||
ntraps = MAXTRAPS;
|
||||
i = ntraps;
|
||||
while (i--)
|
||||
{
|
||||
/*
|
||||
* not only wouldn't it be NICE to have traps in mazes
|
||||
* (not that we care about being nice), since the trap
|
||||
* number is stored where the passage number is, we
|
||||
* can't actually do it.
|
||||
*/
|
||||
do
|
||||
{
|
||||
find_floor(rs,(struct room *) NULL, &stairs, FALSE, FALSE);
|
||||
} while (chat(stairs.y, stairs.x) != FLOOR);
|
||||
sp = &flat(stairs.y, stairs.x);
|
||||
*sp &= ~F_REAL;
|
||||
*sp |= rnd(NTRAPS);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Place the staircase down.
|
||||
*/
|
||||
find_floor(rs,(struct room *) NULL, &stairs, FALSE, FALSE);
|
||||
chat(stairs.y, stairs.x) = STAIRS;
|
||||
seenstairs = FALSE;
|
||||
|
||||
for (tp = mlist; tp != NULL; tp = next(tp))
|
||||
tp->t_room = roomin(rs,&tp->t_pos);
|
||||
|
||||
find_floor(rs,(struct room *) NULL, &hero, FALSE, TRUE);
|
||||
enter_room(rs,&hero);
|
||||
mvaddch(hero.y, hero.x, PLAYER);
|
||||
if (on(player, SEEMONST))
|
||||
turn_see(rs,FALSE);
|
||||
if (on(player, ISHALU))
|
||||
visuals(rs,0);
|
||||
}
|
||||
|
||||
/*
|
||||
* rnd_room:
|
||||
* Pick a room that is really there
|
||||
*/
|
||||
int
|
||||
rnd_room()
|
||||
{
|
||||
int rm;
|
||||
|
||||
do
|
||||
{
|
||||
rm = rnd(MAXROOMS);
|
||||
} while (rooms[rm].r_flags & ISGONE);
|
||||
return rm;
|
||||
}
|
||||
|
||||
/*
|
||||
* put_things:
|
||||
* Put potions and scrolls on this level
|
||||
*/
|
||||
|
||||
void
|
||||
put_things(struct rogue_state *rs)
|
||||
{
|
||||
int i;
|
||||
THING *obj;
|
||||
|
||||
/*
|
||||
* Once you have found the amulet, the only way to get new stuff is
|
||||
* go down into the dungeon.
|
||||
*/
|
||||
if (amulet && level < max_level)
|
||||
return;
|
||||
/*
|
||||
* check for treasure rooms, and if so, put it in.
|
||||
*/
|
||||
if (rnd(TREAS_ROOM) == 0)
|
||||
treas_room(rs);
|
||||
/*
|
||||
* Do MAXOBJ attempts to put things on a level
|
||||
*/
|
||||
for (i = 0; i < MAXOBJ; i++)
|
||||
if (rnd(100) < 36)
|
||||
{
|
||||
/*
|
||||
* Pick a new object and link it in the list
|
||||
*/
|
||||
obj = new_thing(rs);
|
||||
//fprintf(stderr,"put_things i.%d obj.%p\n",i,obj);
|
||||
attach(lvl_obj, obj);
|
||||
/*
|
||||
* Put it somewhere
|
||||
*/
|
||||
find_floor(rs,(struct room *) NULL, &obj->o_pos, FALSE, FALSE);
|
||||
chat(obj->o_pos.y, obj->o_pos.x) = (char) obj->o_type;
|
||||
}
|
||||
/*
|
||||
* If he is really deep in the dungeon and he hasn't found the
|
||||
* amulet yet, put it somewhere on the ground
|
||||
*/
|
||||
if (level >= AMULETLEVEL && !amulet)
|
||||
{
|
||||
obj = new_item();
|
||||
attach(lvl_obj, obj);
|
||||
obj->o_hplus = 0;
|
||||
obj->o_dplus = 0;
|
||||
strncpy(obj->o_damage,"0x0",sizeof(obj->o_damage));
|
||||
strncpy(obj->o_hurldmg,"0x0",sizeof(obj->o_hurldmg));
|
||||
obj->o_arm = 11;
|
||||
obj->o_type = AMULET;
|
||||
/*
|
||||
* Put it somewhere
|
||||
*/
|
||||
find_floor(rs,(struct room *) NULL, &obj->o_pos, FALSE, FALSE);
|
||||
chat(obj->o_pos.y, obj->o_pos.x) = AMULET;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* treas_room:
|
||||
* Add a treasure room
|
||||
*/
|
||||
#define MAXTRIES 10 /* max number of tries to put down a monster */
|
||||
|
||||
|
||||
void
|
||||
treas_room(struct rogue_state *rs)
|
||||
{
|
||||
int nm;
|
||||
THING *tp;
|
||||
struct room *rp;
|
||||
int spots, num_monst;
|
||||
static coord mp;
|
||||
|
||||
rp = &rooms[rnd_room()];
|
||||
spots = (rp->r_max.y - 2) * (rp->r_max.x - 2) - MINTREAS;
|
||||
if (spots > (MAXTREAS - MINTREAS))
|
||||
spots = (MAXTREAS - MINTREAS);
|
||||
num_monst = nm = rnd(spots) + MINTREAS;
|
||||
while (nm--)
|
||||
{
|
||||
find_floor(rs,rp, &mp, 2 * MAXTRIES, FALSE);
|
||||
//fprintf(stderr,"treas_room\n");
|
||||
tp = new_thing(rs);
|
||||
tp->o_pos = mp;
|
||||
attach(lvl_obj, tp);
|
||||
chat(mp.y, mp.x) = (char) tp->o_type;
|
||||
}
|
||||
|
||||
/*
|
||||
* fill up room with monsters from the next level down
|
||||
*/
|
||||
|
||||
if ((nm = rnd(spots) + MINTREAS) < num_monst + 2)
|
||||
nm = num_monst + 2;
|
||||
spots = (rp->r_max.y - 2) * (rp->r_max.x - 2);
|
||||
if (nm > spots)
|
||||
nm = spots;
|
||||
level++;
|
||||
while (nm--)
|
||||
{
|
||||
spots = 0;
|
||||
if (find_floor(rs,rp, &mp, MAXTRIES, TRUE))
|
||||
{
|
||||
tp = new_item();
|
||||
new_monster(rs,tp, randmonster(FALSE), &mp);
|
||||
tp->t_flags |= ISMEAN; /* no sloughers in THIS room */
|
||||
give_pack(rs,tp);
|
||||
}
|
||||
}
|
||||
level--;
|
||||
}
|
||||
@@ -1,504 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* This file has all the code for the option command. I would rather
|
||||
* this command were not necessary, but it is the only way to keep the
|
||||
* wolves off of my back.
|
||||
*
|
||||
* @(#)options.c 4.24 (Berkeley) 05/10/83
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <curses.h>
|
||||
//#include <ctype.h>
|
||||
//#include <string.h>
|
||||
#include "rogue.h"
|
||||
|
||||
#define EQSTR(a, b, c) (strncmp(a, b, c) == 0)
|
||||
|
||||
#define NUM_OPTS (sizeof optlist / sizeof (OPTION))
|
||||
|
||||
/*
|
||||
* description of an option and what to do with it
|
||||
*/
|
||||
struct optstruct {
|
||||
char *o_name; /* option name */
|
||||
char *o_prompt; /* prompt for interactive entry */
|
||||
void *o_opt; /* pointer to thing to set */
|
||||
/* function to print value */
|
||||
void (*o_putfunc)(void *opt);
|
||||
/* function to get value interactively */
|
||||
int (*o_getfunc)(struct rogue_state *rs,void *opt, WINDOW *win);
|
||||
};
|
||||
|
||||
typedef struct optstruct OPTION;
|
||||
|
||||
void pr_optname(OPTION *op);
|
||||
|
||||
OPTION optlist[] = {
|
||||
{"terse", "Terse output",
|
||||
&terse, put_bool, get_bool },
|
||||
{"flush", "Flush typeahead during battle",
|
||||
&fight_flush, put_bool, get_bool },
|
||||
{"jump", "Show position only at end of run",
|
||||
&jump, put_bool, get_bool },
|
||||
{"seefloor", "Show the lamp-illuminated floor",
|
||||
&see_floor, put_bool, get_sf },
|
||||
{"passgo", "Follow turnings in passageways",
|
||||
&passgo, put_bool, get_bool },
|
||||
{"tombstone", "Print out tombstone when killed",
|
||||
&tombstone, put_bool, get_bool },
|
||||
{"inven", "Inventory style",
|
||||
&inv_type, put_inv_t, get_inv_t },
|
||||
{"name", "Name",
|
||||
whoami, put_str, get_str },
|
||||
{"fruit", "Fruit",
|
||||
fruit, put_str, get_str },
|
||||
{"file", "Save file",
|
||||
file_name, put_str, get_str }
|
||||
};
|
||||
|
||||
/*
|
||||
* option:
|
||||
* Print and then set options from the terminal
|
||||
*/
|
||||
|
||||
void
|
||||
option(struct rogue_state *rs)
|
||||
{
|
||||
OPTION *op;
|
||||
int retval;
|
||||
|
||||
wclear(hw);
|
||||
/*
|
||||
* Display current values of options
|
||||
*/
|
||||
for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++)
|
||||
{
|
||||
pr_optname(op);
|
||||
(*op->o_putfunc)(op->o_opt);
|
||||
waddch(hw, '\n');
|
||||
}
|
||||
/*
|
||||
* Set values
|
||||
*/
|
||||
wmove(hw, 0, 0);
|
||||
for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++)
|
||||
{
|
||||
pr_optname(op);
|
||||
retval = (*op->o_getfunc)(rs,op->o_opt, hw);
|
||||
if (retval)
|
||||
{
|
||||
if (retval == QUIT)
|
||||
break;
|
||||
else if (op > optlist) { /* MINUS */
|
||||
wmove(hw, (int)(op - optlist) - 1, 0);
|
||||
op -= 2;
|
||||
}
|
||||
else /* trying to back up beyond the top */
|
||||
{
|
||||
putchar('\007');
|
||||
wmove(hw, 0, 0);
|
||||
op--;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Switch back to original screen
|
||||
*/
|
||||
wmove(hw, LINES - 1, 0);
|
||||
waddstr(hw, "--Press space to continue--");
|
||||
wrefresh(hw);
|
||||
wait_for(rs,' ');
|
||||
clearok(curscr, TRUE);
|
||||
touchwin(stdscr);
|
||||
after = FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* pr_optname:
|
||||
* Print out the option name prompt
|
||||
*/
|
||||
|
||||
void
|
||||
pr_optname(OPTION *op)
|
||||
{
|
||||
wprintw(hw, "%s (\"%s\"): ", op->o_prompt, op->o_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* put_bool
|
||||
* Put out a boolean
|
||||
*/
|
||||
|
||||
void
|
||||
put_bool(void *b)
|
||||
{
|
||||
waddstr(hw, *(bool *) b ? "True" : "False");
|
||||
}
|
||||
|
||||
/*
|
||||
* put_str:
|
||||
* Put out a string
|
||||
*/
|
||||
|
||||
void
|
||||
put_str(void *str)
|
||||
{
|
||||
waddstr(hw, (char *) str);
|
||||
}
|
||||
|
||||
/*
|
||||
* put_inv_t:
|
||||
* Put out an inventory type
|
||||
*/
|
||||
|
||||
void
|
||||
put_inv_t(void *ip)
|
||||
{
|
||||
waddstr(hw, inv_t_name[*(int *) ip]);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_bool:
|
||||
* Allow changing a boolean option and print it out
|
||||
*/
|
||||
int
|
||||
get_bool(struct rogue_state *rs,void *vp, WINDOW *win)
|
||||
{
|
||||
bool *bp = (bool *) vp;
|
||||
int oy, ox;
|
||||
bool op_bad;
|
||||
|
||||
op_bad = TRUE;
|
||||
getyx(win, oy, ox);
|
||||
waddstr(win, *bp ? "True" : "False");
|
||||
while (op_bad)
|
||||
{
|
||||
wmove(win, oy, ox);
|
||||
wrefresh(win);
|
||||
switch (readchar(rs))
|
||||
{
|
||||
case 't':
|
||||
case 'T':
|
||||
*bp = TRUE;
|
||||
op_bad = FALSE;
|
||||
break;
|
||||
case 'f':
|
||||
case 'F':
|
||||
*bp = FALSE;
|
||||
op_bad = FALSE;
|
||||
break;
|
||||
case '\n':
|
||||
case '\r':
|
||||
op_bad = FALSE;
|
||||
break;
|
||||
case ESCAPE:
|
||||
return QUIT;
|
||||
case '-':
|
||||
return MINUS;
|
||||
default:
|
||||
wmove(win, oy, ox + 10);
|
||||
waddstr(win, "(T or F)");
|
||||
}
|
||||
}
|
||||
wmove(win, oy, ox);
|
||||
waddstr(win, *bp ? "True" : "False");
|
||||
waddch(win, '\n');
|
||||
return NORM;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_sf:
|
||||
* Change value and handle transition problems from see_floor to
|
||||
* !see_floor.
|
||||
*/
|
||||
int
|
||||
get_sf(struct rogue_state *rs,void *vp, WINDOW *win)
|
||||
{
|
||||
bool *bp = (bool *) vp;
|
||||
bool was_sf;
|
||||
int retval;
|
||||
|
||||
was_sf = see_floor;
|
||||
retval = get_bool(rs,bp, win);
|
||||
if (retval == QUIT) return(QUIT);
|
||||
if (was_sf != see_floor)
|
||||
{
|
||||
if (!see_floor) {
|
||||
see_floor = TRUE;
|
||||
erase_lamp(&hero, proom);
|
||||
see_floor = FALSE;
|
||||
}
|
||||
else
|
||||
look(rs,FALSE);
|
||||
}
|
||||
return(NORM);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_str:
|
||||
* Set a string option
|
||||
*/
|
||||
#define MAXINP 50 /* max string to read from terminal or environment */
|
||||
|
||||
int
|
||||
get_str(struct rogue_state *rs,void *vopt, WINDOW *win)
|
||||
{
|
||||
char *opt = (char *) vopt;
|
||||
char *sp;
|
||||
int oy, ox;
|
||||
int i;
|
||||
signed char c;
|
||||
static char buf[MAXSTR];
|
||||
|
||||
getyx(win, oy, ox);
|
||||
wrefresh(win);
|
||||
/*
|
||||
* loop reading in the string, and put it in a temporary buffer
|
||||
*/
|
||||
for (sp = buf; (c = readchar(rs)) != '\n' && c != '\r' && c != ESCAPE;
|
||||
wclrtoeol(win), wrefresh(win))
|
||||
{
|
||||
if (c == -1)
|
||||
continue;
|
||||
/*else if (c == erasechar()) // process erase character
|
||||
{
|
||||
if (sp > buf)
|
||||
{
|
||||
sp--;
|
||||
for (i = (int) strlen(unctrl(*sp)); i; i--)
|
||||
waddch(win, '\b');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (c == killchar()) // process kill character
|
||||
{
|
||||
sp = buf;
|
||||
wmove(win, oy, ox);
|
||||
continue;
|
||||
}*/
|
||||
else if (sp == buf)
|
||||
{
|
||||
if (c == '-' && win != stdscr)
|
||||
break;
|
||||
else if (c == '~')
|
||||
{
|
||||
strcpy(buf, home);
|
||||
waddstr(win, home);
|
||||
sp += strlen(home);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (sp >= &buf[MAXINP] || !(isprint(c) || c == ' '))
|
||||
putchar(CTRL('G'));
|
||||
else
|
||||
{
|
||||
*sp++ = c;
|
||||
waddstr(win, unctrl(c));
|
||||
}
|
||||
}
|
||||
*sp = '\0';
|
||||
if (sp > buf) /* only change option if something has been typed */
|
||||
strucpy(opt, buf, (int) strlen(buf));
|
||||
mvwprintw(win, oy, ox, "%s\n", opt);
|
||||
wrefresh(win);
|
||||
if (win == stdscr)
|
||||
mpos += (int)(sp - buf);
|
||||
if (c == '-')
|
||||
return MINUS;
|
||||
else if (c == ESCAPE)
|
||||
return QUIT;
|
||||
else
|
||||
return NORM;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_inv_t
|
||||
* Get an inventory type name
|
||||
*/
|
||||
int
|
||||
get_inv_t(struct rogue_state *rs,void *vp, WINDOW *win)
|
||||
{
|
||||
int *ip = (int *) vp;
|
||||
int oy, ox;
|
||||
bool op_bad;
|
||||
|
||||
op_bad = TRUE;
|
||||
getyx(win, oy, ox);
|
||||
waddstr(win, inv_t_name[*ip]);
|
||||
while (op_bad)
|
||||
{
|
||||
wmove(win, oy, ox);
|
||||
wrefresh(win);
|
||||
switch (readchar(rs))
|
||||
{
|
||||
case 'o':
|
||||
case 'O':
|
||||
*ip = INV_OVER;
|
||||
op_bad = FALSE;
|
||||
break;
|
||||
case 's':
|
||||
case 'S':
|
||||
*ip = INV_SLOW;
|
||||
op_bad = FALSE;
|
||||
break;
|
||||
case 'c':
|
||||
case 'C':
|
||||
*ip = INV_CLEAR;
|
||||
op_bad = FALSE;
|
||||
break;
|
||||
case '\n':
|
||||
case '\r':
|
||||
op_bad = FALSE;
|
||||
break;
|
||||
case ESCAPE:
|
||||
return QUIT;
|
||||
case '-':
|
||||
return MINUS;
|
||||
default:
|
||||
wmove(win, oy, ox + 15);
|
||||
waddstr(win, "(O, S, or C)");
|
||||
}
|
||||
}
|
||||
mvwprintw(win, oy, ox, "%s\n", inv_t_name[*ip]);
|
||||
return NORM;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MASTER
|
||||
/*
|
||||
* get_num:
|
||||
* Get a numeric option
|
||||
*/
|
||||
int
|
||||
get_num(struct rogue_state *rs,void *vp, WINDOW *win)
|
||||
{
|
||||
short *opt = (short *) vp;
|
||||
int i;
|
||||
static char buf[MAXSTR];
|
||||
|
||||
if ((i = get_str(rs,buf, win)) == NORM)
|
||||
*opt = (short) atoi(buf);
|
||||
return i;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* parse_opts:
|
||||
* Parse options from string, usually taken from the environment.
|
||||
* The string is a series of comma seperated values, with booleans
|
||||
* being stated as "name" (true) or "noname" (false), and strings
|
||||
* being "name=....", with the string being defined up to a comma
|
||||
* or the end of the entire option string.
|
||||
*/
|
||||
|
||||
void
|
||||
parse_opts(char *str)
|
||||
{
|
||||
char *sp;
|
||||
OPTION *op;
|
||||
int len;
|
||||
const char **i;
|
||||
char *start;
|
||||
|
||||
while (*str)
|
||||
{
|
||||
/*
|
||||
* Get option name
|
||||
*/
|
||||
for (sp = str; isalpha(*sp); sp++)
|
||||
continue;
|
||||
len = (int)(sp - str);
|
||||
/*
|
||||
* Look it up and deal with it
|
||||
*/
|
||||
for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++)
|
||||
if (EQSTR(str, op->o_name, len))
|
||||
{
|
||||
if (op->o_putfunc == put_bool) /* if option is a boolean */
|
||||
*(bool *)op->o_opt = TRUE; /* NOSTRICT */
|
||||
else /* string option */
|
||||
{
|
||||
/*
|
||||
* Skip to start of string value
|
||||
*/
|
||||
for (str = sp + 1; *str == '='; str++)
|
||||
continue;
|
||||
if (*str == '~')
|
||||
{
|
||||
strcpy((char *) op->o_opt, home); /* NOSTRICT */
|
||||
start = (char *) op->o_opt + strlen(home);/* NOSTRICT */
|
||||
while (*++str == '/')
|
||||
continue;
|
||||
}
|
||||
else
|
||||
start = (char *) op->o_opt; /* NOSTRICT */
|
||||
/*
|
||||
* Skip to end of string value
|
||||
*/
|
||||
for (sp = str + 1; *sp && *sp != ','; sp++)
|
||||
continue;
|
||||
/*
|
||||
* check for type of inventory
|
||||
*/
|
||||
if (op->o_putfunc == put_inv_t)
|
||||
{
|
||||
if (islower(*str))
|
||||
*str = (char) toupper(*str);
|
||||
for (i = inv_t_name; i <= &inv_t_name[INV_CLEAR]; i++)
|
||||
if (strncmp(str, *i, sp - str) == 0)
|
||||
{
|
||||
inv_type = (int)(i - inv_t_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
strucpy(start, str, (int)(sp - str));
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* check for "noname" for booleans
|
||||
*/
|
||||
else if (op->o_putfunc == put_bool
|
||||
&& EQSTR(str, "no", 2) && EQSTR(str + 2, op->o_name, len - 2))
|
||||
{
|
||||
*(bool *)op->o_opt = FALSE; /* NOSTRICT */
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* skip to start of next option name
|
||||
*/
|
||||
while (*sp && !isalpha(*sp))
|
||||
sp++;
|
||||
str = sp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* strucpy:
|
||||
* Copy string using unctrl for things
|
||||
*/
|
||||
|
||||
void
|
||||
strucpy(char *s1, char *s2, int len)
|
||||
{
|
||||
if (len > MAXINP)
|
||||
len = MAXINP;
|
||||
while (len--)
|
||||
{
|
||||
if (isprint(*s2) || *s2 == ' ')
|
||||
*s1++ = *s2;
|
||||
s2++;
|
||||
}
|
||||
*s1 = '\0';
|
||||
}
|
||||
@@ -1,543 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Routines to deal with the pack
|
||||
*
|
||||
* @(#)pack.c 4.40 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <string.h>
|
||||
//#include <curses.h>
|
||||
//#include <ctype.h>
|
||||
#include "rogue.h"
|
||||
|
||||
/*
|
||||
* add_pack:
|
||||
* Pick up an object and add it to the pack. If the argument is
|
||||
* non-null use it as the linked_list pointer instead of gettting
|
||||
* it off the ground.
|
||||
*/
|
||||
|
||||
void
|
||||
add_pack(struct rogue_state *rs,THING *obj, bool silent)
|
||||
{
|
||||
THING *op, *lp;
|
||||
bool from_floor;
|
||||
|
||||
from_floor = FALSE;
|
||||
if (obj == NULL)
|
||||
{
|
||||
if ((obj = find_obj(rs,hero.y, hero.x)) == NULL)
|
||||
return;
|
||||
from_floor = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for and deal with scare monster scrolls
|
||||
*/
|
||||
if (obj->o_type == SCROLL && obj->o_which == S_SCARE)
|
||||
if (obj->o_flags & ISFOUND)
|
||||
{
|
||||
detach(lvl_obj, obj);
|
||||
mvaddch(hero.y, hero.x, floor_ch());
|
||||
chat(hero.y, hero.x) = (proom->r_flags & ISGONE) ? PASSAGE : FLOOR;
|
||||
discard(obj);
|
||||
msg(rs,"the scroll turns to dust as you pick it up");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pack == NULL)
|
||||
{
|
||||
pack = obj;
|
||||
obj->o_packch = pack_char();
|
||||
inpack++;
|
||||
}
|
||||
else
|
||||
{
|
||||
lp = NULL;
|
||||
for (op = pack; op != NULL; op = next(op))
|
||||
{
|
||||
if (op->o_type != obj->o_type)
|
||||
lp = op;
|
||||
else
|
||||
{
|
||||
while (op->o_type == obj->o_type && op->o_which != obj->o_which)
|
||||
{
|
||||
lp = op;
|
||||
if (next(op) == NULL)
|
||||
break;
|
||||
else
|
||||
op = next(op);
|
||||
}
|
||||
if (op->o_type == obj->o_type && op->o_which == obj->o_which)
|
||||
{
|
||||
if (ISMULT(op->o_type))
|
||||
{
|
||||
if (!pack_room(rs,from_floor, obj))
|
||||
return;
|
||||
op->o_count++;
|
||||
dump_it:
|
||||
discard(obj);
|
||||
obj = op;
|
||||
lp = NULL;
|
||||
goto out;
|
||||
}
|
||||
else if (obj->o_group)
|
||||
{
|
||||
lp = op;
|
||||
while (op->o_type == obj->o_type
|
||||
&& op->o_which == obj->o_which
|
||||
&& op->o_group != obj->o_group)
|
||||
{
|
||||
lp = op;
|
||||
if (next(op) == NULL)
|
||||
break;
|
||||
else
|
||||
op = next(op);
|
||||
}
|
||||
if (op->o_type == obj->o_type
|
||||
&& op->o_which == obj->o_which
|
||||
&& op->o_group == obj->o_group)
|
||||
{
|
||||
op->o_count += obj->o_count;
|
||||
inpack--;
|
||||
if (!pack_room(rs,from_floor, obj))
|
||||
return;
|
||||
goto dump_it;
|
||||
}
|
||||
}
|
||||
else
|
||||
lp = op;
|
||||
}
|
||||
out:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lp != NULL)
|
||||
{
|
||||
if (!pack_room(rs,from_floor, obj))
|
||||
return;
|
||||
else
|
||||
{
|
||||
obj->o_packch = pack_char();
|
||||
next(obj) = next(lp);
|
||||
prev(obj) = lp;
|
||||
if (next(lp) != NULL)
|
||||
prev(next(lp)) = obj;
|
||||
next(lp) = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj->o_flags |= ISFOUND;
|
||||
|
||||
/*
|
||||
* If this was the object of something's desire, that monster will
|
||||
* get mad and run at the hero.
|
||||
*/
|
||||
for (op = mlist; op != NULL; op = next(op))
|
||||
if (op->t_dest == &obj->o_pos)
|
||||
op->t_dest = &hero;
|
||||
|
||||
if (obj->o_type == AMULET)
|
||||
amulet = TRUE;
|
||||
/*
|
||||
* Notify the user
|
||||
*/
|
||||
if (!silent)
|
||||
{
|
||||
if (!terse)
|
||||
addmsg(rs,"you now have ");
|
||||
msg(rs,"%s (%c)", inv_name(obj, !terse), obj->o_packch);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t num_packitems(struct rogue_state *rs)
|
||||
{
|
||||
THING *list = pack;
|
||||
int32_t type = 0,n = 0,total = 0;
|
||||
for (; list != NULL; list = next(list))
|
||||
{
|
||||
if ( thing_find(list) < 0 )
|
||||
{
|
||||
fprintf(stderr,"num_packitems cant find %p\n",list);
|
||||
return(-1);
|
||||
}
|
||||
if ( list->o_packch != 0 )
|
||||
{
|
||||
n++;
|
||||
total += rogue_total(list);
|
||||
}
|
||||
}
|
||||
if ( rs->guiflag != 0 )
|
||||
{
|
||||
char str[MAXSTR];
|
||||
sprintf(str,"strength*2 %d vs total.%d vs %d inventory letters\n",ROGUE_MAXTOTAL,total,n);
|
||||
add_line(rs,"%s",str);
|
||||
}
|
||||
if ( total > ROGUE_MAXTOTAL )
|
||||
return(MAXPACK);
|
||||
return(n);
|
||||
}
|
||||
|
||||
/*
|
||||
* pack_room:
|
||||
* See if there's room in the pack. If not, print out an
|
||||
* appropriate message
|
||||
*/
|
||||
bool pack_room(struct rogue_state *rs,bool from_floor, THING *obj)
|
||||
{
|
||||
inpack = num_packitems(rs);
|
||||
if ( ++inpack > MAXPACK )
|
||||
{
|
||||
if (!terse)
|
||||
addmsg(rs,"there's ");
|
||||
addmsg(rs,"no room");
|
||||
if (!terse)
|
||||
addmsg(rs," in your pack");
|
||||
endmsg(rs);
|
||||
if (from_floor)
|
||||
move_msg(rs,obj);
|
||||
inpack = MAXPACK;
|
||||
return FALSE;
|
||||
}
|
||||
//fprintf(stderr,"inpack.%d vs MAX.%d\n",inpack,MAXPACK), sleep(2);
|
||||
if ( from_floor != 0 )
|
||||
{
|
||||
detach(lvl_obj, obj);
|
||||
mvaddch(hero.y, hero.x, floor_ch());
|
||||
chat(hero.y, hero.x) = (proom->r_flags & ISGONE) ? PASSAGE : FLOOR;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* leave_pack:
|
||||
* take an item out of the pack
|
||||
*/
|
||||
THING *
|
||||
leave_pack(struct rogue_state *rs,THING *obj, bool newobj, bool all)
|
||||
{
|
||||
THING *nobj;
|
||||
|
||||
inpack--;
|
||||
nobj = obj;
|
||||
if (obj->o_count > 1 && !all)
|
||||
{
|
||||
last_pick = obj;
|
||||
obj->o_count--;
|
||||
if (obj->o_group)
|
||||
inpack++;
|
||||
if (newobj)
|
||||
{
|
||||
nobj = new_item();
|
||||
*nobj = *obj;
|
||||
next(nobj) = NULL;
|
||||
prev(nobj) = NULL;
|
||||
nobj->o_count = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
last_pick = NULL;
|
||||
pack_used[obj->o_packch - 'a'] = FALSE;
|
||||
detach(pack, obj);
|
||||
}
|
||||
return nobj;
|
||||
}
|
||||
|
||||
/*
|
||||
* pack_char:
|
||||
* Return the next unused pack character.
|
||||
*/
|
||||
char
|
||||
pack_char()
|
||||
{
|
||||
bool *bp;
|
||||
|
||||
for (bp = pack_used; *bp; bp++)
|
||||
continue;
|
||||
*bp = TRUE;
|
||||
return (char)((int)(bp - pack_used) + 'a');
|
||||
}
|
||||
|
||||
/*
|
||||
* inventory:
|
||||
* List what is in the pack. Return TRUE if there is something of
|
||||
* the given type.
|
||||
*/
|
||||
|
||||
|
||||
bool
|
||||
inventory(struct rogue_state *rs,THING *list, int type)
|
||||
{
|
||||
char inv_temp[MAXSTR];
|
||||
|
||||
n_objs = 0;
|
||||
for (; list != NULL; list = next(list))
|
||||
{
|
||||
if (type && type != list->o_type && !(type == CALLABLE &&
|
||||
list->o_type != FOOD && list->o_type != AMULET) &&
|
||||
!(type == R_OR_S && (list->o_type == RING || list->o_type == STICK)))
|
||||
continue;
|
||||
n_objs++;
|
||||
#ifdef MASTER
|
||||
if (!list->o_packch)
|
||||
strcpy(inv_temp, "%s");
|
||||
else
|
||||
#endif
|
||||
sprintf(inv_temp, "%c) %%s", list->o_packch);
|
||||
msg_esc = TRUE;
|
||||
if (add_line(rs,inv_temp, inv_name(list, FALSE)) == ESCAPE)
|
||||
{
|
||||
msg_esc = FALSE;
|
||||
msg(rs,"");
|
||||
return TRUE;
|
||||
}
|
||||
msg_esc = FALSE;
|
||||
}
|
||||
//if ( n_objs != inpack )
|
||||
// fprintf(stderr,"n_objs.%d vs inpack.%d\n",n_objs,inpack), sleep(2);
|
||||
if (n_objs == 0)
|
||||
{
|
||||
if (terse)
|
||||
msg(rs,type == 0 ? (char *)"empty handed" : (char *)"nothing appropriate");
|
||||
else
|
||||
msg(rs,type == 0 ? (char *)"you are empty handed" : (char *)"you don't have anything appropriate");
|
||||
return FALSE;
|
||||
}
|
||||
end_line(rs);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* pick_up:
|
||||
* Add something to characters pack.
|
||||
*/
|
||||
|
||||
void
|
||||
pick_up(struct rogue_state *rs,char ch)
|
||||
{
|
||||
THING *obj;
|
||||
|
||||
if (on(player, ISLEVIT))
|
||||
return;
|
||||
|
||||
obj = find_obj(rs,hero.y, hero.x);
|
||||
if (move_on)
|
||||
move_msg(rs,obj);
|
||||
else
|
||||
switch (ch)
|
||||
{
|
||||
case GOLD:
|
||||
if (obj == NULL)
|
||||
return;
|
||||
money(rs,obj->o_goldval);
|
||||
detach(lvl_obj, obj);
|
||||
discard(obj);
|
||||
proom->r_goldval = 0;
|
||||
break;
|
||||
default:
|
||||
#ifdef MASTER
|
||||
debug("Where did you pick a '%s' up???", unctrl(ch));
|
||||
#endif
|
||||
case ARMOR:
|
||||
case POTION:
|
||||
case FOOD:
|
||||
case WEAPON:
|
||||
case SCROLL:
|
||||
case AMULET:
|
||||
case RING:
|
||||
case STICK:
|
||||
add_pack(rs,(THING *) NULL, FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* move_msg:
|
||||
* Print out the message if you are just moving onto an object
|
||||
*/
|
||||
|
||||
void
|
||||
move_msg(struct rogue_state *rs,THING *obj)
|
||||
{
|
||||
if (!terse)
|
||||
addmsg(rs,"you ");
|
||||
msg(rs,"moved onto %s", inv_name(obj, TRUE));
|
||||
}
|
||||
|
||||
/*
|
||||
* picky_inven:
|
||||
* Allow player to inventory a single item
|
||||
*/
|
||||
|
||||
void
|
||||
picky_inven(struct rogue_state *rs)
|
||||
{
|
||||
THING *obj;
|
||||
char mch;
|
||||
|
||||
if (pack == NULL)
|
||||
msg(rs,"you aren't carrying anything");
|
||||
else if (next(pack) == NULL)
|
||||
msg(rs,"a) %s", inv_name(pack, FALSE));
|
||||
else
|
||||
{
|
||||
msg(rs,terse ? (char *)"item: " : (char *)"which item do you wish to inventory: ");
|
||||
mpos = 0;
|
||||
if ((mch = readchar(rs)) == ESCAPE)
|
||||
{
|
||||
msg(rs,"");
|
||||
return;
|
||||
}
|
||||
for (obj = pack; obj != NULL; obj = next(obj))
|
||||
if (mch == obj->o_packch)
|
||||
{
|
||||
msg(rs,"%c) %s", mch, inv_name(obj, FALSE));
|
||||
return;
|
||||
}
|
||||
msg(rs,"'%s' not in pack", unctrl(mch));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* get_item:
|
||||
* Pick something out of a pack for a purpose
|
||||
*/
|
||||
THING *
|
||||
get_item(struct rogue_state *rs,char *purpose, int type)
|
||||
{
|
||||
THING *obj;
|
||||
char ch;
|
||||
|
||||
if (pack == NULL)
|
||||
msg(rs,"you aren't carrying anything");
|
||||
else if (again)
|
||||
if (last_pick)
|
||||
return last_pick;
|
||||
else
|
||||
msg(rs,"you ran out");
|
||||
else
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if ( rs->replaydone != 0 )
|
||||
return(NULL);
|
||||
if (!terse)
|
||||
addmsg(rs,"which object do you want to ");
|
||||
addmsg(rs,purpose);
|
||||
if (terse)
|
||||
addmsg(rs," what");
|
||||
msg(rs,"? (* for list): ");
|
||||
ch = readchar(rs);
|
||||
mpos = 0;
|
||||
/*
|
||||
* Give the poor player a chance to abort the command
|
||||
*/
|
||||
if (ch == ESCAPE)
|
||||
{
|
||||
reset_last();
|
||||
after = FALSE;
|
||||
msg(rs,"");
|
||||
return NULL;
|
||||
}
|
||||
n_objs = 1; /* normal case: person types one char */
|
||||
if (ch == '*')
|
||||
{
|
||||
mpos = 0;
|
||||
if (inventory(rs,pack, type) == 0)
|
||||
{
|
||||
after = FALSE;
|
||||
return NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (obj = pack; obj != NULL; obj = next(obj))
|
||||
if (obj->o_packch == ch)
|
||||
break;
|
||||
if (obj == NULL)
|
||||
{
|
||||
//msg(rs,"'%s' is not a valid item",unctrl(ch));
|
||||
//continue;
|
||||
reset_last();
|
||||
after = FALSE;
|
||||
msg(rs,"'%s' is not a valid item",unctrl(ch));
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* money:
|
||||
* Add or subtract gold from the pack
|
||||
*/
|
||||
|
||||
void
|
||||
money(struct rogue_state *rs,int value)
|
||||
{
|
||||
purse += value;
|
||||
mvaddch(hero.y, hero.x, floor_ch());
|
||||
chat(hero.y, hero.x) = (proom->r_flags & ISGONE) ? PASSAGE : FLOOR;
|
||||
if (value > 0)
|
||||
{
|
||||
if (!terse)
|
||||
addmsg(rs,"you found ");
|
||||
msg(rs,"%d gold pieces", value);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* floor_ch:
|
||||
* Return the appropriate floor character for her room
|
||||
*/
|
||||
char
|
||||
floor_ch()
|
||||
{
|
||||
if (proom->r_flags & ISGONE)
|
||||
return PASSAGE;
|
||||
return (show_floor() ? FLOOR : ' ');
|
||||
}
|
||||
|
||||
/*
|
||||
* floor_at:
|
||||
* Return the character at hero's position, taking see_floor
|
||||
* into account
|
||||
*/
|
||||
char
|
||||
floor_at()
|
||||
{
|
||||
char ch;
|
||||
|
||||
ch = chat(hero.y, hero.x);
|
||||
if (ch == FLOOR)
|
||||
ch = floor_ch();
|
||||
return ch;
|
||||
}
|
||||
|
||||
/*
|
||||
* reset_last:
|
||||
* Reset the last command when the current one is aborted
|
||||
*/
|
||||
|
||||
void
|
||||
reset_last()
|
||||
{
|
||||
last_comm = l_last_comm;
|
||||
last_dir = l_last_dir;
|
||||
last_pick = l_last_pick;
|
||||
}
|
||||
@@ -1,427 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Draw the connecting passages
|
||||
*
|
||||
* @(#)passages.c 4.22 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <curses.h>
|
||||
#include "rogue.h"
|
||||
|
||||
/*
|
||||
* do_passages:
|
||||
* Draw all the passages on a level.
|
||||
*/
|
||||
|
||||
void
|
||||
do_passages(struct rogue_state *rs)
|
||||
{
|
||||
struct rdes *r1, *r2 = NULL;
|
||||
int i, j;
|
||||
int roomcount;
|
||||
static struct rdes
|
||||
{
|
||||
bool conn[MAXROOMS]; /* possible to connect to room i? */
|
||||
bool isconn[MAXROOMS]; /* connection been made to room i? */
|
||||
bool ingraph; /* this room in graph already? */
|
||||
} rdes[MAXROOMS] = {
|
||||
{ { 0, 1, 0, 1, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 },
|
||||
{ { 1, 0, 1, 0, 1, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 },
|
||||
{ { 0, 1, 0, 0, 0, 1, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 },
|
||||
{ { 1, 0, 0, 0, 1, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 },
|
||||
{ { 0, 1, 0, 1, 0, 1, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 },
|
||||
{ { 0, 0, 1, 0, 1, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 },
|
||||
{ { 0, 0, 0, 1, 0, 0, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 },
|
||||
{ { 0, 0, 0, 0, 1, 0, 1, 0, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 },
|
||||
{ { 0, 0, 0, 0, 0, 1, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 },
|
||||
};
|
||||
|
||||
/*
|
||||
* reinitialize room graph description
|
||||
*/
|
||||
for (r1 = rdes; r1 <= &rdes[MAXROOMS-1]; r1++)
|
||||
{
|
||||
for (j = 0; j < MAXROOMS; j++)
|
||||
r1->isconn[j] = FALSE;
|
||||
r1->ingraph = FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* starting with one room, connect it to a random adjacent room and
|
||||
* then pick a new room to start with.
|
||||
*/
|
||||
roomcount = 1;
|
||||
r1 = &rdes[rnd(MAXROOMS)];
|
||||
r1->ingraph = TRUE;
|
||||
do
|
||||
{
|
||||
/*
|
||||
* find a room to connect with
|
||||
*/
|
||||
j = 0;
|
||||
for (i = 0; i < MAXROOMS; i++)
|
||||
if (r1->conn[i] && !rdes[i].ingraph && rnd(++j) == 0)
|
||||
r2 = &rdes[i];
|
||||
/*
|
||||
* if no adjacent rooms are outside the graph, pick a new room
|
||||
* to look from
|
||||
*/
|
||||
if (j == 0)
|
||||
{
|
||||
do
|
||||
r1 = &rdes[rnd(MAXROOMS)];
|
||||
until (r1->ingraph);
|
||||
}
|
||||
/*
|
||||
* otherwise, connect new room to the graph, and draw a tunnel
|
||||
* to it
|
||||
*/
|
||||
else
|
||||
{
|
||||
r2->ingraph = TRUE;
|
||||
i = (int)(r1 - rdes);
|
||||
j = (int)(r2 - rdes);
|
||||
conn(rs,i, j);
|
||||
r1->isconn[j] = TRUE;
|
||||
r2->isconn[i] = TRUE;
|
||||
roomcount++;
|
||||
}
|
||||
} while (roomcount < MAXROOMS);
|
||||
|
||||
/*
|
||||
* attempt to add passages to the graph a random number of times so
|
||||
* that there isn't always just one unique passage through it.
|
||||
*/
|
||||
for (roomcount = rnd(5); roomcount > 0; roomcount--)
|
||||
{
|
||||
r1 = &rdes[rnd(MAXROOMS)]; /* a random room to look from */
|
||||
/*
|
||||
* find an adjacent room not already connected
|
||||
*/
|
||||
j = 0;
|
||||
for (i = 0; i < MAXROOMS; i++)
|
||||
if (r1->conn[i] && !r1->isconn[i] && rnd(++j) == 0)
|
||||
r2 = &rdes[i];
|
||||
/*
|
||||
* if there is one, connect it and look for the next added
|
||||
* passage
|
||||
*/
|
||||
if (j != 0)
|
||||
{
|
||||
i = (int)(r1 - rdes);
|
||||
j = (int)(r2 - rdes);
|
||||
conn(rs,i, j);
|
||||
r1->isconn[j] = TRUE;
|
||||
r2->isconn[i] = TRUE;
|
||||
}
|
||||
}
|
||||
passnum();
|
||||
}
|
||||
|
||||
/*
|
||||
* conn:
|
||||
* Draw a corridor from a room in a certain direction.
|
||||
*/
|
||||
|
||||
void
|
||||
conn(struct rogue_state *rs,int r1, int r2)
|
||||
{
|
||||
struct room *rpf, *rpt = NULL;
|
||||
int rmt;
|
||||
int distance = 0, turn_spot, turn_distance = 0;
|
||||
int rm;
|
||||
char direc;
|
||||
static coord del, curr, turn_delta, spos, epos;
|
||||
|
||||
if (r1 < r2)
|
||||
{
|
||||
rm = r1;
|
||||
if (r1 + 1 == r2)
|
||||
direc = 'r';
|
||||
else
|
||||
direc = 'd';
|
||||
}
|
||||
else
|
||||
{
|
||||
rm = r2;
|
||||
if (r2 + 1 == r1)
|
||||
direc = 'r';
|
||||
else
|
||||
direc = 'd';
|
||||
}
|
||||
rpf = &rooms[rm];
|
||||
/*
|
||||
* Set up the movement variables, in two cases:
|
||||
* first drawing one down.
|
||||
*/
|
||||
if (direc == 'd')
|
||||
{
|
||||
rmt = rm + 3; /* room # of dest */
|
||||
rpt = &rooms[rmt]; /* room pointer of dest */
|
||||
del.x = 0; /* direction of move */
|
||||
del.y = 1;
|
||||
spos.x = rpf->r_pos.x; /* start of move */
|
||||
spos.y = rpf->r_pos.y;
|
||||
epos.x = rpt->r_pos.x; /* end of move */
|
||||
epos.y = rpt->r_pos.y;
|
||||
if (!(rpf->r_flags & ISGONE)) /* if not gone pick door pos */
|
||||
do
|
||||
{
|
||||
spos.x = rpf->r_pos.x + rnd(rpf->r_max.x - 2) + 1;
|
||||
spos.y = rpf->r_pos.y + rpf->r_max.y - 1;
|
||||
} while ((rpf->r_flags&ISMAZE) && !(flat(spos.y, spos.x)&F_PASS));
|
||||
if (!(rpt->r_flags & ISGONE))
|
||||
do
|
||||
{
|
||||
epos.x = rpt->r_pos.x + rnd(rpt->r_max.x - 2) + 1;
|
||||
} while ((rpt->r_flags&ISMAZE) && !(flat(epos.y, epos.x)&F_PASS));
|
||||
distance = abs(spos.y - epos.y) - 1; /* distance to move */
|
||||
turn_delta.y = 0; /* direction to turn */
|
||||
turn_delta.x = (spos.x < epos.x ? 1 : -1);
|
||||
turn_distance = abs(spos.x - epos.x); /* how far to turn */
|
||||
}
|
||||
else if (direc == 'r') /* setup for moving right */
|
||||
{
|
||||
rmt = rm + 1;
|
||||
rpt = &rooms[rmt];
|
||||
del.x = 1;
|
||||
del.y = 0;
|
||||
spos.x = rpf->r_pos.x;
|
||||
spos.y = rpf->r_pos.y;
|
||||
epos.x = rpt->r_pos.x;
|
||||
epos.y = rpt->r_pos.y;
|
||||
if (!(rpf->r_flags & ISGONE))
|
||||
do
|
||||
{
|
||||
spos.x = rpf->r_pos.x + rpf->r_max.x - 1;
|
||||
spos.y = rpf->r_pos.y + rnd(rpf->r_max.y - 2) + 1;
|
||||
} while ((rpf->r_flags&ISMAZE) && !(flat(spos.y, spos.x)&F_PASS));
|
||||
if (!(rpt->r_flags & ISGONE))
|
||||
do
|
||||
{
|
||||
epos.y = rpt->r_pos.y + rnd(rpt->r_max.y - 2) + 1;
|
||||
} while ((rpt->r_flags&ISMAZE) && !(flat(epos.y, epos.x)&F_PASS));
|
||||
distance = abs(spos.x - epos.x) - 1;
|
||||
turn_delta.y = (spos.y < epos.y ? 1 : -1);
|
||||
turn_delta.x = 0;
|
||||
turn_distance = abs(spos.y - epos.y);
|
||||
}
|
||||
#ifdef MASTER
|
||||
else
|
||||
debug("error in connection tables");
|
||||
#endif
|
||||
|
||||
turn_spot = rnd(distance - 1) + 1; /* where turn starts */
|
||||
|
||||
/*
|
||||
* Draw in the doors on either side of the passage or just put #'s
|
||||
* if the rooms are gone.
|
||||
*/
|
||||
if (!(rpf->r_flags & ISGONE))
|
||||
door(rpf, &spos);
|
||||
else
|
||||
putpass(&spos);
|
||||
if (!(rpt->r_flags & ISGONE))
|
||||
door(rpt, &epos);
|
||||
else
|
||||
putpass(&epos);
|
||||
/*
|
||||
* Get ready to move...
|
||||
*/
|
||||
curr.x = spos.x;
|
||||
curr.y = spos.y;
|
||||
while (distance > 0)
|
||||
{
|
||||
/*
|
||||
* Move to new position
|
||||
*/
|
||||
curr.x += del.x;
|
||||
curr.y += del.y;
|
||||
/*
|
||||
* Check if we are at the turn place, if so do the turn
|
||||
*/
|
||||
if (distance == turn_spot)
|
||||
while (turn_distance--)
|
||||
{
|
||||
putpass(&curr);
|
||||
curr.x += turn_delta.x;
|
||||
curr.y += turn_delta.y;
|
||||
}
|
||||
/*
|
||||
* Continue digging along
|
||||
*/
|
||||
putpass(&curr);
|
||||
distance--;
|
||||
}
|
||||
curr.x += del.x;
|
||||
curr.y += del.y;
|
||||
if (!ce(curr, epos))
|
||||
msg(rs,"warning, connectivity problem on this level");
|
||||
}
|
||||
|
||||
/*
|
||||
* putpass:
|
||||
* add a passage character or secret passage here
|
||||
*/
|
||||
|
||||
void
|
||||
putpass(coord *cp)
|
||||
{
|
||||
PLACE *pp;
|
||||
|
||||
pp = INDEX(cp->y, cp->x);
|
||||
pp->p_flags |= F_PASS;
|
||||
if (rnd(10) + 1 < level && rnd(40) == 0)
|
||||
pp->p_flags &= ~F_REAL;
|
||||
else
|
||||
pp->p_ch = PASSAGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* door:
|
||||
* Add a door or possibly a secret door. Also enters the door in
|
||||
* the exits array of the room.
|
||||
*/
|
||||
|
||||
void
|
||||
door(struct room *rm, coord *cp)
|
||||
{
|
||||
PLACE *pp;
|
||||
|
||||
rm->r_exit[rm->r_nexits++] = *cp;
|
||||
|
||||
if (rm->r_flags & ISMAZE)
|
||||
return;
|
||||
|
||||
pp = INDEX(cp->y, cp->x);
|
||||
if (rnd(10) + 1 < level && rnd(5) == 0)
|
||||
{
|
||||
if (cp->y == rm->r_pos.y || cp->y == rm->r_pos.y + rm->r_max.y - 1)
|
||||
pp->p_ch = '-';
|
||||
else
|
||||
pp->p_ch = '|';
|
||||
pp->p_flags &= ~F_REAL;
|
||||
}
|
||||
else
|
||||
pp->p_ch = DOOR;
|
||||
}
|
||||
|
||||
#ifdef MASTER
|
||||
/*
|
||||
* add_pass:
|
||||
* Add the passages to the current window (wizard command)
|
||||
*/
|
||||
|
||||
void
|
||||
add_pass()
|
||||
{
|
||||
PLACE *pp;
|
||||
int y, x;
|
||||
char ch;
|
||||
|
||||
for (y = 1; y < NUMLINES - 1; y++)
|
||||
for (x = 0; x < NUMCOLS; x++)
|
||||
{
|
||||
pp = INDEX(y, x);
|
||||
if ((pp->p_flags & F_PASS) || pp->p_ch == DOOR ||
|
||||
(!(pp->p_flags&F_REAL) && (pp->p_ch == '|' || pp->p_ch == '-')))
|
||||
{
|
||||
ch = pp->p_ch;
|
||||
if (pp->p_flags & F_PASS)
|
||||
ch = PASSAGE;
|
||||
pp->p_flags |= F_SEEN;
|
||||
move(y, x);
|
||||
if (pp->p_monst != NULL)
|
||||
pp->p_monst->t_oldch = pp->p_ch;
|
||||
else if (pp->p_flags & F_REAL)
|
||||
addch(ch);
|
||||
else
|
||||
{
|
||||
standout();
|
||||
addch((pp->p_flags & F_PASS) ? PASSAGE : DOOR);
|
||||
standend();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* passnum:
|
||||
* Assign a number to each passageway
|
||||
*/
|
||||
static int pnum;
|
||||
static bool newpnum;
|
||||
|
||||
|
||||
void
|
||||
passnum()
|
||||
{
|
||||
struct room *rp;
|
||||
int i;
|
||||
|
||||
pnum = 0;
|
||||
newpnum = FALSE;
|
||||
for (rp = passages; rp < &passages[MAXPASS]; rp++)
|
||||
rp->r_nexits = 0;
|
||||
for (rp = rooms; rp < &rooms[MAXROOMS]; rp++)
|
||||
for (i = 0; i < rp->r_nexits; i++)
|
||||
{
|
||||
newpnum ^= 1;//newpnum++;
|
||||
numpass(rp->r_exit[i].y, rp->r_exit[i].x);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* numpass:
|
||||
* Number a passageway square and its brethren
|
||||
*/
|
||||
|
||||
void
|
||||
numpass(int y, int x)
|
||||
{
|
||||
char *fp;
|
||||
struct room *rp;
|
||||
char ch;
|
||||
|
||||
if (x >= NUMCOLS || x < 0 || y >= NUMLINES || y <= 0)
|
||||
return;
|
||||
fp = &flat(y, x);
|
||||
if (*fp & F_PNUM)
|
||||
return;
|
||||
if (newpnum)
|
||||
{
|
||||
pnum++;
|
||||
newpnum = FALSE;
|
||||
}
|
||||
/*
|
||||
* check to see if it is a door or secret door, i.e., a new exit,
|
||||
* or a numerable type of place
|
||||
*/
|
||||
if ((ch = chat(y, x)) == DOOR ||
|
||||
(!(*fp & F_REAL) && (ch == '|' || ch == '-')))
|
||||
{
|
||||
rp = &passages[pnum];
|
||||
rp->r_exit[rp->r_nexits].y = y;
|
||||
rp->r_exit[rp->r_nexits++].x = x;
|
||||
}
|
||||
else if (!(*fp & F_PASS))
|
||||
return;
|
||||
*fp |= pnum;
|
||||
/*
|
||||
* recurse on the surrounding places
|
||||
*/
|
||||
numpass(y + 1, x);
|
||||
numpass(y - 1, x);
|
||||
numpass(y, x + 1);
|
||||
numpass(y, x - 1);
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Function(s) for dealing with potions, mmmmmmmmmm, potionzzzzzzz
|
||||
*
|
||||
* @(#)potions.c 4.46 (Berkeley) 06/07/83
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <curses.h>
|
||||
//#include <ctype.h>
|
||||
#include "rogue.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int pa_flags;
|
||||
void (*pa_daemon)(struct rogue_state *rs,int);
|
||||
int pa_time;
|
||||
char *pa_high, *pa_straight;
|
||||
} PACT;
|
||||
|
||||
static PACT p_actions[] =
|
||||
{
|
||||
{ ISHUH, unconfuse, HUHDURATION, /* P_CONFUSE */
|
||||
"what a tripy feeling!",
|
||||
"wait, what's going on here. Huh? What? Who?" },
|
||||
{ ISHALU, come_down, SEEDURATION, /* P_LSD */
|
||||
"Oh, wow! Everything seems so cosmic!",
|
||||
"Oh, wow! Everything seems so cosmic!" },
|
||||
{ 0, NULL, 0 }, /* P_POISON */
|
||||
{ 0, NULL, 0 }, /* P_STRENGTH */
|
||||
{ CANSEE, unsee, SEEDURATION, /* P_SEEINVIS */
|
||||
prbuf,
|
||||
prbuf },
|
||||
{ 0, NULL, 0 }, /* P_HEALING */
|
||||
{ 0, NULL, 0 }, /* P_MFIND */
|
||||
{ 0, NULL, 0 }, /* P_TFIND */
|
||||
{ 0, NULL, 0 }, /* P_RAISE */
|
||||
{ 0, NULL, 0 }, /* P_XHEAL */
|
||||
{ 0, NULL, 0 }, /* P_HASTE */
|
||||
{ 0, NULL, 0 }, /* P_RESTORE */
|
||||
{ ISBLIND, sight, SEEDURATION, /* P_BLIND */
|
||||
"oh, bummer! Everything is dark! Help!",
|
||||
"a cloak of darkness falls around you" },
|
||||
{ ISLEVIT, land, HEALTIME, /* P_LEVIT */
|
||||
"oh, wow! You're floating in the air!",
|
||||
"you start to float in the air" }
|
||||
};
|
||||
|
||||
/*
|
||||
* quaff:
|
||||
* Quaff a potion from the pack
|
||||
*/
|
||||
|
||||
void quaff(struct rogue_state *rs)
|
||||
{
|
||||
THING *obj, *tp, *mp;
|
||||
bool discardit = FALSE;
|
||||
bool show, trip;
|
||||
|
||||
obj = get_item(rs,"quaff", POTION);
|
||||
/*
|
||||
* Make certain that it is somethings that we want to drink
|
||||
*/
|
||||
if (obj == NULL)
|
||||
return;
|
||||
if (obj->o_type != POTION)
|
||||
{
|
||||
if (!terse)
|
||||
msg(rs,"yuk! Why would you want to drink that?");
|
||||
else
|
||||
msg(rs,"that's undrinkable");
|
||||
return;
|
||||
}
|
||||
if (obj == cur_weapon)
|
||||
cur_weapon = NULL;
|
||||
/*
|
||||
* Calculate the effect it has on the poor guy.
|
||||
*/
|
||||
trip = on(player, ISHALU);
|
||||
discardit = (bool)(obj->o_count == 1);
|
||||
leave_pack(rs,obj, FALSE, FALSE);
|
||||
switch (obj->o_which)
|
||||
{
|
||||
case P_CONFUSE:
|
||||
do_pot(rs,P_CONFUSE, !trip);
|
||||
when P_POISON:
|
||||
pot_info[P_POISON].oi_know = TRUE;
|
||||
if (ISWEARING(R_SUSTSTR))
|
||||
msg(rs,"you feel momentarily sick");
|
||||
else
|
||||
{
|
||||
chg_str(-(rnd(3) + 1));
|
||||
msg(rs,"you feel very sick now");
|
||||
come_down(rs,0);
|
||||
}
|
||||
when P_HEALING:
|
||||
pot_info[P_HEALING].oi_know = TRUE;
|
||||
if ((pstats.s_hpt += roll(pstats.s_lvl, 4)) > max_hp)
|
||||
pstats.s_hpt = ++max_hp;
|
||||
sight(rs,0);
|
||||
msg(rs,"you begin to feel better");
|
||||
when P_STRENGTH:
|
||||
pot_info[P_STRENGTH].oi_know = TRUE;
|
||||
chg_str(1);
|
||||
msg(rs,"you feel stronger, now. What bulging muscles!");
|
||||
when P_MFIND:
|
||||
player.t_flags |= SEEMONST;
|
||||
fuse((void(*)(struct rogue_state *rs,int))turn_see, TRUE, HUHDURATION, AFTER);
|
||||
if (!turn_see(rs,FALSE))
|
||||
msg(rs,"you have a %s feeling for a moment, then it passes",
|
||||
choose_str("normal", "strange"));
|
||||
when P_TFIND:
|
||||
/*
|
||||
* Potion of magic detection. Show the potions and scrolls
|
||||
*/
|
||||
show = FALSE;
|
||||
if (lvl_obj != NULL)
|
||||
{
|
||||
wclear(hw);
|
||||
for (tp = lvl_obj; tp != NULL; tp = next(tp))
|
||||
{
|
||||
if (is_magic(tp))
|
||||
{
|
||||
show = TRUE;
|
||||
wmove(hw, tp->o_pos.y, tp->o_pos.x);
|
||||
waddch(hw, MAGIC);
|
||||
pot_info[P_TFIND].oi_know = TRUE;
|
||||
}
|
||||
}
|
||||
for (mp = mlist; mp != NULL; mp = next(mp))
|
||||
{
|
||||
for (tp = mp->t_pack; tp != NULL; tp = next(tp))
|
||||
{
|
||||
if (is_magic(tp))
|
||||
{
|
||||
show = TRUE;
|
||||
wmove(hw, mp->t_pos.y, mp->t_pos.x);
|
||||
waddch(hw, MAGIC);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (show)
|
||||
{
|
||||
pot_info[P_TFIND].oi_know = TRUE;
|
||||
show_win(rs,"You sense the presence of magic on this level.--More--");
|
||||
}
|
||||
else
|
||||
msg(rs,"you have a %s feeling for a moment, then it passes",
|
||||
choose_str("normal", "strange"));
|
||||
when P_LSD:
|
||||
if (!trip)
|
||||
{
|
||||
if (on(player, SEEMONST))
|
||||
turn_see(rs,FALSE);
|
||||
start_daemon(visuals, 0, BEFORE);
|
||||
seenstairs = seen_stairs();
|
||||
}
|
||||
do_pot(rs,P_LSD, TRUE);
|
||||
when P_SEEINVIS:
|
||||
sprintf(prbuf, "this potion tastes like %s juice", fruit);
|
||||
show = on(player, CANSEE);
|
||||
do_pot(rs,P_SEEINVIS, FALSE);
|
||||
if (!show)
|
||||
invis_on();
|
||||
sight(rs,0);
|
||||
when P_RAISE:
|
||||
pot_info[P_RAISE].oi_know = TRUE;
|
||||
msg(rs,"you suddenly feel much more skillful");
|
||||
raise_level(rs);
|
||||
when P_XHEAL:
|
||||
pot_info[P_XHEAL].oi_know = TRUE;
|
||||
if ((pstats.s_hpt += roll(pstats.s_lvl, 8)) > max_hp)
|
||||
{
|
||||
if (pstats.s_hpt > max_hp + pstats.s_lvl + 1)
|
||||
++max_hp;
|
||||
pstats.s_hpt = ++max_hp;
|
||||
}
|
||||
sight(rs,0);
|
||||
come_down(rs,0);
|
||||
msg(rs,"you begin to feel much better");
|
||||
when P_HASTE:
|
||||
pot_info[P_HASTE].oi_know = TRUE;
|
||||
after = FALSE;
|
||||
if (add_haste(rs,TRUE))
|
||||
msg(rs,"you feel yourself moving much faster");
|
||||
when P_RESTORE:
|
||||
if (ISRING(LEFT, R_ADDSTR))
|
||||
add_str(&pstats.s_str, -cur_ring[LEFT]->o_arm);
|
||||
if (ISRING(RIGHT, R_ADDSTR))
|
||||
add_str(&pstats.s_str, -cur_ring[RIGHT]->o_arm);
|
||||
if (pstats.s_str < max_stats.s_str)
|
||||
pstats.s_str = max_stats.s_str;
|
||||
if (ISRING(LEFT, R_ADDSTR))
|
||||
add_str(&pstats.s_str, cur_ring[LEFT]->o_arm);
|
||||
if (ISRING(RIGHT, R_ADDSTR))
|
||||
add_str(&pstats.s_str, cur_ring[RIGHT]->o_arm);
|
||||
msg(rs,"hey, this tastes great. It make you feel warm all over");
|
||||
when P_BLIND:
|
||||
do_pot(rs,P_BLIND, TRUE);
|
||||
when P_LEVIT:
|
||||
do_pot(rs,P_LEVIT, TRUE);
|
||||
#ifdef MASTER
|
||||
otherwise:
|
||||
msg(rs,"what an odd tasting potion!");
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
status(rs);
|
||||
/*
|
||||
* Throw the item away
|
||||
*/
|
||||
|
||||
call_it(rs,&pot_info[obj->o_which]);
|
||||
|
||||
if (discardit)
|
||||
discard(obj);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_magic:
|
||||
* Returns true if an object radiates magic
|
||||
*/
|
||||
bool
|
||||
is_magic(THING *obj)
|
||||
{
|
||||
switch (obj->o_type)
|
||||
{
|
||||
case ARMOR:
|
||||
return (bool)((obj->o_flags&ISPROT) || obj->o_arm != a_class[obj->o_which]);
|
||||
case WEAPON:
|
||||
return (bool)(obj->o_hplus != 0 || obj->o_dplus != 0);
|
||||
case POTION:
|
||||
case SCROLL:
|
||||
case STICK:
|
||||
case RING:
|
||||
case AMULET:
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* invis_on:
|
||||
* Turn on the ability to see invisible
|
||||
*/
|
||||
|
||||
void
|
||||
invis_on()
|
||||
{
|
||||
THING *mp;
|
||||
|
||||
player.t_flags |= CANSEE;
|
||||
for (mp = mlist; mp != NULL; mp = next(mp))
|
||||
if (on(*mp, ISINVIS) && see_monst(mp) && !on(player, ISHALU))
|
||||
mvaddch(mp->t_pos.y, mp->t_pos.x, mp->t_disguise);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* seen_stairs:
|
||||
* Return TRUE if the player has seen the stairs
|
||||
*/
|
||||
bool
|
||||
seen_stairs()
|
||||
{
|
||||
THING *tp;
|
||||
|
||||
move(stairs.y, stairs.x);
|
||||
if (inch() == STAIRS) /* it's on the map */
|
||||
return TRUE;
|
||||
if (ce(hero, stairs)) /* It's under him */
|
||||
return TRUE;
|
||||
|
||||
/*
|
||||
* if a monster is on the stairs, this gets hairy
|
||||
*/
|
||||
if ((tp = moat(stairs.y, stairs.x)) != NULL)
|
||||
{
|
||||
if (see_monst(tp) && on(*tp, ISRUN)) /* if it's visible and awake */
|
||||
return TRUE; /* it must have moved there */
|
||||
|
||||
if (on(player, SEEMONST) /* if she can detect monster */
|
||||
&& tp->t_oldch == STAIRS) /* and there once were stairs */
|
||||
return TRUE; /* it must have moved there */
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* raise_level:
|
||||
* The guy just magically went up a level.
|
||||
*/
|
||||
|
||||
void
|
||||
raise_level(struct rogue_state *rs)
|
||||
{
|
||||
pstats.s_exp = e_levels[pstats.s_lvl-1] + 1L;
|
||||
check_level(rs);
|
||||
}
|
||||
|
||||
/*
|
||||
* do_pot:
|
||||
* Do a potion with standard setup. This means it uses a fuse and
|
||||
* turns on a flag
|
||||
*/
|
||||
|
||||
void
|
||||
do_pot(struct rogue_state *rs,int type, bool knowit)
|
||||
{
|
||||
PACT *pp;
|
||||
int t;
|
||||
|
||||
pp = &p_actions[type];
|
||||
if (!pot_info[type].oi_know)
|
||||
pot_info[type].oi_know = knowit;
|
||||
t = spread(pp->pa_time);
|
||||
if (!on(player, pp->pa_flags))
|
||||
{
|
||||
player.t_flags |= pp->pa_flags;
|
||||
fuse(pp->pa_daemon, 0, t, AFTER);
|
||||
look(rs,FALSE);
|
||||
}
|
||||
else
|
||||
lengthen(pp->pa_daemon, t);
|
||||
msg(rs,choose_str(pp->pa_high, pp->pa_straight));
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Routines dealing specifically with rings
|
||||
*
|
||||
* @(#)rings.c 4.19 (Berkeley) 05/29/83
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <curses.h>
|
||||
#include "rogue.h"
|
||||
|
||||
/*
|
||||
* ring_on:
|
||||
* Put a ring on a hand
|
||||
*/
|
||||
|
||||
void
|
||||
ring_on(struct rogue_state *rs)
|
||||
{
|
||||
THING *obj;
|
||||
int ring;
|
||||
|
||||
obj = get_item(rs,"put on", RING);
|
||||
/*
|
||||
* Make certain that it is somethings that we want to wear
|
||||
*/
|
||||
if (obj == NULL)
|
||||
return;
|
||||
if (obj->o_type != RING)
|
||||
{
|
||||
if (!terse)
|
||||
msg(rs,"it would be difficult to wrap that around a finger");
|
||||
else
|
||||
msg(rs,"not a ring");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* find out which hand to put it on
|
||||
*/
|
||||
if (is_current(rs,obj))
|
||||
return;
|
||||
|
||||
if (cur_ring[LEFT] == NULL && cur_ring[RIGHT] == NULL)
|
||||
{
|
||||
if ((ring = gethand(rs)) < 0)
|
||||
return;
|
||||
}
|
||||
else if (cur_ring[LEFT] == NULL)
|
||||
ring = LEFT;
|
||||
else if (cur_ring[RIGHT] == NULL)
|
||||
ring = RIGHT;
|
||||
else
|
||||
{
|
||||
if (!terse)
|
||||
msg(rs,"you already have a ring on each hand");
|
||||
else
|
||||
msg(rs,"wearing two");
|
||||
return;
|
||||
}
|
||||
cur_ring[ring] = obj;
|
||||
|
||||
/*
|
||||
* Calculate the effect it has on the poor guy.
|
||||
*/
|
||||
switch (obj->o_which)
|
||||
{
|
||||
case R_ADDSTR:
|
||||
chg_str(obj->o_arm);
|
||||
break;
|
||||
case R_SEEINVIS:
|
||||
invis_on();
|
||||
break;
|
||||
case R_AGGR:
|
||||
aggravate(rs);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!terse)
|
||||
addmsg(rs,"you are now wearing ");
|
||||
msg(rs,"%s (%c)", inv_name(obj, TRUE), obj->o_packch);
|
||||
}
|
||||
|
||||
/*
|
||||
* ring_off:
|
||||
* take off a ring
|
||||
*/
|
||||
|
||||
void
|
||||
ring_off(struct rogue_state *rs)
|
||||
{
|
||||
int ring;
|
||||
THING *obj;
|
||||
|
||||
if (cur_ring[LEFT] == NULL && cur_ring[RIGHT] == NULL)
|
||||
{
|
||||
if (terse)
|
||||
msg(rs,"no rings");
|
||||
else
|
||||
msg(rs,"you aren't wearing any rings");
|
||||
return;
|
||||
}
|
||||
else if (cur_ring[LEFT] == NULL)
|
||||
ring = RIGHT;
|
||||
else if (cur_ring[RIGHT] == NULL)
|
||||
ring = LEFT;
|
||||
else
|
||||
if ((ring = gethand(rs)) < 0)
|
||||
return;
|
||||
mpos = 0;
|
||||
obj = cur_ring[ring];
|
||||
if (obj == NULL)
|
||||
{
|
||||
msg(rs,"not wearing such a ring");
|
||||
return;
|
||||
}
|
||||
if (dropcheck(rs,obj))
|
||||
msg(rs,"was wearing %s(%c)", inv_name(obj, TRUE), obj->o_packch);
|
||||
}
|
||||
|
||||
/*
|
||||
* gethand:
|
||||
* Which hand is the hero interested in?
|
||||
*/
|
||||
int
|
||||
gethand(struct rogue_state *rs)
|
||||
{
|
||||
int c;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if ( rs->replaydone != 0 )
|
||||
return(-1);
|
||||
if (terse)
|
||||
msg(rs,"left or right ring? ");
|
||||
else
|
||||
msg(rs,"left hand or right hand? ");
|
||||
if ((c = readchar(rs)) == ESCAPE)
|
||||
return -1;
|
||||
mpos = 0;
|
||||
if (c == 'l' || c == 'L')
|
||||
return LEFT;
|
||||
else if (c == 'r' || c == 'R')
|
||||
return RIGHT;
|
||||
if (terse)
|
||||
msg(rs,"L or R");
|
||||
else
|
||||
msg(rs,"please type L or R");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ring_eat:
|
||||
* How much food does this ring use up?
|
||||
*/
|
||||
int
|
||||
ring_eat(int hand)
|
||||
{
|
||||
THING *ring;
|
||||
int eat;
|
||||
static int uses[] = {
|
||||
1, /* R_PROTECT */ 1, /* R_ADDSTR */
|
||||
1, /* R_SUSTSTR */ -3, /* R_SEARCH */
|
||||
-5, /* R_SEEINVIS */ 0, /* R_NOP */
|
||||
0, /* R_AGGR */ -3, /* R_ADDHIT */
|
||||
-3, /* R_ADDDAM */ 2, /* R_REGEN */
|
||||
-2, /* R_DIGEST */ 0, /* R_TELEPORT */
|
||||
1, /* R_STEALTH */ 1 /* R_SUSTARM */
|
||||
};
|
||||
|
||||
if ((ring = cur_ring[hand]) == NULL)
|
||||
return 0;
|
||||
if ((eat = uses[ring->o_which]) < 0)
|
||||
eat = (rnd(-eat) == 0);
|
||||
if (ring->o_which == R_DIGEST)
|
||||
eat = -eat;
|
||||
return eat;
|
||||
}
|
||||
|
||||
/*
|
||||
* ring_num:
|
||||
* Print ring bonuses
|
||||
*/
|
||||
char *
|
||||
ring_num(THING *obj)
|
||||
{
|
||||
static char buf[10];
|
||||
|
||||
if (!(obj->o_flags & ISKNOW))
|
||||
return "";
|
||||
switch (obj->o_which)
|
||||
{
|
||||
case R_PROTECT:
|
||||
case R_ADDSTR:
|
||||
case R_ADDDAM:
|
||||
case R_ADDHIT:
|
||||
sprintf(buf, " [%s]", num(obj->o_arm, 0, RING));
|
||||
otherwise:
|
||||
return "";
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
@@ -1,464 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* File for the fun ends
|
||||
* Death or a total win
|
||||
*
|
||||
* @(#)rip.c 4.57 (Berkeley) 02/05/99
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <string.h>
|
||||
//#include <time.h>
|
||||
#include <signal.h>
|
||||
//#include <sys/types.h>
|
||||
//#include <ctype.h>
|
||||
//#include <fcntl.h>
|
||||
//#include <curses.h>
|
||||
#include "rogue.h"
|
||||
#include "score.h"
|
||||
|
||||
static char *rip[] = {
|
||||
" __________\n",
|
||||
" / \\\n",
|
||||
" / REST \\\n",
|
||||
" / IN \\\n",
|
||||
" / PEACE \\\n",
|
||||
" / \\\n",
|
||||
" | |\n",
|
||||
" | |\n",
|
||||
" | killed by a |\n",
|
||||
" | |\n",
|
||||
" | 1980 |\n",
|
||||
" *| * * * | *\n",
|
||||
" ________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______\n",
|
||||
0
|
||||
};
|
||||
|
||||
/*
|
||||
* score:
|
||||
* Figure score and post it.
|
||||
*/
|
||||
/* VARARGS2 */
|
||||
|
||||
void
|
||||
score(struct rogue_state *rs,int amount, int flags, char monst)
|
||||
{
|
||||
SCORE *scp;
|
||||
int i;
|
||||
SCORE *sc2;
|
||||
SCORE *top_ten, *endp;
|
||||
if ( rs->guiflag == 0 )
|
||||
return;
|
||||
# ifdef MASTER
|
||||
int prflags = 0;
|
||||
# endif
|
||||
void (*fp)(int);
|
||||
unsigned int uid;
|
||||
static char *reason[] = {
|
||||
"killed",
|
||||
"quit",
|
||||
"A total winner",
|
||||
"killed with Amulet"
|
||||
};
|
||||
|
||||
start_score();
|
||||
|
||||
if (flags >= 0
|
||||
#ifdef MASTER
|
||||
|| wizard
|
||||
#endif
|
||||
)
|
||||
{
|
||||
mvaddstr(LINES - 1, 0 , "[Press return to continue]");
|
||||
refresh();
|
||||
wgetnstr(stdscr,prbuf,80);
|
||||
endwin();
|
||||
printf("\n");
|
||||
resetltchars();
|
||||
/*
|
||||
* free up space to "guarantee" there is space for the top_ten
|
||||
*/
|
||||
delwin(stdscr);
|
||||
delwin(curscr);
|
||||
if (hw != NULL)
|
||||
delwin(hw);
|
||||
hw = NULL;
|
||||
}
|
||||
|
||||
top_ten = (SCORE *) malloc(numscores * sizeof (SCORE));
|
||||
endp = &top_ten[numscores];
|
||||
for (scp = top_ten; scp < endp; scp++)
|
||||
{
|
||||
scp->sc_score = 0;
|
||||
for (i = 0; i < MAXSTR; i++)
|
||||
scp->sc_name[i] = (unsigned char) rnd(255);
|
||||
scp->sc_flags = RN;
|
||||
scp->sc_level = RN;
|
||||
scp->sc_monster = (unsigned short) RN;
|
||||
scp->sc_uid = RN;
|
||||
}
|
||||
|
||||
signal(SIGINT, SIG_DFL);
|
||||
|
||||
#ifdef MASTER
|
||||
if (wizard)
|
||||
if (strcmp(prbuf, "names") == 0)
|
||||
prflags = 1;
|
||||
else if (strcmp(prbuf, "edit") == 0)
|
||||
prflags = 2;
|
||||
#endif
|
||||
rd_score(top_ten);
|
||||
/*
|
||||
* Insert her in list if need be
|
||||
*/
|
||||
sc2 = NULL;
|
||||
if (!noscore)
|
||||
{
|
||||
uid = md_getuid();
|
||||
for (scp = top_ten; scp < endp; scp++)
|
||||
if (amount > scp->sc_score)
|
||||
break;
|
||||
else if (!allscore && /* only one score per nowin uid */
|
||||
flags != 2 && scp->sc_uid == uid && scp->sc_flags != 2)
|
||||
scp = endp;
|
||||
if (scp < endp)
|
||||
{
|
||||
if (flags != 2 && !allscore)
|
||||
{
|
||||
for (sc2 = scp; sc2 < endp; sc2++)
|
||||
{
|
||||
if (sc2->sc_uid == uid && sc2->sc_flags != 2)
|
||||
break;
|
||||
}
|
||||
if (sc2 >= endp)
|
||||
sc2 = endp - 1;
|
||||
}
|
||||
else
|
||||
sc2 = endp - 1;
|
||||
while (sc2 > scp)
|
||||
{
|
||||
*sc2 = sc2[-1];
|
||||
sc2--;
|
||||
}
|
||||
scp->sc_score = amount;
|
||||
strncpy(scp->sc_name, whoami, MAXSTR);
|
||||
scp->sc_flags = flags;
|
||||
if (flags == 2)
|
||||
scp->sc_level = max_level;
|
||||
else
|
||||
scp->sc_level = level;
|
||||
scp->sc_monster = monst;
|
||||
scp->sc_uid = uid;
|
||||
sc2 = scp;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Print the list
|
||||
*/
|
||||
if (flags != -1)
|
||||
putchar('\n');
|
||||
printf("Top %s %s:\n", Numname, allscore ? "Scores" : "Rogueists");
|
||||
printf(" Score Name\n");
|
||||
for (scp = top_ten; scp < endp; scp++)
|
||||
{
|
||||
if (scp->sc_score) {
|
||||
if (sc2 == scp)
|
||||
md_raw_standout();
|
||||
printf("%2d %5d %s: %s on level %d", (int) (scp - top_ten + 1),
|
||||
scp->sc_score, scp->sc_name, reason[scp->sc_flags],
|
||||
scp->sc_level);
|
||||
if (scp->sc_flags == 0 || scp->sc_flags == 3)
|
||||
printf(" by %s", killname((char) scp->sc_monster, TRUE));
|
||||
#ifdef MASTER
|
||||
if (prflags == 1)
|
||||
{
|
||||
printf(" (%s)", md_getrealname(scp->sc_uid));
|
||||
}
|
||||
else if (prflags == 2)
|
||||
{
|
||||
fflush(stdout);
|
||||
if ( fgets(prbuf,10,stdin) != 0 )
|
||||
fprintf(stderr,"fgets error\n");
|
||||
if (prbuf[0] == 'd')
|
||||
{
|
||||
for (sc2 = scp; sc2 < endp - 1; sc2++)
|
||||
*sc2 = *(sc2 + 1);
|
||||
sc2 = endp - 1;
|
||||
sc2->sc_score = 0;
|
||||
for (i = 0; i < MAXSTR; i++)
|
||||
sc2->sc_name[i] = (char) rnd(255);
|
||||
sc2->sc_flags = RN;
|
||||
sc2->sc_level = RN;
|
||||
sc2->sc_monster = (unsigned short) RN;
|
||||
scp--;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /* MASTER */
|
||||
printf(".");
|
||||
if (sc2 == scp)
|
||||
md_raw_standend();
|
||||
putchar('\n');
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Update the list file
|
||||
*/
|
||||
if (sc2 != NULL)
|
||||
{
|
||||
if (lock_sc())
|
||||
{
|
||||
fp = signal(SIGINT, SIG_IGN);
|
||||
wr_score(top_ten);
|
||||
unlock_sc();
|
||||
signal(SIGINT, fp);
|
||||
}
|
||||
}
|
||||
free(top_ten);
|
||||
}
|
||||
|
||||
/*
|
||||
* death:
|
||||
* Do something really fun when he dies
|
||||
*/
|
||||
|
||||
void
|
||||
death(struct rogue_state *rs,char monst)
|
||||
{
|
||||
char **dp, *killer;
|
||||
struct tm *lt;
|
||||
static time_t date;
|
||||
//struct tm *localtime(const time_t *);
|
||||
if ( rs->guiflag == 0 )
|
||||
{
|
||||
//fprintf(stderr,"death during replay by (%c)\n",monst); //sleep(3);
|
||||
rs->replaydone = (uint32_t)time(NULL);
|
||||
return;
|
||||
}
|
||||
signal(SIGINT, SIG_IGN);
|
||||
purse -= purse / 10;
|
||||
signal(SIGINT, leave);
|
||||
clear();
|
||||
killer = killname(monst, FALSE);
|
||||
if (!tombstone)
|
||||
{
|
||||
mvprintw(LINES - 2, 0, "Killed by ");
|
||||
killer = killname(monst, FALSE);
|
||||
if (monst != 's' && monst != 'h')
|
||||
printw("a%s ", vowelstr(killer));
|
||||
printw("%s with %d gold", killer, purse);
|
||||
}
|
||||
else
|
||||
{
|
||||
time(&date);
|
||||
lt = localtime(&date);
|
||||
move(8, 0);
|
||||
dp = rip;
|
||||
while (*dp)
|
||||
addstr(*dp++);
|
||||
mvaddstr(17, center(killer), killer);
|
||||
if (monst == 's' || monst == 'h')
|
||||
mvaddch(16, 32, ' ');
|
||||
else
|
||||
mvaddstr(16, 33, vowelstr(killer));
|
||||
mvaddstr(14, center(whoami), whoami);
|
||||
sprintf(prbuf, "%d Au", purse);
|
||||
move(15, center(prbuf));
|
||||
addstr(prbuf);
|
||||
sprintf(prbuf, "%4d", 1900+lt->tm_year);
|
||||
mvaddstr(18, 26, prbuf);
|
||||
}
|
||||
move(LINES - 1, 0);
|
||||
refresh();
|
||||
score(rs,purse, amulet ? 3 : 0, monst);
|
||||
rogue_bailout(rs);
|
||||
printf("[Press return to continue]");
|
||||
fflush(stdout);
|
||||
if ( fgets(prbuf,10,stdin) != 0 )
|
||||
;
|
||||
my_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* center:
|
||||
* Return the index to center the given string
|
||||
*/
|
||||
int
|
||||
center(char *str)
|
||||
{
|
||||
return 28 - (((int)strlen(str) + 1) / 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* total_winner:
|
||||
* Code for a winner
|
||||
*/
|
||||
|
||||
void
|
||||
total_winner(struct rogue_state *rs)
|
||||
{
|
||||
THING *obj;
|
||||
struct obj_info *op;
|
||||
int worth = 0;
|
||||
int oldpurse;
|
||||
|
||||
clear();
|
||||
standout();
|
||||
addstr(" \n");
|
||||
addstr(" @ @ @ @ @ @@@ @ @ \n");
|
||||
addstr(" @ @ @@ @@ @ @ @ @ \n");
|
||||
addstr(" @ @ @@@ @ @ @ @ @ @@@ @@@@ @@@ @ @@@ @ \n");
|
||||
addstr(" @@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ \n");
|
||||
addstr(" @ @ @ @ @ @ @ @@@@ @ @ @@@@@ @ @ @ \n");
|
||||
addstr(" @ @ @ @ @ @@ @ @ @ @ @ @ @ @ @ @ \n");
|
||||
addstr(" @@@ @@@ @@ @ @ @ @@@@ @@@@ @@@ @@@ @@ @ \n");
|
||||
addstr(" \n");
|
||||
addstr(" Congratulations, you have made it to the light of day! \n");
|
||||
standend();
|
||||
addstr("\nYou have joined the elite ranks of those who have escaped the\n");
|
||||
addstr("Dungeons of Doom alive. You journey home and sell all your loot at\n");
|
||||
addstr("a great profit and are admitted to the Fighters' Guild.\n");
|
||||
mvaddstr(LINES - 1, 0, "--Press space to continue--");
|
||||
refresh();
|
||||
wait_for(rs,' ');
|
||||
clear();
|
||||
mvaddstr(0, 0, " Worth Item\n");
|
||||
oldpurse = purse;
|
||||
for (obj = pack; obj != NULL; obj = next(obj))
|
||||
{
|
||||
switch (obj->o_type)
|
||||
{
|
||||
case FOOD:
|
||||
worth = 2 * obj->o_count;
|
||||
when WEAPON:
|
||||
worth = weap_info[obj->o_which].oi_worth;
|
||||
worth *= 3 * (obj->o_hplus + obj->o_dplus) + obj->o_count;
|
||||
obj->o_flags |= ISKNOW;
|
||||
when ARMOR:
|
||||
worth = arm_info[obj->o_which].oi_worth;
|
||||
worth += (9 - obj->o_arm) * 100;
|
||||
worth += (10 * (a_class[obj->o_which] - obj->o_arm));
|
||||
obj->o_flags |= ISKNOW;
|
||||
when SCROLL:
|
||||
worth = scr_info[obj->o_which].oi_worth;
|
||||
worth *= obj->o_count;
|
||||
op = &scr_info[obj->o_which];
|
||||
if (!op->oi_know)
|
||||
worth /= 2;
|
||||
op->oi_know = TRUE;
|
||||
when POTION:
|
||||
worth = pot_info[obj->o_which].oi_worth;
|
||||
worth *= obj->o_count;
|
||||
op = &pot_info[obj->o_which];
|
||||
if (!op->oi_know)
|
||||
worth /= 2;
|
||||
op->oi_know = TRUE;
|
||||
when RING:
|
||||
op = &ring_info[obj->o_which];
|
||||
worth = op->oi_worth;
|
||||
if (obj->o_which == R_ADDSTR || obj->o_which == R_ADDDAM ||
|
||||
obj->o_which == R_PROTECT || obj->o_which == R_ADDHIT)
|
||||
{
|
||||
if (obj->o_arm > 0)
|
||||
worth += obj->o_arm * 100;
|
||||
else
|
||||
worth = 10;
|
||||
}
|
||||
if (!(obj->o_flags & ISKNOW))
|
||||
worth /= 2;
|
||||
obj->o_flags |= ISKNOW;
|
||||
op->oi_know = TRUE;
|
||||
when STICK:
|
||||
op = &ws_info[obj->o_which];
|
||||
worth = op->oi_worth;
|
||||
worth += 20 * obj->o_charges;
|
||||
if (!(obj->o_flags & ISKNOW))
|
||||
worth /= 2;
|
||||
obj->o_flags |= ISKNOW;
|
||||
op->oi_know = TRUE;
|
||||
when AMULET:
|
||||
worth = 1000;
|
||||
}
|
||||
if (worth < 0)
|
||||
worth = 0;
|
||||
printw("%c) %5d %s\n", obj->o_packch, worth, inv_name(obj, FALSE));
|
||||
purse += worth;
|
||||
}
|
||||
printw(" %5d Gold Pieces ", oldpurse);
|
||||
refresh();
|
||||
score(rs,purse, 2, ' ');
|
||||
my_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* killname:
|
||||
* Convert a code to a monster name
|
||||
*/
|
||||
char *
|
||||
killname(char monst, bool doart)
|
||||
{
|
||||
struct h_list *hp;
|
||||
char *sp;
|
||||
bool article;
|
||||
static struct h_list nlist[] = {
|
||||
{'a', "arrow", TRUE},
|
||||
{'b', "bolt", TRUE},
|
||||
{'d', "dart", TRUE},
|
||||
{'h', "hypothermia", FALSE},
|
||||
{'s', "starvation", FALSE},
|
||||
{'\0'}
|
||||
};
|
||||
|
||||
if (isupper(monst))
|
||||
{
|
||||
sp = monsters[monst-'A'].m_name;
|
||||
article = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
sp = "Wally the Wonder Badger";
|
||||
article = FALSE;
|
||||
for (hp = nlist; hp->h_ch; hp++)
|
||||
if (hp->h_ch == monst)
|
||||
{
|
||||
sp = hp->h_desc;
|
||||
article = hp->h_print;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (doart && article)
|
||||
sprintf(prbuf, "a%s ", vowelstr(sp));
|
||||
else
|
||||
prbuf[0] = '\0';
|
||||
strcat(prbuf, sp);
|
||||
return prbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* death_monst:
|
||||
* Return a monster appropriate for a random death.
|
||||
*/
|
||||
char
|
||||
death_monst()
|
||||
{
|
||||
static char poss[] =
|
||||
{
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
|
||||
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'h', 'd', 's',
|
||||
' ' /* This is provided to generate the "Wally the Wonder Badger"
|
||||
message for killer */
|
||||
};
|
||||
|
||||
return poss[rnd(sizeof poss / sizeof (char))];
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
.\"
|
||||
.\" @(#)rogue.6 6.2 (Berkeley) 5/6/86
|
||||
.\"
|
||||
.\" Rogue: Exploring the Dungeons of Doom
|
||||
.\" Copyright (C) 1980-1983, 1985, 1986 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" See the file LICENSE.TXT for full copyright and licensing information.
|
||||
.\"
|
||||
.TH ROGUE 6 "May 6, 1986"
|
||||
.UC 4
|
||||
.SH NAME
|
||||
rogue \- Exploring The Dungeons of Doom
|
||||
.SH SYNOPSIS
|
||||
.B rogue
|
||||
[
|
||||
.B \-r
|
||||
]
|
||||
[
|
||||
.I save_file
|
||||
]
|
||||
[
|
||||
.B \-s
|
||||
]
|
||||
[
|
||||
.B \-d
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
.I Rogue
|
||||
is a computer fantasy game with a new twist. It is crt oriented and the
|
||||
object of the game is to survive the attacks of various monsters and get
|
||||
a lot of gold, rather than the puzzle solving orientation of most computer
|
||||
fantasy games.
|
||||
.PP
|
||||
To get started you really only need to know two commands. The command
|
||||
.B ?
|
||||
will give you a list of the available commands and the command
|
||||
.B /
|
||||
will identify the things you see on the screen.
|
||||
.PP
|
||||
To win the game (as opposed to merely playing to beat other people's high
|
||||
scores) you must locate the Amulet of Yendor which is somewhere below
|
||||
the 20th level of the dungeon and get it out. Nobody has achieved this
|
||||
yet and if somebody does, they will probably go down in history as a hero
|
||||
among heroes.
|
||||
.PP
|
||||
When the game ends, either by your death, when you quit, or if you (by
|
||||
some miracle) manage to win,
|
||||
.I rogue
|
||||
will give you a list of the top-ten scorers. The scoring is based entirely
|
||||
upon how much gold you get. There is a 10% penalty for getting yourself
|
||||
killed.
|
||||
.PP
|
||||
If
|
||||
.I save_file
|
||||
is specified,
|
||||
rogue will be restored from the specified saved game file.
|
||||
If the
|
||||
.B \-r
|
||||
option is used, the save game file is presumed to be the default.
|
||||
.PP
|
||||
The
|
||||
.B \-s
|
||||
option will print out the list of scores.
|
||||
.PP
|
||||
The
|
||||
.B \-d
|
||||
option will kill you and try to add you to the score file.
|
||||
.PP
|
||||
For more detailed directions, read the document
|
||||
.I "A Guide to the Dungeons of Doom."
|
||||
.SH AUTHORS
|
||||
Michael C. Toy,
|
||||
Kenneth C. R. C. Arnold,
|
||||
Glenn Wichman
|
||||
.SH FILES
|
||||
.DT
|
||||
.ta \w'rogue.scr\ \ \ 'u
|
||||
rogue.scr Score file
|
||||
.br
|
||||
\fB~\fP/rogue.save Default save file
|
||||
.SH SEE ALSO
|
||||
Michael C. Toy
|
||||
and
|
||||
Kenneth C. R. C. Arnold,
|
||||
.I "A guide to the Dungeons of Doom"
|
||||
.SH BUGS
|
||||
.PP
|
||||
Probably infinite
|
||||
(although countably infinite).
|
||||
However,
|
||||
that Ice Monsters sometimes transfix you permanently is
|
||||
.I not
|
||||
a bug.
|
||||
It's a feature.
|
||||
@@ -1,96 +0,0 @@
|
||||
.\"
|
||||
.\" @(#)rogue.6 6.2 (Berkeley) 5/6/86
|
||||
.\"
|
||||
.\" Rogue: Exploring the Dungeons of Doom
|
||||
.\" Copyright (C) 1980-1983, 1985, 1986 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" See the file LICENSE.TXT for full copyright and licensing information.
|
||||
.\"
|
||||
.TH ROGUE 6 "May 6, 1986"
|
||||
.UC 4
|
||||
.SH NAME
|
||||
rogue \- Exploring The Dungeons of Doom
|
||||
.SH SYNOPSIS
|
||||
.B @PROGRAM@
|
||||
[
|
||||
.B \-r
|
||||
]
|
||||
[
|
||||
.I save_file
|
||||
]
|
||||
[
|
||||
.B \-s
|
||||
]
|
||||
[
|
||||
.B \-d
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
.I Rogue
|
||||
is a computer fantasy game with a new twist. It is crt oriented and the
|
||||
object of the game is to survive the attacks of various monsters and get
|
||||
a lot of gold, rather than the puzzle solving orientation of most computer
|
||||
fantasy games.
|
||||
.PP
|
||||
To get started you really only need to know two commands. The command
|
||||
.B ?
|
||||
will give you a list of the available commands and the command
|
||||
.B /
|
||||
will identify the things you see on the screen.
|
||||
.PP
|
||||
To win the game (as opposed to merely playing to beat other people's high
|
||||
scores) you must locate the Amulet of Yendor which is somewhere below
|
||||
the 20th level of the dungeon and get it out. Nobody has achieved this
|
||||
yet and if somebody does, they will probably go down in history as a hero
|
||||
among heroes.
|
||||
.PP
|
||||
When the game ends, either by your death, when you quit, or if you (by
|
||||
some miracle) manage to win,
|
||||
.I rogue
|
||||
will give you a list of the top-ten scorers. The scoring is based entirely
|
||||
upon how much gold you get. There is a 10% penalty for getting yourself
|
||||
killed.
|
||||
.PP
|
||||
If
|
||||
.I save_file
|
||||
is specified,
|
||||
rogue will be restored from the specified saved game file.
|
||||
If the
|
||||
.B \-r
|
||||
option is used, the save game file is presumed to be the default.
|
||||
.PP
|
||||
The
|
||||
.B \-s
|
||||
option will print out the list of scores.
|
||||
.PP
|
||||
The
|
||||
.B \-d
|
||||
option will kill you and try to add you to the score file.
|
||||
.PP
|
||||
For more detailed directions, read the document
|
||||
.I "A Guide to the Dungeons of Doom."
|
||||
.SH AUTHORS
|
||||
Michael C. Toy,
|
||||
Kenneth C. R. C. Arnold,
|
||||
Glenn Wichman
|
||||
.SH FILES
|
||||
.DT
|
||||
.ta \w'@SCOREFILE@\ \ \ 'u
|
||||
@SCOREFILE@ Score file
|
||||
.br
|
||||
\fB~\fP/rogue.save Default save file
|
||||
.SH SEE ALSO
|
||||
Michael C. Toy
|
||||
and
|
||||
Kenneth C. R. C. Arnold,
|
||||
.I "A guide to the Dungeons of Doom"
|
||||
.SH BUGS
|
||||
.PP
|
||||
Probably infinite
|
||||
(although countably infinite).
|
||||
However,
|
||||
that Ice Monsters sometimes transfix you permanently is
|
||||
.I not
|
||||
a bug.
|
||||
It's a feature.
|
||||
@@ -1,776 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*
|
||||
* @(#)main.c 4.22 (Berkeley) 02/05/99
|
||||
*/
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <string.h>
|
||||
#include <signal.h>
|
||||
//#include <unistd.h>
|
||||
//#include <curses.h>
|
||||
|
||||
#include "rogue.h"
|
||||
#ifdef STANDALONE
|
||||
#include "../hush3/src/hush_cJSON.h"
|
||||
#else
|
||||
#include "../../hush_cJSON.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* main:
|
||||
* The main program, of course
|
||||
*/
|
||||
struct rogue_state globalR;
|
||||
char Gametxidstr[67];
|
||||
void garbage_collect();
|
||||
|
||||
void purge_obj_guess(struct obj_info *array,int32_t n)
|
||||
{
|
||||
int32_t i;
|
||||
for (i=0; i<n; i++)
|
||||
if ( array[i].oi_guess != 0 )
|
||||
free(array[i].oi_guess), array[i].oi_guess = 0;
|
||||
}
|
||||
|
||||
void rogueiterate(struct rogue_state *rs)
|
||||
{
|
||||
THING *tp;
|
||||
seed = rs->seed;
|
||||
//clear();
|
||||
purge_obj_guess(things,NUMTHINGS);
|
||||
purge_obj_guess(ring_info,MAXRINGS);
|
||||
purge_obj_guess(pot_info,MAXPOTIONS);
|
||||
purge_obj_guess(arm_info,MAXARMORS);
|
||||
purge_obj_guess(scr_info,MAXSCROLLS);
|
||||
purge_obj_guess(weap_info,MAXWEAPONS + 1);
|
||||
purge_obj_guess(ws_info,MAXSTICKS);
|
||||
free_list(player._t._t_pack);
|
||||
for (tp = mlist; tp != NULL; tp = next(tp))
|
||||
free_list(tp->t_pack);
|
||||
free_list(mlist);
|
||||
free_list(lvl_obj);
|
||||
garbage_collect();
|
||||
|
||||
externs_clear();
|
||||
memset(d_list,0,sizeof(d_list));
|
||||
|
||||
memcpy(passages,origpassages,sizeof(passages));
|
||||
memcpy(monsters,origmonsters,sizeof(monsters));
|
||||
memcpy(things,origthings,sizeof(things));
|
||||
|
||||
memcpy(ring_info,origring_info,sizeof(ring_info));
|
||||
memcpy(pot_info,origpot_info,sizeof(pot_info));
|
||||
memcpy(arm_info,origarm_info,sizeof(arm_info));
|
||||
memcpy(scr_info,origscr_info,sizeof(scr_info));
|
||||
memcpy(weap_info,origweap_info,sizeof(weap_info));
|
||||
memcpy(ws_info,origws_info,sizeof(ws_info));
|
||||
|
||||
initscr(); /* Start up cursor package */
|
||||
init_probs(); /* Set up prob tables for objects */
|
||||
init_player(rs); /* Set up initial player stats */
|
||||
init_names(); /* Set up names of scrolls */
|
||||
init_colors(); /* Set up colors of potions */
|
||||
init_stones(); /* Set up stone settings of rings */
|
||||
init_materials(); /* Set up materials of wands */
|
||||
setup();
|
||||
|
||||
/*
|
||||
* The screen must be at least NUMLINES x NUMCOLS
|
||||
*/
|
||||
if (LINES < NUMLINES || COLS < NUMCOLS)
|
||||
{
|
||||
printf("\nSorry, the screen must be at least %dx%d\n", NUMLINES, NUMCOLS);
|
||||
endwin();
|
||||
my_exit(1);
|
||||
}
|
||||
//fprintf(stderr,"LINES %d, COLS %d\n",LINES,COLS);
|
||||
|
||||
// Set up windows
|
||||
if ( hw == NULL )
|
||||
{
|
||||
hw = newwin(LINES, COLS, 0, 0);
|
||||
}
|
||||
idlok(stdscr, TRUE);
|
||||
idlok(hw, TRUE);
|
||||
#ifdef MASTER
|
||||
noscore = wizard;
|
||||
#endif
|
||||
new_level(rs); // Draw current level
|
||||
// Start up daemons and fuses
|
||||
start_daemon(runners, 0, AFTER);
|
||||
start_daemon(doctor, 0, AFTER);
|
||||
fuse(swander, 0, WANDERTIME, AFTER);
|
||||
start_daemon(stomach, 0, AFTER);
|
||||
if ( rs->restoring != 0 )
|
||||
{
|
||||
restore_player(rs);
|
||||
}
|
||||
playit(rs);
|
||||
}
|
||||
|
||||
int32_t roguefname(char *fname,uint64_t seed,int32_t counter)
|
||||
{
|
||||
sprintf(fname,"rogue.%llu.%d",(long long)seed,counter);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int32_t flushkeystrokes_local(struct rogue_state *rs,int32_t waitflag)
|
||||
{
|
||||
#ifndef BUILD_ROGUE
|
||||
char fname[1024]; FILE *fp; int32_t i,retflag = -1;
|
||||
rs->counter++;
|
||||
roguefname(fname,rs->seed,rs->counter);
|
||||
if ( (fp= fopen(fname,"wb")) != 0 )
|
||||
{
|
||||
if ( fwrite(rs->buffered,1,rs->num,fp) == rs->num )
|
||||
{
|
||||
rs->num = 0;
|
||||
retflag = 0;
|
||||
fclose(fp);
|
||||
/*if ( (fp= fopen("savefile","wb")) != 0 )
|
||||
{
|
||||
save_file(rs,fp,0);
|
||||
if ( 0 && (fp= fopen("savefile","rb")) != 0 )
|
||||
{
|
||||
for (i=0; i<0x150; i++)
|
||||
fprintf(stderr,"%02x",fgetc(fp));
|
||||
fprintf(stderr," first part rnd.%d\n",rnd(1000));
|
||||
fclose(fp);
|
||||
}*/
|
||||
roguefname(fname,rs->seed,rs->counter+1);
|
||||
if ( (fp= fopen(fname,"wb")) != 0 ) // truncate next file
|
||||
fclose(fp);
|
||||
//fprintf(stderr,"savefile <- %s retflag.%d\n",fname,retflag);
|
||||
//}
|
||||
} else fprintf(stderr,"error writing (%s)\n",fname);
|
||||
} else fprintf(stderr,"error creating (%s)\n",fname);
|
||||
return(retflag);
|
||||
#else
|
||||
return(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BUILD_ROGUE
|
||||
// stubs for inside daemon
|
||||
|
||||
int32_t rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num)
|
||||
{
|
||||
}
|
||||
|
||||
int32_t rogue_setplayerdata(struct rogue_state *rs,char *gametxidstr)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t flushkeystrokes(struct rogue_state *rs,int32_t waitflag)
|
||||
{
|
||||
if ( rs->num > 0 )
|
||||
{
|
||||
if ( rogue_progress(rs,waitflag,rs->seed,rs->buffered,rs->num) > 0 )
|
||||
{
|
||||
flushkeystrokes_local(rs,waitflag);
|
||||
memset(rs->buffered,0,sizeof(rs->buffered));
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
void rogue_bailout(struct rogue_state *rs)
|
||||
{
|
||||
char cmd[512];
|
||||
flushkeystrokes(rs,1);
|
||||
//sleep(5);
|
||||
return;
|
||||
/*fprintf(stderr,"bailing out\n");
|
||||
sprintf(cmd,"./hush-cli -ac_name=ROGUE cclib bailout 17 \\\"[%%22%s%%22]\\\" >> bailout.log",Gametxidstr);
|
||||
if ( system(cmd) != 0 )
|
||||
fprintf(stderr,"error issuing (%s)\n",cmd);*/
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#define sleep(x) Sleep(1000*(x))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int32_t rogue_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct rogue_player *player,int32_t sleepmillis)
|
||||
{
|
||||
struct rogue_state *rs; FILE *fp; int32_t i,n;
|
||||
rs = (struct rogue_state *)calloc(1,sizeof(*rs));
|
||||
rs->seed = seed;
|
||||
rs->keystrokes = keystrokes;
|
||||
rs->numkeys = num;
|
||||
rs->sleeptime = sleepmillis * 1000;
|
||||
if ( player != 0 )
|
||||
{
|
||||
rs->P = *player;
|
||||
rs->restoring = 1;
|
||||
//fprintf(stderr,"restore player packsize.%d HP.%d\n",rs->P.packsize,rs->P.hitpoints);
|
||||
if ( rs->P.packsize > MAXPACK )
|
||||
rs->P.packsize = MAXPACK;
|
||||
}
|
||||
globalR = *rs;
|
||||
uint32_t starttime = (uint32_t)time(NULL);
|
||||
rogueiterate(rs);
|
||||
|
||||
/*
|
||||
// keypress after replay
|
||||
printf("[Press return to continue]");
|
||||
fflush(stdout);
|
||||
if (fgets(prbuf, 10, stdin) != 0);
|
||||
*/
|
||||
|
||||
if ( 0 )
|
||||
{
|
||||
fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL) - starttime);
|
||||
sleep(2);
|
||||
|
||||
starttime = (uint32_t)time(NULL);
|
||||
for (i=0; i<10000; i++)
|
||||
{
|
||||
memset(rs,0,sizeof(*rs));
|
||||
rs->seed = seed;
|
||||
rs->keystrokes = keystrokes;
|
||||
rs->numkeys = num;
|
||||
rs->sleeptime = 0;
|
||||
rogueiterate(rs);
|
||||
}
|
||||
fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL)-starttime);
|
||||
sleep(3);
|
||||
}
|
||||
if ( (fp= fopen("checkfile","wb")) != 0 )
|
||||
{
|
||||
save_file(rs,fp,0);
|
||||
//fprintf(stderr,"gold.%d hp.%d strength.%d/%d level.%d exp.%d dungeon.%d data[%d]\n",rs->P.gold,rs->P.hitpoints,rs->P.strength&0xffff,rs->P.strength>>16,rs->P.level,rs->P.experience,rs->P.dungeonlevel,rs->playersize);
|
||||
if ( newdata != 0 && rs->playersize > 0 )
|
||||
memcpy(newdata,rs->playerdata,rs->playersize);
|
||||
}
|
||||
n = rs->playersize;
|
||||
free(rs);
|
||||
return(n);
|
||||
}
|
||||
|
||||
long get_filesize(FILE *fp)
|
||||
{
|
||||
long fsize,fpos = ftell(fp);
|
||||
fseek(fp,0,SEEK_END);
|
||||
fsize = ftell(fp);
|
||||
fseek(fp,fpos,SEEK_SET);
|
||||
return(fsize);
|
||||
}
|
||||
|
||||
char *rogue_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter)
|
||||
{
|
||||
char fname[1024],*keystrokes = 0; FILE *fp; long fsize; int32_t num = 0;
|
||||
*numkeysp = 0;
|
||||
while ( 1 )
|
||||
{
|
||||
roguefname(fname,seed,counter);
|
||||
//printf("check (%s)\n",fname);
|
||||
if ( (fp= fopen(fname,"rb")) == 0 )
|
||||
break;
|
||||
if ( (fsize= get_filesize(fp)) <= 0 )
|
||||
{
|
||||
fclose(fp);
|
||||
//printf("fsize.%ld\n",fsize);
|
||||
break;
|
||||
}
|
||||
if ( (keystrokes= (char *)realloc(keystrokes,num+fsize)) == 0 )
|
||||
{
|
||||
fprintf(stderr,"error reallocating keystrokes\n");
|
||||
fclose(fp);
|
||||
return(0);
|
||||
}
|
||||
if ( fread(&keystrokes[num],1,fsize,fp) != fsize )
|
||||
{
|
||||
fprintf(stderr,"error reading keystrokes from (%s)\n",fname);
|
||||
fclose(fp);
|
||||
free(keystrokes);
|
||||
return(0);
|
||||
}
|
||||
fclose(fp);
|
||||
num += fsize;
|
||||
counter++;
|
||||
//fprintf(stderr,"loaded %ld from (%s) total %d\n",fsize,fname,num);
|
||||
}
|
||||
*numkeysp = num;
|
||||
return(keystrokes);
|
||||
}
|
||||
|
||||
int32_t rogue_replay(uint64_t seed,int32_t sleeptime)
|
||||
{
|
||||
FILE *fp; char fname[1024]; char *keystrokes = 0; long fsize; int32_t i,num=0,counter = 0; struct rogue_state *rs; struct rogue_player P,*player = 0;
|
||||
if ( seed == 0 )
|
||||
seed = 777;
|
||||
keystrokes = rogue_keystrokesload(&num,seed,counter);
|
||||
if ( num > 0 )
|
||||
{
|
||||
sprintf(fname,"rogue.%llu.player",(long long)seed);
|
||||
if ( (fp=fopen(fname,"rb")) != 0 )
|
||||
{
|
||||
if ( fread(&P,1,sizeof(P),fp) > 0 )
|
||||
{
|
||||
//printf("max size player\n");
|
||||
player = &P;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
rogue_replay2(0,seed,keystrokes,num,player,sleeptime);
|
||||
mvaddstr(LINES - 2, 0, (char *)"replay completed");
|
||||
endwin();
|
||||
my_exit(0);
|
||||
}
|
||||
if ( keystrokes != 0 )
|
||||
free(keystrokes);
|
||||
return(num);
|
||||
}
|
||||
|
||||
int rogue(int argc, char **argv, char **envp)
|
||||
{
|
||||
char *env; int lowtime; struct rogue_state *rs = &globalR;
|
||||
memset(rs,0,sizeof(*rs));
|
||||
rs->guiflag = 1;
|
||||
rs->sleeptime = 1; // non-zero to allow refresh()
|
||||
if ( argc == 3 && strlen(argv[2]) == 64 )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
rs->seed = _strtoui64(argv[1], NULL, 10);
|
||||
#else
|
||||
rs->seed = atol(argv[1]); // windows, but not MSVC
|
||||
#endif // _MSC_VER
|
||||
#else
|
||||
rs->seed = atol(argv[1]); // non-windows
|
||||
#endif // _WIN32
|
||||
strcpy(Gametxidstr,argv[2]);
|
||||
fprintf(stderr,"setplayerdata\n");
|
||||
if ( rogue_setplayerdata(rs,Gametxidstr) < 0 )
|
||||
{
|
||||
fprintf(stderr,"invalid gametxid, or already started\n");
|
||||
return(-1);
|
||||
}
|
||||
} else rs->seed = 777;
|
||||
md_init();
|
||||
|
||||
#ifdef MASTER
|
||||
/*
|
||||
* Check to see if he is a wizard
|
||||
*/
|
||||
if (argc >= 2 && argv[1][0] == '\0')
|
||||
if (strcmp(PASSWD, md_crypt(md_getpass("wizard's password: "), "mT")) == 0)
|
||||
{
|
||||
wizard = TRUE;
|
||||
player.t_flags |= SEEMONST;
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* get home and options from environment
|
||||
*/
|
||||
|
||||
strncpy(home, md_gethomedir(), MAXSTR);
|
||||
|
||||
strcpy(file_name, home);
|
||||
strcat(file_name, "rogue.save");
|
||||
|
||||
if ((env = getenv("ROGUEOPTS")) != NULL)
|
||||
parse_opts(env);
|
||||
//if (env == NULL || whoami[0] == '\0')
|
||||
// strucpy(whoami, md_getusername(), (int) strlen(md_getusername()));
|
||||
lowtime = (int) time(NULL);
|
||||
#ifdef MASTER
|
||||
if (wizard && getenv("SEED") != NULL)
|
||||
rs->seed = atoi(getenv("SEED"));
|
||||
else
|
||||
#endif
|
||||
//dnum = lowtime + md_getpid();
|
||||
if ( rs != 0 )
|
||||
seed = rs->seed;
|
||||
else seed = 777;
|
||||
//dnum = (int)seed;
|
||||
|
||||
open_score();
|
||||
|
||||
/*
|
||||
* Drop setuid/setgid after opening the scoreboard file.
|
||||
*/
|
||||
|
||||
md_normaluser();
|
||||
|
||||
/*
|
||||
* check for print-score option
|
||||
*/
|
||||
|
||||
md_normaluser(); /* we drop any setgid/setuid priveldges here */
|
||||
|
||||
if (argc == 2)
|
||||
{
|
||||
if (strcmp(argv[1], "-s") == 0)
|
||||
{
|
||||
noscore = TRUE;
|
||||
score(rs,0, -1, 0);
|
||||
exit(0);
|
||||
}
|
||||
else if (strcmp(argv[1], "-d") == 0)
|
||||
{
|
||||
rs->seed = rnd(100); /* throw away some rnd()s to break patterns */
|
||||
while (--rs->seed)
|
||||
rnd(100);
|
||||
purse = rnd(100) + 1;
|
||||
level = rnd(100) + 1;
|
||||
initscr();
|
||||
getltchars();
|
||||
death(rs,death_monst());
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
init_check(); /* check for legal startup */
|
||||
if (argc == 2)
|
||||
if (!restore(rs,argv[1], envp)) /* Note: restore will never return */
|
||||
my_exit(1);
|
||||
#ifdef MASTER
|
||||
if (wizard)
|
||||
printf("Hello %s, welcome to dungeon #%d", whoami, dnum);
|
||||
else
|
||||
#endif
|
||||
printf("Hello %s, just a moment while I dig the dungeon... seed.%llu", whoami,(long long)rs->seed);
|
||||
fflush(stdout);
|
||||
fprintf(stderr,"rogueiterate\n");
|
||||
rogueiterate(rs);
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* endit:
|
||||
* Exit the program abnormally.
|
||||
*/
|
||||
|
||||
void
|
||||
endit(int sig)
|
||||
{
|
||||
NOOP(sig);
|
||||
fatal("Okay, bye bye!\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* fatal:
|
||||
* Exit the program, printing a message.
|
||||
*/
|
||||
|
||||
void
|
||||
fatal(char *s)
|
||||
{
|
||||
mvaddstr(LINES - 2, 0, s);
|
||||
refresh();
|
||||
endwin();
|
||||
my_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* rnd:
|
||||
* Pick a very random number.
|
||||
*/
|
||||
int
|
||||
rnd(int range)
|
||||
{
|
||||
return range == 0 ? 0 : abs((int) RN) % range;
|
||||
}
|
||||
|
||||
/*
|
||||
* roll:
|
||||
* Roll a number of dice
|
||||
*/
|
||||
int
|
||||
roll(int number, int sides)
|
||||
{
|
||||
int dtotal = 0;
|
||||
|
||||
while (number--)
|
||||
dtotal += rnd(sides)+1;
|
||||
return dtotal;
|
||||
}
|
||||
|
||||
/*
|
||||
* tstp:
|
||||
* Handle stop and start signals
|
||||
*/
|
||||
|
||||
void
|
||||
tstp(int ignored)
|
||||
{
|
||||
int y, x;
|
||||
int oy, ox;
|
||||
|
||||
NOOP(ignored);
|
||||
|
||||
/*
|
||||
* leave nicely
|
||||
*/
|
||||
getyx(curscr, oy, ox);
|
||||
mvcur(0, COLS - 1, LINES - 1, 0);
|
||||
endwin();
|
||||
resetltchars();
|
||||
fflush(stdout);
|
||||
md_tstpsignal();
|
||||
|
||||
/*
|
||||
* start back up again
|
||||
*/
|
||||
md_tstpresume();
|
||||
raw();
|
||||
noecho();
|
||||
keypad(stdscr,1);
|
||||
playltchars();
|
||||
clearok(curscr, TRUE);
|
||||
wrefresh(curscr);
|
||||
getyx(curscr, y, x);
|
||||
mvcur(y, x, oy, ox);
|
||||
fflush(stdout);
|
||||
wmove(curscr,oy,ox);
|
||||
/*#ifndef __APPLE__
|
||||
#ifndef BUILD_ROGUE
|
||||
curscr->_cury = oy;
|
||||
curscr->_curx = ox;
|
||||
#endif
|
||||
#endif*/
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
void usleep(int32_t micros)
|
||||
{
|
||||
if (micros < 1000)
|
||||
Sleep(1);
|
||||
else Sleep(micros / 1000);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* playit:
|
||||
* The main loop of the program. Loop until the game is over,
|
||||
* refreshing things and looking at the proper times.
|
||||
*/
|
||||
|
||||
void
|
||||
playit(struct rogue_state *rs)
|
||||
{
|
||||
char *opts;
|
||||
|
||||
/*
|
||||
* set up defaults for slow terminals
|
||||
*/
|
||||
|
||||
if (baudrate() <= 1200)
|
||||
{
|
||||
terse = TRUE;
|
||||
jump = TRUE;
|
||||
see_floor = FALSE;
|
||||
}
|
||||
|
||||
if (md_hasclreol())
|
||||
inv_type = INV_CLEAR;
|
||||
|
||||
/*
|
||||
* parse environment declaration of options
|
||||
*/
|
||||
if ((opts = getenv("ROGUEOPTS")) != NULL)
|
||||
parse_opts(opts);
|
||||
|
||||
|
||||
oldpos = hero;
|
||||
oldrp = roomin(rs,&hero);
|
||||
while (playing)
|
||||
{
|
||||
command(rs); // Command execution
|
||||
if ( rs->guiflag == 0 )
|
||||
{
|
||||
if ( rs->replaydone != 0 )
|
||||
{
|
||||
if ( 0 && rs->sleeptime != 0 )
|
||||
sleep(3);
|
||||
return;
|
||||
}
|
||||
if ( rs->sleeptime != 0 )
|
||||
usleep(rs->sleeptime);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( rs->needflush != 0 )
|
||||
{
|
||||
if ( flushkeystrokes(rs,0) == 0 )
|
||||
rs->needflush = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( rs->guiflag != 0 )
|
||||
flushkeystrokes(rs,1);
|
||||
endit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int32_t _quit()
|
||||
{
|
||||
struct rogue_state *rs = &globalR;
|
||||
int oy, ox, c;
|
||||
//fprintf(stderr,"inside quit(%d)\n",sig);
|
||||
getyx(curscr, oy, ox);
|
||||
msg(rs,"really quit?");
|
||||
//sleep(1);
|
||||
if ( (c= readchar(rs)) == 'y')
|
||||
{
|
||||
if ( rs->guiflag != 0 )
|
||||
{
|
||||
signal(SIGINT, leave);
|
||||
clear();
|
||||
mvprintw(LINES - 2, 0, "You quit with %d gold pieces", purse);
|
||||
move(LINES - 1, 0);
|
||||
if ( rs->sleeptime != 0 )
|
||||
refresh();
|
||||
score(rs,purse, 1, 0);
|
||||
flushkeystrokes(rs,1);
|
||||
my_exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
//score(rs,purse, 1, 0);
|
||||
//fprintf(stderr,"done! (%c)\n",c);
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
//fprintf(stderr,"'Q' answer (%c)\n",c);
|
||||
move(0, 0);
|
||||
clrtoeol();
|
||||
status(rs);
|
||||
move(oy, ox);
|
||||
if ( rs->sleeptime != 0 )
|
||||
refresh();
|
||||
mpos = 0;
|
||||
count = 0;
|
||||
to_death = FALSE;
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* quit:
|
||||
* Have player make certain, then exit.
|
||||
*/
|
||||
|
||||
void quit(int sig)
|
||||
{
|
||||
struct rogue_state *rs = &globalR;
|
||||
int oy, ox, c;
|
||||
//fprintf(stderr,"inside quit(%d)\n",sig);
|
||||
if ( rs->guiflag != 0 )
|
||||
{
|
||||
NOOP(sig);
|
||||
|
||||
/*
|
||||
* Reset the signal in case we got here via an interrupt
|
||||
*/
|
||||
if (!q_comm)
|
||||
mpos = 0;
|
||||
}
|
||||
_quit();
|
||||
}
|
||||
|
||||
/*
|
||||
* leave:
|
||||
* Leave quickly, but curteously
|
||||
*/
|
||||
|
||||
void
|
||||
leave(int sig)
|
||||
{
|
||||
static char buf[BUFSIZ];
|
||||
|
||||
NOOP(sig);
|
||||
|
||||
setbuf(stdout, buf); /* throw away pending output */
|
||||
|
||||
if (!isendwin())
|
||||
{
|
||||
mvcur(0, COLS - 1, LINES - 1, 0);
|
||||
endwin();
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
my_exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* shell:
|
||||
* Let them escape for a while
|
||||
*/
|
||||
|
||||
void
|
||||
shell(struct rogue_state *rs)
|
||||
{
|
||||
if ( rs != 0 && rs->guiflag != 0 )
|
||||
{
|
||||
/*
|
||||
* Set the terminal back to original mode
|
||||
*/
|
||||
move(LINES-1, 0);
|
||||
refresh();
|
||||
endwin();
|
||||
resetltchars();
|
||||
putchar('\n');
|
||||
in_shell = TRUE;
|
||||
after = FALSE;
|
||||
fflush(stdout);
|
||||
/*
|
||||
* Fork and do a shell
|
||||
*/
|
||||
md_shellescape();
|
||||
|
||||
printf("\n[Press return to continue]");
|
||||
fflush(stdout);
|
||||
noecho();
|
||||
raw();
|
||||
keypad(stdscr,1);
|
||||
playltchars();
|
||||
in_shell = FALSE;
|
||||
wait_for(rs,'\n');
|
||||
clearok(stdscr, TRUE);
|
||||
}
|
||||
else fprintf(stderr,"no shell in the blockchain\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* my_exit:
|
||||
* Leave the process properly
|
||||
*/
|
||||
|
||||
void
|
||||
my_exit(int st)
|
||||
{
|
||||
uint32_t counter;
|
||||
resetltchars();
|
||||
if ( globalR.guiflag != 0 || globalR.sleeptime != 0 )
|
||||
exit(st);
|
||||
else if ( counter++ < 10 )
|
||||
{
|
||||
fprintf(stderr,"would have exit.(%d) sleeptime.%d\n",st,globalR.sleeptime);
|
||||
globalR.replaydone = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
ROGUE(6) ROGUE(6)
|
||||
|
||||
|
||||
|
||||
NAME
|
||||
rogue - Exploring The Dungeons of Doom
|
||||
|
||||
SYNOPSIS
|
||||
rogue [ -r ] [ save_file ] [ -s ] [ -d ]
|
||||
|
||||
DESCRIPTION
|
||||
Rogue is a computer fantasy game with a new twist. It is crt oriented
|
||||
and the object of the game is to survive the attacks of various mon-
|
||||
sters and get a lot of gold, rather than the puzzle solving orientation
|
||||
of most computer fantasy games.
|
||||
|
||||
To get started you really only need to know two commands. The command
|
||||
? will give you a list of the available commands and the command /
|
||||
will identify the things you see on the screen.
|
||||
|
||||
To win the game (as opposed to merely playing to beat other people's
|
||||
high scores) you must locate the Amulet of Yendor which is somewhere
|
||||
below the 20th level of the dungeon and get it out. Nobody has
|
||||
achieved this yet and if somebody does, they will probably go down in
|
||||
history as a hero among heroes.
|
||||
|
||||
When the game ends, either by your death, when you quit, or if you (by
|
||||
some miracle) manage to win, rogue will give you a list of the top-ten
|
||||
scorers. The scoring is based entirely upon how much gold you get.
|
||||
There is a 10% penalty for getting yourself killed.
|
||||
|
||||
If save_file is specified, rogue will be restored from the specified
|
||||
saved game file. If the -r option is used, the save game file is pre-
|
||||
sumed to be the default.
|
||||
|
||||
The -s option will print out the list of scores.
|
||||
|
||||
The -d option will kill you and try to add you to the score file.
|
||||
|
||||
For more detailed directions, read the document A Guide to the Dungeons
|
||||
of Doom.
|
||||
|
||||
AUTHORS
|
||||
Michael C. Toy, Kenneth C. R. C. Arnold, Glenn Wichman
|
||||
|
||||
FILES
|
||||
rogue.scr Score file
|
||||
~/rogue.save Default save file
|
||||
|
||||
SEE ALSO
|
||||
Michael C. Toy and Kenneth C. R. C. Arnold, A guide to the Dungeons of
|
||||
Doom
|
||||
|
||||
BUGS
|
||||
Probably infinite (although countably infinite). However, that Ice
|
||||
Monsters sometimes transfix you permanently is not a bug. It's a fea-
|
||||
ture.
|
||||
|
||||
|
||||
|
||||
4th Berkeley Distribution May 6, 1986 ROGUE(6)
|
||||
@@ -1,61 +0,0 @@
|
||||
ROGUE(6) ROGUE(6)
|
||||
|
||||
|
||||
|
||||
NAME
|
||||
rogue - Exploring The Dungeons of Doom
|
||||
|
||||
SYNOPSIS
|
||||
@PROGRAM@ [ -r ] [ save_file ] [ -s ] [ -d ]
|
||||
|
||||
DESCRIPTION
|
||||
Rogue is a computer fantasy game with a new twist. It is crt oriented
|
||||
and the object of the game is to survive the attacks of various mon-
|
||||
sters and get a lot of gold, rather than the puzzle solving orientation
|
||||
of most computer fantasy games.
|
||||
|
||||
To get started you really only need to know two commands. The command
|
||||
? will give you a list of the available commands and the command /
|
||||
will identify the things you see on the screen.
|
||||
|
||||
To win the game (as opposed to merely playing to beat other people's
|
||||
high scores) you must locate the Amulet of Yendor which is somewhere
|
||||
below the 20th level of the dungeon and get it out. Nobody has
|
||||
achieved this yet and if somebody does, they will probably go down in
|
||||
history as a hero among heroes.
|
||||
|
||||
When the game ends, either by your death, when you quit, or if you (by
|
||||
some miracle) manage to win, rogue will give you a list of the top-ten
|
||||
scorers. The scoring is based entirely upon how much gold you get.
|
||||
There is a 10% penalty for getting yourself killed.
|
||||
|
||||
If save_file is specified, rogue will be restored from the specified
|
||||
saved game file. If the -r option is used, the save game file is pre-
|
||||
sumed to be the default.
|
||||
|
||||
The -s option will print out the list of scores.
|
||||
|
||||
The -d option will kill you and try to add you to the score file.
|
||||
|
||||
For more detailed directions, read the document A Guide to the Dungeons
|
||||
of Doom.
|
||||
|
||||
AUTHORS
|
||||
Michael C. Toy, Kenneth C. R. C. Arnold, Glenn Wichman
|
||||
|
||||
FILES
|
||||
@SCOREFILE@ Score file
|
||||
~/rogue.save Default save file
|
||||
|
||||
SEE ALSO
|
||||
Michael C. Toy and Kenneth C. R. C. Arnold, A guide to the Dungeons of
|
||||
Doom
|
||||
|
||||
BUGS
|
||||
Probably infinite (although countably infinite). However, that Ice
|
||||
Monsters sometimes transfix you permanently is not a bug. It's a fea-
|
||||
ture.
|
||||
|
||||
|
||||
|
||||
4th Berkeley Distribution May 6, 1986 ROGUE(6)
|
||||
@@ -1,11 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Name=Rogue
|
||||
GenericName=Rogue
|
||||
Comment=The original curses-based adventure game
|
||||
Exec=rogue
|
||||
Icon=rogue.png
|
||||
Terminal=true
|
||||
Type=Application
|
||||
Categories=Game;RolePlaying;
|
||||
Version=1.0
|
||||
@@ -1,858 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
Michael C. Toy
|
||||
Kenneth C. R. C. Arnold
|
||||
|
||||
|
||||
Computer Systems Research Group
|
||||
Department of Electrical Engineering and Computer Science
|
||||
University of California
|
||||
Berkeley, California 94720
|
||||
|
||||
|
||||
|
||||
|
||||
ABSTRACT
|
||||
|
||||
Rogue is a visual CRT based fantasy game which runs
|
||||
under the UNIX timesharing system. This paper de-
|
||||
scribes how to play rogue, and gives a few hints for
|
||||
those who might otherwise get lost in the Dungeons
|
||||
of Doom.
|
||||
|
||||
|
||||
|
||||
|
||||
1. Introduction
|
||||
|
||||
You have just finished your years as a student at the
|
||||
local fighter's guild. After much practice and sweat you
|
||||
have finally completed your training and are ready to embark
|
||||
upon a perilous adventure. As a test of your skills, the
|
||||
local guildmasters have sent you into the Dungeons of Doom.
|
||||
Your task is to return with the Amulet of Yendor. Your
|
||||
reward for the completion of this task will be a full mem-
|
||||
bership in the local guild. In addition, you are allowed to
|
||||
keep all the loot you bring back from the dungeons.
|
||||
|
||||
In preparation for your journey, you are given an
|
||||
enchanted mace, a bow, and a quiver of arrows taken from a
|
||||
dragon's hoard in the far off Dark Mountains. You are also
|
||||
outfitted with elf-crafted armor and given enough food to
|
||||
reach the dungeons. You say goodbye to family and friends
|
||||
for what may be the last time and head up the road.
|
||||
|
||||
You set out on your way to the dungeons and after sev-
|
||||
eral days of uneventful travel, you see the ancient ruins
|
||||
that mark the entrance to the Dungeons of Doom. It is late
|
||||
at night, so you make camp at the entrance and spend the
|
||||
____________________
|
||||
UNIX is a trademark of Bell Laboratories
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-2 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
night sleeping under the open skies. In the morning you
|
||||
gather your weapons, put on your armor, eat what is almost
|
||||
your last food, and enter the dungeons.
|
||||
|
||||
2. What is going on here?
|
||||
|
||||
You have just begun a game of rogue. Your goal is to
|
||||
grab as much treasure as you can, find the Amulet of Yendor,
|
||||
and get out of the Dungeons of Doom alive. On the screen, a
|
||||
map of where you have been and what you have seen on the
|
||||
current dungeon level is kept. As you explore more of the
|
||||
level, it appears on the screen in front of you.
|
||||
|
||||
Rogue differs from most computer fantasy games in that
|
||||
it is screen oriented. Commands are all one or two
|
||||
keystrokes1 and the results of your commands are displayed
|
||||
graphically on the screen rather than being explained in
|
||||
words.2
|
||||
|
||||
Another major difference between rogue and other com-
|
||||
puter fantasy games is that once you have solved all the
|
||||
puzzles in a standard fantasy game, it has lost most of its
|
||||
excitement and it ceases to be fun. Rogue, on the other
|
||||
hand, generates a new dungeon every time you play it and
|
||||
even the author finds it an entertaining and exciting game.
|
||||
|
||||
3. What do all those things on the screen mean?
|
||||
|
||||
In order to understand what is going on in rogue you
|
||||
have to first get some grasp of what rogue is doing with the
|
||||
screen. The rogue screen is intended to replace the "You
|
||||
can see ..." descriptions of standard fantasy games. Figure
|
||||
1 is a sample of what a rogue screen might look like.
|
||||
|
||||
3.1. The bottom line
|
||||
|
||||
At the bottom line of the screen are a few pieces of
|
||||
cryptic information describing your current status. Here is
|
||||
an explanation of what these things mean:
|
||||
|
||||
Level This number indicates how deep you have gone in the
|
||||
dungeon. It starts at one and goes up as you go
|
||||
deeper into the dungeon.
|
||||
|
||||
Gold The number of gold pieces you have managed to find
|
||||
and keep with you so far.
|
||||
____________________
|
||||
1 As opposed to pseudo English sentences.
|
||||
2 A minimum screen size of 24 lines by 80 columns is re-
|
||||
quired. If the screen is larger, only the 24x80 section
|
||||
will be used for the map.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-3
|
||||
|
||||
|
||||
|
||||
____________________________________________________________
|
||||
|
||||
|
||||
------------
|
||||
|..........+
|
||||
|..@....]..|
|
||||
|....B.....|
|
||||
|..........|
|
||||
-----+------
|
||||
|
||||
|
||||
|
||||
Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0
|
||||
|
||||
Figure 1
|
||||
____________________________________________________________
|
||||
|
||||
|
||||
Hp Your current and maximum health points. Health
|
||||
points indicate how much damage you can take before
|
||||
you die. The more you get hit in a fight, the lower
|
||||
they get. You can regain health points by resting.
|
||||
The number in parentheses is the maximum number your
|
||||
health points can reach.
|
||||
|
||||
Str Your current strength and maximum ever strength.
|
||||
This can be any integer less than or equal to 31, or
|
||||
greater than or equal to three. The higher the num-
|
||||
ber, the stronger you are. The number in the paren-
|
||||
theses is the maximum strength you have attained so
|
||||
far this game.
|
||||
|
||||
Arm Your current armor protection. This number indicates
|
||||
how effective your armor is in stopping blows from
|
||||
unfriendly creatures. The higher this number is, the
|
||||
more effective the armor.
|
||||
|
||||
Exp These two numbers give your current experience level
|
||||
and experience points. As you do things, you gain
|
||||
experience points. At certain experience point
|
||||
totals, you gain an experience level. The more expe-
|
||||
rienced you are, the better you are able to fight and
|
||||
to withstand magical attacks.
|
||||
|
||||
3.2. The top line
|
||||
|
||||
The top line of the screen is reserved for printing
|
||||
messages that describe things that are impossible to repre-
|
||||
sent visually. If you see a "--More--" on the top line,
|
||||
this means that rogue wants to print another message on the
|
||||
screen, but it wants to make certain that you have read the
|
||||
one that is there first. To read the next message, just
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-4 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
type a space.
|
||||
|
||||
3.3. The rest of the screen
|
||||
|
||||
The rest of the screen is the map of the level as you
|
||||
have explored it so far. Each symbol on the screen repre-
|
||||
sents something. Here is a list of what the various symbols
|
||||
mean:
|
||||
|
||||
@ This symbol represents you, the adventurer.
|
||||
|
||||
-| These symbols represent the walls of rooms.
|
||||
|
||||
+ A door to/from a room.
|
||||
|
||||
. The floor of a room.
|
||||
|
||||
# The floor of a passage between rooms.
|
||||
|
||||
* A pile or pot of gold.
|
||||
|
||||
) A weapon of some sort.
|
||||
|
||||
] A piece of armor.
|
||||
|
||||
! A flask containing a magic potion.
|
||||
|
||||
? A piece of paper, usually a magic scroll.
|
||||
|
||||
= A ring with magic properties
|
||||
|
||||
/ A magical staff or wand
|
||||
|
||||
^ A trap, watch out for these.
|
||||
|
||||
% A staircase to other levels
|
||||
|
||||
: A piece of food.
|
||||
|
||||
A-Z The uppercase letters represent the various inhabitants
|
||||
of the Dungeons of Doom. Watch out, they can be nasty
|
||||
and vicious.
|
||||
|
||||
4. Commands
|
||||
|
||||
Commands are given to rogue by typing one or two char-
|
||||
acters. Most commands can be preceded by a count to repeat
|
||||
them (e.g. typing "10s" will do ten searches). Commands for
|
||||
which counts make no sense have the count ignored. To can-
|
||||
cel a count or a prefix, type <ESCAPE>. The list of com-
|
||||
mands is rather long, but it can be read at any time during
|
||||
the game with the "?" command. Here it is for reference,
|
||||
with a short explanation of each command.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-5
|
||||
|
||||
|
||||
? The help command. Asks for a character to give help
|
||||
on. If you type a "*", it will list all the commands,
|
||||
otherwise it will explain what the character you typed
|
||||
does.
|
||||
|
||||
/ This is the "What is that on the screen?" command. A
|
||||
"/" followed by any character that you see on the
|
||||
level, will tell you what that character is. For
|
||||
instance, typing "/@" will tell you that the "@" symbol
|
||||
represents you, the player.
|
||||
|
||||
h, H, ^H
|
||||
Move left. You move one space to the left. If you use
|
||||
upper case "h", you will continue to move left until
|
||||
you run into something. This works for all movement
|
||||
commands (e.g. "L" means run in direction "l") If you
|
||||
use the "control" "h", you will continue moving in the
|
||||
specified direction until you pass something interest-
|
||||
ing or run into a wall. You should experiment with
|
||||
this, since it is a very useful command, but very dif-
|
||||
ficult to describe. This also works for all movement
|
||||
commands.
|
||||
|
||||
j Move down.
|
||||
|
||||
k Move up.
|
||||
|
||||
l Move right.
|
||||
|
||||
y Move diagonally up and left.
|
||||
|
||||
u Move diagonally up and right.
|
||||
|
||||
b Move diagonally down and left.
|
||||
|
||||
n Move diagonally down and right.
|
||||
|
||||
t Throw an object. This is a prefix command. When fol-
|
||||
lowed with a direction it throws an object in the spec-
|
||||
ified direction. (e.g. type "th" to throw something to
|
||||
the left.)
|
||||
|
||||
f Fight until someone dies. When followed with a direc-
|
||||
tion this will force you to fight the creature in that
|
||||
direction until either you or it bites the big one.
|
||||
|
||||
m Move onto something without picking it up. This will
|
||||
move you one space in the direction you specify and, if
|
||||
there is an object there you can pick up, it won't do
|
||||
it.
|
||||
|
||||
z Zap prefix. Point a staff or wand in a given direction
|
||||
and fire it. Even non-directional staves must be
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-6 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
pointed in some direction to be used.
|
||||
|
||||
^ Identify trap command. If a trap is on your map and
|
||||
you can't remember what type it is, you can get rogue
|
||||
to remind you by getting next to it and typing "^" fol-
|
||||
lowed by the direction that would move you on top of
|
||||
it.
|
||||
|
||||
s Search for traps and secret doors. Examine each space
|
||||
immediately adjacent to you for the existence of a trap
|
||||
or secret door. There is a large chance that even if
|
||||
there is something there, you won't find it, so you
|
||||
might have to search a while before you find something.
|
||||
|
||||
> Climb down a staircase to the next level. Not surpris-
|
||||
ingly, this can only be done if you are standing on
|
||||
staircase.
|
||||
|
||||
< Climb up a staircase to the level above. This can't be
|
||||
done without the Amulet of Yendor in your possession.
|
||||
|
||||
. Rest. This is the "do nothing" command. This is good
|
||||
for waiting and healing.
|
||||
|
||||
, Pick up something. This picks up whatever you are cur-
|
||||
rently standing on, if you are standing on anything at
|
||||
all.
|
||||
|
||||
i Inventory. List what you are carrying in your pack.
|
||||
|
||||
I Selective inventory. Tells you what a single item in
|
||||
your pack is.
|
||||
|
||||
q Quaff one of the potions you are carrying.
|
||||
|
||||
r Read one of the scrolls in your pack.
|
||||
|
||||
e Eat food from your pack.
|
||||
|
||||
w Wield a weapon. Take a weapon out of your pack and
|
||||
carry it for use in combat, replacing the one you are
|
||||
currently using (if any).
|
||||
|
||||
W Wear armor. You can only wear one suit of armor at a
|
||||
time. This takes extra time.
|
||||
|
||||
T Take armor off. You can't remove armor that is cursed.
|
||||
This takes extra time.
|
||||
|
||||
P Put on a ring. You can wear only two rings at a time
|
||||
(one on each hand). If you aren't wearing any rings,
|
||||
this command will ask you which hand you want to wear
|
||||
it on, otherwise, it will place it on the unused hand.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-7
|
||||
|
||||
|
||||
The program assumes that you wield your sword in your
|
||||
right hand.
|
||||
|
||||
R Remove a ring. If you are only wearing one ring, this
|
||||
command takes it off. If you are wearing two, it will
|
||||
ask you which one you wish to remove,
|
||||
|
||||
d Drop an object. Take something out of your pack and
|
||||
leave it lying on the floor. Only one object can
|
||||
occupy each space. You cannot drop a cursed object at
|
||||
all if you are wielding or wearing it.
|
||||
|
||||
c Call an object something. If you have a type of object
|
||||
in your pack which you wish to remember something
|
||||
about, you can use the call command to give a name to
|
||||
that type of object. This is usually used when you
|
||||
figure out what a potion, scroll, ring, or staff is
|
||||
after you pick it up, or when you want to remember
|
||||
which of those swords in your pack you were wielding.
|
||||
|
||||
D Print out which things you've discovered something
|
||||
about. This command will ask you what type of thing
|
||||
you are interested in. If you type the character for a
|
||||
given type of object (e.g. "!" for potion) it will
|
||||
tell you which kinds of that type of object you've dis-
|
||||
covered (i.e., figured out what they are). This com-
|
||||
mand works for potions, scrolls, rings, and staves and
|
||||
wands.
|
||||
|
||||
o Examine and set options. This command is further
|
||||
explained in the section on options.
|
||||
|
||||
^R Redraws the screen. Useful if spurious messages or
|
||||
transmission errors have messed up the display.
|
||||
|
||||
^P Print last message. Useful when a message disappears
|
||||
before you can read it. This only repeats the last
|
||||
message that was not a mistyped command so that you
|
||||
don't loose anything by accidentally typing the wrong
|
||||
character instead of ^P.
|
||||
|
||||
<ESCAPE>
|
||||
Cancel a command, prefix, or count.
|
||||
|
||||
! Escape to a shell for some commands.
|
||||
|
||||
Q Quit. Leave the game.
|
||||
|
||||
S Save the current game in a file. It will ask you
|
||||
whether you wish to use the default save file. Caveat:
|
||||
Rogue won't let you start up a copy of a saved game,
|
||||
and it removes the save file as soon as you start up a
|
||||
restored game. This is to prevent people from saving a
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-8 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
game just before a dangerous position and then restart-
|
||||
ing it if they die. To restore a saved game, give the
|
||||
file name as an argument to rogue. As in
|
||||
% rogue save_file
|
||||
|
||||
To restart from the default save file (see below), run
|
||||
% rogue -r
|
||||
|
||||
v Prints the program version number.
|
||||
|
||||
) Print the weapon you are currently wielding
|
||||
|
||||
] Print the armor you are currently wearing
|
||||
|
||||
= Print the rings you are currently wearing
|
||||
|
||||
@ Reprint the status line on the message line
|
||||
|
||||
5. Rooms
|
||||
|
||||
Rooms in the dungeons are either lit or dark. If you
|
||||
walk into a lit room, the entire room will be drawn on the
|
||||
screen as soon as you enter. If you walk into a dark room,
|
||||
it will only be displayed as you explore it. Upon leaving a
|
||||
room, all monsters inside the room are erased from the
|
||||
screen. In the darkness you can only see one space in all
|
||||
directions around you. A corridor is always dark.
|
||||
|
||||
6. Fighting
|
||||
|
||||
If you see a monster and you wish to fight it, just
|
||||
attempt to run into it. Many times a monster you find will
|
||||
mind its own business unless you attack it. It is often the
|
||||
case that discretion is the better part of valor.
|
||||
|
||||
7. Objects you can find
|
||||
|
||||
When you find something in the dungeon, it is common to
|
||||
want to pick the object up. This is accomplished in rogue
|
||||
by walking over the object (unless you use the "m" prefix,
|
||||
see above). If you are carrying too many things, the pro-
|
||||
gram will tell you and it won't pick up the object, other-
|
||||
wise it will add it to your pack and tell you what you just
|
||||
picked up.
|
||||
|
||||
Many of the commands that operate on objects must
|
||||
prompt you to find out which object you want to use. If you
|
||||
change your mind and don't want to do that command after
|
||||
all, just type an <ESCAPE> and the command will be aborted.
|
||||
|
||||
Some objects, like armor and weapons, are easily dif-
|
||||
ferentiated. Others, like scrolls and potions, are given
|
||||
labels which vary according to type. During a game, any two
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-9
|
||||
|
||||
|
||||
of the same kind of object with the same label are the same
|
||||
type. However, the labels will vary from game to game.
|
||||
|
||||
When you use one of these labeled objects, if its
|
||||
effect is obvious, rogue will remember what it is for you.
|
||||
If it's effect isn't extremely obvious you will be asked
|
||||
what you want to scribble on it so you will recognize it
|
||||
later, or you can use the "call" command (see above).
|
||||
|
||||
7.1. Weapons
|
||||
|
||||
Some weapons, like arrows, come in bunches, but most
|
||||
come one at a time. In order to use a weapon, you must
|
||||
wield it. To fire an arrow out of a bow, you must first
|
||||
wield the bow, then throw the arrow. You can only wield one
|
||||
weapon at a time, but you can't change weapons if the one
|
||||
you are currently wielding is cursed. The commands to use
|
||||
weapons are "w" (wield) and "t" (throw).
|
||||
|
||||
7.2. Armor
|
||||
|
||||
There are various sorts of armor lying around in the
|
||||
dungeon. Some of it is enchanted, some is cursed, and some
|
||||
is just normal. Different armor types have different armor
|
||||
protection. The higher the armor protection, the more pro-
|
||||
tection the armor affords against the blows of monsters.
|
||||
Here is a list of the various armor types and their normal
|
||||
armor protection:
|
||||
|
||||
|
||||
+-----------------------------------------+
|
||||
| Type Protection |
|
||||
|None 0 |
|
||||
|Leather armor 2 |
|
||||
|Studded leather / Ring mail 3 |
|
||||
|Scale mail 4 |
|
||||
|Chain mail 5 |
|
||||
|Banded mail / Splint mail 6 |
|
||||
|Plate mail 7 |
|
||||
+-----------------------------------------+
|
||||
|
||||
|
||||
If a piece of armor is enchanted, its armor protection will
|
||||
be higher than normal. If a suit of armor is cursed, its
|
||||
armor protection will be lower, and you will not be able to
|
||||
remove it. However, not all armor with a protection that is
|
||||
lower than normal is cursed.
|
||||
|
||||
The commands to use weapons are "W" (wear) and "T"
|
||||
(take off).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-10 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
7.3. Scrolls
|
||||
|
||||
Scrolls come with titles in an unknown tongue3. After
|
||||
you read a scroll, it disappears from your pack. The com-
|
||||
mand to use a scroll is "r" (read).
|
||||
|
||||
7.4. Potions
|
||||
|
||||
Potions are labeled by the color of the liquid inside
|
||||
the flask. They disappear after being quaffed. The command
|
||||
to use a scroll is "q" (quaff).
|
||||
|
||||
7.5. Staves and Wands
|
||||
|
||||
Staves and wands do the same kinds of things. Staves
|
||||
are identified by a type of wood; wands by a type of metal
|
||||
or bone. They are generally things you want to do to some-
|
||||
thing over a long distance, so you must point them at what
|
||||
you wish to affect to use them. Some staves are not
|
||||
affected by the direction they are pointed, though. Staves
|
||||
come with multiple magic charges, the number being random,
|
||||
and when they are used up, the staff is just a piece of wood
|
||||
or metal.
|
||||
|
||||
The command to use a wand or staff is "z" (zap)
|
||||
|
||||
7.6. Rings
|
||||
|
||||
Rings are very useful items, since they are relatively
|
||||
permanent magic, unlike the usually fleeting effects of
|
||||
potions, scrolls, and staves. Of course, the bad rings are
|
||||
also more powerful. Most rings also cause you to use up
|
||||
food more rapidly, the rate varying with the type of ring.
|
||||
Rings are differentiated by their stone settings. The com-
|
||||
mands to use rings are "P" (put on) and "R" (remove).
|
||||
|
||||
7.7. Food
|
||||
|
||||
Food is necessary to keep you going. If you go too
|
||||
long without eating you will faint, and eventually die of
|
||||
starvation. The command to use food is "e" (eat).
|
||||
|
||||
8. Options
|
||||
|
||||
Due to variations in personal tastes and conceptions of
|
||||
the way rogue should do things, there are a set of options
|
||||
you can set that cause rogue to behave in various different
|
||||
____________________
|
||||
3 Actually, it's a dialect spoken only by the twenty-sev-
|
||||
en members of a tribe in Outer Mongolia, but you're not sup-
|
||||
posed to know that.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-11
|
||||
|
||||
|
||||
ways.
|
||||
|
||||
8.1. Setting the options
|
||||
|
||||
There are two ways to set the options. The first is
|
||||
with the "o" command of rogue; the second is with the
|
||||
"ROGUEOPTS" environment variable4.
|
||||
|
||||
8.1.1. Using the `o' command
|
||||
|
||||
When you type "o" in rogue, it clears the screen and
|
||||
displays the current settings for all the options. It then
|
||||
places the cursor by the value of the first option and waits
|
||||
for you to type. You can type a <RETURN> which means to go
|
||||
to the next option, a "-" which means to go to the previous
|
||||
option, an <ESCAPE> which means to return to the game, or
|
||||
you can give the option a value. For boolean options this
|
||||
merely involves typing "t" for true or "f" for false. For
|
||||
string options, type the new value followed by a <RETURN>.
|
||||
|
||||
8.1.2. Using the ROGUEOPTS variable
|
||||
|
||||
The ROGUEOPTS variable is a string containing a comma
|
||||
separated list of initial values for the various options.
|
||||
Boolean variables can be turned on by listing their name or
|
||||
turned off by putting a "no" in front of the name. Thus to
|
||||
set up an environment variable so that jump is on, terse is
|
||||
off, and the name is set to "Blue Meanie", use the command
|
||||
% setenv ROGUEOPTS "jump,noterse,name=Blue Meanie"5
|
||||
|
||||
8.2. Option list
|
||||
|
||||
Here is a list of the options and an explanation of
|
||||
what each one is for. The default value for each is
|
||||
enclosed in square brackets. For character string options,
|
||||
input over fifty characters will be ignored.
|
||||
|
||||
terse [noterse]
|
||||
Useful for those who are tired of the sometimes lengthy
|
||||
messages of rogue. This is a useful option for playing
|
||||
on slow terminals, so this option defaults to terse if
|
||||
you are on a slow (1200 baud or under) terminal.
|
||||
|
||||
|
||||
____________________
|
||||
4 On Version 6 systems, there is no equivalent of the
|
||||
ROGUEOPTS feature.
|
||||
5 For those of you who use the Bourne shell sh (1), the
|
||||
commands would be
|
||||
$ ROGUEOPTS="jump,noterse,name=Blue Meanie"
|
||||
$ export ROGUEOPTS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-12 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
jump [nojump]
|
||||
If this option is set, running moves will not be dis-
|
||||
played until you reach the end of the move. This saves
|
||||
considerable cpu and display time. This option
|
||||
defaults to jump if you are using a slow terminal.
|
||||
|
||||
flush [noflush]
|
||||
All typeahead is thrown away after each round of bat-
|
||||
tle. This is useful for those who type far ahead and
|
||||
then watch in dismay as a Bat kills them.
|
||||
|
||||
seefloor [seefloor]
|
||||
Display the floor around you on the screen as you move
|
||||
through dark rooms. Due to the amount of characters
|
||||
generated, this option defaults to noseefloor if you
|
||||
are using a slow terminal.
|
||||
|
||||
passgo [nopassgo]
|
||||
Follow turnings in passageways. If you run in a pas-
|
||||
sage and you run into stone or a wall, rogue will see
|
||||
if it can turn to the right or left. If it can only
|
||||
turn one way, it will turn that way. If it can turn
|
||||
either or neither, it will stop. This algorithm can
|
||||
sometimes lead to slightly confusing occurrences which
|
||||
is why it defaults to nopassgo.
|
||||
|
||||
tombstone [tombstone]
|
||||
Print out the tombstone at the end if you get killed.
|
||||
This is nice but slow, so you can turn it off if you
|
||||
like.
|
||||
|
||||
inven [overwrite]
|
||||
Inventory type. This can have one of three values:
|
||||
overwrite, slow, or clear. With overwrite the top
|
||||
lines of the map are overwritten with the list when
|
||||
inventory is requested or when "Which item do you wish
|
||||
to . . .? " questions are answered with a "*". How-
|
||||
ever, if the list is longer than a screenful, the
|
||||
screen is cleared. With slow, lists are displayed one
|
||||
item at a time on the top of the screen, and with
|
||||
clear, the screen is cleared, the list is displayed,
|
||||
and then the dungeon level is re-displayed. Due to
|
||||
speed considerations, clear is the default for termi-
|
||||
nals without clear-to-end-of-line capabilities.
|
||||
|
||||
name [account name]
|
||||
This is the name of your character. It is used if you
|
||||
get on the top ten scorer's list.
|
||||
|
||||
fruit [slime-mold]
|
||||
This should hold the name of a fruit that you enjoy
|
||||
eating. It is basically a whimsey that rogue uses in a
|
||||
couple of places.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-13
|
||||
|
||||
|
||||
file [~/rogue.save]
|
||||
The default file name for saving the game. If your
|
||||
phone is hung up by accident, rogue will automatically
|
||||
save the game in this file. The file name may start
|
||||
with the special character "~" which expands to be your
|
||||
home directory.
|
||||
|
||||
9. Scoring
|
||||
|
||||
Rogue usually maintains a list of the top scoring peo-
|
||||
ple or scores on your machine. Depending on how it is set
|
||||
up, it can post either the top scores or the top players.
|
||||
In the latter case, each account on the machine can post
|
||||
only one non-winning score on this list. If you score
|
||||
higher than someone else on this list, or better your previ-
|
||||
ous score on the list, you will be inserted in the proper
|
||||
place under your current name. How many scores are kept can
|
||||
also be set up by whoever installs it on your machine.
|
||||
|
||||
If you quit the game, you get out with all of your gold
|
||||
intact. If, however, you get killed in the Dungeons of
|
||||
Doom, your body is forwarded to your next-of-kin, along with
|
||||
90% of your gold; ten percent of your gold is kept by the
|
||||
Dungeons' wizard as a fee6. This should make you consider
|
||||
whether you want to take one last hit at that monster and
|
||||
possibly live, or quit and thus stop with whatever you have.
|
||||
If you quit, you do get all your gold, but if you swing and
|
||||
live, you might find more.
|
||||
|
||||
If you just want to see what the current top play-
|
||||
ers/games list is, you can type
|
||||
% rogue -s
|
||||
|
||||
10. Acknowledgements
|
||||
|
||||
Rogue was originally conceived of by Glenn Wichman and
|
||||
Michael Toy. Ken Arnold and Michael Toy then smoothed out
|
||||
the user interface, and added jillions of new features. We
|
||||
would like to thank Bob Arnold, Michelle Busch, Andy
|
||||
Hatcher, Kipp Hickman, Mark Horton, Daniel Jensen, Bill Joy,
|
||||
Joe Kalash, Steve Maurer, Marty McNary, Jan Miller, and
|
||||
Scott Nelson for their ideas and assistance; and also the
|
||||
teeming multitudes who graciously ignored work, school, and
|
||||
social life to play rogue and send us bugs, complaints, sug-
|
||||
gestions, and just plain flames. And also Mom.
|
||||
|
||||
|
||||
|
||||
____________________
|
||||
6 The Dungeon's wizard is named Wally the Wonder Badger.
|
||||
Invocations should be accompanied by a sizable donation.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,858 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
Michael C. Toy
|
||||
Kenneth C. R. C. Arnold
|
||||
|
||||
|
||||
Computer Systems Research Group
|
||||
Department of Electrical Engineering and Computer Science
|
||||
University of California
|
||||
Berkeley, California 94720
|
||||
|
||||
|
||||
|
||||
|
||||
ABSTRACT
|
||||
|
||||
Rogue is a visual CRT based fantasy game which runs
|
||||
under the UNIX timesharing system. This paper de-
|
||||
scribes how to play rogue, and gives a few hints for
|
||||
those who might otherwise get lost in the Dungeons
|
||||
of Doom.
|
||||
|
||||
|
||||
|
||||
|
||||
1. Introduction
|
||||
|
||||
You have just finished your years as a student at the
|
||||
local fighter's guild. After much practice and sweat you
|
||||
have finally completed your training and are ready to embark
|
||||
upon a perilous adventure. As a test of your skills, the
|
||||
local guildmasters have sent you into the Dungeons of Doom.
|
||||
Your task is to return with the Amulet of Yendor. Your
|
||||
reward for the completion of this task will be a full mem-
|
||||
bership in the local guild. In addition, you are allowed to
|
||||
keep all the loot you bring back from the dungeons.
|
||||
|
||||
In preparation for your journey, you are given an
|
||||
enchanted mace, a bow, and a quiver of arrows taken from a
|
||||
dragon's hoard in the far off Dark Mountains. You are also
|
||||
outfitted with elf-crafted armor and given enough food to
|
||||
reach the dungeons. You say goodbye to family and friends
|
||||
for what may be the last time and head up the road.
|
||||
|
||||
You set out on your way to the dungeons and after sev-
|
||||
eral days of uneventful travel, you see the ancient ruins
|
||||
that mark the entrance to the Dungeons of Doom. It is late
|
||||
at night, so you make camp at the entrance and spend the
|
||||
____________________
|
||||
UNIX is a trademark of Bell Laboratories
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-2 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
night sleeping under the open skies. In the morning you
|
||||
gather your weapons, put on your armor, eat what is almost
|
||||
your last food, and enter the dungeons.
|
||||
|
||||
2. What is going on here?
|
||||
|
||||
You have just begun a game of rogue. Your goal is to
|
||||
grab as much treasure as you can, find the Amulet of Yendor,
|
||||
and get out of the Dungeons of Doom alive. On the screen, a
|
||||
map of where you have been and what you have seen on the
|
||||
current dungeon level is kept. As you explore more of the
|
||||
level, it appears on the screen in front of you.
|
||||
|
||||
Rogue differs from most computer fantasy games in that
|
||||
it is screen oriented. Commands are all one or two
|
||||
keystrokes1 and the results of your commands are displayed
|
||||
graphically on the screen rather than being explained in
|
||||
words.2
|
||||
|
||||
Another major difference between rogue and other com-
|
||||
puter fantasy games is that once you have solved all the
|
||||
puzzles in a standard fantasy game, it has lost most of its
|
||||
excitement and it ceases to be fun. Rogue, on the other
|
||||
hand, generates a new dungeon every time you play it and
|
||||
even the author finds it an entertaining and exciting game.
|
||||
|
||||
3. What do all those things on the screen mean?
|
||||
|
||||
In order to understand what is going on in rogue you
|
||||
have to first get some grasp of what rogue is doing with the
|
||||
screen. The rogue screen is intended to replace the "You
|
||||
can see ..." descriptions of standard fantasy games. Figure
|
||||
1 is a sample of what a rogue screen might look like.
|
||||
|
||||
3.1. The bottom line
|
||||
|
||||
At the bottom line of the screen are a few pieces of
|
||||
cryptic information describing your current status. Here is
|
||||
an explanation of what these things mean:
|
||||
|
||||
Level This number indicates how deep you have gone in the
|
||||
dungeon. It starts at one and goes up as you go
|
||||
deeper into the dungeon.
|
||||
|
||||
Gold The number of gold pieces you have managed to find
|
||||
and keep with you so far.
|
||||
____________________
|
||||
1 As opposed to pseudo English sentences.
|
||||
2 A minimum screen size of 24 lines by 80 columns is re-
|
||||
quired. If the screen is larger, only the 24x80 section
|
||||
will be used for the map.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-3
|
||||
|
||||
|
||||
|
||||
____________________________________________________________
|
||||
|
||||
|
||||
------------
|
||||
|..........+
|
||||
|..@....]..|
|
||||
|....B.....|
|
||||
|..........|
|
||||
-----+------
|
||||
|
||||
|
||||
|
||||
Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0
|
||||
|
||||
Figure 1
|
||||
____________________________________________________________
|
||||
|
||||
|
||||
Hp Your current and maximum health points. Health
|
||||
points indicate how much damage you can take before
|
||||
you die. The more you get hit in a fight, the lower
|
||||
they get. You can regain health points by resting.
|
||||
The number in parentheses is the maximum number your
|
||||
health points can reach.
|
||||
|
||||
Str Your current strength and maximum ever strength.
|
||||
This can be any integer less than or equal to 31, or
|
||||
greater than or equal to three. The higher the num-
|
||||
ber, the stronger you are. The number in the paren-
|
||||
theses is the maximum strength you have attained so
|
||||
far this game.
|
||||
|
||||
Arm Your current armor protection. This number indicates
|
||||
how effective your armor is in stopping blows from
|
||||
unfriendly creatures. The higher this number is, the
|
||||
more effective the armor.
|
||||
|
||||
Exp These two numbers give your current experience level
|
||||
and experience points. As you do things, you gain
|
||||
experience points. At certain experience point
|
||||
totals, you gain an experience level. The more expe-
|
||||
rienced you are, the better you are able to fight and
|
||||
to withstand magical attacks.
|
||||
|
||||
3.2. The top line
|
||||
|
||||
The top line of the screen is reserved for printing
|
||||
messages that describe things that are impossible to repre-
|
||||
sent visually. If you see a "--More--" on the top line,
|
||||
this means that rogue wants to print another message on the
|
||||
screen, but it wants to make certain that you have read the
|
||||
one that is there first. To read the next message, just
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-4 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
type a space.
|
||||
|
||||
3.3. The rest of the screen
|
||||
|
||||
The rest of the screen is the map of the level as you
|
||||
have explored it so far. Each symbol on the screen repre-
|
||||
sents something. Here is a list of what the various symbols
|
||||
mean:
|
||||
|
||||
@ This symbol represents you, the adventurer.
|
||||
|
||||
-| These symbols represent the walls of rooms.
|
||||
|
||||
+ A door to/from a room.
|
||||
|
||||
. The floor of a room.
|
||||
|
||||
# The floor of a passage between rooms.
|
||||
|
||||
* A pile or pot of gold.
|
||||
|
||||
) A weapon of some sort.
|
||||
|
||||
] A piece of armor.
|
||||
|
||||
! A flask containing a magic potion.
|
||||
|
||||
? A piece of paper, usually a magic scroll.
|
||||
|
||||
= A ring with magic properties
|
||||
|
||||
/ A magical staff or wand
|
||||
|
||||
^ A trap, watch out for these.
|
||||
|
||||
% A staircase to other levels
|
||||
|
||||
: A piece of food.
|
||||
|
||||
A-Z The uppercase letters represent the various inhabitants
|
||||
of the Dungeons of Doom. Watch out, they can be nasty
|
||||
and vicious.
|
||||
|
||||
4. Commands
|
||||
|
||||
Commands are given to rogue by typing one or two char-
|
||||
acters. Most commands can be preceded by a count to repeat
|
||||
them (e.g. typing "10s" will do ten searches). Commands for
|
||||
which counts make no sense have the count ignored. To can-
|
||||
cel a count or a prefix, type <ESCAPE>. The list of com-
|
||||
mands is rather long, but it can be read at any time during
|
||||
the game with the "?" command. Here it is for reference,
|
||||
with a short explanation of each command.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-5
|
||||
|
||||
|
||||
? The help command. Asks for a character to give help
|
||||
on. If you type a "*", it will list all the commands,
|
||||
otherwise it will explain what the character you typed
|
||||
does.
|
||||
|
||||
/ This is the "What is that on the screen?" command. A
|
||||
"/" followed by any character that you see on the
|
||||
level, will tell you what that character is. For
|
||||
instance, typing "/@" will tell you that the "@" symbol
|
||||
represents you, the player.
|
||||
|
||||
h, H, ^H
|
||||
Move left. You move one space to the left. If you use
|
||||
upper case "h", you will continue to move left until
|
||||
you run into something. This works for all movement
|
||||
commands (e.g. "L" means run in direction "l") If you
|
||||
use the "control" "h", you will continue moving in the
|
||||
specified direction until you pass something interest-
|
||||
ing or run into a wall. You should experiment with
|
||||
this, since it is a very useful command, but very dif-
|
||||
ficult to describe. This also works for all movement
|
||||
commands.
|
||||
|
||||
j Move down.
|
||||
|
||||
k Move up.
|
||||
|
||||
l Move right.
|
||||
|
||||
y Move diagonally up and left.
|
||||
|
||||
u Move diagonally up and right.
|
||||
|
||||
b Move diagonally down and left.
|
||||
|
||||
n Move diagonally down and right.
|
||||
|
||||
t Throw an object. This is a prefix command. When fol-
|
||||
lowed with a direction it throws an object in the spec-
|
||||
ified direction. (e.g. type "th" to throw something to
|
||||
the left.)
|
||||
|
||||
f Fight until someone dies. When followed with a direc-
|
||||
tion this will force you to fight the creature in that
|
||||
direction until either you or it bites the big one.
|
||||
|
||||
m Move onto something without picking it up. This will
|
||||
move you one space in the direction you specify and, if
|
||||
there is an object there you can pick up, it won't do
|
||||
it.
|
||||
|
||||
z Zap prefix. Point a staff or wand in a given direction
|
||||
and fire it. Even non-directional staves must be
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-6 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
pointed in some direction to be used.
|
||||
|
||||
^ Identify trap command. If a trap is on your map and
|
||||
you can't remember what type it is, you can get rogue
|
||||
to remind you by getting next to it and typing "^" fol-
|
||||
lowed by the direction that would move you on top of
|
||||
it.
|
||||
|
||||
s Search for traps and secret doors. Examine each space
|
||||
immediately adjacent to you for the existence of a trap
|
||||
or secret door. There is a large chance that even if
|
||||
there is something there, you won't find it, so you
|
||||
might have to search a while before you find something.
|
||||
|
||||
> Climb down a staircase to the next level. Not surpris-
|
||||
ingly, this can only be done if you are standing on
|
||||
staircase.
|
||||
|
||||
< Climb up a staircase to the level above. This can't be
|
||||
done without the Amulet of Yendor in your possession.
|
||||
|
||||
. Rest. This is the "do nothing" command. This is good
|
||||
for waiting and healing.
|
||||
|
||||
, Pick up something. This picks up whatever you are cur-
|
||||
rently standing on, if you are standing on anything at
|
||||
all.
|
||||
|
||||
i Inventory. List what you are carrying in your pack.
|
||||
|
||||
I Selective inventory. Tells you what a single item in
|
||||
your pack is.
|
||||
|
||||
q Quaff one of the potions you are carrying.
|
||||
|
||||
r Read one of the scrolls in your pack.
|
||||
|
||||
e Eat food from your pack.
|
||||
|
||||
w Wield a weapon. Take a weapon out of your pack and
|
||||
carry it for use in combat, replacing the one you are
|
||||
currently using (if any).
|
||||
|
||||
W Wear armor. You can only wear one suit of armor at a
|
||||
time. This takes extra time.
|
||||
|
||||
T Take armor off. You can't remove armor that is cursed.
|
||||
This takes extra time.
|
||||
|
||||
P Put on a ring. You can wear only two rings at a time
|
||||
(one on each hand). If you aren't wearing any rings,
|
||||
this command will ask you which hand you want to wear
|
||||
it on, otherwise, it will place it on the unused hand.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-7
|
||||
|
||||
|
||||
The program assumes that you wield your sword in your
|
||||
right hand.
|
||||
|
||||
R Remove a ring. If you are only wearing one ring, this
|
||||
command takes it off. If you are wearing two, it will
|
||||
ask you which one you wish to remove,
|
||||
|
||||
d Drop an object. Take something out of your pack and
|
||||
leave it lying on the floor. Only one object can
|
||||
occupy each space. You cannot drop a cursed object at
|
||||
all if you are wielding or wearing it.
|
||||
|
||||
c Call an object something. If you have a type of object
|
||||
in your pack which you wish to remember something
|
||||
about, you can use the call command to give a name to
|
||||
that type of object. This is usually used when you
|
||||
figure out what a potion, scroll, ring, or staff is
|
||||
after you pick it up, or when you want to remember
|
||||
which of those swords in your pack you were wielding.
|
||||
|
||||
D Print out which things you've discovered something
|
||||
about. This command will ask you what type of thing
|
||||
you are interested in. If you type the character for a
|
||||
given type of object (e.g. "!" for potion) it will
|
||||
tell you which kinds of that type of object you've dis-
|
||||
covered (i.e., figured out what they are). This com-
|
||||
mand works for potions, scrolls, rings, and staves and
|
||||
wands.
|
||||
|
||||
o Examine and set options. This command is further
|
||||
explained in the section on options.
|
||||
|
||||
^R Redraws the screen. Useful if spurious messages or
|
||||
transmission errors have messed up the display.
|
||||
|
||||
^P Print last message. Useful when a message disappears
|
||||
before you can read it. This only repeats the last
|
||||
message that was not a mistyped command so that you
|
||||
don't loose anything by accidentally typing the wrong
|
||||
character instead of ^P.
|
||||
|
||||
<ESCAPE>
|
||||
Cancel a command, prefix, or count.
|
||||
|
||||
! Escape to a shell for some commands.
|
||||
|
||||
Q Quit. Leave the game.
|
||||
|
||||
S Save the current game in a file. It will ask you
|
||||
whether you wish to use the default save file. Caveat:
|
||||
Rogue won't let you start up a copy of a saved game,
|
||||
and it removes the save file as soon as you start up a
|
||||
restored game. This is to prevent people from saving a
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-8 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
game just before a dangerous position and then restart-
|
||||
ing it if they die. To restore a saved game, give the
|
||||
file name as an argument to rogue. As in
|
||||
% rogue save_file
|
||||
|
||||
To restart from the default save file (see below), run
|
||||
% rogue -r
|
||||
|
||||
v Prints the program version number.
|
||||
|
||||
) Print the weapon you are currently wielding
|
||||
|
||||
] Print the armor you are currently wearing
|
||||
|
||||
= Print the rings you are currently wearing
|
||||
|
||||
@ Reprint the status line on the message line
|
||||
|
||||
5. Rooms
|
||||
|
||||
Rooms in the dungeons are either lit or dark. If you
|
||||
walk into a lit room, the entire room will be drawn on the
|
||||
screen as soon as you enter. If you walk into a dark room,
|
||||
it will only be displayed as you explore it. Upon leaving a
|
||||
room, all monsters inside the room are erased from the
|
||||
screen. In the darkness you can only see one space in all
|
||||
directions around you. A corridor is always dark.
|
||||
|
||||
6. Fighting
|
||||
|
||||
If you see a monster and you wish to fight it, just
|
||||
attempt to run into it. Many times a monster you find will
|
||||
mind its own business unless you attack it. It is often the
|
||||
case that discretion is the better part of valor.
|
||||
|
||||
7. Objects you can find
|
||||
|
||||
When you find something in the dungeon, it is common to
|
||||
want to pick the object up. This is accomplished in rogue
|
||||
by walking over the object (unless you use the "m" prefix,
|
||||
see above). If you are carrying too many things, the pro-
|
||||
gram will tell you and it won't pick up the object, other-
|
||||
wise it will add it to your pack and tell you what you just
|
||||
picked up.
|
||||
|
||||
Many of the commands that operate on objects must
|
||||
prompt you to find out which object you want to use. If you
|
||||
change your mind and don't want to do that command after
|
||||
all, just type an <ESCAPE> and the command will be aborted.
|
||||
|
||||
Some objects, like armor and weapons, are easily dif-
|
||||
ferentiated. Others, like scrolls and potions, are given
|
||||
labels which vary according to type. During a game, any two
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-9
|
||||
|
||||
|
||||
of the same kind of object with the same label are the same
|
||||
type. However, the labels will vary from game to game.
|
||||
|
||||
When you use one of these labeled objects, if its
|
||||
effect is obvious, rogue will remember what it is for you.
|
||||
If it's effect isn't extremely obvious you will be asked
|
||||
what you want to scribble on it so you will recognize it
|
||||
later, or you can use the "call" command (see above).
|
||||
|
||||
7.1. Weapons
|
||||
|
||||
Some weapons, like arrows, come in bunches, but most
|
||||
come one at a time. In order to use a weapon, you must
|
||||
wield it. To fire an arrow out of a bow, you must first
|
||||
wield the bow, then throw the arrow. You can only wield one
|
||||
weapon at a time, but you can't change weapons if the one
|
||||
you are currently wielding is cursed. The commands to use
|
||||
weapons are "w" (wield) and "t" (throw).
|
||||
|
||||
7.2. Armor
|
||||
|
||||
There are various sorts of armor lying around in the
|
||||
dungeon. Some of it is enchanted, some is cursed, and some
|
||||
is just normal. Different armor types have different armor
|
||||
protection. The higher the armor protection, the more pro-
|
||||
tection the armor affords against the blows of monsters.
|
||||
Here is a list of the various armor types and their normal
|
||||
armor protection:
|
||||
|
||||
|
||||
+-----------------------------------------+
|
||||
| Type Protection |
|
||||
|None 0 |
|
||||
|Leather armor 2 |
|
||||
|Studded leather / Ring mail 3 |
|
||||
|Scale mail 4 |
|
||||
|Chain mail 5 |
|
||||
|Banded mail / Splint mail 6 |
|
||||
|Plate mail 7 |
|
||||
+-----------------------------------------+
|
||||
|
||||
|
||||
If a piece of armor is enchanted, its armor protection will
|
||||
be higher than normal. If a suit of armor is cursed, its
|
||||
armor protection will be lower, and you will not be able to
|
||||
remove it. However, not all armor with a protection that is
|
||||
lower than normal is cursed.
|
||||
|
||||
The commands to use weapons are "W" (wear) and "T"
|
||||
(take off).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-10 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
7.3. Scrolls
|
||||
|
||||
Scrolls come with titles in an unknown tongue3. After
|
||||
you read a scroll, it disappears from your pack. The com-
|
||||
mand to use a scroll is "r" (read).
|
||||
|
||||
7.4. Potions
|
||||
|
||||
Potions are labeled by the color of the liquid inside
|
||||
the flask. They disappear after being quaffed. The command
|
||||
to use a scroll is "q" (quaff).
|
||||
|
||||
7.5. Staves and Wands
|
||||
|
||||
Staves and wands do the same kinds of things. Staves
|
||||
are identified by a type of wood; wands by a type of metal
|
||||
or bone. They are generally things you want to do to some-
|
||||
thing over a long distance, so you must point them at what
|
||||
you wish to affect to use them. Some staves are not
|
||||
affected by the direction they are pointed, though. Staves
|
||||
come with multiple magic charges, the number being random,
|
||||
and when they are used up, the staff is just a piece of wood
|
||||
or metal.
|
||||
|
||||
The command to use a wand or staff is "z" (zap)
|
||||
|
||||
7.6. Rings
|
||||
|
||||
Rings are very useful items, since they are relatively
|
||||
permanent magic, unlike the usually fleeting effects of
|
||||
potions, scrolls, and staves. Of course, the bad rings are
|
||||
also more powerful. Most rings also cause you to use up
|
||||
food more rapidly, the rate varying with the type of ring.
|
||||
Rings are differentiated by their stone settings. The com-
|
||||
mands to use rings are "P" (put on) and "R" (remove).
|
||||
|
||||
7.7. Food
|
||||
|
||||
Food is necessary to keep you going. If you go too
|
||||
long without eating you will faint, and eventually die of
|
||||
starvation. The command to use food is "e" (eat).
|
||||
|
||||
8. Options
|
||||
|
||||
Due to variations in personal tastes and conceptions of
|
||||
the way rogue should do things, there are a set of options
|
||||
you can set that cause rogue to behave in various different
|
||||
____________________
|
||||
3 Actually, it's a dialect spoken only by the twenty-sev-
|
||||
en members of a tribe in Outer Mongolia, but you're not sup-
|
||||
posed to know that.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-11
|
||||
|
||||
|
||||
ways.
|
||||
|
||||
8.1. Setting the options
|
||||
|
||||
There are two ways to set the options. The first is
|
||||
with the "o" command of rogue; the second is with the
|
||||
"ROGUEOPTS" environment variable4.
|
||||
|
||||
8.1.1. Using the `o' command
|
||||
|
||||
When you type "o" in rogue, it clears the screen and
|
||||
displays the current settings for all the options. It then
|
||||
places the cursor by the value of the first option and waits
|
||||
for you to type. You can type a <RETURN> which means to go
|
||||
to the next option, a "-" which means to go to the previous
|
||||
option, an <ESCAPE> which means to return to the game, or
|
||||
you can give the option a value. For boolean options this
|
||||
merely involves typing "t" for true or "f" for false. For
|
||||
string options, type the new value followed by a <RETURN>.
|
||||
|
||||
8.1.2. Using the ROGUEOPTS variable
|
||||
|
||||
The ROGUEOPTS variable is a string containing a comma
|
||||
separated list of initial values for the various options.
|
||||
Boolean variables can be turned on by listing their name or
|
||||
turned off by putting a "no" in front of the name. Thus to
|
||||
set up an environment variable so that jump is on, terse is
|
||||
off, and the name is set to "Blue Meanie", use the command
|
||||
% setenv ROGUEOPTS "jump,noterse,name=Blue Meanie"5
|
||||
|
||||
8.2. Option list
|
||||
|
||||
Here is a list of the options and an explanation of
|
||||
what each one is for. The default value for each is
|
||||
enclosed in square brackets. For character string options,
|
||||
input over fifty characters will be ignored.
|
||||
|
||||
terse [noterse]
|
||||
Useful for those who are tired of the sometimes lengthy
|
||||
messages of rogue. This is a useful option for playing
|
||||
on slow terminals, so this option defaults to terse if
|
||||
you are on a slow (1200 baud or under) terminal.
|
||||
|
||||
|
||||
____________________
|
||||
4 On Version 6 systems, there is no equivalent of the
|
||||
ROGUEOPTS feature.
|
||||
5 For those of you who use the Bourne shell sh (1), the
|
||||
commands would be
|
||||
$ ROGUEOPTS="jump,noterse,name=Blue Meanie"
|
||||
$ export ROGUEOPTS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
USD:33-12 A Guide to the Dungeons of Doom
|
||||
|
||||
|
||||
jump [nojump]
|
||||
If this option is set, running moves will not be dis-
|
||||
played until you reach the end of the move. This saves
|
||||
considerable cpu and display time. This option
|
||||
defaults to jump if you are using a slow terminal.
|
||||
|
||||
flush [noflush]
|
||||
All typeahead is thrown away after each round of bat-
|
||||
tle. This is useful for those who type far ahead and
|
||||
then watch in dismay as a Bat kills them.
|
||||
|
||||
seefloor [seefloor]
|
||||
Display the floor around you on the screen as you move
|
||||
through dark rooms. Due to the amount of characters
|
||||
generated, this option defaults to noseefloor if you
|
||||
are using a slow terminal.
|
||||
|
||||
passgo [nopassgo]
|
||||
Follow turnings in passageways. If you run in a pas-
|
||||
sage and you run into stone or a wall, rogue will see
|
||||
if it can turn to the right or left. If it can only
|
||||
turn one way, it will turn that way. If it can turn
|
||||
either or neither, it will stop. This algorithm can
|
||||
sometimes lead to slightly confusing occurrences which
|
||||
is why it defaults to nopassgo.
|
||||
|
||||
tombstone [tombstone]
|
||||
Print out the tombstone at the end if you get killed.
|
||||
This is nice but slow, so you can turn it off if you
|
||||
like.
|
||||
|
||||
inven [overwrite]
|
||||
Inventory type. This can have one of three values:
|
||||
overwrite, slow, or clear. With overwrite the top
|
||||
lines of the map are overwritten with the list when
|
||||
inventory is requested or when "Which item do you wish
|
||||
to . . .? " questions are answered with a "*". How-
|
||||
ever, if the list is longer than a screenful, the
|
||||
screen is cleared. With slow, lists are displayed one
|
||||
item at a time on the top of the screen, and with
|
||||
clear, the screen is cleared, the list is displayed,
|
||||
and then the dungeon level is re-displayed. Due to
|
||||
speed considerations, clear is the default for termi-
|
||||
nals without clear-to-end-of-line capabilities.
|
||||
|
||||
name [account name]
|
||||
This is the name of your character. It is used if you
|
||||
get on the top ten scorer's list.
|
||||
|
||||
fruit [slime-mold]
|
||||
This should hold the name of a fruit that you enjoy
|
||||
eating. It is basically a whimsey that rogue uses in a
|
||||
couple of places.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
A Guide to the Dungeons of Doom USD:33-13
|
||||
|
||||
|
||||
file [~/rogue.save]
|
||||
The default file name for saving the game. If your
|
||||
phone is hung up by accident, rogue will automatically
|
||||
save the game in this file. The file name may start
|
||||
with the special character "~" which expands to be your
|
||||
home directory.
|
||||
|
||||
9. Scoring
|
||||
|
||||
Rogue usually maintains a list of the top scoring peo-
|
||||
ple or scores on your machine. Depending on how it is set
|
||||
up, it can post either the top scores or the top players.
|
||||
In the latter case, each account on the machine can post
|
||||
only one non-winning score on this list. If you score
|
||||
higher than someone else on this list, or better your previ-
|
||||
ous score on the list, you will be inserted in the proper
|
||||
place under your current name. How many scores are kept can
|
||||
also be set up by whoever installs it on your machine.
|
||||
|
||||
If you quit the game, you get out with all of your gold
|
||||
intact. If, however, you get killed in the Dungeons of
|
||||
Doom, your body is forwarded to your next-of-kin, along with
|
||||
90% of your gold; ten percent of your gold is kept by the
|
||||
Dungeons' wizard as a fee6. This should make you consider
|
||||
whether you want to take one last hit at that monster and
|
||||
possibly live, or quit and thus stop with whatever you have.
|
||||
If you quit, you do get all your gold, but if you swing and
|
||||
live, you might find more.
|
||||
|
||||
If you just want to see what the current top play-
|
||||
ers/games list is, you can type
|
||||
% @PROGRAM@ -s
|
||||
|
||||
10. Acknowledgements
|
||||
|
||||
Rogue was originally conceived of by Glenn Wichman and
|
||||
Michael Toy. Ken Arnold and Michael Toy then smoothed out
|
||||
the user interface, and added jillions of new features. We
|
||||
would like to thank Bob Arnold, Michelle Busch, Andy
|
||||
Hatcher, Kipp Hickman, Mark Horton, Daniel Jensen, Bill Joy,
|
||||
Joe Kalash, Steve Maurer, Marty McNary, Jan Miller, and
|
||||
Scott Nelson for their ideas and assistance; and also the
|
||||
teeming multitudes who graciously ignored work, school, and
|
||||
social life to play rogue and send us bugs, complaints, sug-
|
||||
gestions, and just plain flames. And also Mom.
|
||||
|
||||
|
||||
|
||||
____________________
|
||||
6 The Dungeon's wizard is named Wally the Wonder Badger.
|
||||
Invocations should be accompanied by a sizable donation.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,863 +0,0 @@
|
||||
// Copyright (c) 2016-2023 The Hush developers
|
||||
// Distributed under the GPLv3 software license, see the accompanying
|
||||
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
/*
|
||||
* Rogue definitions and variable declarations
|
||||
*
|
||||
* @(#)rogue.h 5.42 (Berkeley) 08/06/83
|
||||
*
|
||||
* Rogue: Exploring the Dungeons of Doom
|
||||
* Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
* All rights reserved.
|
||||
*
|
||||
* See the file LICENSE.TXT for full copyright and licensing information.
|
||||
*/
|
||||
|
||||
#ifndef H_ROGUE_H
|
||||
#define H_ROGUE_H
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h> /* we need va_list */
|
||||
#include <stddef.h> /* we want wchar_t */
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef BUILD_ROGUE
|
||||
#include <curses.h>
|
||||
#else
|
||||
#include "cursesd.h"
|
||||
#endif
|
||||
|
||||
#ifdef LINES
|
||||
#undef LINES
|
||||
#endif
|
||||
#ifdef COLS
|
||||
#undef COLS
|
||||
#endif
|
||||
|
||||
#define LINES 24
|
||||
#define COLS 80
|
||||
|
||||
#include "extern.h"
|
||||
|
||||
|
||||
#undef lines
|
||||
|
||||
#define NOOP(x) (x += 0)
|
||||
#define CCHAR(x) ( (char) (x & A_CHARTEXT) )
|
||||
/*
|
||||
* Maximum number of different things
|
||||
*/
|
||||
#define MAXROOMS 9
|
||||
#define MAXTHINGS 9
|
||||
#define MAXOBJ 9
|
||||
#define MAXPACK 23
|
||||
#define MAXTRAPS 10
|
||||
#define AMULETLEVEL 26
|
||||
#define NUMTHINGS 7 /* number of types of things */
|
||||
#define MAXPASS 13 /* upper limit on number of passages */
|
||||
#define NUMLINES 24
|
||||
#define NUMCOLS 80
|
||||
#define STATLINE (NUMLINES - 1)
|
||||
#define BORE_LEVEL 50
|
||||
|
||||
/*
|
||||
* return values for get functions
|
||||
*/
|
||||
#define NORM 0 /* normal exit */
|
||||
#define QUIT 1 /* quit option setting */
|
||||
#define MINUS 2 /* back up one option */
|
||||
|
||||
/*
|
||||
* inventory types
|
||||
*/
|
||||
#define INV_OVER 0
|
||||
#define INV_SLOW 1
|
||||
#define INV_CLEAR 2
|
||||
|
||||
/*
|
||||
* All the fun defines
|
||||
*/
|
||||
#define when break;case
|
||||
#define otherwise break;default
|
||||
#define until(expr) while(!(expr))
|
||||
#define next(ptr) (*ptr).l_next
|
||||
#define prev(ptr) (*ptr).l_prev
|
||||
#define winat(y,x) (moat(y,x) != NULL ? moat(y,x)->t_disguise : chat(y,x))
|
||||
#define ce(a,b) ((a).x == (b).x && (a).y == (b).y)
|
||||
#define hero player.t_pos
|
||||
#define pstats player.t_stats
|
||||
#define pack player.t_pack
|
||||
#define proom player.t_room
|
||||
#define max_hp player.t_stats.s_maxhp
|
||||
#define attach(a,b) _attach(&a,b)
|
||||
#define detach(a,b) _detach(&a,b)
|
||||
#define free_list(a) _free_list(&a)
|
||||
#undef max
|
||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
||||
#define on(thing,flag) ((bool)(((thing).t_flags & (flag)) != 0))
|
||||
#define GOLDCALC (rnd(50 + 10 * level) + 2)
|
||||
#define ISRING(h,r) (cur_ring[h] != NULL && cur_ring[h]->o_which == r)
|
||||
#define ISWEARING(r) (ISRING(LEFT, r) || ISRING(RIGHT, r))
|
||||
#define ISMULT(type) (type == POTION || type == SCROLL || type == FOOD)
|
||||
#define INDEX(y,x) (&places[((x) << 5) + (y)])
|
||||
#define chat(y,x) (places[((x) << 5) + (y)].p_ch)
|
||||
#define flat(y,x) (places[((x) << 5) + (y)].p_flags)
|
||||
#define moat(y,x) (places[((x) << 5) + (y)].p_monst)
|
||||
#define unc(cp) (cp).y, (cp).x
|
||||
#ifdef MASTER
|
||||
#define debug if (wizard) msg
|
||||
#endif
|
||||
|
||||
/*
|
||||
* things that appear on the screens
|
||||
*/
|
||||
#define PASSAGE '#'
|
||||
#define DOOR '+'
|
||||
#define FLOOR '.'
|
||||
#define PLAYER '@'
|
||||
#define TRAP '^'
|
||||
#define STAIRS '%'
|
||||
#define GOLD '*'
|
||||
#define POTION '!'
|
||||
#define SCROLL '?'
|
||||
#define MAGIC '$'
|
||||
#define FOOD ':'
|
||||
#define WEAPON ')'
|
||||
#define ARMOR ']'
|
||||
#define AMULET ','
|
||||
#define RING '='
|
||||
#define STICK '/'
|
||||
#define CALLABLE -1
|
||||
#define R_OR_S -2
|
||||
|
||||
/*
|
||||
* Various constants
|
||||
*/
|
||||
#define BEARTIME spread(3)
|
||||
#define SLEEPTIME spread(5)
|
||||
#define HOLDTIME spread(2)
|
||||
#define WANDERTIME spread(70)
|
||||
#define BEFORE spread(1)
|
||||
#define AFTER spread(2)
|
||||
#define HEALTIME 30
|
||||
#define HUHDURATION 20
|
||||
#define SEEDURATION 850
|
||||
#define HUNGERTIME 1300
|
||||
#define MORETIME 150
|
||||
#define STOMACHSIZE 2000
|
||||
#define STARVETIME 850
|
||||
#define ESCAPE 27
|
||||
#define LEFT 0
|
||||
#define RIGHT 1
|
||||
#define BOLT_LENGTH 6
|
||||
#define LAMPDIST 3
|
||||
#ifdef MASTER
|
||||
#ifndef PASSWD
|
||||
#define PASSWD "mTBellIQOsLNA"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Save against things
|
||||
*/
|
||||
#define VS_POISON 00
|
||||
#define VS_PARALYZATION 00
|
||||
#define VS_DEATH 00
|
||||
#define VS_BREATH 02
|
||||
#define VS_MAGIC 03
|
||||
|
||||
/*
|
||||
* Various flag bits
|
||||
*/
|
||||
/* flags for rooms */
|
||||
#define ISDARK 0000001 /* room is dark */
|
||||
#define ISGONE 0000002 /* room is gone (a corridor) */
|
||||
#define ISMAZE 0000004 /* room is gone (a corridor) */
|
||||
|
||||
/* flags for objects */
|
||||
#define ISCURSED 000001 /* object is cursed */
|
||||
#define ISKNOW 0000002 /* player knows details about the object */
|
||||
#define ISMISL 0000004 /* object is a missile type */
|
||||
#define ISMANY 0000010 /* object comes in groups */
|
||||
/* ISFOUND 0000020 ...is used for both objects and creatures */
|
||||
#define ISPROT 0000040 /* armor is permanently protected */
|
||||
|
||||
/* flags for creatures */
|
||||
#define CANHUH 0000001 /* creature can confuse */
|
||||
#define CANSEE 0000002 /* creature can see invisible creatures */
|
||||
#define ISBLIND 0000004 /* creature is blind */
|
||||
#define ISCANC 0000010 /* creature has special qualities cancelled */
|
||||
#define ISLEVIT 0000010 /* hero is levitating */
|
||||
#define ISFOUND 0000020 /* creature has been seen (used for objects) */
|
||||
#define ISGREED 0000040 /* creature runs to protect gold */
|
||||
#define ISHASTE 0000100 /* creature has been hastened */
|
||||
#define ISTARGET 000200 /* creature is the target of an 'f' command */
|
||||
#define ISHELD 0000400 /* creature has been held */
|
||||
#define ISHUH 0001000 /* creature is confused */
|
||||
#define ISINVIS 0002000 /* creature is invisible */
|
||||
#define ISMEAN 0004000 /* creature can wake when player enters room */
|
||||
#define ISHALU 0004000 /* hero is on acid trip */
|
||||
#define ISREGEN 0010000 /* creature can regenerate */
|
||||
#define ISRUN 0020000 /* creature is running at the player */
|
||||
#define SEEMONST 040000 /* hero can detect unseen monsters */
|
||||
#define ISFLY 0040000 /* creature can fly */
|
||||
#define ISSLOW 0100000 /* creature has been slowed */
|
||||
|
||||
/*
|
||||
* Flags for level map
|
||||
*/
|
||||
#define F_PASS 0x80 /* is a passageway */
|
||||
#define F_SEEN 0x40 /* have seen this spot before */
|
||||
#define F_DROPPED 0x20 /* object was dropped here */
|
||||
#define F_LOCKED 0x20 /* door is locked */
|
||||
#define F_REAL 0x10 /* what you see is what you get */
|
||||
#define F_PNUM 0x0f /* passage number mask */
|
||||
#define F_TMASK 0x07 /* trap number mask */
|
||||
|
||||
/*
|
||||
* Trap types
|
||||
*/
|
||||
#define T_DOOR 00
|
||||
#define T_ARROW 01
|
||||
#define T_SLEEP 02
|
||||
#define T_BEAR 03
|
||||
#define T_TELEP 04
|
||||
#define T_DART 05
|
||||
#define T_RUST 06
|
||||
#define T_MYST 07
|
||||
#define NTRAPS 8
|
||||
|
||||
/*
|
||||
* Potion types
|
||||
*/
|
||||
#define P_CONFUSE 0
|
||||
#define P_LSD 1
|
||||
#define P_POISON 2
|
||||
#define P_STRENGTH 3
|
||||
#define P_SEEINVIS 4
|
||||
#define P_HEALING 5
|
||||
#define P_MFIND 6
|
||||
#define P_TFIND 7
|
||||
#define P_RAISE 8
|
||||
#define P_XHEAL 9
|
||||
#define P_HASTE 10
|
||||
#define P_RESTORE 11
|
||||
#define P_BLIND 12
|
||||
#define P_LEVIT 13
|
||||
#define MAXPOTIONS 14
|
||||
|
||||
/*
|
||||
* Scroll types
|
||||
*/
|
||||
#define S_CONFUSE 0
|
||||
#define S_MAP 1
|
||||
#define S_HOLD 2
|
||||
#define S_SLEEP 3
|
||||
#define S_ARMOR 4
|
||||
#define S_ID_POTION 5
|
||||
#define S_ID_SCROLL 6
|
||||
#define S_ID_WEAPON 7
|
||||
#define S_ID_ARMOR 8
|
||||
#define S_ID_R_OR_S 9
|
||||
#define S_SCARE 10
|
||||
#define S_FDET 11
|
||||
#define S_TELEP 12
|
||||
#define S_ENCH 13
|
||||
#define S_CREATE 14
|
||||
#define S_REMOVE 15
|
||||
#define S_AGGR 16
|
||||
#define S_PROTECT 17
|
||||
#define MAXSCROLLS 18
|
||||
|
||||
/*
|
||||
* Weapon types
|
||||
*/
|
||||
#define MACE 0
|
||||
#define SWORD 1
|
||||
#define BOW 2
|
||||
#define ARROW 3
|
||||
#define DAGGER 4
|
||||
#define TWOSWORD 5
|
||||
#define DART 6
|
||||
#define SHIRAKEN 7
|
||||
#define SPEAR 8
|
||||
#define FLAME 9 /* fake entry for dragon breath (ick) */
|
||||
#define MAXWEAPONS 9 /* this should equal FLAME */
|
||||
|
||||
/*
|
||||
* Armor types
|
||||
*/
|
||||
#define LEATHER 0
|
||||
#define RING_MAIL 1
|
||||
#define STUDDED_LEATHER 2
|
||||
#define SCALE_MAIL 3
|
||||
#define CHAIN_MAIL 4
|
||||
#define SPLINT_MAIL 5
|
||||
#define BANDED_MAIL 6
|
||||
#define PLATE_MAIL 7
|
||||
#define MAXARMORS 8
|
||||
|
||||
/*
|
||||
* Ring types
|
||||
*/
|
||||
#define R_PROTECT 0
|
||||
#define R_ADDSTR 1
|
||||
#define R_SUSTSTR 2
|
||||
#define R_SEARCH 3
|
||||
#define R_SEEINVIS 4
|
||||
#define R_NOP 5
|
||||
#define R_AGGR 6
|
||||
#define R_ADDHIT 7
|
||||
#define R_ADDDAM 8
|
||||
#define R_REGEN 9
|
||||
#define R_DIGEST 10
|
||||
#define R_TELEPORT 11
|
||||
#define R_STEALTH 12
|
||||
#define R_SUSTARM 13
|
||||
#define MAXRINGS 14
|
||||
|
||||
/*
|
||||
* Rod/Wand/Staff types
|
||||
*/
|
||||
#define WS_LIGHT 0
|
||||
#define WS_INVIS 1
|
||||
#define WS_ELECT 2
|
||||
#define WS_FIRE 3
|
||||
#define WS_COLD 4
|
||||
#define WS_POLYMORPH 5
|
||||
#define WS_MISSILE 6
|
||||
#define WS_HASTE_M 7
|
||||
#define WS_SLOW_M 8
|
||||
#define WS_DRAIN 9
|
||||
#define WS_NOP 10
|
||||
#define WS_TELAWAY 11
|
||||
#define WS_TELTO 12
|
||||
#define WS_CANCEL 13
|
||||
#define MAXSTICKS 14
|
||||
|
||||
/*
|
||||
* Now we define the structures and types
|
||||
*/
|
||||
|
||||
#define SMALLVAL 0.000000000000001
|
||||
#define SATOSHIDEN ((uint64_t)100000000L)
|
||||
#define dstr(x) ((double)(x) / SATOSHIDEN)
|
||||
|
||||
#ifndef _BITS256
|
||||
#define _BITS256
|
||||
union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; };
|
||||
typedef union _bits256 bits256;
|
||||
#endif
|
||||
|
||||
#include "rogue_player.h" // interface to rpc
|
||||
|
||||
struct rogue_state
|
||||
{
|
||||
uint64_t seed;
|
||||
char *keystrokes,*keystrokeshex;
|
||||
uint32_t needflush,replaydone;
|
||||
int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum;
|
||||
FILE *logfp;
|
||||
struct rogue_player P;
|
||||
char buffered[10000];
|
||||
uint8_t playerdata[10000];
|
||||
};
|
||||
extern struct rogue_state globalR;
|
||||
|
||||
int rogue(int argc, char **argv, char **envp);
|
||||
void rogueiterate(struct rogue_state *rs);
|
||||
int32_t roguefname(char *fname,uint64_t seed,int32_t counter);
|
||||
int32_t flushkeystrokes(struct rogue_state *rs,int32_t waitflag);
|
||||
int32_t rogue_restorepack(struct rogue_state *rs);
|
||||
void restore_player(struct rogue_state *rs);
|
||||
int32_t rogue_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct rogue_player *player,int32_t sleepmillis);
|
||||
void rogue_bailout(struct rogue_state *rs);
|
||||
int32_t rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num);
|
||||
int32_t rogue_setplayerdata(struct rogue_state *rs,char *gametxidstr);
|
||||
|
||||
#define ROGUE_MAXTOTAL (pstats.s_str*2)
|
||||
|
||||
/*
|
||||
* Help list
|
||||
*/
|
||||
struct h_list {
|
||||
char h_ch;
|
||||
char *h_desc;
|
||||
bool h_print;
|
||||
};
|
||||
|
||||
/*
|
||||
* Coordinate data type
|
||||
*/
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
} coord;
|
||||
|
||||
typedef unsigned int str_t;
|
||||
|
||||
/*
|
||||
* Stuff about objects
|
||||
*/
|
||||
struct obj_info {
|
||||
char *oi_name;
|
||||
int oi_prob;
|
||||
int oi_worth;
|
||||
char *oi_guess;
|
||||
bool oi_know;
|
||||
};
|
||||
|
||||
/*
|
||||
* Room structure
|
||||
*/
|
||||
struct room {
|
||||
coord r_pos; /* Upper left corner */
|
||||
coord r_max; /* Size of room */
|
||||
coord r_gold; /* Where the gold is */
|
||||
int r_goldval; /* How much the gold is worth */
|
||||
short r_flags; /* info about the room */
|
||||
int r_nexits; /* Number of exits */
|
||||
coord r_exit[12]; /* Where the exits are */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure describing a fighting being
|
||||
*/
|
||||
struct stats {
|
||||
str_t s_str; /* Strength */
|
||||
int s_exp; /* Experience */
|
||||
int s_lvl; /* level of mastery */
|
||||
int s_arm; /* Armor class */
|
||||
int s_hpt; /* Hit points */
|
||||
char s_dmg[13]; /* String describing damage done */
|
||||
int s_maxhp; /* Max hit points */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure for monsters and player
|
||||
*/
|
||||
union thing {
|
||||
struct {
|
||||
union thing *_l_next, *_l_prev; /* Next pointer in link */
|
||||
coord _t_pos; /* Position */
|
||||
bool _t_turn; /* If slowed, is it a turn to move */
|
||||
char _t_type; /* What it is */
|
||||
char _t_disguise; /* What mimic looks like */
|
||||
char _t_oldch; /* Character that was where it was */
|
||||
coord *_t_dest; /* Where it is running to */
|
||||
short _t_flags; /* State word */
|
||||
struct stats _t_stats; /* Physical description */
|
||||
struct room *_t_room; /* Current room for thing */
|
||||
union thing *_t_pack; /* What the thing is carrying */
|
||||
int _t_reserved;
|
||||
} _t;
|
||||
struct {
|
||||
union thing *_l_next, *_l_prev; /* Next pointer in link */
|
||||
int _o_type; /* What kind of object it is */
|
||||
coord _o_pos; /* Where it lives on the screen */
|
||||
char *_o_text; /* What it says if you read it */
|
||||
int _o_launch; /* What you need to launch it */
|
||||
char _o_packch; /* What character it is in the pack */
|
||||
char _o_damage[8]; /* Damage if used like sword */
|
||||
char _o_hurldmg[8]; /* Damage if thrown */
|
||||
int _o_count; /* count for plural objects */
|
||||
int _o_which; /* Which object of a type it is */
|
||||
int _o_hplus; /* Plusses to hit */
|
||||
int _o_dplus; /* Plusses to damage */
|
||||
int _o_arm; /* Armor protection */
|
||||
int _o_flags; /* information about objects */
|
||||
int _o_group; /* group number for this object */
|
||||
char *_o_label; /* Label for object */
|
||||
} _o;
|
||||
};
|
||||
|
||||
typedef union thing THING;
|
||||
|
||||
#define l_next _t._l_next
|
||||
#define l_prev _t._l_prev
|
||||
#define t_pos _t._t_pos
|
||||
#define t_turn _t._t_turn
|
||||
#define t_type _t._t_type
|
||||
#define t_disguise _t._t_disguise
|
||||
#define t_oldch _t._t_oldch
|
||||
#define t_dest _t._t_dest
|
||||
#define t_flags _t._t_flags
|
||||
#define t_stats _t._t_stats
|
||||
#define t_pack _t._t_pack
|
||||
#define t_room _t._t_room
|
||||
#define t_reserved _t._t_reserved
|
||||
#define o_type _o._o_type
|
||||
#define o_pos _o._o_pos
|
||||
#define o_text _o._o_text
|
||||
#define o_launch _o._o_launch
|
||||
#define o_packch _o._o_packch
|
||||
#define o_damage _o._o_damage
|
||||
#define o_hurldmg _o._o_hurldmg
|
||||
#define o_count _o._o_count
|
||||
#define o_which _o._o_which
|
||||
#define o_hplus _o._o_hplus
|
||||
#define o_dplus _o._o_dplus
|
||||
#define o_arm _o._o_arm
|
||||
#define o_charges o_arm
|
||||
#define o_goldval o_arm
|
||||
#define o_flags _o._o_flags
|
||||
#define o_group _o._o_group
|
||||
#define o_label _o._o_label
|
||||
|
||||
/*
|
||||
* describe a place on the level map
|
||||
*/
|
||||
typedef struct {
|
||||
char p_ch;
|
||||
char p_flags;
|
||||
THING *p_monst;
|
||||
} PLACE;
|
||||
|
||||
/*
|
||||
* Array containing information on all the various types of monsters
|
||||
*/
|
||||
struct monster {
|
||||
char *m_name; /* What to call the monster */
|
||||
int m_carry; /* Probability of carrying something */
|
||||
short m_flags; /* things about the monster */
|
||||
struct stats m_stats; /* Initial stats */
|
||||
};
|
||||
|
||||
/*
|
||||
* External variables
|
||||
*/
|
||||
extern const char *tr_name[],*inv_t_name[];
|
||||
extern const int32_t a_class[], e_levels[];
|
||||
extern const struct h_list helpstr[];
|
||||
extern const char *h_names[],*m_names[];
|
||||
|
||||
|
||||
extern const struct monster origmonsters[26];
|
||||
extern const struct room origpassages[MAXPASS];
|
||||
extern const struct obj_info origthings[NUMTHINGS],origring_info[MAXRINGS],origpot_info[MAXPOTIONS],origarm_info[MAXARMORS],origscr_info[MAXSCROLLS],origws_info[MAXSTICKS],origweap_info[MAXWEAPONS + 1];
|
||||
extern struct monster monsters[26];
|
||||
extern struct room passages[MAXPASS];
|
||||
extern struct obj_info things[NUMTHINGS],ring_info[MAXRINGS],pot_info[MAXPOTIONS],arm_info[MAXARMORS],scr_info[MAXSCROLLS],weap_info[MAXWEAPONS + 1],ws_info[MAXSTICKS];
|
||||
|
||||
extern bool after, again, allscore, amulet, door_stop, fight_flush,
|
||||
firstmove, has_hit, inv_describe, jump, kamikaze,
|
||||
lower_msg, move_on, msg_esc, pack_used[],
|
||||
passgo, playing, q_comm, running, save_msg, see_floor,
|
||||
seenstairs, stat_msg, terse, to_death, tombstone;
|
||||
|
||||
extern char dir_ch, file_name[], home[], huh[],
|
||||
l_last_comm, l_last_dir, last_comm, last_dir, *Numname,
|
||||
outbuf[], *release, *s_names[], runch, take;
|
||||
extern const char *ws_made[], *r_stones[], *p_colors[], *ws_type[];
|
||||
|
||||
extern int count, food_left, hungry_state, inpack,
|
||||
inv_type, lastscore, level, max_hit, max_level, mpos,
|
||||
n_objs, no_command, no_food, no_move, noscore, ntraps, purse,
|
||||
quiet, vf_hit;
|
||||
|
||||
extern unsigned int numscores;
|
||||
|
||||
extern uint64_t seed;
|
||||
|
||||
extern WINDOW *hw;
|
||||
|
||||
extern coord delta, oldpos, stairs;
|
||||
|
||||
extern PLACE places[];
|
||||
|
||||
extern THING *cur_armor, *cur_ring[], *cur_weapon, *l_last_pick,
|
||||
*last_pick, *lvl_obj, *mlist, player;
|
||||
|
||||
|
||||
extern struct room *oldrp, rooms[];
|
||||
|
||||
extern struct stats max_stats;
|
||||
|
||||
|
||||
/*
|
||||
* Function types
|
||||
*/
|
||||
void _attach(THING **list, THING *item);
|
||||
void _detach(THING **list, THING *item);
|
||||
void _free_list(THING **ptr);
|
||||
void addmsg(struct rogue_state *rs,char *fmt, ...);
|
||||
bool add_haste(struct rogue_state *rs,bool potion);
|
||||
char add_line(struct rogue_state *rs,char *fmt, char *arg);
|
||||
void add_pack(struct rogue_state *rs,THING *obj, bool silent);
|
||||
void add_pass(void);
|
||||
void add_str(str_t *sp, int amt);
|
||||
void accnt_maze(int y, int x, int ny, int nx);
|
||||
void aggravate(struct rogue_state *rs);
|
||||
int attack(struct rogue_state *rs,THING *mp);
|
||||
void badcheck(char *name, struct obj_info *info, int bound);
|
||||
void bounce(struct rogue_state *rs,THING *weap, char *mname, bool noend);
|
||||
void call(struct rogue_state *rs);
|
||||
void call_it(struct rogue_state *rs,struct obj_info *info);
|
||||
bool cansee(struct rogue_state *rs,int y, int x);
|
||||
int center(char *str);
|
||||
void chg_str(int amt);
|
||||
void check_level(struct rogue_state *rs);
|
||||
void conn(struct rogue_state *rs,int r1, int r2);
|
||||
void command(struct rogue_state *rs);
|
||||
void create_obj(struct rogue_state *rs);
|
||||
|
||||
void current(struct rogue_state *rs,THING *cur, char *how, char *where);
|
||||
void d_level(struct rogue_state *rs);
|
||||
void death(struct rogue_state *rs,char monst);
|
||||
char death_monst(void);
|
||||
void dig(struct rogue_state *rs,int y, int x);
|
||||
void discard(THING *item);
|
||||
void discovered(struct rogue_state *rs);
|
||||
int dist(int y1, int x1, int y2, int x2);
|
||||
int dist_cp(coord *c1, coord *c2);
|
||||
int do_chase(struct rogue_state *rs,THING *th);
|
||||
void do_daemons(struct rogue_state *rs,int flag);
|
||||
void do_fuses(struct rogue_state *rs,int flag);
|
||||
void do_maze(struct rogue_state *rs,struct room *rp);
|
||||
void do_motion(struct rogue_state *rs,THING *obj, int ydelta, int xdelta);
|
||||
void do_move(struct rogue_state *rs,int dy, int dx);
|
||||
void do_passages(struct rogue_state *rs);
|
||||
void do_pot(struct rogue_state *rs,int type, bool knowit);
|
||||
void do_rooms(struct rogue_state *rs);
|
||||
void do_run(char ch);
|
||||
void do_zap(struct rogue_state *rs);
|
||||
void doadd(struct rogue_state *rs,char *fmt, va_list args);
|
||||
void door(struct room *rm, coord *cp);
|
||||
void door_open(struct rogue_state *rs,struct room *rp);
|
||||
void drain(struct rogue_state *rs);
|
||||
void draw_room(struct rogue_state *rs,struct room *rp);
|
||||
void drop(struct rogue_state *rs);
|
||||
void eat(struct rogue_state *rs);
|
||||
size_t encread(char *start, size_t size, FILE *inf);
|
||||
size_t encwrite(char *start, size_t size, FILE *outf);
|
||||
int endmsg(struct rogue_state *rs);
|
||||
void enter_room(struct rogue_state *rs,coord *cp);
|
||||
void erase_lamp(coord *pos, struct room *rp);
|
||||
int exp_add(THING *tp);
|
||||
void extinguish(void (*func)(struct rogue_state *rs,int));
|
||||
void fall(struct rogue_state *rs,THING *obj, bool pr);
|
||||
void fire_bolt(struct rogue_state *rs,coord *start, coord *dir, char *name);
|
||||
char floor_at(void);
|
||||
void flush_type(void);
|
||||
int fight(struct rogue_state *rs,coord *mp, THING *weap, bool thrown);
|
||||
void fix_stick(THING *cur);
|
||||
void fuse(void (*func)(struct rogue_state *rs,int), int arg, int time, int type);
|
||||
bool get_dir(struct rogue_state *rs);
|
||||
int gethand(struct rogue_state *rs);
|
||||
void give_pack(struct rogue_state *rs,THING *tp);
|
||||
void help(struct rogue_state *rs);
|
||||
void hit(struct rogue_state *rs,char *er, char *ee, bool noend);
|
||||
void horiz(struct room *rp, int starty);
|
||||
void leave_room(struct rogue_state *rs,coord *cp);
|
||||
void lengthen(void (*func)(struct rogue_state *rs,int), int xtime);
|
||||
void look(struct rogue_state *rs,bool wakeup);
|
||||
int hit_monster(struct rogue_state *rs,int y, int x, THING *obj);
|
||||
void identify(struct rogue_state *rs);
|
||||
void illcom(struct rogue_state *rs,int ch);
|
||||
void init_check(void);
|
||||
void init_colors(void);
|
||||
void init_materials(void);
|
||||
void init_names(void);
|
||||
void init_player(struct rogue_state *rs);
|
||||
void init_probs(void);
|
||||
void init_stones(void);
|
||||
void init_weapon(THING *weap, int which);
|
||||
bool inventory(struct rogue_state *rs,THING *list, int type);
|
||||
void invis_on(void);
|
||||
void killed(struct rogue_state *rs,THING *tp, bool pr);
|
||||
void kill_daemon(void (*func)(struct rogue_state *rs,int));
|
||||
bool lock_sc(void);
|
||||
void miss(struct rogue_state *rs,char *er, char *ee, bool noend);
|
||||
void missile(struct rogue_state *rs,int ydelta, int xdelta);
|
||||
void money(struct rogue_state *rs,int value);
|
||||
int move_monst(struct rogue_state *rs,THING *tp);
|
||||
void move_msg(struct rogue_state *rs,THING *obj);
|
||||
int msg(struct rogue_state *rs,char *fmt, ...);
|
||||
void nameit(THING *obj, const char *type, const char *which, struct obj_info *op, char *(*prfunc)(THING *));
|
||||
void new_level(struct rogue_state *rs);
|
||||
void new_monster(struct rogue_state *rs,THING *tp, char type, coord *cp);
|
||||
void numpass(int y, int x);
|
||||
void option(struct rogue_state *rs);
|
||||
void open_score(void);
|
||||
void parse_opts(char *str);
|
||||
void passnum(void);
|
||||
char *pick_color(char *col);
|
||||
int pick_one(struct rogue_state *rs,struct obj_info *info, int nitems);
|
||||
void pick_up(struct rogue_state *rs,char ch);
|
||||
void picky_inven(struct rogue_state *rs);
|
||||
void pr_spec(struct obj_info *info, int nitems);
|
||||
void pr_list(void);
|
||||
void put_bool(void *b);
|
||||
void put_inv_t(void *ip);
|
||||
void put_str(void *str);
|
||||
void put_things(struct rogue_state *rs);
|
||||
void putpass(coord *cp);
|
||||
void print_disc(struct rogue_state *rs,char);
|
||||
void quaff(struct rogue_state *rs);
|
||||
void raise_level(struct rogue_state *rs);
|
||||
char randmonster(bool wander);
|
||||
void read_scroll(struct rogue_state *rs);
|
||||
void relocate(struct rogue_state *rs,THING *th, coord *new_loc);
|
||||
void remove_mon(struct rogue_state *rs,coord *mp, THING *tp, bool waskill);
|
||||
void reset_last(void);
|
||||
bool restore(struct rogue_state *rs,char *file, char **envp);
|
||||
int ring_eat(int hand);
|
||||
void ring_on(struct rogue_state *rs);
|
||||
void ring_off(struct rogue_state *rs);
|
||||
int rnd(int range);
|
||||
int rnd_room(void);
|
||||
int roll(int number, int sides);
|
||||
int rs_save_file(struct rogue_state *rs,FILE *savef);
|
||||
int rs_restore_file(FILE *inf);
|
||||
void runto(struct rogue_state *rs,coord *runner);
|
||||
void rust_armor(struct rogue_state *rs,THING *arm);
|
||||
int save(int which);
|
||||
void save_file(struct rogue_state *rs,FILE *savef,int32_t guiflag);
|
||||
void save_game(struct rogue_state *rs);
|
||||
int save_throw(int which, THING *tp);
|
||||
void score(struct rogue_state *rs,int amount, int flags, char monst);
|
||||
void search(struct rogue_state *rs);
|
||||
void set_know(THING *obj, struct obj_info *info);
|
||||
void set_oldch(THING *tp, coord *cp);
|
||||
void setup(void);
|
||||
void shell(struct rogue_state *rs);
|
||||
bool show_floor(void);
|
||||
void show_map(void);
|
||||
void show_win(struct rogue_state *rs,char *message);
|
||||
int sign(int nm);
|
||||
int spread(int nm);
|
||||
void start_daemon(void (*func)(struct rogue_state *rs,int), int arg, int type);
|
||||
void start_score(void);
|
||||
void status(struct rogue_state *rs);
|
||||
int step_ok(int ch);
|
||||
void strucpy(char *s1, char *s2, int len);
|
||||
int swing(int at_lvl, int op_arm, int wplus);
|
||||
void take_off(struct rogue_state *rs);
|
||||
void teleport(struct rogue_state *rs);
|
||||
void total_winner(struct rogue_state *rs);
|
||||
void thunk(struct rogue_state *rs,THING *weap, char *mname, bool noend);
|
||||
void treas_room(struct rogue_state *rs);
|
||||
void turnref(void);
|
||||
void u_level(struct rogue_state *rs);
|
||||
void uncurse(THING *obj);
|
||||
void unlock_sc(void);
|
||||
void vert(struct room *rp, int startx);
|
||||
void wait_for(struct rogue_state *rs,int ch);
|
||||
THING *wake_monster(struct rogue_state *rs,int y, int x);
|
||||
void wanderer(struct rogue_state *rs);
|
||||
void waste_time(struct rogue_state *rs);
|
||||
void wear(struct rogue_state *rs);
|
||||
void whatis(struct rogue_state *rs,bool insist, int type);
|
||||
void wield(struct rogue_state *rs);
|
||||
|
||||
bool chase(THING *tp, coord *ee);
|
||||
bool diag_ok(coord *sp, coord *ep);
|
||||
bool dropcheck(struct rogue_state *rs,THING *obj);
|
||||
bool fallpos(coord *pos, coord *newpos);
|
||||
bool find_floor(struct rogue_state *rs,struct room *rp, coord *cp, int limit, bool monst);
|
||||
bool is_magic(THING *obj);
|
||||
bool is_symlink(char *sp);
|
||||
bool levit_check(struct rogue_state *rs);
|
||||
bool pack_room(struct rogue_state *rs,bool from_floor, THING *obj);
|
||||
bool roll_em(THING *thatt, THING *thdef, THING *weap, bool hurl);
|
||||
bool see_monst(THING *mp);
|
||||
bool seen_stairs(void);
|
||||
bool turn_ok(int y, int x);
|
||||
bool turn_see(struct rogue_state *rs,bool turn_off);
|
||||
bool is_current(struct rogue_state *rs,THING *obj);
|
||||
int passwd(void);
|
||||
|
||||
char be_trapped(struct rogue_state *rs,coord *tc);
|
||||
char floor_ch(void);
|
||||
char pack_char(void);
|
||||
char readchar(struct rogue_state *rs);
|
||||
char rnd_thing(void);
|
||||
|
||||
char *charge_str(THING *obj);
|
||||
char *choose_str(char *ts, char *ns);
|
||||
char *inv_name(THING *obj, bool drop);
|
||||
char *nullstr(THING *ignored);
|
||||
char *num(int n1, int n2, char type);
|
||||
char *ring_num(THING *obj);
|
||||
char *set_mname(THING *tp);
|
||||
char *vowelstr(char *str);
|
||||
|
||||
int get_bool(struct rogue_state *rs,void *vp, WINDOW *win);
|
||||
int get_inv_t(struct rogue_state *rs,void *vp, WINDOW *win);
|
||||
int get_num(struct rogue_state *rs,void *vp, WINDOW *win);
|
||||
int get_sf(struct rogue_state *rs,void *vp, WINDOW *win);
|
||||
int get_str(struct rogue_state *rs,void *vopt, WINDOW *win);
|
||||
int trip_ch(int y, int x, int ch);
|
||||
|
||||
coord *find_dest(struct rogue_state *rs,THING *tp);
|
||||
coord *rndmove(THING *who);
|
||||
|
||||
THING *find_obj(struct rogue_state *rs,int y, int x);
|
||||
THING *get_item(struct rogue_state *rs,char *purpose, int type);
|
||||
THING *leave_pack(struct rogue_state *rs,THING *obj, bool newobj, bool all);
|
||||
THING *new_item(void);
|
||||
THING *new_thing(struct rogue_state *rs);
|
||||
void end_line(struct rogue_state *rs);
|
||||
int32_t num_packitems(struct rogue_state *rs);
|
||||
int32_t rogue_total(THING *o);
|
||||
|
||||
void runners(struct rogue_state *rs,int);
|
||||
void land(struct rogue_state *rs,int);
|
||||
void visuals(struct rogue_state *rs,int);
|
||||
void come_down(struct rogue_state *rs,int);
|
||||
void stomach(struct rogue_state *rs,int);
|
||||
void nohaste(struct rogue_state *rs,int);
|
||||
void sight(struct rogue_state *rs,int);
|
||||
void unconfuse(struct rogue_state *rs,int);
|
||||
void rollwand(struct rogue_state *rs,int);
|
||||
void unsee(struct rogue_state *rs,int);
|
||||
void swander(struct rogue_state *rs,int);
|
||||
void doctor(struct rogue_state *rs,int);
|
||||
|
||||
void playit(struct rogue_state *rs);
|
||||
|
||||
struct room *roomin(struct rogue_state *rs,coord *cp);
|
||||
int32_t thing_find(THING *ptr);
|
||||
|
||||
#define MAXDAEMONS 20
|
||||
|
||||
extern struct delayed_action {
|
||||
int d_type;
|
||||
void (*d_func)(struct rogue_state *rs,int);
|
||||
int d_arg;
|
||||
int d_time;
|
||||
} d_list[MAXDAEMONS];
|
||||
|
||||
typedef struct {
|
||||
char *st_name;
|
||||
int st_value;
|
||||
} STONE;
|
||||
|
||||
extern int total;
|
||||
extern int between;
|
||||
extern int group;
|
||||
extern coord nh;
|
||||
extern const char *rainbow[];
|
||||
extern int cNCOLORS;
|
||||
extern const STONE stones[];
|
||||
extern int cNSTONES;
|
||||
extern const char *wood[];
|
||||
extern int cNWOOD;
|
||||
extern const char *metal[];
|
||||
extern int cNMETAL;
|
||||
|
||||
//extern WINDOW *stdscr,*curscr;
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,892 +0,0 @@
|
||||
.\"
|
||||
.\" @(#)rogue.me 6.2 (Berkeley) 4/28/86
|
||||
.\"
|
||||
.\" Rogue: Exploring the Dungeons of Doom
|
||||
.\" Copyright (C) 1980-1983, 1985, 1986 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" See the file LICENSE.TXT for full copyright and licensing information.
|
||||
.\"
|
||||
.ds E \s-2<ESCAPE>\s0
|
||||
.ds R \s-2<RETURN>\s0
|
||||
.ds U \s-2UNIX\s0
|
||||
.ie t .ds _ \d\(mi\u
|
||||
.el .ds _ _
|
||||
.de Cs
|
||||
\&\\$3\*(lq\\$1\*(rq\\$2
|
||||
..
|
||||
.sp 5
|
||||
.ce 1000
|
||||
.ps +4
|
||||
.vs +4p
|
||||
.b
|
||||
A Guide to the Dungeons of Doom
|
||||
.r
|
||||
.vs
|
||||
.ps
|
||||
.sp 2
|
||||
.i
|
||||
Michael C. Toy
|
||||
Kenneth C. R. C. Arnold
|
||||
.r
|
||||
.sp 2
|
||||
Computer Systems Research Group
|
||||
Department of Electrical Engineering and Computer Science
|
||||
University of California
|
||||
Berkeley, California 94720
|
||||
.sp 4
|
||||
.i ABSTRACT
|
||||
.ce 0
|
||||
.(b I F
|
||||
.bi Rogue
|
||||
is a visual CRT based fantasy game
|
||||
which runs under the \*U\(dg timesharing system.
|
||||
.(f
|
||||
\fR\(dg\*U is a trademark of Bell Laboratories\fP
|
||||
.)f
|
||||
This paper describes how to play rogue,
|
||||
and gives a few hints
|
||||
for those who might otherwise get lost in the Dungeons of Doom.
|
||||
.)b
|
||||
\".he '''\fBA Guide to the Dungeons of Doom\fP'
|
||||
\" .fo ''- % -''
|
||||
.eh 'USD:33-%''A Guide to the Dungeons of Doom'
|
||||
.oh 'A Guide to the Dungeons of Doom''USD:33-%'
|
||||
.sh 1 Introduction
|
||||
.pp
|
||||
You have just finished your years as a student at the local fighter's guild.
|
||||
After much practice and sweat you have finally completed your training
|
||||
and are ready to embark upon a perilous adventure.
|
||||
As a test of your skills,
|
||||
the local guildmasters have sent you into the Dungeons of Doom.
|
||||
Your task is to return with the Amulet of Yendor.
|
||||
Your reward for the completion of this task
|
||||
will be a full membership in the local guild.
|
||||
In addition,
|
||||
you are allowed to keep all the loot you bring back from the dungeons.
|
||||
.pp
|
||||
In preparation for your journey,
|
||||
you are given an enchanted mace,
|
||||
a bow, and a quiver of arrows
|
||||
taken from a dragon's hoard in the far off Dark Mountains.
|
||||
You are also outfitted with elf-crafted armor
|
||||
and given enough food to reach the dungeons.
|
||||
You say goodbye to family and friends for what may be the last time
|
||||
and head up the road.
|
||||
.pp
|
||||
You set out on your way to the dungeons
|
||||
and after several days of uneventful travel,
|
||||
you see the ancient ruins
|
||||
that mark the entrance to the Dungeons of Doom.
|
||||
It is late at night,
|
||||
so you make camp at the entrance
|
||||
and spend the night sleeping under the open skies.
|
||||
In the morning you gather your weapons,
|
||||
put on your armor,
|
||||
eat what is almost your last food,
|
||||
and enter the dungeons.
|
||||
.sh 1 "What is going on here?"
|
||||
.pp
|
||||
You have just begun a game of rogue.
|
||||
Your goal is to grab as much treasure as you can,
|
||||
find the Amulet of Yendor,
|
||||
and get out of the Dungeons of Doom alive.
|
||||
On the screen,
|
||||
a map of where you have been
|
||||
and what you have seen on the current dungeon level is kept.
|
||||
As you explore more of the level,
|
||||
it appears on the screen in front of you.
|
||||
.pp
|
||||
Rogue differs from most computer fantasy games in that it is screen oriented.
|
||||
Commands are all one or two keystrokes\**
|
||||
.(f
|
||||
\** As opposed to pseudo English sentences.
|
||||
.)f
|
||||
and the results of your commands
|
||||
are displayed graphically on the screen rather
|
||||
than being explained in words.\**
|
||||
.(f
|
||||
\** A minimum screen size of 24 lines by 80 columns is required.
|
||||
If the screen is larger, only the 24x80 section will be used
|
||||
for the map.
|
||||
.)f
|
||||
.pp
|
||||
Another major difference between rogue and other computer fantasy games
|
||||
is that once you have solved all the puzzles in a standard fantasy game,
|
||||
it has lost most of its excitement and it ceases to be fun.
|
||||
Rogue,
|
||||
on the other hand,
|
||||
generates a new dungeon every time you play it
|
||||
and even the author finds it an entertaining and exciting game.
|
||||
.sh 1 "What do all those things on the screen mean?"
|
||||
.pp
|
||||
In order to understand what is going on in rogue
|
||||
you have to first get some grasp of what rogue is doing with the screen.
|
||||
The rogue screen is intended
|
||||
to replace the \*(lqYou can see ...\*(rq descriptions
|
||||
of standard fantasy games.
|
||||
Figure 1 is a sample of what a rogue screen might look like.
|
||||
.(z
|
||||
.hl
|
||||
.nf
|
||||
.TS
|
||||
center;
|
||||
ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce.
|
||||
- - - - - - - - - - - -
|
||||
| . . . . . . . . . . +
|
||||
| . . @ . . . . ] . . |
|
||||
| . . . . B . . . . . |
|
||||
| . . . . . . . . . . |
|
||||
- - - - - + - - - - - -
|
||||
.TE
|
||||
|
||||
|
||||
.ce 1000
|
||||
Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0
|
||||
|
||||
Figure 1
|
||||
.ce
|
||||
.hl
|
||||
.)z
|
||||
.sh 2 "The bottom line"
|
||||
.pp
|
||||
At the bottom line of the screen
|
||||
are a few pieces of cryptic information
|
||||
describing your current status.
|
||||
Here is an explanation of what these things mean:
|
||||
.ip Level \w'Level\ \ 'u
|
||||
This number indicates how deep you have gone in the dungeon.
|
||||
It starts at one and goes up as you go deeper into the dungeon.
|
||||
.ip Gold \w'Level\ \ 'u
|
||||
The number of gold pieces you have managed to find
|
||||
and keep with you so far.
|
||||
.ip Hp \w'Level\ \ 'u
|
||||
Your current and maximum health points.
|
||||
Health points indicate how much damage you can take before you die.
|
||||
The more you get hit in a fight,
|
||||
the lower they get.
|
||||
You can regain health points by resting.
|
||||
The number in parentheses
|
||||
is the maximum number your health points can reach.
|
||||
.ip Str \w'Level\ \ 'u
|
||||
Your current strength and maximum ever strength.
|
||||
This can be any integer less than or equal to 31,
|
||||
or greater than or equal to three.
|
||||
The higher the number,
|
||||
the stronger you are.
|
||||
The number in the parentheses
|
||||
is the maximum strength you have attained so far this game.
|
||||
.ip Arm \w'Level\ \ 'u
|
||||
Your current armor protection.
|
||||
This number indicates how effective your armor is
|
||||
in stopping blows from unfriendly creatures.
|
||||
The higher this number is,
|
||||
the more effective the armor.
|
||||
.ip Exp \w'Level\ \ 'u
|
||||
These two numbers give your current experience level
|
||||
and experience points.
|
||||
As you do things,
|
||||
you gain experience points.
|
||||
At certain experience point totals,
|
||||
you gain an experience level.
|
||||
The more experienced you are,
|
||||
the better you are able to fight and to withstand magical attacks.
|
||||
.sh 2 "The top line"
|
||||
.pp
|
||||
The top line of the screen is reserved
|
||||
for printing messages that describe things
|
||||
that are impossible to represent visually.
|
||||
If you see a \*(lq--More--\*(rq on the top line,
|
||||
this means that rogue wants to print another message on the screen,
|
||||
but it wants to make certain
|
||||
that you have read the one that is there first.
|
||||
To read the next message,
|
||||
just type a space.
|
||||
.sh 2 "The rest of the screen"
|
||||
.pp
|
||||
The rest of the screen is the map of the level
|
||||
as you have explored it so far.
|
||||
Each symbol on the screen represents something.
|
||||
Here is a list of what the various symbols mean:
|
||||
.ip @
|
||||
This symbol represents you, the adventurer.
|
||||
.ip "-\^|"
|
||||
These symbols represent the walls of rooms.
|
||||
.ip +
|
||||
A door to/from a room.
|
||||
.ip .
|
||||
The floor of a room.
|
||||
.ip #
|
||||
The floor of a passage between rooms.
|
||||
.ip *
|
||||
A pile or pot of gold.
|
||||
.ip )
|
||||
A weapon of some sort.
|
||||
.ip ]
|
||||
A piece of armor.
|
||||
.ip !
|
||||
A flask containing a magic potion.
|
||||
.ip ?
|
||||
A piece of paper, usually a magic scroll.
|
||||
.ip =
|
||||
A ring with magic properties
|
||||
.ip /
|
||||
A magical staff or wand
|
||||
.ip ^
|
||||
A trap, watch out for these.
|
||||
.ip %
|
||||
A staircase to other levels
|
||||
.ip :
|
||||
A piece of food.
|
||||
.ip A-Z
|
||||
The uppercase letters
|
||||
represent the various inhabitants of the Dungeons of Doom.
|
||||
Watch out, they can be nasty and vicious.
|
||||
.sh 1 Commands
|
||||
.pp
|
||||
Commands are given to rogue by typing one or two characters.
|
||||
Most commands can be preceded by a count to repeat them
|
||||
(e.g. typing
|
||||
.Cs 10s
|
||||
will do ten searches).
|
||||
Commands for which counts make no sense
|
||||
have the count ignored.
|
||||
To cancel a count or a prefix,
|
||||
type \*E.
|
||||
The list of commands is rather long,
|
||||
but it can be read at any time during the game with the
|
||||
.Cs ?
|
||||
command.
|
||||
Here it is for reference,
|
||||
with a short explanation of each command.
|
||||
.ip ?
|
||||
The help command.
|
||||
Asks for a character to give help on.
|
||||
If you type a
|
||||
.Cs * ,
|
||||
it will list all the commands,
|
||||
otherwise it will explain what the character you typed does.
|
||||
.ip /
|
||||
This is the \*(lqWhat is that on the screen?\*(rq command.
|
||||
A
|
||||
.Cs /
|
||||
followed by any character that you see on the level,
|
||||
will tell you what that character is.
|
||||
For instance,
|
||||
typing
|
||||
.Cs /@
|
||||
will tell you that the
|
||||
.Cs @
|
||||
symbol represents you, the player.
|
||||
.ip "h, H, ^H"
|
||||
Move left.
|
||||
You move one space to the left.
|
||||
If you use upper case
|
||||
.Cs h ,
|
||||
you will continue to move left until you run into something.
|
||||
This works for all movement commands
|
||||
(e.g.
|
||||
.Cs L
|
||||
means run in direction
|
||||
.Cs l )
|
||||
If you use the \*(lqcontrol\*(rq
|
||||
.Cs h ,
|
||||
you will continue moving in the specified direction
|
||||
until you pass something interesting or run into a wall.
|
||||
You should experiment with this,
|
||||
since it is a very useful command,
|
||||
but very difficult to describe.
|
||||
This also works for all movement commands.
|
||||
.ip j
|
||||
Move down.
|
||||
.ip k
|
||||
Move up.
|
||||
.ip l
|
||||
Move right.
|
||||
.ip y
|
||||
Move diagonally up and left.
|
||||
.ip u
|
||||
Move diagonally up and right.
|
||||
.ip b
|
||||
Move diagonally down and left.
|
||||
.ip n
|
||||
Move diagonally down and right.
|
||||
.ip t
|
||||
Throw an object.
|
||||
This is a prefix command.
|
||||
When followed with a direction
|
||||
it throws an object in the specified direction.
|
||||
(e.g. type
|
||||
.Cs th
|
||||
to throw
|
||||
something to the left.)
|
||||
.ip f
|
||||
Fight until someone dies.
|
||||
When followed with a direction
|
||||
this will force you to fight the creature in that direction
|
||||
until either you or it bites the big one.
|
||||
.ip m
|
||||
Move onto something without picking it up.
|
||||
This will move you one space in the direction you specify and,
|
||||
if there is an object there you can pick up,
|
||||
it won't do it.
|
||||
.ip z
|
||||
Zap prefix.
|
||||
Point a staff or wand in a given direction
|
||||
and fire it.
|
||||
Even non-directional staves must be pointed in some direction
|
||||
to be used.
|
||||
.ip ^
|
||||
Identify trap command.
|
||||
If a trap is on your map
|
||||
and you can't remember what type it is,
|
||||
you can get rogue to remind you
|
||||
by getting next to it and typing
|
||||
.Cs ^
|
||||
followed by the direction that would move you on top of it.
|
||||
.ip s
|
||||
Search for traps and secret doors.
|
||||
Examine each space immediately adjacent to you
|
||||
for the existence of a trap or secret door.
|
||||
There is a large chance that even if there is something there,
|
||||
you won't find it,
|
||||
so you might have to search a while before you find something.
|
||||
.ip >
|
||||
Climb down a staircase to the next level.
|
||||
Not surprisingly, this can only be done if you are standing on staircase.
|
||||
.ip <
|
||||
Climb up a staircase to the level above.
|
||||
This can't be done without the Amulet of Yendor in your possession.
|
||||
.ip "."
|
||||
Rest.
|
||||
This is the \*(lqdo nothing\*(rq command.
|
||||
This is good for waiting and healing.
|
||||
.ip ,
|
||||
Pick up something.
|
||||
This picks up whatever you are currently standing on,
|
||||
if you are standing on anything at all.
|
||||
.ip i
|
||||
Inventory.
|
||||
List what you are carrying in your pack.
|
||||
.ip I
|
||||
Selective inventory.
|
||||
Tells you what a single item in your pack is.
|
||||
.ip q
|
||||
Quaff one of the potions you are carrying.
|
||||
.ip r
|
||||
Read one of the scrolls in your pack.
|
||||
.ip e
|
||||
Eat food from your pack.
|
||||
.ip w
|
||||
Wield a weapon.
|
||||
Take a weapon out of your pack and carry it for use in combat,
|
||||
replacing the one you are currently using (if any).
|
||||
.ip W
|
||||
Wear armor.
|
||||
You can only wear one suit of armor at a time.
|
||||
This takes extra time.
|
||||
.ip T
|
||||
Take armor off.
|
||||
You can't remove armor that is cursed.
|
||||
This takes extra time.
|
||||
.ip P
|
||||
Put on a ring.
|
||||
You can wear only two rings at a time
|
||||
(one on each hand).
|
||||
If you aren't wearing any rings,
|
||||
this command will ask you which hand you want to wear it on,
|
||||
otherwise, it will place it on the unused hand.
|
||||
The program assumes that you wield your sword in your right hand.
|
||||
.ip R
|
||||
Remove a ring.
|
||||
If you are only wearing one ring,
|
||||
this command takes it off.
|
||||
If you are wearing two,
|
||||
it will ask you which one you wish to remove,
|
||||
.ip d
|
||||
Drop an object.
|
||||
Take something out of your pack and leave it lying on the floor.
|
||||
Only one object can occupy each space.
|
||||
You cannot drop a cursed object at all
|
||||
if you are wielding or wearing it.
|
||||
.ip c
|
||||
Call an object something.
|
||||
If you have a type of object in your pack
|
||||
which you wish to remember something about,
|
||||
you can use the call command to give a name to that type of object.
|
||||
This is usually used when you figure out what a
|
||||
potion, scroll, ring, or staff is
|
||||
after you pick it up,
|
||||
or when you want to remember
|
||||
which of those swords in your pack you were wielding.
|
||||
.ip D
|
||||
Print out which things you've discovered something about.
|
||||
This command will ask you what type of thing you are interested in.
|
||||
If you type the character for a given type of object
|
||||
(\fIe.g.\fP
|
||||
.Cs !
|
||||
for potion)
|
||||
it will tell you which kinds of that type of object you've discovered
|
||||
(\fIi.e.\fP, figured out what they are).
|
||||
This command works for potions, scrolls, rings, and staves and wands.
|
||||
.ip o
|
||||
Examine and set options.
|
||||
This command is further explained in the section on options.
|
||||
.ip ^R
|
||||
Redraws the screen.
|
||||
Useful if spurious messages or transmission errors
|
||||
have messed up the display.
|
||||
.ip ^P
|
||||
Print last message.
|
||||
Useful when a message disappears before you can read it.
|
||||
This only repeats the last message
|
||||
that was not a mistyped command
|
||||
so that you don't loose anything by accidentally typing
|
||||
the wrong character instead of ^P.
|
||||
.ip \*E
|
||||
Cancel a command, prefix, or count.
|
||||
.ip !
|
||||
Escape to a shell for some commands.
|
||||
.ip Q
|
||||
Quit.
|
||||
Leave the game.
|
||||
.ip S
|
||||
Save the current game in a file.
|
||||
It will ask you whether you wish to use the default save file.
|
||||
.i Caveat :
|
||||
Rogue won't let you start up a copy of a saved game,
|
||||
and it removes the save file as soon as you start up a restored game.
|
||||
This is to prevent people from saving a game just before a dangerous position
|
||||
and then restarting it if they die.
|
||||
To restore a saved game,
|
||||
give the file name as an argument to rogue.
|
||||
As in
|
||||
.ti +1i
|
||||
.nf
|
||||
% rogue \fIsave\*_file\fP
|
||||
.ip
|
||||
To restart from the default save file (see below),
|
||||
run
|
||||
.ti +1i
|
||||
.nf
|
||||
% rogue \-r
|
||||
.ip v
|
||||
Prints the program version number.
|
||||
.ip )
|
||||
Print the weapon you are currently wielding
|
||||
.ip ]
|
||||
Print the armor you are currently wearing
|
||||
.ip =
|
||||
Print the rings you are currently wearing
|
||||
.ip @
|
||||
Reprint the status line on the message line
|
||||
.sh 1 Rooms
|
||||
.pp
|
||||
Rooms in the dungeons are either lit or dark.
|
||||
If you walk into a lit room,
|
||||
the entire room will be drawn on the screen as soon as you enter.
|
||||
If you walk into a dark room,
|
||||
it will only be displayed as you explore it.
|
||||
Upon leaving a room,
|
||||
all monsters inside the room
|
||||
are erased from the screen.
|
||||
In the darkness you can only see one space
|
||||
in all directions around you.
|
||||
A corridor is always dark.
|
||||
.sh 1 Fighting
|
||||
.pp
|
||||
If you see a monster and you wish to fight it,
|
||||
just attempt to run into it.
|
||||
Many times a monster you find will mind its own business
|
||||
unless you attack it.
|
||||
It is often the case that discretion is the better part of valor.
|
||||
.sh 1 "Objects you can find"
|
||||
.pp
|
||||
When you find something in the dungeon,
|
||||
it is common to want to pick the object up.
|
||||
This is accomplished in rogue by walking over the object
|
||||
(unless you use the
|
||||
.Cs m
|
||||
prefix, see above).
|
||||
If you are carrying too many things,
|
||||
the program will tell you and it won't pick up the object,
|
||||
otherwise it will add it to your pack
|
||||
and tell you what you just picked up.
|
||||
.pp
|
||||
Many of the commands that operate on objects must prompt you
|
||||
to find out which object you want to use.
|
||||
If you change your mind and don't want to do that command after all,
|
||||
just type an \*E and the command will be aborted.
|
||||
.pp
|
||||
Some objects, like armor and weapons,
|
||||
are easily differentiated.
|
||||
Others, like scrolls and potions,
|
||||
are given labels which vary according to type.
|
||||
During a game,
|
||||
any two of the same kind of object
|
||||
with the same label
|
||||
are the same type.
|
||||
However,
|
||||
the labels will vary from game to game.
|
||||
.pp
|
||||
When you use one of these labeled objects,
|
||||
if its effect is obvious,
|
||||
rogue will remember what it is for you.
|
||||
If it's effect isn't extremely obvious
|
||||
you will be asked what you want to scribble on it
|
||||
so you will recognize it later,
|
||||
or you can use the
|
||||
.Cs call
|
||||
command
|
||||
(see above).
|
||||
.sh 2 Weapons
|
||||
.pp
|
||||
Some weapons,
|
||||
like arrows,
|
||||
come in bunches,
|
||||
but most come one at a time.
|
||||
In order to use a weapon,
|
||||
you must wield it.
|
||||
To fire an arrow out of a bow,
|
||||
you must first wield the bow,
|
||||
then throw the arrow.
|
||||
You can only wield one weapon at a time,
|
||||
but you can't change weapons if the one
|
||||
you are currently wielding is cursed.
|
||||
The commands to use weapons are
|
||||
.Cs w
|
||||
(wield)
|
||||
and
|
||||
.Cs t
|
||||
(throw).
|
||||
.sh 2 Armor
|
||||
.pp
|
||||
There are various sorts of armor lying around in the dungeon.
|
||||
Some of it is enchanted,
|
||||
some is cursed,
|
||||
and some is just normal.
|
||||
Different armor types have different armor protection.
|
||||
The higher the armor protection,
|
||||
the more protection the armor affords against the blows of monsters.
|
||||
Here is a list of the various armor types and their normal armor protection:
|
||||
.(b
|
||||
.TS
|
||||
box center;
|
||||
l r.
|
||||
\ \ \fIType Protection\fP
|
||||
None 0
|
||||
Leather armor 2
|
||||
Studded leather / Ring mail 3
|
||||
Scale mail 4
|
||||
Chain mail 5
|
||||
Banded mail / Splint mail 6
|
||||
Plate mail 7
|
||||
.TE
|
||||
.)b
|
||||
.lp
|
||||
If a piece of armor is enchanted,
|
||||
its armor protection will be higher than normal.
|
||||
If a suit of armor is cursed,
|
||||
its armor protection will be lower,
|
||||
and you will not be able to remove it.
|
||||
However, not all armor with a protection that is lower than normal is cursed.
|
||||
.pp
|
||||
The commands to use weapons are
|
||||
.Cs W
|
||||
(wear)
|
||||
and
|
||||
.Cs T
|
||||
(take off).
|
||||
.sh 2 Scrolls
|
||||
.pp
|
||||
Scrolls come with titles in an unknown tongue\**.
|
||||
.(f
|
||||
\** Actually, it's a dialect spoken only by the twenty-seven members
|
||||
of a tribe in Outer Mongolia,
|
||||
but you're not supposed to
|
||||
.i know
|
||||
that.
|
||||
.)f
|
||||
After you read a scroll,
|
||||
it disappears from your pack.
|
||||
The command to use a scroll is
|
||||
.Cs r
|
||||
(read).
|
||||
.sh 2 Potions
|
||||
.pp
|
||||
Potions are labeled by the color of the liquid inside the flask.
|
||||
They disappear after being quaffed.
|
||||
The command to use a scroll is
|
||||
.Cs q
|
||||
(quaff).
|
||||
.sh 2 "Staves and Wands"
|
||||
.pp
|
||||
Staves and wands do the same kinds of things.
|
||||
Staves are identified by a type of wood;
|
||||
wands by a type of metal or bone.
|
||||
They are generally things you want to do to something
|
||||
over a long distance,
|
||||
so you must point them at what you wish to affect
|
||||
to use them.
|
||||
Some staves are not affected by the direction they are pointed, though.
|
||||
Staves come with multiple magic charges,
|
||||
the number being random,
|
||||
and when they are used up,
|
||||
the staff is just a piece of wood or metal.
|
||||
.pp
|
||||
The command to use a wand or staff is
|
||||
.Cs z
|
||||
(zap)
|
||||
.sh 2 Rings
|
||||
.pp
|
||||
Rings are very useful items,
|
||||
since they are relatively permanent magic,
|
||||
unlike the usually fleeting effects of potions, scrolls, and staves.
|
||||
Of course,
|
||||
the bad rings are also more powerful.
|
||||
Most rings also cause you to use up food more rapidly,
|
||||
the rate varying with the type of ring.
|
||||
Rings are differentiated by their stone settings.
|
||||
The commands to use rings are
|
||||
.Cs P
|
||||
(put on)
|
||||
and
|
||||
.Cs R
|
||||
(remove).
|
||||
.sh 2 Food
|
||||
.pp
|
||||
Food is necessary to keep you going.
|
||||
If you go too long without eating you will faint,
|
||||
and eventually die of starvation.
|
||||
The command to use food is
|
||||
.Cs e
|
||||
(eat).
|
||||
.sh 1 Options
|
||||
.pp
|
||||
Due to variations in personal tastes
|
||||
and conceptions of the way rogue should do things,
|
||||
there are a set of options you can set
|
||||
that cause rogue to behave in various different ways.
|
||||
.sh 2 "Setting the options"
|
||||
.pp
|
||||
There are two ways to set the options.
|
||||
The first is with the
|
||||
.Cs o
|
||||
command of rogue;
|
||||
the second is with the
|
||||
.Cs ROGUEOPTS
|
||||
environment variable\**.
|
||||
.(f
|
||||
\** On Version 6 systems,
|
||||
there is no equivalent of the ROGUEOPTS feature.
|
||||
.br
|
||||
.)f
|
||||
.br
|
||||
.sh 3 "Using the `o' command"
|
||||
.pp
|
||||
When you type
|
||||
.Cs o
|
||||
in rogue,
|
||||
it clears the screen
|
||||
and displays the current settings for all the options.
|
||||
It then places the cursor by the value of the first option
|
||||
and waits for you to type.
|
||||
You can type a \*R
|
||||
which means to go to the next option,
|
||||
a
|
||||
.Cs \-
|
||||
which means to go to the previous option,
|
||||
an \*E
|
||||
which means to return to the game,
|
||||
or you can give the option a value.
|
||||
For boolean options this merely involves typing
|
||||
.Cs t
|
||||
for true or
|
||||
.Cs f
|
||||
for false.
|
||||
For string options,
|
||||
type the new value followed by a \*R.
|
||||
.sh 3 "Using the ROGUEOPTS variable"
|
||||
.pp
|
||||
The ROGUEOPTS variable is a string
|
||||
containing a comma separated list of initial values
|
||||
for the various options.
|
||||
Boolean variables can be turned on by listing their name
|
||||
or turned off by putting a
|
||||
.Cs no
|
||||
in front of the name.
|
||||
Thus to set up an environment variable so that
|
||||
.b jump
|
||||
is on,
|
||||
.b terse
|
||||
is off,
|
||||
and the
|
||||
.b name
|
||||
is set to \*(lqBlue Meanie\*(rq,
|
||||
use the command
|
||||
.nf
|
||||
.ti +3n
|
||||
% setenv ROGUEOPTS "jump,noterse,name=Blue Meanie"\**
|
||||
.fi
|
||||
.(f
|
||||
\**
|
||||
For those of you who use the Bourne shell sh (1), the commands would be
|
||||
.in +3
|
||||
.nf
|
||||
$ ROGUEOPTS="jump,noterse,name=Blue Meanie"
|
||||
$ export ROGUEOPTS
|
||||
.fi
|
||||
.in +0
|
||||
.)f
|
||||
.sh 2 "Option list"
|
||||
.pp
|
||||
Here is a list of the options
|
||||
and an explanation of what each one is for.
|
||||
The default value for each is enclosed in square brackets.
|
||||
For character string options,
|
||||
input over fifty characters will be ignored.
|
||||
.ip "\fBterse\fP [\fI\^noterse\^\fP]"
|
||||
Useful for those who are tired of the sometimes lengthy messages of rogue.
|
||||
This is a useful option for playing on slow terminals,
|
||||
so this option defaults to
|
||||
.i terse
|
||||
if you
|
||||
are on a slow (1200 baud or under) terminal.
|
||||
.ip "\fBjump\fP [\fI\^nojump\^\fP]"
|
||||
If this option is set,
|
||||
running moves will not be displayed
|
||||
until you reach the end of the move.
|
||||
This saves considerable cpu and display time.
|
||||
This option defaults to
|
||||
.i jump
|
||||
if you are using a slow terminal.
|
||||
.ip "\fBflush\fP [\fI\^noflush\^\fP]"
|
||||
All typeahead is thrown away after each round of battle.
|
||||
This is useful for those who type far ahead
|
||||
and then watch in dismay as a Bat kills them.
|
||||
.ip "\fBseefloor\fP [\fI\^seefloor\^\fP]"
|
||||
Display the floor around you on the screen
|
||||
as you move through dark rooms.
|
||||
Due to the amount of characters generated,
|
||||
this option defaults to
|
||||
.i noseefloor
|
||||
if you are using a slow terminal.
|
||||
.ip "\fBpassgo\fP [\fI\^nopassgo\^\fP]"
|
||||
Follow turnings in passageways.
|
||||
If you run in a passage
|
||||
and you run into stone or a wall,
|
||||
rogue will see if it can turn to the right or left.
|
||||
If it can only turn one way,
|
||||
it will turn that way.
|
||||
If it can turn either or neither,
|
||||
it will stop.
|
||||
This algorithm can sometimes lead to slightly confusing occurrences
|
||||
which is why it defaults to \fInopassgo\fP.
|
||||
.ip "\fBtombstone\fP [\fI\^tombstone\^\fP]"
|
||||
Print out the tombstone at the end if you get killed.
|
||||
This is nice but slow, so you can turn it off if you like.
|
||||
.ip "\fBinven\fP [\fI\^overwrite\^\fP]"
|
||||
Inventory type.
|
||||
This can have one of three values:
|
||||
.i overwrite ,
|
||||
.i slow ,
|
||||
or
|
||||
.i clear .
|
||||
With
|
||||
.i overwrite
|
||||
the top lines of the map are overwritten
|
||||
with the list
|
||||
when inventory is requested
|
||||
or when
|
||||
\*(lqWhich item do you wish to \fB. . .\fP? \*(rq questions
|
||||
are answered with a
|
||||
.Cs * .
|
||||
However, if the list is longer than a screenful,
|
||||
the screen is cleared.
|
||||
With
|
||||
.i slow ,
|
||||
lists are displayed one item at a time on the top of the screen,
|
||||
and with
|
||||
.i clear ,
|
||||
the screen is cleared,
|
||||
the list is displayed,
|
||||
and then the dungeon level is re-displayed.
|
||||
Due to speed considerations,
|
||||
.i clear
|
||||
is the default for terminals without
|
||||
clear-to-end-of-line capabilities.
|
||||
.ip "\fBname\fP [account name]"
|
||||
This is the name of your character.
|
||||
It is used if you get on the top ten scorer's list.
|
||||
.ip "\fBfruit\fP [\fI\^slime-mold\^\fP]"
|
||||
This should hold the name of a fruit that you enjoy eating.
|
||||
It is basically a whimsey that rogue uses in a couple of places.
|
||||
.ip "\fBfile\fP [\fI\^~/rogue.save\^\fP]"
|
||||
The default file name for saving the game.
|
||||
If your phone is hung up by accident,
|
||||
rogue will automatically save the game in this file.
|
||||
The file name may start with the special character
|
||||
.Cs ~
|
||||
which expands to be your home directory.
|
||||
.sh 1 Scoring
|
||||
.pp
|
||||
Rogue usually maintains a list
|
||||
of the top scoring people or scores on your machine.
|
||||
Depending on how it is set up,
|
||||
it can post either the top scores
|
||||
or the top players.
|
||||
In the latter case,
|
||||
each account on the machine
|
||||
can post only one non-winning score on this list.
|
||||
If you score higher than someone else on this list,
|
||||
or better your previous score on the list,
|
||||
you will be inserted in the proper place
|
||||
under your current name.
|
||||
How many scores are kept
|
||||
can also be set up by whoever installs it on your machine.
|
||||
.pp
|
||||
If you quit the game, you get out with all of your gold intact.
|
||||
If, however, you get killed in the Dungeons of Doom,
|
||||
your body is forwarded to your next-of-kin,
|
||||
along with 90% of your gold;
|
||||
ten percent of your gold is kept by the Dungeons' wizard as a fee\**.
|
||||
.(f
|
||||
\** The Dungeon's wizard is named Wally the Wonder Badger.
|
||||
Invocations should be accompanied by a sizable donation.
|
||||
.)f
|
||||
This should make you consider whether you want to take one last hit
|
||||
at that monster and possibly live,
|
||||
or quit and thus stop with whatever you have.
|
||||
If you quit, you do get all your gold,
|
||||
but if you swing and live, you might find more.
|
||||
.pp
|
||||
If you just want to see what the current top players/games list is,
|
||||
you can type
|
||||
.ti +1i
|
||||
.nf
|
||||
% rogue \-s
|
||||
.br
|
||||
.sh 1 Acknowledgements
|
||||
.pp
|
||||
Rogue was originally conceived of by Glenn Wichman and Michael Toy.
|
||||
Ken Arnold and Michael Toy then smoothed out the user interface,
|
||||
and added jillions of new features.
|
||||
We would like to thank
|
||||
Bob Arnold,
|
||||
Michelle Busch,
|
||||
Andy Hatcher,
|
||||
Kipp Hickman,
|
||||
Mark Horton,
|
||||
Daniel Jensen,
|
||||
Bill Joy,
|
||||
Joe Kalash,
|
||||
Steve Maurer,
|
||||
Marty McNary,
|
||||
Jan Miller,
|
||||
and
|
||||
Scott Nelson
|
||||
for their ideas and assistance;
|
||||
and also the teeming multitudes
|
||||
who graciously ignored work, school, and social life to play rogue
|
||||
and send us bugs, complaints, suggestions, and just plain flames.
|
||||
And also Mom.
|
||||
@@ -1,892 +0,0 @@
|
||||
.\"
|
||||
.\" @(#)rogue.me 6.2 (Berkeley) 4/28/86
|
||||
.\"
|
||||
.\" Rogue: Exploring the Dungeons of Doom
|
||||
.\" Copyright (C) 1980-1983, 1985, 1986 Michael Toy, Ken Arnold and Glenn Wichman
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" See the file LICENSE.TXT for full copyright and licensing information.
|
||||
.\"
|
||||
.ds E \s-2<ESCAPE>\s0
|
||||
.ds R \s-2<RETURN>\s0
|
||||
.ds U \s-2UNIX\s0
|
||||
.ie t .ds _ \d\(mi\u
|
||||
.el .ds _ _
|
||||
.de Cs
|
||||
\&\\$3\*(lq\\$1\*(rq\\$2
|
||||
..
|
||||
.sp 5
|
||||
.ce 1000
|
||||
.ps +4
|
||||
.vs +4p
|
||||
.b
|
||||
A Guide to the Dungeons of Doom
|
||||
.r
|
||||
.vs
|
||||
.ps
|
||||
.sp 2
|
||||
.i
|
||||
Michael C. Toy
|
||||
Kenneth C. R. C. Arnold
|
||||
.r
|
||||
.sp 2
|
||||
Computer Systems Research Group
|
||||
Department of Electrical Engineering and Computer Science
|
||||
University of California
|
||||
Berkeley, California 94720
|
||||
.sp 4
|
||||
.i ABSTRACT
|
||||
.ce 0
|
||||
.(b I F
|
||||
.bi Rogue
|
||||
is a visual CRT based fantasy game
|
||||
which runs under the \*U\(dg timesharing system.
|
||||
.(f
|
||||
\fR\(dg\*U is a trademark of Bell Laboratories\fP
|
||||
.)f
|
||||
This paper describes how to play rogue,
|
||||
and gives a few hints
|
||||
for those who might otherwise get lost in the Dungeons of Doom.
|
||||
.)b
|
||||
\".he '''\fBA Guide to the Dungeons of Doom\fP'
|
||||
\" .fo ''- % -''
|
||||
.eh 'USD:33-%''A Guide to the Dungeons of Doom'
|
||||
.oh 'A Guide to the Dungeons of Doom''USD:33-%'
|
||||
.sh 1 Introduction
|
||||
.pp
|
||||
You have just finished your years as a student at the local fighter's guild.
|
||||
After much practice and sweat you have finally completed your training
|
||||
and are ready to embark upon a perilous adventure.
|
||||
As a test of your skills,
|
||||
the local guildmasters have sent you into the Dungeons of Doom.
|
||||
Your task is to return with the Amulet of Yendor.
|
||||
Your reward for the completion of this task
|
||||
will be a full membership in the local guild.
|
||||
In addition,
|
||||
you are allowed to keep all the loot you bring back from the dungeons.
|
||||
.pp
|
||||
In preparation for your journey,
|
||||
you are given an enchanted mace,
|
||||
a bow, and a quiver of arrows
|
||||
taken from a dragon's hoard in the far off Dark Mountains.
|
||||
You are also outfitted with elf-crafted armor
|
||||
and given enough food to reach the dungeons.
|
||||
You say goodbye to family and friends for what may be the last time
|
||||
and head up the road.
|
||||
.pp
|
||||
You set out on your way to the dungeons
|
||||
and after several days of uneventful travel,
|
||||
you see the ancient ruins
|
||||
that mark the entrance to the Dungeons of Doom.
|
||||
It is late at night,
|
||||
so you make camp at the entrance
|
||||
and spend the night sleeping under the open skies.
|
||||
In the morning you gather your weapons,
|
||||
put on your armor,
|
||||
eat what is almost your last food,
|
||||
and enter the dungeons.
|
||||
.sh 1 "What is going on here?"
|
||||
.pp
|
||||
You have just begun a game of rogue.
|
||||
Your goal is to grab as much treasure as you can,
|
||||
find the Amulet of Yendor,
|
||||
and get out of the Dungeons of Doom alive.
|
||||
On the screen,
|
||||
a map of where you have been
|
||||
and what you have seen on the current dungeon level is kept.
|
||||
As you explore more of the level,
|
||||
it appears on the screen in front of you.
|
||||
.pp
|
||||
Rogue differs from most computer fantasy games in that it is screen oriented.
|
||||
Commands are all one or two keystrokes\**
|
||||
.(f
|
||||
\** As opposed to pseudo English sentences.
|
||||
.)f
|
||||
and the results of your commands
|
||||
are displayed graphically on the screen rather
|
||||
than being explained in words.\**
|
||||
.(f
|
||||
\** A minimum screen size of 24 lines by 80 columns is required.
|
||||
If the screen is larger, only the 24x80 section will be used
|
||||
for the map.
|
||||
.)f
|
||||
.pp
|
||||
Another major difference between rogue and other computer fantasy games
|
||||
is that once you have solved all the puzzles in a standard fantasy game,
|
||||
it has lost most of its excitement and it ceases to be fun.
|
||||
Rogue,
|
||||
on the other hand,
|
||||
generates a new dungeon every time you play it
|
||||
and even the author finds it an entertaining and exciting game.
|
||||
.sh 1 "What do all those things on the screen mean?"
|
||||
.pp
|
||||
In order to understand what is going on in rogue
|
||||
you have to first get some grasp of what rogue is doing with the screen.
|
||||
The rogue screen is intended
|
||||
to replace the \*(lqYou can see ...\*(rq descriptions
|
||||
of standard fantasy games.
|
||||
Figure 1 is a sample of what a rogue screen might look like.
|
||||
.(z
|
||||
.hl
|
||||
.nf
|
||||
.TS
|
||||
center;
|
||||
ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce.
|
||||
- - - - - - - - - - - -
|
||||
| . . . . . . . . . . +
|
||||
| . . @ . . . . ] . . |
|
||||
| . . . . B . . . . . |
|
||||
| . . . . . . . . . . |
|
||||
- - - - - + - - - - - -
|
||||
.TE
|
||||
|
||||
|
||||
.ce 1000
|
||||
Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0
|
||||
|
||||
Figure 1
|
||||
.ce
|
||||
.hl
|
||||
.)z
|
||||
.sh 2 "The bottom line"
|
||||
.pp
|
||||
At the bottom line of the screen
|
||||
are a few pieces of cryptic information
|
||||
describing your current status.
|
||||
Here is an explanation of what these things mean:
|
||||
.ip Level \w'Level\ \ 'u
|
||||
This number indicates how deep you have gone in the dungeon.
|
||||
It starts at one and goes up as you go deeper into the dungeon.
|
||||
.ip Gold \w'Level\ \ 'u
|
||||
The number of gold pieces you have managed to find
|
||||
and keep with you so far.
|
||||
.ip Hp \w'Level\ \ 'u
|
||||
Your current and maximum health points.
|
||||
Health points indicate how much damage you can take before you die.
|
||||
The more you get hit in a fight,
|
||||
the lower they get.
|
||||
You can regain health points by resting.
|
||||
The number in parentheses
|
||||
is the maximum number your health points can reach.
|
||||
.ip Str \w'Level\ \ 'u
|
||||
Your current strength and maximum ever strength.
|
||||
This can be any integer less than or equal to 31,
|
||||
or greater than or equal to three.
|
||||
The higher the number,
|
||||
the stronger you are.
|
||||
The number in the parentheses
|
||||
is the maximum strength you have attained so far this game.
|
||||
.ip Arm \w'Level\ \ 'u
|
||||
Your current armor protection.
|
||||
This number indicates how effective your armor is
|
||||
in stopping blows from unfriendly creatures.
|
||||
The higher this number is,
|
||||
the more effective the armor.
|
||||
.ip Exp \w'Level\ \ 'u
|
||||
These two numbers give your current experience level
|
||||
and experience points.
|
||||
As you do things,
|
||||
you gain experience points.
|
||||
At certain experience point totals,
|
||||
you gain an experience level.
|
||||
The more experienced you are,
|
||||
the better you are able to fight and to withstand magical attacks.
|
||||
.sh 2 "The top line"
|
||||
.pp
|
||||
The top line of the screen is reserved
|
||||
for printing messages that describe things
|
||||
that are impossible to represent visually.
|
||||
If you see a \*(lq--More--\*(rq on the top line,
|
||||
this means that rogue wants to print another message on the screen,
|
||||
but it wants to make certain
|
||||
that you have read the one that is there first.
|
||||
To read the next message,
|
||||
just type a space.
|
||||
.sh 2 "The rest of the screen"
|
||||
.pp
|
||||
The rest of the screen is the map of the level
|
||||
as you have explored it so far.
|
||||
Each symbol on the screen represents something.
|
||||
Here is a list of what the various symbols mean:
|
||||
.ip @
|
||||
This symbol represents you, the adventurer.
|
||||
.ip "-\^|"
|
||||
These symbols represent the walls of rooms.
|
||||
.ip +
|
||||
A door to/from a room.
|
||||
.ip .
|
||||
The floor of a room.
|
||||
.ip #
|
||||
The floor of a passage between rooms.
|
||||
.ip *
|
||||
A pile or pot of gold.
|
||||
.ip )
|
||||
A weapon of some sort.
|
||||
.ip ]
|
||||
A piece of armor.
|
||||
.ip !
|
||||
A flask containing a magic potion.
|
||||
.ip ?
|
||||
A piece of paper, usually a magic scroll.
|
||||
.ip =
|
||||
A ring with magic properties
|
||||
.ip /
|
||||
A magical staff or wand
|
||||
.ip ^
|
||||
A trap, watch out for these.
|
||||
.ip %
|
||||
A staircase to other levels
|
||||
.ip :
|
||||
A piece of food.
|
||||
.ip A-Z
|
||||
The uppercase letters
|
||||
represent the various inhabitants of the Dungeons of Doom.
|
||||
Watch out, they can be nasty and vicious.
|
||||
.sh 1 Commands
|
||||
.pp
|
||||
Commands are given to rogue by typing one or two characters.
|
||||
Most commands can be preceded by a count to repeat them
|
||||
(e.g. typing
|
||||
.Cs 10s
|
||||
will do ten searches).
|
||||
Commands for which counts make no sense
|
||||
have the count ignored.
|
||||
To cancel a count or a prefix,
|
||||
type \*E.
|
||||
The list of commands is rather long,
|
||||
but it can be read at any time during the game with the
|
||||
.Cs ?
|
||||
command.
|
||||
Here it is for reference,
|
||||
with a short explanation of each command.
|
||||
.ip ?
|
||||
The help command.
|
||||
Asks for a character to give help on.
|
||||
If you type a
|
||||
.Cs * ,
|
||||
it will list all the commands,
|
||||
otherwise it will explain what the character you typed does.
|
||||
.ip /
|
||||
This is the \*(lqWhat is that on the screen?\*(rq command.
|
||||
A
|
||||
.Cs /
|
||||
followed by any character that you see on the level,
|
||||
will tell you what that character is.
|
||||
For instance,
|
||||
typing
|
||||
.Cs /@
|
||||
will tell you that the
|
||||
.Cs @
|
||||
symbol represents you, the player.
|
||||
.ip "h, H, ^H"
|
||||
Move left.
|
||||
You move one space to the left.
|
||||
If you use upper case
|
||||
.Cs h ,
|
||||
you will continue to move left until you run into something.
|
||||
This works for all movement commands
|
||||
(e.g.
|
||||
.Cs L
|
||||
means run in direction
|
||||
.Cs l )
|
||||
If you use the \*(lqcontrol\*(rq
|
||||
.Cs h ,
|
||||
you will continue moving in the specified direction
|
||||
until you pass something interesting or run into a wall.
|
||||
You should experiment with this,
|
||||
since it is a very useful command,
|
||||
but very difficult to describe.
|
||||
This also works for all movement commands.
|
||||
.ip j
|
||||
Move down.
|
||||
.ip k
|
||||
Move up.
|
||||
.ip l
|
||||
Move right.
|
||||
.ip y
|
||||
Move diagonally up and left.
|
||||
.ip u
|
||||
Move diagonally up and right.
|
||||
.ip b
|
||||
Move diagonally down and left.
|
||||
.ip n
|
||||
Move diagonally down and right.
|
||||
.ip t
|
||||
Throw an object.
|
||||
This is a prefix command.
|
||||
When followed with a direction
|
||||
it throws an object in the specified direction.
|
||||
(e.g. type
|
||||
.Cs th
|
||||
to throw
|
||||
something to the left.)
|
||||
.ip f
|
||||
Fight until someone dies.
|
||||
When followed with a direction
|
||||
this will force you to fight the creature in that direction
|
||||
until either you or it bites the big one.
|
||||
.ip m
|
||||
Move onto something without picking it up.
|
||||
This will move you one space in the direction you specify and,
|
||||
if there is an object there you can pick up,
|
||||
it won't do it.
|
||||
.ip z
|
||||
Zap prefix.
|
||||
Point a staff or wand in a given direction
|
||||
and fire it.
|
||||
Even non-directional staves must be pointed in some direction
|
||||
to be used.
|
||||
.ip ^
|
||||
Identify trap command.
|
||||
If a trap is on your map
|
||||
and you can't remember what type it is,
|
||||
you can get rogue to remind you
|
||||
by getting next to it and typing
|
||||
.Cs ^
|
||||
followed by the direction that would move you on top of it.
|
||||
.ip s
|
||||
Search for traps and secret doors.
|
||||
Examine each space immediately adjacent to you
|
||||
for the existence of a trap or secret door.
|
||||
There is a large chance that even if there is something there,
|
||||
you won't find it,
|
||||
so you might have to search a while before you find something.
|
||||
.ip >
|
||||
Climb down a staircase to the next level.
|
||||
Not surprisingly, this can only be done if you are standing on staircase.
|
||||
.ip <
|
||||
Climb up a staircase to the level above.
|
||||
This can't be done without the Amulet of Yendor in your possession.
|
||||
.ip "."
|
||||
Rest.
|
||||
This is the \*(lqdo nothing\*(rq command.
|
||||
This is good for waiting and healing.
|
||||
.ip ,
|
||||
Pick up something.
|
||||
This picks up whatever you are currently standing on,
|
||||
if you are standing on anything at all.
|
||||
.ip i
|
||||
Inventory.
|
||||
List what you are carrying in your pack.
|
||||
.ip I
|
||||
Selective inventory.
|
||||
Tells you what a single item in your pack is.
|
||||
.ip q
|
||||
Quaff one of the potions you are carrying.
|
||||
.ip r
|
||||
Read one of the scrolls in your pack.
|
||||
.ip e
|
||||
Eat food from your pack.
|
||||
.ip w
|
||||
Wield a weapon.
|
||||
Take a weapon out of your pack and carry it for use in combat,
|
||||
replacing the one you are currently using (if any).
|
||||
.ip W
|
||||
Wear armor.
|
||||
You can only wear one suit of armor at a time.
|
||||
This takes extra time.
|
||||
.ip T
|
||||
Take armor off.
|
||||
You can't remove armor that is cursed.
|
||||
This takes extra time.
|
||||
.ip P
|
||||
Put on a ring.
|
||||
You can wear only two rings at a time
|
||||
(one on each hand).
|
||||
If you aren't wearing any rings,
|
||||
this command will ask you which hand you want to wear it on,
|
||||
otherwise, it will place it on the unused hand.
|
||||
The program assumes that you wield your sword in your right hand.
|
||||
.ip R
|
||||
Remove a ring.
|
||||
If you are only wearing one ring,
|
||||
this command takes it off.
|
||||
If you are wearing two,
|
||||
it will ask you which one you wish to remove,
|
||||
.ip d
|
||||
Drop an object.
|
||||
Take something out of your pack and leave it lying on the floor.
|
||||
Only one object can occupy each space.
|
||||
You cannot drop a cursed object at all
|
||||
if you are wielding or wearing it.
|
||||
.ip c
|
||||
Call an object something.
|
||||
If you have a type of object in your pack
|
||||
which you wish to remember something about,
|
||||
you can use the call command to give a name to that type of object.
|
||||
This is usually used when you figure out what a
|
||||
potion, scroll, ring, or staff is
|
||||
after you pick it up,
|
||||
or when you want to remember
|
||||
which of those swords in your pack you were wielding.
|
||||
.ip D
|
||||
Print out which things you've discovered something about.
|
||||
This command will ask you what type of thing you are interested in.
|
||||
If you type the character for a given type of object
|
||||
(\fIe.g.\fP
|
||||
.Cs !
|
||||
for potion)
|
||||
it will tell you which kinds of that type of object you've discovered
|
||||
(\fIi.e.\fP, figured out what they are).
|
||||
This command works for potions, scrolls, rings, and staves and wands.
|
||||
.ip o
|
||||
Examine and set options.
|
||||
This command is further explained in the section on options.
|
||||
.ip ^R
|
||||
Redraws the screen.
|
||||
Useful if spurious messages or transmission errors
|
||||
have messed up the display.
|
||||
.ip ^P
|
||||
Print last message.
|
||||
Useful when a message disappears before you can read it.
|
||||
This only repeats the last message
|
||||
that was not a mistyped command
|
||||
so that you don't loose anything by accidentally typing
|
||||
the wrong character instead of ^P.
|
||||
.ip \*E
|
||||
Cancel a command, prefix, or count.
|
||||
.ip !
|
||||
Escape to a shell for some commands.
|
||||
.ip Q
|
||||
Quit.
|
||||
Leave the game.
|
||||
.ip S
|
||||
Save the current game in a file.
|
||||
It will ask you whether you wish to use the default save file.
|
||||
.i Caveat :
|
||||
Rogue won't let you start up a copy of a saved game,
|
||||
and it removes the save file as soon as you start up a restored game.
|
||||
This is to prevent people from saving a game just before a dangerous position
|
||||
and then restarting it if they die.
|
||||
To restore a saved game,
|
||||
give the file name as an argument to rogue.
|
||||
As in
|
||||
.ti +1i
|
||||
.nf
|
||||
% rogue \fIsave\*_file\fP
|
||||
.ip
|
||||
To restart from the default save file (see below),
|
||||
run
|
||||
.ti +1i
|
||||
.nf
|
||||
% rogue \-r
|
||||
.ip v
|
||||
Prints the program version number.
|
||||
.ip )
|
||||
Print the weapon you are currently wielding
|
||||
.ip ]
|
||||
Print the armor you are currently wearing
|
||||
.ip =
|
||||
Print the rings you are currently wearing
|
||||
.ip @
|
||||
Reprint the status line on the message line
|
||||
.sh 1 Rooms
|
||||
.pp
|
||||
Rooms in the dungeons are either lit or dark.
|
||||
If you walk into a lit room,
|
||||
the entire room will be drawn on the screen as soon as you enter.
|
||||
If you walk into a dark room,
|
||||
it will only be displayed as you explore it.
|
||||
Upon leaving a room,
|
||||
all monsters inside the room
|
||||
are erased from the screen.
|
||||
In the darkness you can only see one space
|
||||
in all directions around you.
|
||||
A corridor is always dark.
|
||||
.sh 1 Fighting
|
||||
.pp
|
||||
If you see a monster and you wish to fight it,
|
||||
just attempt to run into it.
|
||||
Many times a monster you find will mind its own business
|
||||
unless you attack it.
|
||||
It is often the case that discretion is the better part of valor.
|
||||
.sh 1 "Objects you can find"
|
||||
.pp
|
||||
When you find something in the dungeon,
|
||||
it is common to want to pick the object up.
|
||||
This is accomplished in rogue by walking over the object
|
||||
(unless you use the
|
||||
.Cs m
|
||||
prefix, see above).
|
||||
If you are carrying too many things,
|
||||
the program will tell you and it won't pick up the object,
|
||||
otherwise it will add it to your pack
|
||||
and tell you what you just picked up.
|
||||
.pp
|
||||
Many of the commands that operate on objects must prompt you
|
||||
to find out which object you want to use.
|
||||
If you change your mind and don't want to do that command after all,
|
||||
just type an \*E and the command will be aborted.
|
||||
.pp
|
||||
Some objects, like armor and weapons,
|
||||
are easily differentiated.
|
||||
Others, like scrolls and potions,
|
||||
are given labels which vary according to type.
|
||||
During a game,
|
||||
any two of the same kind of object
|
||||
with the same label
|
||||
are the same type.
|
||||
However,
|
||||
the labels will vary from game to game.
|
||||
.pp
|
||||
When you use one of these labeled objects,
|
||||
if its effect is obvious,
|
||||
rogue will remember what it is for you.
|
||||
If it's effect isn't extremely obvious
|
||||
you will be asked what you want to scribble on it
|
||||
so you will recognize it later,
|
||||
or you can use the
|
||||
.Cs call
|
||||
command
|
||||
(see above).
|
||||
.sh 2 Weapons
|
||||
.pp
|
||||
Some weapons,
|
||||
like arrows,
|
||||
come in bunches,
|
||||
but most come one at a time.
|
||||
In order to use a weapon,
|
||||
you must wield it.
|
||||
To fire an arrow out of a bow,
|
||||
you must first wield the bow,
|
||||
then throw the arrow.
|
||||
You can only wield one weapon at a time,
|
||||
but you can't change weapons if the one
|
||||
you are currently wielding is cursed.
|
||||
The commands to use weapons are
|
||||
.Cs w
|
||||
(wield)
|
||||
and
|
||||
.Cs t
|
||||
(throw).
|
||||
.sh 2 Armor
|
||||
.pp
|
||||
There are various sorts of armor lying around in the dungeon.
|
||||
Some of it is enchanted,
|
||||
some is cursed,
|
||||
and some is just normal.
|
||||
Different armor types have different armor protection.
|
||||
The higher the armor protection,
|
||||
the more protection the armor affords against the blows of monsters.
|
||||
Here is a list of the various armor types and their normal armor protection:
|
||||
.(b
|
||||
.TS
|
||||
box center;
|
||||
l r.
|
||||
\ \ \fIType Protection\fP
|
||||
None 0
|
||||
Leather armor 2
|
||||
Studded leather / Ring mail 3
|
||||
Scale mail 4
|
||||
Chain mail 5
|
||||
Banded mail / Splint mail 6
|
||||
Plate mail 7
|
||||
.TE
|
||||
.)b
|
||||
.lp
|
||||
If a piece of armor is enchanted,
|
||||
its armor protection will be higher than normal.
|
||||
If a suit of armor is cursed,
|
||||
its armor protection will be lower,
|
||||
and you will not be able to remove it.
|
||||
However, not all armor with a protection that is lower than normal is cursed.
|
||||
.pp
|
||||
The commands to use weapons are
|
||||
.Cs W
|
||||
(wear)
|
||||
and
|
||||
.Cs T
|
||||
(take off).
|
||||
.sh 2 Scrolls
|
||||
.pp
|
||||
Scrolls come with titles in an unknown tongue\**.
|
||||
.(f
|
||||
\** Actually, it's a dialect spoken only by the twenty-seven members
|
||||
of a tribe in Outer Mongolia,
|
||||
but you're not supposed to
|
||||
.i know
|
||||
that.
|
||||
.)f
|
||||
After you read a scroll,
|
||||
it disappears from your pack.
|
||||
The command to use a scroll is
|
||||
.Cs r
|
||||
(read).
|
||||
.sh 2 Potions
|
||||
.pp
|
||||
Potions are labeled by the color of the liquid inside the flask.
|
||||
They disappear after being quaffed.
|
||||
The command to use a scroll is
|
||||
.Cs q
|
||||
(quaff).
|
||||
.sh 2 "Staves and Wands"
|
||||
.pp
|
||||
Staves and wands do the same kinds of things.
|
||||
Staves are identified by a type of wood;
|
||||
wands by a type of metal or bone.
|
||||
They are generally things you want to do to something
|
||||
over a long distance,
|
||||
so you must point them at what you wish to affect
|
||||
to use them.
|
||||
Some staves are not affected by the direction they are pointed, though.
|
||||
Staves come with multiple magic charges,
|
||||
the number being random,
|
||||
and when they are used up,
|
||||
the staff is just a piece of wood or metal.
|
||||
.pp
|
||||
The command to use a wand or staff is
|
||||
.Cs z
|
||||
(zap)
|
||||
.sh 2 Rings
|
||||
.pp
|
||||
Rings are very useful items,
|
||||
since they are relatively permanent magic,
|
||||
unlike the usually fleeting effects of potions, scrolls, and staves.
|
||||
Of course,
|
||||
the bad rings are also more powerful.
|
||||
Most rings also cause you to use up food more rapidly,
|
||||
the rate varying with the type of ring.
|
||||
Rings are differentiated by their stone settings.
|
||||
The commands to use rings are
|
||||
.Cs P
|
||||
(put on)
|
||||
and
|
||||
.Cs R
|
||||
(remove).
|
||||
.sh 2 Food
|
||||
.pp
|
||||
Food is necessary to keep you going.
|
||||
If you go too long without eating you will faint,
|
||||
and eventually die of starvation.
|
||||
The command to use food is
|
||||
.Cs e
|
||||
(eat).
|
||||
.sh 1 Options
|
||||
.pp
|
||||
Due to variations in personal tastes
|
||||
and conceptions of the way rogue should do things,
|
||||
there are a set of options you can set
|
||||
that cause rogue to behave in various different ways.
|
||||
.sh 2 "Setting the options"
|
||||
.pp
|
||||
There are two ways to set the options.
|
||||
The first is with the
|
||||
.Cs o
|
||||
command of rogue;
|
||||
the second is with the
|
||||
.Cs ROGUEOPTS
|
||||
environment variable\**.
|
||||
.(f
|
||||
\** On Version 6 systems,
|
||||
there is no equivalent of the ROGUEOPTS feature.
|
||||
.br
|
||||
.)f
|
||||
.br
|
||||
.sh 3 "Using the `o' command"
|
||||
.pp
|
||||
When you type
|
||||
.Cs o
|
||||
in rogue,
|
||||
it clears the screen
|
||||
and displays the current settings for all the options.
|
||||
It then places the cursor by the value of the first option
|
||||
and waits for you to type.
|
||||
You can type a \*R
|
||||
which means to go to the next option,
|
||||
a
|
||||
.Cs \-
|
||||
which means to go to the previous option,
|
||||
an \*E
|
||||
which means to return to the game,
|
||||
or you can give the option a value.
|
||||
For boolean options this merely involves typing
|
||||
.Cs t
|
||||
for true or
|
||||
.Cs f
|
||||
for false.
|
||||
For string options,
|
||||
type the new value followed by a \*R.
|
||||
.sh 3 "Using the ROGUEOPTS variable"
|
||||
.pp
|
||||
The ROGUEOPTS variable is a string
|
||||
containing a comma separated list of initial values
|
||||
for the various options.
|
||||
Boolean variables can be turned on by listing their name
|
||||
or turned off by putting a
|
||||
.Cs no
|
||||
in front of the name.
|
||||
Thus to set up an environment variable so that
|
||||
.b jump
|
||||
is on,
|
||||
.b terse
|
||||
is off,
|
||||
and the
|
||||
.b name
|
||||
is set to \*(lqBlue Meanie\*(rq,
|
||||
use the command
|
||||
.nf
|
||||
.ti +3n
|
||||
% setenv ROGUEOPTS "jump,noterse,name=Blue Meanie"\**
|
||||
.fi
|
||||
.(f
|
||||
\**
|
||||
For those of you who use the Bourne shell sh (1), the commands would be
|
||||
.in +3
|
||||
.nf
|
||||
$ ROGUEOPTS="jump,noterse,name=Blue Meanie"
|
||||
$ export ROGUEOPTS
|
||||
.fi
|
||||
.in +0
|
||||
.)f
|
||||
.sh 2 "Option list"
|
||||
.pp
|
||||
Here is a list of the options
|
||||
and an explanation of what each one is for.
|
||||
The default value for each is enclosed in square brackets.
|
||||
For character string options,
|
||||
input over fifty characters will be ignored.
|
||||
.ip "\fBterse\fP [\fI\^noterse\^\fP]"
|
||||
Useful for those who are tired of the sometimes lengthy messages of rogue.
|
||||
This is a useful option for playing on slow terminals,
|
||||
so this option defaults to
|
||||
.i terse
|
||||
if you
|
||||
are on a slow (1200 baud or under) terminal.
|
||||
.ip "\fBjump\fP [\fI\^nojump\^\fP]"
|
||||
If this option is set,
|
||||
running moves will not be displayed
|
||||
until you reach the end of the move.
|
||||
This saves considerable cpu and display time.
|
||||
This option defaults to
|
||||
.i jump
|
||||
if you are using a slow terminal.
|
||||
.ip "\fBflush\fP [\fI\^noflush\^\fP]"
|
||||
All typeahead is thrown away after each round of battle.
|
||||
This is useful for those who type far ahead
|
||||
and then watch in dismay as a Bat kills them.
|
||||
.ip "\fBseefloor\fP [\fI\^seefloor\^\fP]"
|
||||
Display the floor around you on the screen
|
||||
as you move through dark rooms.
|
||||
Due to the amount of characters generated,
|
||||
this option defaults to
|
||||
.i noseefloor
|
||||
if you are using a slow terminal.
|
||||
.ip "\fBpassgo\fP [\fI\^nopassgo\^\fP]"
|
||||
Follow turnings in passageways.
|
||||
If you run in a passage
|
||||
and you run into stone or a wall,
|
||||
rogue will see if it can turn to the right or left.
|
||||
If it can only turn one way,
|
||||
it will turn that way.
|
||||
If it can turn either or neither,
|
||||
it will stop.
|
||||
This algorithm can sometimes lead to slightly confusing occurrences
|
||||
which is why it defaults to \fInopassgo\fP.
|
||||
.ip "\fBtombstone\fP [\fI\^tombstone\^\fP]"
|
||||
Print out the tombstone at the end if you get killed.
|
||||
This is nice but slow, so you can turn it off if you like.
|
||||
.ip "\fBinven\fP [\fI\^overwrite\^\fP]"
|
||||
Inventory type.
|
||||
This can have one of three values:
|
||||
.i overwrite ,
|
||||
.i slow ,
|
||||
or
|
||||
.i clear .
|
||||
With
|
||||
.i overwrite
|
||||
the top lines of the map are overwritten
|
||||
with the list
|
||||
when inventory is requested
|
||||
or when
|
||||
\*(lqWhich item do you wish to \fB. . .\fP? \*(rq questions
|
||||
are answered with a
|
||||
.Cs * .
|
||||
However, if the list is longer than a screenful,
|
||||
the screen is cleared.
|
||||
With
|
||||
.i slow ,
|
||||
lists are displayed one item at a time on the top of the screen,
|
||||
and with
|
||||
.i clear ,
|
||||
the screen is cleared,
|
||||
the list is displayed,
|
||||
and then the dungeon level is re-displayed.
|
||||
Due to speed considerations,
|
||||
.i clear
|
||||
is the default for terminals without
|
||||
clear-to-end-of-line capabilities.
|
||||
.ip "\fBname\fP [account name]"
|
||||
This is the name of your character.
|
||||
It is used if you get on the top ten scorer's list.
|
||||
.ip "\fBfruit\fP [\fI\^slime-mold\^\fP]"
|
||||
This should hold the name of a fruit that you enjoy eating.
|
||||
It is basically a whimsey that rogue uses in a couple of places.
|
||||
.ip "\fBfile\fP [\fI\^~/rogue.save\^\fP]"
|
||||
The default file name for saving the game.
|
||||
If your phone is hung up by accident,
|
||||
rogue will automatically save the game in this file.
|
||||
The file name may start with the special character
|
||||
.Cs ~
|
||||
which expands to be your home directory.
|
||||
.sh 1 Scoring
|
||||
.pp
|
||||
Rogue usually maintains a list
|
||||
of the top scoring people or scores on your machine.
|
||||
Depending on how it is set up,
|
||||
it can post either the top scores
|
||||
or the top players.
|
||||
In the latter case,
|
||||
each account on the machine
|
||||
can post only one non-winning score on this list.
|
||||
If you score higher than someone else on this list,
|
||||
or better your previous score on the list,
|
||||
you will be inserted in the proper place
|
||||
under your current name.
|
||||
How many scores are kept
|
||||
can also be set up by whoever installs it on your machine.
|
||||
.pp
|
||||
If you quit the game, you get out with all of your gold intact.
|
||||
If, however, you get killed in the Dungeons of Doom,
|
||||
your body is forwarded to your next-of-kin,
|
||||
along with 90% of your gold;
|
||||
ten percent of your gold is kept by the Dungeons' wizard as a fee\**.
|
||||
.(f
|
||||
\** The Dungeon's wizard is named Wally the Wonder Badger.
|
||||
Invocations should be accompanied by a sizable donation.
|
||||
.)f
|
||||
This should make you consider whether you want to take one last hit
|
||||
at that monster and possibly live,
|
||||
or quit and thus stop with whatever you have.
|
||||
If you quit, you do get all your gold,
|
||||
but if you swing and live, you might find more.
|
||||
.pp
|
||||
If you just want to see what the current top players/games list is,
|
||||
you can type
|
||||
.ti +1i
|
||||
.nf
|
||||
% @PROGRAM@ \-s
|
||||
.br
|
||||
.sh 1 Acknowledgements
|
||||
.pp
|
||||
Rogue was originally conceived of by Glenn Wichman and Michael Toy.
|
||||
Ken Arnold and Michael Toy then smoothed out the user interface,
|
||||
and added jillions of new features.
|
||||
We would like to thank
|
||||
Bob Arnold,
|
||||
Michelle Busch,
|
||||
Andy Hatcher,
|
||||
Kipp Hickman,
|
||||
Mark Horton,
|
||||
Daniel Jensen,
|
||||
Bill Joy,
|
||||
Joe Kalash,
|
||||
Steve Maurer,
|
||||
Marty McNary,
|
||||
Jan Miller,
|
||||
and
|
||||
Scott Nelson
|
||||
for their ideas and assistance;
|
||||
and also the teeming multitudes
|
||||
who graciously ignored work, school, and social life to play rogue
|
||||
and send us bugs, complaints, suggestions, and just plain flames.
|
||||
And also Mom.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1005 B |
@@ -1,107 +0,0 @@
|
||||
Name: rogue
|
||||
Version: 5.4.4
|
||||
Release: 1%{?dist}
|
||||
Summary: The original graphical adventure game
|
||||
|
||||
Group: Amusements/Games
|
||||
License: BSD
|
||||
URL: http://rogue.rogueforge.net/
|
||||
Source0: http://rogue.rogueforge.net/files/rogue5.4/rogue5.4.4-src.tar.gz
|
||||
Source1: rogue.desktop
|
||||
Source2: rogue.png
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
|
||||
BuildRequires: desktop-file-utils
|
||||
BuildRequires: ncurses-devel
|
||||
|
||||
%description
|
||||
The one, the only, the original graphical adventure game that spawned
|
||||
an entire genre.
|
||||
|
||||
%prep
|
||||
%setup -q -n %{name}%{version}
|
||||
|
||||
|
||||
%build
|
||||
%configure --enable-setgid=games --enable-scorefile=%{_var}/games/roguelike/rogue54.scr --enable-lockfile=%{_var}/games/roguelike/rogue54.lck
|
||||
make %{_smp_mflags}
|
||||
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
make install DESTDIR=$RPM_BUILD_ROOT
|
||||
|
||||
desktop-file-install --vendor fedora \
|
||||
--dir ${RPM_BUILD_ROOT}%{_datadir}/applications \
|
||||
%{SOURCE1}
|
||||
mkdir -p $RPM_BUILD_ROOT/%{_datadir}/icons/hicolor/32x32/apps/
|
||||
install -p -m 644 %{SOURCE2} $RPM_BUILD_ROOT/%{_datadir}/icons/hicolor/32x32/apps/
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%post
|
||||
touch --no-create %{_datadir}/icons/hicolor || :
|
||||
if [ -x %{_bindir}/gtk-update-icon-cache ]; then
|
||||
%{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
|
||||
fi
|
||||
|
||||
%postun
|
||||
touch --no-create %{_datadir}/icons/hicolor || :
|
||||
if [ -x %{_bindir}/gtk-update-icon-cache ]; then
|
||||
%{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || :
|
||||
fi
|
||||
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%attr(2755,games,games) %{_bindir}/rogue
|
||||
%{_mandir}/man6/rogue.6.gz
|
||||
%{_datadir}/applications/fedora-%{name}.desktop
|
||||
%{_datadir}/icons/hicolor/32x32/apps/rogue.png
|
||||
%dir %attr(0775,games,games) %{_var}/games/roguelike
|
||||
%config(noreplace) %attr(0664,games,games) %{_var}/games/roguelike/rogue54.scr
|
||||
%doc %{_docdir}/%{name}-%{version}
|
||||
|
||||
|
||||
%changelog
|
||||
* Sun Sep 2 2007 Wart <wart at kobold.org> 5.4.4-1
|
||||
- Update to 5.4.4
|
||||
|
||||
* Mon Aug 20 2007 Wart <wart at kobold.org> 5.4.3-1
|
||||
- Update to 5.4.3
|
||||
|
||||
* Sun Jul 15 2007 Wart <wart at kobold.org> 5.4.2-9
|
||||
- New upstream home page and download URL
|
||||
- Add patch when reading long values from the save file on 64-bit arch
|
||||
(BZ #248283)
|
||||
- Add patch removing many compiler warnings
|
||||
- Use proper version in the .desktop file
|
||||
|
||||
* Sat Mar 3 2007 Wart <wart at kobold.org> 5.4.2-8
|
||||
- Use better sourceforge download url
|
||||
- Use more precise desktop file categories
|
||||
|
||||
* Mon Aug 28 2006 Wart <wart at kobold.org> 5.4.2-7
|
||||
- Rebuild for Fedora Extras
|
||||
|
||||
* Tue May 16 2006 Wart <wart at kobold.org> 5.4.2-6
|
||||
- Added empty initial scoreboard file.
|
||||
|
||||
* Mon May 15 2006 Wart <wart at kobold.org> 5.4.2-5
|
||||
- Better setuid/setgid handling (again) (BZ #187392)
|
||||
|
||||
* Thu Mar 30 2006 Wart <wart at kobold.org> 5.4.2-4
|
||||
- Better setuid/setgid handling (BZ #187392)
|
||||
- Resize desktop icon to match directory name
|
||||
|
||||
* Mon Mar 13 2006 Wart <wart at kobold.org> 5.4.2-3
|
||||
- Added icon for .desktop file.
|
||||
|
||||
* Sun Mar 12 2006 Wart <wart at kobold.org> 5.4.2-2
|
||||
- Added missing BR: ncurses-devel, desktop-file-utils
|
||||
|
||||
* Sat Feb 25 2006 Wart <wart at kobold.org> 5.4.2-1
|
||||
- Initial spec file.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user