03.01.2010, 07:46
(
Последний раз редактировалось [HiC]TheKiller; 17.10.2011 в 00:56.
Причина: Edited the whole TUT
)
[TUT]Creating Simple Buyable Houses!
Introduction
This is pretty much just a remake of my other tutorial. The other tutorial was buggy as and was pretty damn inefficient. There are some things here that are slightly inefficient such as doing the queries on CreateHouse instead of doing one big query. I've done it like I have so people can understand it better. It uses SQLite which is inbuilt into samp. SQLite databases can be viewed using the program HERE. I've kinda rushed this tutorial because everyone kept asking me about it and the layout may not be that great, I'll eventually fix it . This tutorial also uses ZCMD because most people used it and if I did it with just strcmp on OnPlayerCommandText, people would have issues. It also uses the streamer include because it looks better and it's much better than pickups. You can download the streamer plugin HERE.
Lets get started!
Firstly, we have to include the samp include, the streamer include, the ZCMD include and create our variables. For SQLite, we must have a variable to store the database, the database result and the format string for our query. DB tag is connected to the database variable and the DBResult is connected to the query result.
pawn Код:
#include <a_samp>
#include <zcmd>
#include <streamer>
new DB:database, DBResult:queryresult, fquery[300];
pawn Код:
#define MAX_HOUSES 300 //MAX_HOUSES is currently 300.
#define MAX_HOUSES_PER_PLAYER 2 //MAX_HOUSES_PER_PLAYER is currently 2
#define databasename "housedb.db" //The Databasename is currently housedb.db
The owner variable in the enum stores the owners name if there is a owner of the house. Hname represents the house name, it must be different to all of the other house names because it is used as our ID to get information such as owner information from the database. Costprice is going to store the cost price of the house, pretty self explanatory. EnterPos[3] is going to store the house checkpoint position that will show if the player is close enough to the house. The 3 cells on the end of EnterPos[3] represents the X Position, the Y Position and the Z position. TelePos is similar to the EnterPos. It just is the position that we are sent to if we enter the house. The sellprice variable is also very self explanatory, it allows us to /sell our house later on in the tutorial for the price that you have defined. The interiors variable allows us to set the interior of the player when the teleports inside. Lastly in our enum comes the checkpointid. The textid variable stores the textid (information 3D text label that is outside of our house. I've changed it from gametext to 3D text labels in this tutorial because it looks better and it is way easier. The checkpointid stores the checkpoint number that our streamer has given us.
We will use it later to find if the player is near a house / can enter it or if the player is inside the house and enters the checkpoint to exit the house. The HouseInformation variable is pretty much all of these variable mixed together. You can have a maximum amount of houses (Defined above) with the information in the enum. The variable houseid shows us what houseid we are currently up to. It tells us what the next empty slot to store information in is. The InHouse variable pretty much tells us if the player is in a house or not. We will use this later for the /sell command and also you can use it somewhere else in your script if needed. Lastly, the InHouseCP variable is used to store if the player is in a house checkpoint or not. This is if they want to buy the house so we don't have to call multiple loops, only one.
pawn Код:
enum hinfo
{
owner[24],
Hname[100],
costprice,
Float:EnterPos[3],
Float:TelePos[3],
sellprice,
interiors,
Text3D:textid,
checkpointidx[2]
};
new HouseInformation[MAX_HOUSES][hinfo], houseid;
new InHouse[MAX_PLAYERS], InHouseCP[MAX_PLAYERS];
pawn Код:
public OnFilterScriptInit()
{
database = db_open(databasename);
db_query(database, "CREATE TABLE IF NOT EXISTS `HOUSEINFO` (housename varchar(51), houseowner varchar(25))");
return 1;
}
pawn Код:
public OnPlayerConnect(playerid)
{
InHouse[playerid] = -1;
InHouseCP[playerid] = -1;
return 1;
}
Код:
CreateHouse(HouseName[], CostP, Float:EnterX, Float:EnterY, Float:EnterZ, Float:TeleX, Float:TeleY, Float:TeleZ, SellP, Interiorx)
Код:
HouseName[] - The unique house name that you have chosen for the house (Ex: "Big Mansion5") CostP - The Cost price of the house (Ex: 500000) Float:EnterX - The X entry point of the house, it will also be where the outside checkpoint will be (Ex: 1264.53433) Float:EnterY - The Y entry point of the house, it will also be where the outside checkpoint will be (Ex: 2625.6436) Float:EnterZ - The Z entry point of the house, it will also be where the outside checkpoint will be (Ex: 200.532) Float:TeleX - The X teleport that teleports the player into the interior (Ex: 432.4234) Float:TeleY - The Y teleport that teleports the player into the interior (Ex: 4216.4152) Float:TeleZ - The Z teleport that teleports the player into the interior (Ex: 10.53672) SellP - It's the selling price that the owner can sell the house for (Ex: 200000) Interiorx - The interior ID of the interior that we will teleport to.
the SELECT statement.
Код:
SELECT [VARIABLE] FROM [TABLENAME] WHERE [ANOTHER VARIABLE] = [A VALUE] VARIABLE = houseowner TABLENAME = HOUSEINFO ANOTHER VARIABLE = housename A VALUE = our house name.
We also have to add our enter and exit checkpoints into our script using CreateDynamicCP. It creates the checkpoints and stores the IDS into checkpointid[0] for the enter CP and checkpointid[1] for the exit CP. We are also going to make our 3D text information variable with CreateDynamic3DTextLabel and store it into the variable textid.
pawn Код:
stock CreateHouse(HouseName[], CostP, Float:EnterX, Float:EnterY, Float:EnterZ, Float:TeleX, Float:TeleY, Float:TeleZ, Interiorx, SellP)
{
format(HouseInformation[houseid][Hname], 100, "%s", HouseName);
HouseInformation[houseid][costprice] = CostP;
HouseInformation[houseid][EnterPos][0] = EnterX;
HouseInformation[houseid][EnterPos][1] = EnterY;
HouseInformation[houseid][EnterPos][2] = EnterZ;
HouseInformation[houseid][TelePos][0] = TeleX;
HouseInformation[houseid][TelePos][1] = TeleY;
HouseInformation[houseid][TelePos][2] = TeleZ;
HouseInformation[houseid][sellprice] = SellP;
HouseInformation[houseid][interiors] = Interiorx;
format(fquery, sizeof(fquery), "SELECT houseowner FROM HOUSEINFO WHERE housename = '%s'", HouseName); //Formats the SELECT query
queryresult = db_query(database, fquery); //Query result variable has been used to query the string above.
if(db_num_rows(queryresult) != 0) db_get_field_assoc(queryresult, "houseowner", HouseInformation[houseid][owner], 24); //Fetches the field information db_free_result(queryresult);
HouseInformation[houseid][checkpointidx][0] = CreateDynamicCP(EnterX, EnterY, EnterZ, 1.0);
//CreateDynamicCP(Float:x, Float:y, Float:z, Float:size);
HouseInformation[houseid][checkpointidx][1] = CreateDynamicCP(TeleX, TeleY, TeleZ, 1.0, 15500000+houseid, Interiorx);
//CreateDynamicCP(Float:x, Float:y, Float:z, Float:size, worldid, interiorid);
if(!HouseInformation[houseid][owner][0]) format(fquery, sizeof(fquery), "House Name: %s \n House Price:$%d \n Sell Price: $%d", HouseName, CostP, SellP);
//If there is nothing in the owners variable, we check if the first cell doesn't have a character.
else if(HouseInformation[houseid][owner][0] != 0) format(fquery, sizeof(fquery), "House Name: %s \n Owner: %s", HouseName, HouseInformation[houseid][owner]);
//If there is something in the owners variable we check if the first cell has a character.
HouseInformation[houseid][textid] = CreateDynamic3DTextLabel(fquery, 0xFFFFFF, EnterX, EnterY, EnterZ + 0.5, 50.0);
//CreateDynamic3DTextLabel(const text[], color, Float:x, Float:y, Float:z, Float:drawdistance, attachedplayer = INVALID_PLAYER_ID, attachedvehicle = INVALID_VEHICLE_ID, testlos = 0, worldid = -1, interiorid = -1, playerid = -1, Float:distance = 100.0);
houseid ++; //We go to the next free slot in our variable.
return 1;
}
Код:
INSERT INTO [TABLENAME] ([FIELD1], [FIELD2]) VALUES ([VALUE1], [VALUE2]) TABLENAME - HOUSEINFO FIELD1 - housename FIELD2 - houseowner VALUE1 - Our housename value VALUE2 - Our houseowner value
Код:
DELETE FROM [TABLENAME] WHERE [FIELD] = [VALUE] TABLENAME - HOUSEINFO FIELD - housename VALUE - the housename
we update the 3DTextLabel's Text to being owned / not being owned. It's the same
for both of them.
pawn Код:
stock SetOwner(HouseName[], ownername[], houseids)
{
format(fquery, sizeof(fquery), "INSERT INTO `HOUSEINFO` (`housename`, `houseowner`) VALUES('%s', '%s')", HouseName, ownername);
db_query(database, fquery); //Queries the SQLite database.
format(HouseInformation[houseids][owner], 24, "%s", ownername);
format(fquery, sizeof(fquery), "House Name: %s \n Owner: %s", HouseName, HouseInformation[houseids][owner]);
UpdateDynamic3DTextLabelText(HouseInformation[houseids][textid], 0xFFFFFF, fquery); //Updates the text label.
return 1;
}
stock DeleteOwner(HouseName[], houseids)
{
format(HouseInformation[houseids][owner], 24, "%s", "\0");
format(fquery, sizeof(fquery), "DELETE FROM `HOUSEINFO` WHERE `housename` = '%s'", HouseName);
db_query(database, fquery); //Queries the SQLite database.
format(fquery, sizeof(fquery), "House Name: %s \n House Price:$%d \n Sell Price: $%d", HouseName, HouseInformation[houseids][costprice], HouseInformation[houseids][sellprice]);
UpdateDynamic3DTextLabelText(HouseInformation[houseids][textid], 0xFFFFFF, fquery); //Updates the text label.
return 1;
}
We firstly check if the player is the owner so he can instantly enter the house.
If so, we will telepor him to inside the house.
Then it checks if the owner has a owner and sends a message to the player if there
is no owner.
Then it checks if the player is in the exit checkpoint and sets his position outside
of the house.
Lastly, we use the callback OnPlayerExitDynamicCP to exit the checkpoint
pawn Код:
public OnPlayerEnterDynamicCP(playerid, checkpointid)
{
for(new x; x<houseid; x++) //Loops through all current house ids.
{
if(HouseInformation[x][checkpointidx][0] == checkpointid) //If the entry checkpoint is entry checkpoint.
{
if(InHouse[playerid] != -1)
{
InHouse[playerid] = -1; //Sets the player to outside the house.
return 1;
}
InHouseCP[playerid] = x; //Sets the InHouseCP variable.
new Pname[24]; //Pname variable.
GetPlayerName(playerid, Pname, 24); //Gets the players name.
if(HouseInformation[x][owner][0] != 0 && !strcmp(Pname, HouseInformation[x][owner][0]))
//The line above checks that the owner string has something in it, then it
//Will compare it to our current player.
{
SetPlayerPos(playerid, HouseInformation[x][TelePos][0], HouseInformation[x][TelePos][1], HouseInformation[x][TelePos][2]); //Sets players position where x = houseid.
SetPlayerInterior(playerid, HouseInformation[x][interiors]); //Sets players interior
SetPlayerVirtualWorld(playerid, 15500000 + x); //Sets the virtual world
//This is used if you want multiple houses per interior.
//Sets the inhouse variable to the house he's in.
}
if(!HouseInformation[x][owner][0]) SendClientMessage(playerid, -1, "This house is for sale /buy to buy it!");
//If there is no owner, it will send a message telling the player to buy it :).
return 1;
//We do this so the loop doesn't continue after or it tries to go for more checkpoints
//We could alternitivly use break;
}
if(HouseInformation[x][checkpointidx][1] == checkpointid) //If the player enters the house exit.
{
if(InHouse[playerid] == -1)
{
InHouse[playerid] = x;
return 1;
}
SetPlayerPos(playerid, HouseInformation[x][EnterPos][0], HouseInformation[x][EnterPos][1], HouseInformation[x][EnterPos][2]);
SetPlayerInterior(playerid, 0); //Sets the player back to interior 0 (Outside)
SetPlayerVirtualWorld(playerid, 0); //Sets the players Virtual world to 0.
return 1;
//We do this so the loop doesn't continue after or it tries to go for more checkpoints
//We could alternitivly use break;
}
}
return 1;
}
public OnPlayerLeaveDynamicCP(playerid, checkpointid)
{
if(InHouseCP[playerid] != - 1) InHouseCP[playerid] = -1; //Sets the players InHouseCP variable to 0.
return 1;
}
OK, on to the /buy command. We firstly check if the player is in the enterance of a house. We will use our InHouseCP variable that we had created earlier. After that, we go through the following checks
Is the house for sale - We check if the owner variable has a value.
Has the player got enough money - We just check the GetPlayerMoney against the variable.
Has the player got the max number of houses already - We use a simple database query which you will see below.
Then, all we need to do it use our SetOwner function above to set the houses owner and set the players position to inside the house.
Now we can get onto the /sell command which sells the house if the owner is inside
their house. We only have two errors on this one which is checking if the player is in a house and checking if they own it.
pawn Код:
CMD:buy(playerid, params)
{
if(InHouseCP[playerid] == -1) return SendClientMessage(playerid, 0xFF0000, "You are not in any house checkpoints!");
if(HouseInformation[InHouseCP[playerid]][owner][0] != 0) return SendClientMessage(playerid, 0xFF0000, "This house has a owner");
if(GetPlayerMoney(playerid) < HouseInformation[InHouseCP[playerid]][costprice]) return SendClientMessage(playerid, 0xFF0000, "You don't have enough money!"); //Player has a lack of cash!
new PlayerName[24];
GetPlayerName(playerid, PlayerName, 24);
format(fquery, sizeof(fquery), "SELECT `houseowner` FROM `HOUSEINFO` WHERE `houseowner` = '%s'", PlayerName); //Formats the SELECT query
queryresult = db_query(database, fquery); //Query result variable has been used to query the string above.
if(db_num_rows(queryresult) == MAX_HOUSES_PER_PLAYER) return SendClientMessage(playerid, 0xFF0000, "You already have the max amount of houses"); //If the player has the max houses
db_free_result(queryresult);
//This is the point where the player can buy the house
SetOwner(HouseInformation[InHouseCP[playerid]][Hname], PlayerName, InHouseCP[playerid]);
//SetOwner(HouseName[], ownername[], houseids)
SetPlayerPos(playerid, HouseInformation[InHouseCP[playerid]][TelePos][0], HouseInformation[InHouseCP[playerid]][TelePos][1], HouseInformation[InHouseCP[playerid]][TelePos][2]); //Sets players position where InHouseCP[playerid] = houseid.
SetPlayerInterior(playerid, HouseInformation[InHouseCP[playerid]][interiors]); //Sets players interior
SetPlayerVirtualWorld(playerid, 15500000 + InHouseCP[playerid]); //Sets the virtual world
GivePlayerMoney(playerid, - HouseInformation[InHouseCP[playerid]][costprice]);
GameTextForPlayer(playerid, "House ~r~Purchased!", 3000, 3); //Tells them they have purchased a house
return 1;
}
CMD:sell(playerid, params)
{
if(InHouse[playerid] == -1) return SendClientMessage(playerid, 0xFF0000, "You have to be inside your house to sell it!");
new Pname[24];
GetPlayerName(playerid, Pname, 24);
if(strcmp(HouseInformation[InHouse[playerid]][owner], Pname) != 0) return SendClientMessage(playerid, 0xFF0000, "This is not your house!");
//This is the point where the player can sell the house
DeleteOwner(HouseInformation[InHouse[playerid]][Hname], InHouse[playerid]);
//DeleteOwner(HouseName[], houseids)
GivePlayerMoney(playerid, HouseInformation[InHouse[playerid]][sellprice]);
SetPlayerPos(playerid, HouseInformation[InHouse[playerid]][EnterPos][0], HouseInformation[InHouse[playerid]][EnterPos][1], HouseInformation[InHouse[playerid]][EnterPos][2]);
SetPlayerInterior(playerid, 0); //Sets the player back to interior 0 (Outside)
SetPlayerVirtualWorld(playerid, 0); //Sets the players Virtual world to 0.
InHouseCP[playerid] = InHouse[playerid];
GameTextForPlayer(playerid, "House ~g~sold!", 3000, 3); //Tells them they have sold a house
return 1;
}
Place the following under OnFilterScriptInit / OnGameModeInit. Make sure that it's under the db_open function, not above it. The houses are located HERE.
pawn Код:
CreateHouse("TestHouse1", 1, 2317.130615, 692.398498, 11.460937, 266.857757, 305.001586, 999.148437, 2, 1);
CreateHouse("TestHouse2", 1, 2346.872802, 692.999267, 11.460937, 266.857757, 305.001586, 999.148437, 2, 1);
CreateHouse("TestHouse3", 1, 2396.482666, 691.487060, 11.453125, 2196.850341, -1204.343261, 1049.023437, 6, 1);
CreateHouse("TestHouse4", 1, 2398.476074, 735.344665, 11.460937, 2196.850341, -1204.343261, 1049.023437, 6, 1);
CreateHouse("TestHouse5", 1, 2368.863525, 733.627502, 11.460937, 2196.850341, -1204.343261, 1049.023437, 6, 1);
CreateHouse("TestHouse6", 1, 2013.253906, 731.041870, 11.453125, 266.857757, 305.001586, 999.148437, 5, 1);
CreateHouse("TestHouse7", 1, 2449.826660, 742.588806, 11.460937, 266.857757, 305.001586, 999.148437, 5, 1);
CreateHouse("TestHouse8", 1, 2449.662353, 714.210693, 11.468292, 266.857757, 305.001586, 999.148437, 5, 1);
I hope that you have learnt something from this (Not copied and pasted). Here is the full script if you are having any issues, please contact me on MSN or post here if there are any bugs.