5 * Created by Alyssa Milburn on Mon May 31 2004.
6 * Copyright (c) 2004 Alyssa Milburn. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
22 #include <stdlib.h> // rand()
24 #include <math.h> // abs()/fabs()
28 #include "Catalogue.h"
30 #include <cctype> // toupper/tolower
31 #include <algorithm> // transform
32 #include <boost/format.hpp>
35 #include "PointerAgent.h"
36 #include "CreatureAgent.h"
42 %pragma variants c2 cv c3 sm
44 Script-local variables (exist only in the current script) with xx being from 00 to 99. Examples: VA01, VA45.
47 VM_PARAM_INTEGER(index
); caos_assert(index
>= 0 && index
< 100),
49 var
[index
] = newvalue
)
55 %pragma variants c1 c2
57 %pragma implementation caosVM::v_VAxx
58 %pragma saveimpl caosVM::s_VAxx
60 Like VAxx, but restricted to 0-9. Legacy from Creatures 1.
68 Like OVxx, only for OWNR, not TARG.
70 CAOS_LVALUE_WITH(MVxx
, owner
,
71 VM_PARAM_INTEGER(index
); caos_assert(index
>= 0 && index
< 100),
73 owner
->var
[index
] = newvalue
)
76 ADDS (command) var (variable) value (string)
79 Append the given string to the given variable.
81 void caosVM::c_ADDS() {
83 VM_PARAM_STRING(value
)
84 VM_PARAM_VARIABLE(variable
)
86 caos_assert(variable
->hasString());
87 variable
->setString(variable
->getString() + value
);
91 SETS (command) var (variable) value (string)
94 Sets the given variable to the given string.
96 void caosVM::c_SETS() {
98 VM_PARAM_STRING(value
)
99 VM_PARAM_VARIABLE(var
)
101 var
->setString(value
);
105 SETV (command) var (variable) value (decimal)
107 %pragma variants c1 c2 cv c3 sm
109 %% Don't enable c1 or c2 here; we activate them with horrible hacks later
111 Sets the given variable to the given decimal value.
113 void caosVM::c_SETV() {
115 // TODO: hackery for c2
116 //VM_PARAM_DECIMAL(value)
117 VM_PARAM_VALUE(value
)
118 VM_PARAM_VARIABLE(var
)
121 // TODO: hackery for c2
122 if (value
.hasAgent()) {
123 var
->setAgent(value
.getAgent());
125 } else caos_assert(value
.hasDecimal());
127 if (value
.hasFloat()) {
128 var
->setFloat(value
.getFloat());
129 } else { // VM_PARAM_DECIMAL guarantees us float || int
130 var
->setInt(value
.getInt());
135 SETA (command) var (variable) value (agent)
138 Sets the given variable to the given agent.
140 void caosVM::c_SETA() {
142 VM_PARAM_AGENT(value
)
143 VM_PARAM_VARIABLE(var
)
145 var
->setAgent(value
);
152 %pragma variants c2 cv c3 sm
154 Agent-local variables (exist only in the current agent's VM) from TARG, with xx being from 00 to 99. Examples: OV01, OV45.
156 CAOS_LVALUE_TARG(OVxx
,
157 VM_PARAM_INTEGER(index
); caos_assert(index
>= 0 && index
< 100),
159 targ
->var
[index
] = newvalue
)
165 %pragma variants c1 c2
166 %pragma implementation caosVM::v_OVxx
167 %pragma saveimpl caosVM::s_OVxx
169 Like OVxx, but restricted to 0-2 in C1, or 0-9 in C2. Legacy from Creatures 1.
171 // TODO: restrict to 0-2 in C1?
174 TYPE (integer) value (anything)
177 Returns a integer value representing the type of data in 'value'. 0 is integer, 1 is float, 2 is string, 3 is agent.
179 void caosVM::v_TYPE() {
180 VM_PARAM_VALUE(value
)
182 caos_assert(!value
.isEmpty());
186 else if (value
.hasFloat())
188 else if (value
.hasString())
190 else if (value
.hasAgent()) {
191 boost::shared_ptr
<Agent
> a
= value
.getAgent();
194 else if (typeid(*a
) == typeid(SimpleAgent
))
196 else if (typeid(*a
) == typeid(PointerAgent
))
198 else if (typeid(*a
) == typeid(CompoundAgent
))
200 else if (typeid(*a
) == typeid(Vehicle
))
203 CreatureAgent
*c
= dynamic_cast<CreatureAgent
*>(a
.get());
207 result
.setInt(-2); // unknown agent
210 throw creaturesException("caosVar confused us terribly in TYPE");
214 MODV (command) var (variable) mod (integer)
216 %pragma variants c1 c2 cv c3 sm
219 Divides the given variable by the given integer, and returns the remainder (var % mod).
221 void caosVM::c_MODV() {
223 VM_PARAM_INTEGER(mod
)
224 VM_PARAM_VARIABLE(v
) // integer
225 if (!v
->hasInt()) throw badParamException();
226 v
->setInt(v
->getInt() % mod
);
230 ANDV (command) var (variable) and (integer)
232 %pragma variants c1 c2 cv c3 sm
235 Returns the result of a bitwise AND comparison of the given variable and the given integer (var & and).
237 void caosVM::c_ANDV() {
239 VM_PARAM_INTEGER(andv
)
241 if (!v
->hasInt()) throw badParamException();
242 v
->setInt(v
->getInt() & andv
);
246 ORRV (command) var (variable) or (integer)
248 %pragma variants c1 c2 cv c3 sm
251 Returns the result of a bitwise OR comparison of the given variable and the given integer (var | or)
253 void caosVM::c_ORRV() {
255 VM_PARAM_INTEGER(orv
)
257 if (!v
->hasInt()) throw badParamException();
258 v
->setInt(v
->getInt() | orv
);
262 ADDV (command) var (variable) add (integer)
264 %pragma variants c1 c2 cv c3 sm
267 Adds the given integer to the given variable and returns the result.
269 void caosVM::c_ADDV() {
271 VM_PARAM_DECIMAL(add
)
274 v
->setFloat(v
->getFloat() + (add
.hasFloat() ? add
.getFloat() : add
.getInt()));
275 else if (v
->hasInt())
276 v
->setInt((int)(v
->getInt() + (add
.hasFloat() ? add
.getFloat() : add
.getInt())));
278 throw badParamException();
282 SUBV (command) var (variable) sub (integer)
284 %pragma variants c1 c2 cv c3 sm
287 Subtracts the given integer from the given variable and returns the result.
289 void caosVM::c_SUBV() {
291 VM_PARAM_DECIMAL(sub
)
294 v
->setFloat(v
->getFloat() - (sub
.hasFloat() ? sub
.getFloat() : sub
.getInt()));
295 else if (v
->hasInt())
296 v
->setInt((int)(v
->getInt() - (sub
.hasFloat() ? sub
.getFloat() : sub
.getInt())));
298 throw badParamException();
302 NEGV (command) var (variable)
304 %pragma variants c1 c2 cv c3 sm
307 Returns the inverse of (negates) the given variable. For example, 1 to -1, or -4 to 4.
309 void caosVM::c_NEGV() {
313 v
->setFloat(-v
->getFloat());
314 else if (v
->hasInt())
315 v
->setInt(-v
->getInt());
317 throw badParamException();
321 DIVV (command) var (variable) div (decimal)
323 %pragma variants c1 c2 cv c3 sm
326 Divides the given variable by the given integer and returns the result.
328 void caosVM::c_DIVV() {
330 VM_PARAM_DECIMAL(div
)
333 caos_assert(div
.hasDecimal());
334 if (div
.getFloat() == 0.0f
) throw caosException("attempt to divide by zero");
336 if ((engine
.version
< 3 && v
->hasDecimal() && div
.hasDecimal()) || (v
->hasInt() && div
.hasInt())) {
338 v
->setInt(v
->getInt() / div
.getInt());
339 } else if (v
->hasInt() || v
->hasFloat()) {
340 // floating point division
341 v
->setFloat((v
->hasFloat() ? v
->getFloat() : v
->getInt()) /
342 (div
.hasFloat() ? div
.getFloat() : div
.getInt()));
344 throw badParamException();
348 MULV (command) var (variable) mul (decimal)
350 %pragma variants c1 c2 cv c3 sm
353 Multiplies the given variable by the given integer and returns the result.
355 void caosVM::c_MULV() {
357 VM_PARAM_DECIMAL(mul
)
359 if (v
->hasFloat() || mul
.hasFloat())
360 v
->setFloat(v
->getFloat() * mul
.getFloat());
361 else if (v
->hasInt() && mul
.hasInt())
362 v
->setInt(v
->getInt() * mul
.getInt());
364 throw badParamException();
367 int calculateRand(int value1
, int value2
) {
368 // TODO: i'm sure there's a better way to do this. tired. - fuzzie
370 if (abs(value2
) < abs(value1
))
371 diff
= abs(value1
- value2
) + 1;
373 diff
= abs(value2
- value1
) + 1;
379 double r
= rand() / ((unsigned int)RAND_MAX
+ 1.0);
381 return (int)(r
* diff
) + val
;
386 RAND (integer) value1 (integer) value2 (integer)
389 Returns a random integer between 'value1' and 'value2', inclusive.
391 void caosVM::v_RAND() {
393 VM_PARAM_INTEGER(value2
)
394 VM_PARAM_INTEGER(value1
)
396 result
.setInt(calculateRand(value1
, value2
));
403 Re-reads all catalogue files.
405 void caosVM::c_REAF() {
409 world
.initCatalogue();
416 Returns 'uname -a' on platforms which support it, or OS details in another format otherwise.
418 void caosVM::v_UFOS() {
420 result
.setString("some random platform"); // TODO
428 Returns information about which modules are being used by the engine (for now, backend and audio backend names).
430 void caosVM::v_MODU() {
432 result
.setString(engine
.getBackendName() + ", " + engine
.getAudioBackendName());
433 //result.setString("OriginalDisplay SDL (netbabel 148)"); // TODO
441 Returns the currently-running game (like "Creatures 1" or "Docking Station").
443 void caosVM::v_GNAM() {
445 result
.setString(engine
.getGameName());
449 ABSV (command) var (variable)
452 Modifies the given variable, if negative, so that its value is positive (absolute value).
454 void caosVM::c_ABSV() {
456 VM_PARAM_VARIABLE(var
)
458 if (var
->hasFloat()) var
->setFloat(fabs(var
->getFloat()));
459 else if (var
->hasInt()) var
->setInt(abs(var
->getInt()));
460 else throw badParamException();
464 ACOS (float) x (float)
467 Returns the arccosine of x in degrees.
469 void caosVM::v_ACOS() {
480 ASIN (float) x (float)
483 Returns the arcsine of x in degrees.
485 void caosVM::v_ASIN() {
496 ATAN (float) x (float)
499 Returns the arctangent of x in degrees.
501 void caosVM::v_ATAN() {
512 COS_ (float) x (float)
515 Returns the cosine of x in degrees.
517 void caosVM::v_COS_() {
521 double f
= x
* (M_PI
* 2);
523 result
.setFloat(cos(f
));
527 SIN_ (float) x (float)
530 Returns the sine of x in degrees.
532 void caosVM::v_SIN_() {
536 double f
= x
* (M_PI
* 2);
539 result
.setFloat(sin(f
));
543 TAN_ (float) x (float)
546 Returns the tangent of x in degrees.
548 void caosVM::v_TAN_() {
552 double f
= x
* (M_PI
* 2);
555 result
.setFloat(tan(f
));
559 SQRT (float) v (float)
562 Returns the square root of v.
564 void caosVM::v_SQRT() {
568 caos_assert(x
>= 0); // no imaginary numbers for you!
570 result
.setFloat(sqrt(x
));
575 %pragma implementation caosVM::v_P1
576 %pragma saveimpl caosVM::s_P1
578 %pragma variants c2 cv c3 sm
580 The first argument given to the current script.
585 %pragma implementation caosVM::v_P2
586 %pragma saveimpl caosVM::s_P2
588 %pragma variants c2 cv c3 sm
590 The second argument given to the current script.
592 CAOS_LVALUE_SIMPLE(P1
, _p_
[0])
593 CAOS_LVALUE_SIMPLE(P2
, _p_
[1])
596 AVAR (variable) agent (agent) index (integer)
599 Returns the value of OVxx for the given agent, where xx is equal to 'index'.
603 VM_PARAM_INTEGER(index
)
604 VM_PARAM_AGENT(agent
)
605 caos_assert(index
>= 0 && index
< 100);
610 agent
->var
[index
] = newvalue
614 VTOS (string) value (decimal)
617 Returns a string representation of the given value.
619 void caosVM::v_VTOS() {
621 VM_PARAM_DECIMAL(value
)
623 if (value
.hasInt()) {
624 result
.setString(boost::str(boost::format("%i") % value
.getInt()));
626 // TODO: this format isn't right (see OUTS also)
627 result
.setString(boost::str(boost::format("%f") % value
.getFloat()));
632 CHAR (integer) str (string) index (integer)
635 Returns the character at position 'index' of the given string.
638 void caosVM::v_CHAR() {
640 VM_PARAM_INTEGER(index
)
643 caos_assert(index
>= 1);
644 caos_assert(str
.size() >= (unsigned int)index
);
646 result
.setInt(str
[index
- 1]);
650 CHAR (command) string (variable) index (integer) character (integer)
653 Sets the character at position 'index' of the given string.
656 void caosVM::c_CHAR() {
657 VM_PARAM_INTEGER(character
)
658 VM_PARAM_INTEGER(index
)
659 VM_PARAM_VARIABLE(str
)
661 caos_assert(0 <= character
&& character
<= 255);
663 caos_assert(str
->hasString());
664 std::string mystr
= str
->getString();
665 caos_assert(index
>= 1);
666 caos_assert(mystr
.size() >= (unsigned int)index
);
668 mystr
[index
- 1] = (unsigned char)character
;
669 str
->setString(mystr
);
673 ITOF (float) number (integer)
676 Converts the given integer to a float.
678 void caosVM::v_ITOF() {
679 VM_PARAM_FLOAT(number
) // watson tells me this function is COMPLETELY pointless - fuzzie
681 result
.setFloat(number
);
685 FTOI (integer) number (float)
688 Converts the given float to an integer, by rounding.
690 void caosVM::v_FTOI() {
691 VM_PARAM_INTEGER(number
)
693 result
.setInt(number
);
697 STRL (integer) string (string)
700 Returns the length in characters of the given string.
702 void caosVM::v_STRL() {
703 VM_PARAM_STRING(string
)
705 result
.setInt(string
.size());
709 READ (string) tag (string) offset (integer)
712 Returns the value of the string at 'offset' inside the given catalogue tag.
713 Offsets start at zero. Throws an error if tag doesn't exist.
715 void caosVM::v_READ() {
716 VM_PARAM_INTEGER(offset
)
719 // TODO: i'm not sure if we're meant to throw errors here. - fuzzie
720 caos_assert(catalogue
.hasTag(tag
));
721 const std::vector
<std::string
> &t
= catalogue
.getTag(tag
);
722 caos_assert(offset
>= 0);
723 caos_assert((unsigned int)offset
< t
.size());
724 result
.setString(t
[offset
]);
728 REAQ (integer) tag (string)
731 Returns 1 if the specified catalogue tag exists, or 0 otherwise.
733 void caosVM::v_REAQ() {
736 if (catalogue
.hasTag(tag
))
746 Returns the agent category of the TARG agent.
748 void caosVM::v_CATA() {
751 result
.setInt(targ
->category
);
755 CATI (integer) family (integer) genus (integer) species (integer)
758 Returns the agent category for the given family/genus/species.
760 void caosVM::v_CATI() {
761 VM_PARAM_INTEGER(species
)
762 VM_PARAM_INTEGER(genus
)
763 VM_PARAM_INTEGER(family
) // TODO: check values are in range
765 result
.setInt(world
.findCategory(family
, genus
, species
));
769 CATX (string) category_id (integer)
772 Returns a string containing the name of the given category.
774 void caosVM::v_CATX() {
775 VM_PARAM_INTEGER(category_id
)
777 caos_assert(catalogue
.hasTag("Agent Categories"));
778 const std::vector
<std::string
> &t
= catalogue
.getTag("Agent Categories");
779 if (category_id
>= 0 && (unsigned int)category_id
< t
.size())
780 result
.setString(t
[category_id
]);
782 result
.setString("");
787 CATO (command) category_id (integer)
790 void caosVM::c_CATO() {
791 VM_PARAM_INTEGER(category_id
)
794 targ
->category
= category_id
;
798 WILD (string) family (integer) genus (integer) species (integer) tag (string) offset (integer)
801 Searches for a catalogue tag starting with 'tag' and matching the given
802 family/genus/species. For instance, 'Agent Help 2 3 4' where family is 2,
803 genus is 3 and species is 4. If it doesn't find it, it sets the minor value
804 to 0 repeatedly until it either finds one, or discovers 'tag 0 0 0' doesn't
805 exist, at which point an error is thrown. If it does find a suitable one, it
806 returns the string at offset inside the tag. See READ.
808 void caosVM::v_WILD() {
809 VM_PARAM_INTEGER(offset
)
811 VM_PARAM_INTEGER(species
)
812 VM_PARAM_INTEGER(genus
)
813 VM_PARAM_INTEGER(family
)
815 std::string searchstring
= catalogue
.calculateWildcardTag(tag
, family
, genus
, species
); // calculate tag name
816 caos_assert(searchstring
.size()); // check we found a tag
818 const std::vector
<std::string
> &t
= catalogue
.getTag(searchstring
); // retrieve tag
819 caos_assert(offset
>= 0);
820 caos_assert((unsigned int)offset
< t
.size()); // check the offset is useful for the tag we found
822 result
.setString(t
[offset
]);
826 NAME (variable) name (anything)
829 Named, agent-local variables (like OVxx) in the TARG agent.
831 CAOS_LVALUE_TARG(NAME
, VM_PARAM_VALUE(name
),
832 targ
->name_variables
[name
],
833 targ
->name_variables
[name
] = newvalue
837 MAME (variable) name (anything)
840 Like NAME variables, except for OWNR rather than TARG.
842 CAOS_LVALUE_WITH(MAME
, owner
,
843 VM_PARAM_VALUE(name
),
844 owner
->name_variables
[name
],
845 owner
->name_variables
[name
] = newvalue
849 SUBS (string) value (string) start (integer) count (integer)
852 Returns the text in a string starting at 'start' into the string (starting at 1), and with 'count' characters.
854 void caosVM::v_SUBS() {
855 VM_PARAM_INTEGER(count
)
856 VM_PARAM_INTEGER(start
)
857 VM_PARAM_STRING(value
)
859 result
.setString(value
.substr(start
- 1, count
)); // TODO: check start/count are valid?
863 STOI (integer) string (string)
866 Returns the provided string as an integer, or 0 if it can't be converted.
868 void caosVM::v_STOI() {
869 VM_PARAM_STRING(string
)
871 result
.setInt(atoi(string
.c_str()));
875 STOF (float) string (string)
878 Returns the provided string as a float, or 0 if it can't be converted.
880 void caosVM::v_STOF() {
881 VM_PARAM_STRING(string
)
883 result
.setFloat(atof(string
.c_str()));
887 LOWA (string) string (string)
890 Return a lower-cased version of a string.
892 void caosVM::v_LOWA() {
893 VM_PARAM_STRING(string
)
895 std::transform(string
.begin(), string
.end(), string
.begin(), (int(*)(int))tolower
);
896 result
.setString(string
);
900 UPPA (string) string (string)
903 Return an upper-cased version of a string.
905 void caosVM::v_UPPA() {
906 VM_PARAM_STRING(string
)
908 std::transform(string
.begin(), string
.end(), string
.begin(), (int(*)(int))toupper
);
909 result
.setString(string
);
913 SINS (integer) string (string) index (integer) searchstring (string)
916 Searches for an occurance of 'searchstring' inside 'string', starting at the given index inside 'string' (first character is 1).
917 The index of the 'searchstring' inside the string is returned, or -1 if the searchstring wasn't found.
919 void caosVM::v_SINS() {
920 VM_PARAM_STRING(searchstring
)
921 VM_PARAM_INTEGER(index
) caos_assert(index
> 0);
922 VM_PARAM_STRING(string
)
924 // TODO: check for string having a size, perhaps?
925 std::string::size_type i
= string
.find(searchstring
, index
- 1);
927 if (i
== string
.npos
)
930 result
.setInt(i
+ 1);
934 REAN (integer) tag (string)
937 Return number of strings associated with the catalogue tag specified.
939 void caosVM::v_REAN() {
942 if (!catalogue
.hasTag(tag
))
945 const std::vector
<std::string
> &t
= catalogue
.getTag(tag
);
946 result
.setInt(t
.size());
951 DELN (command) name (anything)
954 Delete the specified NAME variable on the target agent.
956 void caosVM::c_DELN() {
960 std::map
<caosVar
, caosVar
, caosVarCompare
>::iterator i
= targ
->name_variables
.find(name
);
961 if (i
== targ
->name_variables
.end()) return;
962 targ
->name_variables
.erase(i
);
966 GAMN (string) previous (string)
969 void caosVM::v_GAMN() {
970 VM_PARAM_STRING(previous
)
972 // TODO: we assume that GAME variables don't have an empty string
973 if (previous
.empty()) {
974 if (world
.variables
.size() == 0)
975 result
.setString("");
977 result
.setString(world
.variables
.begin()->first
);
979 std::map
<std::string
, caosVar
>::iterator i
= world
.variables
.find(previous
);
980 caos_assert(i
!= world
.variables
.end()); // TODO: this probably isn't correct behaviour
982 if (i
== world
.variables
.end())
983 result
.setString("");
985 result
.setString(i
->first
);
990 NAMN (command) previous (variable)
993 void caosVM::c_NAMN() {
994 VM_PARAM_VARIABLE(previous
)
997 // TODO: we assume that NAME variables don't have an empty string
998 if (previous
->hasString() && previous
->getString().empty()) {
999 if (targ
->name_variables
.size() == 0)
1000 previous
->setString("");
1002 *previous
= targ
->name_variables
.begin()->first
;
1004 std::map
<caosVar
, caosVar
, caosVarCompare
>::iterator i
= targ
->name_variables
.find(*previous
);
1005 caos_assert(i
!= targ
->name_variables
.end()); // TODO: this probably isn't correct behaviour
1007 if (i
== targ
->name_variables
.end())
1008 previous
->setString("");
1010 *previous
= i
->first
;
1015 * POWV (command) value (variable) exponent (decimal)
1018 * Raises value to the exponent power, and saves the result back in value.
1019 * Results are undefined if value is negative.
1021 * Openc2e-only command
1024 void caosVM::c_POWV() {
1025 VM_PARAM_FLOAT(exponent
)
1026 VM_PARAM_VARIABLE(value
)
1028 value
->setFloat(powf(value
->getFloat(), exponent
));
1032 RNDV (command) var (variable) value1 (integer) value (integer)
1034 %pragma variants c1 c2
1037 void caosVM::c_RNDV() {
1038 VM_PARAM_INTEGER(value2
)
1039 VM_PARAM_INTEGER(value1
)
1040 VM_PARAM_VARIABLE(var
)
1042 var
->setInt(calculateRand(value1
, value2
));
1050 Returns the egg limit - when there are more than this many norns in the world, eggs should not hatch.
1052 void caosVM::v_EGGL() {
1053 result
.setInt(16); // TODO
1061 Returns the hatchery limit - when there are more than this many norns in the world, the hatchery should shut down.
1063 void caosVM::v_HATL() {
1064 result
.setInt(12); // TODO
1067 /* vim: set noet: */