Soleone-cards 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile ADDED
@@ -0,0 +1,27 @@
1
+ h1. Cards (temporary name)
2
+
3
+ Cards wants to be a framework for building card games like Magic: The Gathering.
4
+
5
+ h2. User Guide
6
+
7
+ A @Card@ can have a @name@, @type@, @description@, a subclass is @Creature@ (something to "put on the table").
8
+ You can inherit from the @Creature@ model to create monsters and people, etc.
9
+
10
+ Creatures include the @Fighting@ module, which gives them @power@, @defense@ and @health@
11
+ They can attack another creature and deal damage according to their power.
12
+ If a creature has 3 power and the other one 1 defense, then the other creature will loose 2 health.
13
+
14
+ *TODO*
15
+
16
+
17
+ h2. TODO
18
+
19
+ These things need to be done:
20
+
21
+ # -Summon Creatures-
22
+ # -Creatures can fight each other-
23
+ # -Creatures stay until killed-
24
+ # Rewrite MetaClass implementation (use class instance variable @attributes instead of @@attributes for easy value inheritance)
25
+ # Enchant creatures with temporary or permanent "spells"
26
+ # "Cast spells" that do something only once (like 2 damage to player)
27
+ # Try to be theme-agnostic could be either fantasy, science fiction or even reality-like
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 0
4
+ :patch: 3
data/lib/cards.rb ADDED
@@ -0,0 +1,4 @@
1
+ lib = %w[lib/meta_class behavior/fighting behavior/effects behavior/abilities card creatures/creature creatures/human creatures/animal]
2
+ lib.each do |file|
3
+ require File.dirname(__FILE__) + "/cards/#{file}.rb"
4
+ end
@@ -0,0 +1,69 @@
1
+ module Cards
2
+ module Behavior
3
+ module Abilities
4
+ def self.included(base)
5
+ base.send(:include, MetaClass)
6
+ base.class_eval do
7
+ define_method(:abilities) do
8
+ @abilities ||= AbilityArray.new(self)
9
+ end
10
+ end
11
+ end
12
+
13
+ # for convenience (use all abilities at once)
14
+ class AbilityArray < Array
15
+ def initialize(creature)
16
+ @creature = creature
17
+ end
18
+
19
+ def use
20
+ each { |ab| ab.use(@creature) }
21
+ end
22
+ end
23
+
24
+
25
+ class Ability
26
+ attr_reader :name, :description, :action
27
+
28
+ def initialize(name, description, action=nil)
29
+ @name, @description, @action = name, description, action
30
+ deactivate!
31
+ end
32
+
33
+ def use(creature)
34
+ @action.call(creature) unless active?
35
+ activate!
36
+ end
37
+
38
+ def text
39
+ "#{name} - #{description}"
40
+ end
41
+
42
+ def activate!
43
+ @active = true
44
+ end
45
+
46
+ def deactivate!
47
+ @active = false
48
+ end
49
+
50
+ def active?; @active; end
51
+ end
52
+
53
+ # An ability that temporarily changes stats by putting effects on the creature
54
+ class AttributeAbility < Ability
55
+ def initialize(attribute, modifier, name=nil, description=nil)
56
+ super(name, description)
57
+ @action = lambda do |creature|
58
+ creature.effects << Cards::Behavior::Effects::Effect.new(attribute, modifier)
59
+ creature.effects.activate!
60
+ end
61
+ end
62
+
63
+ def text
64
+ string = (modifier >= 0 ? "In" : "De") + "creases #{attribute} by #{modifier.abs}"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,56 @@
1
+ module Cards
2
+ module Behavior
3
+ module Effects
4
+ def self.included(base)
5
+ base.send(:include, MetaClass)
6
+ base.class_eval do
7
+ define_method(:effects) do
8
+ @effects ||= EffectArray.new(self)
9
+ end
10
+ end
11
+ end
12
+
13
+ # for convenience (de/activate all effects at once)
14
+ class EffectArray < Array
15
+ def initialize(creature)
16
+ @creature = creature
17
+ end
18
+
19
+ def activate!
20
+ each { |effect| effect.activate!(@creature) }
21
+ end
22
+
23
+ def deactivate!
24
+ each { |effect| effect.deactivate!(@creature) }
25
+ end
26
+ end
27
+
28
+
29
+ class Effect
30
+ attr_reader :attribute, :modifier
31
+
32
+ # Example: Effect.new(:power, -2)
33
+ # Decreases the power of the affected creature temporarily by 2
34
+ def initialize(attribute, modifier, temporary=true)
35
+ @attribute, @modifier, @temporary = attribute, modifier, temporary
36
+ @active = false
37
+ end
38
+
39
+ def temporary?; @temporary; end
40
+
41
+ def activate!(creature)
42
+ creature.send("#{@attribute}!", @modifier) unless active?
43
+ @active = true
44
+ end
45
+
46
+ def deactivate!(creature)
47
+ creature.send("#{@attribute}!", -@modifier) if active?
48
+ @active = false
49
+ end
50
+
51
+ def active?; @active ;end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,108 @@
1
+ module Cards
2
+ module Behavior
3
+ module Fighting
4
+ def self.included(base)
5
+ base.send(:include, MetaClass)
6
+ base.instance_eval do
7
+ attributes :power, :defense, :health
8
+
9
+ power 0
10
+ defense 1
11
+ health 1
12
+ end
13
+ end
14
+
15
+ # Saves details of a hit from one creature to another
16
+ class Hit
17
+ attr_reader :source, :target
18
+
19
+ def initialize(source, target, options={})
20
+ @source, @target, @options = source, target, options
21
+ end
22
+
23
+ def power
24
+ @power ||= @source.power
25
+ end
26
+
27
+ def double!
28
+ @power = power * 2
29
+ end
30
+
31
+ def apply
32
+ double! if @options[:double]
33
+ @target.health -= impact
34
+ @applied = true
35
+ self
36
+ end
37
+
38
+ def applied?
39
+ @applied || false
40
+ end
41
+
42
+ # damage after defense modifier
43
+ def impact
44
+ power > 0 ? power - @target.defense : 0
45
+ end
46
+ end
47
+
48
+ # instance methods
49
+ # ================
50
+
51
+ # try to attack another creature
52
+ # TODO: could later be used to only hit if not blocked, deflected or similar.
53
+ # Creatures could also have different chances to hit in an additional attribute (more complex rules though)
54
+ def attack(creature, options={})
55
+ creature.take_hit(self, options)
56
+ end
57
+
58
+ # the creature gets hit
59
+ def take_hit(from, options={})
60
+ @hits ||= []
61
+ hit = Hit.new(from, self, options)
62
+ @hits << hit.apply
63
+ event(:hit, hit)
64
+ end
65
+
66
+ # all the hits this creature has endured
67
+ # TODO: could later be used for bonuses or special abilities for example
68
+ def hits
69
+ @hits || []
70
+ end
71
+
72
+ def killed?
73
+ kill! if health <= 0
74
+ @killed
75
+ end
76
+
77
+ def kill!
78
+ return if @killed
79
+ health = 0
80
+ event(:killed)
81
+ @killed = true
82
+ end
83
+
84
+ protected
85
+ # overwrite this in subclasses to react on incoming hits
86
+ def hit_taken(hit)
87
+ puts "#{type} has been hit for #{hit.impact}"
88
+ end
89
+
90
+ # overwrite this in subclasses to react when the creature dies
91
+ def killed
92
+ puts "#{type} has been killed"
93
+ end
94
+
95
+ private
96
+ def event(name, *args)
97
+ case name
98
+ when :hit
99
+ self.hit_taken(args.first)
100
+ self.killed?
101
+ when :killed
102
+ self.killed
103
+ end
104
+ end
105
+
106
+ end
107
+ end
108
+ end
data/lib/cards/card.rb ADDED
@@ -0,0 +1,10 @@
1
+ module Cards
2
+ class Card
3
+ include MetaClass
4
+ attributes :title, :type, :text, :description
5
+
6
+ def type
7
+ self.class.name.split("::").last
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ module Cards
2
+ module Creatures
3
+
4
+ # Base animal
5
+ class Animal < Creature
6
+ title "Animal"
7
+ description "Animals come in all sorts of flavors. They can be tiny and sneaky or big and powerful."
8
+ end
9
+
10
+ # List actual animals
11
+ class Rhino < Animal
12
+ title "Rhino"
13
+ description "Huge Rhinos can have enormous attacking power and they have a thick armor plating as skin."
14
+
15
+ power 3
16
+ defense 3
17
+ health 2
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module Cards
2
+ module Creatures
3
+ class Creature < Card
4
+ include Behavior::Fighting
5
+ include Behavior::Effects
6
+ include Behavior::Abilities
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module Cards
2
+ module Creatures
3
+ class Human < Creature
4
+ title "Human"
5
+ description "Humans are the most common species on planet Earth."
6
+
7
+ health 2
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,72 @@
1
+ module Cards
2
+ module MetaClass
3
+
4
+ def self.included(base)
5
+ base.send(:extend, ClassMethods)
6
+ end
7
+
8
+ private
9
+ module ClassMethods
10
+ def metaclass
11
+ class << self; self; end
12
+ end
13
+
14
+ def recursive_attributes
15
+ all_attributes = {}
16
+ ancestors[0..-1].each do |clazz|
17
+ if clazz.respond_to?(:attributes) && clazz.attributes.is_a?(Hash)
18
+ clazz.attributes.each do |key, value|
19
+ all_attributes[key] = value unless all_attributes[key]
20
+ end
21
+ end
22
+ end
23
+ all_attributes
24
+ end
25
+
26
+ def all_attributes
27
+ @@attributes
28
+ end
29
+
30
+ def attributes(*names)
31
+ return @@attributes[self.name] if names.empty?
32
+
33
+ attr_accessor *names
34
+
35
+ names.each do |name|
36
+ class_eval do
37
+ # generates e.g.: power!(-2) # changes power by -2
38
+ define_method("#{name}!") do |value|
39
+ old = instance_variable_get("@#{name}")
40
+ instance_variable_set("@#{name}", old + value)
41
+ end
42
+ end
43
+
44
+ metaclass.instance_eval do
45
+ define_method(name) do |value|
46
+ @@attributes ||= {}
47
+ @@attributes[self.name] ||= {}
48
+ @@attributes[self.name][name] = value
49
+ end
50
+
51
+ define_method(:create) do |options|
52
+ instance = new
53
+ options.each do |name, value|
54
+ instance.instance_variable_set("@#{name}", value)
55
+ end
56
+ instance
57
+ end
58
+ end
59
+ end
60
+
61
+ class_eval do
62
+ define_method(:initialize) do
63
+ self.class.recursive_attributes.each do |name, value|
64
+ instance_variable_set("@#{name}", value)
65
+ end
66
+ end
67
+ end
68
+ end # end attributes
69
+ end
70
+
71
+ end
72
+ end
@@ -0,0 +1,10 @@
1
+ module Cards
2
+ class Resource
3
+ include MetaClass
4
+ attributes :title, :type, :text, :description
5
+
6
+ def type
7
+ self.class.name.split("::").last
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ include Cards::Creatures
4
+ include Cards::Behavior::Abilities
5
+
6
+ describe "Abilities" do
7
+ before do
8
+ @creature = Creature.new
9
+ end
10
+
11
+ context "when included in a Class should add methods:" do
12
+ it { @creature.should respond_to(:abilities) }
13
+ end
14
+
15
+ context "AttributeAbilities" do
16
+ before do
17
+ @rage = AttributeAbility.new(:power, +2)
18
+ @defense = AttributeAbility.new(:defense, +1)
19
+ end
20
+
21
+ it "activate all abilities a creature has with abilities#use" do
22
+ @creature.abilities << @rage
23
+ @creature.abilities << @defense
24
+ @creature.abilities.each { |ab| ab.should_not be_active }
25
+
26
+ @creature.abilities.use
27
+ @creature.abilities.each { |ab| ab.should be_active }
28
+ end
29
+
30
+ it "should activate an Effect when using the Ability" do |variable|
31
+ @creature.abilities << @rage
32
+ @creature.effects.should be_empty
33
+ lambda { @creature.abilities.use }.should change{@creature.power}.by(2)
34
+ @creature.should have(1).effects
35
+ @creature.effects.first.should be_active
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ include Cards::Creatures
4
+
5
+ describe "Effects" do
6
+ before do
7
+ @creature = Creature.new
8
+ end
9
+
10
+ context "when included in a Class should add methods:" do
11
+ it { @creature.should respond_to(:effects) }
12
+ end
13
+ end
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ include Cards::Creatures
4
+
5
+ describe "Fighting" do
6
+ before do
7
+ @creature = Creature.new
8
+ end
9
+
10
+ context "when included in a Class should add methods:" do
11
+ it { @creature.should respond_to(:power) }
12
+ it { @creature.should respond_to(:defense) }
13
+ it { @creature.should respond_to(:health) }
14
+
15
+ it { @creature.should respond_to(:attack) }
16
+ it { @creature.should respond_to(:take_hit) }
17
+
18
+ it { @creature.should respond_to(:hit_taken) }
19
+ it { @creature.should respond_to(:killed) }
20
+
21
+ it { @creature.should respond_to(:kill!) }
22
+ it { @creature.should respond_to(:killed?) }
23
+
24
+ end
25
+
26
+ context "attack" do
27
+ before do
28
+ @target = Creature.new
29
+ @man = Human.create(:power => 2)
30
+ @woman = Human.create(:power => 1, :health => 10)
31
+ end
32
+
33
+ it "should create a Hit and apply it" do
34
+ lambda { @creature.attack(@target) }.should change { @target.hits.size }.from(0).to(1)
35
+ @target.hits.first.should be_applied
36
+ end
37
+
38
+ it "should invoke the 'hit_taken' callback" do
39
+ @target.should_receive(:hit_taken)
40
+ @creature.attack(@target)
41
+ end
42
+
43
+ it "should invoke the generic 'event' callback" do
44
+ @target.should_receive(:event)
45
+ @creature.attack(@target)
46
+ end
47
+
48
+ it "should lose 0 health with 1 defense when being hit with 1 power" do
49
+ lambda{ @woman.attack(@man) }.should_not change{@man.health}
50
+ end
51
+
52
+ it "should lose 1 health with 1 defense when being hit with 2 power" do
53
+ lambda{ @man.attack(@woman) }.should change{@woman.health}.by(-1)
54
+ end
55
+
56
+ it "should lose 3 health with 1 defense when hit with 2 power and double-modifier" do
57
+ lambda{ @man.attack(@woman, :double => true) }.should change{@woman.health}.by(-3)
58
+ end
59
+
60
+ it "should get killed when health reaches 0" do
61
+ @man.attack(@target)
62
+ @target.health.should == 0
63
+ @target.should be_killed
64
+ end
65
+
66
+ it "should call killed event when killed for first time" do
67
+ @target.should_receive(:killed)
68
+ @target.kill!
69
+ end
70
+
71
+ it "should not call killed event when killed and already dead" do
72
+ @target.should_receive(:killed).once
73
+ @target.kill!
74
+ @target.kill!
75
+ end
76
+
77
+ end
78
+
79
+ end
data/spec/card_spec.rb ADDED
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + "/spec_helper"
2
+
3
+ describe "Card" do
4
+ before do
5
+ @card = Card.new
6
+ end
7
+
8
+ it do
9
+ Card.ancestors.should include(MetaClass)
10
+ end
11
+
12
+ context ".attr_generate" do
13
+ it "should respond to .title" do
14
+ Card.should respond_to(:title)
15
+ end
16
+
17
+ it do
18
+ @card.should respond_to(:title)
19
+ end
20
+ end
21
+
22
+ it "should have a attr_generated title, text and description" do
23
+ @card.should respond_to(:title)
24
+ @card.should respond_to(:text)
25
+ @card.should respond_to(:description)
26
+ Card.should respond_to(:title)
27
+ Card.should respond_to(:text)
28
+ Card.should respond_to(:description)
29
+ end
30
+
31
+ it "#type should be 'Card'" do
32
+ @card.type.should == "Card"
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe "Creature" do
4
+ context "when created" do
5
+ before do
6
+ @creature = Creature.new
7
+ end
8
+
9
+ it "#type should be 'Creature'" do
10
+ @creature.type.should == "Creature"
11
+ end
12
+
13
+ it "#text should be nil" do
14
+ @creature.text.should be_nil
15
+ end
16
+
17
+ it "should have 0 power" do
18
+ @creature.power.should be(0)
19
+ end
20
+
21
+ it "should have 1 defense" do
22
+ @creature.defense.should be(1)
23
+ end
24
+
25
+ it "should have 1 health" do
26
+ @creature.health.should be(1)
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ include Cards::Creatures
4
+
5
+ describe "Human" do
6
+ it "#type should be 'Human'" do
7
+ Human.new.type.should == "Human"
8
+ end
9
+
10
+ it "#text should be nil" do
11
+ Human.new.text.should be_nil
12
+ end
13
+
14
+ context "when created" do
15
+ it "should have 0 power" do
16
+ Human.new.power.should be(0)
17
+ end
18
+
19
+ it "should have 1 defense" do
20
+ Human.new.defense.should be(1)
21
+ end
22
+
23
+ it "should have 2 health" do
24
+ Human.new.health.should be(2)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ include Cards
4
+
5
+ class Person
6
+ include MetaClass
7
+ attributes :first_name, :last_name
8
+ end
9
+
10
+
11
+ describe "MetaClass" do
12
+ before do
13
+ @person = Person.new
14
+ end
15
+
16
+ context "when included" do
17
+ it "should add a class method called create_attribute to class" do
18
+ lambda do
19
+ class A
20
+ include MetaClass
21
+ attributes :name
22
+ end
23
+ end.should_not raise_error(NoMethodError)
24
+ end
25
+ end
26
+
27
+ context ".attributes <name>" do
28
+ it "should generate a class method called <name>" do
29
+ Person.should respond_to(:first_name)
30
+ end
31
+
32
+ it "should generate a getter for <name>" do
33
+ @person.should respond_to(:first_name)
34
+ end
35
+
36
+ it "should generate a setter for <name>" do
37
+ @person.should respond_to(:first_name=)
38
+ end
39
+
40
+ it "getter for <name> should return set value" do
41
+ @person.first_name = "Someone"
42
+ @person.first_name.should == "Someone"
43
+ end
44
+
45
+ it "getter should return default value" do
46
+ class Person
47
+ first_name "Me"
48
+ end
49
+ Person.new.first_name.should == "Me"
50
+ end
51
+ end
52
+
53
+ context "generated class method <name>" do
54
+ it "should allow Fixnum as default value for getter <name>" do
55
+ class Person
56
+ attributes :amount
57
+ amount 5
58
+ end
59
+ Person.new.amount.should == 5
60
+ end
61
+
62
+ it "should allow Array as default value for getter <name>" do
63
+ class Person
64
+ attributes :stuff
65
+ stuff [1,2,3]
66
+ end
67
+ Person.new.stuff.should == [1,2,3]
68
+ end
69
+
70
+ it "should allow Hash as default value for getter <name>" do
71
+ class Person
72
+ attributes :stuff
73
+ stuff({:asd => 1})
74
+ end
75
+ Person.new.stuff.should == {:asd => 1}
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/../lib/cards"
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: Soleone-cards
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Soleone
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-04 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Create different types of cards in any setting (fantasy, real, science-fiction) that can fight with each other.
17
+ email: soleone@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README.textile
26
+ - VERSION.yml
27
+ - lib/cards
28
+ - lib/cards/behavior
29
+ - lib/cards/behavior/abilities.rb
30
+ - lib/cards/behavior/effects.rb
31
+ - lib/cards/behavior/fighting.rb
32
+ - lib/cards/card.rb
33
+ - lib/cards/creatures
34
+ - lib/cards/creatures/animal.rb
35
+ - lib/cards/creatures/creature.rb
36
+ - lib/cards/creatures/human.rb
37
+ - lib/cards/lib
38
+ - lib/cards/lib/meta_class.rb
39
+ - lib/cards/resource.rb
40
+ - lib/cards.rb
41
+ - spec/behavior
42
+ - spec/behavior/abilities_spec.rb
43
+ - spec/behavior/effects_spec.rb
44
+ - spec/behavior/fighting_spec.rb
45
+ - spec/card_spec.rb
46
+ - spec/creatures
47
+ - spec/creatures/creature_spec.rb
48
+ - spec/creatures/human_spec.rb
49
+ - spec/lib
50
+ - spec/lib/meta_class_spec.rb
51
+ - spec/spec_helper.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/Soleone/cards
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --inline-source
57
+ - --charset=UTF-8
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.2.0
76
+ signing_key:
77
+ specification_version: 2
78
+ summary: "Tiny Framework for creating card based games like Magic: The Gathering."
79
+ test_files: []
80
+