Soleone-cards 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+