With this newest installment of "theSystem" I'm introducing a large number of new objects and I'm taking a minute to re-release the code from articles I and II. First off is a refresher of the code from theSystem I and II. There is nothing new here and this code is being provided to correct any errors in the initial release. I've tried to handle any and all cases of the code that may be loaded to someones system if they have been following the series. The only difference is that I have been tweaking the creation percentage values in the land tables.
That being said the code discussed in this article will function if you are following the series from its beginning or just starting with this article. Lets go over all of the objects we will be covering so I don't forget and so you know what your getting yourself into. First off we have two new schemas in this release test and dice. Following that we will be going over two new additions to the sys.messages table. We have not added any records to this table in past articles but this is a great system supplied object I plan on taking full advantage of in the future. Next up we will be taking a quick look into the two metadata entries into metadata.logging table for the test and dice schemas . We will then jump into the three new tables introduced in this article one in the test schema dice, and two in the land schema currentMapIdent and movementTracker. Finally we will wrap things up with a dive into twelve new or modified procedures which makes up the bulk of this release.
I and II:
----------------------------------------------------------
--
--SFIBICH 4/20/2008
--This script was created to fix any missing object creation
--or bugs in the previous two parts of the theSystem release
--(Part I and Part II) It incorpartes the follwoing scripts:
--theSystemDBStructure.sql
--theSystem.land.sql
--theSystemInsertValues.land.sql
--theSystem.PartII.sql
--It also includes a few bug fixes to those scripts.
--Previously menttionedd scripts are now retired.
--
----------------------------------------------------------
use master
go
if not exists (select * from sys.databases where name = N'theSystem')
BEGIN
create database theSystem
print 'database [theSystem] created'
END
ELSE
BEGIN
drop database [theSystem] --Feel free to add this back in if you fee you need to.
create database theSystem
print 'database [theSystsem] already exists so it was dropped and re-created'
END
go
USE [theSystem]
go
if not exists (select * from INFORMATION_SCHEMA.SCHEMATA where schema_name='land')
BEGIN
execute ('CREATE SCHEMA [land] AUTHORIZATION [dbo]')
print 'Schema [land] created'
END
ELSE
BEGIN
print 'Schema [land] already exists'
END
GO
if not exists (select * from INFORMATION_SCHEMA.SCHEMATA where schema_name='metadata')
BEGIN
execute ('Create SCHEMA [metadata] authorization dbo')
print 'Schema [metadata] created'
END
ELSE
BEGIN
print 'Schema land already exists'
END
USE [theSystem]
go
-------------------------------------------------------
--
--Object Drops due to micro-compiling in SQL 2008 CTP 5
--
-------------------------------------------------------
USE [theSystem]
GO
IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[land].[landTypeExistanceMod_vw]'))
DROP VIEW [land].[landTypeExistanceMod_vw]
GO
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[land].[fk_landTypeExistanceMod_01]') AND parent_object_id = OBJECT_ID(N'[land].[landTypeExistanceMod]'))
ALTER TABLE [land].[landTypeExistanceMod] DROP CONSTRAINT [fk_landTypeExistanceMod_01]
GO
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[land].[fk_landTypeExistanceMod_02]') AND parent_object_id = OBJECT_ID(N'[land].[landTypeExistanceMod]'))
ALTER TABLE [land].[landTypeExistanceMod] DROP CONSTRAINT [fk_landTypeExistanceMod_02]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[land].[landTypeExistanceMod]') AND type in (N'U'))
DROP TABLE [land].[landTypeExistanceMod]
GO
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[land].[fk_map_01]') AND parent_object_id = OBJECT_ID(N'[land].[map]'))
ALTER TABLE [land].[map] DROP CONSTRAINT [fk_map_01]
GO
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[land].[fk_map_02]') AND parent_object_id = OBJECT_ID(N'[land].[map]'))
ALTER TABLE [land].[map] DROP CONSTRAINT [fk_map_02]
GO
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF__map__z_axis__0DAF0CB0]') AND type = 'D')
BEGIN
ALTER TABLE [land].[map] DROP CONSTRAINT [DF__map__z_axis__0DAF0CB0]
END
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[land].[map]') AND type in (N'U'))
DROP TABLE [land].[map]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[land].[mapType]') AND type in (N'U'))
DROP TABLE [land].[mapType]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[metadata].[logging]') AND type in (N'U'))
DROP TABLE [metadata].[logging]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[metadata].[debugLogging]') AND type in (N'U'))
DROP TABLE [metadata].[debugLogging]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[land].[landTypes]') AND type in (N'U'))
DROP TABLE [land].[landTypes]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[land].[spaceBuilder]') AND type in (N'P', N'PC'))
DROP PROCEDURE [land].[spaceBuilder]
GO
CREATE TABLE [metadata].[logging](
[schema_name] [sysname] NOT NULL,
[debug] [int] NOT NULL,
PRIMARY KEY CLUSTERED
(
[schema_name] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
go
create table land.landTypes (
land_ident int identity(1,1) primary key
,land_desc varchar (50) constraint un_landTypes_01 unique
,base_incounter_percentage decimal(10,10) not null
,base_special_incounter_percentage decimal(10,10) not null
,base_existance_percentage decimal(10,10) not null
)
go
create table land.mapType
(
map_type_ident int identity(1,1) primary key
,map_type_desc varchar(50) constraint un_mapType_01 unique
)
go
create table land.map (
map_ident int identity(1,1)
,land_ident int constraint fk_map_01 foreign key references land.landTypes (land_ident)
,x_axis int
,y_axis int
,z_axis int default(0)
,map_type int constraint fk_map_02 foreign key references land.mapType(map_type_ident)
,constraint un_map_01 unique (x_axis,y_axis,z_axis,map_type)
)
go
create table land.landTypeExistanceMod
(
land_ident int not null
constraint fk_landTypeExistanceMod_01 foreign key references land.landTypes(land_ident)
,new_land_ident int not null
constraint fk_landTypeExistanceMod_02 foreign key references land.landTypes(land_ident)
,modification_existance_percentage decimal(10,10)
,constraint pk_landTypeExistanceMod primary key (land_ident,new_land_ident)
)
go
create view land.landTypeExistanceMod_vw
as
------------------------------------------------------------------
--Created by:Steve Fibich
--Created on: 2007.11.29
--Version:1.0
--Notes: This view is to show land to land modification percentages
--
------------------------------------------------------------------
select
l.land_ident
,l.new_land_ident
,lt.land_desc
,lt2.land_desc new_land_desc
,lt2.base_existance_percentage+l.modification_existance_percentage mod_existance_percentage
,lt2.base_existance_percentage
,l.modification_existance_percentage
from
land.landTypeExistanceMod l
inner join
land.landTypes lt
on
lt.land_ident=l.land_ident
inner join
land.landTypes lt2
on
lt2.land_ident=l.new_land_ident
go
create procedure land.spaceBuilder
@max_x_axis int
,@max_y_axis int
,@debug bit = 0
,@map_type int = 1
------------------------------------------------------------------
--Created by:Steve Fibich
--Created on: 2007.11.28
--Version:1.0
--Notes: this procedure creates one space in the land.map table. A space is
--one unique x-axis, y-axis, z-axis combination
--
------------------------------------------------------------------
as
begin
set nocount on
------------------------------
--Check global debug status --
------------------------------
if @debug=0
BEGIN
select @debug=debug from metadata.logging where schema_name='land'
END
if @debug=1
BEGIN
insert into metadata.debugLogging (object_name,event_date,user_name,debug_message)
values('land.spaceBuilder',getdate(),suser_name(),'Starting proc after debug check')
END
-------------------
--Proc Variables --
-------------------
declare @land_ident int
declare @rand float
declare @percentage float
declare @ending_percentage numeric(11,10)
declare @landBuilder as table(
land_ident int primary key
,starting_percentage numeric(11,10)
,ending_percentage numeric(11,10)
)
declare @landBuilder_mep as table(
new_land_ident int primary key
,modification_existance_percentage numeric(11,10)
)
------------
--defaults--
------------
set @land_ident =0
set @ending_Percentage=0
delete @landBuilder_mep
delete @landBuilder
--------
--Code--
--------
insert into @landBuilder_mep
select
new_land_ident
,sum(modification_existance_percentage)modification_existance_percentage
from
land.map m
inner join
land.landTypeExistanceMod l
on
m.land_ident=l.land_ident
where
x_axis between @max_x_axis-1 and @max_x_axis+1
and
y_axis between @max_y_axis-1 and @max_y_axis+1
group by
new_land_ident
order by
new_land_ident
---------
--debug--
---------
if @debug=1
BEGIN
select
*
from @landBuilder_mep
insert into metadata.debugLogging (object_name,event_date,user_name,debug_message)
values('land.spaceBuilder',getdate(),suser_name(),'Inserted into @landBuilder_mep')
insert into metadata.debugLogging (object_name,event_date,user_name,debug_message)
values('land.spaceBuilder',getdate(),suser_name(),'@max_x_axis: '+cast(@max_x_axis as varchar(25))+' @max_y_axis: '+cast(@max_y_axis as varchar(25)))
END
--Fill the temp table with landtypes and the percentage range for when they will exist
--Currently no modifiers outside of making the percantage cover the entire range from 0 to 1
--This is where land modifiers will be added in later
set @land_ident=0
while (select count(*) from land.landTypes where land_ident>=@land_ident) > 0
begin
insert into @landBuilder
(land_ident,starting_percentage,ending_percentage)
select
l.land_ident
,case
when l.land_ident=1 then 0
else coalesce(@ending_percentage+.0000000001,0)
end
,case
when (coalesce(@ending_percentage,0)+coalesce(lm.modification_existance_percentage,0)+.0000000001+l.base_existance_percentage) > @ending_percentage
then coalesce(@ending_percentage,0)+.0000000001+coalesce(lm.modification_existance_percentage,0)+l.base_existance_percentage
else NULL
end
from
land.landTypes l
left outer join
@landbuilder lb
on
l.land_ident=lb.land_ident+1
left outer join
@landBuilder_mep lm
on
l.land_ident=lm.new_land_ident
where
l.land_ident=@land_ident
if (select ending_percentage from @landBuilder where land_ident=@land_ident) is not null
BEGIN
select @ending_Percentage=ending_percentage
from @landBuilder
where land_ident=@land_ident
END
set @land_ident=@land_ident+1
end
set @rand=rand()
select @percentage=1/max(ending_percentage)
from
@landBuilder
---------
--debug--
---------
if @debug=1
BEGIN
select @percentage percentage
insert into metadata.debugLogging (object_name,event_date,user_name,debug_message)
values('land.spaceBuilder',getdate(),suser_name(),'@percentage:'+cast(@percentage as varchar(25)))
select *
from
@landBuilder
select @max_x_axis x_axis, @max_y_axis y_axis
insert into metadata.debugLogging (object_name,event_date,user_name,debug_message)
values('land.spaceBuilder',getdate(),suser_name(),'@max_x_axis: '+cast(@max_x_axis as varchar(25))+' @max_y_axis: '+cast(@max_y_axis as varchar(25)))
END
insert into land.map
(land_ident,x_axis,y_axis,z_axis,map_type)
select
coalesce(land_ident,1)
,@max_x_axis
,@max_y_axis
,0
,1
from
@landBuilder
where
@rand between cast(starting_percentage as float) * @percentage and cast(ending_percentage as float)*@percentage
end
go
USE [theSystem]
GO
/****** Object: StoredProcedure [land].[landBuilder] Script Date: 11/28/2007 13:40:27 ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[land].[showmap]') AND type in (N'P', N'PC'))
DROP PROCEDURE [land].[showmap]
go
create proc land.showmap
@x_axis int =5
,@y_axis int=5
,@debug bit = 0
as
------------------------------------------------------------------
--Created by:Steve Fibich
--Created on: 2007.11.28
--Version:1.0
--Notes:
--
------------------------------------------------------------------
declare @sqlString nvarchar(1000)
set @sqlString='select y_axis
,['+cast(@x_axis-4 as nvarchar(10))+'],['+cast(@x_axis-3 as nvarchar(10))+'],['+cast(@x_axis-2 as nvarchar(10))+'],['+cast(@x_axis-1 as nvarchar(10))+'],['+cast(@x_axis as nvarchar(10))+'],['+cast(@x_axis+1 as nvarchar(10))+'],['+cast(@x_axis+2 as nvarchar(10))+'],['+cast(@x_axis+3 as nvarchar(10))+'],['+cast(@x_axis+4 as nvarchar(10))+']
from
(
select
m.x_axis
,m.y_axis
,land_desc
from
land.map m
inner join
land.landTypes lt
on
m.land_ident=lt.land_ident
where
m.y_axis between '+cast(@y_axis-4 as nvarchar(10))+' and '+cast(@y_axis+4 as nvarchar(10))+'
) as sourceTable
pivot
(
max(land_desc)
FOR x_axis in (
['+cast(@x_axis-4 as nvarchar(10))+'],['+cast(@x_axis-3 as nvarchar(10))+'],['+cast(@x_axis-2 as nvarchar(10))+'],['+cast(@x_axis-1 as nvarchar(10))+'],['+cast(@x_axis as nvarchar(10))+'],['+cast(@x_axis+1 as nvarchar(10))+'],['+cast(@x_axis+2 as nvarchar(10))+'],['+cast(@x_axis+3 as nvarchar(10))+'],['+cast(@x_axis+4 as nvarchar(10))+']
)
)as mapTable '
if @debug=1
BEGIN
select @sqlString
END
execute (@sqlString)
go
USE [theSystem]
go
--------------------------
--metadata.logging--
--------------------------
insert into metadata.logging
values('land',0)
go
insert into metadata.logging
values('metadata',0)
--------------------------
--land.landTypes Inserts--
--------------------------
go
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('light forest',.00005,.00001,.02)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('forest',.00005,.00005,.05)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('heavy forest',.0005,.00005,.02)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('aired plains',.00005,.00005,.02)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('plains',.00005,.00005,.05)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('hills',.0005,.00005,.01)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('mountains',.00005,.00005,.01)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('high mountains',.000005,.00005,.001)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('swamp',.0005,.00005,.001)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('lake',.0000005,.00005,.001)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('river',.0000005,.00005,.001)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('volcano',.0000005,.1,.000001)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('tundra',.0000005,.05,.001)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('glacier',.0000005,.05,.0001)
insert into land.landTypes
(land_desc,base_incounter_percentage,base_special_incounter_percentage,base_existance_percentage)
values('sea',.0000005,.001,.1)
go
------------------------
--land.mapType Inserts--
------------------------
insert into land.mapType
(map_type_desc)
values('map')
go
-------------------------------------
--land.landTypeExistanceMod Inserts--
-------------------------------------
delete land.landTypeExistanceMod
go
------------------------------------------------
--Standard Percentages--
--20% - same land types (rare)
--10% - same land types (or similar land types (rare))
--5% - same land types (or similar land types (rare))
--1% - similar land types
--.5% - distant land types
--.1% - unlikely land types
--Negative Values -- Very unlikely land types
------------------------------------------------
--Light Forest--
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,1,.05)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,2,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,3,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,4,.005)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,5,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,6,.005)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,7,.005)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,8,.005)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,9,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,10,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,11,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,13,.001)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,14,.001)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(1,15,-.099)
--Forest--
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,1,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,2,.05)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,3,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,4,.005)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,5,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,6,.005)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,7,.005)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,8,.005)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,9,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,10,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,11,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,13,.001)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,14,.001)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(2,15,-.099)
--Heavy Forest--
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,1,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,2,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,3,.05)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,4,.005)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,5,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,6,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,7,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,8,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,9,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,10,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,11,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,13,.001)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,14,.001)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(3,15,-.099)
--mountains--
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(7,7,.05)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(7,8,.01)
--high mountains--
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(8,7,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(8,8,.10)
--tundra--
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(13,7,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(13,8,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(13,14,.2)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(13,15,.05)
--glacier--
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(14,7,.01)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(14,8,.05)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(14,14,.05)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(14,15,.2)
--Everthing vs. Sea--
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(4,15,-.099)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(5,15,-.099)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(6,15,-.099)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(7,15,-.099)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(8,15,-.099)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(9,15,-.099)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(10,15,-.099)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(11,15,-.025)
--Sea--
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,1,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,2,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,3,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,4,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,5,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,6,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,7,.001)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,8,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,9,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,10,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,11,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,12,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,13,.001)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,14,-.1)
insert into land.landTypeExistanceMod
(land_ident,new_land_ident,modification_existance_percentage)
values(15,15,.50)
go
use theSystem
go
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[metadata].[DebugLogging]') AND type in (N'U'))
DROP TABLE [metadata].[DebugLogging]
GO
create table metadata.debugLogging
(
object_name sysname not null
,event_date datetime not null
,user_name sysname not null
,debug_message varchar(2500) null
)
go
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[land].[landBuilder]') AND type in (N'P', N'PC'))
DROP PROCEDURE [land].[landBuilder]
go
create procedure land.landBuilder
@debug bit = 0
------------------------------------------------------------------
--Created by:Steve Fibich
--Created on: 2007.11.28
--Version:1.0
--Notes: this procedure will add one row and one column worth of map values
--to the land.map table. Currently this proc does not take into consideration
--any land creation modifiers
--
------------------------------------------------------------------
as
begin
set nocount on
------------------------------
--Check global debug status --
------------------------------
if @debug=0
BEGIN
select @debug=debug from metadata.logging where schema_name='land'
END
--------------
-- Vars --
--------------
declare @map_type int
declare @counter int
declare @z_axis int
declare @max_x_axis int
declare @max_y_axis int
declare @min_x_axis int
declare @min_y_axis int
declare @x_axis int
declare @y_axis int
------------
--defaults--
------------
set @map_type =1
select
@max_x_axis=coalesce(max(x_axis),1)
,@max_y_axis=coalesce(max(y_axis),1)
,@min_x_axis=coalesce(min(x_axis),1)
,@min_y_axis=coalesce(min(y_axis),1)
,@z_axis=0
from
land.map
if @debug=1
BEGIN
insert into metadata.debuglogging(object_name,event_date,user_name,debug_message)
values('land.landBuilder',getdate(),suser_name(),'@max_x_axis:' +cast(@max_x_axis as varchar(11))
+' @max_y_axis:'+cast(@max_y_axis as varchar(11))+' @min_x_axis:'+cast(@min_x_axis as varchar(11))
+' @min_y_axis:'+cast(@min_y_axis as varchar(11))+' @z_axis:'+cast(@z_axis as varchar(11)))
END
--------
--Code--
--------
set @counter=@min_y_axis
set @x_axis=@max_x_axis+1
while @counter <= @max_y_axis+1
begin
if (select map_ident from land.map where x_axis=@x_axis and y_axis=@counter) is null
BEGIN
executeland.spaceBuilder @x_axis,@counter,@debug,@map_type
END
---------
--debug--
---------
if @debug=1
BEGIN
insert into metadata.debuglogging(object_name,event_date,user_name,debug_message)
values('land.landBuilder',getdate(),suser_name(),'@x_axis:' +cast(@x_axis as varchar(11))+
'@y_axis:' +cast(@counter as varchar(11)))
END
set @counter=@counter+1
end
set @counter=@min_x_axis
set @y_axis=@max_y_axis+1
while @counter <= @max_x_axis
begin
if (select map_ident from land.map where x_axis=@counter and y_axis=@y_axis) is null
BEGIN
executeland.spaceBuilder @counter,@y_axis,@debug,@map_type
END
---------
--debug--
---------
if @debug=1
BEGIN
insert into metadata.debuglogging(object_name,event_date,user_name,debug_message)
values('land.landBuilder',getdate(),suser_name(),'@x_axis:' +cast(@counter as varchar(11))+
'@y_axis:' +cast(@y_axis as varchar(11)))
END
set @counter=@counter+1
end
end
go
----------------------------------------------------------
--
--The code below will insert values into the land.map table
--and show it in both a land.showmap format and a simple
--select from the land.map table
--
----------------------------------------------------------
declare @mapCount int
set @mapCount=1
delete land.map
--initialize the land.map table...this first value can make a difference
insert into land.map
(land_ident,x_axis,y_axis,z_axis,map_type)
values(1,1,1,0,1)
while @mapCount < 100
BEGIN
execute land.landBuilder @debug=0
set @mapCount=@mapCount+1
END
execute land.showmap 60,60
------------------------------------------------------
--
--Shows how much of the map is made of Sea tiles
--This can be a large issue if the land percentages
--are not set correctly
--
------------------------------------------------------
select
x_axis
,MIN(y_axis) starting_sea
,MAX(y_axis) ending_sea
,COUNT(y_axis) total_sea_tiles
,case MAX(y_axis)-MIN(y_axis)+1-COUNT(y_axis)
when 0 then 'All sea'
else 'some sea'
end
from land.map
where land_ident=15
group by x_axis
order by x_axis
So lets dig into this releases objects starting with two new schema's test and dice and the reasons for creating them. I'm not just putting all my objects into dbo for particular reasons. Dividing my objects across schema makes it easier for me to logically divide functionality into distinct units. This may not be a big deal when there is only one database developer but it comes in handy when you are working on a team.
Secondly it allows me to search for objects with in a schema by knowing what the object does but maybe not its name. I realize this may not seem like a very handy thing to have when all your objects are in say sys.tables but I find myself knowing what I'm looking for but not the exact name. So then if I'm looking for a table dealing with land types I only have to search sys.tables where schema_id = (select schema_id from sys.schemas where schema name ='land') Once again you may say I can just look through sys.tables or SSMS object explorer and find what I need much faster and keep everything in the dbo schema then run a query. You could and I wouldn't disagree with you.....as long as you only have 100 or so tables, procedures, or functions in the are you are looking through. Also if you divide objects across schemas SSMS object explorer will list your objects accordingly as well.
Once you start down the path of building integrated database system where everything is or almost is in the database using tables and there constructs (primary keys, unique constraints, foreign keys, triggers, defaults, check constraints ect..) to enforce data integrity and using procedures, functions, views, and triggers to enforce business logic, a large number of objects makes it a little cumbersome to search through only one schema. Yes,I said business logic in the database using the database as its own data access layer enforcing access through objects to control that all views of the data are presented in a uniformed manner for a specific purpose.
Finally and I don't know if this is really a great reason because it can be handled in other ways it makes securing logical areas of the database very easy. You don't want users to have access to the system metadata no problem restrict that schema, you want users to have access to all objects in the player schema simply give them access to it all. So on to the schema explanation, Test is a schema that exist just for that test objects that may be useful in a production environment. This is different then having a development, testing/QA, production environment for code promotion. This schema exists so you can run test scenario's using production data in a production environment but do not modify the production data itself.
For example , you have a new land generation procedure but want to execute it in the production environment using production data but not impact the production land.map table you could have a procedure written and dumped into the test schema for this purpose. Or you may have a piece of production code that you want to put through its paces for stress testing on the production system but you do not want to fill a production table with test data, the test schema allows for an area to dump this data to.
I am still advocating a separate Development, Test, and production environment, but in some instances its necessary to test your code in a semi production environment and this schema allows for the storage of these types of objects. The Dice schema will hold objects related controlled random number generation. I say controlled because I have a need of generating numbers in certain number ranges and I will need to implement this over and over again and instead risking different developers or event the same developer doing this different ways different times encapsulating this code is a stored procedure seems to be a wiser move. As an example is generating random numbers 1 thru 6 will be a common piece of code that I need throughout this gaming system and I can think of a two ways off the top of my head that produce numbers between 1 thru 6 with different weights for the outcome of a given number. Example pseudo-code: floor(rand()*6)+1 if 7 then 6, abs(ceiling(rand()*6)-1)+1, and I'm sure there are more.
Schemas:
----------------------------------
--Create Schemas------------------
----------------------------------
if not exists (select * from INFORMATION_SCHEMA.SCHEMATA where schema_name='test')
BEGIN
execute('create schema test authorization dbo')
print 'created schema test'
END
go
if not exists (select * from INFORMATION_SCHEMA.SCHEMATA where schema_name='dice')
BEGIN
execute('create schema dice authorization dbo')
print 'created schema dice'
END
go
----------------------------------
--Metadata Inserts----------------
----------------------------------
if not exists
(select debug from metadata.logging where schema_name='test')
BEGIN
insert into metadata.logging
(schema_name,debug)
values('test',0)
END
if not exists
(select debug from metadata.logging where schema_name='test')
BEGIN
insert into metadatda.logging
(schema_name,debug)
values('dice',0)
END
We are adding two entries into the sys.messages table. If you are unfamiliar with this table it is the system table that the raiseError T-SQL function pulls its string messages from. This table allows you to add messages formatted in a C printf type syntax so that specific values can be inserted at run-time for specific error messages. We currently have two error messages that may need to be called from a few different places so I would like to have a single place to manage the details of the message.
Once the message is inserted into this table and if you view the parameters list as sort of an interface you can change this message anytime as long as you keep the parameters the same. This interface type of functionallity can be very useful if you decide you would like enhance the error description later, the change would be limited to single update statement in a table vs updating a number of procedures. The two messages we are adding are for missing character id on a given map table, and a message for the non valid direction command. Using raiseError for relaying error messages has a number of benefits including adding reactive alerts (That themselves can fire jobs and or messages out to notify personnel) based on certain errors and logging to SQL Server Error log. We may take advantage of both of these features in later articles but for now we are just using sys.messages, a storage place for standardized messages. raiseError is great piece of T-SQL that is very under utilized in my opinion. If you have a chance I suggest you take a few minutes and dig into this command and see what you can do with it.
Sys.Messages:
----------------------------------
--Custom Errors-------------------
----------------------------------
if not exists(select message_id from sys.messages where message_id =50001)
BEGIN
execute sp_addmessage 50001,11,'This character id: %d does not exist on any map. Please contact support',NULL
END
if not exists(select message_id from sys.messages where message_id =50002)
BEGIN
execute sp_addmessage 50002,11,'please choose a valid direction 8-up,2-down,4-left,6-right %d is not valid',NULL
END
As mentioned earlier we have three new tables we will be covering in this article test.dice, land.currentMapIdent, and land.movementTracker. The first table we will review is the test.dice table. This table's sole purpose is to allow us to verify the randomness of our dice stored procedures mentioned later on in this article. This table has no real production value and will not be utilized in anyway in the actual game itself. I need a way to test to see if my dice procedures are working properly proving me with the proper "randomness" to the number generation process and I wanted a way to test weighting the outcomes of these procedures. This table has 4 columns dice_Counter and identity field that servers as a primary key. The dice sides a tinyInt column that represents the number of sides the dice being tested has. The dice column holds the result for that particular test in a tinyInt, and the dice_Date column which holds the date and time at which the test occurred.
The land.currentMapIdent table holds a record for each being on the map. I say being because an entry into this table can be a Player, a System Administrative Player, or a Monster in the game. This is the first time we see the character_id column so I will take a minute to define its role within the game system itself. The character_id column represents any users character, system administrated character, and or system controlled monster.
A quick run down, on those terms, a system users character is just that a character in the game controlled by a regular player (PC). A system administrated player is one controlled by system administrators and stick to the rules of regular players but can be used to move the game play along by having someone with an overarching system view into the game make things happen in the game environment(NPC). Finally there is the system controlled monsters and this are entities that are controlled automatically by the game engine and are not activity controlled by system administrators.
Currently all system user characters and system administrated characters are regular log-ins and therefore their Id's fall into the standard integer (INT) range of values, system controlled monsters will have all of their IDs start in the range outside of a standard integer (MAX(INT)+1) and go to the top of the bigInt range. The land.currentMapIdent has 3 columns to represent this piece of data, the character_id field as a bigInt defined above. A map_ident column INT that is foreign key to the land.map table, and finally a movement_time column that represents the dateTime when that character entered that particular map space. You will notice that the land.currentMapIdent has a primary key of the character_id column so it will only allow one entry per player into this table.
The last table we will introduce in this code release is the land.movementTracker. This table is basically a history of the land.currentMapIdent table. It has 4 columns the first three of which are values from the land.currentMapIdent table and match them in definition as well. The forth is timestamp row_id column that I will use to see which movement entry was truly put in place first if I have entities arriving at the same map_ident at the same time. This table will be utilized in the game to allow players to see where they have been, to see where others have been if that player has that ability, and to allow for the game engine to have system controlled monster behave intelligently as they move around the map and possible hunt players! Its important to note this table will only ever receive insert values, never and update. I will address this with security settings in a future article.
Tables:
----------------------------------
--Create and Alter Tables---------
----------------------------------
------------------------------------------------------
--
--Adding primary key tothe land.map table
--I'm must have forgotten to do this in prior builds
--
------------------------------------------------------
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[land].[map]') AND name = N'pk_map_01')
BEGIN
alter table land.map
add constraint pk_map_01 primary key (map_ident)
END
-----------------------
--Table Clean up-------
-----------------------
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[land].[movementTracker]') AND type in (N'U'))
DROP TABLE [land].movementTracker
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[land].[CurrentMapIdent]') AND type in (N'U'))
DROP TABLE [land].[CurrentMapIdent]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[dice]') AND type in (N'U'))
DROP TABLE [test].dice
GO
-----------------------
--Create tables--------
-----------------------
create table test.dice
(
dice_Counter int identity(1,1) primary key
,dice_sides tinyint check (dice_sides in (4,6,8,10,12,20,100))
,dice tinyint
,dice_Date datetime
)
create table land.CurrentMapIdent
(
character_id bigint not null primary key
,map_ident int not null foreign key references land.map(map_ident)
,movement_Time datetime not null
)
go
create table land.movementTracker
(
map_ident int not null foreign key references land.map(map_ident)
,character_id bigint not null
,movement_Time datetime not null
,row_id rowversion not null
)
create index idx_land_movementTracker_principal_covering
on land.movementTracker(character_id)
include (map_ident,movement_Time,row_id)
create index idx_land_movement_ttracker_mapIdent_covering
on land.movementTracker(map_ident)
include (character_id)
The new procedures for this release and article can be broken into four logical areas and accordingly broken across 4 schemas, metadata procedures, random number generation procedures, testing procedures, and movement procedures related to the land objects. We will start with the one procedure that has to do with metadata, the metadata.TryCatchDebugLogging procedure. The point of this procedure is once again encapsulation of logic that will be utilized over and over again. SQL 2005 introduced the concept of a try catch block implemented through the use of BEGIN TRY END TRY, BEGIN CATCH END CATCH commands. I find this command can be very useful and so I utilize quite often.
There are however some limited to issues with it. First there is not simple one line call of a logging mechanism attached to but they do provide TRY CATCH block functions that will return most of what you would want to use for logging an error in the code block. So I have taken these functions and built a logging procedure that will allow me to control logging from a TRY CATCH block in one single place. Currently I have the logging into the metadata.debuglogging table but there is no reason that I could not have this procedure log to another area in the error log or outside of the database if I needed it to.
The one issue I do have is that MS provided a manner to capture the error number, error message, the procedure that errored, the error state and even the error line, but they provided no way to capture the schema of the procedure that errored so if you have more than one procedure in two different schema's you have no way of capturing which is firing this event without expanding the procedure to pass in the schema value. I am purposely keeping this procedure with no parameters so that it is very easy to call and will not be avoided by database developers. If I can figure out a way to capture the errored procedure schema I will add that into this object in the future.
Next up are the procedures that deal with random number generation or to be specific those procedures that imitate the function of dice in a standard RPG. I have diced to put these objects into their own schema named appropriately dice. Each of these procedures are pretty much a cookie cutter of the procedure dice.d4, so I will explain the purpose behind that particular procedure and leave it up to you to investigate the other similar procedures.
dice.d4 is a fairly simple procedure that generates a number using the T-SQL function rand() and assigns it to a float variable. I will want to use the exact value generated later so I capture it in a variable vs executing the entire function in one select. The T-SQL rand() function produces a float value between 0 and 1 so I then multiple this value by 4 and run it through the T-SQL floor() function to round off an decimal value, I then and add one to the result. This guarantees I will have a value between 1 and 5, I then have a case statement in a select that takes values 1 thru 4 and assigns the corresponding values to the @dice variable data type INT. If I have a value of 5 I assign that to 4 and if I somehow get a value outside of that range I insert a record into my debug.logging table and assign a value of to the @dice variable. I really wanted these procedures to exist in functions because I thought it would be easier to call in subsequent code but I quickly realized that the rand() function is not allowed in a UDF so a procedure it is. This rounding and bounding logic is expanded upon for the d6,d8,d10,d12,d20, and d100 procedures but is basically the same code.
Procedures designed for testing in both your development and production environment is a new concept I am introducing with this article. This is something new for me as well so I will see if I continue this concept in future articles but so far I really like being able to run some test in both my development environment and then migrate that test to a production environment and be able to execute the same test with different sets and quantities of data.
There are three test procedures we will be working with initially, the test.diceTester, test.movementLocks, and test.spaceBuilderViewer. The first up the test.diceTester does what it sounds like, it runs the dice procedures through an exhaust set of runs and logs each of the values returned to the test.dice table. This procedure takes two parameters the first @dice_sides tinyInt determines the dice.dX procedure that is called value of 4 will call dice.d4 and so on. The second parameter is defaulted to 10000 and is the number of times that the dice procedure will be executed. test.diceTester will then build a dynamic SQL statement based on the first parameter and execute it in a while loop based on the second parameter, inserting the OUTPUT value into the test.dice table as well as information for the dice_sides that was being executed, and the time it was executed so we can report on these results later. This procedure has no debug parameter as the only debug value I would want to look for is always logged to the metadata.debuglogging table.
The next test procedure is the test.movementLocks, this procedure was built to help me test locking in my land.currentLandIdent and land.movementTracker tables. So far in my limited testing I have not had any locking issues when I run 3 concurrent sessions with different users moving about the map. I expect to utilize this procedure more in the future and do expect that I will see locking issue as the game engine becomes more complex, but it is never two early to start testing your code and to start building your test code. test.movementLocks requires one parameter which is a valid user id (character_id) and has one defaulted parameter for debugging as is standard. It then cycles through a total of 100 counts of movements of this character_id in a random direction checking for a possible direction change with each iteration. I plan on giving a little bit more control to the random direction that is chosen in the future so there are two loops one using a @x_axis counter and the other using a @y_axis counter but at this point they are used strictly as loop counters with no weights being applied to the logic that determines the direction.
The last procedure in the testing space is the test.spaceBuilderViewer which is my personal favorite of this release. It allows you to see without building the chance of any particular map tile being placed next to any tile on the current land.map table. This procedure rips the tile building logic from the land.spaceBuilder procedure and allows you to see the percentage chance of any map tile being generated from the raw land.landTypes table. The procedure currently does not take any map tiles into consideration and is displaying values in there raw form. This will be enhanced in future releases so you can see what a land.landTypes records percentage chance of being built next to any existing land.map map_ident value. For now its nice to see what the percentage chance of a given land.landTypes is and how it compares to the other land.landType records it also shows you its relative percentage range.
Please take a look at the logic and ask any question you may have on how or why this procedure is executing in the manner that it is. Unfortunately it requires a loop over the land.landTypes table to build a percentage range out of a set of base percentages and modification percentage points.
The last set of procedures we will be going over are the map related land.move and land.showMap. The land.showMap procedures as been altered to add additional logic that will allow a player to see themselves on the map and to see where they have been on that map screen. This is done by adding the land.currentMapIdent and land.movementTracker tables to the dynamic SQL statement that is being run by this procedure. When security is introduced this will be one of the first procedures available to the "player" database role.
The last procedure we will be discussing is land.move. This will be the core movement procedure and will be responsible for checking a number of game constraints to ensure that movement rules set up in the game are followed. Currently this procedure only ensures that the movement direction is valid (8-up,2-down,4-left,5,-right) is valid. It ensures the character_id being moved is a valid ID in the map currently. It also insures that the player is always within map bounds by adding to the map if a player gets too close to one of the edges. Finally it ensures that the land.movementTracker and land.currentLandIdent tables stay in sync. This could have been done by the user of a trigger and may still be moved in that direction but currently I wanted to have more flexibility in-case there becomes a reason why I need to break this direct tie between the two objects. It is also just easier to modify all of my movement logic in one place vs. having to deal with a procedure and a trigger. If the trigger is deemed the best logic construct to hold this code then the transaction that keeps the land.movementTracker and land.currentMapIdent in-sync will be moved to a trigger.
Procedures:
----------------------------------
--Create Procedures---------------
----------------------------------
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dice].[d4]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dice].[d4]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dice].[d6]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dice].[d6]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dice].[d8]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dice].[d8]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dice].[d10]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dice].[d10]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dice].[d12]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dice].[d12]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dice].[d20]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dice].[d20]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dice].[d100]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dice].[d100]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[diceTester]') AND type in (N'P', N'PC'))
DROP PROCEDURE [test].[diceTester]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[spaceBuilderViewer]') AND type in (N'P', N'PC'))
DROP PROCEDURE [test].[spaceBuilderViewer]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[land].[move]') AND type in (N'P', N'PC'))
DROP PROCEDURE [land].[move]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[movementLocks]') AND type in (N'P', N'PC'))
DROP PROCEDURE [test].[movementLocks]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[metadata].[tryCatchDebugLogging]') AND type in (N'P', N'PC'))
DROP PROCEDURE [metadata].[tryCatchDebugLogging]
GO
create procedure metadata.tryCatchDebugLogging
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.5.4
--Version:1.0
--Notes: This is a generic procedure for wrighting data from the
--try catch ERROR_() objects to the metadata.debugLogging table.
--The purpose of this procedure is to standardized and incapsulate
--this method into a single procedure so it does not have to be
--re-written many times with the risk of it being written different
--
------------------------------------------------------------------
as
BEGIN
declare @debug_message as varchar(2500)
set @debug_message='Error Number:' + cast(ERROR_NUMBER() as varchar(10))
+' Error Severity:' + cast(ERROR_SEVERITY() as varchar(10))
+' Error State:' + cast(ERROR_STATE() as varchar(10))
+' Error Line:' + cast(ERROR_LINE() as varchar(10))
+' Error Message:' + ERROR_MESSAGE()
insert into metadata.debugLogging
(object_name,event_date,user_name,debug_message)
values(error_procedure(),getdate(),suser_name(),@debug_message)
END
GO
create procedure dice.d4
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.4.22
--Version:1.0
--Notes: This procedures and others named with the dice.dX naming
--convention generate random numbers based on the rand function.
--Rand() produces a value between 0 and 1 which is multiplied by
--the number of dice sides. I thought using the floor function then
--adding one and returning the top value when value is greater than
--the number of the dice sides would create a bias towards that number
--but that doesn't seem to be the case
--
--bug: Possible bug I ran this one time and got a value of null return
--I have no idea why but because of that this procedure now does a
--little bit more than it has to so I can log any strange occurance.
------------------------------------------------------------------
@dice tinyint output
as
BEGIN
declare @float float
set @float=rand()
select @dice = case cast(floor(@float*4)+1 as tinyint)
when 5 then 4
when 4 then 4
when 3 then 3
when 2 then 2
when 1 then 1
else 255
end
if @dice=255
BEGIN
set @dice=1
insert into metadata.debugLogging
(object_name,event_date,user_name,debug_message)
values('dice.d4',getdate(),suser_name(), '@dice value not within range; @float='+cast(@float as varchar(20)))
END
END
GO
create procedure dice.d6
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.4.22
--Version:1.0
--Notes: This procedures and others named with the dice.dX naming
--convention generate random numbers based on the rand function.
--Rand() produces a value between 0 and 1 which is multiplied by
--the number of dice sides. I thought using the floor function then
--adding one and returning the top value when value is greater than
--the number of the dice sides would create a bias towards that number
--but that doesn't seem to be the case
--
--bug: Possible bug I ran this one time and got a value of null return
--I have no idea why but because of that this procedure now does a
--little bit more than it has to so I can log any strange occurance.
------------------------------------------------------------------
@dice tinyint output
as
BEGIN
declare @float float
set @float=rand()
select @dice = case cast(floor(@float*6)+1 as tinyint)
when 7 then 6
when 6 then 6
when 5 then 5
when 4 then 4
when 3 then 3
when 2 then 2
when 1 then 1
else 255
end
if @dice=255
BEGIN
set @dice=1
insert into metadata.debugLogging
(object_name,event_date,user_name,debug_message)
values('dice.d6',getdate(),suser_name(), '@dice value not within range; @float='+cast(@float as varchar(20)))
END
END
GO
create procedure dice.d8
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.4.22
--Version:1.0
--Notes: This procedures and others named with the dice.dX naming
--convention generate random numbers based on the rand function.
--Rand() produces a value between 0 and 1 which is multiplied by
--the number of dice sides. I thought using the floor function then
--adding one and returning the top value when value is greater than
--the number of the dice sides would create a bias towards that number
--but that doesn't seem to be the case
--
--bug: Possible bug I ran this one time and got a value of null return
--I have no idea why but because of that this procedure now does a
--little bit more than it has to so I can log any strange occurance.
------------------------------------------------------------------
@dice tinyint output
as
BEGIN
declare @float float
set @float=rand()
select @dice = case cast(floor(@float*8)+1 as tinyint)
when 9 then 8
when 8 then 8
when 7 then 7
when 6 then 6
when 5 then 5
when 4 then 4
when 3 then 3
when 2 then 2
when 1 then 1
else 255
end
if @dice=255
BEGIN
set @dice=1
insert into metadata.debugLogging
(object_name,event_date,user_name,debug_message)
values('dice.d8',getdate(),suser_name(), '@dice value not within range; @float='+cast(@float as varchar(20)))
END
END
GO
create procedure dice.d10
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.4.22
--Version:1.0
--Notes: This procedures and others named with the dice.dX naming
--convention generate random numbers based on the rand function.
--Rand() produces a value between 0 and 1 which is multiplied by
--the number of dice sides. I thought using the floor function then
--adding one and returning the top value when value is greater than
--the number of the dice sides would create a bias towards that number
--but that doesn't seem to be the case
--
--bug: Possible bug I ran this one time and got a value of null return
--I have no idea why but because of that this procedure now does a
--little bit more than it has to so I can log any strange occurance.
------------------------------------------------------------------
@dice tinyint output
as
BEGIN
declare @float float
set @float=rand()
select @dice = case cast(floor(@float*10)+1 as tinyint)
when 11 then 10
when 10 then 10
when 9 then 9
when 8 then 8
when 7 then 7
when 6 then 6
when 5 then 5
when 4 then 4
when 3 then 3
when 2 then 2
when 1 then 1
else 255
end
if @dice=255
BEGIN
set @dice=1
insert into metadata.debugLogging
(object_name,event_date,user_name,debug_message)
values('dice.d10',getdate(),suser_name(), '@dice value not within range; @float='+cast(@float as varchar(20)))
END
END
GO
create procedure dice.d12
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.4.22
--Version:1.0
--Notes: This procedures and others named with the dice.dX naming
--convention generate random numbers based on the rand function.
--Rand() produces a value between 0 and 1 which is multiplied by
--the number of dice sides. I thought using the floor function then
--adding one and returning the top value when value is greater than
--the number of the dice sides would create a bias towards that number
--but that doesn't seem to be the case
--
--bug: Possible bug I ran this one time and got a value of null return
--I have no idea why but because of that this procedure now does a
--little bit more than it has to so I can log any strange occurance.
------------------------------------------------------------------
@dice tinyint output
as
BEGIN
declare @float float
set @float=rand()
select @dice = case cast(floor(@float*12)+1 as tinyint)
when 13 then 12
when 12 then 12
when 11 then 11
when 10 then 10
when 9 then 9
when 8 then 8
when 7 then 7
when 6 then 6
when 5 then 5
when 4 then 4
when 3 then 3
when 2 then 2
when 1 then 1
else 255
end
if @dice=255
BEGIN
set @dice=1
insert into metadata.debugLogging
(object_name,event_date,user_name,debug_message)
values('dice.d12',getdate(),suser_name(), '@dice value not within range; @float='+cast(@float as varchar(20)))
END
END
GO
create procedure dice.d20
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.4.22
--Version:1.0
--Notes: This procedures and others named with the dice.dX naming
--convention generate random numbers based on the rand function.
--Rand() produces a value between 0 and 1 which is multiplied by
--the number of dice sides. I thought using the floor function then
--adding one and returning the top value when value is greater than
--the number of the dice sides would create a bias towards that number
--but that doesn't seem to be the case
--
--bug: Possible bug I ran this one time and got a value of null return
--I have no idea why but because of that this procedure now does a
--little bit more than it has to so I can log any strange occurance.
------------------------------------------------------------------
@dice tinyint output
as
BEGIN
declare @float float
set @float=rand()
set @dice=cast(floor(@float*20)+1 as tinyint)
select @dice= case
when @dice=21 then 20
when @dice=1 then @dice
else 255
end
if @dice=255
BEGIN
set @dice=1
insert into metadata.debugLogging
(object_name,event_date,user_name,debug_message)
values('dice.d20',getdate(),suser_name(), '@dice value not within range; @float='+cast(@float as varchar(20)))
END
END
GO
create procedure dice.d100
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.4.22
--Version:1.0
--Notes: This procedures and others named with the dice.dX naming
--convention generate random numbers based on the rand function.
--Rand() produces a value between 0 and 1 which is multiplied by
--the number of dice sides. I thought using the floor function then
--adding one and returning the top value when value is greater than
--the number of the dice sides would create a bias towards that number
--but that doesn't seem to be the case
--
--bug: Possible bug I ran this one time and got a value of null return
--I have no idea why but because of that this procedure now does a
--little bit more than it has to so I can log any strange occurance.
------------------------------------------------------------------
@dice tinyint output
as
BEGIN
declare @float float
set @float=rand()
set @dice=cast(floor(@float*100)+1 as tinyint)
select @dice= case
when @dice=101 then 100
when @dice=1 then @dice
else 255
end
if @dice=255
BEGIN
set @dice=1
insert into metadata.debugLogging
(object_name,event_date,user_name,debug_message)
values('dice.d100',getdate(),suser_name(), '@dice value not within range; @float='+cast(@float as varchar(20)))
END
END
GO
create procedure test.diceTester
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.4.22
--Version:1.0
--Notes:This procedure will test the different dice random number
--generator procedures.
--
------------------------------------------------------------------
@dice_sides tinyint
,@tests int =10000
as
BEGIN
if not exists (select 1 where @dice_sides in(4,6,8,10,12,20,100))
BEGIN
Print 'Please enter a valid dice size'
return(-1)
END
declare @looper int
declare @dice int
declare @sqlString nvarchar(250)
declare @parmDef nvarchar(50)
declare @diceOut tinyint
declare @procString varchar(9)
set @looper=1
select @procString=case @dice_sides
when 4 then 'dice.d4'
when 6 then 'dice.d6'
when 8 then 'dice.d8'
when 10 then 'dice.d10'
when 12 then 'dice.d12'
when 20 then 'dice.d20'
when 100 then 'dice.d100'
end
set @sqlString =@procString+N' @dice OUTPUT'
set @parmDef=N'@dice tinyint OUTPUT'
while @looper < @tests
begin
execute sp_executesql
@sqlString
,@parmDef
,@dice=@diceOut OUTPUT
insert into test.dice
(dice_sides,dice,dice_date)
values(@dice_sides,@diceOut,getdate())
set @looper = @looper+1
end
END
GO
create procedure test.spaceBuilderViewer
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.4.22
--Version:1.0
--Notes: this is a test procedure that shows the user the chance of
--a given map tile being created when the 3x3 @landbuilder_mep table is used as
--a base. This calculations function exactly the same manner as teh spaceBuilder procedure
--in the land schema. The default @landBuilder_mep variable is empty by default
--showing the user the base unmodified percentage with no pre-existing mep tiles
--This procedure is useful when trying new land existance and existance modification values.
--
------------------------------------------------------------------
as
declare @percentage float
declare @land_ident int
declare @ending_percentage numeric(11,10)
declare @landBuilder_mep as table(
new_land_ident int primary key
,modification_existance_percentage numeric(11,10)
)
declare @landBuilder as table(
land_ident int primary key
,starting_percentage numeric(11,10)
,ending_percentage numeric(11,10)
)
set @ending_Percentage=0
set @land_ident=0
while (select count(*) from land.landTypes where land_ident>=@land_ident) > 0
begin
insert into @landBuilder
(land_ident,starting_percentage,ending_percentage)
select
l.land_ident
,case
when l.land_ident=1 then 0
else coalesce(@ending_percentage+.0000000001,0)
end
,case
when (coalesce(@ending_percentage,0)+coalesce(lm.modification_existance_percentage,0)+.0000000001+l.base_existance_percentage) > @ending_percentage
then coalesce(@ending_percentage,0)+.0000000001+coalesce(lm.modification_existance_percentage,0)+l.base_existance_percentage
else NULL
end
from
land.landTypes l
left outer join
@landbuilder lb
on
l.land_ident=lb.land_ident+1
left outer join
@landBuilder_mep lm
on
l.land_ident=lm.new_land_ident
where
l.land_ident=@land_ident
if (select ending_percentage from @landBuilder where land_ident=@land_ident) is not null
BEGIN
select @ending_Percentage=ending_percentage
from @landBuilder
where land_ident=@land_ident
END
set @land_ident=@land_ident+1
end
select @percentage=1/max(ending_percentage)
from
@landBuilder
select
lt.land_ident
,lt.land_desc
,round(lb.starting_percentage * @percentage*100,2) starting_percent
,round(lb.ending_percentage * @percentage*100,2) ending_percent
,round(lb.ending_percentage * @percentage*100-lb.starting_percentage * @percentage*100,2) total_percent_chance_of_existing
from @landBuilder lb
inner join
land.landTypes lt
on
lt.land_ident=lb.land_ident
GO
create procedure land.move
------------------------------------------------------------------
--
--Created by:Steve Fibich
--Created on: 2008.4.22
--Version:1.0
--Notes: This is a the basic move procedure for any PC/NPC/Monster on
--the map procedure. This procedure moves a character one space at a time
--on the map. This procedure insures that the land.currentMapIdent and
--the land.movementTracker are in sync. This procedure will be added onto
--in the future, this is the basic movement control.
--
------------------------------------------------------------------
@direction int
,@character_id bigint
,@debug bit = 0
as
declare @map_ident int
declare @x_axis int
declare @y_axis int
declare @z_axis int
declare @x_adder int
declare @y_adder int
declare @z_adder int
------------------------------
--Check global debug status --
------------------------------
if @debug=0
BEGIN
select @debug=debug from metadata.logging where schema_name='land'
END
BEGIN
---------
--debug--
---------
if @debug=1
BEGIN
insert into metadata.debuglogging(object_name,event_date,user_name,debug_message)
values('land.move',getdate(),suser_name(),'direction:'+cast(@direction as varchar(2)))
END
if not exists(select @direction where @direction in (8,2,4,6))
BEGIN
RAISERROR(50002,11,1,@direction) with LOG
END
ELSE
BEGIN
set @x_adder=0
set @y_adder=0
set @z_adder=0
if @direction=8
BEGIN
set @y_adder=1
END
if @direction=2
BEGIN
set @y_adder=-1
END
if @direction=4
BEGIN
set @x_adder=-1
END
if @direction=6
BEGIN
set @x_adder=1
END
END
select
@map_ident=map_ident
from
land.CurrentMapIdent
where
character_id=@character_Id
---------
--debug--
---------
if @debug=1
BEGIN
insert into metadata.debuglogging(object_name,event_date,user_name,debug_message)
values('land.move',getdate(),suser_name(),'suser_id():'+cast(@character_Id as varchar(20))+' map_ident:'+coalesce(cast(@map_ident as varchar(20)),'ISNULL'))
END
if (@map_ident is null)
BEGIN
declare @char_id int
set @char_id=1
RAISERROR(50001,17,1,@char_id) with LOG
return(-1)
END
ELSE
BEGIN
select
@x_axis =x_axis
,@y_axis =y_axis
,@z_axis =z_axis
from
land.map
where
map_ident =@map_ident
select
@map_ident=map_ident
from
land.map
where
x_axis=@x_axis+@x_adder
and
y_axis=@y_axis+@y_adder
and
z_axis=@z_axis+@z_adder
--Check to see if the map table needs to be added to.--
if exists (select map_ident from land.map where x_axis=@x_axis+@x_adder+5
and y_axis=@y_axis+@y_adder+5 and z_axis=@z_axis+@z_adder+5)
BEGIN
execute land.landBuilder @debug=@debug
END
if exists(select @map_ident)
BEGIN
BEGIN TRAN
BEGIN TRY
insert into land.movementTracker
(map_ident,character_id,movement_Time)
values(@map_ident,@character_id,getdate())
update land.currentMapIdent
set movement_time = getdate()
,map_ident=@map_ident
where
character_id=@character_id
END TRY
BEGIN CATCH
ROLLBACK TRAN
execute metadata.tryCatchDebugLogging
declare @error_message varchar(2048)
set @error_message = cast(ERROR_MESSAGE() as varchar(2048))
RAISERROR(@error_message,17,1)
return(-2)
END CATCH
COMMIT TRAN
END
END
return(0)
END
go
create procedure test.movementLocks
@userIdStart int
,@debug int=0
as
declare @map_ident int
declare @userX int
set @userX=@userIdStart
declare @x_axis int
set @x_axis=1
declare @y_axis int
set @y_axis=1
declare @Z_axis int
set @z_axis=0
declare @direction int
while @y_axis < 10
BEGIN
while @x_axis < 10
BEGIN
select @direction = case floor(rand()*4)+1
when 1 then 2
when 2 then 2
when 3 then 4
when 4 then 8
when 5 then 6
else 6
end
execute land.move
@direction,@userX,@debug
set @x_axis=@x_axis+1
END
set @x_axis=1
set @y_axis=@y_axis+1
END
GO
alter proc [land].[showmap]
@x_axis int =null
,@y_axis int=null
,@debug bit = 0
as
------------------------------------------------------------------
--Created by:Steve Fibich
--Created on: 2007.5.7
--Version:1.1
--Notes: This procedure is not truly inteded for game play rather it
--used to demonstrate a simple user interface. While all game play is
--possible certian aspects may be made easier with some sort of user interface
--
------------------------------------------------------------------
if @x_axis is null
BEGIN
select
@x_axis=m.x_axis
,@y_axis=m.y_axis
from
land.CurrentMapIdent cmi
inner join
land.map m
on
cmi.map_ident=m.map_ident
where
character_id=suser_id()
END
declare @sqlString nvarchar(1000)
set @sqlString='select y_axis
,['+cast(@x_axis-4 as nvarchar(10))+'],['+cast(@x_axis-3 as nvarchar(10))+'],['+cast(@x_axis-2 as nvarchar(10))+'],['+cast(@x_axis-1 as nvarchar(10))+'],['+cast(@x_axis as nvarchar(10))+'],['+cast(@x_axis+1 as nvarchar(10))+'],['+cast(@x_axis+2 as nvarchar(10))+'],['+cast(@x_axis+3 as nvarchar(10))+'],['+cast(@x_axis+4 as nvarchar(10))+']
from
(
select
m.x_axis
,m.y_axis
,land_desc+coalesce('' **CID:''+cast(cmi.character_id as varchar(10)),
coalesce(replace(cast(mt.character_id as varchar(10)),suser_id(),'' **''),''''))land_desc
from
land.map m
inner join
land.landTypes lt
on
m.land_ident=lt.land_ident
left join
land.CurrentMapIdent cmi
on
m.map_ident=cmi.map_ident
and
cmi.character_id=suser_id()
left outer join
land.movementTracker mt
on
m.map_ident=mt.map_Ident
and
mt.character_id=suser_id()
where
m.y_axis between '+cast(@y_axis-4 as nvarchar(10))+' and '+cast(@y_axis+4 as nvarchar(10))+'
and
m.x_axis between '+cast(@x_axis-4 as nvarchar(10))+' and '+cast(@x_axis+4 as nvarchar(10))+'
) as sourceTable
pivot
(
max(land_desc)
FOR x_axis in (
['+cast(@x_axis-4 as nvarchar(10))+'],['+cast(@x_axis-3 as nvarchar(10))+'],['+cast(@x_axis-2 as nvarchar(10))+'],['+cast(@x_axis-1 as nvarchar(10))+'],['+cast(@x_axis as nvarchar(10))+'],['+cast(@x_axis+1 as nvarchar(10))+'],['+cast(@x_axis+2 as nvarchar(10))+'],['+cast(@x_axis+3 as nvarchar(10))+'],['+cast(@x_axis+4 as nvarchar(10))+']
)
)as mapTable
order by y_axis asc'
if @debug=1
BEGIN
select @sqlString
END
execute (@sqlString)
GO
Well that's it for the code review and explanation section of this article the only thing left is to run the example code and play around with the very beginnings of the game. This is really the first time a user can start to manipulate the system using objects that are intended for game play! As always please let me know how you feel about the project, the design, or anything else that comes to mind.
Run the Code!
-------------------------------
--Run the code-----------------
-------------------------------
insert into land.currentMapIdent
(character_id,map_ident,movement_time)
values(suser_id(),1,getdate())
delete from land.movementTracker
delete from metadata.debugLogging
GO
execute test.diceTester 4
execute test.diceTester 6
execute test.diceTester 8
execute test.diceTester 10
execute test.diceTester 12
execute test.diceTester 20
execute test.diceTester 100
select
dice_sides
,dice
,count(*) diceCount
from
test.dice
group by
dice_sides
,dice
order by
dice_sides
,count(*) desc
--
--GO
--execute test.spaceBuilderViewer
--GO
declare @suser_id int
select @suser_id=suser_id()
execute test.movementLocks @suser_id,1
execute [land].[showmap]
select
object_name
,event_date
,user_name
,debug_message
from
theSystem.metadata.debugLogging
order by
object_name
,event_date